Files
AgenticCode/.planning/codebase/CONVENTIONS.md
2026-03-27 00:21:00 +00:00

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 .js extension 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 + .js extension
  • Examples: gsd-prompt-guard.js, gsd-context-monitor.js, gsd-statusline.js

Functions:

  • camelCase: detectConfigDir(), clearTimeout(), writeFileSync()
  • Single-letter shorthand acceptable for simple operations: f for 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.Z as first comment after shebang
    • Purpose explanation follows immediately

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):

  1. Node.js built-in modules: fs, path, os, child_process
  2. Destructured imports grouped: const { spawn } = require('child_process');
  3. 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 hookSpecificOutput wrapper:
    const output = {
      hookSpecificOutput: {
        hookEventName: 'PreToolUse',
        additionalContext: message
      }
    };
    

Process Exit:

  • Success: process.exit(0) or implicit exit after writing output
  • Error: process.exit(0) (never process.exit(1) to avoid breaking workflows)
  • Cleanup: clearTimeout() called before operations

File I/O Patterns

Synchronous Operations:

  • fs.readFileSync(path, 'utf8') for reads
  • fs.writeFileSync(path, JSON.stringify(data)) for writes
  • fs.existsSync(path) for checks
  • fs.readdirSync(path) for directory listing
  • fs.statSync(path).mtime for file metadata

Path Operations:

  • path.join() for path concatenation: path.join(cwd, '.planning', 'config.json')
  • path.basename(dir) for extracting final component
  • path.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*(.+)/)
  • .source property 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