@wrongstack/tools 0.305.1 → 0.306.2

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 (63) hide show
  1. package/dist/_shell-pick.d.ts +4 -5
  2. package/dist/_util.d.ts +22 -5
  3. package/dist/audit.d.ts +0 -1
  4. package/dist/audit.js +135 -46
  5. package/dist/bash.js +61 -37
  6. package/dist/browser/index.js +29 -9
  7. package/dist/browser/types.d.ts +7 -1
  8. package/dist/builtin.js +1294 -726
  9. package/dist/codebase-index/codebase-search-tool.d.ts +5 -0
  10. package/dist/codebase-index/index.js +223 -152
  11. package/dist/codebase-index/project-server.js +10 -11
  12. package/dist/diff.d.ts +5 -0
  13. package/dist/diff.js +78 -12
  14. package/dist/document.js +18 -6
  15. package/dist/edit.js +69 -16
  16. package/dist/exec.js +44 -22
  17. package/dist/fetch.js +13 -1
  18. package/dist/format.d.ts +4 -2
  19. package/dist/format.js +81 -31
  20. package/dist/glob.js +12 -4
  21. package/dist/grep.d.ts +2 -0
  22. package/dist/grep.js +15 -4
  23. package/dist/index.js +1359 -762
  24. package/dist/install.js +96 -37
  25. package/dist/kanban-tool-types.d.ts +6 -1
  26. package/dist/kanban.js +60 -0
  27. package/dist/languages/index.js +28 -13
  28. package/dist/lint.js +28 -13
  29. package/dist/logs.d.ts +0 -1
  30. package/dist/logs.js +44 -13
  31. package/dist/memory.d.ts +8 -0
  32. package/dist/memory.js +23 -3
  33. package/dist/mode.d.ts +1 -1
  34. package/dist/mode.js +3 -0
  35. package/dist/next-steps.d.ts +2 -3
  36. package/dist/next-steps.js +3 -3
  37. package/dist/outdated.d.ts +0 -3
  38. package/dist/outdated.js +89 -48
  39. package/dist/pack.js +1294 -726
  40. package/dist/plan.js +91 -3
  41. package/dist/process-registry.d.ts +8 -2
  42. package/dist/process-registry.js +28 -13
  43. package/dist/ps-slash.js +22 -12
  44. package/dist/read.js +10 -3
  45. package/dist/replace.d.ts +4 -0
  46. package/dist/replace.js +104 -7
  47. package/dist/search.d.ts +6 -0
  48. package/dist/search.js +47 -26
  49. package/dist/session-kanban.js +3 -1
  50. package/dist/skill.d.ts +6 -0
  51. package/dist/skill.js +9 -10
  52. package/dist/task.js +81 -2
  53. package/dist/test.js +28 -13
  54. package/dist/todo.js +79 -2
  55. package/dist/tool-icons.js +4 -2
  56. package/dist/tool-summary.d.ts +1 -1
  57. package/dist/tool-summary.js +76 -1
  58. package/dist/tool-tier.js +1294 -726
  59. package/dist/tree.js +9 -10
  60. package/dist/typecheck.d.ts +0 -2
  61. package/dist/typecheck.js +98 -31
  62. package/dist/write.js +58 -10
  63. package/package.json +4 -4
@@ -17,6 +17,11 @@
17
17
  */
18
18
  import type { Tool } from '@wrongstack/core/types';
19
19
  import type { SearchResult } from './schema.js';
20
+ /**
21
+ * Language ids the index understands. Single source for the `lang` enum here
22
+ * and the `langs` validation in codebase-index-tool.ts.
23
+ */
24
+ export declare const INDEXABLE_LANG_IDS: readonly ['ts', 'tsx', 'js', 'jsx', 'go', 'py', 'rs', 'json', 'yaml'];
20
25
  export declare const codebaseSearchTool: Tool<CodebaseSearchInput, CodebaseSearchOutput>;
