gitnexus 1.6.11-rc.3 → 1.6.11-rc.5

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 (45) hide show
  1. package/README.md +26 -0
  2. package/dist/_shared/impact-risk.d.ts +35 -0
  3. package/dist/_shared/impact-risk.d.ts.map +1 -0
  4. package/dist/_shared/impact-risk.js +78 -0
  5. package/dist/_shared/impact-risk.js.map +1 -0
  6. package/dist/_shared/index.d.ts +2 -0
  7. package/dist/_shared/index.d.ts.map +1 -1
  8. package/dist/_shared/index.js +2 -0
  9. package/dist/_shared/index.js.map +1 -1
  10. package/dist/cli/analyze-config.d.ts +2 -0
  11. package/dist/cli/analyze-config.js +16 -0
  12. package/dist/cli/analyze-options.d.ts +4 -0
  13. package/dist/cli/analyze.d.ts +17 -0
  14. package/dist/cli/analyze.js +40 -7
  15. package/dist/cli/help-i18n.js +2 -0
  16. package/dist/cli/i18n/en.d.ts +2 -0
  17. package/dist/cli/i18n/en.js +2 -0
  18. package/dist/cli/i18n/resources.d.ts +4 -0
  19. package/dist/cli/i18n/zh-CN.d.ts +2 -0
  20. package/dist/cli/i18n/zh-CN.js +2 -0
  21. package/dist/cli/index.js +9 -2
  22. package/dist/cli/watch-queue.d.ts +41 -0
  23. package/dist/cli/watch-queue.js +184 -0
  24. package/dist/cli/watch.d.ts +20 -0
  25. package/dist/cli/watch.js +372 -0
  26. package/dist/config/ignore-service.d.ts +11 -0
  27. package/dist/config/ignore-service.js +41 -4
  28. package/dist/config/repo-control-file.d.ts +3 -0
  29. package/dist/config/repo-control-file.js +115 -0
  30. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +2 -0
  31. package/dist/core/ingestion/pipeline-phases/parse-impl.js +7 -0
  32. package/dist/core/ingestion/pipeline-phases/parse.d.ts +2 -0
  33. package/dist/core/ingestion/pipeline.js +2 -1
  34. package/dist/core/run-analyze.d.ts +17 -0
  35. package/dist/core/run-analyze.js +72 -10
  36. package/dist/mcp/local/local-backend.js +34 -4
  37. package/dist/mcp/local/pdg-impact.d.ts +8 -4
  38. package/dist/mcp/local/pdg-impact.js +7 -2
  39. package/dist/mcp/tools.js +1 -1
  40. package/dist/storage/parse-cache.js +13 -1
  41. package/dist/types/pipeline.d.ts +2 -0
  42. package/package.json +2 -1
  43. package/scripts/cross-platform-shard.ts +4 -2
  44. package/scripts/cross-platform-tests.ts +2 -1
  45. package/skills/gitnexus-cli.md +5 -1
package/README.md CHANGED
@@ -234,6 +234,7 @@ Your AI agent gets **17 tools** (15 per-repo + 2 group) automatically:
234
234
  gitnexus setup # Configure MCP for detected editors (one-time; use -c to select)
235
235
  gitnexus uninstall # Preview removal of GitNexus MCP/skills/hooks (add --force to apply)
236
236
  gitnexus analyze [path] # Index a repository (or update stale index)
237
+ gitnexus analyze [path] --watch # Watch local files and serialize incremental refreshes
237
238
  gitnexus analyze --repair-fts # Fast path: rebuild/verify only FTS indexes on existing index data
238
239
  gitnexus analyze --force # Full rebuild: re-parse + graph rebuild + FTS rebuild
239
240
  gitnexus analyze --embeddings # Enable embedding generation (slower, better search)
@@ -282,6 +283,31 @@ gitnexus group status <name> # Check staleness of repos in a group
282
283
  gitnexus group impact <name> --target <symbol> --repo <groupPath> # Cross-repo blast radius
