gitnexus 1.6.8-rc.5 → 1.6.8-rc.7

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.
@@ -78,6 +78,12 @@ const KEY_SPECS = {
78
78
  embeddingBatchSize: { target: 'embeddingBatchSize', kind: 'numeric-string' },
79
79
  embeddingSubBatchSize: { target: 'embeddingSubBatchSize', kind: 'numeric-string' },
80
80
  embeddingDevice: { target: 'embeddingDevice', kind: 'string' },
81
+ // #1589/#1852 residual — extra fetch-wrapper function names to treat as HTTP
82
+ // consumers. The auto-detector only flags functions that call the bare global
83
+ // `fetch()`; a wrapper built on axios / a custom client, or named outside the
84
+ // built-in convention set, is otherwise invisible to route_map consumers.
85
+ // Listing it here adds it to the cross-file consumer scan.
86
+ fetchWrappers: { target: 'fetchWrappers', kind: 'string-array' },
81
87
  };
82
88
  /** Top-level container key for the nested form; not itself an `AnalyzeOptions` field. */
83
89
  const NESTED_KEY = 'analyze';
@@ -195,6 +201,39 @@ const normalizeValue = (kind, value, key) => {
195
201
  }
196
202
  return trimmed;
197
203
  }
204
+ case 'string-array': {
205
+ // Generic shared validator — `source` already names the config key, so
206
+ // messages here stay key-agnostic (no fetch-wrapper coupling in the
207
+ // shared normalizer; #1589/#1852 review F7).
208
+ if (!Array.isArray(value)) {
209
+ throw new GitNexusRcError(`${source} must be an array of strings.`);
210
+ }
211
+ const names = [];
212
+ for (const item of value) {
213
+ if (typeof item !== 'string') {
214
+ throw new GitNexusRcError(`${source} entries must all be strings.`);
215
+ }
216
+ const trimmed = item.trim();
217
+ if (!trimmed) {
218
+ throw new GitNexusRcError(`${source} entries must not be empty.`);
219
+ }
220
+ assertNoHiddenChars(trimmed, source);
221
+ // Values may be interpolated into a RegExp downstream. Restrict to
222
+ // identifier / member-access shapes so a config value can never smuggle
223
+ // regex metacharacters into a consumer.
224
+ if (!/^[A-Za-z_$][A-Za-z0-9_$.]*$/.test(trimmed)) {
225
+ throw new GitNexusRcError(`${source} entry "${trimmed}" must be an identifier or member name ` +
226
+ `(letters, digits, _, $, . — e.g. "client.get").`);
227
+ }
228
+ names.push(trimmed);
229
+ }
230
+ if (names.length === 0) {
231
+ throw new GitNexusRcError(`${source} must list at least one string.`);
232
+ }
233
+ // De-duplicate and cap to a sane bound so a pathological config cannot
234
+ // blow up the consumer scan's alternation.
235
+ return Array.from(new Set(names)).slice(0, 100);
236
+ }
198
237
  case 'numeric-string': {
199
238
  // Mirror Commander's contract: these options reach the existing CLI
200
239
  // validation as strings. Accept a JSON number or a string; normalize to a
@@ -58,6 +58,14 @@ export interface AnalyzeOptions {
58
58
  * before being threaded into the generated AGENTS.md / CLAUDE.md content.
59
59
  */
60
60
  defaultBranch?: string;
61
+ /**
62
+ * Index-branch selector (#2106). From `--branch`. Distinct from
63
+ * `defaultBranch` (cosmetic base_ref): this routes the index to a per-branch
64
+ * slot. NOT sourced from `.gitnexusrc` — the `.gitnexusrc` `branch` key is an
65
+ * alias for `defaultBranch` and must not change index placement. Defaults to
66
+ * the checked-out branch inside `runFullAnalysis` when omitted.
67
+ */
68
+ branch?: string;
61
69
  /** Pure index mode: skip all file injection (AGENTS.md, CLAUDE.md, skills). */
62
70
  indexOnly?: boolean;
63
71
  /** Index the folder even when no .git directory is present. */
@@ -93,6 +101,14 @@ export interface AnalyzeOptions {
93
101
  embeddingBatchSize?: string;
94
102
  embeddingSubBatchSize?: string;
95
103
  embeddingDevice?: string;
104
+ /**
105
+ * Extra fetch-wrapper function names to treat as HTTP consumers (#1589/#1852
106
+ * residual). Supplied via `.gitnexusrc` `fetchWrappers: [...]`. Threaded into
107
+ * the routes phase, where the cross-file consumer scan unions them with the
108
+ * auto-detected `fetch()` wrappers so a custom/axios-based wrapper named
109
+ * outside the built-in convention still produces `route_map` consumers.
110
+ */
111
+ fetchWrappers?: string[];
96
112
  }
97
113
  /**
98
114
  * Whether the post-index skill step should run.
@@ -551,6 +551,21 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
551
551
  return;
552
552
  }
553
553
  }
554
+ // Validate the index-branch selector (#2106) the same way, so a malformed
555
+ // `--branch` exits before any expensive analysis starts. Capture the TRIMMED
556
+ // return so a whitespace-padded value (e.g. " feature" from shell completion)
557
+ // normalizes before the checked-out-branch mismatch guard and slug — otherwise
558
+ // it would false-reject on-branch or create a ghost index when detached.
559
+ if (cliOptions?.branch !== undefined) {
560
+ try {
561
+ cliOptions.branch = validateBranchName(cliOptions.branch, '--branch');
562
+ }
563
+ catch (err) {
564
+ cliError(` ${err instanceof Error ? err.message : String(err)}\n`);
565
+ process.exitCode = 1;
566
+ return;
567
+ }
568
+ }
554
569
  // ── Load .gitnexusrc and merge: CLI flags override config (#243) ───
555
570
  // Parse/validate before the progress bar so a malformed config produces an
556
571
  // actionable error and exits before any expensive analysis starts.
@@ -829,6 +844,10 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
829
844
  // Resolved default branch (CLI > .gitnexusrc > auto-detect > "main")
830
845
  // threaded into the generated regression-compare example (#243).
831
846
  defaultBranch: resolvedDefaultBranch,
847
+ // Index-branch selector (#2106). Read straight from the CLI flag (not
848
+ // the .gitnexusrc-merged options) so the cosmetic defaultBranch config
849
+ // can never change index placement. Undefined → auto-detect in pipeline.
850
+ branch: cliOptions?.branch,
832
851
  // commander.js `.option('--no-stats', …)` registers the flag as
833
852
  // `options.stats` (boolean, default true; `false` when the user
834
853
  // passed --no-stats). Reading `options.noStats` here returns
@@ -845,6 +864,9 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
845
864
  // GITNEXUS_WORKER_POOL_SIZE env mutation. `undefined` defers to the
846
865
  // env / auto-formula fallback inside the pipeline.
847
866
  workerPoolSize,
867
+ // Extra fetch-wrapper names from `.gitnexusrc` (#1589/#1852 residual);
868
+ // forwarded to the routes phase consumer scan.
869
+ fetchWrappers: options.fetchWrappers,
848
870
  }, {
849
871
  onProgress: (_phase, percent, message) => {
850
872
  updateBar(percent, message);
@@ -863,13 +885,19 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
863
885
  // preserving the rest of the block (incl. --skills community rows). No-op
864
886
  // when the value already matches, so a routine up-to-date run is silent
865
887
  // (#1996 tri-review P2).
888
+ // Only refresh the repo-root AGENTS.md/CLAUDE.md base_ref for the
889
+ // PRIMARY/flat index (#2106 R2). A non-primary branch's up-to-date
890
+ // analyze must not churn the committed AGENTS.md — this mirrors the
891
+ // in-pipeline `if (!placement.branch)` gate around generateAIContextFiles.
866
892
  let baseRefRefreshed = [];
867
- try {
868
- const { refreshBaseRefLine } = await import('./ai-context.js');
869
- baseRefRefreshed = (await refreshBaseRefLine(repoPath, resolvedDefaultBranch, { skipAgentsMd })).files;
870
- }
871
- catch {
872
- /* best-effort — never fail the fast path over a context refresh */
893
+ if (result.isPrimaryBranch !== false) {
894
+ try {
895
+ const { refreshBaseRefLine } = await import('./ai-context.js');
896
+ baseRefRefreshed = (await refreshBaseRefLine(repoPath, resolvedDefaultBranch, { skipAgentsMd })).files;
897
+ }
898
+ catch {
899
+ /* best-effort — never fail the fast path over a context refresh */
900
+ }
873
901
  }
874
902
  clearInterval(elapsedTimer);
875
903
  process.removeListener('SIGINT', sigintHandler);
@@ -8,4 +8,5 @@ export declare const cleanCommand: (options?: {
8
8
  force?: boolean;
9
9
  all?: boolean;
10
10
  lbugSidecars?: boolean;
11
+ branch?: string;
11
12
  }) => Promise<void>;
package/dist/cli/clean.js CHANGED
@@ -7,10 +7,52 @@
7
7
  import fs from 'fs/promises';
8
8
  import path from 'path';
9
9
  import { logger } from '../core/logger.js';
10
- import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, UnsafeStoragePathError, } from '../storage/repo-manager.js';
10
+ import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, getStoragePaths, removeBranchIndex, UnsafeStoragePathError, } from '../storage/repo-manager.js';
11
11
  import { cleanQuarantinedMissingShadowWals, inspectLbugSidecars, listQuarantinedMissingShadowWals, } from '../core/lbug/sidecar-recovery.js';
