mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
9 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ebbcd5b0b4
|
fix(mcp): name the indexed ref on hot read tool staleness (#3291) (#3293) | ||
|
|
6e42040070
|
docs: fix bundled skill reference drift (#2362)
* docs: fix skill reference drift * docs: complete guide tool coverage and graph schema (#2356 items 5-6) - add the 6 undocumented MCP tools to the guide's Tools Reference (route_map, shape_check, api_impact, tool_map, group_list, group_sync) - document the experimental @groupName cross-repo trace mode - expand the Graph Schema section to the real node/edge type surface, pointing at gitnexus://repo/{name}/schema as the authoritative list - sync the packaged gitnexus/skills copy Item 7 of #2356 (Codex host naming / duplicated filename) does not reproduce on current main - no remaining copy contains it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4c73b18387
|
feat(mcp): add trace tool for shortest call path between symbols (#2173)
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
* feat(mcp): add trace tool for shortest call path between symbols (#1821) Implement the \ race\ MCP tool and \gitnexus trace\ CLI command that finds the shortest directed call path between two symbols using BFS over CALLS + HAS_METHOD edges. - MCP tool definition in tools.ts with READ_ONLY annotations - Directed BFS in local-backend.ts with parent-map path reconstruction - Symbol resolution via resolveSymbolCandidates (name/UID/file-hint) - Gap reporting with furthest reachable node and depth tracking - CLI wiring: gitnexus trace <from> <to> [--from-uid] [--to-uid] [--depth] - i18n keys in en.ts and zh-CN.ts + help-i18n.ts registration - ARCHITECTURE.md tools table entry - 16 unit tests (11 BFS core + 5 CLI wiring) * test(mcp): account for trace tool in tools.test.ts count The trace tool makes GITNEXUS_TOOLS length 15; update the hardcoded count, add 'trace' to the expected-names list, and refresh the stale "13 tools" comment and it() title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): sanitize trace maxDepth to reject 0/NaN/negative `Math.min(params.maxDepth ?? 10, 30)` had no lower bound and `??` does not recover 0 or NaN, so `--depth 0|-5|abc` made the BFS loop run zero iterations and return a false `no_path`. Clamp at the real boundary with a `Number.isInteger && > 0` guard (the MCP inputSchema minimum is advisory only), and reject a non-numeric `--depth` in the CLI up front rather than forwarding NaN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): check trace target before applying test-file filter The `isTestFilePath` filter ran before the target-equality check, but resolveSymbolCandidates does not exclude test-file symbols. A target (or a required hop) that lives in a test file was therefore skipped under the default includeTests=false and produced a false no_path with a misleading dynamic-dispatch suggestion. Match the explicitly-requested target first; non-target test-file nodes are still filtered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(mcp): bound trace BFS with per-level LIMIT and visited cap The per-level query had no LIMIT and the visited set was uncapped, so a high-fanout hub could materialize an unbounded frontier. Cap per-level rows (interpolated LIMIT — Kuzu does not bind LIMIT) and the total visited set; either cap sets a `truncated` flag so a resulting no_path reports that the search was cut short rather than implying the graph was exhausted. Note: the sibling impact BFS shares the same unbounded pattern; applying the cap there is deferred (out of scope for this PR). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(mcp): clarify trace traverses call + class-member edges trace was advertised as a "shortest call path" but also traverses HAS_METHOD (class→member) containment edges so a class-rooted trace can descend into its methods. Keep that capability (consistent with impact/ context) and make the docs honest: rename EDGE_TYPES→TRAVERSAL_EDGE_TYPES, state the call + class-member traversal in the MCP/CLI/i18n/ARCHITECTURE descriptions, and note each hop's edge type is reported in edges[]. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): set status:'error' on trace failure responses Every trace return path sets a `status` discriminator except the caught-error path, so a consumer switching on `result.status` saw undefined on failure. Add status:'error' to both the backend trace() catch and the CLI traceCommand catch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): return a friendly error for non-string trace from/to A non-string from/to reaching resolveSymbolCandidates surfaced a low-level "x.includes is not a function" via name.includes. Guard the four name/uid params at the top of _traceImpl and return a structured status:'error' with a clear message instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(mcp): single row-decode + drop dead field in trace BFS Decode each BFS row once into named locals instead of repeating `(row.x ?? row[N])` across the two parent.set calls and the furthest-tracking. Drop the `type` field from the parent map value (it was written but never read), and rename the internal `deepestInfo` to `lastReached` for accuracy (the output field `furthest` is unchanged). Pure refactor — no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): dedicated trace includeTests i18n key + guard coverage `trace|--include-tests` reused the impact help key, so rewording the impact option would silently change trace's help text. Add a dedicated help.option.trace.includeTests key in en + zh-CN and repoint it. Add CLI coverage for the (already symmetric) --from-uid/--to-uid flag-value guard and for --include-tests forwarding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(mcp): faithful BFS mock + expand trace coverage Fix makeResolveMock: concatenate neighbors across ALL frontier ids (it returned only the first node's, so a multi-node frontier was unmodelled) and key the UID branch on params.uid (the old query-text match never fired). Add coverage: shortest path through the second frontier node (proves the mock fix), confidence floor fallback, HAS_METHOD traversal with a mixed edge-type chain, no_path furthest:null, and from_file disambiguation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * style(trace): apply root prettier formatting to trace files The root `quality / format` gate (prettier --check, printWidth 100) runs on the full repo and flagged the trace sources/tests (the local config masks it). Reformat to root style — no behavior change; trace + tools suites and tsc stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): document the trace tool for AI agents Add `trace` to the GitNexus skill docs so agents reach for it instead of hand-chaining context/impact hops. The guide gains a Tools Reference row and a "shortest path between two symbols" subsection (params, result shape, status/furthest/truncated semantics); the debugging skill gains a "how does A reach B?" pattern row and a trace tool example. Mirrored to the .claude and claude-plugin copies (byte-identical) and the cursor copy (compact style). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(mcp): drop unused trace test fixtures (CodeQL js/unused-local-variable) CodeQL flagged two unused locals in the trace BFS tests: the top-level SYMBOL_C and a SYMBOL_D inside the maxDepth test (both defined, never referenced). Remove them. No behavior change — 58 trace/tools tests stay green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7c3d4e6862
|
feat(pdg): control dependence — post-dominators + CDG (Ferrante) [M5 #2085] (#2188)
* feat(pdg): add CDG + POST_DOMINATE edge types (M5 #2085) * feat(pdg): post-dominator tree on reverse CFG (M5 #2085) * feat(pdg): Ferrante control-dependence over the post-dom tree (M5 #2085) * feat(pdg): emitFileCdg + optional POST_DOMINATE debug edges (M5 #2085) * feat(pdg): wire CDG emission in-phase + pdgModeMismatch CDG-cap stamp (M5 #2085) * test(pdg): CDG snapshot + end-to-end pipeline answerability (M5 #2085) * fix(review): apply autofix feedback (M5 #2085) * fix(pdg): label CDG edges by controller arm sense, not edge kind (#2188 F1/F2/F4) Tri-review (with Codex as the independent engine) found the CDG 'T'/'F' label was wrong for the commonest control flow: the M1 TS visitor wires a condition's fall-through FALSE arm as `seq`/`loop-back`, but `branchSense` mapped both to 'T', so guard clauses, if-no-else, and loop `break` got 'T' instead of 'F' (F1, P1). The structural CDG edges were correct; only the label — the AC3 "under what condition does X run?" answer — was wrong. - F1: replace edge-kind `branchSense` with controller-arm-sense `labelFor`. An ambiguous fall-through edge (seq/loop-back) takes the COMPLEMENT of its source block's explicit cond-true/cond-false sibling arm. This correctly handles do/while (loop-back = TRUE arm) and inner-if-in-loop (loop-back = FALSE arm) — the ambiguity a kind→label table cannot resolve. Adds real-parser regression tests (the hand-built tests used a fictional cond-false edge and missed it). - F2: correct the false "sound over-approximation that never drops a real dependence" claim in post-dominators.ts — exit-unreachable regions both drop and invent control dependences (latent for the current TS visitor, which keeps EXIT reverse-reachable). Reframe the exit-less-loop test to characterize, not bless, the degenerate behavior. - F4: make the AC2 property-test reference compute post-dominance INDEPENDENTLY (node-removal reachability, no shared code with post-dominators.ts), so a post-dom direction bug can no longer pass both the impl and the reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): root-prettier format + run-analyze pdg stamp gains maxCdgEdgesPerFunction (#2085) Two deterministic CI failures from the M5 CDG work: - quality/format: basicblock-roundtrip.test.ts failed CI's root `prettier --check .` (the pre-commit hook uses the gitnexus-local prettier config, which differs); reformatted with the root config. - tests/ubuntu/coverage: run-analyze.test.ts pinned the resolved RepoMeta.pdg shape (DEFAULTS) and the all-zero cap override without the new maxCdgEdgesPerFunction key (default 5000); added it so resolvePdgConfig toEqual and pdgModeMismatch(DEFAULTS) pass. (The stale-test sweep missed this file in PR #2188 — same trap M2 hit.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(mcp): add pdg_query tool definition (controls/flows modes) [M6 #2086] * feat(mcp): pdg_query backend — controls (CDG) + flows (REACHING_DEF) + e2e test [M6 #2086] * feat(mcp): document PDG edges + pdg_query (schema, cypher, skill, --pdg-gated ai-context) [M6 #2086] * fix(mcp): correct pdg_query symbol-anchor lower bound + harden inputs [PR #2188 review] Tri-review (Codex + adversarial + correctness lanes) of the M6 pdg_query surface found the symbol-anchor window over-includes a neighbor function's block. The upper bound was widened to the 1-based BasicBlock basis (symEnd+1) but the lower bound was left 0-based, so a block on the line directly above the target function leaked into the result. Shift both bounds +1 ([symStart+1, symEnd+1]) so the window is the function's true block span. Also from the same review: - pdg_query no longer throws on a no-arguments MCP call: the dispatch passes raw `params`, so default it to {} → a clean mode-validation error instead of a TypeError. (`explain` shares this latent pattern — pre-existing follow-up.) - tools.ts: the controls-mode description no longer hard-codes the 'F' branch sense for guards — `if (!ok) return;` rides the predicate's 'T' arm; the guard:true flag is label-agnostic (regex on the dependent block text). Tests: a hand-seeded adjacency regression (verified failing without the lower-bound +1) + a no-arguments validation test. Skill doc updated to document the two-sided [symStart+1, symEnd+1] window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): drop always-true anchor conditional in pdg_query [CodeQL #2188] CodeQL alert 756 flagged `...(anchor ? { anchor } : {})` in _pdgQueryImpl as a useless conditional: `anchor` is unconditionally assigned in both the file-path and symbol branches before the return (the not-found/ambiguous/no-layer paths return earlier), so it is always truthy. Drop `| undefined` from the declaration (TypeScript definite-assignment holds across both branches) and emit `anchor` directly. No runtime change — the `anchor` field was already present on every result. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(cli): add hasPdg to the noStats bridge expectation [#2188] The M6 work threaded `hasPdg: options.pdg === true` into the AIContextOptions passed to generateAIContextFiles on the --skills regeneration path, but this test's strict .toEqual expectation predated it (4 keys vs 3 → CI failure). Add `hasPdg: false` (the value on this non---pdg path). The assertion stays strict; the #1477 noStats bridging it guards is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(cli): collapse generateGitNexusContent params to an options bag [#2188] The function had grown to 9 positional params; reaching `hasPdg` meant passing six `undefined`s (the M6 review's maintainability flag). Collapse params 3-9 (generatedSkills, groupNames, noStats, skipSkills, runnerPath, defaultBranch, hasPdg) into a `GitNexusContentOptions` object with the defaults moved to destructuring. The body is unchanged (same local names); the single production caller and the test calls become self-documenting named fields. Pure refactor — generated AGENTS.md/CLAUDE.md content is byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cfg): skip CDG for exit-unreachable CFGs (unsound post-dominance) [#2188] M5 review P2: computePostDominators roots only at cfg.exitIndex and nothing enforced that EXIT is reachable from every block. For an entry-reachable region that cannot reach EXIT (a non-terminating loop, or a multi-terminal CFG a future visitor might emit) the EXIT-rooted reverse walk degenerates — it both drops real control dependences and invents spurious ones. Add a pure precondition predicate `isExitReachableFromAllBlocks` (co-located with the algorithm it guards) and gate it in emitFileCdg: a CFG that violates it is skipped for CDG (counted as skippedUnsoundFunctions + one onWarn), while its CFG and REACHING_DEF projections — which do not depend on post-dominance — are kept. A CDG-specific gate, not a widening of isEmitSafeCfg, so the blast radius is exactly the unsound CDG. The current TS visitor always satisfies the precondition (every loop gets a structural header→loopExit edge), so CDG output for real fixtures is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cfg): bound computeControlDependence materialization (heap parity) [#2188] M5 review P2: unlike computeReachingDefs (maxFacts) and the emit-side edge cap, computeControlDependence materialized the full deduped seen/out before emitFileCdg's per-function cap could trim it — O(edges × post-dom depth) heap for a deeply nested function. Add a `maxEdges` ceiling (default 0 = unbounded) returning {edges, truncated}, mirroring computeReachingDefs's {facts, truncated}. The ceiling is checked before pushing a new unique edge, so `truncated` means a genuine overflow (not merely "reached cap"). emitFileCdg passes a FIXED materialization ceiling (8× the default edge cap) — deliberately NOT derived from the runtime edge cap, because CDG's materialization IS the deduped-edge quantity the cap reports on (deriving it would pre-truncate that set and lose the exact dropped count). A ceiling hit is surfaced via onWarn + the truncated flag — never silent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(mcp): share resolveBlockAnchor; fix explain's anchor off-by-one [#2188] M6 review P2 (duplication) + the flagged pre-existing _explainImpl correctness follow-up. _pdgQueryImpl and _explainImpl each carried a near-identical symbol↔block anchor resolver that had DRIFTED: pdg_query used the corrected [symStart+1, symEnd+1] window (BasicBlock startLine is 1-based, the symbol span 0-based) while _explainImpl still used [symStart, symEnd] — dropping a taint source on the function's final line AND leaking a neighbor's block on the line directly above. Extract one `resolveBlockAnchor` helper, used by both, that applies the correct window and a single (bare) clause convention (callers compose their own WHERE). This removes ~50 duplicated lines and fixes explain's anchor in one place. A hand-seeded characterization test (taint-explain Block 4) pins both bounds — verified to FAIL on the pre-fix window (it returned the line-10 neighbor instead of the line-15 final-line source). Existing taint-explain + pdg-query suites are unchanged (their fixtures have interior sources/sinks). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): pdg_query reports "status unknown" when the layer can't be confirmed [#2188] M6 review P3 (Codex): when meta is UNREADABLE and the bounded global existence probe returns zero rows of the edge type, _pdgQueryImpl asserted "no PDG layer" — but a genuinely edge-free layer (all-linear functions) is indistinguishable from a missing one via that probe. Soften only that fallback path to an inconclusive "PDG layer status unknown — was this repo indexed with --pdg?" note. The meta-stamped path (stamp present, cap absent ⇒ layer truly missing) keeps the definitive "no PDG layer" wording. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(mcp): cover pdg_query ambiguous / pagination / Windows-path gaps [#2188] M6 review test-gap follow-ups, all hand-seeded with controlled data: - ambiguous symbol name → status:'ambiguous' + ranked candidates shape (uid/name/filePath/score), never a silent guess; - total/truncated page boundary in both directions (limit below the match count sets truncated with the full total; limit above it omits truncated); - a Windows-style filePath containing ':' resolves and fnLineOf decodes the function-line segment correctly (split-from-right past the drive letter). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): ship gitnexus-pdg-query skill mirrors + add pdg_query to the guide [#2086] M6 bundled pdg_query into this PR, but the skill shipped only in the canonical gitnexus/skills/ root. Mirror it (byte-identical) to the two hand-maintained roots the sibling taint skill uses — .claude/skills/gitnexus/ and the plugin — so Claude Code + plugin users get it too. Also extend the gitnexus-guide tool reference (all 3 copies, now byte-identical): add a `pdg_query` row + a "Control & data dependence" section mirroring the taint/`explain` section, and reconcile the pre-existing drift where only the .claude copy carried the `check` tool row (a real registered tool) — all three now list it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(architecture): refresh CFG/PDG section for the full M1–M6 stack [#2086] The PR body had deferred the "ARCHITECTURE docs refresh" to #2086; now that M6 ships here, do it: - MCP tools table gains `explain` and `pdg_query` (were absent). - "Optional CFG/PDG emission" was M1-only; rewrite to cover the whole opt-in stack — M1 CFG, M2 REACHING_DEF, M3/M4 taint, M5 CDG (Ferrante over CHK post-dominators, with the exit-unreachable skip), M6 read surface (pdg_query + explain, anchored + LIMIT-bounded, shared resolveBlockAnchor) — and note the no-Function→BasicBlock-edge join. - LadybugDB schema notes the `--pdg` additions: the `BasicBlock` node table and the CFG/REACHING_DEF/CDG/TAINTED/SANITIZES/TAINT_PATH relation types, kept out of the default VALID_RELATION_TYPES / web schema. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
14397dd4aa
|
feat(taint): intra-procedural taint analysis (#2083) (#2164)
* feat(taint): harvest occurrence-tagged call/member sites on StatementFacts (#2083 U1) Worker-side site harvest in TsHarvester: call/new/member-read records with dotted callee paths, receiver slots, per-argument occurrence tagging with nested-site links, per-declarator resultDefs, spread/template/require-literal markers. hasTaintSafeSites validation seam. The pdg parse-cache chunk-key namespace is versioned (pdg:1 -> pdg:2) instead of a global SCHEMA_BUMP so flag-off users keep warm caches; bench fingerprints re-baselined for the three call-bearing scenarios (straight-line/dense-bindings byte-unchanged). * feat(taint): built-in TS/JS source/sink/sanitizer model + site matcher (#2083 U2) Typed spec (kind taxonomy; sanitizers carry neutralizes-kinds), the canonical Express/Node model, and matchFunctionSites: ESM alias/namespace + require- literal callee resolution, bare-name fallback restricted to true globals, sanitizers module-or-global only (never user-shadowable by name), spread/ template arg-position rules, deterministic taintModelVersion. * feat(taint): pure intra-procedural taint propagation engine (#2083 U3) Two-rule model (statement-local + du-fact worklist) with per-taint neutralized-kind exclusion sets: sanitizers exclude only the sink kinds they neutralize (escape(req.body) suppresses res.send but still fires db.query; exec(path.basename(t)) fires), intersection-over-paths so a bypass occurrence keeps the taint live, kill locality on resultDefs, propagate-through args+receiver with viaCall hops, one path per finding, deterministic caps, coverage-gap statuses. Test-first: 38 scenarios on real harvested CFGs. * feat(taint): thread taint caps + model version through pdg config/meta (#2083 U5) resolvePdgConfig gains maxTaintFindingsPerFunction (200), maxTaintHops (32), and the taintModelVersion digest; RepoMeta.pdg + RunScopeResolutionInput surfaces added. The key-union comparator trips full writeback on M2->M3 upgrade and on model-version change without --force (mode-flip tested). No CLI flags or rc keys (programmatic parity with the other caps). * feat(taint): in-phase taint emit with sparse TAINTED/SANITIZES edges (#2083 U4) run.ts pdg window: match-first fast path (solver only when a function has both a matched source and sink) -> computeReachingDefs with the shared RD fact derivation -> computeTaintFlows -> per-finding TAINTED (versioned hop-encoded reason via the shared path codec, statement-level occurrence identity) + per-kill SANITIZES, dedup-before-budget, truncate-and-warn. All emit counters surfaced (aggregate warn for gaps/drops, debug for volume); PROF gains taint=. Flag-off golden untouched. * feat(mcp): explain tool for persisted taint findings (#2083 U6) Anchorless calls enumerate the sparse TAINTED table (bounded, deterministic, limit-clamped); anchored calls (file or symbol via resolveSymbolCandidates) return full decoded hop detail. sinkKind rides a version-1 codec header (1;<kind>|hops — no other persisted channel exists; U4/U6 ship together). RepoMeta.pdg probe yields a no-taint-layer note instead of an error. TAINTED/SANITIZES pinned OUT of VALID_RELATION_TYPES (KTD9a negative- membership tests); generators + canonical skill docs + mirrors updated. * test(taint): acceptance fixture battery, snapshots, and bench gates (#2083 U7) pdg-repo taint-cases fixtures complete the six plan shapes; committed findings/kills snapshot via a shared pure-path harness that also feeds the AE2 exact-equality assertion (stored TAINTED == pure-path findings, the no-explosion gate). New taint-dense bench scenario with four --check gates: per-function findings pinned AT the cap, absolute reason-byte + site-bytes disk ceilings (the load-bearing R10 gate), zero-match pass < 0.5x match- dense, N-linearity. Pre-existing scenario baselines untouched. * refactor(taint): share one pointKey helper across propagate + emit (#2083 review) Extract pointKey(ProgramPoint) to cfg/reaching-defs.ts (colon-separated, matching the codebase block:stmt id convention) and import it in both propagate.ts and emit.ts, replacing the two divergent locals (':' vs '.'). Edge-id material now uses the colon form; ids are in-memory only and no test asserts the pointKey segment shape. * fix(taint): discriminate taint state by source occurrence (#2083 review) Two distinct sources flowing into one variable at one def point no longer collapse to a single TAINTED edge: the taint-state key gains a root source-occurrence discriminator ({point, siteIndex} — the same fields recordFinding's identity uses, excluding kind). Def->use fact lookup keys on the source-independent (binding, def-point) portion. Same-source multi-path flows still share one state so their exclusion sets intersect (the raw arm soundly wins); termination holds (finite keys, monotone shrink, no cross-source ping-pong). Restores the KTD6 identity contract. * fix(mcp): route dotted symbol names in explain to symbol resolution (#2083 review) The fileish classifier matched any dotted name (UserController.create) as a file via its extension-like suffix, so symbol resolution never ran and the tool returned a silent empty file-anchored result. Tighten the classifier to require a path separator or a real source extension (derived from the resolver's EXTENSIONS list, multi-language), so dotted/bare names route to resolveSymbolCandidates (found / ambiguous / not-found). * fix(mcp): gate explain no-taint-layer note on taintModelVersion (#2083 review) An M1/M2-era --pdg index has meta.pdg defined (BasicBlock/REACHING_DEF recorded) but no taintModelVersion and zero TAINTED rows. The probe keyed on generic meta.pdg presence, so explain returned the generic empty note instead of the actionable 'no taint layer — run analyze' hint. Gate on meta.pdg?.taintModelVersion (the field M3 stamps) so an M2-era index gets the layer hint; a taint-stamped index with no findings still gets the generic note. * fix(taint): sequence-expression value flows only the final operand (#2083 review) A comma expression in value position (exec((log(x), 'safe'))) default- descended, fanning every operand's occurrences into the enclosing sink argument — over-tainting exec's arg 0 with x. Add an explicit walkValue case that records earlier operands' uses with occurrence fan-out suppressed (new FactAccumulator.suppressOccurrences) and routes only the last operand through the value path. Sites-layer only; defs/uses/mayDefs byte-identical (cfg + reaching-defs snapshots unchanged). * perf(taint): FIFO head-cursor worklist + dedup before chainHops (#2083 review) Replace queue.shift() (O(N) dequeue) with a strict-FIFO head cursor plus order-preserving prefix reclamation; FIFO is load-bearing because chainHops reads the live taints map whose parent/source/viaCall are rewritten order-sensitively on monotone shrink, so hop determinism is dequeue-order contingent. Extract findingKey() and dedup-check before chainHops in the justify branch — already-recorded identities discard their hop chain (first write wins), so the ancestry walk was pure waste. The else kill branch is untouched. Findings + hops byte-identical (snapshot unchanged). * perf(taint): O(1) member-read dedup via composite-key set (#2083 review) addMemberRead rescanned the whole per-statement sites array per call to dedup by (object, property, parent) — O(n^2) on member-read-dense statements. Track a composite-key Set alongside sites for O(1) dedup. (The require-literal join is already O(sites) with a no-op body on non-require sites, so no early-exit is needed there.) Behavior identical: harvest + model-match + taint snapshots unchanged. * refactor(taint): drop test-only export; source taint caps via emit.ts (#2083 review) Remove the sanitizerNeutralizes export (its only consumers were two test assertions — inlined to entry.neutralizes membership). Re-export the DEFAULT_PDG_MAX_TAINT_* caps from emit.ts and point run.ts at emit.ts, so the pipeline's taint dependency surface is the single orchestration module rather than reaching into propagate.ts. * test(taint): extract the shared TS CFG/taint test harness (#2083 review) The parse/collectFunctions/cfgOf/cfgsOf/importsFor harness was copied byte-for-byte across four suites (harvest, model-match, propagate, taint-emit). Promote it to test/helpers/ts-cfg-harness.ts and import it. site-safety/reaching-defs carry a structurally different inlined builder and are left as-is. Pure extraction, no assertion changes. * test(mcp): harden explain limit-rejection battery (#2083 review) Add NaN, Infinity, -Infinity, and a numeric string to the out-of-bounds limit cases — a regression fence over the interpolated LIMIT, confirming the Number.isInteger guard rejects every non-integer/non-finite/string input before it reaches the query. |
||
|
|
4682a477d8
|
feat(mcp): paginate list_repos to avoid client token truncation (#2119) (#2120)
* feat(mcp): paginate list_repos to avoid client token truncation (#2119) list_repos returned every indexed repository in one unpaginated array, which large/LLM MCP clients truncate by token limit — so agents with hundreds of indexed repos could not enumerate them all (the data transmits fully; the consuming client drops it). Add bounded limit/offset pagination to the list_repos tool: - result changes from a bare array to { repositories, pagination: { total, limit, offset, returned, hasMore, nextOffset } }; default page 50, max 200 (shared constants) - reject malformed limit/offset; clamp limit above the max - deterministic order (lower-cased name, then path) over one registry snapshot per call, so paging never skips or duplicates an entry - covers both stdio and remote /api/mcp (shared createMCPServer/callTool) The internal listRepos() method (5 callers), GET /api/repos, and the `gitnexus list` CLI are unchanged. The array->object tool-result shape is a deliberate contract change, documented in CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(mcp): reject list_repos limit above the max instead of clamping (#2119) parseListReposPagination silently clamped limit>max to the maximum while throwing on every other out-of-bounds value (limit<1, offset<0, non-integer, NaN). A client that advanced offset by its requested limit (rather than pagination.nextOffset) then silently skipped repositories and saw hasMore:false — defeating the "never skips" guarantee. Reject an over-max limit too, so validation is symmetric and a caller never gets a smaller page than it asked for without a clear error. Updates the schema/description, the helper + ListReposPagination JSDoc, the guide note, and the two clamp tests. Resolves the cross-engine-corroborated P2 (Codex + adversarial lane) and the maintainability lane's clamp-vs-throw inconsistency from the PR #2120 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(mcp): name the list_repos return type and mark the parser @internal Extract the inline listRepos() element shape into an exported RepoListing interface and use it for both listRepos() and listReposPage().repositories, replacing the opaque Awaited<ReturnType<LocalBackend['listRepos']>> expression the maintainability review flagged. Tag parseListReposPagination @internal (it is exported only for unit testing). Pure type/JSDoc change; no behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(eval-server): type formatListReposResult to the paginated shape Narrow formatListReposResult's parameter from `any` to { repositories: RepoListing[]; pagination?: ListReposPagination } and drop the dead bare-array branch — after #2119 callTool('list_repos') always returns the paginated object, so the Array.isArray shim was unreachable. Add a list_repos continuation hint to the eval-server's getNextStepHint (parity with the MCP server), and cover the previously-untested non-empty + hasMore:false formatter branch. Migrates the two bare-array formatter tests to the object shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(mcp): harden list_repos pagination coverage - Exercise the #2054 sibling-clone guarantee through the real callTool tool path (in the #2054 describe, which has temp-dir cleanup), proving siblings and remoteUrl survive listReposPage's sort+slice — not only listRepos(). - Assert total + limit on the middle-page test (a total miscalculation at a non-zero offset would otherwise slip past it). - Cover the benign boundaries: negative-zero offset (accepted as page 0) and a MAX_SAFE_INTEGER offset (empty page). - Replace the integration test's '\n\n---' split with a string-aware brace scan, so a repo path containing braces can never truncate the JSON parse. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): sync the list_repos pagination example to the guide mirrors The .claude and gitnexus-claude-plugin guide mirrors only carried the one-line table note; add the full "Paginating list_repos" section (shape + multi-page traversal example + notes) so all three guide copies are byte-consistent with the canonical gitnexus/skills/gitnexus-guide.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: drop list_repos CHANGELOG entries from this PR Restore gitnexus/CHANGELOG.md to match main so this PR contributes no changelog change; the changelog is curated separately from feature PRs. 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> |
||
|
|
39b01f101e |
feat(skills): rewrite skill descriptions for better auto-invocation
Skill descriptions were too tool-centric ("using knowledge graph", "blast
radius") which prevented Claude Code from matching them to user intent.
Rewritten to user-intent-driven format with "Use when..." phrasing and
example trigger phrases so Claude can semantically match user requests.
Updated across all 3 sources: gitnexus/skills/, gitnexus-claude-plugin/skills/,
.claude/skills/, and the ai-context.ts fallback generator.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||
|
|
238abbd947 |
refactor(skills): prefix all skill names with gitnexus- for disambiguation
Skill folder names determine invocation paths in Claude Code plugins (e.g. plugin:gitnexus:gitnexus-cli). Generic names like "cli" or "debugging" could collide with other plugins, so prefix them all with gitnexus- for clarity. Updated across plugin dirs, main package source files, ai-context.ts generator, setup.ts installer, and all CLAUDE.md/AGENTS.md routing tables. |
Renamed from gitnexus/skills/guide.md (Browse further)