sverklo 0.2.16 → 0.4.0

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 (69) hide show
  1. package/README.md +25 -0
  2. package/dist/bin/sverklo.js +38 -5
  3. package/dist/bin/sverklo.js.map +1 -1
  4. package/dist/src/indexer/graph-builder.js +11 -2
  5. package/dist/src/indexer/graph-builder.js.map +1 -1
  6. package/dist/src/indexer/indexer.d.ts +2 -0
  7. package/dist/src/indexer/indexer.js +4 -0
  8. package/dist/src/indexer/indexer.js.map +1 -1
  9. package/dist/src/indexer/watcher.js +31 -0
  10. package/dist/src/indexer/watcher.js.map +1 -1
  11. package/dist/src/init.js +11 -0
  12. package/dist/src/init.js.map +1 -1
  13. package/dist/src/memory/import.js +6 -32
  14. package/dist/src/memory/import.js.map +1 -1
  15. package/dist/src/search/hybrid-search.js +18 -3
  16. package/dist/src/search/hybrid-search.js.map +1 -1
  17. package/dist/src/search/token-budget.js +13 -3
  18. package/dist/src/search/token-budget.js.map +1 -1
  19. package/dist/src/server/dashboard-html.js +6 -1
  20. package/dist/src/server/dashboard-html.js.map +1 -1
  21. package/dist/src/server/http-server.js +27 -1
  22. package/dist/src/server/http-server.js.map +1 -1
  23. package/dist/src/server/tools/audit.js +92 -53
  24. package/dist/src/server/tools/audit.js.map +1 -1
  25. package/dist/src/server/tools/dependencies.js +3 -2
  26. package/dist/src/server/tools/dependencies.js.map +1 -1
  27. package/dist/src/server/tools/diff-search.js +3 -2
  28. package/dist/src/server/tools/diff-search.js.map +1 -1
  29. package/dist/src/server/tools/find-references.js +3 -2
  30. package/dist/src/server/tools/find-references.js.map +1 -1
  31. package/dist/src/server/tools/impact.d.ts +4 -0
  32. package/dist/src/server/tools/impact.js +77 -1
  33. package/dist/src/server/tools/impact.js.map +1 -1
  34. package/dist/src/server/tools/lookup.js +3 -2
  35. package/dist/src/server/tools/lookup.js.map +1 -1
  36. package/dist/src/server/tools/overview.js +3 -2
  37. package/dist/src/server/tools/overview.js.map +1 -1
  38. package/dist/src/server/tools/review-diff.js +111 -76
  39. package/dist/src/server/tools/review-diff.js.map +1 -1
  40. package/dist/src/server/tools/search.js +4 -2
  41. package/dist/src/server/tools/search.js.map +1 -1
  42. package/dist/src/server/tools/search.test.js +2 -2
  43. package/dist/src/utils/budget.d.ts +11 -0
  44. package/dist/src/utils/budget.js +26 -0
  45. package/dist/src/utils/budget.js.map +1 -0
  46. package/dist/src/utils/config-file.d.ts +28 -0
  47. package/dist/src/utils/config-file.js +94 -0
  48. package/dist/src/utils/config-file.js.map +1 -0
  49. package/dist/src/utils/ignore.js +6 -0
  50. package/dist/src/utils/ignore.js.map +1 -1
  51. package/dist/src/workspace/cli.d.ts +17 -0
  52. package/dist/src/workspace/cli.js +205 -0
  53. package/dist/src/workspace/cli.js.map +1 -0
  54. package/dist/src/workspace/cross-db.d.ts +49 -0
  55. package/dist/src/workspace/cross-db.js +192 -0
  56. package/dist/src/workspace/cross-db.js.map +1 -0
  57. package/dist/src/workspace/cross-indexer.d.ts +16 -0
  58. package/dist/src/workspace/cross-indexer.js +288 -0
  59. package/dist/src/workspace/cross-indexer.js.map +1 -0
  60. package/dist/src/workspace/graphql-consumer.d.ts +13 -0
  61. package/dist/src/workspace/graphql-consumer.js +293 -0
  62. package/dist/src/workspace/graphql-consumer.js.map +1 -0
  63. package/dist/src/workspace/graphql-extractor.d.ts +13 -0
  64. package/dist/src/workspace/graphql-extractor.js +172 -0
  65. package/dist/src/workspace/graphql-extractor.js.map +1 -0
  66. package/dist/src/workspace/workspace-config.d.ts +32 -0
  67. package/dist/src/workspace/workspace-config.js +91 -0
  68. package/dist/src/workspace/workspace-config.js.map +1 -0
  69. package/package.json +5 -2
