start-vibing 2.0.8 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +177 -176
  2. package/dist/cli.js +38 -11
  3. package/package.json +42 -42
  4. package/template/.claude/CLAUDE.md +174 -152
  5. package/template/.claude/agents/01-orchestration/agent-selector.md +130 -123
  6. package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -131
  7. package/template/.claude/agents/01-orchestration/context-manager.md +138 -124
  8. package/template/.claude/agents/01-orchestration/error-recovery.md +182 -176
  9. package/template/.claude/agents/01-orchestration/orchestrator.md +114 -107
  10. package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -130
  11. package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -118
  12. package/template/.claude/agents/01-orchestration/workflow-router.md +114 -111
  13. package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -180
  14. package/template/.claude/agents/02-typescript/esm-resolver.md +193 -187
  15. package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -149
  16. package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -165
  17. package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -227
  18. package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -162
  19. package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -185
  20. package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -183
  21. package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -197
  22. package/template/.claude/agents/02-typescript/zod-validator.md +158 -153
  23. package/template/.claude/agents/03-testing/playwright-assertions.md +265 -255
  24. package/template/.claude/agents/03-testing/playwright-e2e.md +247 -245
  25. package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -240
  26. package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -261
  27. package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
  28. package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -256
  29. package/template/.claude/agents/03-testing/test-data-generator.md +254 -266
  30. package/template/.claude/agents/03-testing/tester-integration.md +278 -278
  31. package/template/.claude/agents/03-testing/tester-unit.md +207 -204
  32. package/template/.claude/agents/03-testing/vitest-config.md +287 -289
  33. package/template/.claude/agents/04-docker/container-health.md +255 -239
  34. package/template/.claude/agents/04-docker/deployment-validator.md +225 -217
  35. package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -268
  36. package/template/.claude/agents/04-docker/docker-env-manager.md +235 -228
  37. package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -229
  38. package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -204
  39. package/template/.claude/agents/05-database/data-migration.md +0 -293
  40. package/template/.claude/agents/05-database/database-seeder.md +273 -270
  41. package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -219
  42. package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -280
  43. package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -174
  44. package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
  45. package/template/.claude/agents/06-security/auth-session-validator.md +68 -65
  46. package/template/.claude/agents/06-security/input-sanitizer.md +80 -81
  47. package/template/.claude/agents/06-security/owasp-checker.md +97 -87
  48. package/template/.claude/agents/06-security/permission-auditor.md +100 -95
  49. package/template/.claude/agents/06-security/security-auditor.md +84 -82
  50. package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -85
  51. package/template/.claude/agents/07-documentation/api-documenter.md +136 -131
  52. package/template/.claude/agents/07-documentation/changelog-manager.md +105 -96
  53. package/template/.claude/agents/07-documentation/documenter.md +76 -73
  54. package/template/.claude/agents/07-documentation/domain-updater.md +81 -74
  55. package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
  56. package/template/.claude/agents/07-documentation/readme-generator.md +135 -132
  57. package/template/.claude/agents/08-git/branch-manager.md +58 -58
  58. package/template/.claude/agents/08-git/commit-manager.md +63 -61
  59. package/template/.claude/agents/08-git/pr-creator.md +76 -72
  60. package/template/.claude/agents/09-quality/code-reviewer.md +71 -64
  61. package/template/.claude/agents/09-quality/quality-checker.md +67 -67
  62. package/template/.claude/agents/10-research/best-practices-finder.md +89 -82
  63. package/template/.claude/agents/10-research/competitor-analyzer.md +106 -96
  64. package/template/.claude/agents/10-research/pattern-researcher.md +93 -86
  65. package/template/.claude/agents/10-research/research-cache-manager.md +76 -75
  66. package/template/.claude/agents/10-research/research-web.md +98 -91
  67. package/template/.claude/agents/10-research/tech-evaluator.md +101 -94
  68. package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -128
  69. package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -116
  70. package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -120
  71. package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -126
  72. package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -94
  73. package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -111
  74. package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -149
  75. package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -107
  76. package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -126
  77. package/template/.claude/agents/12-performance/performance-profiler.md +115 -108
  78. package/template/.claude/agents/12-performance/query-optimizer.md +124 -116
  79. package/template/.claude/agents/12-performance/render-optimizer.md +154 -148
  80. package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -188
  81. package/template/.claude/agents/13-debugging/debugger.md +149 -137
  82. package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -131
  83. package/template/.claude/agents/13-debugging/network-debugger.md +208 -185
  84. package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -173
  85. package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -173
  86. package/template/.claude/agents/14-validation/final-validator.md +93 -83
  87. package/template/.claude/agents/_backup/analyzer.md +134 -125
  88. package/template/.claude/agents/_backup/code-reviewer.md +279 -272
  89. package/template/.claude/agents/_backup/commit-manager.md +219 -212
  90. package/template/.claude/agents/_backup/debugger.md +280 -271
  91. package/template/.claude/agents/_backup/documenter.md +237 -220
  92. package/template/.claude/agents/_backup/domain-updater.md +197 -194
  93. package/template/.claude/agents/_backup/final-validator.md +169 -164
  94. package/template/.claude/agents/_backup/orchestrator.md +149 -138
  95. package/template/.claude/agents/_backup/performance.md +232 -228
  96. package/template/.claude/agents/_backup/quality-checker.md +240 -240
  97. package/template/.claude/agents/_backup/research.md +315 -299
  98. package/template/.claude/agents/_backup/security-auditor.md +192 -186
  99. package/template/.claude/agents/_backup/tester.md +566 -564
  100. package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -242
  101. package/template/.claude/commands/feature.md +48 -48
  102. package/template/.claude/config/README.md +30 -30
  103. package/template/.claude/config/mcp-config.json +344 -330
  104. package/template/.claude/config/project-config.json +53 -53
  105. package/template/.claude/config/quality-gates.json +46 -46
  106. package/template/.claude/config/security-rules.json +45 -45
  107. package/template/.claude/config/testing-config.json +164 -168
  108. package/template/.claude/hooks/SETUP.md +126 -126
  109. package/template/.claude/hooks/run-hook.ts +176 -172
  110. package/template/.claude/hooks/stop-validator.ts +825 -353
  111. package/template/.claude/hooks/user-prompt-submit.ts +886 -794
  112. package/template/.claude/scripts/mcp-quick-install.ts +151 -151
  113. package/template/.claude/scripts/setup-mcps.ts +651 -628
  114. package/template/.claude/settings.json +275 -276
  115. package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
  116. package/template/.claude/skills/codebase-knowledge/SKILL.md +145 -145
  117. package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -403
  118. package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -281
  119. package/template/.claude/skills/debugging-patterns/SKILL.md +485 -484
  120. package/template/.claude/skills/docker-patterns/SKILL.md +555 -547
  121. package/template/.claude/skills/docs-tracker/SKILL.md +239 -239
  122. package/template/.claude/skills/final-check/SKILL.md +284 -284
  123. package/template/.claude/skills/git-workflow/SKILL.md +454 -454
  124. package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -512
  125. package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -337
  126. package/template/.claude/skills/performance-patterns/SKILL.md +547 -549
  127. package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
  128. package/template/.claude/skills/quality-gate/SKILL.md +294 -294
  129. package/template/.claude/skills/react-patterns/SKILL.md +389 -376
  130. package/template/.claude/skills/research-cache/SKILL.md +222 -207
  131. package/template/.claude/skills/security-scan/SKILL.md +222 -222
  132. package/template/.claude/skills/shadcn-ui/SKILL.md +511 -520
  133. package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -467
  134. package/template/.claude/skills/test-coverage/SKILL.md +467 -464
  135. package/template/.claude/skills/trpc-api/SKILL.md +434 -435
  136. package/template/.claude/skills/typescript-strict/SKILL.md +367 -368
  137. package/template/.claude/skills/ui-ux-audit/SKILL.md +254 -254
  138. package/template/.claude/skills/zod-validation/SKILL.md +403 -405
  139. package/template/CLAUDE.md +25 -25
