mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-08 03:08:13 +00:00
13 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2dc0cc6398
|
fix(mcp): prevent sibling-clone repo ID collisions and correct generated MCP tool names (#2067) | ||
|
|
8a9b13fc3b
|
feat(cli): add .gitnexusrc config and --default-branch for analyze (#243) (#1996)
* feat(cli): add .gitnexusrc config and --default-branch for analyze (#243) Let a repo preconfigure recurring `gitnexus analyze` options via a project-local `.gitnexusrc` (JSON) plus a new `--default-branch` flag, so projects on `develop`/`master` no longer get the generated regression example rewritten to `base_ref: "main"` on every analyze run. - New `cli/analyze-config.ts`: locate/parse/validate `.gitnexusrc` (flat + nested `analyze` form, alias mapping, fail-closed on unknown keys / bad types / hidden chars), merge with CLI (CLI overrides config), and resolve the default branch (CLI > config defaultBranch/branch > auto-detected origin/HEAD > "main"). - `getDefaultBranch()` in storage/git.ts (best-effort, local-only, no network). - Thread `defaultBranch` through analyze -> run-analyze -> ai-context so the generated regression-compare example uses the configured branch, JSON-escaped; the --skills re-generation path uses the same branch. - `skipContextFiles`/`skipAiContext` alias `skipAgentsMd` (block only, does not imply skipSkills); `indexOnly` stays the stronger "skip all injection". - README + CLI help; unit tests for the config module and end-to-end wiring tests that fail if config is parsed but not threaded into analyze/context. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): harden .gitnexusrc against Markdown injection and stale base_ref (#243) Addresses the tri-review findings on PR #1996. - P1 (Markdown injection into generated AGENTS.md/CLAUDE.md): reject the backtick in validateBranchName (covers --default-branch, .gitnexusrc, and the origin/HEAD auto-detect via sanitizeDetectedBranch) and strip it at the ai-context sink (markdownSafeBranch); reject Markdown-significant chars (` * [ ] < >) in the config `name` (it lands in generated bold/code-spans), while still allowing `_ . - /`. Corrected the false "can't break the code span" comment. - P2 (configured defaultBranch silently no-ops on an up-to-date repo): on the alreadyUpToDate fast path, surgically refresh only the `base_ref:` line in AGENTS.md/CLAUDE.md (refreshBaseRefLine), preserving the rest of the block incl. --skills community rows; no-op when unchanged. - P3: gate the .gitnexusrc key lookup with Object.hasOwn so inherited keys (__proto__, constructor, …) hit the actionable "Unknown key" error. - Cleanups: strip a leading UTF-8 BOM before JSON.parse; give --default-branch CLI validation its own `default-branch-invalid` recovery hint; drop the dead `options.defaultBranch` write and the now-redundant `options?.` chaining. - Tests: backtick rejection + even-backtick generated output, 255-char branch bound, config `name` Markdown rejection, __proto__ → Unknown key, BOM, mergeAnalyzeOptions omits defaultBranch, willGenerateContext suppression, and the fast-path base_ref refresh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
f885330b34
|
fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) (#1945)
* fix(cli): steer npm 11 users away from npx install crash (#1939) Prefer global gitnexus or pnpm dlx in hooks and generated AI context, warn when npm 11.x would use the broken npx path, and document workarounds for the arborist node.target null failure mode. Co-authored-by: Cursor <cursoragent@cursor.com> * test(hooks): stage resolve-analyze-cmd.cjs for antigravity adapter; harden load checks The antigravity adapter gained a top-level require('./resolve-analyze-cmd.cjs') but stageAdapter() did not copy it, so the spawned adapter crashed with MODULE_NOT_FOUND. Three load-sensitive tests failed; four silent-path tests false-passed on empty stdout. Stage the helper alongside the other sibling helpers, and assert status===0 and no MODULE_NOT_FOUND on the four silent-path tests so a non-loading hook can never pass green again. Force a deterministic invocation mode in the stale-index test so the emitted analyze command no longer varies by CI-runner PATH. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): standardize invocation hints on gitnexus@latest; single-source CJS helper NPX_REF becomes a literal `gitnexus@latest` in resolve-invocation.ts, dropping the package.json require and the module-load throw (a malformed/absent version can no longer crash any CLI command at import). The safety this PR delivers is the install method steered to (global / pnpm dlx), not a pinned gitnexus version, and the in-repo CJS mirror already degraded to `latest` once copied outside the package. Make the two resolve-analyze-cmd.cjs copies byte-identical and add a parity test that fails on drift. The separate, version-pinned NPX_REF that setup.ts writes into the MCP server registration is intentional and left unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * perf(cli): move npm-11 npx warning off module load; memoize invocation mode warnIfNpm11NpxRisk() ran at index.ts module load, so every CLI invocation (including the `gitnexus mcp` stdio hot path) paid which/where + npm --version spawns — against the lazy-startup/MCP-stdout discipline (#207, #1383). Move the call into analyzeCommand, after the ensureHeap() re-exec guard, so it fires once in the working process and only for `analyze`. Memoize the PATH-probe-derived invocation mode (the GITNEXUS_INVOCATION override stays uncached) so repeated callers don't re-probe, and add a test-only reset so the cache + once-only warning flag don't leak across the unit suite. Covers the mode!=='npx', npm<11, and npm-absent suppression branches. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): detect .exe/extensionless global gitnexus shims on Windows The winGitnexusWrapper branch only matched .cmd/.bat, so a global gitnexus installed by Volta or scoop (a .exe or an extensionless shim) was missed and the hint fell back to pnpm/npx. Accept .exe and treat any non-empty `where` hit as on-PATH (the emitted hint is `gitnexus analyze` regardless of which shim resolves it). Mirror the change into both resolve-analyze-cmd.cjs copies so the TS source and the byte-identical hook mirrors stay in sync. Add Windows-mocked test cases (.exe-only, extensionless, .cmd preference, CRLF stripping) and register resolve-invocation.test.ts in cross-platform-tests.ts so the windows-latest runner exercises the branch. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): emit fixed pnpm dlx analyze command in generated AGENTS.md/CLAUDE.md ai-context baked a machine-resolved command (formatAnalyzeCommand) into git-tracked AGENTS.md/CLAUDE.md, so the stale-index hint varied per machine and churned across branches (the #1706 class). Emit the fixed string `pnpm dlx gitnexus@latest analyze` instead: committed AI-context is the most authoritative instruction an agent reads, so it must name an install-free, crash-free method — never `npx`, the npm-11 path #1939 steers away from. formatAnalyzeCommand stays exported and unit-tested in resolve-invocation.ts (it still mirrors the two .cjs hook copies); ai-context just no longer calls it. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(cli): unify hook-helper copy into one non-silent routine installClaudeCodeHooks copied its four hook helpers in separate try/catch blocks that silently swallowed failures, while installAntigravityHooks recorded an error per failed copy. Extract one copyHookHelpers(srcDir, destDir, label, result) with a single canonical helper list (including resolve-analyze-cmd.cjs) and the antigravity loop's error-reporting policy, and use it from both paths so a missing helper surfaces as a setup error instead of a silent runtime crash. Assert both the Claude and Antigravity install paths co-locate resolve-analyze-cmd.cjs next to the adapter, and that a failed copy records an error rather than passing silently. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(cli): reattach installClaudeCodeHooks JSDoc after helper extraction The extracted HOOK_HELPERS/copyHookHelpers block landed between the installClaudeCodeHooks JSDoc and its function, leaving the doc reading as if it described the helper list. Move the block above the doc so it documents the function again. No behavior change. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): enforce TS<->CJS invocation parity and guard CLI startup posture Tier-2 review found two in-scope gaps in the #1945 follow-up: - The "mirrors resolve-invocation.ts / test enforces parity" comments overclaimed: the parity test only compared the two .cjs copies to each other, so the TS source and the CJS hook copies could silently drift (NPX_REF, the per-mode command, and the Windows shim regex were hand-edited in all three this PR). Add TS<->CJS value parity (NPX_REF + formatAnalyzeCommand for every forced mode) and a source-level shim-regex parity check, and make the mirror comments accurately describe what is enforced. - No test locked the R3/R4 startup posture, so re-adding warnIfNpm11NpxRisk() (or any resolve-invocation import) at index.ts module scope -- the #207/#1383 lazy-startup regression -- would pass CI. Add a guard asserting index.ts has no module-load invocation probe and the warning is wired into analyzeCommand. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(cli): collapse npx-invocation resolver to one source of truth PR #1945 carried the gitnexus/pnpm/npx selection in three hand-synced places — the canonical hook helper, its byte-identical plugin copy, and a full TypeScript re-implementation in resolve-invocation.ts — kept in lockstep by per-mode-command and regex-extracted-by-regex parity tests. The TS formatAnalyzeCommand had no production caller (ai-context emits a fixed string), and the module memoized + exposed a test-only reset for a "repeated callers" case that has exactly one caller. Make hooks/claude/resolve-analyze-cmd.cjs the single source: extract the Windows-shim line-picking into a pure, exported pickPathMatch() and add an injectable probe to resolveInvocationMode() so the shipped logic is testable without spawning or global mocks. resolve-invocation.ts (118 -> 59 lines) now consumes that cjs via createRequire for resolveInvocationMode/NPX_REF and adds only the CLI-only npm-version probe and warning; the relative path resolves identically from src/cli/ (tsx, vitest) and dist/cli/ (shipped, hooks/ is a published sibling of dist/). Tests exercise the real shipped artifact, the NPX_REF/mode-command parity scaffolding is dropped (one implementation can't drift), and parity narrows to the two cjs copies staying byte-identical. No behavior change: hook stale-index hints and the analyze warning are byte-identical; the pre-existing setup.ts resolveGitnexusBin is untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): bound stale-index hook PATH probe under the hook budget (U1) The PostToolUse stale-index hint calls formatAnalyzeCommand(), which probes which/where; named PROBE_TIMEOUT_MS=2000 keeps git rev-parse (~3s) + up to two probes well under Claude Code's 10s hook timeout while preserving the machine-correct hint. Byte-identical in the plugin copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): steer generated cross-repo group commands off npx (#1939) (U2) The Cross-Repo Groups block in generated AGENTS.md/CLAUDE.md still emitted bare 'npx gitnexus group ...', funneling npm-11 users into the arborist crash; switch to fixed 'pnpm dlx gitnexus@latest group ...'. Export generateGitNexusContent and add a group-branch test asserting no 'npx gitnexus' literal survives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: align steering guidance on pnpm dlx gitnexus@latest (U3) README troubleshooting uses gitnexus@latest; the repo's own committed CLAUDE.md/AGENTS.md stale-index hint now matches the generated output (pnpm dlx gitnexus@latest analyze) so the repo dogfoods the fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(hooks): assert exact @latest analyze command and pin invocation mode (U4) Drop dead PKG_VERSION/NPX_REF version-pinned constants; the cjs always emits gitnexus@latest, so assert exact toContain(...) instead of the /@\\S+/ wildcard; pin GITNEXUS_INVOCATION in the --embeddings tests for host-independent determinism. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cli): cover resolver warn/edge branches; document probe seam (U5) Add coverage for the gitnexus-mode warn suppression, getNpmMajorVersion edge inputs (empty/pre-release/non-numeric), and the Windows non-wrapper pickPathMatch branch; widen the InvocationResolver interface to document the optional probe param. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): lower hook PATH-probe timeout to 1000ms (U1) In a linked worktree the stale-index hook runs git rev-parse --git-common-dir (~2s) + rev-parse HEAD (~3s) before up to two PATH probes; PROBE_TIMEOUT_MS=1000 holds the worst case near ~7s under Claude Code's 10s hook budget (was 2000, ~1s headroom). Byte-identical in the plugin copy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): fail closed in gitnexus setup on missing required hook helper/adapter (U2) copyHookHelpers now returns the failed REQUIRED helpers (the .cjs trio; win-rm-list-json.ps1 stays best-effort since it fails open). Both install paths skip hook registration with an actionable error when a required helper failed; the Claude path also gains the adapter-existence guard the Antigravity path already had. Prevents registering a hook that crashes MODULE_NOT_FOUND on every tool event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(skills): steer committed skill files off npx to pnpm dlx gitnexus@latest (U3) All 26 committed skill-file copies (gitnexus/skills, .claude, plugin, cursor) used 'npx gitnexus analyze', contradicting the generated freshness line and funneling npm-11 users into the arborist crash. Replace with 'pnpm dlx gitnexus@latest analyze'; add a regression guard (skills-steering.test.ts) that globs all four locations and fails if any reintroduces it. The cli skill's non-analyze npx subcommands (status/clean/list/wiki) are left as-is (out of the analyze-funnel scope). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): guard resolver import shape; assert group-impact steering (U4) Add a load-time guard on the createRequire(resolve-analyze-cmd.cjs) cast so a drifted/renamed cjs export fails loudly at module load instead of as a late TypeError in warnIfNpm11NpxRisk. Add the missing 'group impact' assertion to the ai-context Cross-Repo Groups test, and a resolver-contract test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): auto-select invocation path with pnpm --allow-build (#1939) Probe npm/pnpm versions and PATH to pick a working analyze command without user configuration: global gitnexus first, pnpm dlx with --allow-build on npm 11+ (Ladybug native scripts), npx on npm 10 and earlier. Update docs, skills, and tests to match the canonical install-free command. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): place pnpm --allow-build before dlx, repair version-injection seam (#1939) The auto-selected install command emitted `pnpm dlx --allow-build=… analyze`, but pnpm < 10.14 keeps `dlx` in its argv escape list, so flags placed *after* `dlx` are parsed as package specs and rejected (ERR_PNPM_SPEC_NOT_SUPPORTED) on pnpm 10.2–10.13.x — strictly worse than the bare command. Move the flags before `dlx` (the position pnpm has honored since 10.2.0) in both byte-identical hook copies, the committed AGENTS.md / CLAUDE.md, and every skill tree. Also repairs the CI-red resolveInvocationMode seam: injecting `{ npmMajor: null }` to simulate an absent npm fell through `??` to the host's real `npm --version` (npm 10.x on the CI runners → routed 'npx' instead of 'pnpm'). Use an `'npmMajor' in deps` sentinel so an injected null is honored, drop the dead parseMajorVersion guard, and gate the flags on pnpm >= 10.2 via a single minor-aware probeVersion spawn (skipped for committed docs). Align the TS getNpmMajorVersion timeout to the 1s hook budget and strengthen the skills-steering guard with a pre-dlx positive assertion plus a post-dlx regression check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add npm-11 pnpm caveat to README Quick Starts (#1939) The root, package, and cursor-integration README Quick Starts still steered first-contact users to bare `npx gitnexus analyze` — the exact npm 11.x arborist install crash issue #1939 names as a funnel. Add a one-line pnpm `--allow-build … dlx` caveat (keeping the simple npx default for npm<=10 / pnpm / yarn users); the package README points to its existing npm-11 workaround section. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): route every gitnexus-cli command off npx to pnpm dlx (#1939) The gitnexus-cli skill demonstrated analyze via `pnpm --allow-build … dlx` but still showed status/clean/wiki/list via bare `npx gitnexus` — the same package, the same npm-11 crash-prone install path — and its header claimed "all commands work via npx". Convert every subcommand to the pnpm form across all three skill copies and reconcile the header. Broaden the skills-steering guard to forbid any `npx gitnexus` command in the cli-skill copies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(hook): probe pnpm once on the stale-index path (#1939) The stale-index hook resolved pnpm twice — `which pnpm` for mode selection then `pnpm --version` for the allow-build gate — two spawns for one tool in a ~9s/10s budget. Capture the version once in formatAnalyzeCommand and thread it through the existing deps seam (a successful `pnpm --version` proves presence), sharing a memoized PATH probe with resolveInvocationMode. Add explicit pnpm 10.0-suppress / 10.2-emit boundary tests and relabel the unknown-minor case. Both byte-identical cjs copies updated together. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): single-quote POSIX hook command + assert cliPath patch applied (#1939) The hook `command` written into editor settings is shell-evaluated; the double-quoted `node "<path>"` form left `$`, backtick, and other metacharacters live in an adversarial $HOME. Single-quote the path on POSIX (Windows keeps the double-quoted form — those chars are illegal in Windows filenames). Also assert the cliPath source-literal replace() actually matched, recording an actionable error on drift instead of silently shipping a hook with an unresolved relative path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(setup): normalize expected hook path for the Windows runner (#1939) The new POSIX-escaping test built its expected hook path with path.join, which emits backslashes on the Windows runner, while setup.ts forward-slash- normalizes the path before quoting — so `expect(cmd).toBe(node '<path>')` mismatched on tests/windows-latest. Normalize the expected path the same way. Production code was already correct; only the test's expected value was platform-fragile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): steer docs/skills via a project-local runner, not a pnpm default (#1939) The prior approach hardcoded `pnpm --allow-build=… dlx gitnexus@latest <cmd>` into every committed skill + the generated AGENTS.md/CLAUDE.md, which assumes pnpm is installed. Replace it with a CLI-neutral project-local runner: - `gitnexus analyze` drops `.gitnexus/run.cjs` (a copy of the canonical `resolve-analyze-cmd.cjs`, which gains `buildRunnerArgv` + a `require.main` exec tail) next to the index. Docs/skills reference `node .gitnexus/run.cjs <cmd>`, which auto-selects the runner (global `gitnexus` → `pnpm dlx` → `npx`) at call time — no package-manager assumption. README first-run + an inline bootstrap note stay universal `npx gitnexus analyze`. - The exec tail uses `shell` on Windows so `.cmd`/`.ps1`/`.exe` shims resolve (execFileSync can't otherwise; Node blocks `.cmd` without a shell, CVE-2024-27980), and prints a diagnostic instead of a silent exit 1. Tests: runner exec-tail (real spawn, exit-code propagation + ENOENT diagnostic), copy-failure graceful degradation, and per-subcommand routing + pnpm-fallback vacuity guards. The generated CLAUDE.md block stays under the #856 token budget. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): resolve Windows .cmd version probes so pnpm steering fires (#1939) probeVersion (and the TS getNpmMajorVersion mirror) spawned npm/pnpm --version via execFileSync with no shell, so on Windows the .cmd shims ENOENT'd, the probe reported a present tool as absent, and the stale-index hook recommended the npx crash path #1939 exists to avoid. Add shell: process.platform === 'win32' to the version probes (the exec tail already does this). Parse the first version-shaped line so a Corepack/notice banner on stdout no longer defeats the parse. Carry pnpm presence separately from version so a present-but-unparseable pnpm still selects pnpm. Drop the dead probe ?? resolveOnPath coalesce. Cover resolve-analyze-cmd.cjs (+ plugin twin) with the shell-injection and windowsHide source-regression guards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): widen pnpm allow-build for the --embeddings=N equals form (#1945) buildRunnerArgv detected embeddings via gitnexusArgs.includes('--embeddings'), which missed the equals form (--embeddings=5000) that Commander also accepts, dropping --allow-build=onnxruntime-node on pnpm 10.2+. Match both forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cli): cover the runner exec-tail Windows shell branch on CI (#1945) runner-exec-tail.test.ts was POSIX-only and unregistered in cross-platform-tests.ts, so the run.cjs Windows shell:true exec branch ran on no platform despite the file comment claiming windows-latest covered it. Add a .cmd-shim it.skipIf(onPosix) case and register the file in SPAWN_CLI so the windows-latest job runs it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fix broken troubleshooting anchor in gitnexus README (#1945) The npm-11 quick-start note linked to #npx-gitnexus-crashes-with-nodetarget-is-null-npm-11, which matches no heading; the actual troubleshooting heading slugifies to #cannot-destructure-property-package-of-nodetarget-as-it-is-null. Repoint the link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(hooks): guard resolve-analyze-cmd.cjs in antigravity e2e sanity check (#1945) The antigravity adapter top-level require()s resolve-analyze-cmd.cjs, but the beforeAll helper-presence loop did not check for it — a failed copy would surface as noisy MODULE_NOT_FOUND in downstream tests instead of the intended actionable 'Helper not installed' error. Add it to the loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): tie a missing-runner Cannot-find-module error to recovery (#1945) Generated CLAUDE.md/AGENTS.md make `node .gitnexus/run.cjs` the primary command, but the runner is gitignored, so a fresh clone or git clean leaves an agent facing a raw MODULE_NOT_FOUND. The CLAUDE.md block is token-budget-capped (#856), so the recovery guidance lives in the cli skill (its documented home): the bootstrap note now names the `Cannot find module` error and points at `npx gitnexus analyze` to (re)generate the runner. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): disambiguate the MCP-pinned ref from the @latest hint (#1945) setup.ts and resolve-analyze-cmd.cjs both exported a constant named NPX_REF with different values (version-pinned for the persisted MCP entry vs. gitnexus@latest for hints). Rename setup.ts's module-private constant to MCP_PINNED_REF (value and behavior unchanged — the MCP pin stays pinned), leaving the cjs hint ref and its re-export alone. Also route the createRequire cast through 'unknown' so it reads as an explicit narrowing to the subset this module uses rather than a claim about the cjs's full export shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
954b184248
|
fix(cli): apply --no-stats to keep-marker stats line (#1706) (#1765)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(cli): apply --no-stats to keep-marker stats line (#1706) The keep-marker branch of upsertGitNexusSection rebuilt the index-summary line on every analyze and always re-injected the volatile counts, ignoring --no-stats. For teams that commit a trimmed AGENTS.md/CLAUDE.md with a gitnexus:keep marker, that produced recurring no-value merge conflicts — exactly what --no-stats exists to prevent. Thread noStats into upsertGitNexusSection. Under --no-stats the keep-path stats line becomes "Indexed as **<name>**" with no (N symbols, ...) parenthetical; the project name still refreshes so renames propagate. The statsPattern parenthetical is now optional so a count-free line left by a prior --no-stats run still matches. * test(cli): cover count-return and AGENTS.md parity for --no-stats keep path Addresses review findings F1 and F2 on PR #1765: - F1: add a test that counts RETURN when --no-stats is dropped after a prior count-free run — guards against the flag becoming sticky. - F2: extend the noStats+keep "drops the volatile counts" test to assert AGENTS.md alongside CLAUDE.md, so a future asymmetry between the two upsertGitNexusSection call sites is caught. --------- Co-authored-by: Emmanuel Alawode <platforms@chowbea.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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.
|
||
|
|
925460ab5b
|
refactor(cli): trim duplicated ai-context CLAUDE.md block (#904) | ||
|
|
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 |
||
|
|
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 |
||
|
|
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. |
||
|
|
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> |