@@ -1,3 +1,4 @@
1
+ import { resolveBudget } from "../../utils/budget.js";
1
2
  export const findReferencesTool = {
2
3
  name: "sverklo_refs",
3
4
  description: "Find all references to a symbol across the codebase. Shows where a function, class, or type is imported, called, or used. Matches on identifier word boundaries by default — `embed` does NOT match `embeddingStore`. Pass `exact: false` to opt into substring matching.",
@@ -14,7 +15,7 @@ export const findReferencesTool = {
14
15
  },
15
16
  token_budget: {
16
17
  type: "number",
17
- description: "Max tokens to return (default: 1500)",
18
+ description: "Max tokens to return (default: 2000)",
18
19
  },
19
20
  },
20
21
  required: ["symbol"],
@@ -51,7 +52,7 @@ export function handleFindReferences(indexer, args) {
51
52
  return 'Error: `symbol` is required. Usage: sverklo_refs symbol:"MyClass".';
52
53
  }
53
54
  const exact = args.exact !== false; // default true
54
- const tokenBudget = args.token_budget || 1500;
55
+ const tokenBudget = resolveBudget(args, "refs", null, 2000);
55
56
  const matches = buildSymbolMatcher(symbol, exact);
56
57
  // Use FTS to find candidate chunks mentioning the symbol at all.
57
58
  // FTS still does substring-ish matching on the chunk body, but we
@@ -1 +1 @@
1
- {"version":3,"file":"find-references.js","sourceRoot":"","sources":["../../../../src/server/tools/find-references.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,2QAA2Q;IAC7Q,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,oCAAoC;aAClD;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,sJAAsJ;aACzJ;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,SAAS,kBAAkB,CAAC,MAAc,EAAE,KAAc;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IACD,uDAAuD;IACvD,iEAAiE;IACjE,oEAAoE;IACpE,+DAA+D;IAC/D,+CAA+C;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;IAChE,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAY,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,IAA6B;IAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvD,OAAO,oEAAoE,CAAC;IAC9E,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,eAAe;IACnD,MAAM,WAAW,GAAI,IAAI,CAAC,YAAuB,IAAI,IAAI,CAAC;IAC1D,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAElD,iEAAiE;IACjE,kEAAkE;IAClE,kEAAkE;IAClE,8DAA8D;IAC9D,yCAAyC;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE5D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3C,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACzB,CAAC;IAED,gBAAgB;IAChB,MAAM,MAAM,GAAG,IAAI,GAAG,EAA6D,CAAC;IAEpF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,iEAAiE;QACjE,6DAA6D;QAC7D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,SAAS;QAEtC,4DAA4D;QAC5D,6CAA6C;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACzC,IAAI,CAAC,IAAI,CAAC;oBACR,IAAI,EAAE,KAAK,CAAC,UAAU,GAAG,CAAC;oBAC1B,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;oBACxB,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB,CAAC,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,gBAAgB;IAChB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,WAAW,CAAC;IAE5B,yBAAyB;IACzB,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtD,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,IAAI,CAAC,qBAAqB,MAAM,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IAEhH,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,OAAO,QAAQ,EAAE,CAAC;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;QAClD,IAAI,SAAS,GAAG,UAAU,GAAG,EAAE;YAAE,MAAM;QAEvC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,SAAS,IAAI,UAAU,CAAC;QAExB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;YAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;YAC9C,IAAI,SAAS,GAAG,QAAQ;gBAAE,MAAM;YAChC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,SAAS,IAAI,QAAQ,CAAC;QACxB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,4BAA4B,MAAM,IAAI,CAAC;AACtF,CAAC"}
1
+ {"version":3,"file":"find-references.js","sourceRoot":"","sources":["../../../../src/server/tools/find-references.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,10 @@ export declare const impactTool: {
13
13
  type: string;
14
14
  description: string;
15
15
  };
16
+ cross_repo: {
17
+ type: string;
18
+ description: string;
19
+ };
16
20
  };
17
21
  required: string[];
18
22
  };