283
284
  ```
284
285
 
286
+ `gitnexus analyze --watch` requires a Git repository. It performs an initial
287
+ analysis and then debounces scanner-admitted working-tree changes for 300 ms by
288
+ default into serialized incremental refreshes. Events arriving during a run
289
+ remain queued, and retryable failures retain the same batch with bounded
290
+ backoff. Invalid `.gitnexusrc` or ignore-file reloads pause ordinary refreshes
291
+ until the control file is fixed. Watch refreshes update only the graph: they
292
+ intentionally skip AGENTS.md / CLAUDE.md injection and standard skill
293
+ installation. Run a one-shot `gitnexus analyze` when those generated files need
294
+ updating. Stop watch mode with Ctrl+C.
295
+
296
+ Watch mode accepts `--debounce`, `--workers`, `--worker-timeout`,
297
+ `--max-file-size`, `--branch`, `--pdg`, `--name`, `--allow-duplicate-name`, and
298
+ `--verbose`. Explicit one-shot options such as `--force`, `--repair-fts`,
299
+ embedding flags, `--skills`, `--default-branch`, `--skip-agents-md`,
300
+ `--skip-skills`, `--no-stats`, `--self-commit`, `--index-only`, and `--skip-git`
301
+ are rejected. Unsupported defaults from `.gitnexusrc` are ignored with a warning.
302
+
303
+ POSIX requests clone-first copy-and-swap publication when the live index has no
304
+ orphan sidecars. Windows and sidecar fallback runs update in place: failures
305
+ known to occur before writes are retried, while a failure that may have mutated
306
+ the live index stops the watcher. Watch mode does not pull remotes. Running MCP
307
+ and `serve` processes periodically check for a newly published index and reopen
308
+ it without a restart. MCP checks are throttled to once every five seconds, so a
309
+ tool call before the next check can briefly use the previous index.
310
+
285
311
  GraphQL contract matching is opt-in in the group's `group.yaml`:
286
312
 
287
313
  ```yaml
@@ -0,0 +1,35 @@
1
+ export type ImpactRisk = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' | 'UNKNOWN';
2
+ export type ImpactRiskAxis = 'processes' | 'modules';
3
+ export type UnusedImpactRiskReason = 'file-nodes-have-no-process-or-community-membership' | 'enrichment-skipped' | 'enrichment-budget-exhausted' | 'enrichment-query-failed';
4
+ export interface UnusedImpactRiskAxis {
5
+ axis: ImpactRiskAxis;
6
+ reason: UnusedImpactRiskReason;
7
+ }
8
+ export interface ImpactRiskInput {
9
+ direction: 'upstream' | 'downstream';
10
+ directCount: number;
11
+ processCount: number;
12
+ moduleCount: number;
13
+ impactedCount: number;
14
+ unusedAxes?: readonly UnusedImpactRiskAxis[];
15
+ }
16
+ export interface ImpactRiskResult {
17
+ risk: ImpactRisk;
18
+ riskSharedAxes: ImpactRisk;
19
+ riskScale: {
20
+ comparableAcrossKinds: boolean;
21
+ unusedAxes: readonly UnusedImpactRiskAxis[];
22
+ };
23
+ }
24
+ /** Map walk outcomes to unused process/module axes so comparability matches what was sampled. */
25
+ export declare function unusedAxesForImpactWalk(input: {
26
+ isFileTarget: boolean;
27
+ skipEnrichment: boolean;
28
+ maxChunks: number;
29
+ processQueryFailed: boolean;
30
+ moduleQueryFailed: boolean;
31
+ /** When 0, a zero chunk budget is not an unused-axis event — there was nothing to enrich. */
32
+ impactedCount?: number;
33
+ }): UnusedImpactRiskAxis[];
34
+ export declare function scoreImpactRisk(input: ImpactRiskInput): ImpactRiskResult;
35
+ //# sourceMappingURL=impact-risk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-risk.d.ts","sourceRoot":"","sources":["../src/impact-risk.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5E,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,CAAC;AAErD,MAAM,MAAM,sBAAsB,GAC9B,oDAAoD,GACpD,oBAAoB,GACpB,6BAA6B,GAC7B,yBAAyB,CAAC;AAE9B,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,UAAU,GAAG,YAAY,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,cAAc,EAAE,UAAU,CAAC;IAC3B,SAAS,EAAE;QACT,qBAAqB,EAAE,OAAO,CAAC;QAC/B,UAAU,EAAE,SAAS,oBAAoB,EAAE,CAAC;KAC7C,CAAC;CACH;AA0CD,iGAAiG;AACjG,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,oBAAoB,EAAE,CAiCzB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,CAWxE"}
@@ -0,0 +1,78 @@
1
+ function score(input) {
2
+ const { direction, directCount, processCount, moduleCount, impactedCount } = input;
3
+ if (direction === 'upstream' && impactedCount === 0)
4
+ return 'UNKNOWN';
5
+ if (directCount >= 30 || processCount >= 5 || moduleCount >= 5 || impactedCount >= 200) {
6
+ return 'CRITICAL';
7
+ }
8
+ if (directCount >= 15 || processCount >= 3 || moduleCount >= 3 || impactedCount >= 100) {
9
+ return 'HIGH';
10
+ }
11
+ if (directCount >= 5 || impactedCount >= 30)
12
+ return 'MEDIUM';
13
+ return 'LOW';
14
+ }
15
+ function countsWithUnusedAxesZeroed(input) {
16
+ let processCount = input.processCount;
17
+ let moduleCount = input.moduleCount;
18
+ for (const unused of input.unusedAxes ?? []) {
19
+ if (unused.axis === 'processes')
20
+ processCount = 0;
21
+ if (unused.axis === 'modules')
22
+ moduleCount = 0;
23
+ }
24
+ return {
25
+ direction: input.direction,
26
+ directCount: input.directCount,
27
+ processCount,
28
+ moduleCount,
29
+ impactedCount: input.impactedCount,
30
+ };
31
+ }
32
+ /** Map walk outcomes to unused process/module axes so comparability matches what was sampled. */
33
+ export function unusedAxesForImpactWalk(input) {
34
+ if (input.isFileTarget) {
35
+ return [
36
+ {
37
+ axis: 'processes',
38
+ reason: 'file-nodes-have-no-process-or-community-membership',
39
+ },
40
+ {
41
+ axis: 'modules',
42
+ reason: 'file-nodes-have-no-process-or-community-membership',
43
+ },
44
+ ];
45
+ }
46
+ if (input.skipEnrichment) {
47
+ return [
48
+ { axis: 'processes', reason: 'enrichment-skipped' },
49
+ { axis: 'modules', reason: 'enrichment-skipped' },
50
+ ];
51
+ }
52
+ if (input.maxChunks === 0 && (input.impactedCount ?? 1) > 0) {
53
+ return [
54
+ { axis: 'processes', reason: 'enrichment-budget-exhausted' },
55
+ { axis: 'modules', reason: 'enrichment-budget-exhausted' },
56
+ ];
57
+ }
58
+ const unused = [];
59
+ if (input.processQueryFailed) {
60
+ unused.push({ axis: 'processes', reason: 'enrichment-query-failed' });
61
+ }
62
+ if (input.moduleQueryFailed) {
63
+ unused.push({ axis: 'modules', reason: 'enrichment-query-failed' });
64
+ }
65
+ return unused;
66
+ }
67
+ export function scoreImpactRisk(input) {
68
+ const unusedAxes = input.unusedAxes ?? [];
69
+ return {
70
+ risk: score(countsWithUnusedAxesZeroed(input)),
71
+ riskSharedAxes: score({ ...input, processCount: 0, moduleCount: 0 }),
72
+ riskScale: {
73
+ comparableAcrossKinds: unusedAxes.length === 0,
74
+ unusedAxes,
75
+ },
76
+ };
77
+ }
78
+ //# sourceMappingURL=impact-risk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-risk.js","sourceRoot":"","sources":["../src/impact-risk.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ export type { NodeTableName, RelType } from './lbug/schema-constants.js';
4
4
  export { SupportedLanguages } from './languages.js';
