start-vibing 2.0.44 → 2.0.46

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-vibing",
3
- "version": "2.0.44",
3
+ "version": "2.0.46",
4
4
  "description": "Setup Claude Code agents, skills, and hooks in your project. Smart copy that preserves your custom domains and configurations.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@ The stop hook validates `/CLAUDE.md` before allowing task completion:
13
13
  | Character limit | Max 40,000 chars |
14
14
  | Required sections | Last Change, 30s Overview, Stack, Architecture |
15
15
  | Last Change | Must be updated with session info (branch, date, summary) |
16
+ | Content depth | Relevant rule/flow sections must also be updated |
16
17
  | No stacking | Only ONE Last Change section (latest only) |
17
18
  | Branch | Must be on main (PR merged) |
18
19
  | Git tree | Must be clean (no uncommitted changes) |
@@ -21,6 +22,27 @@ The stop hook validates `/CLAUDE.md` before allowing task completion:
21
22
 
22
23
  **If not on main:** Complete PR workflow (commit, push, PR, merge, checkout main).
23
24
 
25
+ ### Content Depth Rule (CLAUDE_MD_SHALLOW_UPDATE)
26
+
27
+ > **"Last Change" = WHAT was done. Other sections = HOW things work NOW. Both MUST be current.**
28
+
29
+ The stop-validator categorizes changed files and maps them to CLAUDE.md sections:
30
+
31
+ | Changed Files | Must Also Update |
32
+ |---------------|-----------------|
33
+ | API/CRUD routes | Critical Rules, HTTP Requests |
34
+ | UI components | UI Architecture, Component Organization |
35
+ | Pages/layouts | Architecture, Next.js App Router Patterns |
36
+ | Styling/aesthetics | UI Architecture, Design System |
37
+ | Auth/middleware | Critical Rules, Workflow |
38
+ | Database/models | Architecture, Critical Rules |
39
+ | Config files | Stack, Configuration |
40
+ | Hooks/scripts | Workflow, Stop Hook Validations |
41
+
42
+ If ONLY "Last Change" was modified but **significant** categorized source files changed, `CLAUDE_MD_SHALLOW_UPDATE` blocks completion.
43
+
44
+ **Exempt (minor changes):** < 3 categorized files AND < 30 lines changed AND no new files = Last Change only is OK.
45
+
24
46
  ---
25
47
 
26
48
  ## System Architecture
@@ -213,45 +235,139 @@ All implementations MUST:
213
235
 
214
236
  ---
215
237
 
216
- ## Opus 4.6 Best Practices
238
+ ## Claude 4.6 Best Practices
239
+
240
+ > **Source:** [Anthropic Claude 4 Best Practices](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-4-best-practices)
241
+
242
+ ### Model Selection
243
+
244
+ | Model | API ID | Cost (In/Out) | Best For |
245
+ |-------|--------|---------------|----------|
246
+ | **Opus 4.6** | `claude-opus-4-6` | $5/$25 MTok | Orchestrators, long-horizon tasks, 128K output |
247
+ | **Sonnet 4.6** | `claude-sonnet-4-6` | $3/$15 MTok | Daily coding, teammates (5x cheaper) |
248
+ | **Haiku 4.5** | `claude-haiku-4-5-20251001` | $1/$5 MTok | Classification, simple tasks |
217
249
 
218
250
  ### Effort Levels
219
251
 
220
- | Level | Use For |
221
- |-------|---------|
222
- | `max` | Orchestrators, complex architecture (Opus only) |
223
- | `high` | Default - complex agentic tasks |
224
- | `medium` | Most workloads, balance speed/cost |
225
- | `low` | Subagents, high-volume tasks |
252
+ | Level | Behavior | Use Case |
253
+ |-------|----------|----------|
254
+ | `max` | Always thinks, no limits | Complex architecture (**Opus ONLY**) |
255
+ | `high` | Deep thinking (DEFAULT) | Agentic tasks, complex coding |
256
+ | `medium` | Moderate, may skip simple | Most workloads |
257
+ | `low` | Minimal thinking | Subagents, teammates, high-volume |
226
258
 
227
- ### Prompting Rules
259
+ **CLI:** `/model` then arrow keys to adjust effort.
228
260
 
229
- - **AVOID**: Words like "CRITICAL", "MUST", "be thorough" - causes overtriggering
230
- - **USE**: "Use this tool when it would enhance understanding"
231
- - Keep prompts minimal - Opus 4.6 overthinks with verbose instructions
232
- - Explicit instructions work better than implied expectations
261
+ ### Adaptive Thinking (Replaces budget_tokens)
233
262
 