@@ -1,6 +1,8 @@
1
+ import { findWorkspaceForProject, getWorkspaceDbPath } from "../../workspace/workspace-config.js";
2
+ import { CrossRepoDb } from "../../workspace/cross-db.js";
1
3
  export const impactTool = {
2
4
  name: "sverklo_impact",
3
- description: "Refactor blast-radius: callers of a symbol with confidence scoring. Run before editing.",
5
+ description: "Refactor blast-radius: callers of a symbol with confidence scoring. Run before editing. Use cross_repo:true to see impact across linked projects in a workspace.",
4
6
  inputSchema: {
5
7
  type: "object",
6
8
  properties: {
@@ -12,6 +14,10 @@ export const impactTool = {
12
14
  type: "number",
13
15
  description: "Max references to return (default 50)",
14
16
  },
17
+ cross_repo: {
18
+ type: "boolean",
19
+ description: "Include cross-repo impact from workspace projects (default false)",
20
+ },
15
21
  },
16
22
  required: ["symbol"],
17
23
  },
@@ -97,6 +103,76 @@ export function handleImpact(indexer, args) {
97
103
  parts.push(` L${line} — ${chunkLabel}`);
98
104
  }
99
105
  }
106
+ // Cross-repo impact (if requested and workspace exists)
107
+ if (args.cross_repo) {
108
+ const crossSection = getCrossRepoImpact(indexer, symbol);
109
+ if (crossSection) {
110
+ parts.push("\n" + crossSection);
111
+ }
112
+ }
113
+ return parts.join("\n");
114
+ }
115
+ function getCrossRepoImpact(indexer, symbol) {
116
+ try {
117
+ const wsConfig = findWorkspaceForProject(indexer.rootPath);
118
+ if (!wsConfig) {
119
+ return "\n_No workspace found for this project. Create one with `sverklo workspace init <name> <path1> <path2>`._";
120
+ }
121
+ const db = new CrossRepoDb(getWorkspaceDbPath(wsConfig.workspace));
122
+ try {
123
+ // Find contracts matching this symbol
124
+ const contracts = db.getContractBySymbol(symbol);
125
+ if (contracts.length === 0) {
126
+ // Try with Type.field pattern
127
+ const fieldContracts = db.getContractBySymbol(`%.${symbol}`);
128
+ if (fieldContracts.length === 0) {
129
+ return null; // No cross-repo contracts for this symbol
130
+ }
131
+ return formatCrossImpact(db, fieldContracts, wsConfig.workspace);
132
+ }
133
+ return formatCrossImpact(db, contracts, wsConfig.workspace);
134
+ }
135
+ finally {
136
+ db.close();
137
+ }
138
+ }
139
+ catch {
140
+ return null; // Silently skip cross-repo if anything fails
141
+ }
142
+ }
143
+ function formatCrossImpact(db, contracts, workspaceName) {
144
+ const parts = [`\n## Cross-repo impact (workspace: ${workspaceName})`];
145
+ let totalEdges = 0;
146
+ for (const contract of contracts) {
147
+ const edges = db.getCrossEdgesForContract(contract.id);
148
+ if (edges.length === 0)
149
+ continue;
150
+ totalEdges += edges.length;
151
+ parts.push(`\n### ${contract.symbolName} (${contract.interfaceType} ${contract.symbolKind})`);
152
+ parts.push(` defined in: \`${contract.sourceFile}:${contract.fileLine || "?"}\``);
153
+ // Group edges by project
154
+ const byProject = new Map();
155
+ for (const edge of edges) {
156
+ const arr = byProject.get(edge.consumerProjectId) || [];
157
+ arr.push(edge);
158
+ byProject.set(edge.consumerProjectId, arr);
159
+ }
160
+ for (const [projectId, projectEdges] of byProject) {
161
+ const project = db.getProject(projectId);
162
+ const projectName = project?.name || projectId;
163
+ parts.push(`\n **${projectName}** (${projectEdges.length} reference${projectEdges.length === 1 ? "" : "s"}):`);
164
+ for (const edge of projectEdges.slice(0, 20)) {
165
+ const conf = edge.confidence < 1.0 ? ` (confidence: ${edge.confidence})` : "";
166
+ parts.push(` · \`${edge.consumerFile}:${edge.consumerLine || "?"}\` — ${edge.consumerSymbol} [${edge.edgeType}]${conf}`);
167
+ }
168
+ if (projectEdges.length > 20) {
169
+ parts.push(` _...and ${projectEdges.length - 20} more_`);
170
+ }
171
+ }
172
+ }
173
+ if (totalEdges === 0)
174
+ return null;
175
+ parts.unshift(""); // blank line before section
100
176
  return parts.join("\n");
101
177
  }
102
178
  //# sourceMappingURL=impact.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../../../src/server/tools/impact.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"impact.js","sourceRoot":"","sources":["../../../../src/server/tools/impact.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { formatLookup } from "../../search/token-budget.js";
