@wrongstack/tools 0.87.0 → 0.89.3

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 (59) hide show
  1. package/dist/audit.js +1 -7
  2. package/dist/audit.js.map +1 -1
  3. package/dist/{background-indexer-DYm1FUxK.d.ts → background-indexer-C2014mH0.d.ts} +1 -1
  4. package/dist/bash.js +6 -18
  5. package/dist/bash.js.map +1 -1
  6. package/dist/builtin.js +59 -134
  7. package/dist/builtin.js.map +1 -1
  8. package/dist/circuit-breaker.js +1 -1
  9. package/dist/circuit-breaker.js.map +1 -1
  10. package/dist/codebase-index/index.d.ts +2 -21
  11. package/dist/codebase-index/index.js +25 -69
  12. package/dist/codebase-index/index.js.map +1 -1
  13. package/dist/diff.js.map +1 -1
  14. package/dist/document.js +1 -1
  15. package/dist/document.js.map +1 -1
  16. package/dist/edit.js.map +1 -1
  17. package/dist/exec.js +3 -15
  18. package/dist/exec.js.map +1 -1
  19. package/dist/fetch.js.map +1 -1
  20. package/dist/format.js +4 -16
  21. package/dist/format.js.map +1 -1
  22. package/dist/git.d.ts +2 -0
  23. package/dist/git.js +15 -8
  24. package/dist/git.js.map +1 -1
  25. package/dist/glob.js.map +1 -1
  26. package/dist/grep.js +1 -7
  27. package/dist/grep.js.map +1 -1
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.js +173 -137
  30. package/dist/index.js.map +1 -1
  31. package/dist/install.js +4 -16
  32. package/dist/install.js.map +1 -1
  33. package/dist/lint.js +4 -16
  34. package/dist/lint.js.map +1 -1
  35. package/dist/logs.js.map +1 -1
  36. package/dist/memory.d.ts +29 -1
  37. package/dist/memory.js +115 -4
  38. package/dist/memory.js.map +1 -1
  39. package/dist/outdated.js.map +1 -1
  40. package/dist/pack.js +59 -134
  41. package/dist/pack.js.map +1 -1
  42. package/dist/patch.js.map +1 -1
  43. package/dist/process-registry.js +2 -7
  44. package/dist/process-registry.js.map +1 -1
  45. package/dist/read.js +1 -1
  46. package/dist/read.js.map +1 -1
  47. package/dist/replace.js +1 -7
  48. package/dist/replace.js.map +1 -1
  49. package/dist/scaffold.js.map +1 -1
  50. package/dist/search.js +2 -7
  51. package/dist/search.js.map +1 -1
  52. package/dist/test.js +4 -16
  53. package/dist/test.js.map +1 -1
  54. package/dist/tree.js +1 -7
  55. package/dist/tree.js.map +1 -1
  56. package/dist/typecheck.js +4 -16
  57. package/dist/typecheck.js.map +1 -1
  58. package/dist/write.js.map +1 -1
  59. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,30 +1,19 @@
1
1
  import * as fs4 from 'node:fs/promises';
2
+ import * as Core from '@wrongstack/core';
3
+ import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, resolveWstackPaths } from '@wrongstack/core';
2
4
  import * as path from 'node:path';
3
5
  import { resolve, sep, dirname } from 'node:path';
4
- import * as Core from '@wrongstack/core';
5
- import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, resolveWstackPaths } from '@wrongstack/core';
6
6
  import { spawn, execFileSync, spawnSync } from 'node:child_process';
7
7
  import * as os from 'node:os';
8
8
  import * as dns from 'node:dns/promises';
9
9
  import * as net from 'node:net';
10
10
  import { Agent } from 'undici';
11
11
  import * as fs13 from 'node:fs';
12
- import { statSync, mkdirSync, writeFileSync } from 'node:fs';
12
+ import { statSync, writeFileSync, mkdirSync } from 'node:fs';
13
13
  import { createRequire } from 'node:module';
14
14
  import * as ts from 'typescript';
15
15
 