5
5
  export { getLanguageFromFilename, getSyntaxLanguageFromFilename, isBladeTemplateFilename, } from './language-detection.js';
6
6
  export type { MroStrategy } from './mro-strategy.js';
7
+ export { scoreImpactRisk, unusedAxesForImpactWalk } from './impact-risk.js';
8
+ export type { ImpactRisk, ImpactRiskAxis, ImpactRiskInput, ImpactRiskResult, UnusedImpactRiskAxis, UnusedImpactRiskReason, } from './impact-risk.js';
7
9
  export type { PipelinePhase, PipelineProgress } from './pipeline.js';
8
10
  export type { ParameterTypeClass, SymbolDefinition, TypeParameter, } from './scope-resolution/symbol-definition.js';
9
11
  export type { ScopeId, DefId, ScopeKind, Range, Capture, CaptureMatch, BindingRef, ImportEdge, TypeRef, Scope, ResolutionEvidence, Resolution, Reference, ReferenceIndex, LookupParams, RegistryContributor, ParsedImport, ParsedTypeBinding, WorkspaceIndex, Callsite, ScopeLookup, } from './scope-resolution/types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAGzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,YAAY,EACV,UAAU,EACV,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIrE,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,OAAO,EACP,KAAK,EACL,SAAS,EACT,KAAK,EACL,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,OAAO,EACP,KAAK,EACL,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,WAAW,GACZ,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAG/E,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAG5F,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,YAAY,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAKrF,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAGpF,YAAY,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,aAAa,EACb,aAAa,EACb,QAAQ,EACR,cAAc,GACf,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,mBAAmB,EACnB,6BAA6B,EAC7B,uBAAuB,EACvB,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AACvF,YAAY,EACV,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AACpE,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AACrF,YAAY,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AACvF,YAAY,EACV,cAAc,EACd,mBAAmB,GACpB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AACrF,YAAY,EACV,aAAa,EACb,kBAAkB,GACnB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AACrF,YAAY,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAC;AACrF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAC1E,YAAY,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AACpF,YAAY,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACpG,YAAY,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,MAAM,6CAA6C,CAAC;AAC/E,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,GACZ,MAAM,0CAA0C,CAAC;AAClD,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,sBAAsB,EACtB,YAAY,EACZ,iBAAiB,GAClB,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACrF,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAM1E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACjG,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EACL,cAAc,EACd,4BAA4B,EAC5B,kBAAkB,EAClB,eAAe,EACf,sBAAsB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG/E,OAAO,EACL,+BAA+B,EAC/B,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,GACf,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC"}
