gitnexus 1.6.6-rc.43 → 1.6.6-rc.45

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 (57) hide show
  1. package/dist/cli/clean.d.ts +1 -0
  2. package/dist/cli/clean.js +39 -9
  3. package/dist/cli/cli-message.d.ts +8 -0
  4. package/dist/cli/cli-message.js +14 -0
  5. package/dist/cli/detect-changes-format.d.ts +1 -0
  6. package/dist/cli/detect-changes-format.js +45 -0
  7. package/dist/cli/doctor.d.ts +2 -0
  8. package/dist/cli/doctor.js +59 -20
  9. package/dist/cli/eval-server.d.ts +1 -1
  10. package/dist/cli/eval-server.js +2 -31
  11. package/dist/cli/help-i18n.d.ts +2 -0
  12. package/dist/cli/help-i18n.js +198 -0
  13. package/dist/cli/i18n/en.d.ts +206 -0
  14. package/dist/cli/i18n/en.js +206 -0
  15. package/dist/cli/i18n/index.d.ts +9 -0
  16. package/dist/cli/i18n/index.js +41 -0
  17. package/dist/cli/i18n/resources.d.ts +414 -0
  18. package/dist/cli/i18n/resources.js +6 -0
  19. package/dist/cli/i18n/zh-CN.d.ts +206 -0
  20. package/dist/cli/i18n/zh-CN.js +206 -0
  21. package/dist/cli/index.js +5 -16
  22. package/dist/cli/list.js +15 -10
  23. package/dist/cli/remove.js +12 -11
  24. package/dist/cli/serve.js +4 -13
  25. package/dist/cli/status.js +11 -10
  26. package/dist/cli/tool.js +7 -39
  27. package/package.json +1 -1
  28. package/web/assets/{agent-CBcds30d.js → agent-CNGl256w.js} +1 -1
  29. package/web/assets/{architectureDiagram-UL44E2DR-dIoPPr6x.js → architectureDiagram-UL44E2DR-DJTnN4-A.js} +1 -1
  30. package/web/assets/{chunk-LCXTWHL2-B8hbjKUm.js → chunk-LCXTWHL2-D6tMtD_-.js} +1 -1
  31. package/web/assets/{chunk-RG4AUYOV-EfsAenro.js → chunk-RG4AUYOV-C3CY7gW4.js} +1 -1
  32. package/web/assets/{classDiagram-KGZ6W3CR-_hSUwNQJ.js → classDiagram-KGZ6W3CR-COdiqi1G.js} +1 -1
  33. package/web/assets/{classDiagram-v2-72OJOZXJ-C0NcgLqj.js → classDiagram-v2-72OJOZXJ-B7YiUGDv.js} +1 -1
  34. package/web/assets/{diagram-3NCE3AQN-CYrNJJUh.js → diagram-3NCE3AQN-aSkD3QID.js} +1 -1
  35. package/web/assets/{diagram-GF46GFSD-56NpS1jw.js → diagram-GF46GFSD-DlsGDkUv.js} +1 -1
  36. package/web/assets/{diagram-QXG6HAR7-DwXkFq_r.js → diagram-QXG6HAR7-NPw8jZAE.js} +1 -1
  37. package/web/assets/{diagram-WEQXMOUZ-C6BTq9za.js → diagram-WEQXMOUZ-CsLi0zm5.js} +1 -1
  38. package/web/assets/{erDiagram-L5TCEMPS-BcEjYsUQ.js → erDiagram-L5TCEMPS-cjritYTk.js} +1 -1
  39. package/web/assets/{flowDiagram-H6V6AXG4-DWAVIV6V.js → flowDiagram-H6V6AXG4-hAr62LB-.js} +1 -1
  40. package/web/assets/index-BeHwDjNI.css +2 -0
  41. package/web/assets/index-Cj2GDX22.js +626 -0
  42. package/web/assets/{infoDiagram-3YFTVSEB-ui-e52GZ.js → infoDiagram-3YFTVSEB-_sF9KVQz.js} +1 -1
  43. package/web/assets/{ishikawaDiagram-BNXS4ZKH-DGimV4zg.js → ishikawaDiagram-BNXS4ZKH-BtwawoWC.js} +1 -1
  44. package/web/assets/{kanban-definition-75IXJCU3-BOyfgvKL.js → kanban-definition-75IXJCU3-CljJPOuK.js} +1 -1
  45. package/web/assets/{mindmap-definition-2TDM6QVE-Ba3QrYSU.js → mindmap-definition-2TDM6QVE-DsqPS_X-.js} +1 -1
  46. package/web/assets/{pieDiagram-CU6KROY3-DMFBXNrM.js → pieDiagram-CU6KROY3-CTUDdOgg.js} +1 -1
  47. package/web/assets/{requirementDiagram-JXO7QTGE-bS4xboSz.js → requirementDiagram-JXO7QTGE-DM8hDKq-.js} +1 -1
  48. package/web/assets/{sequenceDiagram-VS2MUI6T-BqKET_2i.js → sequenceDiagram-VS2MUI6T-m6_R47U2.js} +1 -1
  49. package/web/assets/{stateDiagram-7D4R322I-DP9kvX2i.js → stateDiagram-7D4R322I-Cc1HvF6o.js} +1 -1
  50. package/web/assets/{stateDiagram-v2-36443NZ5-DB-cZ1VL.js → stateDiagram-v2-36443NZ5-Kw9j23FO.js} +1 -1
  51. package/web/assets/{timeline-definition-O6YCAMPW-DNScSOi7.js → timeline-definition-O6YCAMPW-BtENAtHS.js} +1 -1
  52. package/web/assets/{vennDiagram-MWXL3ELB-Bd1zTNWW.js → vennDiagram-MWXL3ELB-DYHpZsEN.js} +1 -1
  53. package/web/assets/{wardleyDiagram-CUQ6CDDI-DqCDQKFt.js → wardleyDiagram-CUQ6CDDI-BNPunZ-h.js} +1 -1
  54. package/web/assets/{xychartDiagram-N2JHSOCM-B8Cje_Ei.js → xychartDiagram-N2JHSOCM-BGBiR7xJ.js} +1 -1
  55. package/web/index.html +2 -2
  56. package/web/assets/index-Czp-OFT-.js +0 -624
  57. package/web/assets/index-nSZgUaIx.css +0 -2
