Commit graph

9 commits

Author SHA1 Message Date
RezaAlmiro
a3eef48ce3
fix(cli): make --no-stats actually omit volatile counts (#1477) (#1478)
* fix(cli): make --no-stats actually omit volatile counts (#1477)

Closes #1477.

The `--no-stats` flag on `gitnexus analyze` was advertised as
"Omit volatile file/symbol counts from AGENTS.md and CLAUDE.md"
but had no effect: every reindex still rewrote the markdown with
fresh count phrases, producing chore-commit churn on every run —
the exact problem the flag was added to solve in #704.

Root cause is commander.js negation-flag semantics. `.option(
'--no-stats', ...)` registers the option under the accessor
`stats` (boolean, default `true`; `false` when the flag is passed),
NOT `noStats`. The two action-handler reads in `analyze.ts`
(lines 414 and 500 pre-fix) read `options?.noStats`, which is
always `undefined`, so the `noStats` payload always reached
`runFullAnalysis` / `generateAIContextFiles` as `undefined`/falsy
and the count branch in the template always fired.

Fixed by replacing `options?.noStats` with `options?.stats === false`
at both reads. The strict `=== false` check (rather than
`!options?.stats`) means absent options or absent `.stats` field
fall through as no-stats=false, preserving the documented default-on
behaviour. Also updated the `AnalyzeOptions` interface to declare
`stats?: boolean` (matching commander's actual output) with a
JSDoc explaining the negation, since the prior `noStats?: boolean`
shape was a static-type misrepresentation of what commander
provides at runtime.

Internal call sites that re-pack `{ noStats: ... }` for
downstream consumers (`run-analyze.ts`, `ai-context.ts`) keep
their existing field name — those interfaces are not commander-
shaped, so `noStats` is the correct name there.

## Regression tests

Two new unit tests in `test/unit/ai-context.test.ts`:

* `omits volatile counts when noStats option is set (#1477)` —
  asserts the count parenthetical is absent from both CLAUDE.md
  and AGENTS.md when `noStats: true` is passed.
* `preserves volatile counts when noStats is not set (default)` —
  documents the default-on path so a future refactor can't
  silently flip the default.

Both call `generateAIContextFiles` directly with distinctive numbers
that would unmistakably leak through if the omit branch is broken.

## Manual verification

* `vitest run test/unit/ai-context.test.ts` → 13/13 pass
  (11 prior + 2 new).
* Verified before-fix behaviour by checking out main, running
  `npx gitnexus analyze --no-stats` against an indexed repo, and
  observing the count phrase still present. Re-running on the fix
  branch with the same flag strips the phrase as documented.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(autofix): apply prettier + eslint fixes via /autofix command

* fix(cli): resolve merge conflict markers in analyze.ts (PR #1478)

Remove leftover conflict hunks from main merge; keep commander stats
shape (stats?: boolean), wire noStats: options?.stats === false into
runFullAnalysis and generateAIContextFiles, and retain indexOnly /
skipSkills / skipAgentsMd wiring from main.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(cli): cover analyzeCommand → runFullAnalysis noStats bridge (#1477)

Assert commander-shaped options.stats maps to the internal noStats
payload (including explicit true/false and skipAgentsMd combination)
so the CLI bridge cannot regress without failing tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(cli): cover AGENTS.md default stats + skills noStats bridge (#1478)

- Assert volatile stats phrase in both CLAUDE.md and AGENTS.md when noStats is omitted
- Add bridge test for --skills regeneration path with stats:false → generateAIContextFiles noStats
- Note shared noStats expression beside skills-path call; stub process.exit for full analyze path

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 08:26:27 +01:00
Dennis Palatov
6229417bd5
feat: gitnexus:keep marker preserves custom context sections (resubmit of #605) (#1508)
* feat: gitnexus:keep marker preserves custom context sections

When <!-- gitnexus:keep --> is present inside the gitnexus block,
analyze only updates the stats line instead of replacing the entire
section with the verbose template. Lets users maintain lean custom
context without it being overwritten on every reindex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: improve gitnexus:keep marker to reliably preserve custom sections

The `<!-- gitnexus:keep -->` marker inside a GitNexus block tells
`analyze` to only update the stats line (node/edge/flow counts)
while preserving the user's custom layout. This lets teams trim
the verbose default template to a lean format without having it
overwritten on every reindex.

Changes:
- Broaden stats-line regex to match both "Indexed as" and
  "indexed by GitNexus as" formats
- Improve stats extraction from generated content (prefer
  structured match over greedy parentheses)
- If keep marker is present but no stats line found, preserve
  the section as-is instead of falling through to full replace
- Add tests for keep preservation and no-keep replacement

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR #1508 review findings (F1-F5)

Refactor the keep-marker stats-update path and close the test-coverage
gaps surfaced by the production-readiness review.

## Findings 2 + 3 (high) — fragile extraction → silent corruption

Stop re-extracting `newName` (first `**bold**`) and `newStats` (first
`(...)`, with fallback) from generated content. Both are structurally
fragile:

- F2: newName silently picks the wrong value if the template ever
  emits bold text before the project-name line (no current bug; an
  unstated contract with no enforcement)
- F3: newStats fallback `\(([^)]+)\)` matches `({target: "symbolName",
  direction: "upstream"})` from the Always-Do bullet when
  `noStats: true` suppresses the canonical stats line, silently
  corrupting the stats output

Fix: pass `projectName: string` and `stats: RepoStats` directly into
`upsertGitNexusSection`. Build the stats line from those values. Both
callers in `generateAIContextFiles` already have them in scope.

## Finding 1 (high) — misleading return value

When a keep marker is present but no stats line matches the pattern,
the function previously returned `'updated'` without writing,
producing `CLAUDE.md (updated)` in CLI output for a file that was
not touched. Add a distinct `'preserved'` return variant; CLI now
reports `CLAUDE.md (preserved)` honestly.

## Finding 4 (medium) — unanchored stats regex

`/(?:Indexed as|...) \*\*[^*]+\*\* \([^)]+\)/` could match prose
embedded mid-paragraph in user content (e.g. "you'll see it Indexed
as **Foo** (note: ...)"). Anchor with `^...$` plus the `m` flag so
only standalone stats lines match.

## Finding 5 — test coverage gaps

Seven new tests, each cross-referenced to the review finding:

- keep marker OUTSIDE the GitNexus section has no effect
- AGENTS.md keep path preserves custom layout (parity with CLAUDE.md)
- idempotent: second run produces byte-identical output
- CRLF file with keep marker: stats line updates correctly
- noStats + keep marker: not corrupted by Always-Do tuple text (F3 regression guard)
- returns 'preserved' (not 'updated') when no stats line matches (F1 regression guard)
- project name with markdown punctuation (hyphens/slash/dot) lands intact

All 23 ai-context tests pass; typecheck, prettier, eslint clean.

* docs(ai-context): address PR #1508 review findings on keep-marker path

- Clarify that noStats affects generated template only, not keep-section stats updates
- Fix stats-line regex comment to match behavior (no end anchor; trailing suffix kept)
- Assert '. MCP tools.' survives stats replacement in preserve-custom-section test
- Document LF normalization when rewriting CRLF seed in keep-marker CRLF test

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: dp-web4 <dp@web4.ai>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 07:40:15 +01:00
achianuri
fdf1effb2a
feat(cli): add --skip-skills and --index-only flags to analyze (resubmit of #742) (#1485)
* feat(cli): add --skip-skills and --index-only flags to analyze command

The `installSkills()` call in `generateAIContextFiles()` runs
unconditionally, injecting 6 skill files into `.claude/skills/gitnexus/`
even when `--skip-agents-md` is passed. This is problematic for bulk
indexing operations on read-only mirrors or third-party repos.

Add two new flags:
- `--skip-skills`: suppress standard GitNexus skill file injection
- `--index-only`: pure index mode that suppresses all file injection
  (AGENTS.md, CLAUDE.md, and skills), writing only to `.gitnexus/`

This gives users three levels of control:
- `--skip-agents-md` — suppress only root context files
- `--skip-skills` — suppress only skill injection
- `--index-only` — suppress everything (pure indexing)

Discovery context: while bulk-indexing 176 repos with
`--skip-agents-md`, all 144 indexed repos were contaminated with
`.claude/skills/gitnexus/` files requiring manual cleanup.

* fix(cli): address PR #742 review — gate community skills, drop dangling refs, add tests

Bot review (#742) flagged three issues with the original commit:

1. `--index-only --skills` still wrote community-derived skill files
   to `.claude/skills/generated/`. The `--skills` branch in analyze.ts
   was not gated by `skipAll`, so the "skip all file injection" contract
   was violated. Gate `generateSkillFiles()` with `!skipAll` so
   `--index-only` truly wins over `--skills`.

2. `--skip-skills` without `--skip-agents-md` produced AGENTS.md /
   CLAUDE.md that still referenced `.claude/skills/gitnexus/*/SKILL.md`
   files that were never installed — every agent load incurred 6
   failed reads. Pass `skipSkills` through to `generateGitNexusContent()`
   and omit the standard-skill rows (and the entire `## CLI` heading
   when the table is empty). Community skills, when present via
   `--skills`, are unaffected.

3. No filesystem tests for `skipSkills` / `indexOnly`. Add three
   regression guards to `test/unit/ai-context.test.ts`:
   - `.claude/skills/gitnexus/` is NOT created when skipSkills=true
   - Nothing is written when both skipAgentsMd and skipSkills are true
     (the resolved-flag state from --index-only)
   - AGENTS.md/CLAUDE.md routing table omits standard skill references
     when skipSkills=true, but preserves the load-bearing imperative
     sections (Always Do / Never Do / Resources)

* test(cli): PR 1485 review follow-ups (help text, gate test, --skip-skills docs)

- Assert --skip-skills and --index-only in analyze --help (skip-git-cli.test.ts).

- Export shouldGenerateCommunitySkillFiles; unit-test index-only+skills gate.

- Clarify --skip-skills does not suppress --skills community files; --index-only for full skip.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): warn when --index-only silently overrides --skills

Address review findings on PR 1485 follow-ups:
- analyze.ts emits a one-line note when both --index-only and --skills
  are set, so users see why a pipeline re-index ran with no skill files
  written.
- index.ts --skills help text now flags the --index-only override.
- shouldGenerateCommunitySkillFiles JSDoc documents the dual role of
  the gate (community skills + AGENTS.md/CLAUDE.md re-generation).
- skip-git-cli.test.ts pins the override-warning surface end-to-end.

---------

Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 15:00:58 +01:00
azizur100389
e262dda35b
fix(cli): only match <!-- gitnexus:* --> markers at section position (#1041) (#1042)
`upsertGitNexusSection` in ai-context.ts uses `indexOf` to locate the
bounds of the GitNexus section in CLAUDE.md / AGENTS.md before
replacement. `indexOf` matches the first occurrence of the marker
anywhere in the file, including inline prose references in backtick-
quoted fragments mid-sentence.

The shipped CLAUDE.md contains exactly such a reference ("See the
`<!-- gitnexus:start --> … <!-- gitnexus:end -->` block in AGENTS.md
for the canonical MCP tools..."). Running `gitnexus analyze` on a
fresh install matches those inline markers as section delimiters and
replaces the prose between them with the full ~100-line injected
block, breaking the backtick and corrupting markdown for every user.

Fix: new private `findSectionMarkerIndex` helper that only matches
markers occupying their own line — preceded by `\n` or start-of-file,
followed by `\n` / `\r` (CRLF files) / end-of-file. `\r` is explicit
so CRLF-terminated sections on Windows (core.autocrlf = true) still
match. The generator always emits markers alone on their line, so
every legitimate section continues to update in place; only inline
prose references now fall through to the append branch, which leaves
existing content untouched.

Two new unit tests:
- #1041 regression — seed CLAUDE.md with the shipped inline prose
  line, run analyze twice, assert inline prose preserved verbatim
  and marker counts stay at 2/2 (1 inline + 1 section-position)
- CRLF handling — seed a CRLF file with inline prose + legitimate
  section, run analyze, assert section replaced in place, inline
  prose preserved, stale stub content removed

No destructive ops, no bypass flags, no new deps. Behaviour change
is strictly narrowing — files that previously updated correctly
still do; files that previously got corrupted now fall through to
the safer append branch.

Closes #1041.
2026-04-23 07:58:07 +01:00
azizur100389
925460ab5b
refactor(cli): trim duplicated ai-context CLAUDE.md block (#904) 2026-04-18 07:10:44 +01:00
Gabriel J Campbell
b03413dcf9
feat: added skip-agents-md cli flag (#517)
* feat: added skip-agents-md cli flag

* fix: apply prettier formatting

* feat: added skip-agents-md cli flag

* fix: apply prettier formatting

* feat: add skipAgentsMd option to skip AGENTS.md and CLAUDE.md updates

* fixed bad merge
2026-03-28 21:23:59 +00:00
Gergő Magyar
acf6fbdd39
feat: configure eslint with unused import removal (#564)
* feat: configure eslint with unused import removal

Add ESLint v9 (flat config) for code quality:
- eslint-plugin-unused-imports for auto-removing dead imports
- @typescript-eslint for TypeScript-aware linting
- eslint-plugin-react-hooks for React hooks rules
- eslint-config-prettier to avoid formatting conflicts
- lint-staged runs eslint --fix before prettier on .ts/.tsx
- CI lint job added to ci-quality.yml

* refactor: remove unused imports via eslint --fix

Auto-fixed by eslint-plugin-unused-imports. No logic changes.

* chore: add eslint fix commit to .git-blame-ignore-revs
2026-03-28 15:28:09 +00:00
Gergő Magyar
bf09eab95b
feat: configure prettier with pre-commit hook (#563)
* feat: configure prettier with pre-commit hook integration

Add prettier, lint-staged, and prettier-plugin-tailwindcss at the repo
root with husky pre-commit hook integration. Moves husky from
gitnexus/ to root package.json for reliable hook installation.

- Root package.json with prepare/format/format:check scripts
- .prettierrc with endOfLine:lf and tailwindStylesheet for TW v4
- .prettierignore excluding fixtures, vendor, generated, *.d.ts, *.md
- .gitattributes enforcing LF line endings for Windows consistency
- Pre-commit hook uses direct node_modules/.bin/ paths (no npx)

* style: apply prettier formatting to entire codebase

One-time bulk format. No logic changes.
Use .git-blame-ignore-revs to skip this commit in git blame.

* chore: add .git-blame-ignore-revs for prettier format commit

* perf: pre-commit hook runs only tests related to staged files

Use vitest --related to scope test execution to tests that import
the changed files, instead of running the full suite on every commit.

* perf: remove vitest from pre-commit hook, keep in CI only

Pre-commit now runs lint-staged + tsc only. Tests run in CI
(ci-tests.yml) where they belong — keeps commits fast.

* ci: add prettier format check to quality workflow

PRs will now fail if code isn't formatted with prettier.
2026-03-28 14:58:04 +00:00
abhigyanpatwari
8a100a76d3 test: add test suite with vitest (unit + integration + fixtures)
- 59 test files covering unit and integration tests
- vitest config with coverage thresholds and fork pooling
- Test fixtures (mini-repo + multi-language sample code)
- Add vitest + coverage-v8 to devDependencies
- Add test scripts (test, test:integration, test:all, test:watch, test:coverage)
- Move typescript to devDependencies where it belongs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:07:02 +05:30