@@ -3,6 +3,8 @@ export { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME, } from '.
3
3
  // Language support
4
4
  export { SupportedLanguages } from './languages.js';
5
5
  export { getLanguageFromFilename, getSyntaxLanguageFromFilename, isBladeTemplateFilename, } from './language-detection.js';
6
+ // Impact risk scoring
7
+ export { scoreImpactRisk, unusedAxesForImpactWalk } from './impact-risk.js';
6
8
  // Evidence + tie-break constants (RFC Appendix A, Appendix B)
7
9
  export { EvidenceWeights, typeBindingWeightAtDepth } from './scope-resolution/evidence-weights.js';
8
10
  export { ORIGIN_PRIORITY } from './scope-resolution/origin-priority.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAqCjC,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AA4BxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,GACZ,MAAM,0CAA0C,CAAC;AAUlD,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,8EAA8E;AAC9E,wEAAwE;AACxE,wEAAwE;AACxE,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEjG,OAAO,EACL,cAAc,EACd,4BAA4B,EAC5B,kBAAkB,EAClB,eAAe,EACf,sBAAsB,GACvB,MAAM,mCAAmC,CAAC;AAG3C,mDAAmD;AACnD,OAAO,EACL,+BAA+B,EAC/B,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,GACf,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AAGjC,sBAAsB;AACtB,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AA4C5E,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AA4BxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,GACZ,MAAM,0CAA0C,CAAC;AAUlD,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,8EAA8E;AAC9E,wEAAwE;AACxE,wEAAwE;AACxE,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEjG,OAAO,EACL,cAAc,EACd,4BAA4B,EAC5B,kBAAkB,EAClB,eAAe,EACf,sBAAsB,GACvB,MAAM,mCAAmC,CAAC;AAG3C,mDAAmD;AACnD,OAAO,EACL,+BAA+B,EAC/B,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,GACf,MAAM,sCAAsC,CAAC"}
@@ -58,6 +58,8 @@ export declare function sanitizeDetectedBranch(value: string | null | undefined)
58
58
  * (the normal case). Throws {@link GitNexusRcError} on any problem.
59
59
  */
60
60
  export declare function loadAnalyzeConfig(repoRoot: string): Partial<AnalyzeOptions> | undefined;
61
+ /** Load `.gitnexusrc` through the strict bounded reader used by watch mode. */
62
+ export declare function loadAnalyzeConfigStrict(repoRoot: string): Promise<Partial<AnalyzeOptions> | undefined>;
61
63
  /**
62
64
  * Merge CLI options over `.gitnexusrc` defaults. CLI wins whenever it provides a
63
65
  * value — including an explicit `false` (so an explicit CLI off-switch beats a
@@ -29,6 +29,7 @@
29
29
  */
30
30
  import fs from 'node:fs';
31
31
  import path from 'node:path';
32
+ import { readRepoControlFile } from '../config/repo-control-file.js';
32
33
  export const GITNEXUS_RC_FILENAME = '.gitnexusrc';
33
34
  /** Final fallback when no branch is configured or detectable. */
34
35
  export const DEFAULT_BRANCH_FALLBACK = 'main';
@@ -328,6 +329,21 @@ export function loadAnalyzeConfig(repoRoot) {
328
329
  return undefined;
329
330
  throw new GitNexusRcError(`Could not read ${GITNEXUS_RC_FILENAME}: ${err.message}`);
330
331
  }
