@wrongstack/tools 0.1.9 → 0.2.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 (65) hide show
  1. package/dist/audit.js +19 -17
  2. package/dist/audit.js.map +1 -1
  3. package/dist/bash.js +2 -80
  4. package/dist/bash.js.map +1 -1
  5. package/dist/batch-tool-use.js +1 -1
  6. package/dist/batch-tool-use.js.map +1 -1
  7. package/dist/builtin.js +3002 -2867
  8. package/dist/builtin.js.map +1 -1
  9. package/dist/diff.js +8 -3
  10. package/dist/diff.js.map +1 -1
  11. package/dist/document.js +31 -5
  12. package/dist/document.js.map +1 -1
  13. package/dist/edit.js +1 -3
  14. package/dist/edit.js.map +1 -1
  15. package/dist/exec.js +26 -82
  16. package/dist/exec.js.map +1 -1
  17. package/dist/fetch.js +5 -1
  18. package/dist/fetch.js.map +1 -1
  19. package/dist/format.js +24 -18
  20. package/dist/format.js.map +1 -1
  21. package/dist/git.js +6 -6
  22. package/dist/git.js.map +1 -1
  23. package/dist/glob.js.map +1 -1
  24. package/dist/grep.js +23 -23
  25. package/dist/grep.js.map +1 -1
  26. package/dist/index.d.ts +37 -1
  27. package/dist/index.js +310 -169
  28. package/dist/index.js.map +1 -1
  29. package/dist/install.js +31 -20
  30. package/dist/install.js.map +1 -1
  31. package/dist/json.js +4 -1
  32. package/dist/json.js.map +1 -1
  33. package/dist/lint.js +19 -17
  34. package/dist/lint.js.map +1 -1
  35. package/dist/logs.js +25 -22
  36. package/dist/logs.js.map +1 -1
  37. package/dist/memory.js.map +1 -1
  38. package/dist/mode.js +5 -1
  39. package/dist/mode.js.map +1 -1
  40. package/dist/outdated.js +10 -7
  41. package/dist/outdated.js.map +1 -1
  42. package/dist/patch.js +4 -9
  43. package/dist/patch.js.map +1 -1
  44. package/dist/read.js +5 -1
  45. package/dist/read.js.map +1 -1
  46. package/dist/replace.js +43 -30
  47. package/dist/replace.js.map +1 -1
  48. package/dist/scaffold.js +35 -20
  49. package/dist/scaffold.js.map +1 -1
  50. package/dist/search.js +5 -1
  51. package/dist/search.js.map +1 -1
  52. package/dist/test.js +17 -15
  53. package/dist/test.js.map +1 -1
  54. package/dist/todo.js.map +1 -1
  55. package/dist/tool-help.js +10 -8
  56. package/dist/tool-help.js.map +1 -1
  57. package/dist/tool-search.js.map +1 -1
  58. package/dist/tool-use.js +1 -1
  59. package/dist/tool-use.js.map +1 -1
  60. package/dist/tree.js +9 -1
  61. package/dist/tree.js.map +1 -1
  62. package/dist/typecheck.js +17 -15
  63. package/dist/typecheck.js.map +1 -1
  64. package/dist/write.js.map +1 -1
  65. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as fs4 from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import { dirname } from 'path';
4
- import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, stripAnsi } from '@wrongstack/core';
4
+ import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, buildChildEnv, stripAnsi, loadPlan, emptyPlan, clearPlan, savePlan, removePlanItem, setPlanItemStatus, addPlanItem, formatPlan } from '@wrongstack/core';
5
5
  import { spawn } from 'child_process';
6
6
  import * as os from 'os';
7
7
  import * as dns from 'dns/promises';
@@ -79,7 +79,11 @@ var readTool = {
79
79
  const allLines = text.split(/\r\n|\r|\n/);
80
80
  const total = allLines.length;
81
81
  const offset = Math.max(1, input.offset ?? 1);
82
- const limit = Math.max(1, Math.min(input.limit ?? 2e3, 5e3));
82
+ const limit = Math.max(0, Math.min(input.limit ?? 2e3, 5e3));
83
+ if (limit === 0) {
84
+ ctx.recordRead(absPath, stat9.mtimeMs);
85
+ return { text: "", total_lines: total, encoding: "utf8", truncated: total > 0 };
86
+ }
83
87
  const slice = allLines.slice(offset - 1, offset - 1 + limit);
84
88
  const truncated = offset - 1 + slice.length < total;
85
89
  const width = String(offset + slice.length - 1).length;
@@ -174,9 +178,7 @@ var editTool = {
174
178
  });
175
179
  if (!stat9.isFile()) throw new Error(`edit: "${input.path}" is not a regular file`);
176
180
  if (!ctx.hasRead(absPath)) {
177
- throw new Error(
178
- `edit: file "${input.path}" was not read in this session. Read it first.`
179
- );
181
+ throw new Error(`edit: file "${input.path}" was not read in this session. Read it first.`);
180
182
  }
