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>
`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.
* 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.
- 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>