@@ -7,4 +7,5 @@
7
7
  export declare const cleanCommand: (options?: {
8
8
  force?: boolean;
9
9
  all?: boolean;
10
+ lbugSidecars?: boolean;
10
11
  }) => Promise<void>;
package/dist/cli/clean.js CHANGED
@@ -5,22 +5,52 @@
5
5
  * Also unregisters it from the global registry.
6
6
  */
7
7
  import fs from 'fs/promises';
8
+ import path from 'path';
8
9
  import { logger } from '../core/logger.js';
9
10
  import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, UnsafeStoragePathError, } from '../storage/repo-manager.js';
11
+ import { cleanQuarantinedMissingShadowWals, inspectLbugSidecars, listQuarantinedMissingShadowWals, } from '../core/lbug/sidecar-recovery.js';
12
+ import { t } from './i18n/index.js';
10
13
  export const cleanCommand = async (options) => {
14
+ if (options?.lbugSidecars) {
15
+ const cwd = process.cwd();
16
+ const repo = await findRepo(cwd);
17
+ if (!repo) {
18
+ console.log(t('clean.notFoundHere'));
19
+ return;
20
+ }
21
+ const lbugPath = path.join(repo.storagePath, 'lbug');
22
+ const state = await inspectLbugSidecars(lbugPath);
23
+ const quarantined = await listQuarantinedMissingShadowWals(lbugPath);
24
+ console.log(t('clean.lbugSidecars.state', { state: state.kind }));
25
+ if (quarantined.length === 0) {
26
+ console.log(t('clean.lbugSidecars.none'));
27
+ return;
28
+ }
29
+ if (!options.force) {
30
+ console.log(t('clean.lbugSidecars.preview', { count: quarantined.length }));
31
+ for (const file of quarantined) {
32
+ console.log(` - ${file}`);
33
+ }
34
+ console.log(`\n${t('common.runForceConfirm')}`);
35
+ return;
36
+ }
37
+ const deleted = await cleanQuarantinedMissingShadowWals(lbugPath);
38
+ console.log(t('clean.lbugSidecars.deleted', { count: deleted.length }));
39
+ return;
40
+ }
11
41
  // --all flag: clean all indexed repos
12
42
  if (options?.all) {
13
43
  if (!options?.force) {
14
44
  const entries = await listRegisteredRepos();
15
45
  if (entries.length === 0) {
16
- console.log('No indexed repositories found.');
46
+ console.log(t('common.notIndexed'));
17
47
  return;
18
48
  }
19
- console.log(`This will delete GitNexus indexes for ${entries.length} repo(s):`);
49
+ console.log(t('clean.deleteAll', { count: entries.length }));
20
50
  for (const entry of entries) {
21
51
  console.log(` - ${entry.name} (${entry.path})`);
22
52
  }
23
- console.log('\nRun with --force to confirm deletion.');
53
+ console.log(`\n${t('common.runForceConfirm')}`);
24
54
  return;
25
55
  }
26
56
  const entries = await listRegisteredRepos();
@@ -46,7 +76,7 @@ export const cleanCommand = async (options) => {
46
76
  try {
47
77
  await fs.rm(entry.storagePath, { recursive: true, force: true });
48
78
  await unregisterRepo(entry.path);
49
- console.log(`Deleted: ${entry.name} (${entry.storagePath})`);
79
+ console.log(t('clean.deletedRepo', { name: entry.name, storagePath: entry.storagePath }));
50
80
  }
51
81
  catch (err) {
52
82
  logger.error({ err }, `Failed to delete ${entry.name}:`);
@@ -58,20 +88,20 @@ export const cleanCommand = async (options) => {
58
88
  const cwd = process.cwd();
59
89
  const repo = await findRepo(cwd);
60
90
  if (!repo) {
61
- console.log('No indexed repository found in this directory.');
91
+ console.log(t('clean.notFoundHere'));
62
92
  return;
63
93
  }
64
94
  const repoName = repo.repoPath.split(/[/\\]/).pop() || repo.repoPath;
65
95
  if (!options?.force) {
66
- console.log(`This will delete the GitNexus index for: ${repoName}`);
67
- console.log(` Path: ${repo.storagePath}`);
68
- console.log('\nRun with --force to confirm deletion.');
96
+ console.log(t('clean.deleteCurrent', { repoName }));
97
+ console.log(` ${t('common.path')}: ${repo.storagePath}`);
98
+ console.log(`\n${t('common.runForceConfirm')}`);
69
99
  return;
70
100
  }
71
101
  try {
72
102
  await fs.rm(repo.storagePath, { recursive: true, force: true });
73
103
  await unregisterRepo(repo.repoPath);
74
- console.log(`Deleted: ${repo.storagePath}`);
104
+ console.log(t('common.deleted', { target: repo.storagePath }));
75
105
  }
76
106
  catch (err) {
77
107
  logger.error({ err }, 'Failed to delete:');
@@ -1,3 +1,4 @@
1
+ import { type CliMessageKey, type CliMessageVars } from './i18n/index.js';
1
2
  /**
2
3
  * String-literal union of all `recoveryHint` tags emitted by the CLI.
3
4
  *
@@ -25,13 +26,20 @@ export interface CliMessageFields extends Record<string, unknown> {
25
26
  * and any message the user expects to read in plain text.
26
27
  */
27
28
  export declare function cliInfo(msg: string, fields?: CliMessageFields): void;
29
+ /**
30
+ * Key-based informational message. Keeps the legacy string API intact while
31
+ * allowing commands to opt into localized user-facing stderr output.
32
+ */
33
+ export declare function cliInfoKey(key: CliMessageKey, vars?: CliMessageVars, fields?: Record<string, unknown>): void;
28
34
  /**
29
35
  * User-facing warning. Operator-actionable but non-fatal — `cliWarn`
30
36
  * indicates the command can still proceed in some form.
31
37
  */
32
38
  export declare function cliWarn(msg: string, fields?: CliMessageFields): void;
39
+ export declare function cliWarnKey(key: CliMessageKey, vars?: CliMessageVars, fields?: Record<string, unknown>): void;
33
40
  /**
34
41
  * User-facing error. Indicates the command cannot proceed; usually
35
42
  * paired with a non-zero exit code at the call site.
36
43
  */
37
44
  export declare function cliError(msg: string, fields?: CliMessageFields): void;
45
+ export declare function cliErrorKey(key: CliMessageKey, vars?: CliMessageVars, fields?: Record<string, unknown>): void;
@@ -28,6 +28,7 @@
28
28
  * stdout would corrupt that pipeline.
29
29
  */
30
30
  import { logger } from '../core/logger.js';
31
+ import { t } from './i18n/index.js';
31
32
  function writeStderr(msg) {
32
33
  // Direct write — bypassing `console.*` so it cannot be intercepted by
33
34
  // progress-bar redirection (see `cli/analyze.ts:barLog`) or other
@@ -43,6 +44,13 @@ export function cliInfo(msg, fields) {
43
44
  writeStderr(msg);
44
45
  logger.info(fields ?? {}, msg);
45
46
  }
47
+ /**
48
+ * Key-based informational message. Keeps the legacy string API intact while
49
+ * allowing commands to opt into localized user-facing stderr output.
50
+ */
51
+ export function cliInfoKey(key, vars, fields) {
52
+ cliInfo(t(key, vars), fields);
53
+ }
46
54
  /**
47
55
  * User-facing warning. Operator-actionable but non-fatal — `cliWarn`
48
56
  * indicates the command can still proceed in some form.
@@ -51,6 +59,9 @@ export function cliWarn(msg, fields) {
51
59
  writeStderr(msg);
52
60
  logger.warn(fields ?? {}, msg);
53
61
  }
62
+ export function cliWarnKey(key, vars, fields) {
63
+ cliWarn(t(key, vars), fields);
64
+ }
54
65
  /**
55
66
  * User-facing error. Indicates the command cannot proceed; usually
56
67
  * paired with a non-zero exit code at the call site.
@@ -59,3 +70,6 @@ export function cliError(msg, fields) {
59
70
  writeStderr(msg);
60
71
  logger.error(fields ?? {}, msg);
61
72
  }
73
+ export function cliErrorKey(key, vars, fields) {
74
+ cliError(t(key, vars), fields);
75
+ }
@@ -0,0 +1 @@
1
+ export declare function formatDetectChangesResult(result: unknown): string;
@@ -0,0 +1,45 @@
1
+ import { t } from './i18n/index.js';
2
+ export function formatDetectChangesResult(result) {
3
+ const payload = (result ?? {});
4
+ if (payload.error)
5
+ return t('common.error', { message: String(payload.error) });
6
+ const summary = payload.summary ?? {};
7
+ if ((summary.changed_count ?? 0) === 0) {
8
+ return t('tool.detectChanges.noChanges');
9
+ }
10
+ const lines = [];
11
+ lines.push(t('tool.detectChanges.changesSummary', {
12
+ files: summary.changed_files ?? 0,
13
+ symbols: summary.changed_count ?? 0,
14
+ }));
15
+ lines.push(t('tool.detectChanges.affectedProcesses', { count: summary.affected_count ?? 0 }));
16
+ lines.push(t('tool.detectChanges.riskLevel', {
17
+ risk: summary.risk_level || t('tool.detectChanges.unknownRisk'),
18
+ }));
19
+ lines.push('');
20
+ const changed = Array.isArray(payload.changed_symbols) ? payload.changed_symbols : [];
21
+ if (changed.length > 0) {
22
+ lines.push(t('tool.detectChanges.changedSymbols'));
23
+ for (const symbol of changed.slice(0, 15)) {
24
+ lines.push(` ${symbol.type ?? 'Symbol'} ${symbol.name ?? '?'} → ${symbol.filePath ?? '?'}`);
25
+ }
26
+ if (changed.length > 15) {
27
+ lines.push(t('tool.detectChanges.overflowMore', { count: changed.length - 15 }));
28
+ }
29
+ lines.push('');
30
+ }
31
+ const affected = Array.isArray(payload.affected_processes) ? payload.affected_processes : [];
32
+ if (affected.length > 0) {
33
+ lines.push(t('tool.detectChanges.affectedExecutionFlows'));
34
+ for (const processInfo of affected.slice(0, 10)) {
35
+ const changedSteps = Array.isArray(processInfo.changed_steps)
36
+ ? processInfo.changed_steps
37
+ : [];
38
+ const steps = changedSteps.map((step) => step.symbol ?? '?').join(', ');
39
+ lines.push(` • ${processInfo.name ?? '?'} (${t('tool.detectChanges.steps', {
40
+ count: processInfo.step_count ?? 0,
41
+ })}) — ${t('tool.detectChanges.changedSteps', { steps })}`);
42
+ }
43
+ }
44
+ return lines.join('\n').trim();
45
+ }
@@ -1 +1,3 @@
1
+ export declare function displayWidth(value: string): number;
2
+ export declare function padDisplayEnd(value: string, columns: number): string;
1
3
  export declare const doctorCommand: () => Promise<void>;
@@ -1,31 +1,70 @@
1
1
  import { getRuntimeCapabilities, getRuntimeFingerprint } from '../core/platform/capabilities.js';
2
2
  import { resolveEmbeddingConfig } from '../core/embeddings/config.js';
3
3
  import { isHttpMode } from '../core/embeddings/http-client.js';
4
+ import { t } from './i18n/index.js';
5
+ function isCombiningMark(codePoint) {
6
+ return ((codePoint >= 0x0300 && codePoint <= 0x036f) ||
7
+ (codePoint >= 0x1ab0 && codePoint <= 0x1aff) ||
8
+ (codePoint >= 0x1dc0 && codePoint <= 0x1dff) ||
9
+ (codePoint >= 0x20d0 && codePoint <= 0x20ff) ||
10
+ (codePoint >= 0xfe20 && codePoint <= 0xfe2f));
11
+ }
12
+ function isWideCodePoint(codePoint) {
13
+ return ((codePoint >= 0x1100 && codePoint <= 0x115f) ||
14
+ codePoint === 0x2329 ||
15
+ codePoint === 0x232a ||
16
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf && codePoint !== 0x303f) ||
17
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
18
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
19
+ (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
20
+ (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
21
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
22
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
23
+ (codePoint >= 0x1f300 && codePoint <= 0x1f64f) ||
24
+ (codePoint >= 0x1f900 && codePoint <= 0x1f9ff) ||
25
+ (codePoint >= 0x20000 && codePoint <= 0x3fffd));
26
+ }
27
+ export function displayWidth(value) {
28
+ let width = 0;
29
+ for (const char of value) {
30
+ const codePoint = char.codePointAt(0);
31
+ if (codePoint === undefined || codePoint === 0)
32
+ continue;
33
+ if (isCombiningMark(codePoint))
34
+ continue;
35
+ width += isWideCodePoint(codePoint) ? 2 : 1;
36
+ }
37
+ return width;
38
+ }
39
+ export function padDisplayEnd(value, columns) {
40
+ return value + ' '.repeat(Math.max(0, columns - displayWidth(value)));
41
+ }
42
+ const label = (key, width) => padDisplayEnd(t(key), width);
4
43
  export const doctorCommand = async () => {
5
44
  const fingerprint = getRuntimeFingerprint();
6
45
  const capabilities = getRuntimeCapabilities();
7
46
  const embeddingConfig = resolveEmbeddingConfig();
8
- console.log('GitNexus Doctor\n');
9
- console.log('Runtime');
10
- console.log(` OS: ${fingerprint.platform}/${fingerprint.arch}`);
11
- console.log(` Node: ${fingerprint.node}`);
12
- console.log(` GitNexus: ${fingerprint.gitnexus}`);
13
- console.log(` LadybugDB: ${fingerprint.ladybugdb ?? 'unknown'}`);
14
- console.log(` ONNX: ${fingerprint.onnxruntime ?? 'unknown'}`);
47
+ console.log(t('doctor.title') + '\n');
48
+ console.log(t('doctor.runtime'));
49
+ console.log(` ${label('doctor.labels.os', 10)}${fingerprint.platform}/${fingerprint.arch}`);
50
+ console.log(` ${label('doctor.labels.node', 10)}${fingerprint.node}`);
51
+ console.log(` ${label('doctor.labels.gitnexus', 10)}${fingerprint.gitnexus}`);
52
+ console.log(` ${label('doctor.labels.ladybugdb', 10)}${fingerprint.ladybugdb ?? 'unknown'}`);
53
+ console.log(` ${label('doctor.labels.onnx', 10)}${fingerprint.onnxruntime ?? 'unknown'}`);
15
54
  console.log('');
16
- console.log('Capabilities');
17
- console.log(` Graph store: ${capabilities.graph}`);
18
- console.log(` Full-text search:${capabilities.fts.padStart(10)}`);
19
- console.log(` VECTOR index: ${capabilities.vector}`);
20
- console.log(` Semantic mode: ${capabilities.semanticMode}`);
21
- console.log(` Exact scan limit:${String(capabilities.exactScanLimit).padStart(9)} chunks`);
55
+ console.log(t('doctor.capabilities'));
56
+ console.log(` ${label('doctor.labels.graphStore', 18)}${capabilities.graph}`);
57
+ console.log(` ${label('doctor.labels.fullTextSearch', 18)}${capabilities.fts}`);
58
+ console.log(` ${label('doctor.labels.vectorIndex', 18)}${capabilities.vector}`);
59
+ console.log(` ${label('doctor.labels.semanticMode', 18)}${capabilities.semanticMode}`);
60
+ console.log(` ${label('doctor.labels.exactScanLimit', 18)}${t('doctor.chunks', { count: capabilities.exactScanLimit })}`);
22
61
  if (capabilities.reason)
23
- console.log(` Note: ${capabilities.reason}`);
62
+ console.log(` ${label('doctor.labels.note', 18)}${capabilities.reason}`);
24
63
  console.log('');
25
- console.log('Embeddings');
26
- console.log(` Backend: ${isHttpMode() ? 'http' : 'local'}`);
27
- console.log(` Device: ${embeddingConfig.device}`);
28
- console.log(` Threads: ${embeddingConfig.threads}`);
29
- console.log(` Batch: ${embeddingConfig.batchSize} nodes`);
30
- console.log(` Sub-batch: ${embeddingConfig.subBatchSize} chunks`);
64
+ console.log(t('doctor.embeddings'));
65
+ console.log(` ${label('doctor.labels.backend', 12)}${isHttpMode() ? 'http' : 'local'}`);
66
+ console.log(` ${label('doctor.labels.device', 12)}${embeddingConfig.device}`);
67
+ console.log(` ${label('doctor.labels.threads', 12)}${embeddingConfig.threads}`);
68
+ console.log(` ${label('doctor.labels.batch', 12)}${t('doctor.nodes', { count: embeddingConfig.batchSize })}`);
69
+ console.log(` ${label('doctor.labels.subBatch', 12)}${t('doctor.chunks', { count: embeddingConfig.subBatchSize })}`);
31
70
  };
@@ -28,6 +28,7 @@
28
28
  * GET /health — Health check. Returns {"status":"ok","repos":[...]}
29
29
  * POST /shutdown — Graceful shutdown.
30
30
  */
31
+ export { formatDetectChangesResult } from './detect-changes-format.js';
31
32
  export interface EvalServerOptions {
32
33
  port?: string;
33
34
  host?: string;
@@ -44,7 +45,6 @@ export declare function formatQueryResult(result: any): string;
44
45
  export declare function formatContextResult(result: any): string;
45
46
  export declare function formatImpactResult(result: any): string;
46
47
  export declare function formatCypherResult(result: any): string;
47
- export declare function formatDetectChangesResult(result: any): string;
48
48
  export declare function formatListReposResult(result: any): string;
49
49
  export declare function evalServerCommand(options?: EvalServerOptions): Promise<void>;
50
50
  export declare const MAX_BODY_SIZE: number;
@@ -34,6 +34,8 @@ import { writeSync } from 'node:fs';
34
34
  import { LocalBackend } from '../mcp/local/local-backend.js';
35
35
  import { logger } from '../core/logger.js';
36
36
  import { cliInfo, cliWarn, cliError } from './cli-message.js';
37
+ import { formatDetectChangesResult } from './detect-changes-format.js';
38
+ export { formatDetectChangesResult } from './detect-changes-format.js';
37
39
  /**
38
40
  * Validate the --host value. Accepts IPv4, IPv6, or "localhost".
39
41
  * Returns the host string unchanged, or null if invalid.
@@ -204,37 +206,6 @@ export function formatCypherResult(result) {
204
206
  }
205
207
  return typeof result === 'string' ? result : JSON.stringify(result, null, 2);
206
208
  }
207
- export function formatDetectChangesResult(result) {
208
- if (result.error)
209
- return `Error: ${result.error}`;
210
- const summary = result.summary || {};
211
- const lines = [];
212
- if (summary.changed_count === 0) {
213
- return 'No changes detected.';
214
- }
215
- lines.push(`Changes: ${summary.changed_files || 0} files, ${summary.changed_count || 0} symbols`);
216
- lines.push(`Affected processes: ${summary.affected_count || 0}`);
217
- lines.push(`Risk level: ${summary.risk_level || 'unknown'}\n`);
218
- const changed = result.changed_symbols || [];
219
- if (changed.length > 0) {
220
- lines.push(`Changed symbols:`);
221
- for (const s of changed.slice(0, 15)) {
222
- lines.push(` ${s.type} ${s.name} → ${s.filePath}`);
223
- }
224
- if (changed.length > 15)
225
- lines.push(` ... and ${changed.length - 15} more`);
226
- lines.push('');
227
- }
228
- const affected = result.affected_processes || [];
229
- if (affected.length > 0) {
230
- lines.push(`Affected execution flows:`);
231
- for (const p of affected.slice(0, 10)) {
232
- const steps = (p.changed_steps || []).map((s) => s.symbol).join(', ');
233
- lines.push(` • ${p.name} (${p.step_count} steps) — changed: ${steps}`);
234
- }
235
- }
236
- return lines.join('\n').trim();
237
- }
238
209
  export function formatListReposResult(result) {
239
210
  if (!Array.isArray(result) || result.length === 0) {
240
211
  return 'No indexed repositories.';
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function localizeCliHelp(program: Command): Command;
@@ -0,0 +1,198 @@
1
+ import { t } from './i18n/index.js';
2
+ const TITLE_KEYS = {
3
+ 'Usage:': 'help.title.usage',
4
+ 'Arguments:': 'help.title.arguments',
5
+ 'Options:': 'help.title.options',
6
+ 'Global Options:': 'help.title.globalOptions',
7
+ 'Commands:': 'help.title.commands',
8
+ };
9
+ const COMMAND_DESCRIPTION_KEYS = {
10
+ '': 'help.description.root',
11
+ setup: 'help.command.setup.description',
12
+ analyze: 'help.command.analyze.description',
13
+ index: 'help.command.index.description',
14
+ serve: 'help.command.serve.description',
15
+ mcp: 'help.command.mcp.description',
16
+ list: 'help.command.list.description',
17
+ status: 'help.command.status.description',
18
+ doctor: 'help.command.doctor.description',
19
+ clean: 'help.command.clean.description',
20
+ remove: 'help.command.remove.description',
21
+ wiki: 'help.command.wiki.description',
22
+ augment: 'help.command.augment.description',
23
+ publish: 'help.command.publish.description',
24
+ query: 'help.command.query.description',
25
+ context: 'help.command.context.description',
26
+ impact: 'help.command.impact.description',
27
+ cypher: 'help.command.cypher.description',
28
+ 'detect-changes': 'help.command.detectChanges.description',
29
+ 'eval-server': 'help.command.evalServer.description',
30
+ group: 'help.command.group.description',
31
+ 'group create': 'help.command.group.create.description',
32
+ 'group add': 'help.command.group.add.description',
33
+ 'group remove': 'help.command.group.remove.description',
34
+ 'group list': 'help.command.group.list.description',
35
+ 'group status': 'help.command.group.status.description',
36
+ 'group sync': 'help.command.group.sync.description',
37
+ 'group impact': 'help.command.group.impact.description',
38
+ 'group query': 'help.command.group.query.description',
39
+ 'group contracts': 'help.command.group.contracts.description',
40
+ };
41
+ const OPTION_DESCRIPTION_KEYS = {
42
+ '|-V, --version': 'help.option.version',
43
+ 'analyze|-f, --force': 'help.option.analyze.force',
44
+ 'analyze|--repair-fts': 'help.option.analyze.repairFts',
45
+ 'analyze|--embeddings [limit]': 'help.option.analyze.embeddings',
46
+ 'analyze|--drop-embeddings': 'help.option.analyze.dropEmbeddings',
47
+ 'analyze|--skills': 'help.option.analyze.skills',
48
+ 'analyze|--skip-agents-md': 'help.option.analyze.skipAgentsMd',
49
+ 'analyze|--no-stats': 'help.option.analyze.noStats',
50
+ 'analyze|--skip-skills': 'help.option.analyze.skipSkills',
51
+ 'analyze|--index-only': 'help.option.analyze.indexOnly',
52
+ 'analyze|--skip-git': 'help.option.skipGit',
53
+ 'analyze|--name <alias>': 'help.option.analyze.name',
54
+ 'analyze|--allow-duplicate-name': 'help.option.analyze.allowDuplicateName',
55
+ 'analyze|-v, --verbose': 'help.option.verbose',
56
+ 'analyze|--max-file-size <kb>': 'help.option.analyze.maxFileSize',
57
+ 'analyze|--worker-timeout <seconds>': 'help.option.analyze.workerTimeout',
58
+ 'analyze|--wal-checkpoint-threshold <bytes>': 'help.option.analyze.walCheckpointThreshold',
59
+ 'analyze|--workers <n>': 'help.option.analyze.workers',
60
+ 'analyze|--embedding-threads <n>': 'help.option.analyze.embeddingThreads',
61
+ 'analyze|--embedding-batch-size <n>': 'help.option.analyze.embeddingBatchSize',
62
+ 'analyze|--embedding-sub-batch-size <n>': 'help.option.analyze.embeddingSubBatchSize',
63
+ 'analyze|--embedding-device <device>': 'help.option.analyze.embeddingDevice',
64
+ 'index|-f, --force': 'help.option.index.force',
65
+ 'index|--allow-non-git': 'help.option.index.allowNonGit',
66
+ 'serve|-p, --port <port>': 'help.option.port',
67
+ 'serve|--host <host>': 'help.option.serve.host',
68
+ 'clean|-f, --force': 'help.option.force.confirmation',
69
+ 'clean|--all': 'help.option.clean.all',
70
+ 'clean|--lbug-sidecars': 'help.option.clean.lbugSidecars',
71
+ 'remove|-f, --force': 'help.option.force.confirmation',
72
+ 'wiki|-f, --force': 'help.option.wiki.force',
73
+ 'wiki|--provider <provider>': 'help.option.wiki.provider',
74
+ 'wiki|--model <model>': 'help.option.wiki.model',
75
+ 'wiki|--base-url <url>': 'help.option.wiki.baseUrl',
76
+ 'wiki|--api-key <key>': 'help.option.wiki.apiKey',
77
+ 'wiki|--api-version <version>': 'help.option.wiki.apiVersion',
78
+ 'wiki|--reasoning-model': 'help.option.wiki.reasoningModel',
79
+ 'wiki|--no-reasoning-model': 'help.option.wiki.noReasoningModel',
80
+ 'wiki|--concurrency <n>': 'help.option.wiki.concurrency',
81
+ 'wiki|--timeout <seconds>': 'help.option.wiki.timeout',
82
+ 'wiki|--retries <n>': 'help.option.wiki.retries',
83
+ 'wiki|--gist': 'help.option.wiki.gist',
84
+ 'wiki|-v, --verbose': 'help.option.verbose',
85
+ 'wiki|--review': 'help.option.wiki.review',
86
+ 'wiki|--lang <lang>': 'help.option.wiki.lang',
87
+ 'publish|--id <owner/repo>': 'help.option.publish.id',
88
+ 'publish|--skip-git': 'help.option.skipGit',
89
+ 'query|-r, --repo <name>': 'help.option.repo.targetOmitOne',
90
+ 'query|-c, --context <text>': 'help.option.query.context',
91
+ 'query|-g, --goal <text>': 'help.option.query.goal',
92
+ 'query|-l, --limit <n>': 'help.option.query.limit',
93
+ 'query|--content': 'help.option.content',
94
+ 'context|-r, --repo <name>': 'help.option.repo.target',
95
+ 'context|-u, --uid <uid>': 'help.option.context.uid',
96
+ 'context|-f, --file <path>': 'help.option.context.file',
97
+ 'context|--content': 'help.option.content',
98
+ 'impact|-d, --direction <dir>': 'help.option.impact.direction',
99
+ 'impact|-r, --repo <name>': 'help.option.repo.target',
100
+ 'impact|--depth <n>': 'help.option.impact.depth',
101
+ 'impact|--include-tests': 'help.option.impact.includeTests',
102
+ 'cypher|-r, --repo <name>': 'help.option.repo.target',
103
+ 'detect-changes|-s, --scope <scope>': 'help.option.detectChanges.scope',
104
+ 'detect-changes|-b, --base-ref <ref>': 'help.option.detectChanges.baseRef',
105
+ 'detect-changes|-r, --repo <name>': 'help.option.repo.target',
106
+ 'eval-server|-p, --port <port>': 'help.option.port',
107
+ 'eval-server|--host <host>': 'help.option.evalServer.host',
108
+ 'eval-server|--idle-timeout <seconds>': 'help.option.evalServer.idleTimeout',
109
+ 'group create|--force': 'help.option.group.create.force',
110
+ 'group sync|--skip-embeddings': 'help.option.group.sync.skipEmbeddings',
111
+ 'group sync|--exact-only': 'help.option.group.sync.exactOnly',
112
+ 'group sync|--allow-stale': 'help.option.group.sync.allowStale',
113
+ 'group sync|--verbose': 'help.option.group.sync.verbose',
114
+ 'group sync|--json': 'help.option.json',
115
+ 'group impact|--target <symbol>': 'help.option.group.impact.target',
116
+ 'group impact|--repo <groupPath>': 'help.option.group.impact.repo',
117
+ 'group impact|--direction <dir>': 'help.option.impact.direction',
118
+ 'group impact|--service <path>': 'help.option.group.impact.service',
119
+ 'group impact|--subgroup <path>': 'help.option.group.impact.subgroup',
120
+ 'group impact|--max-depth <n>': 'help.option.impact.depth',
121
+ 'group impact|--cross-depth <n>': 'help.option.group.impact.crossDepth',
122
+ 'group impact|--min-confidence <n>': 'help.option.group.impact.minConfidence',
123
+ 'group impact|--include-tests': 'help.option.impact.includeTests',
124
+ 'group impact|--timeout-ms <n>': 'help.option.group.impact.timeoutMs',
125
+ 'group impact|--json': 'help.option.json',
126
+ 'group query|--subgroup <path>': 'help.option.group.query.subgroup',
127
+ 'group query|--limit <n>': 'help.option.group.query.limit',
128
+ 'group query|--json': 'help.option.json',
129
+ 'group contracts|--type <type>': 'help.option.group.contracts.type',
130
+ 'group contracts|--repo <repo>': 'help.option.group.contracts.repo',
131
+ 'group contracts|--unmatched': 'help.option.group.contracts.unmatched',
132
+ 'group contracts|--json': 'help.option.json',
133
+ };
134
+ function localizeTitle(title) {
135
+ const key = TITLE_KEYS[title];
136
+ return key ? t(key) : title;
137
+ }
138
+ function localizeOptionDescription(option) {
139
+ const extraInfo = [];
140
+ if (option.argChoices) {
141
+ const label = t('help.optionMeta.choices');
142
+ extraInfo.push(`${label}: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);
143
+ }
144
+ if (option.defaultValue !== undefined) {
145
+ const showDefault = option.required ||
146
+ option.optional ||
147
+ (option.isBoolean() && typeof option.defaultValue === 'boolean');
148
+ if (showDefault) {
149
+ const label = t('help.optionMeta.default');
150
+ extraInfo.push(`${label}: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
151
+ }
152
+ }
153
+ if (option.presetArg !== undefined && option.optional) {
154
+ const label = t('help.optionMeta.preset');
155
+ extraInfo.push(`${label}: ${JSON.stringify(option.presetArg)}`);
156
+ }
157
+ if (option.envVar !== undefined) {
158
+ const label = t('help.optionMeta.env');
159
+ extraInfo.push(`${label}: ${option.envVar}`);
160
+ }
161
+ if (extraInfo.length > 0) {
162
+ const extraDescription = `(${extraInfo.join(', ')})`;
163
+ if (option.description)
164
+ return `${option.description} ${extraDescription}`;
165
+ return extraDescription;
166
+ }
167
+ return option.description;
168
+ }
169
+ function pathFor(commandPath, command) {
170
+ if (!command.parent)
171
+ return '';
172
+ return commandPath ? `${commandPath} ${command.name()}` : command.name();
173
+ }
174
+ function applyHelpI18n(command, commandPath = '') {
175
+ const descriptionKey = COMMAND_DESCRIPTION_KEYS[commandPath];
176
+ if (descriptionKey)
177
+ command.description(t(descriptionKey));
178
+ command.helpOption('-h, --help', t('help.option.help'));
179
+ command.configureHelp({
180
+ styleTitle: localizeTitle,
181
+ optionDescription: localizeOptionDescription,
182
+ });
183
+ if (command.commands.length > 0) {
184
+ command.helpCommand('help [command]', t('help.command.help.description'));
185
+ }
186
+ for (const option of command.options) {
187
+ const optionKey = OPTION_DESCRIPTION_KEYS[`${commandPath}|${option.flags}`];
188
+ if (optionKey)
189
+ option.description = t(optionKey);
190
+ }
191
+ for (const subcommand of command.commands) {
192
+ applyHelpI18n(subcommand, pathFor(commandPath, subcommand));
193
+ }
194
+ }
195
+ export function localizeCliHelp(program) {
196
+ applyHelpI18n(program);
197
+ return program;
198
+ }