181
183
  const lastReadMtime = ctx.lastReadMtime(absPath);
182
184
  const mtimeTolerance = process.platform === "win32" ? 2e3 : 1;
@@ -315,7 +317,10 @@ var replaceTool = {
315
317
  description: "File(s) to target: single path, comma-separated list, or glob pattern"
316
318
  },
317
319
  glob: { type: "string", description: 'Additional glob filter (e.g. "*.ts")' },
318
- replace_all: { type: "boolean", description: "Replace all occurrences in each file (default: true)" },
320
+ replace_all: {
321
+ type: "boolean",
322
+ description: "Replace all occurrences in each file (default: true)"
323
+ },
319
324
  dry_run: { type: "boolean", description: "Preview changes without writing" }
320
325
  },
321
326
  required: ["pattern", "replacement", "files"]
@@ -325,7 +330,7 @@ var replaceTool = {
325
330
  if (input.replacement === void 0) throw new Error("replace: replacement is required");
326
331
  if (!input?.files) throw new Error("replace: files is required");
327
332
  const replaceAll = input.replace_all ?? true;
328
- const compiled = compileUserRegex(input.pattern, replaceAll ? "g" : "");
333
+ const compiled = compileUserRegex(input.pattern, "g");
329
334
  if (!compiled.ok) {
330
335
  throw new Error(`replace: ${compiled.reason}`);
331
336
  }
@@ -364,16 +369,25 @@ var replaceTool = {
364
369
  const style = detectNewlineStyle(content);
365
370
  const contentLf = normalizeToLf(content);
366
371
  re.lastIndex = 0;
367
- const matches = [...contentLf.matchAll(re)];
368
- if (matches.length === 0) continue;
369
- const newContentLf = contentLf.replace(re, input.replacement);
372
+ const allMatches = [...contentLf.matchAll(re)];
373
+ if (allMatches.length === 0) continue;
374
+ const matches = replaceAll ? allMatches : allMatches.slice(0, 1);
375
+ const count = matches.length;
376
+ let newContentLf = contentLf;
377
+ for (let i = matches.length - 1; i >= 0; i--) {
378
+ const m = matches[i];
379
+ newContentLf = newContentLf.slice(0, m.index) + input.replacement + newContentLf.slice(m.index + m[0].length);
380
+ }
370
381
  re.lastIndex = 0;
371
- totalReplacements += matches.length;
382
+ totalReplacements += count;
372
383
  if (!dryRun) {
373
384
  const newContent = toStyle(newContentLf, style);
374
385
  await atomicWrite(realPath, newContent, { mode: stat9.mode & 511 });
375
386
  }
376
- const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), { fromFile: absPath, toFile: absPath }) : void 0;
387
+ const diff = dryRun || matches.length > 0 ? unifiedDiff(content, toStyle(newContentLf, style), {
388
+ fromFile: absPath,
389
+ toFile: absPath
390
+ }) : void 0;
377
391
  results.push({
378
392
  path: absPath,
379
393
  replacements: matches.length,
@@ -457,6 +471,7 @@ async function globNative(pattern, base, extraGlob) {
457
471
  for (const e of entries) {
458
472
  if (DEFAULT_IGNORE.includes(e.name)) continue;
459
473
  const full = path.join(dir, e.name);
474
+ if (e.isSymbolicLink()) continue;
460
475
  if (e.isDirectory()) {
461
476
  await walk(full);
462
477
  } else if (e.isFile()) {
@@ -793,84 +808,6 @@ async function runNative(input, base, mode, limit, signal) {
793
808
  };
794
809
  }
795
810
 
796
- // src/_env.ts
797
- var ALLOWED_KEYS = /* @__PURE__ */ new Set([
798
- "PATH",
799
- "HOME",
800
- "USER",
801
- "USERNAME",
802
- "LOGNAME",
803
- "SHELL",
804
- "LANG",
805
- "LC_ALL",
806
- "LC_CTYPE",
807
- "TERM",
808
- "TZ",
809
- "TMPDIR",
810
- "TEMP",
811
- "TMP",
812
- "PWD",
813
- "OLDPWD",
814
- "COMSPEC",
815
- "SYSTEMROOT",
816
- "SYSTEMDRIVE",
817
- "WINDIR",
818
- "PROGRAMFILES",
819
- "PROGRAMFILES(X86)",
820
- "PROGRAMDATA",
821
- "APPDATA",
822
- "LOCALAPPDATA",
823
- "USERPROFILE",
824
- "PUBLIC",
825
- "PATHEXT"
826
- ]);
827
- var SECRET_NAME_PARTS = [
828
- "TOKEN",
829
- "SECRET",
830
- "PASSWORD",
831
- "PASSWD",
832
- "AUTH",
833
- "CRED",
834
- "BEARER",
835
- "COOKIE",
836
- "PRIVATE"
837
- ];
838
- function looksSecret(name) {
839
- const upper = name.toUpperCase();
840
- for (const p of SECRET_NAME_PARTS) {
841
- if (upper.includes(p)) return true;
842
- }
843
- if (/(?:^|_)KEY(?:$|_|S$)/i.test(upper)) return true;
844
- if (/API[_-]?KEY/i.test(upper)) return true;
845
- if (/ACCESS[_-]?KEY/i.test(upper)) return true;
846
- if (/SESSION[_-]?ID/i.test(upper) === false && /SESSION/i.test(upper)) {
847
- return true;
848
- }
849
- return false;
850
- }
851
- function buildChildEnv(sessionId) {
852
- const passthrough = process.env["WRONGSTACK_BASH_ENV_PASSTHROUGH"] === "1";
853
- const out = {};
854
- for (const [k, v] of Object.entries(process.env)) {
855
- if (v === void 0) continue;
856
- if (passthrough) {
857
- out[k] = v;
858
- continue;
859
- }
860
- const upper = k.toUpperCase();
861
- if (ALLOWED_KEYS.has(upper)) {
862
- out[k] = v;
863
- continue;
864
- }
865
- if (looksSecret(upper)) continue;
866
- if (upper.startsWith("NODE_") || upper.startsWith("NPM_") || upper.startsWith("PNPM_") || upper.startsWith("YARN_") || upper.startsWith("GIT_") || upper.startsWith("CI") || upper.startsWith("XDG_") || upper === "EDITOR" || upper === "VISUAL" || upper === "PAGER") {
867
- out[k] = v;
868
- }
869
- }
870
- if (sessionId) out["WRONGSTACK_SESSION_ID"] = sessionId;
871
- return out;
872
- }
873
-
874
811
  // src/bash.ts
875
812
  var MAX_OUTPUT = 32768;
876
813
  var DEFAULT_TIMEOUT = 3e4;
@@ -908,7 +845,7 @@ var bashTool = {
908
845
  },
909
846
  async *executeStream(input, ctx, opts) {
910
847
  if (!input?.command) throw new Error("bash: command is required");
911
- const timeoutMs = Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT, 6e5);
848
+ const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT, 6e5));
912
849
  const isWin = os.platform() === "win32";
913
850
  const shell = isWin ? process.env["COMSPEC"] ?? "cmd.exe" : process.env["SHELL"] ?? "/bin/bash";
914
851
  const args = isWin ? ["/c", input.command] : ["-c", input.command];
@@ -1058,7 +995,19 @@ var ALLOWED_COMMANDS = {
1058
995
  npm: ["--version", "init", "install", "test", "run", "list", "pkg", "doctor"],
1059
996
  pnpm: ["--version", "init", "install", "add", "remove", "exec", "list", "run", "dlx"],
1060
997
  npx: ["--version"],
1061
- git: ["--version", "status", "log", "diff", "branch", "checkout", "stash", "add", "commit", "push", "pull"],
998
+ git: [
999
+ "--version",
1000
+ "status",
1001
+ "log",
1002
+ "diff",
1003
+ "branch",
1004
+ "checkout",
1005
+ "stash",
1006
+ "add",
1007
+ "commit",
1008
+ "push",
1009
+ "pull"
1010
+ ],
1062
1011
  ls: ["-la", "-l", "-a"],
1063
1012
  cat: [],
1064
1013
  head: ["-n"],
@@ -1092,7 +1041,7 @@ var execTool = {
1092
1041
  description: "Restricted shell that only runs pre-approved commands with constrained arguments. Safer alternative to `bash`.",
1093
1042
  usageHint: "Set `command` (must be in allowlist). `args` passed through. For arbitrary shell access use the `bash` tool instead.",
1094
1043
  permission: "confirm",
1095
- mutating: false,
1044
+ mutating: true,
1096
1045
  timeoutMs: TIMEOUT_MS,
1097
1046
  inputSchema: {
1098
1047
  type: "object",
@@ -1106,7 +1055,16 @@ var execTool = {
1106
1055
  },
1107
1056
  async execute(input, ctx, opts) {
1108
1057
  const cmd = input.command.trim();
1109
- if (!cmd) return { command: cmd, args: [], stdout: "", stderr: "Empty command", exitCode: 1, truncated: false, allowed: false };
1058
+ if (!cmd)
1059
+ return {
1060
+ command: cmd,
1061
+ args: [],
1062
+ stdout: "",
1063
+ stderr: "Empty command",
1064
+ exitCode: 1,
1065
+ truncated: false,
1066
+ allowed: false
1067
+ };
1110
1068
  if (!(cmd in ALLOWED_COMMANDS)) {
1111
1069
  return {
1112
1070
  command: cmd,
@@ -1119,7 +1077,7 @@ var execTool = {
1119
1077
  };
1120
1078
  }
1121
1079
  const args = (input.args ?? []).slice(0, MAX_ARGS);
1122
- const timeout = Math.min(input.timeout ?? TIMEOUT_MS, TIMEOUT_MS);
1080
+ const timeout = Math.max(1, Math.min(input.timeout ?? TIMEOUT_MS, TIMEOUT_MS));
1123
1081
  const requestedCwd = input.cwd ? path.resolve(ctx.projectRoot, input.cwd) : ctx.cwd;
1124
1082
  const rel = path.relative(ctx.projectRoot, requestedCwd);
1125
1083
  if (rel.startsWith("..") || path.isAbsolute(rel)) {
@@ -1272,7 +1230,11 @@ var fetchTool = {
1272
1230
  if (/^image\/|^audio\/|^video\/|application\/octet-stream/.test(ct)) {
1273
1231
  throw new Error(`fetch: refusing to read binary content-type "${ct}"`);
1274
1232
  }
1275
- yield { type: "log", text: `HTTP ${res.status} ${ct}`, data: { status: res.status, contentType: ct } };
1233
+ yield {
1234
+ type: "log",
1235
+ text: `HTTP ${res.status} ${ct}`,
1236
+ data: { status: res.status, contentType: ct }
1237
+ };
1276
1238
  const reader = res.body?.getReader();
1277
1239
  let received = 0;
1278
1240
  const chunks = [];
@@ -1507,7 +1469,11 @@ var searchTool = {
1507
1469
  if (!input?.query) throw new Error("search: query is required");
1508
1470
  const num = Math.max(1, Math.min(input.num_results ?? DEFAULT_NUM, MAX_RESULTS));
1509
1471
  const source = input.source ?? "duckduckgo";
1510
- yield { type: "log", text: `Querying ${source} for "${input.query}"\u2026`, data: { source, query: input.query } };
1472
+ yield {
1473
+ type: "log",
1474
+ text: `Querying ${source} for "${input.query}"\u2026`,
1475
+ data: { source, query: input.query }
1476
+ };
1511
1477
  let output;
1512
1478
  switch (source) {
1513
1479
  case "duckduckgo":
@@ -1724,6 +1690,103 @@ var todoTool = {
1724
1690
  };
1725
1691
  }
1726
1692
  };
1693
+ var planTool = {
1694
+ name: "plan",
1695
+ description: "Inspect or edit the strategic plan board for this session. Plans persist across resume (unlike todos). Use this to lay out the multi-step approach before diving in, then mark steps in_progress/done as the work proceeds.",
1696
+ usageHint: "Set action to one of: show | add | start | done | remove | clear. Pass `title` for add. Pass `target` (item id, 1-based index, or title substring) for start/done/remove. Always returns the formatted plan plus open/total counts.",
1697
+ permission: "auto",
1698
+ mutating: false,
1699
+ timeoutMs: 2e3,
1700
+ inputSchema: {
1701
+ type: "object",
1702
+ properties: {
1703
+ action: {
1704
+ type: "string",
1705
+ enum: ["show", "add", "start", "done", "remove", "clear"]
1706
+ },
1707
+ title: { type: "string", description: "Required when action = add." },
1708
+ details: { type: "string", description: "Optional extra context for add." },
1709
+ target: {
1710
+ type: "string",
1711
+ description: "Plan item id, 1-based index, or title substring. Required for start/done/remove."
1712
+ }
1713
+ },
1714
+ required: ["action"]
1715
+ },
1716
+ async execute(input, ctx) {
1717
+ const planPath = ctx.meta["plan.path"];
1718
+ if (typeof planPath !== "string" || !planPath) {
1719
+ return {
1720
+ ok: false,
1721
+ message: "Plan storage path is not configured for this session.",
1722
+ plan: "",
1723
+ count: 0,
1724
+ open: 0
1725
+ };
1726
+ }
1727
+ const sessionId = ctx.session?.id ?? "unknown";
1728
+ let plan = await loadPlan(planPath) ?? emptyPlan(sessionId);
1729
+ switch (input.action) {
1730
+ case "show":
1731
+ break;
1732
+ case "add": {
1733
+ const title = input.title?.trim();
1734
+ if (!title) {
1735
+ return mkResult(plan, false, "add requires `title`.");
1736
+ }
1737
+ ({ plan } = addPlanItem(plan, title, input.details?.trim() || void 0));
1738
+ await savePlan(planPath, plan);
1739
+ break;
1740
+ }
1741
+ case "start":
1742
+ case "done": {
1743
+ if (!input.target) {
1744
+ return mkResult(plan, false, `${input.action} requires \`target\` (id|index|substring).`);
1745
+ }
1746
+ const next = setPlanItemStatus(
1747
+ plan,
1748
+ input.target,
1749
+ input.action === "start" ? "in_progress" : "done"
1750
+ );
1751
+ if (next === plan) {
1752
+ return mkResult(plan, false, `No plan item matched "${input.target}".`);
1753
+ }
1754
+ plan = next;
1755
+ await savePlan(planPath, plan);
1756
+ break;
1757
+ }
1758
+ case "remove": {
1759
+ if (!input.target) {
1760
+ return mkResult(plan, false, "remove requires `target` (id|index|substring).");
1761
+ }
1762
+ const next = removePlanItem(plan, input.target);
1763
+ if (next === plan) {
1764
+ return mkResult(plan, false, `No plan item matched "${input.target}".`);
1765
+ }
1766
+ plan = next;
1767
+ await savePlan(planPath, plan);
1768
+ break;
1769
+ }
1770
+ case "clear":
1771
+ plan = clearPlan(plan);
1772
+ await savePlan(planPath, plan);
1773
+ break;
1774
+ default:
1775
+ return mkResult(plan, false, `Unknown action "${input.action}".`);
1776
+ }
1777
+ return mkResult(plan, true, `Plan ${input.action} ok.`);
1778
+ }
1779
+ };
1780
+ function mkResult(plan, ok, message) {
1781
+ const open = plan.items.filter((i) => i.status !== "done").length;
1782
+ return {
1783
+ ok,
1784
+ message,
1785
+ plan: formatPlan(plan),
1786
+ count: plan.items.length,
1787
+ open
1788
+ };
1789
+ }
1727
1790
  var TIMEOUT_MS4 = 3e4;
1728
1791
  var MAX_OUTPUT3 = 1e5;
1729
1792
  var gitTool = {
@@ -1820,11 +1883,7 @@ function buildArgs(input) {
1820
1883
  ...input.format === "short" || !input.format ? [] : []
1821
1884
  ];
1822
1885
  case "diff":
1823
- return [
1824
- "diff",
1825
- "--no-color",
1826
- ...files.length ? ["--", ...files] : []
1827
- ];
1886
+ return ["diff", "--no-color", ...files.length ? ["--", ...files] : []];
1828
1887
  case "commit":
1829
1888
  return [
1830
1889
  "commit",
@@ -1835,7 +1894,11 @@ function buildArgs(input) {
1835
1894
  case "branch":
1836
1895
  return input.branch ? ["branch", input.branch] : ["branch"];
1837
1896
  case "checkout":
1838
- return ["checkout", ...input.branch ? [input.branch] : [], ...files.length ? ["--", ...files] : []];
1897
+ return [
1898
+ "checkout",
1899
+ ...input.branch ? [input.branch] : [],
1900
+ ...files.length ? ["--", ...files] : []
1901
+ ];
1839
1902
  case "stash":
1840
1903
  return input.message ? ["stash", "push", "-m", input.message] : ["stash", "push"];
1841
1904
  case "push":
@@ -1933,13 +1996,7 @@ var patchTool = {
1933
1996
  });
1934
1997
  const patchFile = path.join(tmpDir, "in.diff");
1935
1998
  await fs4.writeFile(patchFile, input.patch, { mode: 384 });
1936
- const args = [
1937
- `-p${strip}`,
1938
- "--merge",
1939
- ...dryRun ? ["--dry-run"] : [],
1940
- "-i",
1941
- patchFile
1942
- ];
1999
+ const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
1943
2000
  const result = await runPatch(args, dir, opts.signal);
1944
2001
  if (result.exitCode !== 0 && !dryRun) {
1945
2002
  return {
@@ -1983,7 +2040,7 @@ function runPatch(args, cwd, signal) {
1983
2040
  return new Promise((resolve4) => {
1984
2041
  let stdout = "";
1985
2042
  let stderr = "";
1986
- const env = { ...process.env, LANG: "C", LC_ALL: "C" };
2043
+ const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
1987
2044
  const child = spawn("patch", args, { cwd, signal, env, stdio: ["pipe", "pipe", "pipe"] });
1988
2045
  child.stdout?.on("data", (c) => {
1989
2046
  stdout += c.toString();
@@ -2015,7 +2072,10 @@ var jsonTool = {
2015
2072
  properties: {
2016
2073
  file: { type: "string", description: "Path to JSON/JSON5/YAML file" },
2017
2074
  data: { type: "string", description: "JSON/JSON5/YAML string (alternative to file)" },
2018
- query: { type: "string", description: 'JMESPath-like query (e.g. "a.b[0].c" or "a[*].name")' },
2075
+ query: {
2076
+ type: "string",
2077
+ description: 'JMESPath-like query (e.g. "a.b[0].c" or "a[*].name")'
2078
+ },
2019
2079
  format: {
2020
2080
  type: "string",
2021
2081
  enum: ["json", "json5", "yaml"],
@@ -2208,7 +2268,12 @@ async function fileDiff(input, ctx, signal) {
2208
2268
  input.context ?? 3;
2209
2269
  const files = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim()).filter(Boolean) : [];
2210
2270
  if (files.length === 0) {
2211
- return { diff: "No files specified", files: [], truncated: false, mode: input.mode ?? "unified" };
2271
+ return {
2272
+ diff: "No files specified",
2273
+ files: [],
2274
+ truncated: false,
2275
+ mode: input.mode ?? "unified"
2276
+ };
2212
2277
  }
2213
2278
  const results = [];
2214
2279
  for (const file of files) {
@@ -2231,7 +2296,15 @@ ${formatUnified(lines)}`);
2231
2296
  function formatUnified(lines, context) {
2232
2297
  return lines.map((line, i) => ` ${line}`).join("\n");
2233
2298
  }
2234
- var DEFAULT_IGNORE4 = ["node_modules", ".git", "dist", "build", ".next", "coverage", "__pycache__"];
2299
+ var DEFAULT_IGNORE4 = [
2300
+ "node_modules",
2301
+ ".git",
2302
+ "dist",
2303
+ "build",
2304
+ ".next",
2305
+ "coverage",
2306
+ "__pycache__"
2307
+ ];
2235
2308
  var treeTool = {
2236
2309
  name: "tree",
2237
2310
  description: "Display directory structure as an ASCII tree. Shows files and folders with indentation.",
@@ -2396,6 +2469,7 @@ async function* spawnStream(opts) {
2396
2469
  const child = spawn(opts.cmd, opts.args, {
2397
2470
  cwd: opts.cwd,
2398
2471
  signal: opts.signal,
2472
+ env: buildChildEnv(),
2399
2473
  stdio: ["ignore", "pipe", "pipe"]
2400
2474
  });
2401
2475
  const queue = [];
@@ -2607,7 +2681,11 @@ var formatTool = {
2607
2681
  };
2608
2682
  return;
2609
2683
  }
2610
- yield { type: "log", text: `Running ${detected}\u2026`, data: { fixer: detected, check: !!input.check } };
2684
+ yield {
2685
+ type: "log",
2686
+ text: `Running ${detected}\u2026`,
2687
+ data: { fixer: detected, check: !!input.check }
2688
+ };
2611
2689
  const args = ["format", "--write"];
2612
2690
  if (input.check) args[args.length - 1] = "--check";
2613
2691
  if (input.files) {
@@ -2894,7 +2972,10 @@ var installTool = {
2894
2972
  description: "Save as regular, dev, or optional dependency"
2895
2973
  },
2896
2974
  cwd: { type: "string", description: "Working directory (default: cwd)" },
2897
- dry_run: { type: "boolean", description: "Preview install without modifying (default: false)" },
2975
+ dry_run: {
2976
+ type: "boolean",
2977
+ description: "Preview install without modifying (default: false)"
2978
+ },
2898
2979
  global: { type: "boolean", description: "Install globally (default: false)" }
2899
2980
  }
2900
2981
  },
@@ -2922,9 +3003,15 @@ var installTool = {
2922
3003
  } else {
2923
3004
  args.push("install", ...globalFlag);
2924
3005
  }
2925
- const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map((p) => p.trim()) : [];
3006
+ const pkgList = input.packages ? (Array.isArray(input.packages) ? input.packages : input.packages.split(",")).map(
3007
+ (p) => p.trim()
3008
+ ) : [];
2926
3009
  if (pkgList.length > 0) args.push(...pkgList);
2927
- yield { type: "log", text: `Fetching ${pkgList.length || "all"} packages\u2026`, data: { phase: "fetch" } };
3010
+ yield {
3011
+ type: "log",
3012
+ text: `Fetching ${pkgList.length || "all"} packages\u2026`,
3013
+ data: { phase: "fetch" }
3014
+ };
2928
3015
  const result = yield* spawnStream({
2929
3016
  cmd: pkgManager,
2930
3017
  args,
@@ -3132,13 +3219,16 @@ function runOutdated(manager, args, cwd, signal) {
3132
3219
  const result = parseOutdatedOutput(stdout, code ?? 0);
3133
3220
  resolve4(result);
3134
3221
  });
3135
- child.on("error", (e) => resolve4({
3136
- exit_code: 1,
3137
- packages: [],
3138
- total: 0,
3139
- output: e.message,
3140
- truncated: false
3141
- }));
3222
+ child.on(
3223
+ "error",
3224
+ (e) => resolve4({
3225
+ exit_code: 1,
3226
+ packages: [],
3227
+ total: 0,
3228
+ output: e.message,
3229
+ truncated: false
3230
+ })
3231
+ );
3142
3232
  });
3143
3233
  }
3144
3234
  function parseOutdatedOutput(json, exitCode) {
@@ -3267,13 +3357,16 @@ async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
3267
3357
  stream_mode: false
3268
3358
  });
3269
3359
  });
3270
- child.on("error", (e) => resolve4({
3271
- source: `docker:${service}`,
3272
- entries: [],
3273
- total: 0,
3274
- truncated: false,
3275
- stream_mode: false
3276
- }));
3360
+ child.on(
3361
+ "error",
3362
+ (e) => resolve4({
3363
+ source: `docker:${service}`,
3364
+ entries: [],
3365
+ total: 0,
3366
+ truncated: false,
3367
+ stream_mode: false
3368
+ })
3369
+ );
3277
3370
  });
3278
3371
  }
3279
3372
  var MAX_TAIL_LINES = 1e5;
@@ -3395,7 +3488,13 @@ var documentTool = {
3395
3488
  try {
3396
3489
  const content = await fs4.readFile(absPath, "utf8");
3397
3490
  filesProcessed++;
3398
- const processed = processFile(content, absPath, style, input.overwrite ?? false, input.target ?? "all");
3491
+ const processed = processFile(
3492
+ content,
3493
+ absPath,
3494
+ style,
3495
+ input.overwrite ?? false,
3496
+ input.target ?? "all"
3497
+ );
3399
3498
  results.push(...processed);
3400
3499
  itemsDocumented += processed.filter((r) => r.status === "documented").length;
3401
3500
  } catch (e) {
@@ -3441,23 +3540,43 @@ function processFile(content, absPath, style, overwrite, target) {
3441
3540
  if (target === "all" || target === "function") {
3442
3541
  for (const m of content.matchAll(functionRegex)) {
3443
3542
  if (!m[1]) continue;
3444
- allMatches.push({ name: m[1], sig: m[2] ?? "", type: "function", line: content.slice(0, m.index).split("\n").length });
3543
+ allMatches.push({
3544
+ name: m[1],
3545
+ sig: m[2] ?? "",
3546
+ type: "function",
3547
+ line: content.slice(0, m.index).split("\n").length
3548
+ });
3445
3549
  }
3446
3550
  for (const m of content.matchAll(arrowRegex)) {
3447
3551
  if (!m[1]) continue;
3448
- allMatches.push({ name: m[1], sig: m[2] ?? "", type: "arrow", line: content.slice(0, m.index).split("\n").length });
3552
+ allMatches.push({
3553
+ name: m[1],
3554
+ sig: m[2] ?? "",
3555
+ type: "arrow",
3556
+ line: content.slice(0, m.index).split("\n").length
3557
+ });
3449
3558
  }
3450
3559
  }
3451
3560
  if (target === "all" || target === "class") {
3452
3561
  for (const m of content.matchAll(classRegex)) {
3453
3562
  if (!m[1]) continue;
3454
- allMatches.push({ name: m[1], sig: "", type: "class", line: content.slice(0, m.index).split("\n").length });
3563
+ allMatches.push({
3564
+ name: m[1],
3565
+ sig: "",
3566
+ type: "class",
3567
+ line: content.slice(0, m.index).split("\n").length
3568
+ });
3455
3569
  }
3456
3570
  }
3457
3571
  if (target === "all" || target === "type") {
3458
3572
  for (const m of content.matchAll(typeRegex)) {
3459
3573
  if (!m[1]) continue;
3460
- allMatches.push({ name: m[1], sig: m[0] ?? "", type: "type", line: content.slice(0, m.index).split("\n").length });
3574
+ allMatches.push({
3575
+ name: m[1],
3576
+ sig: m[0] ?? "",
3577
+ type: "type",
3578
+ line: content.slice(0, m.index).split("\n").length
3579
+ });
3461
3580
  }
3462
3581
  }
3463
3582
  for (const m of allMatches) {
@@ -3475,18 +3594,26 @@ var BUILT_IN_TEMPLATES = {
3475
3594
  "npm-package": {
3476
3595
  description: "Basic npm package with ESM",
3477
3596
  files: {
3478
- "package.json": JSON.stringify({
3479
- name: "{{name}}",
3480
- version: "0.1.1",
3481
- type: "module",
3482
- main: "./dist/index.js",
3483
- scripts: { build: "tsc", test: "vitest run" },
3484
- devDependencies: { typescript: "^5.0.0" }
3485
- }, null, 2),
3486
- "tsconfig.json": JSON.stringify({
3487
- compilerOptions: { target: "ES2022", module: "ESNext", strict: true },
3488
- include: ["src"]
3489
- }, null, 2),
3597
+ "package.json": JSON.stringify(
3598
+ {
3599
+ name: "{{name}}",
3600
+ version: "0.1.1",
3601
+ type: "module",
3602
+ main: "./dist/index.js",
3603
+ scripts: { build: "tsc", test: "vitest run" },
3604
+ devDependencies: { typescript: "^5.0.0" }
3605
+ },
3606
+ null,
3607
+ 2
3608
+ ),
3609
+ "tsconfig.json": JSON.stringify(
3610
+ {
3611
+ compilerOptions: { target: "ES2022", module: "ESNext", strict: true },
3612
+ include: ["src"]
3613
+ },
3614
+ null,
3615
+ 2
3616
+ ),
3490
3617
  "src/index.ts": `export function hello() {
3491
3618
  return 'Hello from {{name}}';
3492
3619
  }
@@ -3505,13 +3632,17 @@ describe('hello', () => {
3505
3632
  "cli-tool": {
3506
3633
  description: "CLI tool with argparse",
3507
3634
  files: {
3508
- "package.json": JSON.stringify({
3509
- name: "{{name}}",
3510
- version: "0.1.1",
3511
- type: "module",
3512
- bin: { "{{name}}": "./src/index.js" },
3513
- scripts: { build: "tsc", start: "node dist/index.js" }
3514
- }, null, 2),
3635
+ "package.json": JSON.stringify(
3636
+ {
3637
+ name: "{{name}}",
3638
+ version: "0.1.1",
3639
+ type: "module",
3640
+ bin: { "{{name}}": "./src/index.js" },
3641
+ scripts: { build: "tsc", start: "node dist/index.js" }
3642
+ },
3643
+ null,
3644
+ 2
3645
+ ),
3515
3646
  "src/index.ts": `#!/usr/bin/env node
3516
3647
 
3517
3648
  async function main() {
@@ -3624,7 +3755,10 @@ async function handleBuiltIn(name, templateFiles, cwd, dryRun, vars) {
3624
3755
  function substituteVars(content, name, vars) {
3625
3756
  let result = content;
3626
3757
  result = result.replace(/\{\{name\}\}/g, name.toLowerCase().replace(/\s+/g, "-"));
3627
- result = result.replace(/\{\{Name\}\}/g, name.replace(/(?:^|[-_\s]+)([a-z])/g, (_, c) => c.toUpperCase()));
3758
+ result = result.replace(
3759
+ /\{\{Name\}\}/g,
3760
+ name.replace(/(?:^|[-_\s]+)([a-z])/g, (_, c) => c.toUpperCase())
3761
+ );
3628
3762
  for (const [k, v] of Object.entries(vars)) {
3629
3763
  result = result.replace(new RegExp(`\\{\\{${k}\\}\\}`, "g"), v);
3630
3764
  }
@@ -3704,7 +3838,7 @@ var toolUseTool = {
3704
3838
  description: "Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.",
3705
3839
  usageHint: "Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.",
3706
3840
  permission: "confirm",
3707
- mutating: false,
3841
+ mutating: true,
3708
3842
  timeoutMs: 6e4,
3709
3843
  inputSchema: {
3710
3844
  type: "object",
@@ -3772,7 +3906,7 @@ var batchToolUseTool = {
3772
3906
  description: "Execute multiple tool calls in sequence or parallel. Returns all results.",
3773
3907
  usageHint: "Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).",
3774
3908
  permission: "confirm",
3775
- mutating: false,
3909
+ mutating: true,
3776
3910
  timeoutMs: 12e4,
3777
3911
  inputSchema: {
3778
3912
  type: "object",
@@ -3911,14 +4045,16 @@ var toolHelpTool = {
3911
4045
  return {
3912
4046
  tool: tool.name,
3913
4047
  help: formatToolHelp(tool, format, includeExamples),
3914
- tools: [{
3915
- name: tool.name,
3916
- description: tool.description,
3917
- usageHint: tool.usageHint ?? "",
3918
- inputSchema: tool.inputSchema,
3919
- permission: tool.permission,
3920
- mutating: tool.mutating
3921
- }],
4048
+ tools: [
4049
+ {
4050
+ name: tool.name,
4051
+ description: tool.description,
4052
+ usageHint: tool.usageHint ?? "",
4053
+ inputSchema: tool.inputSchema,
4054
+ permission: tool.permission,
4055
+ mutating: tool.mutating
4056
+ }
4057
+ ],
3922
4058
  total: 1
3923
4059
  };
3924
4060
  }
@@ -4110,7 +4246,11 @@ ${mode.description}`
4110
4246
  };
4111
4247
  }
4112
4248
  default:
4113
- return { action: input.action, success: false, message: `Unknown action "${input.action}"` };
4249
+ return {
4250
+ action: input.action,
4251
+ success: false,
4252
+ message: `Unknown action "${input.action}"`
4253
+ };
4114
4254
  }
4115
4255
  }
4116
4256
  };
@@ -4129,6 +4269,7 @@ var builtinTools = [
4129
4269
  fetchTool,
4130
4270
  searchTool,
4131
4271
  todoTool,
4272
+ planTool,
4132
4273
  gitTool,
4133
4274
  patchTool,
4134
4275
  jsonTool,
@@ -4150,6 +4291,6 @@ var builtinTools = [
4150
4291
  toolHelpTool
4151
4292
  ];
4152
4293
 
4153
- export { auditTool, bashTool, batchToolUseTool, builtinTools, createModeTool, diffTool, documentTool, editTool, execTool, fetchTool, forgetTool, formatTool, gitTool, globTool, grepTool, installTool, jsonTool, lintTool, logsTool, outdatedTool, patchTool, readTool, rememberTool, replaceTool, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
4294
+ export { auditTool, bashTool, batchToolUseTool, builtinTools, createModeTool, diffTool, documentTool, editTool, execTool, fetchTool, forgetTool, formatTool, gitTool, globTool, grepTool, installTool, jsonTool, lintTool, logsTool, outdatedTool, patchTool, planTool, readTool, rememberTool, replaceTool, scaffoldTool, searchTool, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
4154
4295
  //# sourceMappingURL=index.js.map
4155
4296
  //# sourceMappingURL=index.js.map