2
+ import { resolveBudget } from "../../utils/budget.js";
2
3
  // Issue #6: on the first call, sverklo_lookup paid a ~1.6s penalty while
3
4
  // warming up prepared statements via fileStore.getAll() to build a
4
5
  // pagerank-by-file map. The getByNameWithFile JOIN below returns the
@@ -28,7 +29,7 @@ export const lookupTool = {
28
29
  },
29
30
  token_budget: {
30
31
  type: "number",
31
- description: "Max tokens to return (default: 1200)",
32
+ description: "Max tokens to return (default: 2000)",
32
33
  },
33
34
  },
34
35
  required: ["symbol"],
@@ -46,7 +47,7 @@ export function handleLookup(indexer, args) {
46
47
  "The tool schema names this parameter `symbol`, not `name` — common typo.");
47
48
  }
48
49
  const type = args.type || "any";
49
- const tokenBudget = args.token_budget || 1200;
50
+ const tokenBudget = resolveBudget(args, "lookup", null, 2000);
50
51
  // Single JOIN'd query — chunks come back pre-sorted by pagerank DESC
51
52
  // and carry the containing file's path, so no full fileStore scan.
52
53
  let chunks = indexer.chunkStore.getByNameWithFile(symbol, 20);
@@ -1 +1 @@
1
- {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../../src/server/tools/lookup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,yEAAyE;AACzE,mEAAmE;AACnE,qEAAqE;AACrE,mEAAmE;AAEnE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,4HAA4H;IAC9H,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE;oBACJ,UAAU;oBACV,OAAO;oBACP,MAAM;oBACN,WAAW;oBACX,QAAQ;oBACR,UAAU;oBACV,KAAK;iBACN;gBACD,WAAW,EAAE,uBAAuB;aACrC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAC1B,OAAgB,EAChB,IAA6B;IAE7B,kEAAkE;IAClE,iEAAiE;IACjE,mEAAmE;IACnE,mEAAmE;IACnE,sDAAsD;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvD,OAAO,CACL,wEAAwE;YACxE,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAI,IAAI,CAAC,IAA0B,IAAI,KAAK,CAAC;IACvD,MAAM,WAAW,GAAI,IAAI,CAAC,YAAuB,IAAI,IAAI,CAAC;IAE1D,qEAAqE;IACrE,mEAAmE;IACnE,IAAI,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE9D,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,oEAAoE;IACpE,mEAAmE;IACnE,+CAA+C;IAC/C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAsB,CAAC;IACnD,OAAO,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../../../src/server/tools/lookup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,yEAAyE;AACzE,mEAAmE;AACnE,qEAAqE;AACrE,mEAAmE;AAEnE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,gBAAgB;IACtB,WAAW,EACT,4HAA4H;IAC9H,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE;oBACJ,UAAU;oBACV,OAAO;oBACP,MAAM;oBACN,WAAW;oBACX,QAAQ;oBACR,UAAU;oBACV,KAAK;iBACN;gBACD,WAAW,EAAE,uBAAuB;aACrC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;SACF;QACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;KACrB;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAC1B,OAAgB,EAChB,IAA6B;IAE7B,kEAAkE;IAClE,iEAAiE;IACjE,mEAAmE;IACnE,mEAAmE;IACnE,sDAAsD;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvD,OAAO,CACL,wEAAwE;YACxE,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAI,IAAI,CAAC,IAA0B,IAAI,KAAK,CAAC;IACvD,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAE9D,qEAAqE;IACrE,mEAAmE;IACnE,IAAI,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAE9D,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,oEAAoE;IACpE,mEAAmE;IACnE,+CAA+C;IAC/C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAsB,CAAC;IACnD,OAAO,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;AACzD,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { formatOverview } from "../../search/token-budget.js";
2
+ import { resolveBudget } from "../../utils/budget.js";
2
3
  export const overviewTool = {
3
4
  name: "sverklo_overview",
4
5
  description: "Get a structural map of the codebase. Shows the most important files and their key symbols ranked by dependency importance (PageRank). Use this FIRST when starting work on an unfamiliar codebase or directory.",
@@ -11,14 +12,14 @@ export const overviewTool = {
11
12
  },
12
13
  token_budget: {
13
14
  type: "number",
14
- description: "Max tokens to return (default: 2000)",
15
+ description: "Max tokens to return (default: 3000)",
15
16
  },
16
17
  },
17
18
  },
18
19
  };
