6.6 KiB
6.6 KiB
Coding Conventions
Analysis Date: 2026-03-27
Language & Runtime
Primary Language: JavaScript (CommonJS)
- Node.js environment (no TypeScript transpilation)
- All hooks use
.jsextension with shebang:#!/usr/bin/env node
Module System:
- CommonJS (
require()only, no ES6 imports) - Node.js built-in modules:
fs,path,os,child_process - No external npm dependencies in hook files
Naming Patterns
Files:
- Kebab-case:
gsd-hook-name.js - Pattern:
gsd-prefix + feature name +.jsextension - Examples:
gsd-prompt-guard.js,gsd-context-monitor.js,gsd-statusline.js
Functions:
- camelCase:
detectConfigDir(),clearTimeout(),writeFileSync() - Single-letter shorthand acceptable for simple operations:
ffor file in loops
Constants:
- UPPER_SNAKE_CASE for immutable values:
WARNING_THRESHOLD,CRITICAL_THRESHOLD,STALE_SECONDS,DEBOUNCE_CALLS - Inline comments after constants for clarity:
const WARNING_THRESHOLD = 35; // remaining_percentage <= 35%
Variables:
- camelCase:
input,stdinTimeout,data,filePath,findings,configPath - Descriptive names:
remaining_percentage,callsSinceWarn,hookEventName - Single-letter loop variables:
f,e(for errors),p(for patterns)
Types/Objects:
- Object keys use snake_case:
tool_name,tool_input,file_path,hook_version,installed_version - Array items plural:
staleHooks,findings,allowedPatterns,files,hookFiles
Code Style
Formatting:
- No linter configured (no eslintrc, prettier, or biome config files found)
- Line length: typically 80-120 characters, no strict enforced limit observed
- Indentation: 2 spaces consistently across all files
Comments:
- Single-line comments:
// comment - Multi-line blocks: Multiple
//lines stacked (no/* */blocks found) - Header comments with metadata: Version, purpose, triggers, behavior
- Pattern:
// gsd-hook-version: X.Y.Zas first comment after shebang - Purpose explanation follows immediately
- Pattern:
String Formatting:
- Single quotes for regular strings:
'utf8','end' - Template literals for interpolation:
`${variable}` - Backticks for paths and code examples in comments:
`${filePath}`
Error Handling
Try-Catch Pattern:
- All file I/O and JSON parsing wrapped in try-catch
- Silent failures preferred: catch blocks often empty or call
process.exit(0) - Examples from codebase:
try { const data = JSON.parse(input); } catch { process.exit(0); }try { ... } catch (e) { // Silent fail on parse errors }
Graceful Degradation:
- Never block operations with errors
- Timeout guards prevent hanging:
const stdinTimeout = setTimeout(() => process.exit(0), 3000); - File existence checks before operations:
if (fs.existsSync(configPath)) { ... } - Return early on missing critical data:
if (!sessionId) { process.exit(0); }
Error Recovery:
- Corrupted files reset to defaults:
catch (e) { warnData = { callsSinceWarn: 0, lastLevel: null }; } - Optional chain operators for safe access:
data.tool_input?.file_path || ''
Type Coercion & Checks
Null/Undefined Handling:
- Null coalescing default values:
data.session_id || '',data.cwd || process.cwd() - Explicit null checks:
if (remaining != null)(distinguishes null from undefined) - Undefined/null fallbacks:
|| 'unknown',|| [],|| {}
Truthiness Checks:
- Explicit boolean comparisons:
if (config.hooks?.workflow_guard) { ... } - Array length checks:
if (files.length > 0) { ... },if (findings.length === 0) { ... }
Number Conversions:
- Explicit parsing:
Math.floor(Date.now() / 1000),Math.round(100 - usableRemaining) - Clamping with Math:
Math.max(0, value),Math.min(100, value)
Imports & Requires
Order (when present):
- Node.js built-in modules:
fs,path,os,child_process - Destructured imports grouped:
const { spawn } = require('child_process'); - No external package requires in hooks
Require Pattern:
const fs = require('fs');
const path = require('path');
const os = require('os');
const { spawn } = require('child_process');
Object & Array Patterns
Object Creation:
- Literal syntax:
{ hookEventName: 'PreToolUse', additionalContext: message } - Computed properties from template literals:
{ [key]: value }(not observed, uses direct literals) - Spread operators: Not used in codebase
Array Operations:
.filter():files.filter(f => f.startsWith(session)).map():files.map(f => ({ name: f, mtime: fs.statSync(...) })).find():todos.find(t => t.status === 'in_progress').some():allowedPatterns.some(p => p.test(filePath))- Arrow functions for callbacks:
chunk => input += chunk
Sorting & Ordering:
- Reverse chronological:
files.sort((a, b) => b.mtime - a.mtime) - File system operations ordered first, then filtering
Output Patterns
JSON Output:
- All hook output is JSON:
process.stdout.write(JSON.stringify(output)); - Output structure includes
hookSpecificOutputwrapper:const output = { hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: message } };
Process Exit:
- Success:
process.exit(0)or implicit exit after writing output - Error:
process.exit(0)(neverprocess.exit(1)to avoid breaking workflows) - Cleanup:
clearTimeout()called before operations
File I/O Patterns
Synchronous Operations:
fs.readFileSync(path, 'utf8')for readsfs.writeFileSync(path, JSON.stringify(data))for writesfs.existsSync(path)for checksfs.readdirSync(path)for directory listingfs.statSync(path).mtimefor file metadata
Path Operations:
path.join()for path concatenation:path.join(cwd, '.planning', 'config.json')path.basename(dir)for extracting final componentpath.dirname()for parent directory
Regular Expressions
Pattern Definition:
- Captured in constants array at file top:
const INJECTION_PATTERNS = [ /pattern1/i, /pattern2/i ] - Case-insensitive flag commonly used:
/pattern/i - Multiline patterns use raw strings:
/[\u200B-\u200F]/for Unicode detection
Pattern Testing:
.test()method for boolean check:if (pattern.test(content)).match()for capture groups:const versionMatch = content.match(/\\/\\/ gsd-hook-version:\\s*(.+)/).sourceproperty for pattern string:findings.push(pattern.source)
No Additional Frameworks
Testing: Not detected - no test files exist Build Tools: Not detected - runs directly as Node.js scripts Linting/Formatting: Not detected - no .eslintrc, .prettierrc, or similar configs
Convention analysis: 2026-03-27