@wrongstack/tools 0.299.0 → 0.301.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 (53) hide show
  1. package/dist/audit.js +6 -2
  2. package/dist/bash.js +6 -2
  3. package/dist/batch-tool-use.js +3 -1
  4. package/dist/browser/index.js +1 -1
  5. package/dist/builtin.d.ts +3 -2
  6. package/dist/builtin.js +1781 -376
  7. package/dist/codebase-index/bm25.d.ts +7 -1
  8. package/dist/codebase-index/import-extractor.d.ts +39 -0
  9. package/dist/codebase-index/index.js +1418 -267
  10. package/dist/codebase-index/languages.d.ts +24 -0
  11. package/dist/codebase-index/module-resolver.d.ts +78 -0
  12. package/dist/codebase-index/module-roots.d.ts +81 -0
  13. package/dist/codebase-index/parser-output.d.ts +29 -0
  14. package/dist/codebase-index/project-server.js +1402 -249
  15. package/dist/codebase-index/rs-parser.d.ts +22 -0
  16. package/dist/codebase-index/schema.d.ts +24 -1
  17. package/dist/codebase-index/worker.js +1401 -250
  18. package/dist/codebase-index/writer-graph-helpers.d.ts +17 -5
  19. package/dist/codebase-index/writer-ref-mapper.d.ts +3 -0
  20. package/dist/codebase-index/writer-schema.d.ts +15 -3
  21. package/dist/codebase-index/writer.d.ts +76 -3
  22. package/dist/exec.js +35 -2
  23. package/dist/format.js +6 -2
  24. package/dist/git.js +2 -5
  25. package/dist/glob.js +2 -2
  26. package/dist/grep.js +118 -3
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1925 -415
  29. package/dist/install.js +6 -2
  30. package/dist/json.js +132 -2
  31. package/dist/languages/index.js +6 -2
  32. package/dist/lint.js +6 -2
  33. package/dist/logs.js +81 -0
  34. package/dist/next-steps-tool.d.ts +26 -0
  35. package/dist/outdated.js +6 -2
  36. package/dist/pack.js +1781 -376
  37. package/dist/patch.js +206 -45
  38. package/dist/process-registry.d.ts +6 -0
  39. package/dist/process-registry.js +6 -2
  40. package/dist/ps-slash.js +6 -2
  41. package/dist/read.js +1410 -257
  42. package/dist/replace.js +81 -0
  43. package/dist/skill.js +51 -2
  44. package/dist/test.js +6 -2
  45. package/dist/tool-help.js +2 -2
  46. package/dist/tool-search.js +1 -1
  47. package/dist/tool-tier.d.ts +1 -1
  48. package/dist/tool-tier.js +1786 -397
  49. package/dist/tool-use.js +1 -1
  50. package/dist/tree.js +13 -3
  51. package/dist/typecheck.js +6 -2
  52. package/package.json +3 -3
  53. package/dist/codebase-index/refs-extractor.d.ts +0 -11
