@wrongstack/tools 0.77.0 → 0.82.6

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 (93) hide show
  1. package/dist/audit.d.ts +4 -4
  2. package/dist/audit.js +10 -2
  3. package/dist/audit.js.map +1 -1
  4. package/dist/{background-indexer-C70RD7LU.d.ts → background-indexer-DYm1FUxK.d.ts} +19 -19
  5. package/dist/bash.d.ts +4 -4
  6. package/dist/bash.js +18 -4
  7. package/dist/bash.js.map +1 -1
  8. package/dist/batch-tool-use.d.ts +4 -4
  9. package/dist/batch-tool-use.js.map +1 -1
  10. package/dist/builtin.js +173 -70
  11. package/dist/builtin.js.map +1 -1
  12. package/dist/circuit-breaker.d.ts +6 -6
  13. package/dist/circuit-breaker.js.map +1 -1
  14. package/dist/codebase-index/index.d.ts +12 -12
  15. package/dist/codebase-index/index.js +79 -41
  16. package/dist/codebase-index/index.js.map +1 -1
  17. package/dist/diff.d.ts +7 -7
  18. package/dist/diff.js.map +1 -1
  19. package/dist/document.d.ts +6 -6
  20. package/dist/document.js.map +1 -1
  21. package/dist/edit.d.ts +1 -1
  22. package/dist/edit.js.map +1 -1
  23. package/dist/exec.d.ts +3 -3
  24. package/dist/exec.js +15 -3
  25. package/dist/exec.js.map +1 -1
  26. package/dist/fetch.d.ts +1 -1
  27. package/dist/fetch.js +3 -1
  28. package/dist/fetch.js.map +1 -1
  29. package/dist/format.d.ts +4 -4
  30. package/dist/format.js +18 -4
  31. package/dist/format.js.map +1 -1
  32. package/dist/git.d.ts +10 -10
  33. package/dist/git.js +8 -2
  34. package/dist/git.js.map +1 -1
  35. package/dist/glob.d.ts +2 -2
  36. package/dist/glob.js.map +1 -1
  37. package/dist/grep.d.ts +6 -6
  38. package/dist/grep.js +10 -2
  39. package/dist/grep.js.map +1 -1
  40. package/dist/index.d.ts +7 -7
  41. package/dist/index.js +173 -70
  42. package/dist/index.js.map +1 -1
  43. package/dist/install.d.ts +5 -5
  44. package/dist/install.js +18 -4
  45. package/dist/install.js.map +1 -1
  46. package/dist/json.d.ts +8 -8
  47. package/dist/json.js.map +1 -1
  48. package/dist/lint.d.ts +4 -4
  49. package/dist/lint.js +18 -4
  50. package/dist/lint.js.map +1 -1
  51. package/dist/logs.d.ts +8 -8
  52. package/dist/logs.js.map +1 -1
  53. package/dist/memory.d.ts +2 -2
  54. package/dist/memory.js.map +1 -1
  55. package/dist/mode.d.ts +3 -3
  56. package/dist/mode.js.map +1 -1
  57. package/dist/outdated.d.ts +4 -4
  58. package/dist/outdated.js.map +1 -1
  59. package/dist/pack.js +173 -70
  60. package/dist/pack.js.map +1 -1
  61. package/dist/patch.d.ts +3 -3
  62. package/dist/patch.js.map +1 -1
  63. package/dist/process-registry.d.ts +3 -3
  64. package/dist/process-registry.js +7 -1
  65. package/dist/process-registry.js.map +1 -1
  66. package/dist/read.d.ts +2 -2
  67. package/dist/read.js.map +1 -1
  68. package/dist/replace.d.ts +4 -4
  69. package/dist/replace.js +8 -2
  70. package/dist/replace.js.map +1 -1
  71. package/dist/scaffold.d.ts +2 -2
  72. package/dist/scaffold.js.map +1 -1
  73. package/dist/search.d.ts +2 -2
  74. package/dist/search.js +16 -8
  75. package/dist/search.js.map +1 -1
  76. package/dist/test.d.ts +7 -7
  77. package/dist/test.js +18 -4
  78. package/dist/test.js.map +1 -1
  79. package/dist/tool-help.d.ts +4 -4
  80. package/dist/tool-help.js.map +1 -1
  81. package/dist/tool-search.d.ts +5 -5
  82. package/dist/tool-search.js.map +1 -1
  83. package/dist/tool-use.d.ts +2 -2
  84. package/dist/tool-use.js.map +1 -1
  85. package/dist/tree.d.ts +7 -7
  86. package/dist/tree.js +10 -2
  87. package/dist/tree.js.map +1 -1
  88. package/dist/typecheck.d.ts +4 -4
  89. package/dist/typecheck.js +18 -4
  90. package/dist/typecheck.js.map +1 -1
  91. package/dist/write.d.ts +1 -1
  92. package/dist/write.js.map +1 -1
  93. package/package.json +2 -2