234
- ### Model Selection
263
+ ```typescript
264
+ // CORRECT for 4.6 models
265
+ thinking: { type: "adaptive" },
266
+ output_config: { effort: "medium" }
235
267
 
236
- | Model | Use Case |
237
- |-------|----------|
238
- | **Opus 4.6** | Team leads, orchestrators, deep reasoning |
239
- | **Sonnet 4.6** | Teammates, subagents (5x cheaper, near-identical performance) |
240
- | **Haiku 4.5** | Simple classification, no thinking needed |
268
+ // DEPRECATED (will be removed)
269
+ thinking: { type: "enabled", budget_tokens: 10000 }
270
+ ```
271
+
272
+ Adaptive thinking enables **interleaved thinking** Claude reasons BETWEEN tool calls.
273
+
274
+ ### Prompting Rules (CRITICAL)
275
+
276
+ **AVOID (causes overtriggering):**
277
+ - "CRITICAL: You MUST use..."
278
+ - "be thorough", "think carefully", "do not be lazy"
279
+ - "use the think tool to plan your approach"
280
+
281
+ **USE INSTEAD:**
282
+ - "Use this tool when..." (softer language)
283
+ - Remove anti-laziness prompts entirely
284
+ - Lower effort level instead of adding constraints
241
285
 
242
- ### Interleaved Thinking
286
+ **Why:** Opus 4.6 does MORE upfront exploration than older models. Anti-laziness prompts cause runaway thinking.
243
287
 
244
- - Enabled automatically with adaptive thinking
245
- - Claude reasons between every tool call
246
- - Pass thinking blocks back unmodified in tool loops
247
- - Provides transparency into Claude's reasoning process
288
+ ### Tool Use Patterns
289
+
290
+ ```text
291
+ # Claude will only SUGGEST:
292
+ "Can you suggest some changes?"
293
+
294
+ # Claude will IMPLEMENT:
295
+ "Change this function to improve performance."
296
+ ```
297
+
298
+ For proactive action by default, add to system prompt:
299
+ ```text
300
+ By default, implement changes rather than only suggesting them.
301
+ ```
302
+
303
+ ### Subagent Control
304
+
305
+ Opus 4.6 has a **strong predilection for subagents** — may spawn them when direct grep/read is faster.
306
+
307
+ **Use subagents when:** Parallel tasks, isolated context, independent workstreams
308
+ **Work directly when:** Sequential operations, single-file edits, shared state needed
309
+
310
+ ```text
311
+ # Add if seeing excessive subagent use:
312
+ For simple tasks, sequential operations, or single-file edits, work directly.
313
+ ```
314
+
315
+ ### Avoid Over-Engineering
316
+
317
+ Opus 4.6 tends to create extra files and unnecessary abstractions.
318
+
319
+ ```text
320
+ # Add to prompt:
321
+ Avoid over-engineering. Only make changes directly requested.
322
+ Don't add features, docstrings, or error handling beyond what's asked.
323
+ ```
324
+
325
+ ### Parallel Tool Calls
326
+
327
+ ```text
328
+ # Add for maximum efficiency:
329
+ If calling multiple tools with no dependencies, make all calls in parallel.
330
+ ```
248
331
 
249
332
  ### Cost Control
250
333
 
251
- - Set `max_tokens` to 64K+ at high effort
252
- - Use `low` effort for subagents
253
- - Billed for FULL thinking tokens (not summarized output)
254
- - Thinking tokens count toward context window
334
+ | Issue | Solution |
335
+ |-------|----------|
336
+ | Runaway thinking | Lower effort (not prompt constraints) |
337
+ | `stop_reason: "max_tokens"` | Increase `max_tokens` to 64K+ |
338
+ | High costs | Use `low` effort for subagents |
339
+ | Billing confusion | Billed for FULL thinking, not visible summary |
340
+
341
+ ### Common Pitfalls
342
+
343
+ | Pitfall | Fix |
344
+ |---------|-----|
345
+ | `max` effort on Sonnet/Haiku | Error — Opus only |
346
+ | Prefilled responses | Deprecated in 4.6 |
347
+ | Multiple agents editing same file | Isolate files per agent |
348
+ | Aggressive prompts from older models | Remove "MUST", "CRITICAL" |
349
+
350
+ ### API Examples
351
+
352
+ ```typescript
353
+ // Standard agentic call
354
+ await client.messages.create({
355
+ model: "claude-opus-4-6",
356
+ max_tokens: 64000,
357
+ thinking: { type: "adaptive" },
358
+ output_config: { effort: "high" },
359
+ messages: [...]
360
+ });
361
+
362
+ // Cost-optimized subagent
363
+ await client.messages.create({
364
+ model: "claude-sonnet-4-6",
365
+ max_tokens: 16000,
366
+ thinking: { type: "adaptive" },
367
+ output_config: { effort: "low" },
368
+ messages: [...]
369
+ });
370
+ ```
255
371
 