16
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
17
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
18
- }) : x)(function(x) {
19
- if (typeof require !== "undefined") return require.apply(this, arguments);
20
- throw Error('Dynamic require of "' + x + '" is not supported');
21
- });
22
- function expectDefined(value) {
23
- if (value === null || value === void 0) {
24
- throw new Error("Expected value to be defined");
25
- }
26
- return value;
27
- }
16
+ // src/read.ts
28
17
  async function detectPackageManager(cwd) {
29
18
  const { stat: stat10 } = await import('node:fs/promises');
30
19
  try {
@@ -472,12 +461,6 @@ function capSubject(line) {
472
461
  }
473
462
 
474
463
  // src/replace.ts
475
- function expectDefined2(value) {
476
- if (value === null || value === void 0) {
477
- throw new Error("Expected value to be defined");
478
- }
479
- return value;
480
- }
481
464
  var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
482
465
  var replaceTool = {
483
466
  name: "replace",
@@ -557,8 +540,8 @@ var replaceTool = {
557
540
  const count = matches.length;
558
541
  let newContentLf = contentLf;
559
542
  for (let i = matches.length - 1; i >= 0; i--) {
560
- const m = expectDefined2(matches[i]);
561
- newContentLf = newContentLf.slice(0, m.index) + input.replacement + newContentLf.slice(expectDefined2(m.index) + m[0].length);
543
+ const m = expectDefined(matches[i]);
544
+ newContentLf = newContentLf.slice(0, m.index) + input.replacement + newContentLf.slice(expectDefined(m.index) + m[0].length);
562
545
  }
563
546
  re.lastIndex = 0;
564
547
  totalReplacements += count;
@@ -759,12 +742,6 @@ async function readGitignore(dir) {
759
742
  return [];
760
743
  }
761
744
  }
762
- function expectDefined3(value) {
763
- if (value === null || value === void 0) {
764
- throw new Error("Expected value to be defined");
765
- }
766
- return value;
767
- }
768
745
  var DEFAULT_IGNORE3 = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
769
746
  var grepTool = {
770
747
  name: "grep",
@@ -906,7 +883,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
906
883
  waiter = r;
907
884
  });
908
885
  }
909
- const c = expectDefined3(queue.shift());
886
+ const c = expectDefined(queue.shift());
910
887
  if (c.kind === "error") {
911
888
  errored = true;
912
889
  continue;
@@ -1055,7 +1032,7 @@ async function runNative(input, base, mode, limit, signal) {
1055
1032
 
1056
1033
  // src/circuit-breaker.ts
1057
1034
  var DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;
1058
- var DEFAULT_SLOW_CALL_THRESHOLD_MS = 6e4;
1035
+ var DEFAULT_SLOW_CALL_THRESHOLD_MS = 18e4;
1059
1036
  var DEFAULT_MAX_SLOW_CALLS = 3;
1060
1037
  var DEFAULT_WINDOW_MS = 6e4;
1061
1038
  var DEFAULT_MAX_CALLS_PER_WINDOW = 30;
@@ -1198,12 +1175,6 @@ var CircuitBreaker = class {
1198
1175
  };
1199
1176
 
1200
1177
  // src/process-registry.ts
1201
- function expectDefined4(value) {
1202
- if (value === null || value === void 0) {
1203
- throw new Error("Expected value to be defined");
1204
- }
1205
- return value;
1206
- }
1207
1178
  var SENSITIVE_FLAG_PATTERNS = [
1208
1179
  // --flag=value or --flag "value" (value captured up to next space or comma)
1209
1180
  /--(?:token|password|passwd|pwd|secret|api[-_]?key|api[-_]?secret|auth|credential|private[-_]?key|access[-_]?key|github[-_]?token|gh[-_]?token|bearer|jwt|oauth|pin|pincode|passphrase|access[-_]?token)(?:[=\s,][^\s]*)?/gi,
@@ -1224,7 +1195,7 @@ function redactCommand(cmd) {
1224
1195
  const sp = match.search(/\s/);
1225
1196
  const delim = eq !== -1 ? "=" : sp !== -1 ? match[sp] : null;
1226
1197
  if (delim !== null) {
1227
- const flag = match.slice(0, match.indexOf(expectDefined4(delim)) + 1);
1198
+ const flag = match.slice(0, match.indexOf(expectDefined(delim)) + 1);
1228
1199
  return `${flag}[REDACTED]`;
1229
1200
  }
1230
1201
  const flagEnd = match.match(/^--?[a-zA-Z][a-zA-Z0-9_-]*/)?.[0] ?? match;
@@ -1407,7 +1378,7 @@ function _resetProcessRegistry() {
1407
1378
 
1408
1379
  // src/bash.ts
1409
1380
  var MAX_OUTPUT = 32768;
1410
- var DEFAULT_TIMEOUT_MS = 3e4;
1381
+ var DEFAULT_TIMEOUT_MS = 3e5;
1411
1382
  var STREAM_FLUSH_INTERVAL_MS = 200;
1412
1383
  var STREAM_FLUSH_BYTES = 4 * 1024;
1413
1384
  var bashTool = {
@@ -1423,9 +1394,9 @@ var bashTool = {
1423
1394
  // explicitly removes the implicit cross-tool aliasing.
1424
1395
  subjectKey: "command",
1425
1396
  capabilities: ["shell.arbitrary"],
1426
- timeoutMs: 3e4,
1397
+ timeoutMs: 3e5,
1427
1398
  maxOutputBytes: MAX_OUTPUT,
1428
- estimatedDurationMs: 3e3,
1399
+ estimatedDurationMs: 3e4,
1429
1400
  inputSchema: {
1430
1401
  type: "object",
1431
1402
  properties: {
@@ -2275,14 +2246,6 @@ function htmlToMarkdown(html) {
2275
2246
  function stripTags(s) {
2276
2247
  return s.replace(/<[^>]+>/g, "");
2277
2248
  }
2278
-
2279
- // src/search.ts
2280
- function expectDefined5(value) {
2281
- if (value === null || value === void 0) {
2282
- throw new Error("Expected value to be defined");
2283
- }
2284
- return value;
2285
- }
2286
2249
  var DEFAULT_NUM = 10;
2287
2250
  var MAX_RESULTS = 50;
2288
2251
  var TIMEOUT_MS2 = 15e3;
@@ -2378,11 +2341,11 @@ function parseDuckDuckGo(html, num) {
2378
2341
  const snippetRegex = /<a class="result-link"[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>/gi;
2379
2342
  const snippet2Regex = /<a class="result-snippet"[^>]*>([^<]+)<\/a>/gi;
2380
2343
  const linkMatches = takeFrom(
2381
- [...html.matchAll(snippetRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined5(m[1]), title: stripTags2(expectDefined5(m[2])) })),
2344
+ [...html.matchAll(snippetRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined(m[1]), title: stripTags2(expectDefined(m[2])) })),
2382
2345
  num
2383
2346
  );
2384
2347
  const snippetMatches = takeFrom(
2385
- [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2348
+ [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined(m[1]))),
2386
2349
  num
2387
2350
  );
2388
2351
  for (let i = 0; i < linkMatches.length && i < num; i++) {
@@ -2413,15 +2376,15 @@ function parseGoogleResults(html, num) {
2413
2376
  const urlRegex = /<cite[^>]*>([^<]+)<\/cite>/gi;
2414
2377
  const snippetRegex = /<span[^>]*class="[^"]*aXCZ0b[^>]*>([^<]+)<\/span>/gi;
2415
2378
  const titles = takeFrom(
2416
- [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2379
+ [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined(m[1]))),
2417
2380
  num
2418
2381
  );
2419
2382
  const urls = takeFrom(
2420
- [...html.matchAll(urlRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1])).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1")).filter((u) => u.startsWith("http")),
2383
+ [...html.matchAll(urlRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined(m[1])).replace(/^\*(https?:\/\/[^\s]+).*$/, "$1")).filter((u) => u.startsWith("http")),
2421
2384
  num
2422
2385
  );
2423
2386
  const snippets = takeFrom(
2424
- [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2387
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined(m[1]))),
2425
2388
  num
2426
2389
  );
2427
2390
  for (let i = 0; i < Math.min(titles.length, num); i++) {
@@ -2450,11 +2413,11 @@ function parseBingResults(html, num) {
2450
2413
  const titleRegex = /<h2[^>]*>\s*<a[^>]+href="([^"]+)"[^>]*>([^<]+)<\/a>\s*<\/h2>/gi;
2451
2414
  const snippetRegex = /<p[^>]*class="[^"]*b_paractl[^"]*"[^>]*>([^<]+)<\/p>/gi;
2452
2415
  const entries = takeFrom(
2453
- [...html.matchAll(titleRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined5(m[1]), title: stripTags2(expectDefined5(m[2])) })),
2416
+ [...html.matchAll(titleRegex)].filter((m) => m[1] && m[2]).map((m) => ({ url: expectDefined(m[1]), title: stripTags2(expectDefined(m[2])) })),
2454
2417
  num
2455
2418
  );
2456
2419
  const snippets = takeFrom(
2457
- [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined5(m[1]))),
2420
+ [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags2(expectDefined(m[1]))),
2458
2421
  num
2459
2422
  );
2460
2423
  for (let i = 0; i < entries.length; i++) {
@@ -2820,7 +2783,20 @@ var gitTool = {
2820
2783
  };
2821
2784
  }
2822
2785
  const args = buildArgs(input);
2823
- return await runGit(args, gitDir, opts.signal);
2786
+ let stagedDiff;
2787
+ if (input.command === "commit" && !input.dry_run) {
2788
+ try {
2789
+ const diffResult = await runGit(["diff", "--cached"], gitDir, opts.signal);
2790
+ if (diffResult.exitCode === 0) {
2791
+ const MAX_DIFF = 2e4;
2792
+ stagedDiff = diffResult.stdout.length > MAX_DIFF ? diffResult.stdout.slice(0, MAX_DIFF) + "\n\n... (diff truncated)" : diffResult.stdout;
2793
+ }
2794
+ } catch {
2795
+ }
2796
+ }
2797
+ const result = await runGit(args, gitDir, opts.signal);
2798
+ if (stagedDiff !== void 0) result.diff = stagedDiff;
2799
+ return result;
2824
2800
  }
2825
2801
  };
2826
2802
  function validateWorktreeInput(input, projectRoot) {
@@ -3345,12 +3321,6 @@ function formatWithLineNumbers(file, lines) {
3345
3321
  return `--- ${file} (line-numbered dump, not a unified diff) ---
3346
3322
  ${numbered}`;
3347
3323
  }
3348
- function expectDefined6(value) {
3349
- if (value === null || value === void 0) {
3350
- throw new Error("Expected value to be defined");
3351
- }
3352
- return value;
3353
- }
3354
3324
  var DEFAULT_IGNORE4 = [
3355
3325
  "node_modules",
3356
3326
  ".git",
@@ -3463,7 +3433,7 @@ var treeTool = {
3463
3433
  });
3464
3434
  while (!walkDone || queue.length > 0) {
3465
3435
  if (queue.length > 0) {
3466
- yield expectDefined6(queue.shift());
3436
+ yield expectDefined(queue.shift());
3467
3437
  } else {
3468
3438
  let pollTimer;
3469
3439
  const poll = new Promise((r) => {
@@ -3528,12 +3498,6 @@ async function walkDir(dir, depth, opts) {
3528
3498
  }
3529
3499
  }
3530
3500
  }
3531
- function expectDefined7(value) {
3532
- if (value === null || value === void 0) {
3533
- throw new Error("Expected value to be defined");
3534
- }
3535
- return value;
3536
- }
3537
3501
  async function* spawnStream(opts) {
3538
3502
  const max = opts.maxBytes ?? 2e5;
3539
3503
  const flushAt = opts.flushBytes ?? 4 * 1024;
@@ -3585,7 +3549,7 @@ async function* spawnStream(opts) {
3585
3549
  waiter = resolve7;
3586
3550
  });
3587
3551
  }
3588
- const chunk = expectDefined7(queue.shift());
3552
+ const chunk = expectDefined(queue.shift());
3589
3553
  if (chunk.kind === "close") {
3590
3554
  if (!spawnFailed) exitCode = chunk.code ?? 0;
3591
3555
  break;
@@ -5252,8 +5216,8 @@ function rememberTool(memory) {
5252
5216
  return {
5253
5217
  name: "remember",
5254
5218
  category: "Session",
5255
- description: "Persist important long-term facts into project or user memory. These memories survive conversation restarts and are available to future sessions.",
5256
- usageHint: 'USE VERY SPARINGLY \u2014 ONLY FOR HIGH-VALUE RECURRING KNOWLEDGE:\n\n- Good: coding standards, project conventions, user preferences, recurring architecture decisions, important facts.\n- Bad: temporary state, current task progress, one-off notes \u2192 use `todo` or `plan` instead.\n- `scope: "project"` \u2192 visible to all agents on this codebase.\n- `scope: "user"` \u2192 personal to you.\n\nPolluting memory with noise hurts future context quality. Be extremely deliberate.',
5219
+ description: "Persist facts, conventions, decisions, and preferences into long-term memory. Memories survive restarts and are scored for relevance in future sessions.",
5220
+ usageHint: "Persist facts, conventions, decisions, and preferences into long-term memory.\n\nWHEN TO USE:\n- Project conventions discovered during a task (build tool, lint rules, code style)\n- Architecture decisions made (chose X over Y, decided to use pattern Z)\n- User preferences expressed (prefers short names, always uses pnpm)\n- Anti-patterns identified (never do X, avoid pattern Y)\n- File/location references useful across sessions\n\nWHEN NOT TO USE:\n- Temporary task state or progress \u2192 use `todo`\n- One-off debugging notes\n- Information already obvious from the codebase\n\nAlways include `type` and `priority`. Use 1-3 `tags` for grouping.\nBetter to remember a fact now than rediscover it next session.",
5257
5221
  permission: "auto",
5258
5222
  mutating: true,
5259
5223
  timeoutMs: 2e3,
@@ -5268,6 +5232,21 @@ function rememberTool(memory) {
5268
5232
  type: "string",
5269
5233
  enum: ["project-agents", "project-memory", "user-memory"],
5270
5234
  description: "Where to store it: project-memory (shared), user-memory (personal), or project-agents."
5235
+ },
5236
+ type: {
5237
+ type: "string",
5238
+ enum: ["fact", "decision", "convention", "preference", "reference", "anti_pattern"],
5239
+ description: "Category for filtering and relevance scoring."
5240
+ },
5241
+ tags: {
5242
+ type: "array",
5243
+ items: { type: "string" },
5244
+ description: "Hashtag-style tags for grouping and search."
5245
+ },
5246
+ priority: {
5247
+ type: "string",
5248
+ enum: ["critical", "high", "medium", "low"],
5249
+ description: "Priority level. Critical = always injected into context."
5271
5250
  }
5272
5251
  },
5273
5252
  required: ["text"]
@@ -5275,7 +5254,11 @@ function rememberTool(memory) {
5275
5254
  async execute(input) {
5276
5255
  if (!input?.text) throw new Error("remember: text is required");
5277
5256
  const scope = input.scope ?? "project-memory";
5278
- await memory.remember(input.text, scope);
5257
+ await memory.remember(input.text, scope, {
5258
+ type: input.type,
5259
+ tags: input.tags,
5260
+ priority: input.priority
5261
+ });
5279
5262
  return { ok: true, scope };
5280
5263
  }
5281
5264
  };
@@ -5305,6 +5288,98 @@ function forgetTool(memory) {
5305
5288
  }
5306
5289
  };
5307
5290
  }
5291
+ function searchMemoryTool(memory) {
5292
+ return {
5293
+ name: "search_memory",
5294
+ category: "Session",
5295
+ description: "Search memory entries by content. With the default backend this does substring matching; semantic/graph backends use embedding similarity or graph traversal.",
5296
+ usageHint: "Search long-term memory for relevant facts, conventions, or decisions.\n- Returns results ordered by relevance (newest-first for default, similarity for semantic).\n- Use before starting a task to recall project conventions and past decisions.\n- `limit` caps results (default 5, max 20).",
5297
+ permission: "auto",
5298
+ mutating: false,
5299
+ timeoutMs: 2e3,
5300
+ inputSchema: {
5301
+ type: "object",
5302
+ properties: {
5303
+ query: {
5304
+ type: "string",
5305
+ description: "Search query \u2014 words or phrase to find in memory."
5306
+ },
5307
+ scope: {
5308
+ type: "string",
5309
+ enum: ["project-agents", "project-memory", "user-memory"],
5310
+ description: "Which scope to search. Defaults to project-memory."
5311
+ },
5312
+ limit: {
5313
+ type: "number",
5314
+ description: "Maximum results to return (default 5, max 20)."
5315
+ }
5316
+ },
5317
+ required: ["query"]
5318
+ },
5319
+ async execute(input) {
5320
+ if (!input?.query) throw new Error("search_memory: query is required");
5321
+ const scope = input.scope ?? "project-memory";
5322
+ const limit = Math.min(input.limit ?? 5, 20);
5323
+ const entries = await memory.search(input.query, scope, limit);
5324
+ return {
5325
+ results: entries.map((e) => ({
5326
+ text: e.text,
5327
+ ts: e.ts,
5328
+ scope: e.scope,
5329
+ type: e.type,
5330
+ tags: e.tags,
5331
+ priority: e.priority
5332
+ }))
5333
+ };
5334
+ }
5335
+ };
5336
+ }
5337
+ function relatedMemoryTool(memory) {
5338
+ return {
5339
+ name: "find_related_memories",
5340
+ category: "Session",
5341
+ description: "Find memories related to the given text via graph traversal. Only available with graph backends; falls back to content search with file backends.",
5342
+ usageHint: "Discover memories connected to a topic through co-occurrence or similarity edges.\n- Useful for exploring what else the project knows about a given concept.\n- Falls back to content search when no graph backend is configured.\n- `limit` caps results (default 5, max 20).",
5343
+ permission: "auto",
5344
+ mutating: false,
5345
+ timeoutMs: 2e3,
5346
+ inputSchema: {
5347
+ type: "object",
5348
+ properties: {
5349
+ text: {
5350
+ type: "string",
5351
+ description: "Text to find related memories for."
5352
+ },
5353
+ scope: {
5354
+ type: "string",
5355
+ enum: ["project-agents", "project-memory", "user-memory"],
5356
+ description: "Which scope to search. Defaults to project-memory."
5357
+ },
5358
+ limit: {
5359
+ type: "number",
5360
+ description: "Maximum results to return (default 5, max 20)."
5361
+ }
5362
+ },
5363
+ required: ["text"]
5364
+ },
5365
+ async execute(input) {
5366
+ if (!input?.text) throw new Error("find_related_memories: text is required");
5367
+ const scope = input.scope ?? "project-memory";
5368
+ const limit = Math.min(input.limit ?? 5, 20);
5369
+ const entries = memory.findRelated ? await memory.findRelated(input.text, scope, limit) : await memory.search(input.text, scope, limit);
5370
+ return {
5371
+ results: entries.map((e) => ({
5372
+ text: e.text,
5373
+ ts: e.ts,
5374
+ scope: e.scope,
5375
+ type: e.type,
5376
+ tags: e.tags,
5377
+ priority: e.priority
5378
+ }))
5379
+ };
5380
+ }
5381
+ };
5382
+ }
5308
5383
 
5309
5384
  // src/mode.ts
5310
5385
  function createModeTool(modeStore) {
@@ -5426,12 +5501,6 @@ function lspKindToInternalKind(k) {
5426
5501
  }
5427
5502
 
5428
5503
  // src/codebase-index/writer.ts
5429
- function expectDefined8(value) {
5430
- if (value === null || value === void 0) {
5431
- throw new Error("Expected value to be defined");
5432
- }
5433
- return value;
5434
- }
5435
5504
  var DB_FILE = "index.db";
5436
5505
  function resolveIndexDir(projectRoot, override) {
5437
5506
  return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
@@ -5576,7 +5645,7 @@ var IndexStore = class {
5576
5645
  "SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
5577
5646
  ).all(file);
5578
5647
  if (!rows.length) return null;
5579
- const r = expectDefined8(rows[0]);
5648
+ const r = expectDefined(rows[0]);
5580
5649
  return { file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed };
5581
5650
  }
5582
5651
  getAllFileMetas() {
@@ -6489,12 +6558,6 @@ function syncPyParse(filePath, lang) {
6489
6558
  return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
6490
6559
  }
6491
6560
  }
6492
- function expectDefined9(value) {
6493
- if (value === null || value === void 0) {
6494
- throw new Error("Expected value to be defined");
6495
- }
6496
- return value;
6497
- }
6498
6561
  function parseSymbols4(opts) {
6499
6562
  const { file, content, lang } = opts;
6500
6563
  const nativeAvailable = checkNativeParser();
@@ -6534,8 +6597,7 @@ function tryNativeParse(file, content) {
6534
6597
  const toolsDir = path.join(process.cwd(), "tools");
6535
6598
  const crateDir = path.join(toolsDir, "syn-parser");
6536
6599
  const tmpFile = path.join(crateDir, "src", "input.rs");
6537
- const { writeFileSync: writeFileSync3 } = __require("node:fs");
6538
- writeFileSync3(tmpFile, content, "utf8");
6600
+ writeFileSync(tmpFile, content, "utf8");
6539
6601
  const result = spawnSync(
6540
6602
  "cargo",
6541
6603
  ["run", "--manifest-path", path.join(toolsDir, "Cargo.toml")],
@@ -6583,7 +6645,7 @@ function regexParse(opts) {
6583
6645
  let hi = lineOffsets.length - 1;
6584
6646
  while (lo < hi) {
6585
6647
  const mid = lo + hi + 1 >>> 1;
6586
- if (expectDefined9(lineOffsets[mid]) <= offset) lo = mid;
6648
+ if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;
6587
6649
  else hi = mid - 1;
6588
6650
  }
6589
6651
  return lo + 1;
@@ -6595,7 +6657,7 @@ function regexParse(opts) {
6595
6657
  for (const pattern of RS_PATTERNS) {
6596
6658
  pattern.regex.lastIndex = 0;
6597
6659
  for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
6598
- const name = expectDefined9(match[1]);
6660
+ const name = expectDefined(match[1]);
6599
6661
  const offset = match.index ?? 0;
6600
6662
  const line = lineFromOffset(offset);
6601
6663
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -6625,12 +6687,6 @@ function regexParse(opts) {
6625
6687
  });
6626
6688
  return { file, lang, symbols: deduped, mtimeMs: Date.now() };
6627
6689
  }
6628
- function expectDefined10(value) {
6629
- if (value === null || value === void 0) {
6630
- throw new Error("Expected value to be defined");
6631
- }
6632
- return value;
6633
- }
6634
6690
  function parseSymbols5(opts) {
6635
6691
  const { file, content, lang } = opts;
6636
6692
  try {
@@ -6657,14 +6713,14 @@ function regexParse2(opts) {
6657
6713
  let hi = lineOffsets.length - 1;
6658
6714
  while (lo < hi) {
6659
6715
  const mid = lo + hi + 1 >>> 1;
6660
- if (expectDefined10(lineOffsets[mid]) <= offset) lo = mid;
6716
+ if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;
6661
6717
  else hi = mid - 1;
6662
6718
  }
6663
6719
  return lo + 1;
6664
6720
  }
6665
6721
  const rootMatch = content.match(/^\s*\{/m);
6666
6722
  if (rootMatch) {
6667
- const offset = expectDefined10(rootMatch.index);
6723
+ const offset = expectDefined(rootMatch.index);
6668
6724
  const line = lineFromOffset(offset);
6669
6725
  symbols.push(
6670
6726
  makeSymbol({
@@ -6680,7 +6736,7 @@ function regexParse2(opts) {
6680
6736
  }
6681
6737
  const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
6682
6738
  for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
6683
- const key = expectDefined10(match[1]);
6739
+ const key = expectDefined(match[1]);
6684
6740
  const offset = match.index ?? 0;
6685
6741
  const line = lineFromOffset(offset);
6686
6742
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -6727,7 +6783,7 @@ function regexParse2(opts) {
6727
6783
  const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
6728
6784
  const defsMatch = defsRegex.exec(content);
6729
6785
  if (defsMatch !== null) {
6730
- const offset = expectDefined10(defsMatch.index);
6786
+ const offset = expectDefined(defsMatch.index);
6731
6787
  const line = lineFromOffset(offset);
6732
6788
  symbols.push(
6733
6789
  makeSymbol({
@@ -6752,7 +6808,7 @@ function regexParse2(opts) {
6752
6808
  for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
6753
6809
  const offset = match.index ?? 0;
6754
6810
  const line = lineFromOffset(offset);
6755
- const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined10(match[0]);
6811
+ const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined(match[0]);
6756
6812
  symbols.push(
6757
6813
  makeSymbol({
6758
6814
  name: key,
@@ -6771,12 +6827,12 @@ function regexParse2(opts) {
6771
6827
  function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
6772
6828
  const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
6773
6829
  for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
6774
- const blockContent = expectDefined10(match[0]);
6830
+ const blockContent = expectDefined(match[0]);
6775
6831
  const blockOffset = match.index ?? 0;
6776
6832
  const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
6777
6833
  for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
6778
- const key = expectDefined10(scriptMatch[1]);
6779
- const keyOffset = blockOffset + expectDefined10(scriptMatch.index);
6834
+ const key = expectDefined(scriptMatch[1]);
6835
+ const keyOffset = blockOffset + expectDefined(scriptMatch.index);
6780
6836
  const line = lineFromOffset(keyOffset);
6781
6837
  symbols.push(
6782
6838
  makeSymbol({
@@ -6795,12 +6851,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
6795
6851
  function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
6796
6852
  const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
6797
6853
  for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
6798
- const blockContent = expectDefined10(match[0]);
6854
+ const blockContent = expectDefined(match[0]);
6799
6855
  const blockOffset = match.index ?? 0;
6800
6856
  const optKeyRegex = /"(\w[\w]*)"\s*:/g;
6801
6857
  for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
6802
- const key = expectDefined10(optMatch[1]);
6803
- const keyOffset = blockOffset + expectDefined10(optMatch.index);
6858
+ const key = expectDefined(optMatch[1]);
6859
+ const keyOffset = blockOffset + expectDefined(optMatch.index);
6804
6860
  const line = lineFromOffset(keyOffset);
6805
6861
  if (line <= parentLine) continue;
6806
6862
  symbols.push(
@@ -6832,14 +6888,6 @@ function makeSymbol(opts) {
6832
6888
  text: `${opts.name} ${opts.signature}`.trim()
6833
6889
  };
6834
6890
  }
6835
-
6836
- // src/codebase-index/yaml-parser.ts
6837
- function expectDefined11(value) {
6838
- if (value === null || value === void 0) {
6839
- throw new Error("Expected value to be defined");
6840
- }
6841
- return value;
6842
- }
6843
6891
  function parseSymbols6(opts) {
6844
6892
  const { file, content, lang } = opts;
6845
6893
  try {
@@ -6861,14 +6909,14 @@ function regexParse3(opts) {
6861
6909
  let hi = lineOffsets.length - 1;
6862
6910
  while (lo < hi) {
6863
6911
  const mid = lo + hi + 1 >>> 1;
6864
- if (expectDefined11(lineOffsets[mid]) <= offset) lo = mid;
6912
+ if (expectDefined(lineOffsets[mid]) <= offset) lo = mid;
6865
6913
  else hi = mid - 1;
6866
6914
  }
6867
6915
  return lo + 1;
6868
6916
  }
6869
6917
  const anchorRegex = /&(\w[\w-]*)/g;
6870
6918
  for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
6871
- const name = expectDefined11(match[1]);
6919
+ const name = expectDefined(match[1]);
6872
6920
  const offset = match.index ?? 0;
6873
6921
  const line = lineFromOffset(offset);
6874
6922
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -6886,7 +6934,7 @@ function regexParse3(opts) {
6886
6934
  }
6887
6935
  const aliasRegex = /\*(\w[\w-]*)/g;
6888
6936
  for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
6889
- const name = expectDefined11(match[1]);
6937
+ const name = expectDefined(match[1]);
6890
6938
  const offset = match.index ?? 0;
6891
6939
  const line = lineFromOffset(offset);
6892
6940
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -6921,7 +6969,7 @@ function regexParse3(opts) {
6921
6969
  }
6922
6970
  const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
6923
6971
  for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
6924
- const key = expectDefined11(match[2]);
6972
+ const key = expectDefined(match[2]);
6925
6973
  const offset = match.index ?? 0;
6926
6974
  const line = lineFromOffset(offset);
6927
6975
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -6941,7 +6989,7 @@ function regexParse3(opts) {
6941
6989
  }
6942
6990
  const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
6943
6991
  for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
6944
- const key = expectDefined11(match[2]);
6992
+ const key = expectDefined(match[2]);
6945
6993
  const offset = match.index ?? 0;
6946
6994
  const line = lineFromOffset(offset);
6947
6995
  const col = offset - (lineOffsets[line - 1] ?? 0);
@@ -7161,12 +7209,6 @@ function cancelPendingReindexes() {
7161
7209
  }
7162
7210
 
7163
7211
  // src/codebase-index/indexer.ts
7164
- function expectDefined12(value) {
7165
- if (value === null || value === void 0) {
7166
- throw new Error("Expected value to be defined");
7167
- }
7168
- return value;
7169
- }
7170
7212
  var YIELD_EVERY_N = 50;
7171
7213
  function yieldEventLoop() {
7172
7214
  return new Promise((resolve7) => setImmediate(resolve7));
@@ -7275,7 +7317,7 @@ async function runIndexer(_ctx, opts) {
7275
7317
  for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
7276
7318
  }
7277
7319
  for (let fi = 0; fi < files.length; fi++) {
7278
- const file = expectDefined12(files[fi]);
7320
+ const file = expectDefined(files[fi]);
7279
7321
  _setIndexProgress(fi + 1, files.length);
7280
7322
  if (fi > 0 && fi % YIELD_EVERY_N === 0) {
7281
7323
  await yieldEventLoop();
@@ -7332,7 +7374,7 @@ async function runIndexer(_ctx, opts) {
7332
7374
  langStats[lang] = (langStats[lang] ?? 0) + count;
7333
7375
  if (parsed.refs && parsed.refs.length > 0) {
7334
7376
  for (let i = 0; i < symbolsWithIds.length; i++) {
7335
- const sym = expectDefined12(symbolsWithIds[i]);
7377
+ const sym = expectDefined(symbolsWithIds[i]);
7336
7378
  const symRefs = parsed.refs.filter((r) => r.line === sym.line);
7337
7379
  if (symRefs.length > 0) {
7338
7380
  const refsWithFromId = symRefs.map((r) => ({ ...r, fromId: sym.id }));
@@ -7503,12 +7545,6 @@ var Bm25Index = class {
7503
7545
  };
7504
7546
 
7505
7547
  // src/codebase-index/codebase-search-tool.ts
7506
- function expectDefined13(value) {
7507
- if (value === null || value === void 0) {
7508
- throw new Error("Expected value to be defined");
7509
- }
7510
- return value;
7511
- }
7512
7548
  var codebaseSearchTool = {
7513
7549
  name: "codebase-search",
7514
7550
  category: "Project",
@@ -7598,7 +7634,7 @@ var codebaseSearchTool = {
7598
7634
  const top = scored.slice(0, limit);
7599
7635
  const qTokens = tokenise(input.query);
7600
7636
  const results = top.map(({ id, score }) => {
7601
- const c = expectDefined13(candidates.find((c2) => c2.id === id));
7637
+ const c = expectDefined(candidates.find((c2) => c2.id === id));
7602
7638
  const snippet = bm25.extractSnippet(id, qTokens);
7603
7639
  return {
7604
7640
  ...c,
@@ -7723,6 +7759,6 @@ var builtinToolsPack = {
7723
7759
  tools: builtinTools
7724
7760
  };
7725
7761
 
7726
- export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, runStartupIndex, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
7762
+ export { CircuitBreaker, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, gitTool, globTool, grepTool, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, runStartupIndex, scaffoldTool, searchMemoryTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
7727
7763
  //# sourceMappingURL=index.js.map
7728
7764
  //# sourceMappingURL=index.js.map