19
20
  export function handleOverview(indexer, args) {
20
21
  const path = args.path;
21
- const tokenBudget = args.token_budget || 2000;
22
+ const tokenBudget = resolveBudget(args, "overview", null, 3000);
22
23
  const files = indexer.fileStore.getAll(); // already sorted by pagerank DESC
23
24
  const entries = [];
24
25
  for (const file of files) {
@@ -1 +1 @@
1
- {"version":3,"file":"overview.js","sourceRoot":"","sources":["../../../../src/server/tools/overview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAsB,MAAM,8BAA8B,CAAC;AAElF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,kNAAkN;IACpN,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+CAA+C;aAC7D;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;SACF;KACF;CACF,CAAC;AAEF,MAAM,UAAU,cAAc,CAC5B,OAAgB,EAChB,IAA6B;IAE7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAA0B,CAAC;IAC7C,MAAM,WAAW,GAAI,IAAI,CAAC,YAAuB,IAAI,IAAI,CAAC;IAE1D,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,kCAAkC;IAE5E,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"overview.js","sourceRoot":"","sources":["../../../../src/server/tools/overview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAsB,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,kNAAkN;IACpN,WAAW,EAAE;QACX,IAAI,EAAE,QAAiB;QACvB,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+CAA+C;aAC7D;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;SACF;KACF;CACF,CAAC;AAEF,MAAM,UAAU,cAAc,CAC5B,OAAgB,EAChB,IAA6B;IAE7B,MAAM,IAAI,GAAG,IAAI,CAAC,IAA0B,CAAC;IAC7C,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAEhE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,kCAAkC;IAE5E,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;AACpD,CAAC"}
@@ -3,6 +3,7 @@ import { basename } from "node:path";
3
3
  import { computeRiskScore, formatRiskBadge } from "./risk-score.js";
4
4
  import { isTestPath, candidateTestNames } from "./test-paths.js";
5
5
  import { getDiffHunks, runAllHeuristics } from "./diff-heuristics.js";
6
+ import { resolveBudget } from "../../utils/budget.js";
6
7
  export const reviewDiffTool = {
7
8
  name: "sverklo_review_diff",
8
9
  description: "Diff-aware context bundler for code review. Takes a git ref or range and returns: " +
@@ -29,7 +30,7 @@ export const reviewDiffTool = {
29
30
  },
30
31
  token_budget: {
31
32
  type: "number",
32
- description: "Max tokens to return. Default: 2500.",
33
+ description: "Max tokens to return. Default: 4000.",
33
34
  },
34
35
  },
35
36
  },
@@ -38,7 +39,7 @@ export function handleReviewDiff(indexer, args) {
38
39
  const ref = args.ref || "main..HEAD";
39
40
  const includeSimilarity = args.include_added_similarity !== false;
40
41
  const maxFiles = args.max_files || 25;
41
- const tokenBudget = args.token_budget || 2500;
42
+ const tokenBudget = resolveBudget(args, "review_diff", null, 4000);
42
43
  // ─── 1. Get list of changed files ───
43
44
  const changedFiles = getChangedFiles(indexer.rootPath, ref);
44
45
  if (changedFiles === null) {
@@ -245,47 +246,73 @@ export function handleReviewDiff(indexer, args) {
245
246
  // class of bug that symbol-level analysis alone cannot see.
246
247
  const diffHunks = getDiffHunks(indexer.rootPath, ref);
247
248
  const heuristicFindings = runAllHeuristics(diffHunks);
248
- // ─── 7. Format output ───
249
- const parts = [];
250
- parts.push(`# Diff Review: \`${ref}\``);
251
- parts.push("");
252
- parts.push(`**${changedFiles.length} file${changedFiles.length === 1 ? "" : "s"} changed** ` +
253
- `(${addedSymbols.length} added, ${removedSymbols.length} removed, ${modifiedFiles.length} modified)`);
254
- if (truncated) {
255
- parts.push(`_Showing first ${maxFiles} files; ${changedFiles.length - maxFiles} more not analyzed._`);
249
+ // ─── 7. Format output with section-level budgeting ───
250
+ const sections = [];
251
+ let usedTokens = 0;
252
+ const maxTokens = tokenBudget;
253
+ function addSection(section) {
254
+ const cost = Math.ceil(section.length / 3.5);
255
+ if (usedTokens + cost > maxTokens) {
256
+ sections.push("\n_[remaining sections omitted to fit token_budget]_");
257
+ return false;
258
+ }
259
+ sections.push(section);
260
+ usedTokens += cost;
261
+ return true;
262
+ }
263
+ // Header
264
+ {
265
+ const headerLines = [];
266
+ headerLines.push(`# Diff Review: \`${ref}\``);
267
+ headerLines.push("");
268
+ headerLines.push(`**${changedFiles.length} file${changedFiles.length === 1 ? "" : "s"} changed** ` +
269
+ `(${addedSymbols.length} added, ${removedSymbols.length} removed, ${modifiedFiles.length} modified)`);
270
+ if (truncated) {
271
+ headerLines.push(`_Showing first ${maxFiles} files; ${changedFiles.length - maxFiles} more not analyzed._`);
272
+ }
273
+ headerLines.push("");
274
+ if (!addSection(headerLines.join("\n")))
275
+ return sections.join("\n");
256
276
  }
257
- parts.push("");
258
277
  // Changed files table
259
- parts.push("## Changed files");
260
- for (const cf of cappedFiles) {
261
- const indexed = fileCache.get(cf.path);
262
- const pr = indexed ? ` (PR ${indexed.pagerank.toFixed(2)})` : "";
263
- const impact = fileImpact.get(cf.path);
264
- const impactNote = impact && impact.importers > 0 ? ` ${impact.importers} importer${impact.importers === 1 ? "" : "s"}` : "";
265
- const risk = riskByFile.get(cf.path);
266
- const riskNote = risk ? ` · ${formatRiskBadge(risk)}` : "";
267
- parts.push(`- **${cf.status}** \`${cf.path}\` +${cf.added} -${cf.removed}${pr}${impactNote}${riskNote}`);
278
+ {
279
+ const cfLines = [];
280
+ cfLines.push("## Changed files");
281
+ for (const cf of cappedFiles) {
282
+ const indexed = fileCache.get(cf.path);
283
+ const pr = indexed ? ` (PR ${indexed.pagerank.toFixed(2)})` : "";
284
+ const impact = fileImpact.get(cf.path);
285
+ const impactNote = impact && impact.importers > 0 ? ` ${impact.importers} importer${impact.importers === 1 ? "" : "s"}` : "";
286
+ const risk = riskByFile.get(cf.path);
287
+ const riskNote = risk ? ` · ${formatRiskBadge(risk)}` : "";
288
+ cfLines.push(`- **${cf.status}** \`${cf.path}\` +${cf.added} -${cf.removed}${pr}${impactNote}${riskNote}`);
289
+ }
290
+ cfLines.push("");
291
+ if (!addSection(cfLines.join("\n")))
292
+ return sections.join("\n");
268
293
  }
269
- parts.push("");
270
- // Risk hot-list: surface high/critical files with their reasons so a
271
- // reviewer can immediately see *why* a file is flagged.
294
+ // Risk hot-list
272
295
  const riskRanked = [...riskByFile.entries()]
273
296
  .filter(([, r]) => r.level === "high" || r.level === "critical")
274
297
  .sort((a, b) => b[1].total - a[1].total);
275
298
  if (riskRanked.length > 0) {
276
- parts.push("## ⚠️ Highest-risk files");
277
- parts.push("_Risk score combines: untested, security-sensitive paths, fan-in, caller count, dangling refs, churn._");
299
+ const riskLines = [];
300
+ riskLines.push("## ⚠️ Highest-risk files");
301
+ riskLines.push("_Risk score combines: untested, security-sensitive paths, fan-in, caller count, dangling refs, churn._");
278
302
  for (const [path, score] of riskRanked.slice(0, 8)) {
279
- parts.push(`- ${formatRiskBadge(score)} \`${path}\``);
303
+ riskLines.push(`- ${formatRiskBadge(score)} \`${path}\``);
280
304
  if (score.reasons.length > 0) {
281
- parts.push(` _${score.reasons.join("; ")}_`);
305
+ riskLines.push(` _${score.reasons.join("; ")}_`);
282
306
  }
283
307
  }
284
- parts.push("");
308
+ riskLines.push("");
309
+ if (!addSection(riskLines.join("\n")))
310
+ return sections.join("\n");
285
311
  }
286
312
  // Removed symbols + dangling refs (the most important section for safety)
287
313
  if (removedSymbols.length > 0) {
288
- parts.push("## Removed symbols");
314
+ const remLines = [];
315
+ remLines.push("## Removed symbols");
289
316
  let dangerCount = 0;
290
317
  for (const sym of removedSymbols.slice(0, 15)) {
291
318
  const refs = danglingRefs.get(sym.name);
@@ -294,62 +321,70 @@ export function handleReviewDiff(indexer, args) {
294
321
  const refLabel = refCount === 0
295
322
  ? "0 dangling refs (safe to remove)"
296
323
  : `**${refCount} dangling reference${refCount === 1 ? "" : "s"}** in ${refs.files.length} file${refs.files.length === 1 ? "" : "s"}`;
297
- parts.push(`- ${danger} \`${sym.name}\` (${sym.type}) @ ${sym.file}:${sym.line} — ${refLabel}`);
324
+ remLines.push(`- ${danger} \`${sym.name}\` (${sym.type}) @ ${sym.file}:${sym.line} — ${refLabel}`);
298
325
  if (refCount > 0) {
299
326
  dangerCount++;
300
327
  for (const f of refs.files.slice(0, 3)) {
301
- parts.push(` · ${f}`);
328
+ remLines.push(` · ${f}`);
302
329
  }
303
330
  if (refs.files.length > 3) {
304
- parts.push(` · ...and ${refs.files.length - 3} more (call sverklo_impact for full list)`);
331
+ remLines.push(` · ...and ${refs.files.length - 3} more (call sverklo_impact for full list)`);
305
332
  }
306
333
  }
307
334
  }
308
335
  if (dangerCount > 0) {
309
- parts.push("");
310
- parts.push(`**⚠️ ${dangerCount} removed symbol${dangerCount === 1 ? " has" : "s have"} remaining references — review carefully.**`);
336
+ remLines.push("");
337
+ remLines.push(`**⚠️ ${dangerCount} removed symbol${dangerCount === 1 ? " has" : "s have"} remaining references — review carefully.**`);
311
338
  }
312
- parts.push("");
339
+ remLines.push("");
340
+ if (!addSection(remLines.join("\n")))
341
+ return sections.join("\n");
313
342
  }
314
343
  // Added symbols + duplication warnings
315
344
  if (addedSymbols.length > 0) {
316
- parts.push("## Added symbols");
345
+ const addLines = [];
346
+ addLines.push("## Added symbols");
317
347
  for (const sym of addedSymbols.slice(0, 15)) {
318
- parts.push(`- \`${sym.name}\` (${sym.type}) @ ${sym.file}:${sym.line}`);
348
+ addLines.push(`- \`${sym.name}\` (${sym.type}) @ ${sym.file}:${sym.line}`);
319
349
  }
320
350
  if (addedSymbols.length > 15) {
321
- parts.push(` _...and ${addedSymbols.length - 15} more_`);
351
+ addLines.push(` _...and ${addedSymbols.length - 15} more_`);
322
352
  }
323
- parts.push("");
353
+ addLines.push("");
324
354
  if (duplicateCandidates.length > 0) {
325
- parts.push("### ⚠️ Possible duplicates");
355
+ addLines.push("### ⚠️ Possible duplicates");
326
356
  for (const dup of duplicateCandidates) {
327
- parts.push(`- **${dup.added.name}** added in \`${dup.added.file}\` — already exists in:`);
357
+ addLines.push(`- **${dup.added.name}** added in \`${dup.added.file}\` — already exists in:`);
328
358
  for (const s of dup.similar.slice(0, 3)) {
329
- parts.push(` · ${s.file}:${s.line}`);
359
+ addLines.push(` · ${s.file}:${s.line}`);
330
360
  }
331
361
  }
332
- parts.push("");
362
+ addLines.push("");
333
363
  }
364
+ if (!addSection(addLines.join("\n")))
365
+ return sections.join("\n");
334
366
  }
335
367
  // Modified files with high impact (> 3 importers)
336
368
  const highImpactFiles = Array.from(fileImpact.entries())
337
369
  .filter(([, v]) => v.importers >= 3)
338
370
  .sort((a, b) => b[1].importers - a[1].importers);
339
371
  if (highImpactFiles.length > 0) {
340
- parts.push("## High-impact modifications");
341
- parts.push("_These files are imported by many others — changes cascade widely._");
372
+ const hiLines = [];
373
+ hiLines.push("## High-impact modifications");
374
+ hiLines.push("_These files are imported by many others — changes cascade widely._");
342
375
  for (const [path, impact] of highImpactFiles.slice(0, 10)) {
343
- parts.push(`- \`${path}\` ← ${impact.importers} importer${impact.importers === 1 ? "" : "s"}`);
376
+ hiLines.push(`- \`${path}\` ← ${impact.importers} importer${impact.importers === 1 ? "" : "s"}`);
344
377
  }
345
- parts.push("");
378
+ hiLines.push("");
379
+ if (!addSection(hiLines.join("\n")))
380
+ return sections.join("\n");
346
381
  }
347
382
  // Structural heuristic findings (unguarded stream calls, etc.)
348
383
  if (heuristicFindings.length > 0) {
349
- parts.push("## ⚠️ Structural warnings");
350
- parts.push("_These are heuristic matches over the diff text. Some may be false positives; " +
384
+ const hLines = [];
385
+ hLines.push("## ⚠️ Structural warnings");
386
+ hLines.push("_These are heuristic matches over the diff text. Some may be false positives; " +
351
387
  "each finding carries a short explanation so you can triage quickly._");
352
- // Group by heuristic so the reviewer can judge each class at a glance.
353
388
  const grouped = new Map();
354
389
  for (const f of heuristicFindings) {
355
390
  const arr = grouped.get(f.heuristic) || [];
@@ -357,42 +392,42 @@ export function handleReviewDiff(indexer, args) {
357
392
  grouped.set(f.heuristic, arr);
358
393
  }
359
394
  for (const [heuristic, findings] of grouped) {
360
- parts.push(`### ${heuristic} (${findings.length})`);
395
+ hLines.push(`### ${heuristic} (${findings.length})`);
361
396
  for (const f of findings.slice(0, 6)) {
362
397
  const badge = f.severity === "high" ? "🔴" : f.severity === "medium" ? "🟡" : "🟢";
363
- parts.push(`- ${badge} \`${f.file}:${f.line}\` — ${f.message}`);
364
- parts.push(` \`${f.snippet}\``);
398
+ hLines.push(`- ${badge} \`${f.file}:${f.line}\` — ${f.message}`);
399
+ hLines.push(` \`${f.snippet}\``);
365
400
  }
366
401
  if (findings.length > 6) {
367
- parts.push(` _...and ${findings.length - 6} more_`);
402
+ hLines.push(` _...and ${findings.length - 6} more_`);
368
403
  }
369
404
  }
370
- parts.push("");
405
+ hLines.push("");
406
+ if (!addSection(hLines.join("\n")))
407
+ return sections.join("\n");
371
408
  }
372
409
  // Recommendations
373
- parts.push("## Suggested next checks");
374
- if (removedSymbols.length > 0) {
375
- const dangerNames = removedSymbols
376
- .filter((s) => (danglingRefs.get(s.name)?.count ?? 0) > 0)
377
- .map((s) => s.name);
378
- if (dangerNames.length > 0) {
379
- parts.push(`- Run \`sverklo_impact symbol:"${dangerNames[0]}"\` to see all callers of removed symbols`);
410
+ {
411
+ const recLines = [];
412
+ recLines.push("## Suggested next checks");
413
+ if (removedSymbols.length > 0) {
414
+ const dangerNames = removedSymbols
415
+ .filter((s) => (danglingRefs.get(s.name)?.count ?? 0) > 0)
416
+ .map((s) => s.name);
417
+ if (dangerNames.length > 0) {
418
+ recLines.push(`- Run \`sverklo_impact symbol:"${dangerNames[0]}"\` to see all callers of removed symbols`);
419
+ }
380
420
  }
421
+ if (modifiedFiles.length > 0) {
422
+ recLines.push(`- Run \`sverklo_diff_search query:"..."\` to search semantically within these files`);
423
+ }
424
+ if (addedSymbols.length > 0 && duplicateCandidates.length === 0) {
425
+ recLines.push(`- New symbols look unique — no duplication detected against indexed code`);
426
+ }
427
+ recLines.push(`- For exact-match checks, fall back to \`grep -r 'symbol' .\``);
428
+ addSection(recLines.join("\n"));
381
429
  }
382
- if (modifiedFiles.length > 0) {
383
- parts.push(`- Run \`sverklo_diff_search query:"..."\` to search semantically within these files`);
384
- }
385
- if (addedSymbols.length > 0 && duplicateCandidates.length === 0) {
386
- parts.push(`- New symbols look unique — no duplication detected against indexed code`);
387
- }
388
- parts.push(`- For exact-match checks, fall back to \`grep -r 'symbol' .\``);
389
- let output = parts.join("\n");
390
- // Enforce token budget
391
- const maxChars = tokenBudget * 3.5;
392
- if (output.length > maxChars) {
393
- output = output.slice(0, maxChars) + "\n\n_[truncated to fit token budget]_";
394
- }
395
- return output;
430
+ return sections.join("\n");
396
431
  }
397
432
  // ─── git helpers ───
398
433
  function getChangedFiles(rootPath, ref) {