256
372
  ---
257
373
 
@@ -54,6 +54,93 @@ const IGNORE_PATTERNS = [
54
54
 
55
55
  const DOC_EXTENSIONS = new Set(['.md', '.mdx', '.txt', '.rst']);
56
56
 
57
+ // ============================================================================
58
+ // FILE CHANGE CATEGORIES → CLAUDE.MD SECTIONS MAPPING
59
+ // ============================================================================
60
+
61
+ interface ChangeCategory {
62
+ name: string;
63
+ claudeMdSections: string[];
64
+ filePatterns: RegExp[];
65
+ }
66
+
67
+ const CHANGE_CATEGORIES: ChangeCategory[] = [
68
+ {
69
+ name: 'API/CRUD',
70
+ claudeMdSections: ['Critical Rules', 'HTTP Requests'],
71
+ filePatterns: [/\/api\//, /\/routers\//, /\/server\//, /\.route\./, /\.controller\./],
72
+ },
73
+ {
74
+ name: 'UI Components',
75
+ claudeMdSections: ['UI Architecture', 'Component Organization', 'Design System'],
76
+ filePatterns: [/\/components\//, /\/ui\//],
77
+ },
78
+ {
79
+ name: 'Pages/Layouts',
80
+ claudeMdSections: ['Next.js App Router Patterns', 'Architecture'],
81
+ filePatterns: [/\/app\/.*page\.tsx/, /\/app\/.*layout\.tsx/, /\/app\/.*loading\.tsx/],
82
+ },
83
+ {
84
+ name: 'Styling/Aesthetics',
85
+ claudeMdSections: ['UI Architecture', 'Design System', 'Design Trends'],
86
+ filePatterns: [/\.css$/, /globals\.css/, /theme/, /tailwind\.config/],
87
+ },
88
+ {
89
+ name: 'Auth/Middleware',
90
+ claudeMdSections: ['Critical Rules', 'Workflow'],
91
+ filePatterns: [/middleware/, /auth/, /session/],
92
+ },
93
+ {
94
+ name: 'Database/Models',
95
+ claudeMdSections: ['Architecture', 'Critical Rules'],
96
+ filePatterns: [/\/models\//, /\.model\./, /\/schema\//, /\.schema\./],
97
+ },
98
+ {
99
+ name: 'Configuration',
100
+ claudeMdSections: ['Stack', 'Configuration'],
101
+ filePatterns: [/\.config\./, /next\.config/, /tsconfig/],
102
+ },
103
+ {
104
+ name: 'Testing',
105
+ claudeMdSections: ['Quality Gates'],
106
+ filePatterns: [/\.test\./, /\.spec\./, /playwright/, /vitest/],
107
+ },
108
+ {
109
+ name: 'Workflow/Hooks',
110
+ claudeMdSections: ['Workflow', 'Stop Hook Validations'],
111
+ filePatterns: [/\.claude\/hooks\//, /\.claude\/scripts\//, /\.husky\//],
112
+ },
113
+ ];
114
+
115
+ function categorizeChangedFiles(
116
+ files: string[]
117
+ ): Array<{ category: string; sections: string[]; files: string[] }> {
118
+ const result = new Map<string, { sections: string[]; files: string[] }>();
119
+
120
+ for (const file of files) {
121
+ const normalizedFile = file.replace(/\\/g, '/');
122
+ for (const cat of CHANGE_CATEGORIES) {
123
+ for (const pattern of cat.filePatterns) {
124
+ if (pattern.test(normalizedFile)) {
125
+ if (!result.has(cat.name)) {
126
+ result.set(cat.name, { sections: [...cat.claudeMdSections], files: [] });
127
+ }
128
+ const entry = result.get(cat.name)!;
129
+ if (!entry.files.includes(file)) {
130
+ entry.files.push(file);
131
+ }
132
+ break;
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ return Array.from(result.entries()).map(([category, data]) => ({
139
+ category,
140
+ ...data,
141
+ }));
142
+ }
143
+
57
144
  const SOURCE_EXTENSIONS = new Set([
58
145
  '.ts',
59
146
  '.tsx',
@@ -603,7 +690,8 @@ IMPORTANT: This section should ONLY contain the LAST change, not a history.
603
690
  }
604
691
 
605
692
  // Check for multiple Last Change sections (stacking is forbidden)
606
- const multipleChanges = content.match(/## Last Change/g);
693
+ // Use negative lookbehind to avoid matching ### Last Change (H3 subheadings)
694
+ const multipleChanges = content.match(/(?<!#)## Last Change/g);
607
695
  if (multipleChanges && multipleChanges.length > 1) {
608
696
  return {
609
697
  type: 'CLAUDE_MD_STACKED_CHANGES',
@@ -628,8 +716,10 @@ This keeps the file focused and within the 40k character limit.
628
716
  return null;
629
717
  }
630
718
 
631
- function validateClaudeMdUpdated(modifiedFiles: string[]): ValidationError | null {
632
- // Files that don't require CLAUDE.md update (auto-generated, locks, etc)
719
+ /**
720
+ * Helper: Get files exempt from CLAUDE.md update requirement
721
+ */
722
+ function getSignificantFiles(modifiedFiles: string[]): string[] {
633
723
  const EXEMPT_PATTERNS = [
634
724
  'bun.lockb',
635
725
  'package-lock.json',
@@ -641,13 +731,10 @@ function validateClaudeMdUpdated(modifiedFiles: string[]): ValidationError | nul
641
731
  /^packages\/start-vibing\/template\//,
642
732
  ];
643
733
 
644
- // Filter out exempt files
645
- const significantFiles = modifiedFiles.filter((f) => {
646
- // Always exempt CLAUDE.md itself
734
+ return modifiedFiles.filter((f) => {
647
735
  if (f === 'CLAUDE.md' || f.endsWith('/CLAUDE.md') || f.endsWith('\\CLAUDE.md')) {
648
736
  return false;
649
737
  }
650
- // Check exempt patterns
651
738
  for (const pattern of EXEMPT_PATTERNS) {
652
739
  if (typeof pattern === 'string') {
653
740
  if (f === pattern || f.includes(pattern)) return false;
@@ -657,17 +744,39 @@ function validateClaudeMdUpdated(modifiedFiles: string[]): ValidationError | nul
657
744
  }
658
745
  return true;
659
746
  });
747
+ }
748
+
749
+ /**
750
+ * Helper: Build categorized section guidance for error messages
751
+ */
752
+ function buildSectionGuidance(significantFiles: string[]): string {
753
+ const categories = categorizeChangedFiles(significantFiles);
754
+ if (categories.length === 0) return '';
755
+
756
+ const lines = categories.map((c) => {
757
+ const uniqueSections = [...new Set(c.sections)];
758
+ return ` - ${c.category} (${c.files.length} file${c.files.length > 1 ? 's' : ''}) → Update sections: ${uniqueSections.join(', ')}`;
759
+ });
760
+
761
+ return `
762
+ DETECTED CHANGE CATEGORIES (update these CLAUDE.md sections):
763
+
764
+ ${lines.join('\n')}
765
+ `;
766
+ }
660
767
 
661
- // If no significant files modified, no need to check
768
+ function validateClaudeMdUpdated(modifiedFiles: string[]): ValidationError | null {
769
+ const significantFiles = getSignificantFiles(modifiedFiles);
662
770
  if (significantFiles.length === 0) return null;
663
771
 
664
- // Check if CLAUDE.md is in the modified files
665
772
  const claudeMdModified = modifiedFiles.some(
666
773
  (f) => f === 'CLAUDE.md' || f.endsWith('/CLAUDE.md') || f.endsWith('\\CLAUDE.md')
667
774
  );
668
775
 
669
776
  if (claudeMdModified) return null;
670
777
 
778
+ const sectionGuidance = buildSectionGuidance(significantFiles);
779
+
671
780
  return {
672
781
  type: 'CLAUDE_MD_NOT_UPDATED',
673
782
  message: `${significantFiles.length} file(s) were modified but CLAUDE.md was not updated.`,
@@ -683,7 +792,7 @@ ${significantFiles
683
792
  .slice(0, 10)
684
793
  .map((f) => ` - ${f}`)
685
794
  .join('\n')}${significantFiles.length > 10 ? '\n ... and more' : ''}
686
-
795
+ ${sectionGuidance}
687
796
  REQUIRED UPDATES TO CLAUDE.MD:
688
797
 
689
798
  1. Update "## Last Change" section:
@@ -691,24 +800,315 @@ REQUIRED UPDATES TO CLAUDE.MD:
691
800
  **Date:** ${new Date().toISOString().split('T')[0]}
692
801
  **Summary:** What you implemented/fixed
693
802
 
694
- 2. If architecture changed:
695
- Update "## Architecture" section
803
+ 2. UPDATE RELEVANT RULE/FLOW SECTIONS (NOT just Last Change!):
804
+ - Changed API/CRUD logic? → Update "Critical Rules" or "HTTP Requests"
805
+ - Changed UI components? → Update "UI Architecture" or "Component Organization"
806
+ - Changed page structure? → Update "Architecture" or "Next.js App Router Patterns"
807
+ - Changed aesthetic rules? → Update "Design System" or "UI Architecture"
808
+ - Changed auth/middleware? → Update "Critical Rules" or "Workflow"
809
+ - Changed config? → Update "Stack" or "Configuration"
810
+ - Changed testing? → Update "Quality Gates"
811
+
812
+ 3. CONTEXT SYNTHESIS:
813
+ Think about what the user asked and what you learned.
814
+ If a rule changed, document the NEW rule in the relevant section.
815
+ If a flow changed, document the NEW flow.
816
+ CLAUDE.md is the single source of truth for ALL project rules.
817
+
818
+ The stop hook will BLOCK until CLAUDE.md is updated with ALL relevant sections.
819
+ ================================================================================`,
820
+ };
821
+ }
822
+
823
+ // ============================================================================
824
+ // CHANGE RELEVANCE ASSESSMENT
825
+ // ============================================================================
826
+
827
+ /**
828
+ * Minimum thresholds for changes to be considered "relevant enough" to require
829
+ * updating CLAUDE.md sections beyond "Last Change".
830
+ *
831
+ * Minor changes (small fixes, typos, internal refactors) that don't affect
832
+ * project rules, flows, or patterns should NOT require deep section updates.
833
+ */
834
+ const RELEVANCE_THRESHOLDS = {
835
+ /** Minimum total lines added+removed across categorized files to require section updates */
836
+ minLinesChanged: 30,
837
+ /** Minimum number of categorized files changed to require section updates */
838
+ minFilesChanged: 3,
839
+ /** If ANY new files were created in categorized areas, always require section updates */
840
+ newFilesAlwaysRelevant: true,
841
+ };
842
+
843
+ /**
844
+ * Assess whether the changes in categorized files are significant enough
845
+ * to require updating CLAUDE.md sections beyond "Last Change".
846
+ *
847
+ * Returns true if changes are relevant (should require deep updates).
848
+ * Returns false if changes are minor (Last Change only is acceptable).
849
+ */
850
+ function areChangesRelevantForClaudeMd(
851
+ categories: Array<{ category: string; sections: string[]; files: string[] }>
852
+ ): boolean {
853
+ const totalCategorizedFiles = categories.reduce((sum, c) => sum + c.files.length, 0);
696
854
 
697
- 3. If new patterns/rules were established:
698
- Add to appropriate section
855
+ // Check for new files (new files = new patterns/features = always relevant)
856
+ if (RELEVANCE_THRESHOLDS.newFilesAlwaysRelevant) {
857
+ try {
858
+ const untrackedRaw = execSync('git ls-files --others --exclude-standard', {
859
+ cwd: PROJECT_DIR,
860
+ encoding: 'utf8',
861
+ stdio: ['pipe', 'pipe', 'pipe'],
862
+ }).trim();
863
+ const newInLastCommitRaw = execSync('git diff --name-only --diff-filter=A HEAD~1 HEAD 2>/dev/null || echo ""', {
864
+ cwd: PROJECT_DIR,
865
+ encoding: 'utf8',
866
+ stdio: ['pipe', 'pipe', 'pipe'],
867
+ }).trim();
868
+
869
+ const newFiles = [...untrackedRaw.split('\n'), ...newInLastCommitRaw.split('\n')].filter(Boolean);
870
+ const allCategorizedFiles = categories.flatMap((c) => c.files);
871
+ const newCategorizedFiles = newFiles.filter((f) =>
872
+ allCategorizedFiles.some((cf) => f.includes(cf) || cf.includes(f))
873
+ );
874
+
875
+ if (newCategorizedFiles.length > 0) {
876
+ return true; // New files in categorized areas = always relevant
877
+ }
878
+ } catch {
879
+ // Can't determine, continue with other checks
880
+ }
881
+ }
699
882
 
700
- 4. If user mentioned preferences or corrections:
701
- Add as rules in relevant section
883
+ // Check minimum files threshold
884
+ if (totalCategorizedFiles < RELEVANCE_THRESHOLDS.minFilesChanged) {
885
+ // Few files changed - check if the diff is substantial
886
+ try {
887
+ const allCategorizedFiles = categories.flatMap((c) => c.files);
888
+ let totalLinesChanged = 0;
889
+
890
+ // Try to get numstat from various diff sources
891
+ const numstatCommands = [
892
+ 'git diff --numstat',
893
+ 'git diff --cached --numstat',
894
+ 'git diff --numstat HEAD~1 HEAD',
895
+ ];
896
+
897
+ for (const cmd of numstatCommands) {
898
+ try {
899
+ const numstat = execSync(cmd, {
900
+ cwd: PROJECT_DIR,
901
+ encoding: 'utf8',
902
+ stdio: ['pipe', 'pipe', 'pipe'],
903
+ }).trim();
904
+
905
+ if (!numstat) continue;
906
+
907
+ for (const line of numstat.split('\n')) {
908
+ const parts = line.split('\t');
909
+ if (parts.length < 3) continue;
910
+ const added = parseInt(parts[0] || '0', 10) || 0;
911
+ const removed = parseInt(parts[1] || '0', 10) || 0;
912
+ const file = parts[2] || '';
913
+
914
+ // Only count lines in categorized files
915
+ if (allCategorizedFiles.some((cf) => file.includes(cf) || cf.includes(file))) {
916
+ totalLinesChanged += added + removed;
917
+ }
918
+ }
919
+
920
+ if (totalLinesChanged > 0) break;
921
+ } catch {
922
+ continue;
923
+ }
924
+ }
925
+
926
+ if (totalLinesChanged < RELEVANCE_THRESHOLDS.minLinesChanged) {
927
+ return false; // Small diff + few files = minor change, Last Change is enough
928
+ }
929
+ } catch {
930
+ // Can't determine line count, fall through to relevant
931
+ }
932
+ }
702
933
 
703
- CONTEXT SYNTHESIS:
704
- Think about what the user asked and what you learned.
705
- Capture important decisions and patterns for the next session.
934
+ return true; // Meets thresholds = relevant, require section updates
935
+ }
706
936
 
707
- The stop hook will BLOCK until CLAUDE.md is updated.
937
+ /**
938
+ * Content depth validation: Checks if CLAUDE.md was updated but only the
939
+ * "Last Change" section was modified (shallow update).
940
+ *
941
+ * ALLOWS shallow updates when changes are minor:
942
+ * - Few files changed (< 3 categorized files)
943
+ * - Small diff (< 30 lines total in categorized files)
944
+ * - No new files created in categorized areas
945
+ *
946
+ * BLOCKS shallow updates when changes are significant:
947
+ * - Many categorized files changed
948
+ * - Large diffs that likely introduce new rules/flows
949
+ * - New files created (new patterns/features)
950
+ */
951
+ function validateClaudeMdContentDepth(modifiedFiles: string[]): ValidationError | null {
952
+ const significantFiles = getSignificantFiles(modifiedFiles);
953
+ if (significantFiles.length === 0) return null;
954
+
955
+ // Only run if CLAUDE.md WAS modified (otherwise validateClaudeMdUpdated handles it)
956
+ const claudeMdModified = modifiedFiles.some(
957
+ (f) => f === 'CLAUDE.md' || f.endsWith('/CLAUDE.md') || f.endsWith('\\CLAUDE.md')
958
+ );
959
+ if (!claudeMdModified) return null;
960
+
961
+ // Categorize the source changes
962
+ const categories = categorizeChangedFiles(significantFiles);
963
+ if (categories.length === 0) return null; // No categorizable changes, Last Change is enough
964
+
965
+ // RELEVANCE CHECK: Skip if changes are minor/not relevant enough
966
+ if (!areChangesRelevantForClaudeMd(categories)) {
967
+ return null; // Minor changes - Last Change only is acceptable
968
+ }
969
+
970
+ // Try to detect if only "Last Change" section was modified in CLAUDE.md
971
+ // Check git diff of CLAUDE.md (staged, unstaged, or last commit)
972
+ let claudeMdDiff = '';
973
+ const diffCommands = [
974
+ 'git diff -- CLAUDE.md',
975
+ 'git diff --cached -- CLAUDE.md',
976
+ 'git diff HEAD~1 HEAD -- CLAUDE.md',
977
+ ];
978
+
979
+ for (const cmd of diffCommands) {
980
+ try {
981
+ const result = execSync(cmd, {
982
+ cwd: PROJECT_DIR,
983
+ encoding: 'utf8',
984
+ stdio: ['pipe', 'pipe', 'pipe'],
985
+ }).trim();
986
+ if (result) {
987
+ claudeMdDiff = result;
988
+ break;
989
+ }
990
+ } catch {
991
+ continue;
992
+ }
993
+ }
994
+
995
+ if (!claudeMdDiff) return null; // Can't determine diff, skip this check
996
+
997
+ // Parse the diff to detect which sections were modified
998
+ const modifiedSections = parseClaudeMdDiffSections(claudeMdDiff);
999
+
1000
+ // Check if ONLY "Last Change" was modified
1001
+ const onlyLastChange =
1002
+ modifiedSections.size > 0 &&
1003
+ modifiedSections.size === 1 &&
1004
+ modifiedSections.has('Last Change');
1005
+
1006
+ if (!onlyLastChange) return null; // Other sections were also modified, good
1007
+
1008
+ // Build specific guidance for what sections need updating
1009
+ const totalFiles = categories.reduce((sum, c) => sum + c.files.length, 0);
1010
+ const sectionGuidance = categories
1011
+ .map((c) => {
1012
+ const uniqueSections = [...new Set(c.sections)];
1013
+ const fileExamples = c.files.slice(0, 3).join(', ');
1014
+ return ` - ${c.category}: ${fileExamples}\n → Must review/update: ${uniqueSections.join(', ')}`;
1015
+ })
1016
+ .join('\n');
1017
+
1018
+ return {
1019
+ type: 'CLAUDE_MD_SHALLOW_UPDATE',
1020
+ message: `CLAUDE.md was updated but ONLY "Last Change" was modified. ${categories.length} category(ies) with ${totalFiles} file(s) require updating additional rule/flow sections.`,
1021
+ action: `
1022
+ ================================================================================
1023
+ CLAUDE.MD SHALLOW UPDATE DETECTED - MUST UPDATE RELEVANT SECTIONS
1024
+ ================================================================================
1025
+
1026
+ You updated CLAUDE.md but ONLY modified the "Last Change" section.
1027
+ The changes are significant enough to require updating rule/flow sections.
1028
+
1029
+ (Minor changes with < ${RELEVANCE_THRESHOLDS.minFilesChanged} files and < ${RELEVANCE_THRESHOLDS.minLinesChanged} lines are exempt from this check.)
1030
+
1031
+ CHANGES DETECTED THAT REQUIRE SECTION UPDATES:
1032
+
1033
+ ${sectionGuidance}
1034
+
1035
+ --------------------------------------------------------------------------------
1036
+ WHAT TO DO
1037
+ --------------------------------------------------------------------------------
1038
+
1039
+ For EACH category above, review the corresponding CLAUDE.md section and:
1040
+
1041
+ 1. If a RULE changed (e.g., CRUD validation, auth flow):
1042
+ → UPDATE the rule in its section (Critical Rules, Workflow, etc.)
1043
+
1044
+ 2. If an AESTHETIC/UI pattern changed (e.g., new component style):
1045
+ → UPDATE UI Architecture, Design System, or Component Organization
1046
+
1047
+ 3. If a FLOW changed (e.g., new middleware, data fetching pattern):
1048
+ → UPDATE Workflow, Architecture, or Next.js App Router Patterns
1049
+
1050
+ 4. If NOTHING changed in rules/flows (just implementation details):
1051
+ → This should not happen for significant changes. Review again.
1052
+
1053
+ RULE: "Last Change" documents WHAT was done.
1054
+ Other sections document HOW things work NOW.
1055
+ Both must be current for significant changes.
1056
+
1057
+ The stop hook will BLOCK until relevant sections are also updated.
708
1058
  ================================================================================`,
709
1059
  };
710
1060
  }
711
1061
 
1062
+ /**
1063
+ * Parse a git diff of CLAUDE.md to determine which ## sections were modified.
1064
+ * Returns a Set of section names that had actual content changes.
1065
+ */
1066
+ function parseClaudeMdDiffSections(diff: string): Set<string> {
1067
+ const modifiedSections = new Set<string>();
1068
+ const lines = diff.split('\n');
1069
+ let currentSection = '';
1070
+
1071
+ for (const line of lines) {
1072
+ // Skip diff metadata
1073
+ if (
1074
+ line.startsWith('diff ') ||
1075
+ line.startsWith('index ') ||
1076
+ line.startsWith('--- ') ||
1077
+ line.startsWith('+++ ')
1078
+ ) {
1079
+ continue;
1080
+ }
1081
+
1082
+ // Parse @@ hunk headers - they sometimes contain section context
1083
+ const hunkMatch = line.match(/^@@ .+ @@\s*(## .+)?/);
1084
+ if (hunkMatch && hunkMatch[1]) {
1085
+ currentSection = hunkMatch[1].replace('## ', '').trim();
1086
+ continue;
1087
+ }
1088
+ if (line.startsWith('@@')) continue;
1089
+
1090
+ // Detect section headers in changed or context lines
1091
+ const sectionMatch = line.match(/^[+ -]?## (.+)/);
1092
+ if (sectionMatch) {
1093
+ currentSection = sectionMatch[1].trim();
1094
+ // If the section header itself is a change, count it
1095
+ if (line.startsWith('+') || line.startsWith('-')) {
1096
+ modifiedSections.add(currentSection);
1097
+ }
1098
+ continue;
1099
+ }
1100
+
1101
+ // Track actual content changes (+ or - lines, not context)
1102
+ if (line.startsWith('+') || line.startsWith('-')) {
1103
+ if (currentSection) {
1104
+ modifiedSections.add(currentSection);
1105
+ }
1106
+ }
1107
+ }
1108
+
1109
+ return modifiedSections;
1110
+ }
1111
+
712
1112
  function validateDocumentation(sourceFiles: string[]): ValidationError | null {
713
1113
  if (sourceFiles.length === 0) return null;
714
1114
 
@@ -996,7 +1396,13 @@ Validate with: wc -m CLAUDE.md (must be < 40000)`,
996
1396
  },
997
1397
  CLAUDE_MD_NOT_UPDATED: {
998
1398
  agent: 'documenter',
999
- prompt: 'Update CLAUDE.md Last Change section with current session summary',
1399
+ prompt:
1400
+ 'Update CLAUDE.md: Last Change section AND all relevant rule/flow sections based on what files were changed (API rules, UI rules, workflow, architecture, etc.)',
1401
+ },
1402
+ CLAUDE_MD_SHALLOW_UPDATE: {
1403
+ agent: 'documenter',
1404
+ prompt:
1405
+ 'CLAUDE.md was updated but only Last Change was modified. Review changed source files, categorize them, and update the corresponding CLAUDE.md sections (Critical Rules, UI Architecture, Workflow, etc.) to reflect current rules and flows.',
1000
1406
  },
1001
1407
  SOURCE_FILES_NOT_DOCUMENTED: {
1002
1408
  agent: 'documenter',
@@ -1087,11 +1493,15 @@ function collectAllErrors(
1087
1493
  const updatedError = validateClaudeMdUpdated(modifiedFiles);
1088
1494
  if (updatedError) errors.push(updatedError);
1089
1495
 
1090
- // 9. Source files must be documented
1496
+ // 9. CLAUDE.md must have relevant sections updated (not just Last Change)
1497
+ const contentDepthError = validateClaudeMdContentDepth(modifiedFiles);
1498
+ if (contentDepthError) errors.push(contentDepthError);
1499
+
1500
+ // 11. Source files must be documented
1091
1501
  const docError = validateDocumentation(sourceFiles);
1092
1502
  if (docError) errors.push(docError);
1093
1503
 
1094
- // 10. Domain documentation must be complete
1504
+ // 12. Domain documentation must be complete
1095
1505
  const domainDocError = validateDomainDocumentation(modifiedFiles);
1096
1506
  if (domainDocError) errors.push(domainDocError);
1097
1507
 
@@ -75,16 +75,21 @@ async function main(): Promise<void> {
75
75
 
76
76
  5. COMMIT using conventional commits via commit-manager agent.
77
77
 
78
- 6. UPDATE CLAUDE.md BEFORE finishing (MANDATORY):
78
+ 6. UPDATE CLAUDE.md BEFORE finishing (MANDATORY - NOT JUST "Last Change"!):
79
79
  a. "## Last Change" section (date: ${today}, branch, summary). Keep only latest.
80
- b. Architecture changes: Update Architecture section with new patterns, file locations.
81
- c. Business rules: Add new rules to relevant sections (not just summary).
82
- d. UI patterns: Document new components, design decisions, aesthetic rules.
83
- e. Workflows: Update Workflow section if process changed.
84
- f. Gotchas: Add to FORBIDDEN or NRY sections if discovered.
85
- g. Config/Stack: Update if tooling changed.
86
-
87
- CLAUDE.md is the SINGLE SOURCE OF TRUTH. Document everything that affects future sessions.
80
+ b. THEN update ALL sections affected by your changes:
81
+ - Changed API/CRUD rules? Update "Critical Rules" or "HTTP Requests"
82
+ - Changed UI components/aesthetics? Update "UI Architecture", "Design System", "Component Organization"
83
+ - Changed pages/layouts? → Update "Architecture" or "Next.js App Router Patterns"
84
+ - Changed auth/middleware? Update "Critical Rules" or "Workflow"
85
+ - Changed config/stack? Update "Stack" or "Configuration"
86
+ - Changed testing patterns? → Update "Quality Gates"
87
+ - Changed workflow/hooks? Update "Workflow" or "Stop Hook Validations"
88
+ - New gotchas discovered? → Add to "FORBIDDEN" or "NRY"
89
+
90
+ RULE: "Last Change" = WHAT was done. Other sections = HOW things work NOW.
91
+ If you ONLY update Last Change, the stop-validator will BLOCK with CLAUDE_MD_SHALLOW_UPDATE.
92
+ CLAUDE.md is the SINGLE SOURCE OF TRUTH for ALL project rules and flows.
88
93
 
89
94
  7. RUN stop-validator before finishing: npx tsx .claude/hooks/stop-validator.ts`;
90
95