@wrongstack/tools 0.305.1 → 0.306.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/_shell-pick.d.ts +4 -5
  2. package/dist/_util.d.ts +22 -5
  3. package/dist/audit.d.ts +0 -1
  4. package/dist/audit.js +135 -46
  5. package/dist/bash.js +61 -37
  6. package/dist/browser/index.js +29 -9
  7. package/dist/browser/types.d.ts +7 -1
  8. package/dist/builtin.js +1294 -726
  9. package/dist/codebase-index/codebase-search-tool.d.ts +5 -0
  10. package/dist/codebase-index/index.js +223 -152
  11. package/dist/codebase-index/project-server.js +10 -11
  12. package/dist/diff.d.ts +5 -0
  13. package/dist/diff.js +78 -12
  14. package/dist/document.js +18 -6
  15. package/dist/edit.js +69 -16
  16. package/dist/exec.js +44 -22
  17. package/dist/fetch.js +13 -1
  18. package/dist/format.d.ts +4 -2
  19. package/dist/format.js +81 -31
  20. package/dist/glob.js +12 -4
  21. package/dist/grep.d.ts +2 -0
  22. package/dist/grep.js +15 -4
  23. package/dist/index.js +1359 -762
  24. package/dist/install.js +96 -37
  25. package/dist/kanban-tool-types.d.ts +6 -1
  26. package/dist/kanban.js +60 -0
  27. package/dist/languages/index.js +28 -13
  28. package/dist/lint.js +28 -13
  29. package/dist/logs.d.ts +0 -1
  30. package/dist/logs.js +44 -13
  31. package/dist/memory.d.ts +8 -0
  32. package/dist/memory.js +23 -3
  33. package/dist/mode.d.ts +1 -1
  34. package/dist/mode.js +3 -0
  35. package/dist/next-steps.d.ts +2 -3
  36. package/dist/next-steps.js +3 -3
  37. package/dist/outdated.d.ts +0 -3
  38. package/dist/outdated.js +89 -48
  39. package/dist/pack.js +1294 -726
  40. package/dist/plan.js +91 -3
  41. package/dist/process-registry.d.ts +8 -2
  42. package/dist/process-registry.js +28 -13
  43. package/dist/ps-slash.js +22 -12
  44. package/dist/read.js +10 -3
  45. package/dist/replace.d.ts +4 -0
  46. package/dist/replace.js +104 -7
  47. package/dist/search.d.ts +6 -0
  48. package/dist/search.js +47 -26
  49. package/dist/session-kanban.js +3 -1
  50. package/dist/skill.d.ts +6 -0
  51. package/dist/skill.js +9 -10
  52. package/dist/task.js +81 -2
  53. package/dist/test.js +28 -13
  54. package/dist/todo.js +79 -2
  55. package/dist/tool-icons.js +4 -2
  56. package/dist/tool-summary.d.ts +1 -1
  57. package/dist/tool-summary.js +76 -1
  58. package/dist/tool-tier.js +1294 -726
  59. package/dist/tree.js +9 -10
  60. package/dist/typecheck.d.ts +0 -2
  61. package/dist/typecheck.js +98 -31
  62. package/dist/write.js +58 -10
  63. package/package.json +4 -4
