gitnexus 1.6.10-rc.50 → 1.6.10-rc.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +3 -0
  2. package/dist/cli/ai-context.js +7 -36
  3. package/dist/cli/analyze.d.ts +4 -1
  4. package/dist/cli/analyze.js +13 -6
  5. package/dist/cli/help-i18n.js +1 -0
  6. package/dist/cli/i18n/en.d.ts +5 -1
  7. package/dist/cli/i18n/en.js +5 -1
  8. package/dist/cli/i18n/resources.d.ts +9 -1
  9. package/dist/cli/i18n/zh-CN.d.ts +4 -0
  10. package/dist/cli/i18n/zh-CN.js +5 -1
  11. package/dist/cli/index.js +4 -2
  12. package/dist/cli/lazy-action.d.ts +7 -0
  13. package/dist/cli/lazy-action.js +27 -0
  14. package/dist/cli/setup.d.ts +8 -0
  15. package/dist/cli/setup.js +26 -4
  16. package/dist/cli/standard-skills.d.ts +76 -0
  17. package/dist/cli/standard-skills.js +45 -0
  18. package/dist/cli/status.d.ts +4 -1
  19. package/dist/cli/status.js +57 -4
  20. package/dist/cli/uninstall.js +7 -2
  21. package/dist/core/analyzer-identity.d.ts +74 -0
  22. package/dist/core/analyzer-identity.js +1820 -0
  23. package/dist/core/index-freshness.d.ts +5 -0
  24. package/dist/core/index-freshness.js +13 -0
  25. package/dist/core/run-analyze.d.ts +2 -2
  26. package/dist/core/run-analyze.js +32 -30
  27. package/dist/mcp/resources.js +17 -0
  28. package/dist/server/analyze-worker-core.d.ts +2 -1
  29. package/dist/server/analyze-worker-core.js +5 -2
  30. package/dist/server/analyze-worker.js +29 -6
  31. package/dist/storage/git.d.ts +9 -0
  32. package/dist/storage/git.js +36 -1
  33. package/dist/storage/repo-manager.d.ts +50 -0
  34. package/package.json +1 -1
  35. package/scripts/sync-plugin-manifests.mjs +61 -10
  36. package/skills/gitnexus-lfg/README.md +55 -0
  37. package/skills/gitnexus-lfg/SKILL.md +86 -0
  38. package/skills/gitnexus-plan/README.md +142 -0
  39. package/skills/gitnexus-plan/SKILL.md +348 -0
  40. package/skills/gitnexus-plan/references/context-ledger.md +137 -0
  41. package/skills/gitnexus-plan/references/context-pack.md +126 -0
  42. package/skills/gitnexus-plan/references/evidence-provenance.md +272 -0
  43. package/skills/gitnexus-plan/references/pdg-slice.md +109 -0
  44. package/skills/gitnexus-plan/references/plan-template.md +201 -0
  45. package/skills/gitnexus-plan/scripts/evidence-provenance.mjs +2084 -0
  46. package/skills/gitnexus-review/SKILL.md +228 -0
  47. package/skills/gitnexus-work/README.md +71 -0
  48. package/skills/gitnexus-work/SKILL.md +269 -0
  49. package/skills/gitnexus-work/references/evidence-provenance.md +272 -0
  50. package/skills/gitnexus-work/scripts/evidence-provenance.mjs +2084 -0
  51. package/skills/gitnexus-pr-review.md +0 -163
package/README.md CHANGED
@@ -332,6 +332,9 @@ GitNexus ships with skill files that teach AI agents how to use the tools effect
332
332
  - **Refactoring** — Plan safe refactors using dependency mapping
333
333
  - **Guide** — GitNexus tool/resource/schema reference for the agent
334
334
  - **CLI** — Run analyze/status/clean/wiki commands on request
335
+ - **PDG Query** — Statement-level control/data dependence queries (`--pdg` index)
336
+ - **Taint Analysis** — Source→sink data-flow findings (`--pdg` index)
337
+ - **Plan / Work / Review / LFG** — The engineering family: implementation-ready plans, gated plan execution, graph-backed change review with taint + expert lenses, and the end-to-end pipeline
335
338
 
336
339
  Installed automatically by both `gitnexus analyze` (per-repo) and `gitnexus setup` (global). Run `gitnexus analyze --skills` to additionally generate each detected functional area as a direct project skill under `.claude/skills/gitnexus-area-<name>/`.
337
340
 
@@ -8,6 +8,7 @@
8
8
  import fs from 'fs/promises';
9
9
  import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
+ import { STANDARD_SKILL_CATALOG } from './standard-skills.js';
11
12
  import { logger } from '../core/logger.js';
12
13
  // ESM equivalent of __dirname
13
14
  const __filename = fileURLToPath(import.meta.url);
