From 00453a71ccc2f99e197d4b988c305004b0aa863f Mon Sep 17 00:00:00 2001 From: azizur1992 Date: Fri, 17 Apr 2026 23:53:41 +0100 Subject: [PATCH] refactor(cli): trim duplicated ai-context CLAUDE.md block The block that `npx gitnexus analyze` injects between `` and `` into every project's CLAUDE.md and AGENTS.md is ~101 lines / ~5465 characters. Six sub-sections duplicate content that is already present in the installed skill files at `.claude/skills/gitnexus/*/SKILL.md`: - Tools Quick Reference -> gitnexus-guide/SKILL.md - Impact Risk Levels -> gitnexus-impact-analysis/SKILL.md - Self-Check Before Finish -> gitnexus-refactoring/SKILL.md - When Debugging step list -> gitnexus-debugging/SKILL.md - When Refactoring steps -> gitnexus-refactoring/SKILL.md - Keeping the Index Fresh -> gitnexus-cli/SKILL.md (plus the one-line freshness warning retained at the top) Both the CLAUDE.md block and every skill file are loaded into every Claude Code session, so the duplication costs tokens on every turn of every conversation in every indexed repo. This PR trims to a ~40-line / ~2580-char block that keeps only what is genuinely unique-per-repo or load-bearing for the agent before it picks a skill: KEEP: - Header + projectName + stats line - One-line freshness warning - Always Do (imperatives the agent must know up-front) - Never Do (safety constraints the agent must know up-front) - Resources table (URI patterns interpolated with projectName) - Cross-Repo Groups (conditional, unique per repo) - CLI skills routing table (the load-bearing pointer) Net reduction: 58 lines / ~2900 characters (~52% smaller). The skills routing table already tells the agent which SKILL.md to read for each task, so the trimmed sections are still reachable -- just no longer duplicated into every session. Backward compat: - `` / `` markers unchanged -> the idempotent block-replacement logic keeps working on re-runs. - Function signature, export surface, and caller contracts unchanged -- generateAIContextFiles() is called from analyze.ts and run-analyze.ts with the same arguments. - All six skill files continue to be installed unchanged; they are now the single source of truth for the trimmed content. - skipAgentsMd and noStats flags are unaffected (they operate outside the template body). - No graph/index/storage schema change; revert is a pure text rollback with no side effects. Tests: three new assertions layered on the existing ai-context test suite: - KEEP: block still contains freshness warning, Always Do, Never Do, Resources URIs, and the four skills-routing targets (impact-analysis, refactoring, debugging, cli). - TRIM: block does NOT contain the six removed headers. - BUDGET: block length < 2700 chars (catches any regression that pads back toward the original 5465 size). Verification: npx vitest run test/unit/ai-context.test.ts -> 9 pass npm run test:unit -> 3770 pass (4 pre-existing env failures unchanged: skip-git-cli.test.ts needs built dist/, git-utils.test.ts Windows worktree tmpdir) npx tsc --noEmit -> clean Closes #856 --- gitnexus/src/cli/ai-context.ts | 58 --------------------------- gitnexus/test/unit/ai-context.test.ts | 57 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/gitnexus/src/cli/ai-context.ts b/gitnexus/src/cli/ai-context.ts index ae7f984fb9..984d1432a2 100644 --- a/gitnexus/src/cli/ai-context.ts +++ b/gitnexus/src/cli/ai-context.ts @@ -101,19 +101,6 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s - When exploring unfamiliar code, use \`gitnexus_query({query: "concept"})\` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. - When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use \`gitnexus_context({name: "symbolName"})\`. -## When Debugging - -1. \`gitnexus_query({query: ""})\` — find execution flows related to the issue -2. \`gitnexus_context({name: ""})\` — see all callers, callees, and process participation -3. \`READ gitnexus://repo/${projectName}/process/{processName}\` — trace the full execution flow step by step -4. For regressions: \`gitnexus_detect_changes({scope: "compare", base_ref: "main"})\` — see what your branch changed - -## When Refactoring - -- **Renaming**: MUST use \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with \`dry_run: false\`. -- **Extracting/Splitting**: MUST run \`gitnexus_context({name: "target"})\` to see all incoming/outgoing refs, then \`gitnexus_impact({target: "target", direction: "upstream"})\` to find all external callers before moving code. -- After any refactor: run \`gitnexus_detect_changes({scope: "all"})\` to verify only expected files changed. - ## Never Do - NEVER edit a function, class, or method without first running \`gitnexus_impact\` on it. @@ -121,25 +108,6 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s - NEVER rename symbols with find-and-replace — use \`gitnexus_rename\` which understands the call graph. - NEVER commit changes without running \`gitnexus_detect_changes()\` to check affected scope. -## Tools Quick Reference - -| Tool | When to use | Command | -|------|-------------|---------| -| \`query\` | Find code by concept | \`gitnexus_query({query: "auth validation"})\` | -| \`context\` | 360-degree view of one symbol | \`gitnexus_context({name: "validateUser"})\` | -| \`impact\` | Blast radius before editing | \`gitnexus_impact({target: "X", direction: "upstream"})\` | -| \`detect_changes\` | Pre-commit scope check | \`gitnexus_detect_changes({scope: "staged"})\` | -| \`rename\` | Safe multi-file rename | \`gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})\` | -| \`cypher\` | Custom graph queries | \`gitnexus_cypher({query: "MATCH ..."})\` | - -## Impact Risk Levels - -| Depth | Meaning | Action | -|-------|---------|--------| -| d=1 | WILL BREAK — direct callers/importers | MUST update these | -| d=2 | LIKELY AFFECTED — indirect deps | Should test | -| d=3 | MAY NEED TESTING — transitive | Test if critical path | - ## Resources | Resource | Use for | @@ -149,32 +117,6 @@ This project is indexed by GitNexus as **${projectName}**${noStats ? '' : ` (${s | \`gitnexus://repo/${projectName}/processes\` | All execution flows | | \`gitnexus://repo/${projectName}/process/{name}\` | Step-by-step execution trace | -## Self-Check Before Finishing - -Before completing any code modification task, verify: -1. \`gitnexus_impact\` was run for all modified symbols -2. No HIGH/CRITICAL risk warnings were ignored -3. \`gitnexus_detect_changes()\` confirms changes match expected scope -4. All d=1 (WILL BREAK) dependents were updated - -## Keeping the Index Fresh - -After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it: - -\`\`\`bash -npx gitnexus analyze -\`\`\` - -If the index previously included embeddings, preserve them by adding \`--embeddings\`: - -\`\`\`bash -npx gitnexus analyze --embeddings -\`\`\` - -To check whether embeddings exist, inspect \`.gitnexus/meta.json\` — the \`stats.embeddings\` field shows the count (0 means no embeddings). **Running analyze without \`--embeddings\` will delete any previously generated embeddings.** - -> Claude Code users: A PostToolUse hook handles this automatically after \`git commit\` and \`git merge\`. - ${ groupNames && groupNames.length > 0 ? `## Cross-Repo Groups diff --git a/gitnexus/test/unit/ai-context.test.ts b/gitnexus/test/unit/ai-context.test.ts index 0a9f52a687..7a8ed0e9bc 100644 --- a/gitnexus/test/unit/ai-context.test.ts +++ b/gitnexus/test/unit/ai-context.test.ts @@ -45,6 +45,63 @@ describe('generateAIContextFiles', () => { expect(content).toContain('TestProject'); }); + it('keeps the load-bearing repo-specific sections in the CLAUDE.md block (#856)', async () => { + // The trimmed block must still contain everything that is genuinely + // unique per repo or load-bearing for the agent: the freshness warning, + // the Always Do / Never Do imperative lists, the Resources URI table + // (projectName-interpolated), and the skills routing table that tells + // the agent which skill file to read for each task. + const stats = { nodes: 50, edges: 100, processes: 5 }; + await generateAIContextFiles(tmpDir, storagePath, 'TestProject', stats); + + const content = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + + expect(content).toContain('If any GitNexus tool warns the index is stale'); + expect(content).toContain('## Always Do'); + expect(content).toContain('## Never Do'); + expect(content).toContain('## Resources'); + expect(content).toContain('gitnexus://repo/TestProject/context'); + expect(content).toContain('gitnexus-impact-analysis/SKILL.md'); + expect(content).toContain('gitnexus-refactoring/SKILL.md'); + expect(content).toContain('gitnexus-debugging/SKILL.md'); + expect(content).toContain('gitnexus-cli/SKILL.md'); + }); + + it('does not duplicate content that already lives in skill files (#856)', async () => { + // The six sections listed in issue #856 are redundant with the skill + // files shipped alongside the CLAUDE.md block (both are loaded into + // every Claude Code session). Their absence is the whole point of the + // trim — assert each header is gone so a future regression that pads + // the block back out fails here. + const stats = { nodes: 50, edges: 100, processes: 5 }; + await generateAIContextFiles(tmpDir, storagePath, 'TestProject', stats); + + const content = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + + expect(content).not.toContain('## Tools Quick Reference'); + expect(content).not.toContain('## Impact Risk Levels'); + expect(content).not.toContain('## Self-Check Before Finishing'); + expect(content).not.toContain('## When Debugging'); + expect(content).not.toContain('## When Refactoring'); + expect(content).not.toContain('## Keeping the Index Fresh'); + }); + + it('keeps the CLAUDE.md GitNexus block under the token-cost budget (#856)', async () => { + // The pre-trim block was ~5465 chars. After #856 it's ~2580 — about a + // 52% reduction. 2700 is a soft ceiling that still leaves headroom for + // legitimate future additions but will fail loudly if the trim is + // reverted or someone pads the block back out toward the original size. + const stats = { nodes: 50, edges: 100, processes: 5 }; + await generateAIContextFiles(tmpDir, storagePath, 'TestProject', stats); + + const content = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + const block = content.slice( + content.indexOf(''), + content.indexOf(''), + ); + expect(block.length).toBeLessThan(2700); + }); + it('handles empty stats', async () => { const stats = {}; const result = await generateAIContextFiles(tmpDir, storagePath, 'EmptyProject', stats);