package/dist/format.js CHANGED
@@ -364,8 +364,11 @@ var SENSITIVE_FLAG_PATTERNS = [
364
364
  /--(?: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,
365
365
  // -t short flag (token): attached (-tVALUE), separated (-t VALUE), or -t=VALUE.
366
366
  // (?<![-\w]) anchors to a token start so we don't match the `-t` inside `--token`.
367
+ // The value must be token-like (>= 8 chars) so ordinary combined flags such
368
+ // as `tar -tf` / `ssh -tt` are not eaten. Global flag: EVERY occurrence is
369
+ // redacted, not just the first.
367
370
  // NOTE: synced with @wrongstack/core observability/redact-command.ts.
368
- /(?<![-\w])-t(?:[=\s]+)?[^\s,-]+/,
371
+ /(?<![-\w])-t(?:[=\s]+)?[^\s,-]{8,}/g,
369
372
  // -p|-password|-a (redis auth) short flags: attached + separated + =value.
370
373
  // Same token-start anchor; over-redaction is an accepted tradeoff for a
371
374
  // redaction function. Synced with core copy.
@@ -373,8 +376,9 @@ var SENSITIVE_FLAG_PATTERNS = [
373
376
  // env var–style secrets: TOKEN=x, API_KEY=y, etc.
374
377
  /(?:TOKEN|API_KEY|API_SECRET|AUTH_TOKEN|GITHUB_TOKEN|GH_TOKEN|BEARER|JWT|OAUTH|CREDENTIAL|SECRET|PRIVATE_KEY|PASSWORD|PASSWD)\s*[=:]\s*[^\s,]+/gi,
375
378
  // Generic high-entropy look: base64 strings >32 chars or hex strings >32 digits — but only
376
- // when preceded by a flag name (e.g. --github-token=EyJ...).
377
- /--\w*(?:token|key|secret|password|passwd|auth|credential)\w*[=\s,][A-Za-z0-9+/=]{32,}/
379
+ // when preceded by a flag name (e.g. --github-token=EyJ...). Global flag so
380
+ // every such flag in the command line is redacted, not just the first.
381
+ /--\w*(?:token|key|secret|password|passwd|auth|credential)\w*[=\s,][A-Za-z0-9+/=]{32,}/g
378
382
  ];
379
383
  function redactCommand(cmd) {
380
384
  let result = cmd;
@@ -463,11 +467,15 @@ var ProcessRegistryImpl = class {
463
467
  return Number.isInteger(pid) && pid > 1 && pid !== process.pid && pid !== process.ppid;
464
468
  }
465
469
  _canSignalProcessGroup(p) {
466
- return os.platform() !== "win32" && p.processGroupLeader === true && this._isSafeSignalPid(p.pid) && typeof p.child.pid === "number" && p.child.pid === p.pid;
470
+ return os.platform() !== "win32" && p.processGroupLeader === true && this._isSafeSignalPid(p.pid) && p.child !== null && typeof p.child.pid === "number" && p.child.pid === p.pid;
467
471
  }
468
472
  _killChildDirect(p, signal) {
469
473
  try {
470
- p.child.kill(signal);
474
+ if (p.child) {
475
+ p.child.kill(signal);
476
+ return;
477
+ }
478
+ if (this._isSafeSignalPid(p.pid)) process.kill(p.pid, signal);
471
479
  } catch {
472
480
  }
473
481
  }
@@ -665,15 +673,15 @@ var ProcessRegistryImpl = class {
665
673
  this._pruneStale(pid);
666
674
  const p = this.processes.get(pid);
667
675
  if (!p) return false;
668
- if (p.killed) return true;
676
+ if (p.killed && opts.force !== true) return true;
669
677
  if (p.protected && opts.includeProtected !== true) return false;
670
678
  if (opts.preserveBackground && p.background) return false;
671
679
  const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
672
680
  const isWin2 = os.platform() === "win32";
673
681
  if (isWin2) {
674
- const liveRealChild = p.child.exitCode === null && typeof p.child.pid === "number";
682
+ const liveRealChild = p.child === null || p.child.exitCode === null && typeof p.child.pid === "number";
675
683
  const directFallback = () => {
676
- if (p.child.exitCode === null) {
684
+ if (p.child && p.child.exitCode === null) {
677
685
  try {
678
686
  p.child.kill("SIGKILL");
679
687
  } catch {
@@ -685,10 +693,7 @@ var ProcessRegistryImpl = class {
685
693
  onSettled: directFallback
686
694
  })) {
687
695
  } else {
688
- try {
689
- p.child.kill(force ? "SIGKILL" : "SIGTERM");
690
- } catch {
691
- }
696
+ this._killChildDirect(p, force ? "SIGKILL" : "SIGTERM");
692
697
  }
693
698
  p.killed = true;
694
699
  return true;
@@ -699,7 +704,7 @@ var ProcessRegistryImpl = class {
699
704
  } else {
700
705
  this._killPosix(p, "SIGTERM");
701
706
  const timer = setTimeout(() => {
702
- if (this.processes.has(pid) && !p.child.killed) {
707
+ if (this.processes.has(pid) && !p.child?.killed) {
703
708
  this._killPosix(p, "SIGKILL");
704
709
  }
705
710
  }, graceMs);
@@ -754,6 +759,16 @@ var ProcessRegistryImpl = class {
754
759
  * before reusing a PID, but we want to clean up before that becomes a risk.
755
760
  */
756
761
  _isStaleEntry(entry) {
762
+ if (entry.child === null) {
763
+ if (Date.now() - entry.startedAt <= 6e4) return false;
764
+ if (os.platform() === "win32") return false;
765
+ try {
766
+ process.kill(entry.pid, 0);
767
+ return false;
768
+ } catch (err) {
769
+ return err.code !== "EPERM";
770
+ }
771
+ }
757
772
  return entry.child.exitCode !== null && Date.now() - entry.startedAt > 6e4;
758
773
  }
759
774
  /**
@@ -3590,8 +3605,9 @@ var formatTool = {
3590
3605
  type: "final",
3591
3606
  output: {
3592
3607
  fixer: bridge.language,
3593
- files_checked: 0,
3594
- files_changed: run.summary.errors > 0 ? 0 : 1,
3608
+ // Language-bridge runs don't report per-file counts.
3609
+ files_checked: void 0,
3610
+ files_changed: void 0,
3595
3611
  output: normalizeCommandOutput(run.output || run.error || ""),
3596
3612
  truncated: run.truncated
3597
3613
  }
@@ -3618,11 +3634,14 @@ var formatTool = {
3618
3634
  text: `Running ${detected}\u2026`,
3619
3635
  data: { fixer: detected, check: !!input.check }
3620
3636
  };
3621
- const args = ["format", "--write"];
3622
- if (input.check) args[args.length - 1] = "--check";
3623
- if (input.files) {
3624
- const files = Array.isArray(input.files) ? input.files : input.files.split(",");
3625
- args.push("--", ...files.map((f) => f.trim()));
3637
+ const fileList = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((f) => f.trim()) : [];
3638
+ let args;
3639
+ if (detected === "prettier") {
3640
+ args = [input.check ? "--check" : "--write"];
3641
+ args.push(...fileList.length > 0 ? fileList : ["."]);
3642
+ } else {
3643
+ args = ["format", input.check ? "--check" : "--write"];
3644
+ if (fileList.length > 0) args.push("--", ...fileList);
3626
3645
  }
3627
3646
  const result = yield* spawnStream({
3628
3647
  cmd: detected,
@@ -3631,32 +3650,63 @@ var formatTool = {
3631
3650
  signal: opts.signal,
3632
3651
  maxBytes: 1e5
3633
3652
  });
3634
- const changed = [...result.stdout.matchAll(/\bchanged\b/gi)].length;
3653
+ const combinedOut = `${result.stdout}
3654
+ ${result.stderr}`;
3655
+ const counts = parseFormatterCounts(detected, combinedOut);
3635
3656
  yield {
3636
3657
  type: "final",
3637
3658
  output: {
3638
3659
  fixer: detected,
3639
- files_checked: 0,
3640
- files_changed: changed,
3660
+ files_checked: counts.checked,
3661
+ files_changed: counts.changed,
3641
3662
  output: normalizeCommandOutput(result.stdout || result.stderr || result.error || ""),
3642
3663
  truncated: result.truncated
3643
3664
  }
3644
3665
  };
3645
3666
  }
3646
3667
  };
3668
+ function parseFormatterCounts(fixer, output) {
3669
+ if (fixer !== "biome") return { checked: void 0, changed: void 0 };
3670
+ const checkedMatch = /\b(?:Checked|Formatted)\s+(\d+)\s+files?\b/i.exec(output);
3671
+ const changedMatch = /\bFixed\s+(\d+)\s+files?\b/i.exec(output);
3672
+ return {
3673
+ checked: checkedMatch?.[1] !== void 0 ? Number(checkedMatch[1]) : void 0,
3674
+ changed: changedMatch?.[1] !== void 0 ? Number(changedMatch[1]) : void 0
3675
+ };
3676
+ }
3647
3677
  async function detectFixer(cwd) {
3648
- const { stat: stat5 } = await import("node:fs/promises");
3649
- try {
3650
- await stat5(`${cwd}/biome.json`);
3651
- return "biome";
3652
- } catch {
3678
+ const fs6 = await import("node:fs/promises");
3679
+ const exists = async (file) => {
3653
3680
  try {
3654
- await stat5(`${cwd}/.prettierrc`);
3655
- return "prettier";
3681
+ await fs6.stat(`${cwd}/${file}`);
3682
+ return true;
3656
3683
  } catch {
3657
- return "biome";
3684
+ return false;
3658
3685
  }
3686
+ };
3687
+ if (await exists("biome.json") || await exists("biome.jsonc")) return "biome";
3688
+ const PRETTIER_CONFIGS = [
3689
+ ".prettierrc",
3690
+ ".prettierrc.json",
3691
+ ".prettierrc.yml",
3692
+ ".prettierrc.yaml",
3693
+ ".prettierrc.js",
3694
+ ".prettierrc.cjs",
3695
+ ".prettierrc.mjs",
3696
+ "prettier.config.js",
3697
+ "prettier.config.cjs",
3698
+ "prettier.config.mjs"
3699
+ ];
3700
+ for (const cfg of PRETTIER_CONFIGS) {
3701
+ if (await exists(cfg)) return "prettier";
3702
+ }
3703
+ try {
3704
+ const raw = await fs6.readFile(`${cwd}/package.json`, "utf8");
3705
+ const pkg = JSON.parse(raw);
3706
+ if (pkg["prettier"] !== void 0) return "prettier";
3707
+ } catch {
3659
3708
  }
3709
+ return "biome";
3660
3710
  }
3661
3711
  export {
3662
3712
  formatTool
package/dist/glob.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import * as fs2 from "node:fs/promises";
3
3
  import * as path3 from "node:path";
4
4
  import { compileGlob as compileGlob2, DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
5
+ import { ToolValidationError } from "@wrongstack/core/types";
5
6
 
6
7
  // src/_concurrency.ts
7
8
  async function mapWithConcurrency(items, limit, fn) {
@@ -143,7 +144,7 @@ var WALK_CONCURRENCY = 16;
143
144
  var globTool = {
144
145
  name: "glob",
145
146
  category: "Filesystem",
146
- description: "Find files by path pattern. Use index-backed `codebase-search` first for code symbols or concepts when it is live.",
147
+ description: "Find files by path pattern. Results are sorted by modification time, newest first, so when the list is truncated at `limit` it is the oldest files that are dropped (recency-biased). Use index-backed `codebase-search` first for code symbols or concepts when it is live.",
147
148
  usageHint: "PATH DISCOVERY AND SEARCH SCOPING:\n\n- When `codebase-search` is live, use it first for code concepts; use `glob` for filenames, path patterns, and non-indexed files.\n- Combine with `path` and `limit`.\n- Default ignores common build/dependency directories.\nMuch more efficient than shell `find` for most use cases inside the agent.",
148
149
  selection: {
149
150
  doNotUseWhen: "you need to search inside file contents.",
@@ -154,7 +155,7 @@ var globTool = {
154
155
  capabilities: ["fs.read"],
155
156
  icon: "folder",
156
157
  maxOutputBytes: 65536,
157
- timeoutMs: 5e3,
158
+ timeoutMs: 15e3,
158
159
  inputSchema: {
159
160
  type: "object",
160
161
  properties: {
@@ -168,13 +169,20 @@ var globTool = {
168
169
  },
169
170
  limit: {
170
171
  type: "integer",
171
- description: "Maximum number of results to return (default 1000, max 5000)."
172
+ minimum: 1,
173
+ maximum: 5e3,
174
+ description: "Maximum number of results to return (default 1000, max 5000). Results are sorted by mtime descending, so truncation keeps the most recently modified files."
172
175
  }
173
176
  },
174
177
  required: ["pattern"]
175
178
  },
176
179
  async execute(input, ctx, opts) {
177
- if (!input?.pattern) throw new Error("glob: pattern is required");
180
+ if (!input?.pattern) {
181
+ throw new ToolValidationError({
182
+ message: "glob: pattern is required",
183
+ field: "pattern"
184
+ });
185
+ }
178
186
  const signal = opts?.signal;
179
187
  const base = input.path ? await safeResolveReal(input.path, ctx) : ctx.cwd;
180
188
  const limit = Math.max(1, Math.min(input.limit ?? 1e3, 5e3));
package/dist/grep.d.ts CHANGED
@@ -15,5 +15,7 @@ interface GrepOutput {
15
15
  used: 'rg' | 'native';
16
16
  }
17
17
  export declare const grepTool: Tool<GrepInput, GrepOutput>;
18
+ /** Test-only: forget the cached rg availability so mocks can vary per test. */
19
+ export declare function __resetRgDetectionForTests(): void;
18
20
  export {};
19
21
  //# sourceMappingURL=grep.d.ts.map
package/dist/grep.js CHANGED
@@ -358,7 +358,7 @@ var grepTool = {
358
358
  field: "pattern"
359
359
  });
360
360
  }
361
- const rgAvailable = await detectRg(opts.signal);
361
+ const rgAvailable = await detectRg();
362
362
  if (rgAvailable) {
363
363
  try {
364
364
  yield* runRgStream(input, base, mode, limit, opts.signal);
@@ -371,16 +371,26 @@ var grepTool = {
371
371
  yield { type: "final", output: out };
372
372
  }
373
373
  };
374
- async function detectRg(signal) {
375
- return new Promise((resolve2) => {
374
+ var rgAvailabilityCache;
375
+ function __resetRgDetectionForTests() {
376
+ rgAvailabilityCache = void 0;
377
+ }
378
+ function detectRg() {
379
+ rgAvailabilityCache ??= new Promise((resolve2) => {
376
380
  try {
377
- const p = spawn("rg", ["--version"], { env: buildChildEnv(), stdio: "ignore", signal, windowsHide: true });
381
+ const p = spawn("rg", ["--version"], {
382
+ env: buildChildEnv(),
383
+ stdio: "ignore",
384
+ signal: AbortSignal.timeout(1e4),
385
+ windowsHide: true
386
+ });
378
387
  p.on("error", () => resolve2(false));
379
388
  p.on("close", (code) => resolve2(code === 0));
380
389
  } catch {
381
390
  resolve2(false);
382
391
  }
383
392
  });
393
+ return rgAvailabilityCache;
384
394
  }
385
395
  async function* runRgStream(input, base, mode, limit, signal) {
386
396
  const args = ["--no-heading"];
@@ -701,6 +711,7 @@ async function runNative(input, base, mode, limit, signal) {
701
711
  };
702
712
  }
703
713
  export {
714
+ __resetRgDetectionForTests,
704
715
  grepTool
705
716
  };
706
717
  //# sourceMappingURL=grep.js.map