@@ -91,12 +92,9 @@ export function generateGitNexusContent(projectName, stats, opts = {}) {
91
92
  // are independent of --skip-skills, so they remain when present.
92
93
  const standardSkillsRows = skipSkills
93
94
  ? ''
94
- : `| Understand architecture / "How does X work?" | \`.claude/skills/gitnexus-exploring/SKILL.md\` |
95
- | Blast radius / "What breaks if I change X?" | \`.claude/skills/gitnexus-impact-analysis/SKILL.md\` |
96
- | Trace bugs / "Why is X failing?" | \`.claude/skills/gitnexus-debugging/SKILL.md\` |
97
- | Rename / extract / split / refactor | \`.claude/skills/gitnexus-refactoring/SKILL.md\` |
98
- | Tools, resources, schema reference | \`.claude/skills/gitnexus-guide/SKILL.md\` |
99
- | Index, status, clean, wiki CLI commands | \`.claude/skills/gitnexus-cli/SKILL.md\` |`;
95
+ : STANDARD_SKILL_CATALOG.filter((skill) => skill.distributions.project)
96
+ .map((skill) => `| ${skill.agentTableTask} | \`.claude/skills/${skill.name}/SKILL.md\` |`)
97
+ .join('\n');
100
98
  const tableBody = [standardSkillsRows, generatedRows].filter(Boolean).join('\n');
101
99
  const skillsTable = tableBody
102
100
  ? `| Task | Read this skill file |
@@ -260,34 +258,7 @@ async function installSkills(repoPath) {
260
258
  const skillsDir = path.join(repoPath, '.claude', 'skills');
261
259
  const legacySkillsDir = path.join(skillsDir, 'gitnexus');
262
260
  const installedSkills = [];
263
- // Skill definitions bundled with the package
264
- const skills = [
265
- {
266
- name: 'gitnexus-exploring',
267
- description: 'Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: "How does X work?", "What calls this function?", "Show me the auth flow"',
268
- },
269
- {
270
- name: 'gitnexus-debugging',
271
- description: 'Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: "Why is X failing?", "Where does this error come from?", "Trace this bug"',
272
- },
273
- {
274
- name: 'gitnexus-impact-analysis',
275
- description: 'Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: "Is it safe to change X?", "What depends on this?", "What will break?"',
276
- },
277
- {
278
- name: 'gitnexus-refactoring',
279
- description: 'Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: "Rename this function", "Extract this into a module", "Refactor this class", "Move this to a separate file"',
280
- },
281
- {
282
- name: 'gitnexus-guide',
283
- description: 'Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: "What GitNexus tools are available?", "How do I use GitNexus?"',
284
- },
285
- {
286
- name: 'gitnexus-cli',
287
- description: 'Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: "Index this repo", "Reanalyze the codebase", "Generate a wiki"',
288
- },
289
- ];
290
- for (const skill of skills) {
261
+ for (const skill of STANDARD_SKILL_CATALOG.filter((entry) => entry.distributions.project && entry.distributions.npm)) {
291
262
  const skillDir = path.join(skillsDir, skill.name);
292
263
  const skillPath = path.join(skillDir, 'SKILL.md');
293
264
  try {
@@ -303,12 +274,12 @@ async function installSkills(repoPath) {
303
274
  // Fallback: generate minimal skill content
304
275
  skillContent = `---
305
276
  name: ${skill.name}
306
- description: ${skill.description}
277
+ description: ${skill.fallbackDescription}
307
278
  ---
308
279
 
309
280
  # ${skill.name.charAt(0).toUpperCase() + skill.name.slice(1)}
310
281
 
311
- ${skill.description}
282
+ ${skill.fallbackDescription}
312
283
 
313
284
  Use GitNexus tools to accomplish this task.
314
285
  `;
@@ -7,6 +7,7 @@
7
7
  * This CLI wrapper handles: heap management, progress bar, SIGINT,
8
8
  * skill generation (--skills), summary output, and process.exit().
9
9
  */
10
+ import { type AnalyzerRunnerIdentity } from '../storage/repo-manager.js';
10
11
  /**
11
12
  * RAM-aware re-exec heap cap (MB): `0.75 × effective RAM`, clamped to
12
13
  * `>= DEFAULT_HEAP_MB`. Kept BELOW physical RAM on purpose — a cap `>=` RAM makes
@@ -138,4 +139,6 @@ export interface AnalyzeOptions {
138
139
  * without booting the full analyze pipeline (#742 review).
139
140
  */
140
141
  export declare const shouldGenerateCommunitySkillFiles: (options: Pick<AnalyzeOptions, "skills" | "indexOnly"> | undefined, pipelineResult: unknown) => boolean;
141
- export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
142
+ export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions, runnerIdentityAtBootstrap?: AnalyzerRunnerIdentity) => Promise<void>;
143
+ /** Commander entrypoint used only by the capture-before-import lazy bootstrap. */
144
+ export declare const analyzeCommandWithRunnerIdentity: (runnerIdentityAtBootstrap: AnalyzerRunnerIdentity, inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
@@ -485,7 +485,7 @@ const restoreAnalyzeEnv = (snap) => {
485
485
  * without booting the full analyze pipeline (#742 review).
486
486
  */
487
487
  export const shouldGenerateCommunitySkillFiles = (options, pipelineResult) => Boolean(options?.skills && pipelineResult && !options?.indexOnly);
488
- export const analyzeCommand = async (inputPath, options) => {
488
+ export const analyzeCommand = async (inputPath, options, runnerIdentityAtBootstrap) => {
489
489
  if (await ensureHeap())
490
490
  return;
491
491
  forceHeapOOMForTestIfEnabled();
@@ -506,7 +506,7 @@ export const analyzeCommand = async (inputPath, options) => {
506
506
  // pre-call values.
507
507
  const envSnap = snapshotAnalyzeEnv();
508
508
  try {
509
- await analyzeCommandImpl(inputPath, options);
509
+ await analyzeCommandImpl(inputPath, options, runnerIdentityAtBootstrap);
510
510
  }
511
511
  finally {
512
512
  restoreAnalyzeEnv(envSnap);
@@ -523,7 +523,9 @@ export const analyzeCommand = async (inputPath, options) => {
523
523
  process.exit(typeof process.exitCode === 'number' ? process.exitCode : 1);
524
524
  }
525
525
  };
526
- const analyzeCommandImpl = async (inputPath, cliOptions) => {
526
+ /** Commander entrypoint used only by the capture-before-import lazy bootstrap. */
527
+ export const analyzeCommandWithRunnerIdentity = async (runnerIdentityAtBootstrap, inputPath, options) => analyzeCommand(inputPath, options, runnerIdentityAtBootstrap);
528
+ const analyzeCommandImpl = async (inputPath, cliOptions, runnerIdentityAtBootstrap) => {
527
529
  console.log('\n GitNexus Analyzer\n');
528
530
  // ── Resolve the target repo root ──────────────────────────────────
529
531
  // Resolved FIRST because `.gitnexusrc` is read from the repo root (not the
@@ -988,7 +990,7 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
988
990
  const skipAll = options.indexOnly;
989
991
  const skipAgentsMd = skipAll || options.skipAgentsMd;
990
992
  const skipSkills = skipAll || options.skipSkills;
991
- const result = await runFullAnalysis(repoPath, {
993
+ const runOptions = {
992
994
  // Pipeline re-index — OR'd with --skills because skill generation
993
995
  // needs a fresh pipelineResult. Has no bearing on the registry
994
996
  // collision guard (see allowDuplicateName below).
@@ -1035,12 +1037,17 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
1035
1037
  // in LadybugDB's ClientContext destructor after --pdg writes (#2264). The
1036
1038
  // CHECKPOINT keeps the index durable; process exit reclaims the handles.
1037
1039
  skipNativeCloseOnExit: true,
1038
- }, {
1040
+ };
1041
+ const runCallbacks = {
1039
1042
  onProgress: (_phase, percent, message) => {
1040
1043
  updateBar(percent, message);
1041
1044
  },
1042
1045
  onLog: barLog,
1043
- });
1046
+ };
1047
+ const bootstrapArgs = runnerIdentityAtBootstrap
1048
+ ? [runnerIdentityAtBootstrap]
1049
+ : [];
1050
+ const result = await runFullAnalysis(repoPath, runOptions, runCallbacks, ...bootstrapArgs);
1044
1051
  if (result.alreadyUpToDate) {
1045
1052
  // Even the fast path must prove the repo is discoverable. A prior
1046
1053
  // run can write meta.json and then fail before registerRepo(); in
@@ -75,6 +75,7 @@ const OPTION_DESCRIPTION_KEYS = {
75
75
  'mcp|--auth-token <token>': 'help.option.mcp.authToken',
76
76
  'serve|-p, --port <port>': 'help.option.port',
77
77
  'serve|--host <host>': 'help.option.serve.host',
78
+ 'status|--json': 'help.option.status.json',
78
79
  'uninstall|-f, --force': 'help.option.uninstall.force',
79
80
  'clean|-f, --force': 'help.option.force.confirmation',
80
81
  'clean|--all': 'help.option.clean.all',
@@ -26,6 +26,8 @@ export declare const en: {
26
26
  readonly 'status.indexed': "Indexed";
27
27
  readonly 'status.indexedCommit': "Indexed commit";
28
28
  readonly 'status.currentCommit': "Current commit";
29
+ readonly 'status.indexRunnerIdentity': "Indexed analyzer runner identity";
30
+ readonly 'status.currentRunnerIdentity': "Current analyzer runner identity";
29
31
  readonly 'status.branch': "Branch";
30
32
  readonly 'status.detached': "(detached HEAD)";
31
33
  readonly 'status.workspaceIndexLabel': "Workspace index: last analyzed on '{{primary}}' (re-run gitnexus analyze to follow the current branch)";
@@ -231,6 +233,7 @@ export declare const en: {
231
233
  readonly 'help.option.group.sync.exactOnly': "Exact match only";
232
234
  readonly 'help.option.group.sync.allowStale': "Skip stale index warnings";
233
235
  readonly 'help.option.group.sync.verbose': "Show each cross-link detail";
236
+ readonly 'help.option.status.json': "Emit machine-readable index and analyzer provenance";
234
237
  readonly 'help.option.json': "JSON output";
235
238
  readonly 'help.option.group.impact.target': "Symbol or file name to analyze";
236
239
  readonly 'help.option.group.impact.repo': "Member path from group.yaml (e.g. app/backend), not the indexed repo name";
@@ -244,5 +247,6 @@ export declare const en: {
244
247
  readonly 'help.option.group.contracts.type': "Filter by contract type";
245
248
  readonly 'help.option.group.contracts.repo': "Filter by repo";
246
249
  readonly 'help.option.group.contracts.unmatched': "Show only unmatched contracts";
247
- readonly 'help.analyze.environment': "\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).";
250
+ readonly 'help.identityCache.environment': "\nAnalyzer identity cache:\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir\n Operator-trusted persistent cache for warm cross-process status. The directory must pre-exist, be outside the GitNexus package/build roots, and contain no symlink or junction components. Defaults remain fail-closed on platforms without POSIX ownership APIs.";
251
+ readonly 'help.analyze.environment': "\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir Operator-trusted persistent analyzer identity cache; must pre-exist, be outside package/build roots, and contain no symlink/junction components.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).";
248
252
  };
@@ -26,6 +26,8 @@ export const en = {
26
26
  'status.indexed': 'Indexed',
27
27
  'status.indexedCommit': 'Indexed commit',
28
28
  'status.currentCommit': 'Current commit',
29
+ 'status.indexRunnerIdentity': 'Indexed analyzer runner identity',
30
+ 'status.currentRunnerIdentity': 'Current analyzer runner identity',
29
31
  'status.branch': 'Branch',
30
32
  'status.detached': '(detached HEAD)',
31
33
  'status.workspaceIndexLabel': "Workspace index: last analyzed on '{{primary}}' (re-run gitnexus analyze to follow the current branch)",
@@ -231,6 +233,7 @@ export const en = {
231
233
  'help.option.group.sync.exactOnly': 'Exact match only',
232
234
  'help.option.group.sync.allowStale': 'Skip stale index warnings',
233
235
  'help.option.group.sync.verbose': 'Show each cross-link detail',
236
+ 'help.option.status.json': 'Emit machine-readable index and analyzer provenance',
234
237
  'help.option.json': 'JSON output',
235
238
  'help.option.group.impact.target': 'Symbol or file name to analyze',
236
239
  'help.option.group.impact.repo': 'Member path from group.yaml (e.g. app/backend), not the indexed repo name',
@@ -244,5 +247,6 @@ export const en = {
244
247
  'help.option.group.contracts.type': 'Filter by contract type',
245
248
  'help.option.group.contracts.repo': 'Filter by repo',
246
249
  'help.option.group.contracts.unmatched': 'Show only unmatched contracts',
247
- 'help.analyze.environment': '\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).',
250
+ 'help.identityCache.environment': '\nAnalyzer identity cache:\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir\n Operator-trusted persistent cache for warm cross-process status. The directory must pre-exist, be outside the GitNexus package/build roots, and contain no symlink or junction components. Defaults remain fail-closed on platforms without POSIX ownership APIs.',
251
+ 'help.analyze.environment': '\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir Operator-trusted persistent analyzer identity cache; must pre-exist, be outside package/build roots, and contain no symlink/junction components.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).',
248
252
  };
@@ -27,6 +27,8 @@ export declare const cliResources: {
27
27
  readonly 'status.indexed': "Indexed";
28
28
  readonly 'status.indexedCommit': "Indexed commit";
29
29
  readonly 'status.currentCommit': "Current commit";
30
+ readonly 'status.indexRunnerIdentity': "Indexed analyzer runner identity";
31
+ readonly 'status.currentRunnerIdentity': "Current analyzer runner identity";
30
32
  readonly 'status.branch': "Branch";
31
33
  readonly 'status.detached': "(detached HEAD)";
32
34
  readonly 'status.workspaceIndexLabel': "Workspace index: last analyzed on '{{primary}}' (re-run gitnexus analyze to follow the current branch)";
@@ -232,6 +234,7 @@ export declare const cliResources: {
232
234
  readonly 'help.option.group.sync.exactOnly': "Exact match only";
233
235
  readonly 'help.option.group.sync.allowStale': "Skip stale index warnings";
234
236
  readonly 'help.option.group.sync.verbose': "Show each cross-link detail";
237
+ readonly 'help.option.status.json': "Emit machine-readable index and analyzer provenance";
235
238
  readonly 'help.option.json': "JSON output";
236
239
  readonly 'help.option.group.impact.target': "Symbol or file name to analyze";
237
240
  readonly 'help.option.group.impact.repo': "Member path from group.yaml (e.g. app/backend), not the indexed repo name";
@@ -245,7 +248,8 @@ export declare const cliResources: {
245
248
  readonly 'help.option.group.contracts.type': "Filter by contract type";
246
249
  readonly 'help.option.group.contracts.repo': "Filter by repo";
247
250
  readonly 'help.option.group.contracts.unmatched': "Show only unmatched contracts";
248
- readonly 'help.analyze.environment': "\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).";
251
+ readonly 'help.identityCache.environment': "\nAnalyzer identity cache:\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir\n Operator-trusted persistent cache for warm cross-process status. The directory must pre-exist, be outside the GitNexus package/build roots, and contain no symlink or junction components. Defaults remain fail-closed on platforms without POSIX ownership APIs.";
252
+ readonly 'help.analyze.environment': "\nEnvironment variables:\n GITNEXUS_NO_GITIGNORE=1 Skip .gitignore parsing (still reads .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N Override large-file skip threshold (KB). Default 512, max 32768.\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir Operator-trusted persistent analyzer identity cache; must pre-exist, be outside package/build roots, and contain no symlink/junction components.\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker idle timeout in milliseconds. Default 30000.\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL auto-checkpoint threshold in bytes (default 67108864 = 64 MiB; -1 keeps Ladybug stock ~16 MiB).\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker job byte budget. Default 8388608.\n GITNEXUS_WORKER_POOL_SIZE=N Parse worker count override. Default cores-1 capped at 16.\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N Concurrent in-flight parse chunks. Default 2.\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N Max replacement spawns per slot before drop. Default 3.\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N Total retry wall-time per job. Default 5x sub-batch timeout.\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N Per-slot deaths to trip circuit breaker. Default max(3, poolSize).\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N Max wait at pool shutdown for a retired worker still inside native code (terminated at its next safe point instead of aborting the process). Default 30000.\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N Per-file wall-clock budget for C++ capture extraction; on breach the file keeps partial captures with a warning. Default 20000.\n GITNEXUS_EMBEDDING_THREADS=N Limit local ONNX CPU threads for --embeddings.\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N Max embedding chunks for exact-scan fallback. Default 10000.\n GITNEXUS_VECTOR_MAX_DISTANCE=N Max accepted semantic/vector cosine distance (0 < N <= 2; higher values clamp to 2). Default 0.6 for MCP, 0.5 elsewhere.\n\nFlags override the corresponding env vars when both are provided.\n\nTip: `.gitnexusignore` supports `.gitignore`-style negation. Add e.g.\n `!__tests__/` to index a directory that is auto-filtered by default (#771).";
249
253
  };
250
254
  readonly 'zh-CN': {
251
255
  'common.notIndexed': string;
@@ -275,6 +279,8 @@ export declare const cliResources: {
275
279
  'status.indexed': string;
276
280
  'status.indexedCommit': string;
277
281
  'status.currentCommit': string;
282
+ 'status.indexRunnerIdentity': string;
283
+ 'status.currentRunnerIdentity': string;
278
284
  'status.branch': string;
279
285
  'status.detached': string;
280
286
  'status.workspaceIndexLabel': string;
@@ -480,6 +486,7 @@ export declare const cliResources: {
480
486
  'help.option.group.sync.exactOnly': string;
481
487
  'help.option.group.sync.allowStale': string;
482
488
  'help.option.group.sync.verbose': string;
489
+ 'help.option.status.json': string;
483
490
  'help.option.json': string;
484
491
  'help.option.group.impact.target': string;
485
492
  'help.option.group.impact.repo': string;
@@ -493,6 +500,7 @@ export declare const cliResources: {
493
500
  'help.option.group.contracts.type': string;
494
501
  'help.option.group.contracts.repo': string;
495
502
  'help.option.group.contracts.unmatched': string;
503
+ 'help.identityCache.environment': string;
496
504
  'help.analyze.environment': string;
497
505
  };
498
506
  };
@@ -26,6 +26,8 @@ export declare const zhCN: {
26
26
  'status.indexed': string;
27
27
  'status.indexedCommit': string;
28
28
  'status.currentCommit': string;
29
+ 'status.indexRunnerIdentity': string;
30
+ 'status.currentRunnerIdentity': string;
29
31
  'status.branch': string;
30
32
  'status.detached': string;
31
33
  'status.workspaceIndexLabel': string;
@@ -231,6 +233,7 @@ export declare const zhCN: {
231
233
  'help.option.group.sync.exactOnly': string;
232
234
  'help.option.group.sync.allowStale': string;
233
235
  'help.option.group.sync.verbose': string;
236
+ 'help.option.status.json': string;
234
237
  'help.option.json': string;
235
238
  'help.option.group.impact.target': string;
236
239
  'help.option.group.impact.repo': string;
@@ -244,5 +247,6 @@ export declare const zhCN: {
244
247
  'help.option.group.contracts.type': string;
245
248
  'help.option.group.contracts.repo': string;
246
249
  'help.option.group.contracts.unmatched': string;
250
+ 'help.identityCache.environment': string;
247
251
  'help.analyze.environment': string;
248
252
  };
@@ -26,6 +26,8 @@ export const zhCN = {
26
26
  'status.indexed': '索引时间',
27
27
  'status.indexedCommit': '索引提交',
28
28
  'status.currentCommit': '当前提交',
29
+ 'status.indexRunnerIdentity': '索引记录的分析器运行身份',
30
+ 'status.currentRunnerIdentity': '当前分析器运行身份',
29
31
  'status.branch': '分支',
30
32
  'status.detached': '(分离 HEAD)',
31
33
  'status.workspaceIndexLabel': "工作区索引:最近在 '{{primary}}' 分支上分析(重新运行 gitnexus analyze 以跟随当前分支)",
@@ -231,6 +233,7 @@ export const zhCN = {
231
233
  'help.option.group.sync.exactOnly': '仅精确匹配',
232
234
  'help.option.group.sync.allowStale': '跳过过期索引警告',
233
235
  'help.option.group.sync.verbose': '显示每条跨仓库链接详情',
236
+ 'help.option.status.json': '输出机器可读的索引和分析器来源信息',
234
237
  'help.option.json': 'JSON 输出',
235
238
  'help.option.group.impact.target': '要分析的符号或文件名',
236
239
  'help.option.group.impact.repo': 'group.yaml 中的成员路径(如 app/backend),不是已索引仓库名称',
@@ -244,5 +247,6 @@ export const zhCN = {
244
247
  'help.option.group.contracts.type': '按契约类型过滤',
245
248
  'help.option.group.contracts.repo': '按仓库过滤',
246
249
  'help.option.group.contracts.unmatched': '仅显示未匹配契约',
247
- 'help.analyze.environment': '\n环境变量:\n GITNEXUS_NO_GITIGNORE=1 跳过 .gitignore 解析(仍读取 .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N 覆盖大文件跳过阈值(KB)。默认 512,最大 32768。\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker 空闲超时(毫秒)。默认 30000。\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL 自动 checkpoint 阈值(字节,默认 67108864 = 64 MiB;-1 保持 Ladybug 默认约 16 MiB)。\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker 作业字节预算。默认 8388608。\n GITNEXUS_WORKER_POOL_SIZE=N 解析 worker 数量覆盖值。默认 cores-1,最多 16。\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N 并发进行中的解析分块数。默认 2。\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N 每个 slot 丢弃前允许的最大替换进程数。默认 3。\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N 每个作业的总重试墙钟时间。默认 5 倍子批次超时。\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N 每个 slot 触发熔断的死亡次数。默认 max(3, poolSize)。\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N 线程池关闭时等待仍在原生代码中的已退役 worker 的最长时间(到达安全点后再终止,避免进程级 abort)。默认 30000。\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N C++ 捕获提取的每文件墙钟预算;超出后该文件保留部分捕获并输出警告。默认 20000。\n GITNEXUS_EMBEDDING_THREADS=N 限制 --embeddings 的本地 ONNX CPU 线程数。\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N exact-scan 回退的最大嵌入分块数。默认 10000。\n GITNEXUS_VECTOR_MAX_DISTANCE=N 语义/向量搜索接受的最大余弦距离(0 < N <= 2;超出则钳制为 2)。MCP 默认 0.6,其他路径默认 0.5。\n\n当参数和对应环境变量同时提供时,参数优先。\n\n提示:`.gitnexusignore` 支持 `.gitignore` 风格的取反。比如添加\n `!__tests__/` 可以索引默认自动过滤的目录(#771)。',
250
+ 'help.identityCache.environment': '\n分析器身份缓存:\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir\n 由操作员明确信任的持久缓存,用于跨进程快速查询状态。目录必须预先存在、位于 GitNexus 包/构建根目录之外,且路径中不得包含符号链接或 junction。缺少 POSIX 所有权 API 的平台默认保持故障关闭。',
251
+ 'help.analyze.environment': '\n环境变量:\n GITNEXUS_NO_GITIGNORE=1 跳过 .gitignore 解析(仍读取 .gitnexusignore)\n GITNEXUS_MAX_FILE_SIZE=N 覆盖大文件跳过阈值(KB)。默认 512,最大 32768。\n GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR=/absolute/protected/dir 由操作员明确信任的持久分析器身份缓存;目录必须预先存在、位于包/构建根目录之外,且路径中不得包含符号链接或 junction。\n GITNEXUS_WORKER_SUB_BATCH_TIMEOUT_MS=N Worker 空闲超时(毫秒)。默认 30000。\n GITNEXUS_WAL_CHECKPOINT_THRESHOLD=N LadybugDB WAL 自动 checkpoint 阈值(字节,默认 67108864 = 64 MiB;-1 保持 Ladybug 默认约 16 MiB)。\n GITNEXUS_WORKER_SUB_BATCH_MAX_BYTES=N Worker 作业字节预算。默认 8388608。\n GITNEXUS_WORKER_POOL_SIZE=N 解析 worker 数量覆盖值。默认 cores-1,最多 16。\n GITNEXUS_PARSE_CHUNK_CONCURRENCY=N 并发进行中的解析分块数。默认 2。\n GITNEXUS_WORKER_MAX_RESPAWNS_PER_SLOT=N 每个 slot 丢弃前允许的最大替换进程数。默认 3。\n GITNEXUS_WORKER_MAX_CUMULATIVE_TIMEOUT_MS=N 每个作业的总重试墙钟时间。默认 5 倍子批次超时。\n GITNEXUS_WORKER_CONSECUTIVE_FAILURE_THRESHOLD=N 每个 slot 触发熔断的死亡次数。默认 max(3, poolSize)。\n GITNEXUS_WORKER_SHUTDOWN_DRAIN_MS=N 线程池关闭时等待仍在原生代码中的已退役 worker 的最长时间(到达安全点后再终止,避免进程级 abort)。默认 30000。\n GITNEXUS_CPP_CAPTURE_BUDGET_MS=N C++ 捕获提取的每文件墙钟预算;超出后该文件保留部分捕获并输出警告。默认 20000。\n GITNEXUS_EMBEDDING_THREADS=N 限制 --embeddings 的本地 ONNX CPU 线程数。\n GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT=N exact-scan 回退的最大嵌入分块数。默认 10000。\n GITNEXUS_VECTOR_MAX_DISTANCE=N 语义/向量搜索接受的最大余弦距离(0 < N <= 2;超出则钳制为 2)。MCP 默认 0.6,其他路径默认 0.5。\n\n当参数和对应环境变量同时提供时,参数优先。\n\n提示:`.gitnexusignore` 支持 `.gitignore` 风格的取反。比如添加\n `!__tests__/` 可以索引默认自动过滤的目录(#771)。',
248
252
  };
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Removing it from here improves MCP server startup time significantly.
4
4
  import { Command } from 'commander';
5
5
  import { createRequire } from 'node:module';
6
- import { createLazyAction, createLbugLazyAction } from './lazy-action.js';
6
+ import { createAnalyzerLbugLazyAction, createLazyAction, createLbugLazyAction, } from './lazy-action.js';
7
7
  import { EMBEDDING_DIMS_ERROR, normalizeEmbeddingDims } from './embedding-dims.js';
8
8
  import { registerGroupCommands } from './group.js';
9
9
  import { localizeCliHelp } from './help-i18n.js';
@@ -119,7 +119,7 @@ program
119
119
  process.env.GITNEXUS_EMBEDDING_DIMS = dimsEnvBaseline;
120
120
  }
121
121
  })
122
- .action(createLbugLazyAction(() => import('./analyze.js'), 'analyzeCommand'));
122
+ .action(createAnalyzerLbugLazyAction(() => import('../core/analyzer-identity.js'), () => import('./analyze.js'), 'analyzeCommandWithRunnerIdentity', import.meta.url));
123
123
  program
124
124
  .command('index [path...]')
125
125
  .description('Register an existing .gitnexus/ folder into the global registry (no re-analysis needed)')
@@ -148,6 +148,8 @@ program
148
148
  program
149
149
  .command('status')
150
150
  .description('Show index status for current repo')
151
+ .option('--json', 'Emit machine-readable index and analyzer provenance')
152
+ .addHelpText('after', () => t('help.identityCache.environment'))
151
153
  .action(createLazyAction(() => import('./status.js'), 'statusCommand'));
152
154
  program
153
155
  .command('doctor')
@@ -5,3 +5,10 @@
5
5
  */
6
6
  export declare function createLazyAction<TModule extends Record<string, unknown>, TKey extends string & keyof TModule>(loader: () => Promise<TModule>, exportName: TKey): (...args: unknown[]) => Promise<void>;
7
7
  export declare function createLbugLazyAction<TModule extends Record<string, unknown>, TKey extends string & keyof TModule>(loader: () => Promise<TModule>, exportName: TKey): (...args: unknown[]) => Promise<void>;
8
+ /**
9
+ * Analyze-specific lazy action. Unlike the generic LadybugDB wrapper, this
10
+ * captures the complete analyzer receipt before probing/loading native code or
11
+ * evaluating the analyzer module graph. The target export receives that start
12
+ * receipt as its first argument and threads it to runFullAnalysis.
13
+ */
14
+ export declare function createAnalyzerLbugLazyAction<TModule extends Record<string, unknown>, TKey extends string & keyof TModule>(identityLoader: () => Promise<Pick<typeof import('../core/analyzer-identity.js'), 'captureAnalyzerIdentityBeforeLoad'>>, loader: () => Promise<TModule>, exportName: TKey, analyzerModuleUrl: string): (...args: unknown[]) => Promise<void>;
@@ -33,3 +33,30 @@ export function createLbugLazyAction(loader, exportName) {
33
33
  await action(...args);
34
34
  };
35
35
  }
36
+ /**
37
+ * Analyze-specific lazy action. Unlike the generic LadybugDB wrapper, this
38
+ * captures the complete analyzer receipt before probing/loading native code or
39
+ * evaluating the analyzer module graph. The target export receives that start
40
+ * receipt as its first argument and threads it to runFullAnalysis.
41
+ */
42
+ export function createAnalyzerLbugLazyAction(identityLoader, loader, exportName, analyzerModuleUrl) {
43
+ return async (...args) => {
44
+ const identityModule = await identityLoader();
45
+ const prepared = await identityModule.captureAnalyzerIdentityBeforeLoad(analyzerModuleUrl, async () => {
46
+ const check = checkLbugNative();
47
+ if (!check.ok)
48
+ return { check, module: null };
49
+ return { check, module: await loader() };
50
+ });
51
+ if (!prepared.loaded.check.ok) {
52
+ process.stderr.write(`\n ${prepared.loaded.check.message?.replace(/\n/g, '\n ')}\n\n`);
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+ const action = prepared.loaded.module?.[exportName];
57
+ if (!isCallable(action)) {
58
+ throw new Error(`Lazy action export not found: ${exportName}`);
59
+ }
60
+ await action(prepared.runnerIdentity, ...args);
61
+ };
62
+ }
@@ -37,6 +37,14 @@ interface SetupResult {
37
37
  * install paths copy this same list from hooks/claude/ (the canonical source).
38
38
  */
39
39
  export declare function copyHookHelpers(srcDir: string, destDir: string, label: string, result: SetupResult): Promise<string[]>;
40
+ export declare const RENAMED_SKILL_DIRS: Readonly<Record<string, readonly string[]>>;
41
+ /**
42
+ * Every legacy directory name superseded by a shipped rename. These no longer
43
+ * exist in the bundled skills/ source, but a pre-rename install left them
44
+ * behind in every editor target. Setup only warns about them (it cannot prove
45
+ * it owns the contents); uninstall's ownership-by-name contract removes them.
46
+ */
47
+ export declare const LEGACY_SKILL_DIR_NAMES: readonly string[];
40
48
  export declare const setupCommand: (options?: {
41
49
  codingAgent?: string[] | string;
42
50
  }) => Promise<void>;
package/dist/cli/setup.js CHANGED
@@ -22,8 +22,10 @@ const execFileAsync = promisify(execFile);
22
22
  // a config that persists in the user's editor and is invoked on every MCP
23
23
  // connect. Pinning to the installed version means subsequent invocations
24
24
  // skip the npm-registry metadata roundtrip (and stay reproducible until
25
- // the user upgrades). Static configs and READMEs intentionally use
26
- // `gitnexus@latest` since they're quickstart docs, not persisted state.
25
+ // the user upgrades). The plugin skill mcp.json are likewise pinned and
26
+ // re-stamped every release by scripts/sync-plugin-manifests.mjs (#2445),
27
+ // since they too execute `gitnexus@<version>` on connect. Only the READMEs
28
+ // stay on `gitnexus@latest` — they're quickstart docs, not executed state.
27
29
  const _require = createRequire(import.meta.url);
28
30
  const _pkg = _require('../../package.json');
29
31
  if (typeof _pkg.version !== 'string' || !_pkg.version) {
@@ -882,6 +884,16 @@ async function setupCodex(result) {
882
884
  }
883
885
  }
884
886
  // ─── Skill Installation ───────────────────────────────────────────
887
+ export const RENAMED_SKILL_DIRS = {
888
+ 'gitnexus-review': ['gitnexus-pr-review'],
889
+ };
890
+ /**
891
+ * Every legacy directory name superseded by a shipped rename. These no longer
892
+ * exist in the bundled skills/ source, but a pre-rename install left them
893
+ * behind in every editor target. Setup only warns about them (it cannot prove
894
+ * it owns the contents); uninstall's ownership-by-name contract removes them.
895
+ */
896
+ export const LEGACY_SKILL_DIR_NAMES = Object.values(RENAMED_SKILL_DIRS).flat();
885
897
  /**
886
898
  * Install GitNexus skills to a target directory.
887
899
  * Each skill is installed as {targetDir}/gitnexus-{skillName}/SKILL.md
@@ -937,15 +949,25 @@ async function installSkillsTo(targetDir) {
937
949
  if (source.isDirectory) {
938
950
  const dirSource = path.join(skillsRoot, skillName);
939
951
  await copyDirRecursive(dirSource, skillDir);
940
- installed.push(skillName);
941
952
  }
942
953
  else {
943
954
  const flatSource = path.join(skillsRoot, `${skillName}.md`);
944
955
  const content = await fs.readFile(flatSource, 'utf-8');
945
956
  await fs.mkdir(skillDir, { recursive: true });
946
957
  await fs.writeFile(path.join(skillDir, 'SKILL.md'), content, 'utf-8');
947
- installed.push(skillName);
948
958
  }
959
+ // A directory superseded by a shipped rename is warned about, never
960
+ // deleted: the installer cannot prove it owns the contents (users
961
+ // customize installed skills or hand-write their own under these
962
+ // names), so an upgrade must not destroy data.
963
+ for (const oldName of RENAMED_SKILL_DIRS[skillName] ?? []) {
964
+ const legacyDir = path.join(targetDir, oldName);
965
+ if (await dirExists(legacyDir)) {
966
+ console.log(`[gitnexus] skill "${oldName}" was renamed to "${skillName}"; ` +
967
+ `left ${legacyDir} in place — delete it manually if you have not customized it.`);
968
+ }
969
+ }
970
+ installed.push(skillName);
949
971
  }
950
972
  catch {
951
973
  // Source skill not found — skip
@@ -0,0 +1,76 @@
1
+ export type StandardSkillDistribution = 'project' | 'npm' | 'claudePlugin' | 'cursor';
2
+ export interface StandardSkillCatalogEntry {
3
+ readonly name: `gitnexus-${string}`;
4
+ readonly agentTableTask: string;
5
+ readonly fallbackDescription: string;
6
+ readonly distributions: Readonly<Record<StandardSkillDistribution, boolean>>;
7
+ }
8
+ /**
9
+ * Canonical metadata for the standard skills installed by `gitnexus analyze`.
10
+ *
11
+ * Keep distribution intent here so generated agent guidance and drift guards
12
+ * cannot disagree about which copies should exist. The ordering intentionally
13
+ * matches the generated AGENTS.md / CLAUDE.md task table.
14
+ */
15
+ export declare const STANDARD_SKILL_CATALOG: readonly [{
16
+ readonly name: "gitnexus-exploring";
17
+ readonly agentTableTask: "Understand architecture / \"How does X work?\"";
18
+ readonly fallbackDescription: "Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: \"How does X work?\", \"What calls this function?\", \"Show me the auth flow\"";
19
+ readonly distributions: {
20
+ readonly project: true;
21
+ readonly npm: true;
22
+ readonly claudePlugin: true;
23
+ readonly cursor: true;
24
+ };
25
+ }, {
26
+ readonly name: "gitnexus-impact-analysis";
27
+ readonly agentTableTask: "Blast radius / \"What breaks if I change X?\"";
28
+ readonly fallbackDescription: "Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: \"Is it safe to change X?\", \"What depends on this?\", \"What will break?\"";
29
+ readonly distributions: {
30
+ readonly project: true;
31
+ readonly npm: true;
32
+ readonly claudePlugin: true;
33
+ readonly cursor: true;
34
+ };
35
+ }, {
36
+ readonly name: "gitnexus-debugging";
37
+ readonly agentTableTask: "Trace bugs / \"Why is X failing?\"";
38
+ readonly fallbackDescription: "Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where does this error come from?\", \"Trace this bug\"";
39
+ readonly distributions: {
40
+ readonly project: true;
41
+ readonly npm: true;
42
+ readonly claudePlugin: true;
43
+ readonly cursor: true;
44
+ };
45
+ }, {
46
+ readonly name: "gitnexus-refactoring";
47
+ readonly agentTableTask: "Rename / extract / split / refactor";
48
+ readonly fallbackDescription: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\"";
49
+ readonly distributions: {
50
+ readonly project: true;
51
+ readonly npm: true;
52
+ readonly claudePlugin: true;
53
+ readonly cursor: true;
54
+ };
55
+ }, {
56
+ readonly name: "gitnexus-guide";
57
+ readonly agentTableTask: "Tools, resources, schema reference";
58
+ readonly fallbackDescription: "Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: \"What GitNexus tools are available?\", \"How do I use GitNexus?\"";
59
+ readonly distributions: {
60
+ readonly project: true;
61
+ readonly npm: true;
62
+ readonly claudePlugin: true;
63
+ readonly cursor: false;
64
+ };
65
+ }, {
66
+ readonly name: "gitnexus-cli";
67
+ readonly agentTableTask: "Index, status, clean, wiki CLI commands";
68
+ readonly fallbackDescription: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\"";
69
+ readonly distributions: {
70
+ readonly project: true;
71
+ readonly npm: true;
72
+ readonly claudePlugin: true;
73
+ readonly cursor: false;
74
+ };
75
+ }];
76
+ export type StandardSkillName = (typeof STANDARD_SKILL_CATALOG)[number]['name'];