package/dist/replace.js CHANGED
@@ -27,6 +27,81 @@ var DANGEROUS_PATTERNS = [
27
27
  // Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
28
28
  /[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
29
29
  ];
30
+ function hasAmbiguousQuantifiedAlternation(pattern) {
31
+ for (let i = 0; i < pattern.length; i++) {
32
+ if (pattern[i] !== "(") continue;
33
+ if (i > 0 && pattern[i - 1] === "\\") continue;
34
+ let depth = 0;
35
+ let inClass = false;
36
+ let j = i;
37
+ for (; j < pattern.length; j++) {
38
+ const ch = pattern[j];
39
+ if (ch === "\\") {
40
+ j++;
41
+ continue;
42
+ }
43
+ if (inClass) {
44
+ if (ch === "]") inClass = false;
45
+ continue;
46
+ }
47
+ if (ch === "[") {
48
+ inClass = true;
49
+ continue;
50
+ }
51
+ if (ch === "(") depth++;
52
+ else if (ch === ")") {
53
+ depth--;
54
+ if (depth === 0) break;
55
+ }
56
+ }
57
+ if (j >= pattern.length) return false;
58
+ const next = pattern[j + 1];
59
+ if (next !== "+" && next !== "*" && next !== "{") continue;
60
+ let inner = pattern.slice(i + 1, j);
61
+ inner = inner.replace(/^\?(?::|<?[=!])/u, "");
62
+ const branches = [];
63
+ let current = "";
64
+ let d = 0;
65
+ let cls = false;
66
+ for (let k = 0; k < inner.length; k++) {
67
+ const ch = inner[k];
68
+ if (ch === "\\") {
69
+ current += ch + (inner[k + 1] ?? "");
70
+ k++;
71
+ continue;
72
+ }
73
+ if (cls) {
74
+ if (ch === "]") cls = false;
75
+ current += ch;
76
+ continue;
77
+ }
78
+ if (ch === "[") {
79
+ cls = true;
80
+ current += ch;
81
+ continue;
82
+ }
83
+ if (ch === "(") d++;
84
+ if (ch === ")") d--;
85
+ if (ch === "|" && d === 0) {
86
+ branches.push(current);
87
+ current = "";
88
+ continue;
89
+ }
90
+ current += ch;
91
+ }
92
+ branches.push(current);
93
+ if (branches.length < 2) continue;
94
+ for (let a = 0; a < branches.length; a++) {
95
+ for (let b = a + 1; b < branches.length; b++) {
96
+ const x = branches[a];
97
+ const y = branches[b];
98
+ if (x === "" || y === "") return true;
99
+ if (x === y || x.startsWith(y) || y.startsWith(x)) return true;
100
+ }
101
+ }
102
+ }
103
+ return false;
104
+ }
30
105
  function compileUserRegex(pattern, flags) {
31
106
  if (typeof pattern !== "string") {
32
107
  return { ok: false, reason: "pattern must be a string" };
@@ -45,6 +120,12 @@ function compileUserRegex(pattern, flags) {
45
120
  };
46
121
  }
47
122
  }
123
+ if (hasAmbiguousQuantifiedAlternation(pattern)) {
124
+ return {
125
+ ok: false,
126
+ reason: "pattern quantifies an alternation with overlapping branches \u2014 rewrite so no two branches can match the same text"
127
+ };
128
+ }
48
129
  try {
49
130
  return { ok: true, regex: new RegExp(pattern, flags) };
50
131
  } catch (err) {
package/dist/skill.js CHANGED
@@ -1,6 +1,11 @@
1
1
  // src/skill.ts
2
2
  import * as fs from "node:fs/promises";
3
3
  import * as path from "node:path";
4
+ import {
5
+ missingRequiredRuntimeTools,
6
+ missingRuntimeCapabilities,
7
+ runtimeToolReferencesFromText
8
+ } from "@wrongstack/core/agent-catalog";
4
9
  import { SKILL_LIMITS, stripFrontmatter } from "@wrongstack/core/skills";
5
10
  import { ToolValidationError } from "@wrongstack/core/types";
6
11
  var MAX_BODY_CHARS = SKILL_LIMITS.MAX_SKILL_BODY_CHARS;
@@ -44,12 +49,37 @@ function makeSkillTool(skillLoader) {
44
49
  field: "name"
45
50
  });
46
51
  }
52
+ const availableToolNames = (ctx?.catalogTools ?? ctx?.tools ?? []).map((tool) => tool.name);
53
+ const missingCapabilities = missingRuntimeCapabilities(
54
+ manifest.requiredCapabilities,
55
+ availableToolNames
56
+ );
57
+ const missingTools = missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames);
58
+ if (missingCapabilities.length > 0 || missingTools.length > 0) {
59
+ throw new ToolValidationError({
60
+ message: `skill "${name}" is unavailable in this runtime; ` + [
61
+ missingCapabilities.length > 0 ? `missing capabilities: ${missingCapabilities.join(", ")}` : "",
62
+ missingTools.length > 0 ? `missing tools: ${missingTools.join(", ")}` : ""
63
+ ].filter(Boolean).join("; "),
64
+ field: "name"
65
+ });
66
+ }
47
67
  const dir = path.dirname(manifest.path);
48
68
  let loadedResource;
49
69
  if (input.resource?.trim()) {
50
70
  loadedResource = await loadResource(dir, input.resource.trim());
51
71
  }
52
72
  const raw = await skillLoader.readBody(name);
73
+ const missingBodyTools = missingRequiredRuntimeTools(
74
+ runtimeToolReferencesFromText(raw),
75
+ availableToolNames
76
+ );
77
+ if (missingBodyTools.length > 0) {
78
+ throw new ToolValidationError({
79
+ message: `skill "${name}" references unregistered tools: ${missingBodyTools.join(", ")}`,
80
+ field: "name"
81
+ });
82
+ }
53
83
  const body = stripFrontmatter(raw).trim().slice(0, MAX_BODY_CHARS);
54
84
  const resources = loadedResource ? [] : await listResources(dir);
55
85
  try {
@@ -108,9 +138,26 @@ async function loadResource(skillDir, rel) {
108
138
  field: "resource"
109
139
  });