@@ -20,32 +20,32 @@ interface CircuitBreakerConfig {
20
20
  * Consecutive failures before trip. Default: 5.
21
21
  * A single success resets this counter to 0.
22
22
  */
23
- maxConsecutiveFailures?: number;
23
+ maxConsecutiveFailures?: number | undefined;
24
24
  /**
25
25
  * Slow-call threshold in ms. A call that runs longer than this is
26
26
  * counted as "slow". Default: 60_000 (1 minute).
27
27
  */
28
- slowCallThresholdMs?: number;
28
+ slowCallThresholdMs?: number | undefined;
29
29
  /**
30
30
  * Max slow calls before trip (within the sliding window). Default: 3.
31
31
  */
32
- maxSlowCalls?: number;
32
+ maxSlowCalls?: number | undefined;
33
33
  /**
34
34
  * Sliding window for rate-limit and slow-call counting, in ms.
35
35
  * Default: 60_000 (1 minute).
36
36
  */
37
- windowMs?: number;
37
+ windowMs?: number | undefined;
38
38
  /**
39
39
  * Max calls within the sliding window. Default: 30.
40
40
  * Burst exceeding this trips the breaker immediately.
41
41
  */
42
- maxCallsPerWindow?: number;
42
+ maxCallsPerWindow?: number | undefined;
43
43
  /**
44
44
  * Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).
45
45
  * After this the breaker enters "half-open" state and allows one call
46
46
  * through to test whether the problem is resolved.
47
47
  */
48
- cooldownMs?: number;
48
+ cooldownMs?: number | undefined;
49
49
  }