332
+ return parseAnalyzeConfig(raw);
333
+ }
334
+ /** Load `.gitnexusrc` through the strict bounded reader used by watch mode. */
335
+ export async function loadAnalyzeConfigStrict(repoRoot) {
336
+ let raw;
337
+ try {
338
+ raw = await readRepoControlFile(repoRoot, GITNEXUS_RC_FILENAME);
339
+ }
340
+ catch (err) {
341
+ throw new GitNexusRcError(`Could not read ${GITNEXUS_RC_FILENAME}: ${err.message}`);
342
+ }
343
+ return raw === null ? undefined : parseAnalyzeConfig(raw);
344
+ }
345
+ function parseAnalyzeConfig(rawInput) {
346
+ let raw = rawInput;
331
347
  // Strip a leading UTF-8 BOM: Node's 'utf-8' decode keeps it, and JSON.parse
332
348
  // then fails with a confusing "Unexpected token" on an otherwise-valid file
333
349
  // (#1996 tri-review). Only one leading BOM is stripped; in-string control
@@ -15,6 +15,10 @@
15
15
  * import cycle. `analyze.ts` re-exports the type for existing importers.
16
16
  */
17
17
  export interface AnalyzeOptions {
18
+ /** Keep this repository current with serialized incremental refreshes. */
19
+ watch?: boolean;
20
+ /** Watch quiet period in milliseconds. */
21
+ debounce?: string;
18
22
  force?: boolean;
19
23
  repairFts?: boolean;
20
24
  /**
@@ -25,6 +25,22 @@ export declare function computeHeapCapMb(totalBytes: number, constrainedBytes: n
25
25
  * later-flag-wins semantics when NODE_OPTIONS repeats a flag.
26
26
  */
27
27
  export declare function parseMaxOldSpaceMb(nodeOptions: string): number | null;
28
+ /** Re-exec the process with the RAM-aware auto heap cap + larger semi-space/stack
29
+ * if we're currently below that.
30
+ *
31
+ * Heap-source precedence (#2649):
32
+ * - an explicit per-invocation `--max-old-space-size` (execArgv) always wins;
33
+ * - `GITNEXUS_MEMORY=off` declines the memory autopilot entirely;
34
+ * - an ambient NODE_OPTIONS heap >= the auto cap is honored as-is;
35
+ * - an ambient NODE_OPTIONS heap BELOW the auto cap is treated as an
36
+ * inherited environment default (devcontainers/CI export one for other
37
+ * tooling), not a deliberate per-run choice: warn and respawn with the
38
+ * auto cap. Pre-#2649 this returned early and large repos then OOM'd on
39
+ * whatever heap the environment happened to specify. */
40
+ export declare function forwardedSignalExitCode(signal: NodeJS.Signals, cleanTermination: boolean): number;
41
+ export declare function ensureHeap(options?: {
42
+ cleanForwardedTermination?: boolean;
43
+ }): Promise<boolean>;
28
44
  /**
29
45
  * CLI `analyze` flag shape. Defined in `./analyze-options.js` so
30
46
  * `analyze-config.ts` can reference it without importing this module back —
@@ -50,3 +66,4 @@ export declare const shouldGenerateCommunitySkillFiles: (options: Pick<AnalyzeOp
50
66
  export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions, runnerIdentityAtBootstrap?: AnalyzerRunnerIdentity) => Promise<void>;
51
67
  /** Commander entrypoint used only by the capture-before-import lazy bootstrap. */
52
68
  export declare const analyzeCommandWithRunnerIdentity: (runnerIdentityAtBootstrap: AnalyzerRunnerIdentity, inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
69
+ export declare function analyzeOrWatchCommandWithRunnerIdentity(runnerIdentityAtBootstrap: AnalyzerRunnerIdentity, inputPath?: string, options?: AnalyzeOptions): Promise<void>;
@@ -282,17 +282,29 @@ const runRespawnedAnalyze = (args, env) => new Promise((resolve) => {
282
282
  let stdout = '';
283
283
  let stderr = '';
284
284
  let settled = false;
285
- const finish = (exit) => {
286
- if (settled)
287
- return;
288
- settled = true;
289
- resolve(exit);
290
- };
285
+ let forwardedSignal;
291
286
  const child = spawn(process.execPath, [...args], {
292
287
  stdio: ['inherit', 'pipe', 'pipe'],
293
288
  windowsHide: true,
294
289
  env,
295
290
  });
291
+ const forwardSignal = (signal) => {
292
+ forwardedSignal ??= signal;
293
+ if (child.exitCode === null && child.signalCode === null)
294
+ child.kill(signal);
295
+ };
296
+ const forwardSigint = () => forwardSignal('SIGINT');
297
+ const forwardSigterm = () => forwardSignal('SIGTERM');
298
+ const finish = (exit) => {
299
+ if (settled)
300
+ return;
301
+ settled = true;
302
+ process.removeListener('SIGINT', forwardSigint);
303
+ process.removeListener('SIGTERM', forwardSigterm);
304
+ resolve({ ...exit, forwardedSignal });
305
+ };
306
+ process.once('SIGINT', forwardSigint);
307
+ process.once('SIGTERM', forwardSigterm);
296
308
  child.stdout?.on('data', (chunk) => {
297
309
  stdout = appendOutputTail(stdout, chunk);
298
310
  realStdoutWrite(chunk);
@@ -412,7 +424,16 @@ export function parseMaxOldSpaceMb(nodeOptions) {
412
424
  * tooling), not a deliberate per-run choice: warn and respawn with the
413
425
  * auto cap. Pre-#2649 this returned early and large repos then OOM'd on
414
426
  * whatever heap the environment happened to specify. */
415
- async function ensureHeap() {
427
+ export function forwardedSignalExitCode(signal, cleanTermination) {
428
+ if (cleanTermination)
429
+ return 0;
430
+ if (signal === 'SIGINT')
431
+ return 130;
432
+ if (signal === 'SIGTERM')
433
+ return 143;
434
+ return 1;
435
+ }
436
+ export async function ensureHeap(options = {}) {
416
437
  // Explicit opt-out disables auto-sizing ENTIRELY — both the ambient-pin
417
438
  // override and the default v8-limit respawn — and is honored SILENTLY:
418
439
  // the operator already made the call, and stderr-sensitive consumers
@@ -456,6 +477,10 @@ async function ensureHeap() {
456
477
  if (shouldBridgeRespawnProgressTty())
457
478
  childEnv[RESPAWN_PROGRESS_ENV] = '1';
458
479
  const childExit = await runRespawnedAnalyze(childArgs, childEnv);
480
+ if (childExit.forwardedSignal !== undefined) {
481
+ process.exitCode = forwardedSignalExitCode(childExit.forwardedSignal, options.cleanForwardedTermination === true);
482
+ return true;
483
+ }
459
484
  if (childExit.status !== 0 || childExit.signal) {
460
485
  if (childProcessLikelyOom(childExit)) {
461
486
  cliError(` Analysis likely ran out of memory (heap cap auto-sized to ${RESPAWN_HEAP_MB}MB ≈ 0.75x RAM).\n` +
@@ -573,6 +598,14 @@ export const analyzeCommand = async (inputPath, options, runnerIdentityAtBootstr
573
598
  };
574
599
  /** Commander entrypoint used only by the capture-before-import lazy bootstrap. */
575
600
  export const analyzeCommandWithRunnerIdentity = async (runnerIdentityAtBootstrap, inputPath, options) => analyzeCommand(inputPath, options, runnerIdentityAtBootstrap);
601
+ export async function analyzeOrWatchCommandWithRunnerIdentity(runnerIdentityAtBootstrap, inputPath, options = {}) {
602
+ if (options.watch) {
603
+ const { watchCommandWithRunnerIdentity } = await import('./watch.js');
604
+ await watchCommandWithRunnerIdentity(runnerIdentityAtBootstrap, inputPath, options);
605
+ return;
606
+ }
607
+ await analyzeCommandWithRunnerIdentity(runnerIdentityAtBootstrap, inputPath, options);
608
+ }
576
609
  const analyzeCommandImpl = async (inputPath, cliOptions, runnerIdentityAtBootstrap) => {
577
610
  console.log('\n GitNexus Analyzer\n');
578
611
  // ── Resolve the target repo root ──────────────────────────────────
@@ -68,6 +68,8 @@ const OPTION_DESCRIPTION_KEYS = {
68
68
  'analyze|--embedding-batch-size <n>': 'help.option.analyze.embeddingBatchSize',
69
69
  'analyze|--embedding-sub-batch-size <n>': 'help.option.analyze.embeddingSubBatchSize',
70
70
  'analyze|--embedding-device <device>': 'help.option.analyze.embeddingDevice',
71
+ 'analyze|--watch': 'help.option.analyze.watch',
72
+ 'analyze|--debounce <ms>': 'help.option.analyze.debounce',
71
73
  'index|-f, --force': 'help.option.index.force',
72
74
  'index|--allow-non-git': 'help.option.index.allowNonGit',
73
75
  'mcp|--http': 'help.option.mcp.http',
@@ -171,6 +171,8 @@ export declare const en: {
171
171
  readonly 'help.option.analyze.embeddingBatchSize': "Number of nodes per embedding batch";
172
172
  readonly 'help.option.analyze.embeddingSubBatchSize': "Number of chunks per embedding model call";
173
173
  readonly 'help.option.analyze.embeddingDevice': "Embedding device: auto, cpu, dml, cuda, or wasm";
174
+ readonly 'help.option.analyze.watch': "Keep the index current with serialized incremental refreshes";
175
+ readonly 'help.option.analyze.debounce': "Watch quiet period before refreshing (milliseconds)";
174
176
  readonly 'help.option.index.force': "Register even if index metadata is missing (stats will be empty)";
175
177
  readonly 'help.option.index.allowNonGit': "Allow registering folders that are not Git repositories";
176
178
  readonly 'help.option.port': "Port number";
@@ -173,6 +173,8 @@ export const en = {
173
173
  'help.option.analyze.embeddingBatchSize': 'Number of nodes per embedding batch',
174
174
  'help.option.analyze.embeddingSubBatchSize': 'Number of chunks per embedding model call',
175
175
  'help.option.analyze.embeddingDevice': 'Embedding device: auto, cpu, dml, cuda, or wasm',
176
+ 'help.option.analyze.watch': 'Keep the index current with serialized incremental refreshes',
177
+ 'help.option.analyze.debounce': 'Watch quiet period before refreshing (milliseconds)',
176
178
  'help.option.index.force': 'Register even if index metadata is missing (stats will be empty)',
177
179
  'help.option.index.allowNonGit': 'Allow registering folders that are not Git repositories',
178
180
  'help.option.port': 'Port number',
@@ -172,6 +172,8 @@ export declare const cliResources: {
172
172
  readonly 'help.option.analyze.embeddingBatchSize': "Number of nodes per embedding batch";
173
173
  readonly 'help.option.analyze.embeddingSubBatchSize': "Number of chunks per embedding model call";
174
174
  readonly 'help.option.analyze.embeddingDevice': "Embedding device: auto, cpu, dml, cuda, or wasm";
175
+ readonly 'help.option.analyze.watch': "Keep the index current with serialized incremental refreshes";
176
+ readonly 'help.option.analyze.debounce': "Watch quiet period before refreshing (milliseconds)";
175
177
  readonly 'help.option.index.force': "Register even if index metadata is missing (stats will be empty)";
176
178
  readonly 'help.option.index.allowNonGit': "Allow registering folders that are not Git repositories";
177
179
  readonly 'help.option.port': "Port number";
@@ -426,6 +428,8 @@ export declare const cliResources: {
426
428
  'help.option.analyze.embeddingBatchSize': string;
427
429
  'help.option.analyze.embeddingSubBatchSize': string;
428
430
  'help.option.analyze.embeddingDevice': string;
431
+ 'help.option.analyze.watch': string;
432
+ 'help.option.analyze.debounce': string;
429
433
  'help.option.index.force': string;
430
434
  'help.option.index.allowNonGit': string;
431
435
  'help.option.port': string;
@@ -171,6 +171,8 @@ export declare const zhCN: {
171
171
  'help.option.analyze.embeddingBatchSize': string;
172
172
  'help.option.analyze.embeddingSubBatchSize': string;
173
173
  'help.option.analyze.embeddingDevice': string;
174
+ 'help.option.analyze.watch': string;
175
+ 'help.option.analyze.debounce': string;
174
176
  'help.option.index.force': string;
175
177
  'help.option.index.allowNonGit': string;
176
178
  'help.option.port': string;
@@ -171,6 +171,8 @@ export const zhCN = {
171
171
  'help.option.analyze.embeddingBatchSize': '每个嵌入批次的节点数',
172
172
  'help.option.analyze.embeddingSubBatchSize': '每次嵌入模型调用的分块数',
173
173
  'help.option.analyze.embeddingDevice': '嵌入设备:auto、cpu、dml、cuda 或 wasm',
174
+ 'help.option.analyze.watch': '监视本地源文件变更并串行执行增量刷新',
175
+ 'help.option.analyze.debounce': '刷新前的静默等待时间(毫秒)',
174
176
  'help.option.index.force': '即使缺少索引元数据也注册(统计为空)',
175
177
  'help.option.index.allowNonGit': '允许注册非 Git 仓库文件夹',
176
178
  'help.option.port': '端口号',
package/dist/cli/index.js CHANGED
@@ -36,6 +36,8 @@ let dimsEnvCaptured = false;
36
36
  program
37
37
  .command('analyze [path]')
38
38
  .description('Index a repository (full analysis)')
39
+ .option('--watch', 'Keep the index current with serialized incremental refreshes')
40
+ .option('--debounce <ms>', 'Watch quiet period before refreshing (default: 300 milliseconds)')
39
41
  .option('-f, --force', 'Force full re-index even if up to date')
40
42
  .option('--repair-fts', 'Repair/rebuild search FTS indexes without full re-analysis')
41
43
  .option('--embeddings [limit]', 'Enable embedding generation for semantic search (off by default). ' +
@@ -84,6 +86,11 @@ program
84
86
  'Must match what the index was built with. Overrides GITNEXUS_EMBEDDING_DIMS.')
85
87
  .addHelpText('after', () => t('help.analyze.environment'))
86
88
  .hook('preAction', (thisCommand) => {
89
+ const analyzeOpts = thisCommand.opts();
90
+ if (analyzeOpts['debounce'] !== undefined && analyzeOpts['watch'] !== true) {
91
+ process.stderr.write('\n --debounce requires --watch\n\n');
92
+ process.exit(1);
93
+ }
87
94
  // ONLY GITNEXUS_EMBEDDING_DIMS must be set here: schema.ts reads it at
88
95
  // module-load time during the lazy import('./analyze.js') below (via the
89
96
  // static chain analyze.ts → run-analyze.ts → schema.ts), so deferring to
@@ -91,7 +98,7 @@ program
91
98
  // lazily at runtime (readConfig), so analyzeCommandImpl is their sole
92
99
  // setter — keeping them out of this hook means they fall under the impl's
93
100
  // env snapshot/restore and don't leak across in-process invocations.
94
- const dimsOpt = thisCommand.opts()['embeddingDims'];
101
+ const dimsOpt = analyzeOpts['embeddingDims'];
95
102
  if (dimsOpt !== undefined) {
96
103
  // Validate + normalize BEFORE writing the env var: schema.ts throws on a
97
104
  // bad value at module-load, which — on the synchronous program.parse()
@@ -122,7 +129,7 @@ program
122
129
  process.env.GITNEXUS_EMBEDDING_DIMS = dimsEnvBaseline;
123
130
  }
124
131
  })
125
- .action(createAnalyzerLbugLazyAction(() => import('../core/analyzer-identity.js'), () => import('./analyze.js'), 'analyzeCommandWithRunnerIdentity', import.meta.url));
132
+ .action(createAnalyzerLbugLazyAction(() => import('../core/analyzer-identity.js'), () => import('./analyze.js'), 'analyzeOrWatchCommandWithRunnerIdentity', import.meta.url));
126
133
  program
127
134
  .command('index [path...]')
128
135
  .description('Register an existing .gitnexus/ folder into the global registry (no re-analysis needed)')
@@ -0,0 +1,41 @@
1
+ export type WatchRefresh = (paths: readonly string[]) => Promise<void>;
2
+ export type WatchRefreshError = (error: unknown, paths: readonly string[]) => void;
3
+ export declare const WATCH_FULL_REFRESH_PATH = "*";
4
+ export interface WatchRefreshQueueOptions {
5
+ readonly maxWaitMs?: number;
6
+ readonly maxPendingPaths?: number;
7
+ readonly retryBaseDelayMs?: number;
8
+ readonly retryMaxDelayMs?: number;
9
+ readonly holdEventsUntilInitialRefresh?: boolean;
10
+ readonly isPriorityPath?: (filePath: string) => boolean;
11
+ }
12
+ /** Debounces filesystem events and guarantees that refreshes never overlap. */
13
+ export declare class WatchRefreshQueue {
14
+ private readonly refresh;
15
+ private readonly onError;
16
+ private readonly debounceMs;
17
+ private readonly options;
18
+ private readonly pending;
19
+ private readonly idleWaiters;
20
+ private timer;
21
+ private active;
22
+ private closed;
23
+ private initialPending;
24
+ private firstPendingAt;
25
+ private overflowed;
26
+ private consecutiveFailures;
27
+ private retryNotBefore;
28
+ constructor(refresh: WatchRefresh, onError: WatchRefreshError, debounceMs: number, options?: WatchRefreshQueueOptions);
29
+ enqueue(filePath: string): void;
30
+ private addPendingPath;
31
+ /** Run the initial refresh while still queueing events that arrive during it. */
32
+ runInitial(): Promise<void>;
33
+ waitForIdle(): Promise<void>;
34
+ close(): Promise<void>;
35
+ private schedule;
36
+ private drain;
37
+ private runBatch;
38
+ private hasPendingWork;
39
+ private isIdle;
40
+ private resolveIdleWaiters;
41
+ }