21
26
  interface CodebaseSearchInput {
22
27
  query: string;
@@ -8491,6 +8491,160 @@ function ensureCodebaseIndexServer(options) {
8491
8491
  }
8492
8492
 
8493
8493
  // src/codebase-index/codebase-index-tool.ts
8494
+ import { ToolValidationError } from "@wrongstack/core/types";
8495
+
8496
+ // src/codebase-index/codebase-search-tool.ts
8497
+ import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
8498
+ var INDEXABLE_LANG_IDS = [
8499
+ "ts",
8500
+ "tsx",
8501
+ "js",
8502
+ "jsx",
8503
+ "go",
8504
+ "py",
8505
+ "rs",
8506
+ "json",
8507
+ "yaml"
8508
+ ];
8509
+ var codebaseSearchTool = {
8510
+ name: "codebase-search",
8511
+ category: "Project",
8512
+ icon: "index",
8513
+ description: "Search code symbols using a fast SQLite+BM25 index, with optional LSP fallback. Prefer this before broad `tree`, `glob`, or `grep` exploration when finding code by name or concept. Use `grep` instead for exact text, regexes, unsupported content, or concrete usage sites. Set `preferLsp: true` for live precision when the LSP plugin is active (supersedes codebase-lsp-search).",
8514
+ usageHint: "FIRST CHOICE FOR INDEXABLE CODE UNDERSTANDING:\n\n- Call before broad `tree`, `glob`, or `grep` exploration when locating symbols, concepts, definitions, or candidate modules.\n- `kind` filter is very useful (e.g. only functions or only interfaces).\n- Combine with `file` filter to scope to a specific directory or module.\n- If `indexStatus` reports no persisted data, run `codebase-index` and retry.",
8515
+ permission: "auto",
8516
+ mutating: false,
8517
+ capabilities: ["fs.read"],
8518
+ // The index host has its own 30s read watchdog. Leave enough headroom for
8519
+ // worker teardown and structured timeout reporting.
8520
+ timeoutMs: 35e3,
8521
+ inputSchema: {
8522
+ type: "object",
8523
+ properties: {
8524
+ query: {
8525
+ type: "string",
8526
+ description: "Search query \u2014 searches symbol names, signatures, and doc comments"
8527
+ },
8528
+ kind: {
8529
+ type: "string",
8530
+ enum: [
8531
+ "class",
8532
+ "interface",
8533
+ "enum",
8534
+ "type",
8535
+ "function",
8536
+ "method",
8537
+ "var",
8538
+ "const",
8539
+ "let",
8540
+ "property",
8541
+ "parameter",
8542
+ "namespace",
8543
+ "object",
8544
+ "literal",
8545
+ "schema",
8546
+ "struct",
8547
+ "trait",
8548
+ "impl",
8549
+ "static",
8550
+ "mod"
8551
+ ],
8552
+ description: "Filter by indexed symbol kind"
8553
+ },
8554
+ lang: {
8555
+ type: "string",
8556
+ enum: [...INDEXABLE_LANG_IDS],
8557
+ description: "Filter by indexed language"
8558
+ },
8559
+ lspKind: {
8560
+ type: "integer",
8561
+ description: "Filter by LSP SymbolKind number (e.g. 5=Class, 12=Function, 11=Interface, 10=Enum)"
8562
+ },
8563
+ file: {
8564
+ type: "string",
8565
+ description: "Filter to files matching this path substring"
8566
+ },
8567
+ limit: {
8568
+ type: "integer",
8569
+ description: "Maximum results to return (default 20, max 100)",
8570
+ minimum: 1,
8571
+ maximum: 100
8572
+ },
8573
+ preferLsp: {
8574
+ type: "boolean",
8575
+ description: "Prefer live LSP results over the index. Ignored unless the LSP plugin is active; when it is active and this is true, results come from live workspaceSymbol queries."
8576
+ }
8577
+ },
8578
+ required: ["query"]
8579
+ },
8580
+ async execute(input, ctx, execOpts) {
8581
+ const state = getIndexState();
8582
+ if (state.indexing && !state.ready) {
8583
+ return {
8584
+ results: [],
8585
+ total: 0,
8586
+ query: input.query,
8587
+ indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
8588
+ };
8589
+ }
8590
+ if (state.lastError) {
8591
+ const circuit = state.circuit;
8592
+ const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s); the user can run /codebase-reindex to retry now.` : "Try /codebase-reindex.";
8593
+ return {
8594
+ results: [],
8595
+ total: 0,
8596
+ query: input.query,
8597
+ indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
8598
+ };
8599
+ }
8600
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 20), 100));
8601
+ let searched;
8602
+ try {
8603
+ searched = await searchCodebaseIndex(
8604
+ {
8605
+ projectRoot: ctx.projectRoot,
8606
+ indexDir: codebaseIndexDirOverride(ctx),
8607
+ query: input.query,
8608
+ kind: input.kind?.toLowerCase(),
8609
+ lang: input.lang?.toLowerCase(),
8610
+ file: input.file,
8611
+ lspKind: input.lspKind,
8612
+ limit
8613
+ },
8614
+ { signal: execOpts?.signal }
8615
+ );
8616
+ } catch (err) {
8617
+ if (execOpts?.signal?.aborted) throw err;
8618
+ return {
8619
+ results: [],
8620
+ total: 0,
8621
+ query: input.query,
8622
+ indexStatus: `Index query failed: ${toErrorMessage2(err)}. Fall back to grep/glob for this lookup.`
8623
+ };
8624
+ }
8625
+ const { results, total } = searched;
8626
+ let hasPersistedIndex = state.ready || total > 0;
8627
+ if (!hasPersistedIndex) {
8628
+ try {
8629
+ const stats = await codebaseIndexStats(
8630
+ { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
8631
+ { signal: execOpts?.signal }
8632
+ );
8633
+ hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
8634
+ } catch {
8635
+ }
8636
+ }
8637
+ return {
8638
+ results,
8639
+ total,
8640
+ query: input.query,
8641
+ ...hasPersistedIndex ? {} : { indexStatus: "No persisted index data found. Run codebase-index to build it." }
8642
+ };
8643
+ }
8644
+ };
8645
+
8646
+ // src/codebase-index/codebase-index-tool.ts
8647
+ var MAX_REPORTED_ERRORS = 20;
8494
8648
  var codebaseIndexTool = {
8495
8649
  name: "codebase-index",
8496
8650
  category: "Project",
@@ -8516,12 +8670,23 @@ var codebaseIndexTool = {
8516
8670
  },
8517
8671
  langs: {
8518
8672
  type: "array",
8519
- items: { type: "string" },
8520
- description: "Limit reindex to specific languages: ts, tsx, js, jsx, go, py, rs"
8673
+ items: { type: "string", enum: [...INDEXABLE_LANG_IDS] },
8674
+ description: `Limit reindex to specific languages: ${INDEXABLE_LANG_IDS.join(", ")}`
8521
8675
  }
8522
8676
  }
8523
8677
  },
8524
8678
  async execute(input, ctx, execOpts) {
8679
+ if (input.langs) {
8680
+ const unknown = input.langs.filter(
8681
+ (lang) => !INDEXABLE_LANG_IDS.includes(lang)
8682
+ );
8683
+ if (unknown.length > 0) {
8684
+ throw new ToolValidationError({
8685
+ message: `codebase-index: unknown lang(s) ${unknown.map((l) => `"${l}"`).join(", ")}. Valid ids: ${INDEXABLE_LANG_IDS.join(", ")}.`,
8686
+ field: "langs"
8687
+ });
8688
+ }
8689
+ }
8525
8690
  if (isIndexing()) {
8526
8691
  return {
8527
8692
  filesIndexed: 0,
@@ -8543,23 +8708,32 @@ var codebaseIndexTool = {
8543
8708
  note: `Codebase indexing is paused after repeated failures (last: ${circuit.lastFailure ?? "unknown"}). Auto-retry possible in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s; the user can run /codebase-reindex to retry immediately.`
8544
8709
  };
8545
8710
  }
8546
- return await runStartupIndex({
8711
+ const result = await runStartupIndex({
8547
8712
  projectRoot: ctx.projectRoot,
8548
8713
  force: input.force ?? false,
8549
8714
  langs: input.langs,
8550
8715
  indexDir: codebaseIndexDirOverride(ctx),
8551
8716
  signal: execOpts?.signal
8552
8717
  });
8718
+ if (result.errors.length > MAX_REPORTED_ERRORS) {
8719
+ const hidden = result.errors.length - MAX_REPORTED_ERRORS;
8720
+ return {
8721
+ ...result,
8722
+ errors: [...result.errors.slice(0, MAX_REPORTED_ERRORS), `+${hidden} more`]
8723
+ };
8724
+ }
8725
+ return result;
8553
8726
  }
8554
8727
  };
8555
8728
 
8556
8729
  // src/codebase-index/codebase-incoming-calls-tool.ts
8730
+ import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
8557
8731
  var codebaseIncomingCallsTool = {
8558
8732
  name: "codebase-incoming-calls",
8559
8733
  category: "Project",
8560
8734
  icon: "index",
8561
- description: "Find all callers of a function, method, or symbol \u2014 who invokes or references it. Uses the codebase index ref graph for instant, exact results. Always use this instead of grep when checking impact of a change.",
8562
- usageHint: 'CALL THIS BEFORE REFACTORING OR CHANGING ANY FUNCTION:\n\n- NEVER use grep or manual line reading to check where a function is called.\n- ALWAYS call codebase-incoming-calls({ symbol: "funcName" }) first.\n- Returns exact files, line numbers, caller signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Combine with codebase-outgoing-calls to see what the symbol itself calls.\nIf the index is not built, run codebase-index first.',
8735
+ description: "Find all callers of a function, method, or symbol \u2014 who invokes or references it. Uses the codebase index ref graph for instant, exact results. Prefer this over grep for change-impact checks when the index is available.",
8736
+ usageHint: 'CALL THIS BEFORE REFACTORING OR CHANGING ANY FUNCTION:\n\n- Prefer this over grep when the index is available; fall back to grep when the index is cold/unavailable or for dynamic dispatch the ref graph cannot see.\n- Call codebase-incoming-calls({ symbol: "funcName" }) before editing the symbol.\n- Returns exact files, line numbers, caller signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Combine with codebase-outgoing-calls to see what the symbol itself calls.\nIf the index is not built, run codebase-index first.',
8563
8737
  permission: "auto",
8564
8738
  mutating: false,
8565
8739
  capabilities: ["fs.read"],
@@ -8611,16 +8785,27 @@ var codebaseIncomingCallsTool = {
8611
8785
  }
8612
8786
  const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
8613
8787
  const transitive = input.transitive === true;
8614
- const { calls, symbolFound, ambiguous, totalMatches } = await incomingCallsService2(
8615
- {
8616
- projectRoot: ctx.projectRoot,
8617
- indexDir: codebaseIndexDirOverride(ctx),
8788
+ let serviced;
8789
+ try {
8790
+ serviced = await incomingCallsService2(
8791
+ {
8792
+ projectRoot: ctx.projectRoot,
8793
+ indexDir: codebaseIndexDirOverride(ctx),
8794
+ symbol: input.symbol,
8795
+ file: input.file,
8796
+ limit,
8797
+ transitive
8798
+ }
8799
+ );
8800
+ } catch (err) {
8801
+ return {
8618
8802
  symbol: input.symbol,
8619
- file: input.file,
8620
- limit,
8621
- transitive
8622
- }
8623
- );
8803
+ calls: [],
8804
+ total: 0,
8805
+ indexStatus: `Index query failed: ${toErrorMessage3(err)}. Fall back to grep for this lookup.`
8806
+ };
8807
+ }
8808
+ const { calls, symbolFound, ambiguous, totalMatches } = serviced;
8624
8809
  if (!symbolFound) {
8625
8810
  let hasPersistedIndex = state.ready;
8626
8811
  if (!hasPersistedIndex) {
@@ -8665,12 +8850,13 @@ var codebaseIncomingCallsTool = {
8665
8850
  };
8666
8851
 
8667
8852
  // src/codebase-index/codebase-outgoing-calls-tool.ts
8853
+ import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
8668
8854
  var codebaseOutgoingCallsTool = {
8669
8855
  name: "codebase-outgoing-calls",
8670
8856
  category: "Project",
8671
8857
  icon: "index",
8672
8858
  description: "Find all functions/methods/symbols that a given symbol calls or depends on \u2014 its callees. Uses the codebase index ref graph for instant, exact results. Use this to understand a function's dependencies before modifying it.",
8673
- usageHint: 'USE THIS TO UNDERSTAND A FUNCTION\'S DEPENDENCIES:\n\n- Call codebase-outgoing-calls({ symbol: "funcName" }) to see everything it calls.\n- Returns exact files, line numbers, callee signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Pair with codebase-incoming-calls for a complete impact picture: incoming = who calls you, outgoing = what you call.\nIf the index is not built, run codebase-index first.',
8859
+ usageHint: 'USE THIS TO UNDERSTAND A FUNCTION\'S DEPENDENCIES:\n\n- Prefer this over grep when the index is available; fall back to grep when the index is cold/unavailable or for dynamic dispatch the ref graph cannot see.\n- Call codebase-outgoing-calls({ symbol: "funcName" }) to see everything it calls.\n- Returns exact files, line numbers, callee signatures, and call types in milliseconds.\n- Use `file` to disambiguate when multiple symbols share a name.\n- Pair with codebase-incoming-calls for a complete impact picture: incoming = who calls you, outgoing = what you call.\nIf the index is not built, run codebase-index first.',
8674
8860
  permission: "auto",
8675
8861
  mutating: false,
8676
8862
  capabilities: ["fs.read"],
@@ -8722,16 +8908,27 @@ var codebaseOutgoingCallsTool = {
8722
8908
  }
8723
8909
  const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 50), 200));
8724
8910
  const transitive = input.transitive === true;
8725
- const { calls, symbolFound, unresolvedCount, totalMatches } = await outgoingCallsService2(
8726
- {
8727
- projectRoot: ctx.projectRoot,
8728
- indexDir: codebaseIndexDirOverride(ctx),
8911
+ let serviced;
8912
+ try {
8913
+ serviced = await outgoingCallsService2(
8914
+ {
8915
+ projectRoot: ctx.projectRoot,
8916
+ indexDir: codebaseIndexDirOverride(ctx),
8917
+ symbol: input.symbol,
8918
+ file: input.file,
8919
+ limit,
8920
+ transitive
8921
+ }
8922
+ );
8923
+ } catch (err) {
8924
+ return {
8729
8925
  symbol: input.symbol,
8730
- file: input.file,
8731
- limit,
8732
- transitive
8733
- }
8734
- );
8926
+ calls: [],
8927
+ total: 0,
8928
+ indexStatus: `Index query failed: ${toErrorMessage4(err)}. Fall back to grep for this lookup.`
8929
+ };
8930
+ }
8931
+ const { calls, symbolFound, unresolvedCount, totalMatches } = serviced;
8735
8932
  if (!symbolFound) {
8736
8933
  let hasPersistedIndex = state.ready;
8737
8934
  if (!hasPersistedIndex) {
@@ -8775,132 +8972,6 @@ var codebaseOutgoingCallsTool = {
8775
8972
  }
8776
8973
  };
8777
8974
 
8778
- // src/codebase-index/codebase-search-tool.ts
8779
- var codebaseSearchTool = {
8780
- name: "codebase-search",
8781
- category: "Project",
8782
- icon: "index",
8783
- description: "Search code symbols using a fast SQLite+BM25 index, with optional LSP fallback. Prefer this before broad `tree`, `glob`, or `grep` exploration when finding code by name or concept. Set `preferLsp: true` for live precision when the LSP plugin is active (supersedes codebase-lsp-search).",
8784
- usageHint: "FIRST CHOICE FOR INDEXABLE CODE UNDERSTANDING:\n\n- Call before broad `tree`, `glob`, or `grep` exploration when locating symbols, concepts, definitions, or candidate modules.\n- `kind` filter is very useful (e.g. only functions or only interfaces).\n- Combine with `file` filter to scope to a specific directory or module.\n- If `indexStatus` reports no persisted data, run `codebase-index` and retry.\nUse `grep` afterwards for exact text, regexes, unsupported content, or concrete usage sites.",
8785
- permission: "auto",
8786
- mutating: false,
8787
- capabilities: ["fs.read"],
8788
- // The index host has its own 30s read watchdog. Leave enough headroom for
8789
- // worker teardown and structured timeout reporting.
8790
- timeoutMs: 35e3,
8791
- inputSchema: {
8792
- type: "object",
8793
- properties: {
8794
- query: {
8795
- type: "string",
8796
- description: "Search query \u2014 searches symbol names, signatures, and doc comments"
8797
- },
8798
- kind: {
8799
- type: "string",
8800
- enum: [
8801
- "class",
8802
- "interface",
8803
- "enum",
8804
- "type",
8805
- "function",
8806
- "method",
8807
- "var",
8808
- "const",
8809
- "let",
8810
- "property",
8811
- "parameter",
8812
- "namespace",
8813
- "object",
8814
- "literal",
8815
- "schema",
8816
- "struct",
8817
- "trait",
8818
- "impl",
8819
- "static",
8820
- "mod"
8821
- ],
8822
- description: "Filter by indexed symbol kind"
8823
- },
8824
- lang: {
8825
- type: "string",
8826
- enum: ["ts", "tsx", "js", "jsx", "go", "py", "rs", "json", "yaml"],
8827
- description: "Filter by indexed language"
8828
- },
8829
- lspKind: {
8830
- type: "integer",
8831
- description: "Filter by LSP SymbolKind number (e.g. 5=Class, 12=Function, 11=Interface, 10=Enum)"
8832
- },
8833
- file: {
8834
- type: "string",
8835
- description: "Filter to files matching this path substring"
8836
- },
8837
- limit: {
8838
- type: "integer",
8839
- description: "Maximum results to return (default 20, max 100)",
8840
- minimum: 1,
8841
- maximum: 100
8842
- },
8843
- preferLsp: {
8844
- type: "boolean",
8845
- description: "Prefer live LSP results over the index. Index-only when the LSP plugin is not active. When the LSP plugin is active and this is true, results come from live workspaceSymbol queries."
8846
- }
8847
- },
8848
- required: ["query"]
8849
- },
8850
- async execute(input, ctx, execOpts) {
8851
- const state = getIndexState();
8852
- if (state.indexing && !state.ready) {
8853
- return {
8854
- results: [],
8855
- total: 0,
8856
- query: input.query,
8857
- indexStatus: `Indexing in progress (${state.currentFile}/${state.totalFiles} files) \u2014 retry in a moment.`
8858
- };
8859
- }
8860
- if (state.lastError) {
8861
- const circuit = state.circuit;
8862
- const retryHint = circuit.state === "open" ? `Indexing is paused (circuit open, retry in ${Math.ceil(circuit.cooldownRemainingMs / 1e3)}s); the user can run /codebase-reindex to retry now.` : "Try /codebase-reindex.";
8863
- return {
8864
- results: [],
8865
- total: 0,
8866
- query: input.query,
8867
- indexStatus: `Index build failed: ${state.lastError}. ${retryHint}`
8868
- };
8869
- }
8870
- const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 20), 100));
8871
- const { results, total } = await searchCodebaseIndex(
8872
- {
8873
- projectRoot: ctx.projectRoot,
8874
- indexDir: codebaseIndexDirOverride(ctx),
8875
- query: input.query,
8876
- kind: input.kind?.toLowerCase(),
8877
- lang: input.lang?.toLowerCase(),
8878
- file: input.file,
8879
- lspKind: input.lspKind,
8880
- limit
8881
- },
8882
- { signal: execOpts?.signal }
8883
- );
8884
- let hasPersistedIndex = state.ready || total > 0;
8885
- if (!hasPersistedIndex) {
8886
- try {
8887
- const stats = await codebaseIndexStats(
8888
- { projectRoot: ctx.projectRoot, indexDir: codebaseIndexDirOverride(ctx) },
8889
- { signal: execOpts?.signal }
8890
- );
8891
- hasPersistedIndex = stats.totalFiles > 0 || stats.lastIndexed !== null;
8892
- } catch {
8893
- }
8894
- }
8895
- return {
8896
- results,
8897
- total,
8898
- query: input.query,
8899
- ...hasPersistedIndex ? {} : { indexStatus: "No persisted index data found. Run codebase-index to build it." }
8900
- };
8901
- }
8902
- };
8903
-
8904
8975
  // src/codebase-index/codebase-stats-tool.ts
8905
8976
  var codebaseStatsTool = {
8906
8977
  name: "codebase-stats",
@@ -8994,9 +9065,9 @@ import * as path14 from "node:path";
8994
9065
  var deadCodeScanTool = {
8995
9066
  name: "dead-code-scan",
8996
9067
  category: "Project",
8997
- icon: "search",
9068
+ icon: "index",
8998
9069
  description: "Scan TypeScript/JavaScript source files for exported symbols that appear unused anywhere in the project. Uses the codebase-index reference graph (import/call/type-ref edges) to compute transitive reachability from package.json entry points. Requires a built codebase-index (run `codebase-index` first if you get no results).",
8999
- usageHint: 'PASS `path` TO SCOPE THE SCAN:\n\n- Defaults to the project root (all indexed files).\n- `path` can be a directory to scope the scan (e.g. "src/util/").\n- `entryPoints` overrides auto-detected entry points (comma-separated file paths).\n\nThe scan runs against the existing index; results are best-effort.',
9070
+ usageHint: "SCANS ALL INDEXED FILES UNDER THE PROJECT ROOT:\n\n- `projectRoot` defaults to the current project root; `indexDir` overrides the resolved index location.\n- `entryPoints` is an array of file paths that AUGMENTS the auto-discovered entry points (package.json bin/main/exports/types plus conventional src/index.ts-style files) \u2014 it does not replace them.\n\nThe scan runs against the existing index; results are best-effort (dynamic imports, external consumers, and config-driven registration are invisible).",
9000
9071
  permission: "auto",
9001
9072
  mutating: false,
9002
9073
  capabilities: ["fs.read"],
@@ -2713,7 +2713,8 @@ import {
2713
2713
  useDaemonPerfDefaults,
2714
2714
  watchProjectTree
2715
2715
  } from "@wrongstack/core/utils";
2716
- import { bindProjectEndpoint } from "@wrongstack/persistence";
2716
+ import { restrictFilePermissions } from "@wrongstack/core/security";
2717
+ import { atomicWrite, bindProjectEndpoint } from "@wrongstack/persistence";
2717
2718
 
2718
2719
  // src/codebase-index/indexer.ts
2719
2720
  import { expectDefined as expectDefined5 } from "@wrongstack/core/utils";
@@ -7785,18 +7786,16 @@ function removeMetadataIfOwned() {
7785
7786
  } catch {
7786
7787
  }
7787
7788
  }
7788
- function writeMetadata() {
7789
+ async function writeMetadata() {
7789
7790
  fs11.mkdirSync(path14.dirname(metadataPath), { recursive: true });
7790
- const temporary = `${metadataPath}.${process.pid}.tmp`;
7791
7791
  const metadata = { ...serverInfo, authToken };
7792
- fs11.writeFileSync(temporary, `${JSON.stringify(metadata, null, 2)}
7792
+ await atomicWrite(metadataPath, `${JSON.stringify(metadata, null, 2)}
7793
7793
  `, { mode: 384 });
7794
- try {
7795
- fs11.renameSync(temporary, metadataPath);
7796
- } catch {
7797
- fs11.rmSync(metadataPath, { force: true });
7798
- fs11.renameSync(temporary, metadataPath);
7799
- }
7794
+ await restrictFilePermissions(metadataPath, {
7795
+ label: "codebase-index-metadata",
7796
+ warn: (message) => process.stderr.write(`${message}
7797
+ `)
7798
+ });
7800
7799
  }
7801
7800
  var server = net.createServer((socket) => {
7802
7801
  if (idleTimer) clearTimeout(idleTimer);
@@ -7893,7 +7892,7 @@ void (async () => {
7893
7892
  `);
7894
7893
  process.exitCode = 1;
7895
7894
  });
7896
- writeMetadata();
7895
+ await writeMetadata();
7897
7896
  markMetadataWritten?.();
7898
7897
  scheduleIdleStop();
7899
7898
  })();
package/dist/diff.d.ts CHANGED
@@ -12,7 +12,12 @@ interface DiffOutput {
12
12
  diff: string;
13
13
  files: string[];
14
14
  truncated: boolean;
15
+ /**
16
+ * The format actually produced: 'unified' or 'stat' on the git path,
17
+ * 'dump' on the files-only line-numbered dump path.
18
+ */
15
19
  mode: string;
20
+ note?: string | undefined;
16
21
  }
17
22
  export declare const diffTool: Tool<DiffInput, DiffOutput>;
18
23
  export {};