50
50
  interface CircuitBreakerSnapshot {
51
51
  state: 'closed' | 'open' | 'half-open';
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"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","file":"circuit-breaker.js","sourcesContent":["/**\n * CircuitBreaker — prevents runaway bash/exec tool chains by:\n *\n * - Tripping on consecutive failures (models that keep repeating the\n * same failing command, e.g. `npm install` with wrong args in a loop)\n * - Tripping on slow call ratio (too many long-running commands suggest\n * a hung subprocess that the model doesn't know how to kill)\n * - Rate-limiting bursts (rapid succession of commands without reading\n * output suggests the model isn't processing results)\n * - Auto-recovering after a cooldown period so a fixed model can resume\n *\n * The breaker is owned by the ProcessRegistry so any tool that registers\n * a process participates in the same circuit. \"Per-tool\" isolation is\n * intentionally NOT implemented — the model treats bash/exec as one\n * resource pool; isolating them would let the model route around the\n * breaker by alternating which tool it uses.\n */\n\nexport interface CircuitBreakerConfig {\n /**\n * Consecutive failures before trip. Default: 5.\n * A single success resets this counter to 0.\n */\n maxConsecutiveFailures?: number;\n /**\n * Slow-call threshold in ms. A call that runs longer than this is\n * counted as \"slow\". Default: 60_000 (1 minute).\n */\n slowCallThresholdMs?: number;\n /**\n * Max slow calls before trip (within the sliding window). Default: 3.\n */\n maxSlowCalls?: number;\n /**\n * Sliding window for rate-limit and slow-call counting, in ms.\n * Default: 60_000 (1 minute).\n */\n windowMs?: number;\n /**\n * Max calls within the sliding window. Default: 30.\n * Burst exceeding this trips the breaker immediately.\n */\n maxCallsPerWindow?: number;\n /**\n * Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).\n * After this the breaker enters \"half-open\" state and allows one call\n * through to test whether the problem is resolved.\n */\n cooldownMs?: number;\n}\n\ninterface CallRecord {\n at: number;\n /** True if the call threw or returned an is_error result. */\n failed: boolean;\n /** True if elapsed time exceeded slowCallThresholdMs. */\n slow: boolean;\n}\n\ntype BreakerState = 'closed' | 'open' | 'half-open';\n\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;\nconst DEFAULT_SLOW_CALL_THRESHOLD_MS = 60_000;\nconst DEFAULT_MAX_SLOW_CALLS = 3;\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_CALLS_PER_WINDOW = 30;\nconst DEFAULT_COOLDOWN_MS = 30_000;\n\nexport interface CircuitBreakerSnapshot {\n state: 'closed' | 'open' | 'half-open';\n consecutiveFailures: number;\n slowCallsInWindow: number;\n callsInWindow: number;\n windowMs: number;\n cooldownRemainingMs: number | null;\n lastFailureAt: number | null;\n lastSlowAt: number | null;\n}\n\nexport class CircuitBreaker {\n private readonly maxConsecutiveFailures: number;\n private readonly slowCallThresholdMs: number;\n private readonly maxSlowCalls: number;\n private readonly windowMs: number;\n private readonly maxCallsPerWindow: number;\n private readonly cooldownMs: number;\n\n private state: BreakerState = 'closed';\n private consecutiveFailures = 0;\n private window: CallRecord[] = [];\n private lastFailureAt: number | null = null;\n private lastSlowAt: number | null = null;\n /** Timestamp when the breaker was opened (for cooldown calculation). */\n private openedAt: number | null = null;\n\n constructor(config: CircuitBreakerConfig = {}) {\n this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;\n this.slowCallThresholdMs = config.slowCallThresholdMs ?? DEFAULT_SLOW_CALL_THRESHOLD_MS;\n this.maxSlowCalls = config.maxSlowCalls ?? DEFAULT_MAX_SLOW_CALLS;\n this.windowMs = config.windowMs ?? DEFAULT_WINDOW_MS;\n this.maxCallsPerWindow = config.maxCallsPerWindow ?? DEFAULT_MAX_CALLS_PER_WINDOW;\n this.cooldownMs = config.cooldownMs ?? DEFAULT_COOLDOWN_MS;\n }\n\n /**\n * Returns true if the circuit allows a new call to proceed.\n * When false, callers should abort the tool call and return a\n * circuit-breaker error instead of spawning a process.\n */\n get canProceed(): boolean {\n this._checkStateTransition();\n return this.state !== 'open';\n }\n\n /**\n * Snapshot of the current breaker state for observability (`/kill`).\n */\n snapshot(): CircuitBreakerSnapshot {\n this._checkStateTransition();\n const now = Date.now();\n let cooldownRemaining: number | null = null;\n if (this.openedAt !== null && this.state === 'open') {\n const elapsed = now - this.openedAt;\n cooldownRemaining = Math.max(0, this.cooldownMs - elapsed);\n }\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n slowCallsInWindow: this.window.filter((c) => c.slow).length,\n callsInWindow: this.window.length,\n windowMs: this.windowMs,\n cooldownRemainingMs: cooldownRemaining,\n lastFailureAt: this.lastFailureAt,\n lastSlowAt: this.lastSlowAt,\n };\n }\n\n /**\n * Call this BEFORE spawning a bash/exec process.\n * Returns true if the call is allowed; false if the breaker is open.\n * When false, callers MUST NOT spawn a process.\n */\n beforeCall(): boolean {\n this._checkStateTransition();\n if (this.state === 'open') return false;\n return true;\n }\n\n /**\n * Call this AFTER a bash/exec process finishes (success or failure).\n * `durationMs` is the wall-clock time the process ran.\n * `failed` is true when the process returned a non-zero exit code or\n * threw an exception before spawning.\n */\n afterCall(durationMs: number, failed: boolean): void {\n const now = Date.now();\n\n if (this.state === 'half-open') {\n // First call through after cooldown — if it failed, go back to open.\n if (failed) {\n this._trip();\n return;\n }\n // Success in half-open → reset to closed.\n this._reset();\n return;\n }\n\n // Prune old records outside the sliding window.\n this._pruneWindow(now);\n\n const slow = durationMs >= this.slowCallThresholdMs;\n this.window.push({ at: now, failed, slow });\n\n if (failed) {\n this.consecutiveFailures++;\n this.lastFailureAt = now;\n if (this.consecutiveFailures >= this.maxConsecutiveFailures) {\n this._trip();\n }\n return;\n }\n\n // Success: reset consecutive failure counter.\n this.consecutiveFailures = 0;\n\n if (slow) {\n this.lastSlowAt = now;\n const slowCount = this.window.filter((c) => c.slow).length;\n if (slowCount >= this.maxSlowCalls) {\n this._trip();\n }\n }\n\n const callCount = this.window.length;\n if (callCount >= this.maxCallsPerWindow) {\n // Rate limit exceeded. This is a soft trip — we reset the window\n // and let the next call try immediately (the caller will still see\n // canProceed=false until the window drains naturally).\n this._trip();\n }\n }\n\n /** Force the breaker open. Used by /kill force and Ctrl+C. */\n forceOpen(): void {\n this._trip();\n }\n\n /** Force a reset to closed. Used by tests and /kill reset. */\n forceReset(): void {\n this._reset();\n }\n\n private _trip(): void {\n if (this.state === 'open') return; // already open\n this.state = 'open';\n this.openedAt = Date.now();\n }\n\n private _reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.window = [];\n this.openedAt = null;\n }\n\n /** Transition from open → half-open when cooldown elapses. */\n private _checkStateTransition(): void {\n if (this.state !== 'open' || this.openedAt === null) return;\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = 'half-open';\n this.openedAt = null;\n }\n }\n\n private _pruneWindow(now: number): void {\n const cutoff = now - this.windowMs;\n this.window = this.window.filter((c) => c.at >= cutoff);\n }\n}"]}
1
+ {"version":3,"sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"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","file":"circuit-breaker.js","sourcesContent":["/**\n * CircuitBreaker — prevents runaway bash/exec tool chains by:\n *\n * - Tripping on consecutive failures (models that keep repeating the\n * same failing command, e.g. `npm install` with wrong args in a loop)\n * - Tripping on slow call ratio (too many long-running commands suggest\n * a hung subprocess that the model doesn't know how to kill)\n * - Rate-limiting bursts (rapid succession of commands without reading\n * output suggests the model isn't processing results)\n * - Auto-recovering after a cooldown period so a fixed model can resume\n *\n * The breaker is owned by the ProcessRegistry so any tool that registers\n * a process participates in the same circuit. \"Per-tool\" isolation is\n * intentionally NOT implemented — the model treats bash/exec as one\n * resource pool; isolating them would let the model route around the\n * breaker by alternating which tool it uses.\n */\n\nexport interface CircuitBreakerConfig {\n /**\n * Consecutive failures before trip. Default: 5.\n * A single success resets this counter to 0.\n */\n maxConsecutiveFailures?: number | undefined;\n /**\n * Slow-call threshold in ms. A call that runs longer than this is\n * counted as \"slow\". Default: 60_000 (1 minute).\n */\n slowCallThresholdMs?: number | undefined;\n /**\n * Max slow calls before trip (within the sliding window). Default: 3.\n */\n maxSlowCalls?: number | undefined;\n /**\n * Sliding window for rate-limit and slow-call counting, in ms.\n * Default: 60_000 (1 minute).\n */\n windowMs?: number | undefined;\n /**\n * Max calls within the sliding window. Default: 30.\n * Burst exceeding this trips the breaker immediately.\n */\n maxCallsPerWindow?: number | undefined;\n /**\n * Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).\n * After this the breaker enters \"half-open\" state and allows one call\n * through to test whether the problem is resolved.\n */\n cooldownMs?: number | undefined;\n}\n\ninterface CallRecord {\n at: number;\n /** True if the call threw or returned an is_error result. */\n failed: boolean;\n /** True if elapsed time exceeded slowCallThresholdMs. */\n slow: boolean;\n}\n\ntype BreakerState = 'closed' | 'open' | 'half-open';\n\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;\nconst DEFAULT_SLOW_CALL_THRESHOLD_MS = 60_000;\nconst DEFAULT_MAX_SLOW_CALLS = 3;\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_CALLS_PER_WINDOW = 30;\nconst DEFAULT_COOLDOWN_MS = 30_000;\n\nexport interface CircuitBreakerSnapshot {\n state: 'closed' | 'open' | 'half-open';\n consecutiveFailures: number;\n slowCallsInWindow: number;\n callsInWindow: number;\n windowMs: number;\n cooldownRemainingMs: number | null;\n lastFailureAt: number | null;\n lastSlowAt: number | null;\n}\n\nexport class CircuitBreaker {\n private readonly maxConsecutiveFailures: number;\n private readonly slowCallThresholdMs: number;\n private readonly maxSlowCalls: number;\n private readonly windowMs: number;\n private readonly maxCallsPerWindow: number;\n private readonly cooldownMs: number;\n\n private state: BreakerState = 'closed';\n private consecutiveFailures = 0;\n private window: CallRecord[] = [];\n private lastFailureAt: number | null = null;\n private lastSlowAt: number | null = null;\n /** Timestamp when the breaker was opened (for cooldown calculation). */\n private openedAt: number | null = null;\n\n constructor(config: CircuitBreakerConfig = {}) {\n this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;\n this.slowCallThresholdMs = config.slowCallThresholdMs ?? DEFAULT_SLOW_CALL_THRESHOLD_MS;\n this.maxSlowCalls = config.maxSlowCalls ?? DEFAULT_MAX_SLOW_CALLS;\n this.windowMs = config.windowMs ?? DEFAULT_WINDOW_MS;\n this.maxCallsPerWindow = config.maxCallsPerWindow ?? DEFAULT_MAX_CALLS_PER_WINDOW;\n this.cooldownMs = config.cooldownMs ?? DEFAULT_COOLDOWN_MS;\n }\n\n /**\n * Returns true if the circuit allows a new call to proceed.\n * When false, callers should abort the tool call and return a\n * circuit-breaker error instead of spawning a process.\n */\n get canProceed(): boolean {\n this._checkStateTransition();\n return this.state !== 'open';\n }\n\n /**\n * Snapshot of the current breaker state for observability (`/kill`).\n */\n snapshot(): CircuitBreakerSnapshot {\n this._checkStateTransition();\n const now = Date.now();\n let cooldownRemaining: number | null = null;\n if (this.openedAt !== null && this.state === 'open') {\n const elapsed = now - this.openedAt;\n cooldownRemaining = Math.max(0, this.cooldownMs - elapsed);\n }\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n slowCallsInWindow: this.window.filter((c) => c.slow).length,\n callsInWindow: this.window.length,\n windowMs: this.windowMs,\n cooldownRemainingMs: cooldownRemaining,\n lastFailureAt: this.lastFailureAt,\n lastSlowAt: this.lastSlowAt,\n };\n }\n\n /**\n * Call this BEFORE spawning a bash/exec process.\n * Returns true if the call is allowed; false if the breaker is open.\n * When false, callers MUST NOT spawn a process.\n */\n beforeCall(): boolean {\n this._checkStateTransition();\n if (this.state === 'open') return false;\n return true;\n }\n\n /**\n * Call this AFTER a bash/exec process finishes (success or failure).\n * `durationMs` is the wall-clock time the process ran.\n * `failed` is true when the process returned a non-zero exit code or\n * threw an exception before spawning.\n */\n afterCall(durationMs: number, failed: boolean): void {\n const now = Date.now();\n\n if (this.state === 'half-open') {\n // First call through after cooldown — if it failed, go back to open.\n if (failed) {\n this._trip();\n return;\n }\n // Success in half-open → reset to closed.\n this._reset();\n return;\n }\n\n // Prune old records outside the sliding window.\n this._pruneWindow(now);\n\n const slow = durationMs >= this.slowCallThresholdMs;\n this.window.push({ at: now, failed, slow });\n\n if (failed) {\n this.consecutiveFailures++;\n this.lastFailureAt = now;\n if (this.consecutiveFailures >= this.maxConsecutiveFailures) {\n this._trip();\n }\n return;\n }\n\n // Success: reset consecutive failure counter.\n this.consecutiveFailures = 0;\n\n if (slow) {\n this.lastSlowAt = now;\n const slowCount = this.window.filter((c) => c.slow).length;\n if (slowCount >= this.maxSlowCalls) {\n this._trip();\n }\n }\n\n const callCount = this.window.length;\n if (callCount >= this.maxCallsPerWindow) {\n // Rate limit exceeded. This is a soft trip — we reset the window\n // and let the next call try immediately (the caller will still see\n // canProceed=false until the window drains naturally).\n this._trip();\n }\n }\n\n /** Force the breaker open. Used by /kill force and Ctrl+C. */\n forceOpen(): void {\n this._trip();\n }\n\n /** Force a reset to closed. Used by tests and /kill reset. */\n forceReset(): void {\n this._reset();\n }\n\n private _trip(): void {\n if (this.state === 'open') return; // already open\n this.state = 'open';\n this.openedAt = Date.now();\n }\n\n private _reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.window = [];\n this.openedAt = null;\n }\n\n /** Transition from open → half-open when cooldown elapses. */\n private _checkStateTransition(): void {\n if (this.state !== 'open' || this.openedAt === null) return;\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = 'half-open';\n this.openedAt = null;\n }\n }\n\n private _pruneWindow(now: number): void {\n const cutoff = now - this.windowMs;\n this.window = this.window.filter((c) => c.at >= cutoff);\n }\n}"]}
@@ -1,5 +1,5 @@
1
- import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-C70RD7LU.js';
2
- export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from '../background-indexer-C70RD7LU.js';
1
+ import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-DYm1FUxK.js';
2
+ export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from '../background-indexer-DYm1FUxK.js';
3
3
  import { Context } from '@wrongstack/core';
4
4
 
5
5
  /**
@@ -15,12 +15,12 @@ import { Context } from '@wrongstack/core';
15
15
 
16
16
  interface IndexerOptions {
17
17
  projectRoot: string;
18
- files?: string[];
19
- force?: boolean;
20
- langs?: string[];
21
- ignore?: string[];
18
+ files?: string[] | undefined;
19
+ force?: boolean | undefined;
20
+ langs?: string[] | undefined;
21
+ ignore?: string[] | undefined;
22
22
  /** Override the index directory (default: the global per-project dir). */
23
- indexDir?: string;
23
+ indexDir?: string | undefined;
24
24
  }
25
25
  /** Run a full or incremental index and return statistics. */
26
26
  declare function runIndexer(_ctx: Context, opts: IndexerOptions): Promise<IndexResult>;
@@ -54,7 +54,7 @@ declare class IndexStore {
54
54
  /** Absolute path to this project's index directory. */
55
55
  private readonly indexDir;
56
56
  constructor(projectRoot: string, opts?: {
57
- indexDir?: string;
57
+ indexDir?: string | undefined;
58
58
  });
59
59
  private initSchema;
60
60
  insertSymbols(symbols: Symbol[], nextId: number): number;
@@ -64,10 +64,10 @@ declare class IndexStore {
64
64
  getFileMeta(file: string): FileMeta | null;
65
65
  getAllFileMetas(): FileMeta[];
66
66
  search(query: string, filter?: {
67
- kind?: SymbolKind;
68
- lang?: SymbolLang;
69
- file?: string;
70
- lspKind?: number;
67
+ kind?: SymbolKind | undefined;
68
+ lang?: SymbolLang | undefined;
69
+ file?: string | undefined;
70
+ lspKind?: number | undefined;
71
71
  }): SearchResult[];
72
72
  getAllIndexable(): Array<{
73
73
  id: number;
@@ -81,6 +81,12 @@ function internalKindToLspKind(k) {
81
81
  }
82
82
 
83
83
  // src/codebase-index/writer.ts
84
+ function expectDefined(value) {
85
+ if (value === null || value === void 0) {
86
+ throw new Error("Expected value to be defined");
87
+ }
88
+ return value;
89
+ }
84
90
  var DB_FILE = "index.db";
85
91
  function resolveIndexDir(projectRoot, override) {
86
92
  return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
@@ -225,7 +231,7 @@ var IndexStore = class {
225
231
  "SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
226
232
  ).all(file);
227
233
  if (!rows.length) return null;
228
- const r = rows[0];
234
+ const r = expectDefined(rows[0]);
229
235
  return { file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed };
230
236
  }
231
237
  getAllFileMetas() {
@@ -307,7 +313,7 @@ var IndexStore = class {
307
313
  const lastRows = this.db.prepare(
308
314
  "SELECT value FROM metadata WHERE key = 'last_indexed'"
309
315
  ).all();
310
- const lastIndexed = lastRows.length ? Number(lastRows[0].value) : null;
316
+ const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
311
317
  const totalRows = this.db.prepare("SELECT COUNT(*) FROM symbols").all();
312
318
  const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
313
319
  const fileRows = this.db.prepare("SELECT COUNT(*) FROM files").all();
@@ -382,8 +388,9 @@ var IndexStore = class {
382
388
  let resolved = 0;
383
389
  for (const row of unresolved) {
384
390
  const target = this.db.prepare("SELECT id FROM symbols WHERE name = ? LIMIT 1").all(row.to_name);
385
- if (target.length) {
386
- this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(target[0].id, row.id);
391
+ const first = target[0];
392
+ if (first) {
393
+ this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(first.id, row.id);
387
394
  resolved++;
388
395
  }
389
396
  }
@@ -1137,6 +1144,12 @@ function syncPyParse(filePath, lang) {
1137
1144
  return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
1138
1145
  }
1139
1146
  }
1147
+ function expectDefined2(value) {
1148
+ if (value === null || value === void 0) {
1149
+ throw new Error("Expected value to be defined");
1150
+ }
1151
+ return value;
1152
+ }
1140
1153
  function parseSymbols4(opts) {
1141
1154
  const { file, content, lang } = opts;
1142
1155
  const nativeAvailable = checkNativeParser();
@@ -1218,14 +1231,14 @@ function regexParse(opts) {
1218
1231
  const lines = content.split("\n");
1219
1232
  const lineOffsets = [0];
1220
1233
  for (let i = 0; i < lines.length; i++) {
1221
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
1234
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
1222
1235
  }
1223
1236
  function lineFromOffset(offset) {
1224
1237
  let lo = 0;
1225
1238
  let hi = lineOffsets.length - 1;
1226
1239
  while (lo < hi) {
1227
1240
  const mid = lo + hi + 1 >>> 1;
1228
- if (lineOffsets[mid] <= offset) lo = mid;
1241
+ if (expectDefined2(lineOffsets[mid]) <= offset) lo = mid;
1229
1242
  else hi = mid - 1;
1230
1243
  }
1231
1244
  return lo + 1;
@@ -1237,8 +1250,8 @@ function regexParse(opts) {
1237
1250
  for (const pattern of RS_PATTERNS) {
1238
1251
  pattern.regex.lastIndex = 0;
1239
1252
  for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
1240
- const name = match[1];
1241
- const offset = match.index;
1253
+ const name = expectDefined2(match[1]);
1254
+ const offset = match.index ?? 0;
1242
1255
  const line = lineFromOffset(offset);
1243
1256
  const col = offset - (lineOffsets[line - 1] ?? 0);
1244
1257
  const lineIdx = line - 1;
@@ -1267,6 +1280,12 @@ function regexParse(opts) {
1267
1280
  });
1268
1281
  return { file, lang, symbols: deduped, mtimeMs: Date.now() };
1269
1282
  }
1283
+ function expectDefined3(value) {
1284
+ if (value === null || value === void 0) {
1285
+ throw new Error("Expected value to be defined");
1286
+ }
1287
+ return value;
1288
+ }
1270
1289
  function parseSymbols5(opts) {
1271
1290
  const { file, content, lang } = opts;
1272
1291
  try {
@@ -1286,21 +1305,21 @@ function regexParse2(opts) {
1286
1305
  const lines = content.split("\n");
1287
1306
  const lineOffsets = [0];
1288
1307
  for (let i = 0; i < lines.length; i++) {
1289
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
1308
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
1290
1309
  }
1291
1310
  function lineFromOffset(offset) {
1292
1311
  let lo = 0;
1293
1312
  let hi = lineOffsets.length - 1;
1294
1313
  while (lo < hi) {
1295
1314
  const mid = lo + hi + 1 >>> 1;
1296
- if (lineOffsets[mid] <= offset) lo = mid;
1315
+ if (expectDefined3(lineOffsets[mid]) <= offset) lo = mid;
1297
1316
  else hi = mid - 1;
1298
1317
  }
1299
1318
  return lo + 1;
1300
1319
  }
1301
1320
  const rootMatch = content.match(/^\s*\{/m);
1302
1321
  if (rootMatch) {
1303
- const offset = rootMatch.index;
1322
+ const offset = expectDefined3(rootMatch.index);
1304
1323
  const line = lineFromOffset(offset);
1305
1324
  symbols.push(
1306
1325
  makeSymbol({
@@ -1316,8 +1335,8 @@ function regexParse2(opts) {
1316
1335
  }
1317
1336
  const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
1318
1337
  for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
1319
- const key = match[1];
1320
- const offset = match.index;
1338
+ const key = expectDefined3(match[1]);
1339
+ const offset = match.index ?? 0;
1321
1340
  const line = lineFromOffset(offset);
1322
1341
  const col = offset - (lineOffsets[line - 1] ?? 0);
1323
1342
  let kind = "property";
@@ -1363,7 +1382,7 @@ function regexParse2(opts) {
1363
1382
  const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
1364
1383
  const defsMatch = defsRegex.exec(content);
1365
1384
  if (defsMatch !== null) {
1366
- const offset = defsMatch.index;
1385
+ const offset = expectDefined3(defsMatch.index);
1367
1386
  const line = lineFromOffset(offset);
1368
1387
  symbols.push(
1369
1388
  makeSymbol({
@@ -1386,9 +1405,9 @@ function regexParse2(opts) {
1386
1405
  for (const pat of defsPatterns) {
1387
1406
  pat.lastIndex = 0;
1388
1407
  for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
1389
- const offset = match.index;
1408
+ const offset = match.index ?? 0;
1390
1409
  const line = lineFromOffset(offset);
1391
- const key = match[0].match(/"([^"]+)"/)?.[1] ?? match[0];
1410
+ const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined3(match[0]);
1392
1411
  symbols.push(
1393
1412
  makeSymbol({
1394
1413
  name: key,
@@ -1407,12 +1426,12 @@ function regexParse2(opts) {
1407
1426
  function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
1408
1427
  const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
1409
1428
  for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
1410
- const blockContent = match[0];
1411
- const blockOffset = match.index;
1429
+ const blockContent = expectDefined3(match[0]);
1430
+ const blockOffset = match.index ?? 0;
1412
1431
  const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
1413
1432
  for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
1414
- const key = scriptMatch[1];
1415
- const keyOffset = blockOffset + scriptMatch.index;
1433
+ const key = expectDefined3(scriptMatch[1]);
1434
+ const keyOffset = blockOffset + expectDefined3(scriptMatch.index);
1416
1435
  const line = lineFromOffset(keyOffset);
1417
1436
  symbols.push(
1418
1437
  makeSymbol({
@@ -1431,12 +1450,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
1431
1450
  function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
1432
1451
  const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
1433
1452
  for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
1434
- const blockContent = match[0];
1435
- const blockOffset = match.index;
1453
+ const blockContent = expectDefined3(match[0]);
1454
+ const blockOffset = match.index ?? 0;
1436
1455
  const optKeyRegex = /"(\w[\w]*)"\s*:/g;
1437
1456
  for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
1438
- const key = optMatch[1];
1439
- const keyOffset = blockOffset + optMatch.index;
1457
+ const key = expectDefined3(optMatch[1]);
1458
+ const keyOffset = blockOffset + expectDefined3(optMatch.index);
1440
1459
  const line = lineFromOffset(keyOffset);
1441
1460
  if (line <= parentLine) continue;
1442
1461
  symbols.push(
@@ -1470,6 +1489,12 @@ function makeSymbol(opts) {
1470
1489
  }
1471
1490
 
1472
1491
  // src/codebase-index/yaml-parser.ts
1492
+ function expectDefined4(value) {
1493
+ if (value === null || value === void 0) {
1494
+ throw new Error("Expected value to be defined");
1495
+ }
1496
+ return value;
1497
+ }
1473
1498
  function parseSymbols6(opts) {
1474
1499
  const { file, content, lang } = opts;
1475
1500
  try {
@@ -1484,22 +1509,22 @@ function regexParse3(opts) {
1484
1509
  const lines = content.split("\n");
1485
1510
  const lineOffsets = [0];
1486
1511
  for (let i = 0; i < lines.length; i++) {
1487
- lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
1512
+ lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
1488
1513
  }
1489
1514
  function lineFromOffset(offset) {
1490
1515
  let lo = 0;
1491
1516
  let hi = lineOffsets.length - 1;
1492
1517
  while (lo < hi) {
1493
1518
  const mid = lo + hi + 1 >>> 1;
1494
- if (lineOffsets[mid] <= offset) lo = mid;
1519
+ if (expectDefined4(lineOffsets[mid]) <= offset) lo = mid;
1495
1520
  else hi = mid - 1;
1496
1521
  }
1497
1522
  return lo + 1;
1498
1523
  }
1499
1524
  const anchorRegex = /&(\w[\w-]*)/g;
1500
1525
  for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
1501
- const name = match[1];
1502
- const offset = match.index;
1526
+ const name = expectDefined4(match[1]);
1527
+ const offset = match.index ?? 0;
1503
1528
  const line = lineFromOffset(offset);
1504
1529
  const col = offset - (lineOffsets[line - 1] ?? 0);
1505
1530
  symbols.push(
@@ -1516,8 +1541,8 @@ function regexParse3(opts) {
1516
1541
  }
1517
1542
  const aliasRegex = /\*(\w[\w-]*)/g;
1518
1543
  for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
1519
- const name = match[1];
1520
- const offset = match.index;
1544
+ const name = expectDefined4(match[1]);
1545
+ const offset = match.index ?? 0;
1521
1546
  const line = lineFromOffset(offset);
1522
1547
  const col = offset - (lineOffsets[line - 1] ?? 0);
1523
1548
  symbols.push(
@@ -1534,27 +1559,28 @@ function regexParse3(opts) {
1534
1559
  }
1535
1560
  const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
1536
1561
  for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
1537
- const indent = match[1].length;
1562
+ const indent = match[1]?.length ?? 0;
1538
1563
  const key = match[2];
1539
- const offset = match.index;
1564
+ if (!key) continue;
1565
+ const offset = match.index ?? 0;
1540
1566
  const line = lineFromOffset(offset);
1541
1567
  const col = offset - (lineOffsets[line - 1] ?? 0);
1542
1568
  const lineContent = lines[line - 1] ?? "";
1543
1569
  if (/^[|&>]/.test(lineContent.trim())) continue;
1544
1570
  if (key === "---" || key === "...") continue;
1545
1571
  if (indent > 12) continue;
1546
- const value = extractValue(content, match.index);
1572
+ const value = extractValue(content, match.index ?? 0);
1547
1573
  const kind = isScalar(value) ? "literal" : "property";
1548
1574
  const signature = `${key}: ${truncate(value, 60)}`;
1549
1575
  symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
1550
1576
  }
1551
1577
  const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
1552
1578
  for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
1553
- const key = match[2];
1554
- const offset = match.index;
1579
+ const key = expectDefined4(match[2]);
1580
+ const offset = match.index ?? 0;
1555
1581
  const line = lineFromOffset(offset);
1556
1582
  const col = offset - (lineOffsets[line - 1] ?? 0);
1557
- const value = extractValue(content, offset + match[0].length);
1583
+ const value = extractValue(content, offset + match[0]?.length);
1558
1584
  const kind = isScalar(value) ? "literal" : "property";
1559
1585
  symbols.push(
1560
1586
  makeSymbol2({
@@ -1570,8 +1596,8 @@ function regexParse3(opts) {
1570
1596
  }
1571
1597
  const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
1572
1598
  for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
1573
- const key = match[2];
1574
- const offset = match.index;
1599
+ const key = expectDefined4(match[2]);
1600
+ const offset = match.index ?? 0;
1575
1601
  const line = lineFromOffset(offset);
1576
1602
  const col = offset - (lineOffsets[line - 1] ?? 0);
1577
1603
  symbols.push(
@@ -1790,6 +1816,12 @@ function cancelPendingReindexes() {
1790
1816
  }
1791
1817
 
1792
1818
  // src/codebase-index/indexer.ts
1819
+ function expectDefined5(value) {
1820
+ if (value === null || value === void 0) {
1821
+ throw new Error("Expected value to be defined");
1822
+ }
1823
+ return value;
1824
+ }
1793
1825
  var YIELD_EVERY_N = 50;
1794
1826
  function yieldEventLoop() {
1795
1827
  return new Promise((resolve2) => setImmediate(resolve2));
@@ -1898,7 +1930,7 @@ async function runIndexer(_ctx, opts) {
1898
1930
  for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
1899
1931
  }
1900
1932
  for (let fi = 0; fi < files.length; fi++) {
1901
- const file = files[fi];
1933
+ const file = expectDefined5(files[fi]);
1902
1934
  _setIndexProgress(fi + 1, files.length);
1903
1935
  if (fi > 0 && fi % YIELD_EVERY_N === 0) {
1904
1936
  await yieldEventLoop();
@@ -1955,7 +1987,7 @@ async function runIndexer(_ctx, opts) {
1955
1987
  langStats[lang] = (langStats[lang] ?? 0) + count;
1956
1988
  if (parsed.refs && parsed.refs.length > 0) {
1957
1989
  for (let i = 0; i < symbolsWithIds.length; i++) {
1958
- const sym = symbolsWithIds[i];
1990
+ const sym = expectDefined5(symbolsWithIds[i]);
1959
1991
  const symRefs = parsed.refs.filter((r) => r.line === sym.line);
1960
1992
  if (symRefs.length > 0) {
1961
1993
  const refsWithFromId = symRefs.map((r) => ({ ...r, fromId: sym.id }));
@@ -2126,6 +2158,12 @@ var Bm25Index = class {
2126
2158
  };
2127
2159
 
2128
2160
  // src/codebase-index/codebase-search-tool.ts
2161
+ function expectDefined6(value) {
2162
+ if (value === null || value === void 0) {
2163
+ throw new Error("Expected value to be defined");
2164
+ }
2165
+ return value;
2166
+ }
2129
2167
  var codebaseSearchTool = {
2130
2168
  name: "codebase-search",
2131
2169
  category: "Project",
@@ -2215,7 +2253,7 @@ var codebaseSearchTool = {
2215
2253
  const top = scored.slice(0, limit);
2216
2254
  const qTokens = tokenise(input.query);
2217
2255
  const results = top.map(({ id, score }) => {
2218
- const c = candidates.find((c2) => c2.id === id);
2256
+ const c = expectDefined6(candidates.find((c2) => c2.id === id));
2219
2257
  const snippet = bm25.extractSnippet(id, qTokens);
2220
2258
  return {
2221
2259
  ...c,