12
12
  import { t } from './i18n/index.js';
13
13
  export const cleanCommand = async (options) => {
14
+ // --branch <name>: remove a single non-primary branch's index (#2106 R7).
15
+ // Resolve against the RECORDED branches[] summary (never by slugging the
16
+ // user's raw input, which can disagree with the index-time-sanitized label).
17
+ if (options?.branch) {
18
+ const cwd = process.cwd();
19
+ const repo = await findRepo(cwd);
20
+ if (!repo) {
21
+ console.log(t('clean.notFoundHere'));
22
+ return;
23
+ }
24
+ const entries = await listRegisteredRepos();
25
+ const entry = entries.find((e) => path.resolve(e.path) === path.resolve(repo.repoPath));
26
+ const summary = entry?.branches?.find((b) => b.branch === options.branch);
27
+ if (!summary) {
28
+ console.log(t('clean.branchNotIndexed', { branch: options.branch }));
29
+ return;
30
+ }
31
+ const { storagePath, lbugPath } = getStoragePaths(repo.repoPath, summary.branch);
32
+ const branchDir = path.dirname(lbugPath);
33
+ // Safety guard: the target MUST live under <repo>/.gitnexus/branches/.
34
+ // assertSafeStoragePath only validates the flat `<repo>/.gitnexus`, so this
35
+ // is a dedicated branches-sub-dir check before any destructive fs.rm.
36
+ const branchesRoot = path.join(storagePath, 'branches') + path.sep;
37
+ if (!branchDir.startsWith(branchesRoot)) {
38
+ logger.error(`Refusing to clean branch index outside .gitnexus/branches: ${branchDir}`);
39
+ return;
40
+ }
41
+ if (!options.force) {
42
+ console.log(t('clean.deleteBranch', { branch: summary.branch, path: branchDir }));
43
+ console.log(`\n${t('common.runForceConfirm')}`);
44
+ return;
45
+ }
46
+ try {
47
+ await fs.rm(branchDir, { recursive: true, force: true });
48
+ await removeBranchIndex(repo.repoPath, summary.branch);
49
+ console.log(t('clean.deletedBranch', { branch: summary.branch }));
50
+ }
51
+ catch (err) {
52
+ logger.error({ err }, 'Failed to delete branch index:');
53
+ }
54
+ return;
55
+ }
14
56
  if (options?.lbugSidecars) {
15
57
  const cwd = process.cwd();
16
58
  const repo = await findRepo(cwd);
@@ -155,7 +155,43 @@ export function formatImpactResult(result) {
155
155
  const direction = result.direction;
156
156
  const byDepth = result.byDepth || {};
157
157
  const total = result.impactedCount || 0;
158
+ // #2129 — an ambiguous bare name must not print the "isolated / safe to
159
+ // refactor" headline. Surface the per-candidate blast radius + the maximum,
160
+ // mirroring formatContextResult, so the real impact under whichever symbol the
161
+ // caller meant is visible on the text surface, not just in the JSON.
162
+ if (result.status === 'ambiguous') {
163
+ // #2129 review F11 — report the FULL match count (`totalCandidates`), not the
164
+ // truncated `candidates[]` length; note when the candidate list is capped.
165
+ const shown = result.candidates?.length ?? 0;
166
+ const total = result.totalCandidates ?? shown;
167
+ const countPhrase = total > shown ? `${total} symbols (showing ${shown})` : `${total} symbols`;
168
+ const lines = [
169
+ `${target?.name || '?'}: AMBIGUOUS — ${countPhrase} share this name. ` +
170
+ `Max blast radius ${result.maxImpactedCount ?? 0} (${result.maxRisk ?? 'UNKNOWN'} risk). ` +
171
+ `Disambiguate with --uid for one authoritative result:`,
172
+ ];
173
+ for (const c of result.candidates || []) {
174
+ lines.push(` ${c.kind} ${c.name} → ${c.filePath}:${c.line || '?'} ` +
175
+ `[${c.impactedCount ?? 0} ${direction}, risk ${c.risk ?? 'UNKNOWN'}] (uid: ${c.uid})`);
176
+ }
177
+ // #2129 review F1 — a failed per-candidate probe makes the max a lower bound.
178
+ if (result.partialProbe) {
179
+ lines.push(' ⚠️ One or more candidate probes failed — max blast radius / risk are lower bounds.');
180
+ }
181
+ return lines.join('\n');
182
+ }
158
183
  if (total === 0) {
184
+ // #1858 — "isolated" is a confident claim. If an interface / indirection
185
+ // boundary is on the path, the true count is a lower bound, not zero;
186
+ // callers binding via DI / dynamic dispatch were not traced. Say so instead.
187
+ if (result.epistemic === 'lower-bound') {
188
+ const lines = [
189
+ `${target?.name || '?'}: no direct ${direction} dependencies traced, but this is a LOWER BOUND — unresolved indirection on the path (actual impact may be higher):`,
190
+ ];
191
+ for (const b of result.boundaries || [])
192
+ lines.push(` • ${b}`);
193
+ return lines.join('\n');
194
+ }
159
195
  return `${target?.name || '?'}: No ${direction} dependencies found. This symbol appears isolated.`;
160
196
  }
161
197
  const lines = [];
@@ -164,6 +200,13 @@ export function formatImpactResult(result) {
164
200
  if (result.partial) {
165
201
  lines.push('⚠️ Partial results — graph traversal was interrupted. Deeper impacts may exist.');
166
202
  }
203
+ // #1858 — an interface / indirection boundary on the path makes this a lower
204
+ // bound; surface it so the count is not read as exhaustive.
205
+ if (result.epistemic === 'lower-bound') {
206
+ lines.push('⚠️ Lower bound — unresolved indirection on the path (callers binding via DI / dynamic dispatch are not traced; actual impact may be higher):');
207
+ for (const b of result.boundaries || [])
208
+ lines.push(` • ${b}`);
209
+ }
167
210
  lines.push('');
168
211
  const depthLabels = {
169
212
  1: 'WILL BREAK (direct)',
@@ -69,6 +69,7 @@ const OPTION_DESCRIPTION_KEYS = {
69
69
  'uninstall|-f, --force': 'help.option.uninstall.force',
70
70
  'clean|-f, --force': 'help.option.force.confirmation',
71
71
  'clean|--all': 'help.option.clean.all',
72
+ 'clean|--branch <name>': 'help.option.clean.branch',
72
73
  'clean|--lbug-sidecars': 'help.option.clean.lbugSidecars',
73
74
  'remove|-f, --force': 'help.option.force.confirmation',
74
75
  'wiki|-f, --force': 'help.option.wiki.force',
@@ -89,16 +90,19 @@ const OPTION_DESCRIPTION_KEYS = {
89
90
  'publish|--id <owner/repo>': 'help.option.publish.id',
90
91
  'publish|--skip-git': 'help.option.skipGit',
91
92
  'query|-r, --repo <name>': 'help.option.repo.targetOmitOne',
93
+ 'query|--branch <name>': 'help.option.branch',
92
94
  'query|-c, --context <text>': 'help.option.query.context',
93
95
  'query|-g, --goal <text>': 'help.option.query.goal',
94
96
  'query|-l, --limit <n>': 'help.option.query.limit',
95
97
  'query|--content': 'help.option.content',
96
98
  'context|-r, --repo <name>': 'help.option.repo.target',
99
+ 'context|--branch <name>': 'help.option.branch',
97
100
  'context|-u, --uid <uid>': 'help.option.context.uid',
98
101
  'context|-f, --file <path>': 'help.option.context.file',
99
102
  'context|--content': 'help.option.content',
100
103
  'impact|-d, --direction <dir>': 'help.option.impact.direction',
101
104
  'impact|-r, --repo <name>': 'help.option.repo.target',
105
+ 'impact|--branch <name>': 'help.option.branch',
102
106
  'impact|-u, --uid <uid>': 'help.option.context.uid',
103
107
  'impact|-f, --file <path>': 'help.option.context.file',
104
108
  'impact|--kind <kind>': 'help.option.impact.kind',
@@ -108,9 +112,11 @@ const OPTION_DESCRIPTION_KEYS = {
108
112
  'impact|--offset <n>': 'help.option.impact.offset',
109
113
  'impact|--summary-only': 'help.option.impact.summaryOnly',
110
114
  'cypher|-r, --repo <name>': 'help.option.repo.target',
115
+ 'cypher|--branch <name>': 'help.option.branch',
111
116
  'detect-changes|-s, --scope <scope>': 'help.option.detectChanges.scope',
112
117
  'detect-changes|-b, --base-ref <ref>': 'help.option.detectChanges.baseRef',
113
118
  'detect-changes|-r, --repo <name>': 'help.option.repo.target',
119
+ 'detect-changes|--branch <name>': 'help.option.branch',
114
120
  'eval-server|-p, --port <port>': 'help.option.port',
115
121
  'eval-server|--host <host>': 'help.option.evalServer.host',
116
122
  'eval-server|--idle-timeout <seconds>': 'help.option.evalServer.idleTimeout',
@@ -10,6 +10,9 @@ export declare const en: {
10
10
  readonly 'list.title': "Indexed Repositories ({{count}})";
11
11
  readonly 'list.indexed': "Indexed";
12
12
  readonly 'list.commit': "Commit";
13
+ readonly 'list.branch': "Branch";
14
+ readonly 'list.branchIndexes': "Branch indexes";
15
+ readonly 'list.branchLine': "{{branch}} ({{commit}}, {{indexed}})";
13
16
  readonly 'list.stats': "Stats";
14
17
  readonly 'list.statsValue': "{{files}} files, {{symbols}} symbols, {{edges}} edges";
15
18
  readonly 'list.clusters': "Clusters";
@@ -23,6 +26,9 @@ export declare const en: {
23
26
  readonly 'status.indexed': "Indexed";
24
27
  readonly 'status.indexedCommit': "Indexed commit";
25
28
  readonly 'status.currentCommit': "Current commit";
29
+ readonly 'status.branch': "Branch";
30
+ readonly 'status.detached': "(detached HEAD)";
31
+ readonly 'status.branchNotIndexed': "⚠️ current branch not indexed (primary index is for '{{primary}}'; run gitnexus analyze)";
26
32
  readonly 'status.status': "Status";
27
33
  readonly 'status.upToDate': "✅ up-to-date";
28
34
  readonly 'status.stale': "⚠️ stale (re-run gitnexus analyze)";
@@ -30,6 +36,9 @@ export declare const en: {
30
36
  readonly 'clean.deletedRepo': "Deleted: {{name}} ({{storagePath}})";
31
37
  readonly 'clean.notFoundHere': "No indexed repository found in this directory.";
32
38
  readonly 'clean.deleteCurrent': "This will delete the GitNexus index for: {{repoName}}";
39
+ readonly 'clean.branchNotIndexed': "No indexed branch named \"{{branch}}\" for this repository.";
40
+ readonly 'clean.deleteBranch': "This will delete the branch index \"{{branch}}\" at: {{path}}";
41
+ readonly 'clean.deletedBranch': "Deleted branch index: {{branch}}";
33
42
  readonly 'clean.lbugSidecars.state': "LadybugDB sidecar state: {{state}}";
34
43
  readonly 'clean.lbugSidecars.none': "No quarantined LadybugDB missing-shadow WAL sidecars found.";
35
44
  readonly 'clean.lbugSidecars.preview': "This will delete {{count}} quarantined LadybugDB missing-shadow WAL sidecar(s):";
@@ -156,6 +165,7 @@ export declare const en: {
156
165
  readonly 'help.option.force.confirmation': "Skip confirmation prompt";
157
166
  readonly 'help.option.uninstall.force': "Apply the changes (default is a dry-run preview)";
158
167
  readonly 'help.option.clean.all': "Clean all indexed repos";
168
+ readonly 'help.option.clean.branch': "Delete only the named branch index (not the primary)";
159
169
  readonly 'help.option.clean.lbugSidecars': "Clean quarantined LadybugDB missing-shadow WAL sidecars";
160
170
  readonly 'help.option.wiki.force': "Force full regeneration even if up to date";
161
171
  readonly 'help.option.wiki.provider': "LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)";
@@ -178,6 +188,7 @@ export declare const en: {
178
188
  readonly 'help.option.query.limit': "Max processes to return (default: 5)";
179
189
  readonly 'help.option.content': "Include full symbol source code";
180
190
  readonly 'help.option.repo.target': "Target repository";
191
+ readonly 'help.option.branch': "Scope to a specific branch index (multi-branch repos)";
181
192
  readonly 'help.option.context.uid': "Direct symbol UID (zero-ambiguity lookup)";
182
193
  readonly 'help.option.context.file': "File path to disambiguate common names";
183
194
  readonly 'help.option.impact.kind': "Kind filter to disambiguate common names (e.g. Function, Class, Method)";
@@ -10,6 +10,9 @@ export const en = {
10
10
  'list.title': 'Indexed Repositories ({{count}})',
11
11
  'list.indexed': 'Indexed',
12
12
  'list.commit': 'Commit',
13
+ 'list.branch': 'Branch',
14
+ 'list.branchIndexes': 'Branch indexes',
15
+ 'list.branchLine': '{{branch}} ({{commit}}, {{indexed}})',
13
16
  'list.stats': 'Stats',
14
17
  'list.statsValue': '{{files}} files, {{symbols}} symbols, {{edges}} edges',
15
18
  'list.clusters': 'Clusters',
@@ -23,6 +26,9 @@ export const en = {
23
26
  'status.indexed': 'Indexed',
24
27
  'status.indexedCommit': 'Indexed commit',
25
28
  'status.currentCommit': 'Current commit',
29
+ 'status.branch': 'Branch',
30
+ 'status.detached': '(detached HEAD)',
31
+ 'status.branchNotIndexed': "⚠️ current branch not indexed (primary index is for '{{primary}}'; run gitnexus analyze)",
26
32
  'status.status': 'Status',
27
33
  'status.upToDate': '✅ up-to-date',
28
34
  'status.stale': '⚠️ stale (re-run gitnexus analyze)',
@@ -30,6 +36,9 @@ export const en = {
30
36
  'clean.deletedRepo': 'Deleted: {{name}} ({{storagePath}})',
31
37
  'clean.notFoundHere': 'No indexed repository found in this directory.',
32
38
  'clean.deleteCurrent': 'This will delete the GitNexus index for: {{repoName}}',
39
+ 'clean.branchNotIndexed': 'No indexed branch named "{{branch}}" for this repository.',
40
+ 'clean.deleteBranch': 'This will delete the branch index "{{branch}}" at: {{path}}',
41
+ 'clean.deletedBranch': 'Deleted branch index: {{branch}}',
33
42
  'clean.lbugSidecars.state': 'LadybugDB sidecar state: {{state}}',
34
43
  'clean.lbugSidecars.none': 'No quarantined LadybugDB missing-shadow WAL sidecars found.',
35
44
  'clean.lbugSidecars.preview': 'This will delete {{count}} quarantined LadybugDB missing-shadow WAL sidecar(s):',
@@ -156,6 +165,7 @@ export const en = {
156
165
  'help.option.force.confirmation': 'Skip confirmation prompt',
157
166
  'help.option.uninstall.force': 'Apply the changes (default is a dry-run preview)',
158
167
  'help.option.clean.all': 'Clean all indexed repos',
168
+ 'help.option.clean.branch': 'Delete only the named branch index (not the primary)',
159
169
  'help.option.clean.lbugSidecars': 'Clean quarantined LadybugDB missing-shadow WAL sidecars',
160
170
  'help.option.wiki.force': 'Force full regeneration even if up to date',
161
171
  'help.option.wiki.provider': 'LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)',
@@ -178,6 +188,7 @@ export const en = {
178
188
  'help.option.query.limit': 'Max processes to return (default: 5)',
179
189
  'help.option.content': 'Include full symbol source code',
180
190
  'help.option.repo.target': 'Target repository',
191
+ 'help.option.branch': 'Scope to a specific branch index (multi-branch repos)',
181
192
  'help.option.context.uid': 'Direct symbol UID (zero-ambiguity lookup)',
182
193
  'help.option.context.file': 'File path to disambiguate common names',
183
194
  'help.option.impact.kind': 'Kind filter to disambiguate common names (e.g. Function, Class, Method)',
@@ -11,6 +11,9 @@ export declare const cliResources: {
11
11
  readonly 'list.title': "Indexed Repositories ({{count}})";
12
12
  readonly 'list.indexed': "Indexed";
13
13
  readonly 'list.commit': "Commit";
14
+ readonly 'list.branch': "Branch";
15
+ readonly 'list.branchIndexes': "Branch indexes";
16
+ readonly 'list.branchLine': "{{branch}} ({{commit}}, {{indexed}})";
14
17
  readonly 'list.stats': "Stats";
15
18
  readonly 'list.statsValue': "{{files}} files, {{symbols}} symbols, {{edges}} edges";
16
19
  readonly 'list.clusters': "Clusters";
@@ -24,6 +27,9 @@ export declare const cliResources: {
24
27
  readonly 'status.indexed': "Indexed";
25
28
  readonly 'status.indexedCommit': "Indexed commit";
26
29
  readonly 'status.currentCommit': "Current commit";
30
+ readonly 'status.branch': "Branch";
31
+ readonly 'status.detached': "(detached HEAD)";
32
+ readonly 'status.branchNotIndexed': "⚠️ current branch not indexed (primary index is for '{{primary}}'; run gitnexus analyze)";
27
33
  readonly 'status.status': "Status";
28
34
  readonly 'status.upToDate': "✅ up-to-date";
29
35
  readonly 'status.stale': "⚠️ stale (re-run gitnexus analyze)";
@@ -31,6 +37,9 @@ export declare const cliResources: {
31
37
  readonly 'clean.deletedRepo': "Deleted: {{name}} ({{storagePath}})";
32
38
  readonly 'clean.notFoundHere': "No indexed repository found in this directory.";
33
39
  readonly 'clean.deleteCurrent': "This will delete the GitNexus index for: {{repoName}}";
40
+ readonly 'clean.branchNotIndexed': "No indexed branch named \"{{branch}}\" for this repository.";
41
+ readonly 'clean.deleteBranch': "This will delete the branch index \"{{branch}}\" at: {{path}}";
42
+ readonly 'clean.deletedBranch': "Deleted branch index: {{branch}}";
34
43
  readonly 'clean.lbugSidecars.state': "LadybugDB sidecar state: {{state}}";
35
44
  readonly 'clean.lbugSidecars.none': "No quarantined LadybugDB missing-shadow WAL sidecars found.";
36
45
  readonly 'clean.lbugSidecars.preview': "This will delete {{count}} quarantined LadybugDB missing-shadow WAL sidecar(s):";
@@ -157,6 +166,7 @@ export declare const cliResources: {
157
166
  readonly 'help.option.force.confirmation': "Skip confirmation prompt";
158
167
  readonly 'help.option.uninstall.force': "Apply the changes (default is a dry-run preview)";
159
168
  readonly 'help.option.clean.all': "Clean all indexed repos";
169
+ readonly 'help.option.clean.branch': "Delete only the named branch index (not the primary)";
160
170
  readonly 'help.option.clean.lbugSidecars': "Clean quarantined LadybugDB missing-shadow WAL sidecars";
161
171
  readonly 'help.option.wiki.force': "Force full regeneration even if up to date";
162
172
  readonly 'help.option.wiki.provider': "LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)";
@@ -179,6 +189,7 @@ export declare const cliResources: {
179
189
  readonly 'help.option.query.limit': "Max processes to return (default: 5)";
180
190
  readonly 'help.option.content': "Include full symbol source code";
181
191
  readonly 'help.option.repo.target': "Target repository";
192
+ readonly 'help.option.branch': "Scope to a specific branch index (multi-branch repos)";
182
193
  readonly 'help.option.context.uid': "Direct symbol UID (zero-ambiguity lookup)";
183
194
  readonly 'help.option.context.file': "File path to disambiguate common names";
184
195
  readonly 'help.option.impact.kind': "Kind filter to disambiguate common names (e.g. Function, Class, Method)";
@@ -224,6 +235,9 @@ export declare const cliResources: {
224
235
  'list.title': string;
225
236
  'list.indexed': string;
226
237
  'list.commit': string;
238
+ 'list.branch': string;
239
+ 'list.branchIndexes': string;
240
+ 'list.branchLine': string;
227
241
  'list.stats': string;
228
242
  'list.statsValue': string;
229
243
  'list.clusters': string;
@@ -237,6 +251,9 @@ export declare const cliResources: {
237
251
  'status.indexed': string;
238
252
  'status.indexedCommit': string;
239
253
  'status.currentCommit': string;
254
+ 'status.branch': string;
255
+ 'status.detached': string;
256
+ 'status.branchNotIndexed': string;
240
257
  'status.status': string;
241
258
  'status.upToDate': string;
242
259
  'status.stale': string;
@@ -244,6 +261,9 @@ export declare const cliResources: {
244
261
  'clean.deletedRepo': string;
245
262
  'clean.notFoundHere': string;
246
263
  'clean.deleteCurrent': string;
264
+ 'clean.branchNotIndexed': string;
265
+ 'clean.deleteBranch': string;
266
+ 'clean.deletedBranch': string;
247
267
  'clean.lbugSidecars.state': string;
248
268
  'clean.lbugSidecars.none': string;
249
269
  'clean.lbugSidecars.preview': string;
@@ -370,6 +390,7 @@ export declare const cliResources: {
370
390
  'help.option.force.confirmation': string;
371
391
  'help.option.uninstall.force': string;
372
392
  'help.option.clean.all': string;
393
+ 'help.option.clean.branch': string;
373
394
  'help.option.clean.lbugSidecars': string;
374
395
  'help.option.wiki.force': string;
375
396
  'help.option.wiki.provider': string;
@@ -392,6 +413,7 @@ export declare const cliResources: {
392
413
  'help.option.query.limit': string;
393
414
  'help.option.content': string;
394
415
  'help.option.repo.target': string;
416
+ 'help.option.branch': string;
395
417
  'help.option.context.uid': string;
396
418
  'help.option.context.file': string;
397
419
  'help.option.impact.kind': string;
@@ -10,6 +10,9 @@ export declare const zhCN: {
10
10
  'list.title': string;
11
11
  'list.indexed': string;
12
12
  'list.commit': string;
13
+ 'list.branch': string;
14
+ 'list.branchIndexes': string;
15
+ 'list.branchLine': string;
13
16
  'list.stats': string;
14
17
  'list.statsValue': string;
15
18
  'list.clusters': string;
@@ -23,6 +26,9 @@ export declare const zhCN: {
23
26
  'status.indexed': string;
24
27
  'status.indexedCommit': string;
25
28
  'status.currentCommit': string;
29
+ 'status.branch': string;
30
+ 'status.detached': string;
31
+ 'status.branchNotIndexed': string;
26
32
  'status.status': string;
27
33
  'status.upToDate': string;
28
34
  'status.stale': string;
@@ -30,6 +36,9 @@ export declare const zhCN: {
30
36
  'clean.deletedRepo': string;
31
37
  'clean.notFoundHere': string;
32
38
  'clean.deleteCurrent': string;
39
+ 'clean.branchNotIndexed': string;
40
+ 'clean.deleteBranch': string;
41
+ 'clean.deletedBranch': string;
33
42
  'clean.lbugSidecars.state': string;
34
43
  'clean.lbugSidecars.none': string;
35
44
  'clean.lbugSidecars.preview': string;
@@ -156,6 +165,7 @@ export declare const zhCN: {
156
165
  'help.option.force.confirmation': string;
157
166
  'help.option.uninstall.force': string;
158
167
  'help.option.clean.all': string;
168
+ 'help.option.clean.branch': string;
159
169
  'help.option.clean.lbugSidecars': string;
160
170
  'help.option.wiki.force': string;
161
171
  'help.option.wiki.provider': string;
@@ -178,6 +188,7 @@ export declare const zhCN: {
178
188
  'help.option.query.limit': string;
179
189
  'help.option.content': string;
180
190
  'help.option.repo.target': string;
191
+ 'help.option.branch': string;
181
192
  'help.option.context.uid': string;
182
193
  'help.option.context.file': string;
183
194
  'help.option.impact.kind': string;
@@ -10,6 +10,9 @@ export const zhCN = {
10
10
  'list.title': '已索引仓库({{count}})',
11
11
  'list.indexed': '索引时间',
12
12
  'list.commit': '提交',
13
+ 'list.branch': '分支',
14
+ 'list.branchIndexes': '分支索引',
15
+ 'list.branchLine': '{{branch}}({{commit}},{{indexed}})',
13
16
  'list.stats': '统计',
14
17
  'list.statsValue': '{{files}} 个文件,{{symbols}} 个符号,{{edges}} 条边',
15
18
  'list.clusters': '聚类',
@@ -23,6 +26,9 @@ export const zhCN = {
23
26
  'status.indexed': '索引时间',
24
27
  'status.indexedCommit': '索引提交',
25
28
  'status.currentCommit': '当前提交',
29
+ 'status.branch': '分支',
30
+ 'status.detached': '(分离 HEAD)',
31
+ 'status.branchNotIndexed': "⚠️ 当前分支未索引(主索引对应 '{{primary}}';请运行 gitnexus analyze)",
26
32
  'status.status': '状态',
27
33
  'status.upToDate': '✅ 已是最新',
28
34
  'status.stale': '⚠️ 已过期(重新运行 gitnexus analyze)',
@@ -30,6 +36,9 @@ export const zhCN = {
30
36
  'clean.deletedRepo': '已删除:{{name}}({{storagePath}})',
31
37
  'clean.notFoundHere': '当前目录未找到已索引仓库。',
32
38
  'clean.deleteCurrent': '将删除该仓库的 GitNexus 索引:{{repoName}}',
39
+ 'clean.branchNotIndexed': '该仓库没有名为 “{{branch}}” 的已索引分支。',
40
+ 'clean.deleteBranch': '将删除分支索引 “{{branch}}”,路径:{{path}}',
41
+ 'clean.deletedBranch': '已删除分支索引:{{branch}}',
33
42
  'clean.lbugSidecars.state': 'LadybugDB sidecar 状态:{{state}}',
34
43
  'clean.lbugSidecars.none': '未找到已隔离的 LadybugDB missing-shadow WAL sidecar。',
35
44
  'clean.lbugSidecars.preview': '将删除 {{count}} 个已隔离的 LadybugDB missing-shadow WAL sidecar:',
@@ -156,6 +165,7 @@ export const zhCN = {
156
165
  'help.option.force.confirmation': '跳过确认提示',
157
166
  'help.option.uninstall.force': '应用更改(默认仅为预演预览)',
158
167
  'help.option.clean.all': '清理所有已索引仓库',
168
+ 'help.option.clean.branch': '仅删除指定分支的索引(不影响主索引)',
159
169
  'help.option.clean.lbugSidecars': '清理已隔离的 LadybugDB missing-shadow WAL sidecar',
160
170
  'help.option.wiki.force': '即使已是最新也强制完整重新生成',
161
171
  'help.option.wiki.provider': 'LLM 提供商:openai、openrouter、azure、custom、cursor、claude、codex 或 opencode(默认:openai)',
@@ -178,6 +188,7 @@ export const zhCN = {
178
188
  'help.option.query.limit': '最多返回的流程数(默认:5)',
179
189
  'help.option.content': '包含完整符号源码',
180
190
  'help.option.repo.target': '目标仓库',
191
+ 'help.option.branch': '将查询限定到指定分支的索引(多分支仓库)',
181
192
  'help.option.context.uid': '直接符号 UID(零歧义查找)',
182
193
  'help.option.context.file': '用于消除常见名称歧义的文件路径',
183
194
  'help.option.impact.kind': '用于消除常见名称歧义的类型过滤(如 Function、Class、Method)',