@@ -1,353 +1,825 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Stop Validator Hook - TypeScript version (fallback when Python not available)
4
- *
5
- * VALIDATES:
6
- * 1. NOT on main branch (unless no source changes)
7
- * 2. All modified files are documented
8
- * 3. Documenter agent was executed for source changes
9
- *
10
- * BLOCKS completion if validations fail.
11
- */
12
-
13
- import { execSync } from 'child_process';
14
- import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
15
- import { join, basename, extname } from 'path';
16
-
17
- const IGNORE_DIRS = new Set([
18
- '.next', 'node_modules', 'dist', 'build', 'coverage',
19
- '.git', '__pycache__', '.turbo', '.cache', '.husky',
20
- 'packages'
21
- ]);
22
-
23
- const IGNORE_PATTERNS = [
24
- '.lock', '.log', '.map', '.min.js', '.min.css',
25
- 'package-lock.json', 'bun.lockb', '.DS_Store', 'Thumbs.db'
26
- ];
27
-
28
- const DOC_EXTENSIONS = new Set(['.md', '.mdx', '.txt', '.rst']);
29
-
30
- const SOURCE_EXTENSIONS = new Set([
31
- '.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs',
32
- '.java', '.kt', '.swift', '.vue', '.svelte'
33
- ]);
34
-
35
- const SYSTEM_CONFIG_PATTERNS = [
36
- 'CLAUDE.md',
37
- '.claude/',
38
- 'PROJECT.md',
39
- '.env.example',
40
- ];
41
-
42
- function shouldIgnoreFile(filePath: string): boolean {
43
- const parts = filePath.split(/[/\\]/);
44
-
45
- for (const part of parts) {
46
- if (IGNORE_DIRS.has(part)) return true;
47
- }
48
-
49
- for (const pattern of IGNORE_PATTERNS) {
50
- if (filePath.includes(pattern)) return true;
51
- }
52
-
53
- return false;
54
- }
55
-
56
- function isSourceFile(filePath: string): boolean {
57
- const ext = extname(filePath);
58
- return SOURCE_EXTENSIONS.has(ext) && !shouldIgnoreFile(filePath);
59
- }
60
-
61
- function isSystemConfigFile(filePath: string): boolean {
62
- return SYSTEM_CONFIG_PATTERNS.some(pattern => filePath.includes(pattern));
63
- }
64
-
65
- function getCurrentBranch(projectDir: string): string {
66
- try {
67
- const result = execSync('git rev-parse --abbrev-ref HEAD', {
68
- cwd: projectDir,
69
- encoding: 'utf8',
70
- stdio: ['pipe', 'pipe', 'pipe']
71
- });
72
- return result.trim();
73
- } catch {
74
- return 'unknown';
75
- }
76
- }
77
-
78
- function getModifiedFiles(projectDir: string): string[] {
79
- try {
80
- const staged = execSync('git diff --name-only --cached', {
81
- cwd: projectDir,
82
- encoding: 'utf8',
83
- stdio: ['pipe', 'pipe', 'pipe']
84
- }).trim().split('\n').filter(Boolean);
85
-
86
- const unstaged = execSync('git diff --name-only', {
87
- cwd: projectDir,
88
- encoding: 'utf8',
89
- stdio: ['pipe', 'pipe', 'pipe']
90
- }).trim().split('\n').filter(Boolean);
91
-
92
- const untracked = execSync('git ls-files --others --exclude-standard', {
93
- cwd: projectDir,
94
- encoding: 'utf8',
95
- stdio: ['pipe', 'pipe', 'pipe']
96
- }).trim().split('\n').filter(Boolean);
97
-
98
- return [...new Set([...staged, ...unstaged, ...untracked])].filter(Boolean);
99
- } catch {
100
- return [];
101
- }
102
- }
103
-
104
- function* walkDir(dir: string): Generator<string> {
105
- if (!existsSync(dir)) return;
106
-
107
- const entries = readdirSync(dir);
108
- for (const entry of entries) {
109
- const fullPath = join(dir, entry);
110
- try {
111
- const stat = statSync(fullPath);
112
- if (stat.isDirectory()) {
113
- yield* walkDir(fullPath);
114
- } else if (stat.isFile()) {
115
- yield fullPath;
116
- }
117
- } catch {
118
- continue;
119
- }
120
- }
121
- }
122
-
123
- function searchInDocs(projectDir: string, filePath: string): boolean {
124
- const fileName = basename(filePath);
125
- const fileStem = basename(filePath, extname(filePath));
126
-
127
- const docDirs = [
128
- join(projectDir, 'docs'),
129
- join(projectDir, '.claude', 'skills', 'codebase-knowledge', 'domains'),
130
- ];
131
-
132
- for (const docDir of docDirs) {
133
- if (!existsSync(docDir)) continue;
134
-
135
- for (const docFile of walkDir(docDir)) {
136
- const ext = extname(docFile);
137
- if (!DOC_EXTENSIONS.has(ext)) continue;
138
-
139
- try {
140
- const content = readFileSync(docFile, 'utf8');
141
- if (content.includes(fileName) || content.includes(fileStem) || content.includes(filePath)) {
142
- return true;
143
- }
144
- } catch {
145
- continue;
146
- }
147
- }
148
- }
149
-
150
- return false;
151
- }
152
-
153
- function validateDocumentation(projectDir: string, modifiedFiles: string[]): { undocumented: string[], documented: string[] } {
154
- const undocumented: string[] = [];
155
- const documented: string[] = [];
156
-
157
- for (const filePath of modifiedFiles) {
158
- if (!isSourceFile(filePath)) continue;
159
-
160
- if (searchInDocs(projectDir, filePath)) {
161
- documented.push(filePath);
162
- } else {
163
- undocumented.push(filePath);
164
- }
165
- }
166
-
167
- return { undocumented, documented };
168
- }
169
-
170
- interface HookInput {
171
- stop_hook_active?: boolean;
172
- }
173
-
174
- interface HookResult {
175
- decision: 'approve' | 'block';
176
- reason: string;
177
- }
178
-
179
- async function readStdinWithTimeout(timeoutMs: number): Promise<string> {
180
- return new Promise((resolve) => {
181
- const timeout = setTimeout(() => {
182
- process.stdin.destroy();
183
- resolve('{}');
184
- }, timeoutMs);
185
-
186
- let data = '';
187
- process.stdin.setEncoding('utf8');
188
- process.stdin.on('data', (chunk: string) => {
189
- data += chunk;
190
- });
191
- process.stdin.on('end', () => {
192
- clearTimeout(timeout);
193
- resolve(data || '{}');
194
- });
195
- process.stdin.on('error', () => {
196
- clearTimeout(timeout);
197
- resolve('{}');
198
- });
199
-
200
- if (process.stdin.readableEnded) {
201
- clearTimeout(timeout);
202
- resolve('{}');
203
- }
204
- });
205
- }
206
-
207
- async function main(): Promise<void> {
208
- const projectDir = process.env['CLAUDE_PROJECT_DIR'] || process.cwd();
209
-
210
- let hookInput: HookInput = {};
211
- try {
212
- const stdin = await readStdinWithTimeout(1000);
213
- if (stdin && stdin.trim()) {
214
- hookInput = JSON.parse(stdin);
215
- }
216
- } catch {
217
- hookInput = {};
218
- }
219
-
220
- // Prevent infinite loops
221
- if (hookInput.stop_hook_active) {
222
- const result: HookResult = { decision: 'approve', reason: 'Stop hook cycle detected, allowing exit' };
223
- console.log(JSON.stringify(result));
224
- process.exit(0);
225
- }
226
-
227
- const currentBranch = getCurrentBranch(projectDir);
228
- const modifiedFiles = getModifiedFiles(projectDir);
229
- const sourceFiles = modifiedFiles.filter(isSourceFile);
230
- const systemConfigFiles = modifiedFiles.filter(isSystemConfigFile);
231
-
232
- const errors: Array<{ type: string; message: string }> = [];
233
-
234
- // Check 1: ANY changes on main branch are FORBIDDEN
235
- if ((currentBranch === 'main' || currentBranch === 'master') && modifiedFiles.length > 0) {
236
- const fileList = modifiedFiles.slice(0, 15).map(f => ` - ${f}`).join('\n');
237
- const more = modifiedFiles.length > 15 ? '\n ... and more' : '';
238
-
239
- errors.push({
240
- type: 'DIRECT_MAIN_COMMIT',
241
- message: `
242
- BLOCKED: Attempting to work directly on '${currentBranch}' branch!
243
-
244
- You have ${modifiedFiles.length} modified file(s):
245
- ${fileList}${more}
246
-
247
- REQUIRED ACTION:
248
- 1. Create a feature branch: git checkout -b feature/[name]
249
- 2. Or fix/chore branch: git checkout -b fix/[name] or git checkout -b chore/[name]
250
- 3. Continue your work on the new branch
251
- 4. Create PR to merge back to main
252
-
253
- NEVER make ANY changes directly on main. ALL work must be done on branches.
254
- `
255
- });
256
- }
257
-
258
- // Check 2: Documentation for source files
259
- if (sourceFiles.length > 0) {
260
- const { undocumented } = validateDocumentation(projectDir, sourceFiles);
261
-
262
- if (undocumented.length > 0) {
263
- const fileList = undocumented.slice(0, 15).map(f => ` - ${f}`).join('\n');
264
- const more = undocumented.length > 15 ? '\n ... and more' : '';
265
-
266
- errors.push({
267
- type: 'MISSING_DOCUMENTATION',
268
- message: `
269
- BLOCKED: ${undocumented.length} source file(s) are NOT documented!
270
-
271
- Undocumented files:
272
- ${fileList}${more}
273
-
274
- REQUIRED ACTION:
275
- Run the documenter agent to update documentation:
276
-
277
- Task(subagent_type="documenter", prompt="Update documentation for all modified files")
278
-
279
- The documenter will:
280
- 1. Detect changed files via git diff
281
- 2. Update domain files in .claude/skills/codebase-knowledge/domains/
282
- 3. Update docs/CHANGELOG.md
283
- 4. Ensure all modified files are mentioned in documentation
284
-
285
- DOCUMENTATION IS MANDATORY before task completion.
286
- `
287
- });
288
- }
289
- }
290
-
291
- if (errors.length > 0) {
292
- let errorOutput = `
293
- ================================================================================
294
- STOP VALIDATOR - BLOCKING TASK COMPLETION
295
- ================================================================================
296
- `;
297
- for (const err of errors) {
298
- errorOutput += `
299
- --- ${err.type} ---
300
- ${err.message}
301
- `;
302
- }
303
-
304
- errorOutput += `
305
- ================================================================================
306
- FIX THE ABOVE ISSUES BEFORE COMPLETING THE TASK
307
- ================================================================================
308
- `;
309
-
310
- const result: HookResult = { decision: 'block', reason: errorOutput.trim() };
311
- console.log(JSON.stringify(result));
312
- process.exit(0);
313
- }
314
-
315
- // All validations passed
316
- let nextStepMessage: string;
317
- if (modifiedFiles.length > 0) {
318
- nextStepMessage = `
319
- ================================================================================
320
- ✓ ALL VALIDATIONS PASSED
321
- ================================================================================
322
-
323
- Branch: ${currentBranch}
324
- Modified files: ${modifiedFiles.length} (source: ${sourceFiles.length}, config: ${systemConfigFiles.length})
325
-
326
- MANDATORY NEXT STEP:
327
- You MUST now commit, push, and create a PR:
328
-
329
- 1. git add -A
330
- 2. git commit with conventional commit message
331
- 3. git push -u origin ${currentBranch}
332
- 4. gh pr create to main branch
333
-
334
- DO NOT ask the user - just execute the commit-manager flow.
335
-
336
- ================================================================================
337
- `;
338
- } else {
339
- nextStepMessage = `All validations passed. Branch: ${currentBranch}, no files modified.`;
340
- }
341
-
342
- const result: HookResult = { decision: 'approve', reason: nextStepMessage.trim() };
343
- console.log(JSON.stringify(result));
344
- process.exit(0);
345
- }
346
-
347
- main().catch((err) => {
348
- console.error('Hook error:', err);
349
- // On error, allow to continue to not block user
350
- const result: HookResult = { decision: 'approve', reason: 'Hook error, allowing by default' };
351
- console.log(JSON.stringify(result));
352
- process.exit(0);
353
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Stop Validator Hook - Complete Validation System
4
+ *
5
+ * THIS HOOK BLOCKS TASK COMPLETION IF ANY OF THESE CONDITIONS FAIL:
6
+ *
7
+ * 1. BRANCH CHECK: Must be on 'main' branch (PR must be merged)
8
+ * 2. GIT TREE CHECK: Working tree must be clean (no uncommitted changes)
9
+ * 3. CLAUDE.MD CHECK: Must be updated with session changes
10
+ * 4. CLAUDE.MD STRUCTURE: Must have required sections
11
+ * 5. CLAUDE.MD SIZE: Must not exceed 40,000 characters
12
+ * 6. DOCUMENTATION CHECK: All source files must be documented
13
+ *
14
+ * ERROR MESSAGES ARE DESCRIPTIVE: They guide the agent on exactly what to do.
15
+ */
16
+
17
+ import { execSync } from 'child_process';
18
+ import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
19
+ import { join, basename, extname } from 'path';
20
+
21
+ // ============================================================================
22
+ // CONFIGURATION
23
+ // ============================================================================
24
+
25
+ const PROJECT_DIR = process.env['CLAUDE_PROJECT_DIR'] || process.cwd();
26
+ const CLAUDE_MD_PATH = join(PROJECT_DIR, 'CLAUDE.md');
27
+ const MAX_CHARACTERS = 40000;
28
+
29
+ const IGNORE_DIRS = new Set([
30
+ '.next',
31
+ 'node_modules',
32
+ 'dist',
33
+ 'build',
34
+ 'coverage',
35
+ '.git',
36
+ '__pycache__',
37
+ '.turbo',
38
+ '.cache',
39
+ '.husky',
40
+ 'packages',
41
+ ]);
42
+
43
+ const IGNORE_PATTERNS = [
44
+ '.lock',
45
+ '.log',
46
+ '.map',
47
+ '.min.js',
48
+ '.min.css',
49
+ 'package-lock.json',
50
+ 'bun.lockb',
51
+ '.DS_Store',
52
+ 'Thumbs.db',
53
+ ];
54
+
55
+ const DOC_EXTENSIONS = new Set(['.md', '.mdx', '.txt', '.rst']);
56
+
57
+ const SOURCE_EXTENSIONS = new Set([
58
+ '.ts',
59
+ '.tsx',
60
+ '.js',
61
+ '.jsx',
62
+ '.py',
63
+ '.go',
64
+ '.rs',
65
+ '.java',
66
+ '.kt',
67
+ '.swift',
68
+ '.vue',
69
+ '.svelte',
70
+ ]);
71
+
72
+ // Required sections in CLAUDE.md
73
+ const REQUIRED_SECTIONS = [
74
+ { pattern: /^# .+/m, name: 'Project Title (H1)' },
75
+ { pattern: /^## Last Change/m, name: 'Last Change' },
76
+ { pattern: /^## 30 Seconds Overview/m, name: '30 Seconds Overview' },
77
+ { pattern: /^## Stack/m, name: 'Stack' },
78
+ { pattern: /^## Architecture/m, name: 'Architecture' },
79
+ ];
80
+
81
+ // ============================================================================
82
+ // HELPER FUNCTIONS
83
+ // ============================================================================
84
+
85
+ function shouldIgnoreFile(filePath: string): boolean {
86
+ const parts = filePath.split(/[/\\]/);
87
+ for (const part of parts) {
88
+ if (IGNORE_DIRS.has(part)) return true;
89
+ }
90
+ for (const pattern of IGNORE_PATTERNS) {
91
+ if (filePath.includes(pattern)) return true;
92
+ }
93
+ return false;
94
+ }
95
+
96
+ function isSourceFile(filePath: string): boolean {
97
+ const ext = extname(filePath);
98
+ return SOURCE_EXTENSIONS.has(ext) && !shouldIgnoreFile(filePath);
99
+ }
100
+
101
+ function getCurrentBranch(): string {
102
+ try {
103
+ return execSync('git rev-parse --abbrev-ref HEAD', {
104
+ cwd: PROJECT_DIR,
105
+ encoding: 'utf8',
106
+ stdio: ['pipe', 'pipe', 'pipe'],
107
+ }).trim();
108
+ } catch {
109
+ return 'unknown';
110
+ }
111
+ }
112
+
113
+ function getModifiedFiles(): string[] {
114
+ try {
115
+ const staged = execSync('git diff --name-only --cached', {
116
+ cwd: PROJECT_DIR,
117
+ encoding: 'utf8',
118
+ stdio: ['pipe', 'pipe', 'pipe'],
119
+ })
120
+ .trim()
121
+ .split('\n')
122
+ .filter(Boolean);
123
+
124
+ const unstaged = execSync('git diff --name-only', {
125
+ cwd: PROJECT_DIR,
126
+ encoding: 'utf8',
127
+ stdio: ['pipe', 'pipe', 'pipe'],
128
+ })
129
+ .trim()
130
+ .split('\n')
131
+ .filter(Boolean);
132
+
133
+ const untracked = execSync('git ls-files --others --exclude-standard', {
134
+ cwd: PROJECT_DIR,
135
+ encoding: 'utf8',
136
+ stdio: ['pipe', 'pipe', 'pipe'],
137
+ })
138
+ .trim()
139
+ .split('\n')
140
+ .filter(Boolean);
141
+
142
+ return [...new Set([...staged, ...unstaged, ...untracked])].filter(Boolean);
143
+ } catch {
144
+ return [];
145
+ }
146
+ }
147
+
148
+ function* walkDir(dir: string): Generator<string> {
149
+ if (!existsSync(dir)) return;
150
+ const entries = readdirSync(dir);
151
+ for (const entry of entries) {
152
+ const fullPath = join(dir, entry);
153
+ try {
154
+ const stat = statSync(fullPath);
155
+ if (stat.isDirectory()) {
156
+ yield* walkDir(fullPath);
157
+ } else if (stat.isFile()) {
158
+ yield fullPath;
159
+ }
160
+ } catch {
161
+ continue;
162
+ }
163
+ }
164
+ }
165
+
166
+ function searchInDocs(filePath: string): boolean {
167
+ const fileName = basename(filePath);
168
+ const fileStem = basename(filePath, extname(filePath));
169
+
170
+ const docDirs = [
171
+ join(PROJECT_DIR, 'docs'),
172
+ join(PROJECT_DIR, '.claude', 'skills', 'codebase-knowledge', 'domains'),
173
+ ];
174
+
175
+ for (const docDir of docDirs) {
176
+ if (!existsSync(docDir)) continue;
177
+ for (const docFile of walkDir(docDir)) {
178
+ const ext = extname(docFile);
179
+ if (!DOC_EXTENSIONS.has(ext)) continue;
180
+ try {
181
+ const content = readFileSync(docFile, 'utf8');
182
+ if (
183
+ content.includes(fileName) ||
184
+ content.includes(fileStem) ||
185
+ content.includes(filePath)
186
+ ) {
187
+ return true;
188
+ }
189
+ } catch {
190
+ continue;
191
+ }
192
+ }
193
+ }
194
+ return false;
195
+ }
196
+
197
+ // ============================================================================
198
+ // VALIDATION FUNCTIONS
199
+ // ============================================================================
200
+
201
+ interface ValidationError {
202
+ type: string;
203
+ message: string;
204
+ action: string;
205
+ }
206
+
207
+ function validateBranch(currentBranch: string, modifiedFiles: string[]): ValidationError | null {
208
+ const isMainBranch = currentBranch === 'main' || currentBranch === 'master';
209
+
210
+ // Case 1: On feature branch with changes - need to complete workflow
211
+ if (!isMainBranch && modifiedFiles.length > 0) {
212
+ return {
213
+ type: 'FEATURE_BRANCH_NOT_MERGED',
214
+ message: `Currently on branch '${currentBranch}' with uncommitted changes. The task cannot complete until work is merged to main.`,
215
+ action: `
216
+ ================================================================================
217
+ COMPLETE THE GIT WORKFLOW BEFORE TASK COMPLETION
218
+ ================================================================================
219
+
220
+ You are on branch '${currentBranch}' with ${modifiedFiles.length} modified file(s).
221
+
222
+ REQUIRED STEPS (execute in order):
223
+
224
+ 1. STAGE all changes:
225
+ git add -A
226
+
227
+ 2. COMMIT with conventional message:
228
+ git commit -m "feat: description of changes"
229
+
230
+ 3. PUSH to remote:
231
+ git push -u origin ${currentBranch}
232
+
233
+ 4. CREATE Pull Request:
234
+ gh pr create --title "Title" --body "$(cat <<'EOF'
235
+ ## Summary
236
+ - What was changed
237
+
238
+ ## Test plan
239
+ - How to verify
240
+
241
+ Generated with Claude Code
242
+ EOF
243
+ )"
244
+
245
+ 5. MERGE the PR (auto-delete branch):
246
+ gh pr merge --merge --delete-branch
247
+
248
+ 6. SWITCH to main and pull:
249
+ git checkout main && git pull
250
+
251
+ THEN the task can complete. The stop hook will verify main branch + clean tree.
252
+ ================================================================================`,
253
+ };
254
+ }
255
+
256
+ // Case 2: On feature branch with clean tree - just need to switch
257
+ if (!isMainBranch && modifiedFiles.length === 0) {
258
+ return {
259
+ type: 'NOT_ON_MAIN_BRANCH',
260
+ message: `Currently on branch '${currentBranch}'. Task completion requires being on 'main'.`,
261
+ action: `
262
+ ================================================================================
263
+ SWITCH TO MAIN BRANCH
264
+ ================================================================================
265
+
266
+ The working tree is clean but you're on branch '${currentBranch}'.
267
+
268
+ If your PR was merged:
269
+ git checkout main && git pull
270
+
271
+ If you still need to create/merge PR:
272
+ gh pr create (if not created)
273
+ gh pr merge --merge --delete-branch (to merge)
274
+ git checkout main && git pull
275
+
276
+ IMPORTANT: Task completion is BLOCKED until you are on 'main' with a clean tree.
277
+ ================================================================================`,
278
+ };
279
+ }
280
+
281
+ // Case 3: On main with changes - FORBIDDEN
282
+ if (isMainBranch && modifiedFiles.length > 0) {
283
+ const fileList = modifiedFiles
284
+ .slice(0, 10)
285
+ .map((f) => ` - ${f}`)
286
+ .join('\n');
287
+ return {
288
+ type: 'DIRECT_MAIN_COMMIT_FORBIDDEN',
289
+ message: `CRITICAL: Attempting to work directly on '${currentBranch}' branch with changes!`,
290
+ action: `
291
+ ================================================================================
292
+ FORBIDDEN: DIRECT COMMITS TO MAIN
293
+ ================================================================================
294
+
295
+ You have ${modifiedFiles.length} modified file(s) on main branch:
296
+ ${fileList}${modifiedFiles.length > 10 ? '\n ... and more' : ''}
297
+
298
+ ALL work MUST be done on feature branches. This is MANDATORY.
299
+
300
+ REQUIRED STEPS:
301
+
302
+ 1. CREATE a feature branch:
303
+ git checkout -b feature/your-feature-name
304
+ (or fix/, refactor/, chore/, test/ as appropriate)
305
+
306
+ 2. CONTINUE your work on the new branch
307
+
308
+ 3. When done, create PR to merge back to main
309
+
310
+ NEVER commit directly to main. The stop hook will BLOCK this.
311
+ ================================================================================`,
312
+ };
313
+ }
314
+
315
+ return null; // All good - on main with clean tree
316
+ }
317
+
318
+ function validateGitTree(modifiedFiles: string[]): ValidationError | null {
319
+ if (modifiedFiles.length === 0) return null;
320
+
321
+ const fileList = modifiedFiles
322
+ .slice(0, 15)
323
+ .map((f) => ` - ${f}`)
324
+ .join('\n');
325
+
326
+ return {
327
+ type: 'GIT_TREE_NOT_CLEAN',
328
+ message: `Git working tree is not clean. Found ${modifiedFiles.length} modified/untracked file(s).`,
329
+ action: `
330
+ ================================================================================
331
+ GIT TREE MUST BE CLEAN FOR TASK COMPLETION
332
+ ================================================================================
333
+
334
+ Modified files:
335
+ ${fileList}${modifiedFiles.length > 15 ? '\n ... and more' : ''}
336
+
337
+ The task cannot complete with uncommitted work.
338
+
339
+ OPTIONS:
340
+
341
+ 1. COMMIT the changes (recommended):
342
+ git add -A
343
+ git commit -m "type: description"
344
+ git push
345
+
346
+ 2. STASH for later:
347
+ git stash push -m "WIP: description"
348
+
349
+ 3. DISCARD changes (use with caution):
350
+ git checkout -- .
351
+ git clean -fd
352
+
353
+ After cleaning the tree, the stop hook will pass.
354
+ ================================================================================`,
355
+ };
356
+ }
357
+
358
+ function validateClaudeMdExists(): ValidationError | null {
359
+ if (!existsSync(CLAUDE_MD_PATH)) {
360
+ return {
361
+ type: 'CLAUDE_MD_MISSING',
362
+ message: 'CLAUDE.md file not found at project root.',
363
+ action: `
364
+ ================================================================================
365
+ CLAUDE.MD IS REQUIRED
366
+ ================================================================================
367
+
368
+ The project MUST have a CLAUDE.md file at the root with these sections:
369
+
370
+ # Project Name
371
+
372
+ ## Last Change
373
+ **Branch:** branch-name
374
+ **Date:** YYYY-MM-DD
375
+ **Summary:** What was done in this session
376
+
377
+ ## 30 Seconds Overview
378
+ Quick description of what this project does.
379
+
380
+ ## Stack
381
+ | Component | Technology |
382
+ |-----------|------------|
383
+ | Runtime | Bun |
384
+ | Language | TypeScript |
385
+ | Database | MongoDB |
386
+
387
+ ## Architecture
388
+ Project structure and key directories.
389
+
390
+ CREATE this file before the task can complete.
391
+ ================================================================================`,
392
+ };
393
+ }
394
+ return null;
395
+ }
396
+
397
+ function validateClaudeMdSize(): ValidationError | null {
398
+ if (!existsSync(CLAUDE_MD_PATH)) return null;
399
+
400
+ const content = readFileSync(CLAUDE_MD_PATH, 'utf8');
401
+ if (content.length <= MAX_CHARACTERS) return null;
402
+
403
+ const excess = content.length - MAX_CHARACTERS;
404
+
405
+ return {
406
+ type: 'CLAUDE_MD_SIZE_EXCEEDED',
407
+ message: `CLAUDE.md exceeds 40,000 character limit by ${excess} characters (current: ${content.length}).`,
408
+ action: `
409
+ ================================================================================
410
+ CLAUDE.MD MUST BE COMPACTED (MAX 40,000 CHARACTERS)
411
+ ================================================================================
412
+
413
+ Current size: ${content.length} characters
414
+ Maximum allowed: ${MAX_CHARACTERS} characters
415
+ Excess: ${excess} characters
416
+
417
+ COMPACTION RULES (what to keep vs remove):
418
+
419
+ KEEP (critical):
420
+ - # Project Title
421
+ - ## Last Change (only the MOST RECENT)
422
+ - ## 30 Seconds Overview
423
+ - ## Stack
424
+ - ## Architecture
425
+ - ## Critical Rules
426
+ - ## FORBIDDEN Actions
427
+ - ## Quality Gates
428
+
429
+ REMOVE/CONDENSE:
430
+ - Verbose explanations (use bullet points)
431
+ - Duplicate information
432
+ - Old/outdated sections
433
+ - Long code examples (keep minimal)
434
+ - Multiple "Last Change" entries (keep only latest)
435
+
436
+ After editing, verify: wc -m CLAUDE.md
437
+ ================================================================================`,
438
+ };
439
+ }
440
+
441
+ function validateClaudeMdStructure(): ValidationError | null {
442
+ if (!existsSync(CLAUDE_MD_PATH)) return null;
443
+
444
+ const content = readFileSync(CLAUDE_MD_PATH, 'utf8');
445
+ const missingSections: string[] = [];
446
+
447
+ for (const section of REQUIRED_SECTIONS) {
448
+ if (!section.pattern.test(content)) {
449
+ missingSections.push(section.name);
450
+ }
451
+ }
452
+
453
+ if (missingSections.length === 0) return null;
454
+
455
+ return {
456
+ type: 'CLAUDE_MD_MISSING_SECTIONS',
457
+ message: `CLAUDE.md is missing required sections: ${missingSections.join(', ')}`,
458
+ action: `
459
+ ================================================================================
460
+ CLAUDE.MD MISSING REQUIRED SECTIONS
461
+ ================================================================================
462
+
463
+ Missing sections:
464
+ ${missingSections.map((s) => ` - ${s}`).join('\n')}
465
+
466
+ REQUIRED STRUCTURE:
467
+
468
+ # Project Name <- H1 title
469
+
470
+ ## Last Change <- ONLY the most recent change
471
+ **Branch:** feature/xxx
472
+ **Date:** YYYY-MM-DD
473
+ **Summary:** What was done
474
+
475
+ ## 30 Seconds Overview <- Quick project description
476
+
477
+ ## Stack <- Technology table
478
+
479
+ ## Architecture <- Project structure
480
+
481
+ ADD the missing sections before the task can complete.
482
+ ================================================================================`,
483
+ };
484
+ }
485
+
486
+ function validateClaudeMdLastChange(): ValidationError | null {
487
+ if (!existsSync(CLAUDE_MD_PATH)) return null;
488
+
489
+ const content = readFileSync(CLAUDE_MD_PATH, 'utf8');
490
+ const lastChangeMatch = content.match(/## Last Change\n([\s\S]*?)(?=\n## |$)/);
491
+
492
+ if (!lastChangeMatch) return null; // Covered by structure check
493
+
494
+ const lastChangeContent = lastChangeMatch[1].trim();
495
+
496
+ // Check for meaningful content
497
+ if (lastChangeContent.length < 50) {
498
+ return {
499
+ type: 'CLAUDE_MD_LAST_CHANGE_EMPTY',
500
+ message: 'The "Last Change" section exists but lacks sufficient content.',
501
+ action: `
502
+ ================================================================================
503
+ UPDATE "LAST CHANGE" SECTION
504
+ ================================================================================
505
+
506
+ The "## Last Change" section must contain:
507
+
508
+ **Branch:** the-branch-name-used
509
+ **Date:** ${new Date().toISOString().split('T')[0]}
510
+ **Summary:** 1-2 sentences describing what was changed/implemented
511
+
512
+ Example:
513
+ ## Last Change
514
+
515
+ **Branch:** feature/add-auth
516
+ **Date:** 2025-01-05
517
+ **Summary:** Implemented JWT authentication with refresh tokens and session management.
518
+
519
+ IMPORTANT: This section should ONLY contain the LAST change, not a history.
520
+ ================================================================================`,
521
+ };
522
+ }
523
+
524
+ // Check for multiple Last Change sections (stacking is forbidden)
525
+ const multipleChanges = content.match(/## Last Change/g);
526
+ if (multipleChanges && multipleChanges.length > 1) {
527
+ return {
528
+ type: 'CLAUDE_MD_STACKED_CHANGES',
529
+ message: `Found ${multipleChanges.length} "## Last Change" sections. Only ONE is allowed.`,
530
+ action: `
531
+ ================================================================================
532
+ REMOVE STACKED CHANGES - KEEP ONLY THE LATEST
533
+ ================================================================================
534
+
535
+ Rule: CLAUDE.md should only have ONE "## Last Change" section.
536
+ Previous changes belong in git history, not in the documentation.
537
+
538
+ Found ${multipleChanges.length} instances of "## Last Change".
539
+
540
+ ACTION: Remove all but the most recent "## Last Change" section.
541
+
542
+ This keeps the file focused and within the 40k character limit.
543
+ ================================================================================`,
544
+ };
545
+ }
546
+
547
+ return null;
548
+ }
549
+
550
+ function validateClaudeMdUpdated(modifiedFiles: string[]): ValidationError | null {
551
+ // If no source files modified, no need to check
552
+ const sourceFiles = modifiedFiles.filter(isSourceFile);
553
+ if (sourceFiles.length === 0) return null;
554
+
555
+ // Check if CLAUDE.md is in the modified files
556
+ const claudeMdModified = modifiedFiles.some(
557
+ (f) => f === 'CLAUDE.md' || f.endsWith('/CLAUDE.md') || f.endsWith('\\CLAUDE.md')
558
+ );
559
+
560
+ if (claudeMdModified) return null;
561
+
562
+ return {
563
+ type: 'CLAUDE_MD_NOT_UPDATED',
564
+ message: `${sourceFiles.length} source file(s) were modified but CLAUDE.md was not updated.`,
565
+ action: `
566
+ ================================================================================
567
+ UPDATE CLAUDE.MD WITH SESSION CHANGES (MANDATORY)
568
+ ================================================================================
569
+
570
+ You modified source files but did not update CLAUDE.md.
571
+
572
+ Modified source files:
573
+ ${sourceFiles
574
+ .slice(0, 10)
575
+ .map((f) => ` - ${f}`)
576
+ .join('\n')}${sourceFiles.length > 10 ? '\n ... and more' : ''}
577
+
578
+ REQUIRED UPDATES TO CLAUDE.MD:
579
+
580
+ 1. Update "## Last Change" section:
581
+ **Branch:** current-branch-name
582
+ **Date:** ${new Date().toISOString().split('T')[0]}
583
+ **Summary:** What you implemented/fixed
584
+
585
+ 2. If architecture changed:
586
+ Update "## Architecture" section
587
+
588
+ 3. If new patterns/rules were established:
589
+ Add to appropriate section
590
+
591
+ 4. If user mentioned preferences or corrections:
592
+ Add as rules in relevant section
593
+
594
+ CONTEXT SYNTHESIS:
595
+ Think about what the user asked and what you learned.
596
+ Capture important decisions and patterns for the next session.
597
+
598
+ The stop hook will BLOCK until CLAUDE.md is updated.
599
+ ================================================================================`,
600
+ };
601
+ }
602
+
603
+ function validateDocumentation(sourceFiles: string[]): ValidationError | null {
604
+ if (sourceFiles.length === 0) return null;
605
+
606
+ const undocumented: string[] = [];
607
+ for (const filePath of sourceFiles) {
608
+ if (!searchInDocs(filePath)) {
609
+ undocumented.push(filePath);
610
+ }
611
+ }
612
+
613
+ if (undocumented.length === 0) return null;
614
+
615
+ const fileList = undocumented
616
+ .slice(0, 15)
617
+ .map((f) => ` - ${f}`)
618
+ .join('\n');
619
+
620
+ return {
621
+ type: 'SOURCE_FILES_NOT_DOCUMENTED',
622
+ message: `${undocumented.length} source file(s) are not documented.`,
623
+ action: `
624
+ ================================================================================
625
+ DOCUMENT ALL MODIFIED SOURCE FILES (MANDATORY)
626
+ ================================================================================
627
+
628
+ Undocumented files:
629
+ ${fileList}${undocumented.length > 15 ? '\n ... and more' : ''}
630
+
631
+ REQUIRED ACTION:
632
+
633
+ Run the documenter agent to update documentation:
634
+
635
+ Task(subagent_type="documenter", prompt="Update documentation for all modified files")
636
+
637
+ The documenter will:
638
+ 1. Detect changed files via git diff
639
+ 2. Update domain files in .claude/skills/codebase-knowledge/domains/
640
+ 3. Update docs/ as needed
641
+ 4. Ensure all modified files are mentioned in documentation
642
+
643
+ A file is considered documented if its name appears in:
644
+ - docs/ folder
645
+ - .claude/skills/codebase-knowledge/domains/ folder
646
+
647
+ The stop hook will BLOCK until all source files are documented.
648
+ ================================================================================`,
649
+ };
650
+ }
651
+
652
+ // ============================================================================
653
+ // MAIN HOOK LOGIC
654
+ // ============================================================================
655
+
656
+ interface HookInput {
657
+ stop_hook_active?: boolean;
658
+ }
659
+
660
+ interface HookResult {
661
+ decision: 'approve' | 'block';
662
+ reason: string;
663
+ }
664
+
665
+ async function readStdinWithTimeout(timeoutMs: number): Promise<string> {
666
+ return new Promise((resolve) => {
667
+ const timeout = setTimeout(() => {
668
+ process.stdin.destroy();
669
+ resolve('{}');
670
+ }, timeoutMs);
671
+
672
+ let data = '';
673
+ process.stdin.setEncoding('utf8');
674
+ process.stdin.on('data', (chunk: string) => {
675
+ data += chunk;
676
+ });
677
+ process.stdin.on('end', () => {
678
+ clearTimeout(timeout);
679
+ resolve(data || '{}');
680
+ });
681
+ process.stdin.on('error', () => {
682
+ clearTimeout(timeout);
683
+ resolve('{}');
684
+ });
685
+
686
+ if (process.stdin.readableEnded) {
687
+ clearTimeout(timeout);
688
+ resolve('{}');
689
+ }
690
+ });
691
+ }
692
+
693
+ async function main(): Promise<void> {
694
+ let hookInput: HookInput = {};
695
+ try {
696
+ const stdin = await readStdinWithTimeout(1000);
697
+ if (stdin && stdin.trim()) {
698
+ hookInput = JSON.parse(stdin);
699
+ }
700
+ } catch {
701
+ hookInput = {};
702
+ }
703
+
704
+ // Prevent infinite loops
705
+ if (hookInput.stop_hook_active) {
706
+ const result: HookResult = {
707
+ decision: 'approve',
708
+ reason: 'Stop hook cycle detected, allowing exit',
709
+ };
710
+ console.log(JSON.stringify(result));
711
+ process.exit(0);
712
+ }
713
+
714
+ // Gather state
715
+ const currentBranch = getCurrentBranch();
716
+ const modifiedFiles = getModifiedFiles();
717
+ const sourceFiles = modifiedFiles.filter(isSourceFile);
718
+ const isMainBranch = currentBranch === 'main' || currentBranch === 'master';
719
+ const isCleanTree = modifiedFiles.length === 0;
720
+
721
+ // Run all validations
722
+ const errors: ValidationError[] = [];
723
+
724
+ // Validation order matters - most critical first
725
+ const branchError = validateBranch(currentBranch, modifiedFiles);
726
+ if (branchError) errors.push(branchError);
727
+
728
+ // Only check these if we're close to completion (on main or clean tree)
729
+ if (isMainBranch || isCleanTree) {
730
+ const treeError = validateGitTree(modifiedFiles);
731
+ if (treeError) errors.push(treeError);
732
+ }
733
+
734
+ const claudeMdExistsError = validateClaudeMdExists();
735
+ if (claudeMdExistsError) errors.push(claudeMdExistsError);
736
+
737
+ if (!claudeMdExistsError) {
738
+ const sizeError = validateClaudeMdSize();
739
+ if (sizeError) errors.push(sizeError);
740
+
741
+ const structureError = validateClaudeMdStructure();
742
+ if (structureError) errors.push(structureError);
743
+
744
+ const lastChangeError = validateClaudeMdLastChange();
745
+ if (lastChangeError) errors.push(lastChangeError);
746
+
747
+ const updatedError = validateClaudeMdUpdated(modifiedFiles);
748
+ if (updatedError) errors.push(updatedError);
749
+ }
750
+
751
+ const docError = validateDocumentation(sourceFiles);
752
+ if (docError) errors.push(docError);
753
+
754
+ // ============================================================================
755
+ // OUTPUT RESULTS
756
+ // ============================================================================
757
+
758
+ if (errors.length > 0) {
759
+ let output = `
760
+ ################################################################################
761
+ # STOP VALIDATOR - TASK COMPLETION BLOCKED #
762
+ ################################################################################
763
+
764
+ ${errors.length} validation(s) failed. You MUST fix these before the task can complete.
765
+
766
+ `;
767
+
768
+ for (let i = 0; i < errors.length; i++) {
769
+ const err = errors[i];
770
+ output += `
771
+ --------------------------------------------------------------------------------
772
+ ERROR ${i + 1}/${errors.length}: ${err.type}
773
+ --------------------------------------------------------------------------------
774
+
775
+ ${err.message}
776
+
777
+ ${err.action}
778
+ `;
779
+ }
780
+
781
+ output += `
782
+ ################################################################################
783
+ # FIX ALL ERRORS ABOVE BEFORE TASK CAN COMPLETE #
784
+ ################################################################################
785
+
786
+ SYNTHESIS REMINDER:
787
+ Before completing, ask yourself:
788
+ - Did the user mention any preferences I should remember?
789
+ - Did I learn any patterns that should be documented?
790
+ - Were there any corrections I should add as rules?
791
+
792
+ Update CLAUDE.md with any learnings from this session.
793
+ `;
794
+
795
+ const result: HookResult = { decision: 'block', reason: output.trim() };
796
+ console.log(JSON.stringify(result));
797
+ process.exit(0);
798
+ }
799
+
800
+ // All validations passed
801
+ const successOutput = `
802
+ ################################################################################
803
+ # STOP VALIDATOR - ALL CHECKS PASSED #
804
+ ################################################################################
805
+
806
+ Branch: ${currentBranch}
807
+ Tree: ${isCleanTree ? 'Clean' : `${modifiedFiles.length} modified files`}
808
+ CLAUDE.md: Valid
809
+
810
+ All validations passed. Task may complete.
811
+ ################################################################################
812
+ `;
813
+
814
+ const result: HookResult = { decision: 'approve', reason: successOutput.trim() };
815
+ console.log(JSON.stringify(result));
816
+ process.exit(0);
817
+ }
818
+
819
+ main().catch((err) => {
820
+ console.error('Hook error:', err);
821
+ // On error, allow to continue to not block user
822
+ const result: HookResult = { decision: 'approve', reason: 'Hook error, allowing by default' };
823
+ console.log(JSON.stringify(result));
824
+ process.exit(0);
825
+ });