110
140
  }
141
+ let realPath;
142
+ let realRoot;
143
+ try {
144
+ realRoot = await fs.realpath(root);
145
+ realPath = await fs.realpath(absPath);
146
+ } catch {
147
+ throw new ToolValidationError({
148
+ message: `skill: resource "${rel}" not readable`,
149
+ field: "resource"
150
+ });
151
+ }
152
+ if (realPath !== realRoot && !realPath.startsWith(realRoot + path.sep)) {
153
+ throw new ToolValidationError({
154
+ message: `skill: resource "${rel}" resolves outside the skill directory`,
155
+ field: "resource"
156
+ });
157
+ }
111
158
  let buf;
112
159
  try {
113
- buf = await fs.readFile(absPath);
160
+ buf = await fs.readFile(realPath);
114
161
  } catch {
115
162
  throw new ToolValidationError({
116
163
  message: `skill: resource "${rel}" not readable`,
@@ -121,7 +168,9 @@ async function loadResource(skillDir, rel) {
121
168
  const truncated = raw.length > MAX_RESOURCE_CHARS;
122
169
  return {
123
170
  rel: norm,
124
- absPath,
171
+ // The canonical path — the one actually opened, and the one a follow-up
172
+ // `bash` invocation should use.
173
+ absPath: realPath,
125
174
  content: truncated ? raw.slice(0, MAX_RESOURCE_CHARS) : raw,
126
175
  bytes: buf.length,
127
176
  truncated
package/dist/test.js CHANGED
@@ -669,7 +669,7 @@ var ProcessRegistryImpl = class {
669
669
  const p = this.processes.get(pid);
670
670
  if (!p) return false;
671
671
  if (p.killed) return true;
672
- if (p.protected) return false;
672
+ if (p.protected && opts.includeProtected !== true) return false;
673
673
  if (opts.preserveBackground && p.background) return false;
674
674
  const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
675
675
  const isWin2 = os.platform() === "win32";
@@ -720,9 +720,13 @@ var ProcessRegistryImpl = class {
720
720
  killAll(opts = {}) {
721
721
  const pids = Array.from(this.processes.keys());
722
722
  const killed = [];
723
+ const includeProtected = opts.includeProtected === true;
723
724
  for (const pid of pids) {
724
725
  const p = this.processes.get(pid);
725
- if (p && !p.protected && this.kill(pid, opts)) killed.push(pid);
726
+ if (!p) continue;
727
+ if (p.protected && !includeProtected) continue;
728
+ if (opts.preserveBackground && p.background) continue;
729
+ if (this.kill(pid, opts)) killed.push(pid);
726
730
  }
727
731
  return killed;
728
732
  }
package/dist/tool-help.js CHANGED
@@ -31,7 +31,7 @@ var toolHelpTool = {
31
31
  const format = input.format ?? "short";
32
32
  const includeExamples = input.include_examples ?? false;
33
33
  if (input.tool) {
34
- const tool = ctx.tools.find((t) => t.name === input.tool);
34
+ const tool = (ctx.catalogTools ?? ctx.tools).find((t) => t.name === input.tool);
35
35
  if (!tool) {
36
36
  return {
37
37
  tool: input.tool,
@@ -56,7 +56,7 @@ var toolHelpTool = {
56
56
  total: 1
57
57
  };
58
58
  }
59
- const allTools = ctx.tools.map((t) => ({
59
+ const allTools = (ctx.catalogTools ?? ctx.tools).map((t) => ({
60
60
  name: t.name,
61
61
  description: t.description,
62
62
  usageHint: t.usageHint ?? "",
@@ -40,7 +40,7 @@ var toolSearchTool = {
40
40
  },
41
41
  async execute(input, ctx) {
42
42
  const limit = Math.min(input.limit ?? 20, 100);
43
- const tools = ctx.tools;
43
+ const tools = ctx.catalogTools ?? ctx.tools;
44
44
  const query = input.query?.toLowerCase() ?? "";
45
45
  const filtered = tools.filter((t) => {
46
46
  if (query && !t.name.toLowerCase().includes(query) && !t.description.toLowerCase().includes(query)) {
@@ -1,5 +1,5 @@
1
- import type { ConcreteTokenSavingTier, Tool } from '@wrongstack/core/types';
2
1
  import type { ToolRegistry } from '@wrongstack/core/registry';
2
+ import type { ConcreteTokenSavingTier, Tool } from '@wrongstack/core/types';
3
3
  /**
4
4
  * Select built-in tools for a concrete token-saving tier while preserving the
5
5
  * order and instances supplied by the caller.