@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/search.ts"],"names":[],"mappings":"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","file":"search.js","sourcesContent":["import type { Tool, ToolStreamEvent } from '@wrongstack/core';\r\n\r\ninterface SearchInput {\r\n query: string;\r\n num_results?: number;\r\n source?: 'duckduckgo' | 'google' | 'bing';\r\n}\r\n\r\ninterface SearchOutput {\r\n query: string;\r\n results: { title: string; url: string; snippet: string }[];\r\n source: string;\r\n truncated: boolean;\r\n}\r\n\r\nconst DEFAULT_NUM = 10;\r\nconst MAX_RESULTS = 50;\r\nconst TIMEOUT_MS = 15_000;\r\n\r\nexport const searchTool: Tool<SearchInput, SearchOutput> = {\r\n name: 'search',\r\n description:\r\n 'Search the web for information. Returns title, URL, and snippet for each result.',\r\n usageHint:\r\n 'Set `num_results` (1-50, default 10). Use `source` to pick engine: duckduckgo (default), google, bing.',\r\n permission: 'confirm',\r\n mutating: false,\r\n timeoutMs: TIMEOUT_MS,\r\n inputSchema: {\r\n type: 'object',\r\n properties: {\r\n query: { type: 'string', description: 'Search query' },\r\n num_results: {\r\n type: 'integer',\r\n description: 'Number of results (1-50, default 10)',\r\n minimum: 1,\r\n maximum: MAX_RESULTS,\r\n },\r\n source: {\r\n type: 'string',\r\n enum: ['duckduckgo', 'google', 'bing'],\r\n description: 'Search engine to use (default: duckduckgo)',\r\n },\r\n },\r\n required: ['query'],\r\n },\r\n async execute(input, ctx, opts) {\r\n let final: SearchOutput | undefined;\r\n for await (const ev of searchTool.executeStream!(input, ctx, opts)) {\r\n if (ev.type === 'final') final = ev.output;\r\n }\r\n if (!final) throw new Error('search: stream ended without final event');\r\n return final;\r\n },\r\n async *executeStream(input, _ctx, opts): AsyncGenerator<ToolStreamEvent<SearchOutput>> {\r\n if (!input?.query) throw new Error('search: query is required');\r\n\r\n const num = Math.max(1, Math.min(input.num_results ?? DEFAULT_NUM, MAX_RESULTS));\r\n const source = input.source ?? 'duckduckgo';\r\n\r\n yield { type: 'log', text: `Querying ${source} for \"${input.query}\"…`, data: { source, query: input.query } };\r\n\r\n let output: SearchOutput;\r\n switch (source) {\r\n case 'duckduckgo':\r\n output = await duckduckgoSearch(input.query, num, opts.signal);\r\n break;\r\n case 'google':\r\n output = await googleSearch(input.query, num, opts.signal);\r\n break;\r\n case 'bing':\r\n output = await bingSearch(input.query, num, opts.signal);\r\n break;\r\n default:\r\n throw new Error(`search: unknown source \"${source}\"`);\r\n }\r\n\r\n yield {\r\n type: 'partial_output',\r\n text: `${output.results.length} results from ${output.source}`,\r\n data: { count: output.results.length },\r\n };\r\n yield { type: 'final', output };\r\n },\r\n};\r\n\r\nasync function duckduckgoSearch(\r\n query: string,\r\n num: number,\r\n signal: AbortSignal,\r\n): Promise<SearchOutput> {\r\n const encoded = encodeURIComponent(query);\r\n const url = `https://lite.duckduckgo.com/lite/?q=${encoded}&kd=-1&kl=wt-wt`;\r\n\r\n const results = await fetchWithTimeout(url, signal, TIMEOUT_MS)\r\n .then((r) => r.text())\r\n .then((html) => parseDuckDuckGo(html, num))\r\n .catch(() => [{ title: 'Search unavailable', url: '', snippet: 'Could not reach DuckDuckGo' }]);\r\n\r\n return {\r\n query,\r\n results,\r\n source: 'duckduckgo',\r\n truncated: results.length >= num,\r\n };\r\n}\r\n\r\nfunction takeFrom<T>(iter: Iterable<T>, max: number): T[] {\r\n const out: T[] = [];\r\n for (const item of iter) {\r\n if (out.length >= max) break;\r\n out.push(item);\r\n }\r\n return out;\r\n}\r\n\r\nfunction parseDuckDuckGo(html: string, num: number): SearchOutput['results'] {\r\n const results: SearchOutput['results'] = [];\r\n const snippetRegex = /<a class=\"result-link\"[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi;\r\n const snippet2Regex = /<a class=\"result-snippet\"[^>]*>([^<]+)<\\/a>/gi;\r\n\r\n const linkMatches = takeFrom(\r\n [...html.matchAll(snippetRegex)]\r\n .filter((m) => m[1] && m[2])\r\n .map((m) => ({ url: m[1]!, title: stripTags(m[2]!) })),\r\n num,\r\n );\r\n\r\n const snippetMatches = takeFrom(\r\n [...html.matchAll(snippet2Regex)]\r\n .filter((m) => m[1])\r\n .map((m) => stripTags(m[1]!)),\r\n num,\r\n );\r\n\r\n for (let i = 0; i < linkMatches.length && i < num; i++) {\r\n const entry = linkMatches[i];\r\n results.push({\r\n title: entry?.title ?? '',\r\n url: entry?.url ?? '',\r\n snippet: snippetMatches[i] ?? '',\r\n });\r\n }\r\n\r\n return results;\r\n}\r\n\r\nasync function googleSearch(\r\n query: string,\r\n num: number,\r\n signal: AbortSignal,\r\n): Promise<SearchOutput> {\r\n const encoded = encodeURIComponent(query);\r\n const url = `https://www.google.com/search?q=${encoded}&hl=en`;\r\n\r\n const html = await fetchWithTimeout(url, signal, TIMEOUT_MS)\r\n .then((r) => r.text())\r\n .catch(() => '');\r\n\r\n const results = parseGoogleResults(html, num);\r\n\r\n return {\r\n query,\r\n results,\r\n source: 'google',\r\n truncated: results.length >= num,\r\n };\r\n}\r\n\r\nfunction parseGoogleResults(html: string, num: number): SearchOutput['results'] {\r\n const results: SearchOutput['results'] = [];\r\n const titleRegex = /<h3[^>]*class=\"[^\"]*DKV84\"[^>]*>([^<]+)<\\/h3>/gi;\r\n const urlRegex = /<cite[^>]*>([^<]+)<\\/cite>/gi;\r\n const snippetRegex = /<span[^>]*class=\"[^\"]*aXCZ0b[^>]*>([^<]+)<\\/span>/gi;\r\n\r\n const titles = takeFrom(\r\n [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\r\n num,\r\n );\r\n\r\n const urls = takeFrom(\r\n [...html.matchAll(urlRegex)]\r\n .filter((m) => m[1])\r\n .map((m) => stripTags(m[1]!).replace(/^\\*(https?:\\/\\/[^\\s]+).*$/, '$1'))\r\n .filter((u) => u.startsWith('http')),\r\n num,\r\n );\r\n\r\n const snippets = takeFrom(\r\n [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\r\n num,\r\n );\r\n\r\n for (let i = 0; i < Math.min(titles.length, num); i++) {\r\n results.push({\r\n title: titles[i] ?? '',\r\n url: urls[i] ?? '',\r\n snippet: snippets[i] ?? '',\r\n });\r\n }\r\n\r\n return results;\r\n}\r\n\r\nasync function bingSearch(\r\n query: string,\r\n num: number,\r\n signal: AbortSignal,\r\n): Promise<SearchOutput> {\r\n const encoded = encodeURIComponent(query);\r\n const url = `https://www.bing.com/search?q=${encoded}`;\r\n\r\n const html = await fetchWithTimeout(url, signal, TIMEOUT_MS)\r\n .then((r) => r.text())\r\n .catch(() => '');\r\n\r\n const results = parseBingResults(html, num);\r\n\r\n return {\r\n query,\r\n results,\r\n source: 'bing',\r\n truncated: results.length >= num,\r\n };\r\n}\r\n\r\nfunction parseBingResults(html: string, num: number): SearchOutput['results'] {\r\n const results: SearchOutput['results'] = [];\r\n const titleRegex = /<h2[^>]*>\\s*<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>\\s*<\\/h2>/gi;\r\n const snippetRegex = /<p[^>]*class=\"[^\"]*b_paractl[^\"]*\"[^>]*>([^<]+)<\\/p>/gi;\r\n\r\n const entries = takeFrom(\r\n [...html.matchAll(titleRegex)]\r\n .filter((m) => m[1] && m[2])\r\n .map((m) => ({ url: m[1]!, title: stripTags(m[2]!) })),\r\n num,\r\n );\r\n\r\n const snippets = takeFrom(\r\n [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\r\n num,\r\n );\r\n\r\n for (let i = 0; i < entries.length; i++) {\r\n results.push({\r\n title: entries[i]?.title ?? '',\r\n url: entries[i]?.url ?? '',\r\n snippet: snippets[i] ?? '',\r\n });\r\n }\r\n\r\n return results;\r\n}\r\n\r\nasync function fetchWithTimeout(\r\n url: string,\r\n signal: AbortSignal,\r\n timeoutMs: number,\r\n): Promise<Response> {\r\n const controller = new AbortController();\r\n const timer = setTimeout(() => controller.abort(), timeoutMs);\r\n\r\n const fetchSignal = anySignal(signal, controller.signal);\r\n try {\r\n const res = await fetch(url, {\r\n headers: {\r\n 'User-Agent':\r\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\r\n },\r\n signal: fetchSignal,\r\n });\r\n clearTimeout(timer);\r\n return res;\r\n } catch (e) {\r\n clearTimeout(timer);\r\n throw e;\r\n }\r\n}\r\n\r\nfunction anySignal(...signals: AbortSignal[]): AbortSignal {\r\n const controller = new AbortController();\r\n for (const s of signals) {\r\n if (s.aborted) { controller.abort(); break; }\r\n s.addEventListener('abort', () => controller.abort());\r\n }\r\n return controller.signal;\r\n}\r\n\r\nfunction stripTags(html: string): string {\r\n return html.replace(/<[^>]+>/g, '').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '\"').replace(/&#39;/g, \"'\").trim();\r\n}"]}
1
+ {"version":3,"sources":["../src/search.ts"],"names":[],"mappings":"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","file":"search.js","sourcesContent":["import type { Tool, ToolStreamEvent } from '@wrongstack/core';\n\ninterface SearchInput {\n query: string;\n num_results?: number;\n source?: 'duckduckgo' | 'google' | 'bing';\n}\n\ninterface SearchOutput {\n query: string;\n results: { title: string; url: string; snippet: string }[];\n source: string;\n truncated: boolean;\n}\n\nconst DEFAULT_NUM = 10;\nconst MAX_RESULTS = 50;\nconst TIMEOUT_MS = 15_000;\n\nexport const searchTool: Tool<SearchInput, SearchOutput> = {\n name: 'search',\n description: 'Search the web for information. Returns title, URL, and snippet for each result.',\n usageHint:\n 'Set `num_results` (1-50, default 10). Use `source` to pick engine: duckduckgo (default), google, bing.',\n permission: 'confirm',\n mutating: false,\n timeoutMs: TIMEOUT_MS,\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query' },\n num_results: {\n type: 'integer',\n description: 'Number of results (1-50, default 10)',\n minimum: 1,\n maximum: MAX_RESULTS,\n },\n source: {\n type: 'string',\n enum: ['duckduckgo', 'google', 'bing'],\n description: 'Search engine to use (default: duckduckgo)',\n },\n },\n required: ['query'],\n },\n async execute(input, ctx, opts) {\n let final: SearchOutput | undefined;\n for await (const ev of searchTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('search: stream ended without final event');\n return final;\n },\n async *executeStream(input, _ctx, opts): AsyncGenerator<ToolStreamEvent<SearchOutput>> {\n if (!input?.query) throw new Error('search: query is required');\n\n const num = Math.max(1, Math.min(input.num_results ?? DEFAULT_NUM, MAX_RESULTS));\n const source = input.source ?? 'duckduckgo';\n\n yield {\n type: 'log',\n text: `Querying ${source} for \"${input.query}\"…`,\n data: { source, query: input.query },\n };\n\n let output: SearchOutput;\n switch (source) {\n case 'duckduckgo':\n output = await duckduckgoSearch(input.query, num, opts.signal);\n break;\n case 'google':\n output = await googleSearch(input.query, num, opts.signal);\n break;\n case 'bing':\n output = await bingSearch(input.query, num, opts.signal);\n break;\n default:\n throw new Error(`search: unknown source \"${source}\"`);\n }\n\n yield {\n type: 'partial_output',\n text: `${output.results.length} results from ${output.source}`,\n data: { count: output.results.length },\n };\n yield { type: 'final', output };\n },\n};\n\nasync function duckduckgoSearch(\n query: string,\n num: number,\n signal: AbortSignal,\n): Promise<SearchOutput> {\n const encoded = encodeURIComponent(query);\n const url = `https://lite.duckduckgo.com/lite/?q=${encoded}&kd=-1&kl=wt-wt`;\n\n const results = await fetchWithTimeout(url, signal, TIMEOUT_MS)\n .then((r) => r.text())\n .then((html) => parseDuckDuckGo(html, num))\n .catch(() => [{ title: 'Search unavailable', url: '', snippet: 'Could not reach DuckDuckGo' }]);\n\n return {\n query,\n results,\n source: 'duckduckgo',\n truncated: results.length >= num,\n };\n}\n\nfunction takeFrom<T>(iter: Iterable<T>, max: number): T[] {\n const out: T[] = [];\n for (const item of iter) {\n if (out.length >= max) break;\n out.push(item);\n }\n return out;\n}\n\nfunction parseDuckDuckGo(html: string, num: number): SearchOutput['results'] {\n const results: SearchOutput['results'] = [];\n const snippetRegex = /<a class=\"result-link\"[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>/gi;\n const snippet2Regex = /<a class=\"result-snippet\"[^>]*>([^<]+)<\\/a>/gi;\n\n const linkMatches = takeFrom(\n [...html.matchAll(snippetRegex)]\n .filter((m) => m[1] && m[2])\n .map((m) => ({ url: m[1]!, title: stripTags(m[2]!) })),\n num,\n );\n\n const snippetMatches = takeFrom(\n [...html.matchAll(snippet2Regex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\n num,\n );\n\n for (let i = 0; i < linkMatches.length && i < num; i++) {\n const entry = linkMatches[i];\n results.push({\n title: entry?.title ?? '',\n url: entry?.url ?? '',\n snippet: snippetMatches[i] ?? '',\n });\n }\n\n return results;\n}\n\nasync function googleSearch(\n query: string,\n num: number,\n signal: AbortSignal,\n): Promise<SearchOutput> {\n const encoded = encodeURIComponent(query);\n const url = `https://www.google.com/search?q=${encoded}&hl=en`;\n\n const html = await fetchWithTimeout(url, signal, TIMEOUT_MS)\n .then((r) => r.text())\n .catch(() => '');\n\n const results = parseGoogleResults(html, num);\n\n return {\n query,\n results,\n source: 'google',\n truncated: results.length >= num,\n };\n}\n\nfunction parseGoogleResults(html: string, num: number): SearchOutput['results'] {\n const results: SearchOutput['results'] = [];\n const titleRegex = /<h3[^>]*class=\"[^\"]*DKV84\"[^>]*>([^<]+)<\\/h3>/gi;\n const urlRegex = /<cite[^>]*>([^<]+)<\\/cite>/gi;\n const snippetRegex = /<span[^>]*class=\"[^\"]*aXCZ0b[^>]*>([^<]+)<\\/span>/gi;\n\n const titles = takeFrom(\n [...html.matchAll(titleRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\n num,\n );\n\n const urls = takeFrom(\n [...html.matchAll(urlRegex)]\n .filter((m) => m[1])\n .map((m) => stripTags(m[1]!).replace(/^\\*(https?:\\/\\/[^\\s]+).*$/, '$1'))\n .filter((u) => u.startsWith('http')),\n num,\n );\n\n const snippets = takeFrom(\n [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\n num,\n );\n\n for (let i = 0; i < Math.min(titles.length, num); i++) {\n results.push({\n title: titles[i] ?? '',\n url: urls[i] ?? '',\n snippet: snippets[i] ?? '',\n });\n }\n\n return results;\n}\n\nasync function bingSearch(query: string, num: number, signal: AbortSignal): Promise<SearchOutput> {\n const encoded = encodeURIComponent(query);\n const url = `https://www.bing.com/search?q=${encoded}`;\n\n const html = await fetchWithTimeout(url, signal, TIMEOUT_MS)\n .then((r) => r.text())\n .catch(() => '');\n\n const results = parseBingResults(html, num);\n\n return {\n query,\n results,\n source: 'bing',\n truncated: results.length >= num,\n };\n}\n\nfunction parseBingResults(html: string, num: number): SearchOutput['results'] {\n const results: SearchOutput['results'] = [];\n const titleRegex = /<h2[^>]*>\\s*<a[^>]+href=\"([^\"]+)\"[^>]*>([^<]+)<\\/a>\\s*<\\/h2>/gi;\n const snippetRegex = /<p[^>]*class=\"[^\"]*b_paractl[^\"]*\"[^>]*>([^<]+)<\\/p>/gi;\n\n const entries = takeFrom(\n [...html.matchAll(titleRegex)]\n .filter((m) => m[1] && m[2])\n .map((m) => ({ url: m[1]!, title: stripTags(m[2]!) })),\n num,\n );\n\n const snippets = takeFrom(\n [...html.matchAll(snippetRegex)].filter((m) => m[1]).map((m) => stripTags(m[1]!)),\n num,\n );\n\n for (let i = 0; i < entries.length; i++) {\n results.push({\n title: entries[i]?.title ?? '',\n url: entries[i]?.url ?? '',\n snippet: snippets[i] ?? '',\n });\n }\n\n return results;\n}\n\nasync function fetchWithTimeout(\n url: string,\n signal: AbortSignal,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const fetchSignal = anySignal(signal, controller.signal);\n try {\n const res = await fetch(url, {\n headers: {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\n },\n signal: fetchSignal,\n });\n clearTimeout(timer);\n return res;\n } catch (e) {\n clearTimeout(timer);\n throw e;\n }\n}\n\nfunction anySignal(...signals: AbortSignal[]): AbortSignal {\n const controller = new AbortController();\n for (const s of signals) {\n if (s.aborted) {\n controller.abort();\n break;\n }\n s.addEventListener('abort', () => controller.abort());\n }\n return controller.signal;\n}\n\nfunction stripTags(html: string): string {\n return html\n .replace(/<[^>]+>/g, '')\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .trim();\n}\n"]}
package/dist/test.js CHANGED
@@ -1,22 +1,8 @@
1
1
  import * as path from 'path';
2
2
  import { spawn } from 'child_process';
3
+ import { buildChildEnv } from '@wrongstack/core';
3
4
 
4
5
  // src/test.ts
5
- function resolvePath(input, ctx) {
6
- return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
7
- }
8
- function ensureInsideRoot(absPath, ctx) {
9
- const root = path.resolve(ctx.projectRoot);
10
- const target = path.resolve(absPath);
11
- const rel = path.relative(root, target);
12
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
13
- throw new Error(`Path "${absPath}" is outside project root "${root}"`);
14
- }
15
- return target;
16
- }
17
- function safeResolve(input, ctx) {
18
- return ensureInsideRoot(resolvePath(input, ctx), ctx);
19
- }
20
6
  async function* spawnStream(opts) {
21
7
  const max = opts.maxBytes;
22
8
  const flushAt = opts.flushBytes ?? 4 * 1024;
@@ -27,6 +13,7 @@ async function* spawnStream(opts) {
27
13
  const child = spawn(opts.cmd, opts.args, {
28
14
  cwd: opts.cwd,
29
15
  signal: opts.signal,
16
+ env: buildChildEnv(),
30
17
  stdio: ["ignore", "pipe", "pipe"]
31
18
  });
32
19
  const queue = [];
@@ -94,6 +81,21 @@ async function* spawnStream(opts) {
94
81
  error
95
82
  };
96
83
  }
84
+ function resolvePath(input, ctx) {
85
+ return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
86
+ }
87
+ function ensureInsideRoot(absPath, ctx) {
88
+ const root = path.resolve(ctx.projectRoot);
89
+ const target = path.resolve(absPath);
90
+ const rel = path.relative(root, target);
91
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
92
+ throw new Error(`Path "${absPath}" is outside project root "${root}"`);
93
+ }
94
+ return target;
95
+ }
96
+ function safeResolve(input, ctx) {
97
+ return ensureInsideRoot(resolvePath(input, ctx), ctx);
98
+ }
97
99
 
98
100
  // src/test.ts
99
101
  var testTool = {
package/dist/test.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/_util.ts","../src/_spawn-stream.ts","../src/test.ts"],"names":["resolve","path2"],"mappings":";;;;AAGO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAY,IAAA,CAAA,UAAA,CAAW,KAAK,CAAA,GAAS,IAAA,CAAA,SAAA,CAAU,KAAK,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACrF;AAEO,SAAS,gBAAA,CAAiB,SAAiB,GAAA,EAAsB;AACtE,EAAA,MAAM,IAAA,GAAY,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AACzC,EAAA,MAAM,MAAA,GAAc,aAAQ,OAAO,CAAA;AACnC,EAAA,MAAM,GAAA,GAAW,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AACtC,EAAA,IAAI,IAAI,UAAA,CAAW,IAAI,CAAA,IAAU,IAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,OAAO,CAAA,2BAAA,EAA8B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AACtD;ACSA,gBAAuB,YACrB,IAAA,EACsD;AACtD,EAAA,MAAM,GAAA,GAAM,KAAK,QAAY;AAC7B,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,IAAc,CAAA,GAAI,IAAA;AACvC,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,OAAA,GAAU,EAAA;AACd,EAAA,IAAI,KAAA;AAEJ,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,KAAK,IAAA,EAAM;AAAA,IACvC,KAAK,IAAA,CAAK,GAAA;AAAA,IACV,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,KAAA,EAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAM;AAAA,GACjC,CAAA;AAGD,EAAA,MAAM,QAAiB,EAAC;AACxB,EAAA,IAAI,MAAA;AACJ,EAAA,MAAM,OAAO,MAAM;AACjB,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,CAAA,GAAI,MAAA;AACV,MAAA,MAAA,GAAS,MAAA;AACT,MAAA,CAAA,EAAE;AAAA,IACJ;AAAA,EACF,CAAA;AAEA,EAAA,KAAA,CAAM,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAM;AAC9B,IAAA,MAAM,CAAA,GAAI,EAAE,QAAA,EAAS;AACrB,IAAA,IAAI,MAAA,CAAO,MAAA,GAAS,GAAA,EAAK,MAAA,IAAU,CAAA;AACnC,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,GAAG,CAAA;AACnC,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAM;AAC9B,IAAA,MAAM,CAAA,GAAI,EAAE,QAAA,EAAS;AACrB,IAAA,IAAI,MAAA,CAAO,MAAA,GAAS,GAAA,EAAK,MAAA,IAAU,CAAA;AACnC,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,GAAG,CAAA;AACnC,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,OAAA,EAAS,CAAC,CAAA,KAAM;AACvB,IAAA,KAAA,GAAQ,CAAA,CAAE,OAAA;AACV,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,SAAS,IAAA,EAAM,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,OAAA,EAAS,CAAC,IAAA,KAAS;AAC1B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,MAAM,EAAA,EAAI,IAAA,EAAM,IAAA,IAAQ,CAAA,EAAG,CAAA;AACvD,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AAED,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,WAAS;AACP,IAAA,OAAO,KAAA,CAAM,WAAW,CAAA,EAAG;AACzB,MAAA,MAAM,IAAI,OAAA,CAAc,CAACA,QAAAA,KAAY;AACnC,QAAA,MAAA,GAASA,QAAAA;AAAA,MACX,CAAC,CAAA;AAAA,IACH;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,EAAM;AAC1B,IAAA,IAAI,KAAA,CAAM,SAAS,OAAA,EAAS;AAG1B,MAAA,IAAI,CAAC,WAAA,EAAa,QAAA,GAAW,KAAA,CAAM,IAAA,IAAQ,CAAA;AAC3C,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,SAAS,OAAA,EAAS;AAC1B,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,QAAA,GAAW,CAAA;AAEX,MAAA;AAAA,IACF;AACA,IAAA,OAAA,IAAW,KAAA,CAAM,IAAA;AACjB,IAAA,IAAI,OAAA,CAAQ,UAAU,OAAA,EAAS;AAC7B,MAAA,MAAM,EAAE,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,OAAA,EAAQ;AAC9C,MAAA,OAAA,GAAU,EAAA;AAAA,IACZ;AAAA,EACF;AACA,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,IAAA,MAAM,EAAE,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,OAAA,EAAQ;AAAA,EAChD;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA,EAAW,MAAA,CAAO,MAAA,IAAU,GAAA,IAAO,OAAO,MAAA,IAAU,GAAA;AAAA,IACpD;AAAA,GACF;AACF;;;ACzFO,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EACE,6EAAA;AAAA,EACF,SAAA,EACE,2HAAA;AAAA,EACF,UAAA,EAAY,SAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,IAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,QAAA,EAAU,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA,QACxC,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,oCAAA,EAAqC;AAAA,MAC5E,QAAA,EAAU,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,2CAAA,EAA4C;AAAA,MACtF,GAAA,EAAK,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,kCAAA,EAAmC;AAAA,MACvE,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,8CAAA,EAA+C;AAAA,MACpF,OAAA,EAAS,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,qCAAA;AAAsC;AACjF,GACF;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,IAAI,KAAA;AACJ,IAAA,WAAA,MAAiB,MAAM,QAAA,CAAS,aAAA,CAAe,KAAA,EAAO,GAAA,EAAK,IAAI,CAAA,EAAG;AAChE,MAAA,IAAI,EAAA,CAAG,IAAA,KAAS,OAAA,EAAS,KAAA,GAAQ,EAAA,CAAG,MAAA;AAAA,IACtC;AACA,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,wCAAwC,CAAA;AACpE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAO,aAAA,CAAc,KAAA,EAAO,GAAA,EAAK,IAAA,EAAmD;AAClF,IAAA,MAAM,GAAA,GAAM,MAAM,GAAA,GAAM,WAAA,CAAY,MAAM,GAAA,EAAK,GAAG,IAAI,GAAA,CAAI,GAAA;AAC1D,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,MAAA;AAE/B,IAAA,MAAM,WAAW,MAAA,KAAW,MAAA,GAAS,MAAM,YAAA,CAAa,GAAG,CAAA,GAAI,MAAA;AAC/D,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,OAAA;AAAA,QACN,MAAA,EAAQ;AAAA,UACN,MAAA,EAAQ,MAAA;AAAA,UACR,SAAA,EAAW,CAAA;AAAA,UACX,SAAA,EAAW,CAAA;AAAA,UACX,MAAA,EAAQ,CAAA;AAAA,UACR,MAAA,EAAQ,CAAA;AAAA,UACR,WAAA,EAAa,CAAA;AAAA,UACb,MAAA,EAAQ,wEAAA;AAAA,UACR,SAAA,EAAW;AAAA;AACb,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA,QAAA,EAAW,QAAQ,CAAA,MAAA,CAAA,EAAK,IAAA,EAAM,EAAE,MAAA,EAAQ,QAAA,EAAS,EAAE;AAE9E,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,QAAA,EAAU,KAAK,CAAA;AAEtC,IAAA,MAAM,MAAA,GAAS,OAAO,WAAA,CAAY;AAAA,MAChC,GAAA,EAAK,QAAA;AAAA,MACL,IAAA;AAAA,MACA,GAAA;AAAA,MACA,QAAQ,IAAA,CAAK,MAAA;AAAA,MACb,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAE9B,IAAA,MAAM,EAAE,MAAM,OAAA,EAAS,MAAA,EAAQ,YAAY,QAAA,EAAU,MAAA,EAAQ,QAAQ,CAAA,EAAE;AAAA,EACzE;AACF;AAEA,eAAe,aAAa,GAAA,EAAqC;AAC/D,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,OAAO,aAAkB,CAAA;AAChD,EAAA,MAAM,UAAA,GAAa,CAAC,kBAAA,EAAoB,gBAAA,EAAkB,eAAe,CAAA;AACzE,EAAA,KAAA,MAAW,KAAK,UAAA,EAAY;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAUC,IAAA,CAAA,IAAA,CAAK,GAAA,EAAK,CAAC,CAAC,CAAA;AAC5B,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,QAAA;AACjC,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,MAAA;AAC/B,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,OAAO,CAAA,EAAG,OAAO,OAAA;AAAA,IAClC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,SAAA,CAAU,QAAgB,KAAA,EAA4B;AAC7D,EAAA,MAAM,OAAiB,EAAC;AACxB,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,IAAW,GAAA;AAEjC,EAAA,QAAQ,MAAA;AAAQ,IACd,KAAK,QAAA;AACH,MAAA,IAAA,CAAK,IAAA,CAAK,OAAO,oBAAoB,CAAA;AACrC,MAAA,IAAI,MAAM,KAAA,EAAO;AAAE,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,EAAA;AAAI,QAAA,IAAA,CAAK,KAAK,OAAO,CAAA;AAAA,MAAG;AACrD,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA;AAC1C,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,mBAAA,EAAqB,MAAM,IAAI,CAAA;AACzD,MAAA,IAAA,CAAK,IAAA,CAAK,eAAA,EAAiB,MAAA,CAAO,OAAO,CAAC,CAAA;AAC1C,MAAA;AAAA,IACF,KAAK,MAAA;AACH,MAAA,IAAA,CAAK,KAAK,WAAW,CAAA;AACrB,MAAA,IAAI,KAAA,CAAM,KAAA,EAAO,IAAA,CAAK,IAAA,CAAK,SAAS,CAAA;AACpC,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA;AAC1C,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,mBAAA,EAAqB,MAAM,IAAI,CAAA;AACzD,MAAA,IAAA,CAAK,IAAA,CAAK,eAAA,EAAiB,MAAA,CAAO,OAAO,CAAC,CAAA;AAC1C,MAAA;AAAA,IACF,KAAK,OAAA;AACH,MAAA,IAAA,CAAK,IAAA,CAAK,cAAc,MAAM,CAAA;AAC9B,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,IAAI,CAAA;AAC9C,MAAA,IAAA,CAAK,IAAA,CAAK,WAAA,EAAa,MAAA,CAAO,OAAO,CAAC,CAAA;AACtC,MAAA;AAAA;AAGJ,EAAA,IAAI,MAAM,KAAA,EAAO;AACf,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,GAAG,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,WAAA,CACP,MAAA,EACA,MAAA,EACA,QAAA,EACY;AACZ,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,MAAA,GAAS,MAAA,CAAO,MAAA;AAEnC,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,IAAI,MAAA,GAAS,CAAA;AAEb,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,cAAc,CAAA;AAC5C,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,cAAc,CAAA;AAC5C,IAAA,IAAI,WAAA,GAAc,CAAC,CAAA,EAAG,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA,EAAG,EAAE,CAAA;AACjE,IAAA,IAAI,WAAA,GAAc,CAAC,CAAA,EAAG,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA,EAAG,EAAE,CAAA;AACjE,IAAA,SAAA,GAAY,MAAA,GAAS,MAAA;AAAA,EACvB,CAAA,MAAA,IAAW,WAAW,MAAA,EAAQ;AAC5B,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,8BAA8B,CAAA;AAC5D,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,yBAAyB,CAAA;AACvD,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,yBAAyB,CAAA;AACvD,IAAA,SAAA,GAAY,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AACvD,IAAA,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AACpD,IAAA,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,EACtD;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,WAAW,MAAA,CAAO,QAAA;AAAA,IAClB,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA,EAAa,QAAA;AAAA,IACb,MAAA,EAAQ,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,KAAA,IAAS,EAAA;AAAA,IACzC,WAAW,MAAA,CAAO;AAAA,GACpB;AACF","file":"test.js","sourcesContent":["import * as path from 'node:path';\nimport type { Context } from '@wrongstack/core';\n\r\nexport function resolvePath(input: string, ctx: Context): string {\r\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\r\n}\r\n\r\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\r\n const root = path.resolve(ctx.projectRoot);\r\n const target = path.resolve(absPath);\r\n const rel = path.relative(root, target);\r\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\r\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\r\n }\r\n return target;\r\n}\r\n\r\nexport function safeResolve(input: string, ctx: Context): string {\r\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\r\n}\r\n\r\nexport function truncateMiddle(s: string, max: number): string {\r\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\r\n const half = Math.floor(max / 2);\r\n return (\r\n s.slice(0, half) +\r\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\r\n s.slice(-half)\r\n );\r\n}\r\n\r\nexport function isBinaryBuffer(buf: Buffer): boolean {\r\n const len = Math.min(buf.length, 8192);\r\n for (let i = 0; i < len; i++) {\r\n if (buf[i] === 0) return true;\r\n }\r\n return false;\r\n}\r\n\r\n","import { spawn } from 'node:child_process';\nimport type { ToolProgressEvent } from '@wrongstack/core';\n\nexport interface SpawnStreamResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n truncated: boolean;\n error?: string;\n}\n\nexport interface SpawnStreamOptions {\n cmd: string;\n args: string[];\n cwd: string;\n signal: AbortSignal;\n maxBytes?: number;\n /** Bytes of new stdout/stderr to accumulate before yielding a `partial_output` event. */\n flushBytes?: number;\n}\n\n/**\n * Spawn a child process and yield `partial_output` progress events as\n * stdout/stderr arrive (batched by byte threshold), then return the full\n * buffered result. Shared between install/lint/format/typecheck/test/audit\n * so the TUI live tail sees consistent progress regardless of which tool\n * is running.\n */\nexport async function* spawnStream(\n opts: SpawnStreamOptions,\n): AsyncGenerator<ToolProgressEvent, SpawnStreamResult> {\n const max = opts.maxBytes ?? 200_000;\n const flushAt = opts.flushBytes ?? 4 * 1024;\n let stdout = '';\n let stderr = '';\n let pending = '';\n let error: string | undefined;\n\n const child = spawn(opts.cmd, opts.args, {\n cwd: opts.cwd,\n signal: opts.signal,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n type Chunk = { kind: 'out' | 'err' | 'close' | 'error'; data: string; code?: number };\n const queue: Chunk[] = [];\n let waiter: (() => void) | undefined;\n const wake = () => {\n if (waiter) {\n const w = waiter;\n waiter = undefined;\n w();\n }\n };\n\n child.stdout?.on('data', (c) => {\n const s = c.toString();\n if (stdout.length < max) stdout += s;\n queue.push({ kind: 'out', data: s });\n wake();\n });\n child.stderr?.on('data', (c) => {\n const s = c.toString();\n if (stderr.length < max) stderr += s;\n queue.push({ kind: 'err', data: s });\n wake();\n });\n child.on('error', (e) => {\n error = e.message;\n queue.push({ kind: 'error', data: e.message });\n wake();\n });\n child.on('close', (code) => {\n queue.push({ kind: 'close', data: '', code: code ?? 0 });\n wake();\n });\n\n let exitCode = 0;\n let spawnFailed = false;\n for (;;) {\n while (queue.length === 0) {\n await new Promise<void>((resolve) => {\n waiter = resolve;\n });\n }\n const chunk = queue.shift()!;\n if (chunk.kind === 'close') {\n // If we already saw a spawn error (ENOENT etc.), keep exitCode=1\n // rather than the negative platform code Node fabricates.\n if (!spawnFailed) exitCode = chunk.code ?? 0;\n break;\n }\n if (chunk.kind === 'error') {\n spawnFailed = true;\n exitCode = 1;\n // close usually follows\n continue;\n }\n pending += chunk.data;\n if (pending.length >= flushAt) {\n yield { type: 'partial_output', text: pending };\n pending = '';\n }\n }\n if (pending.length > 0) {\n yield { type: 'partial_output', text: pending };\n }\n\n return {\n stdout,\n stderr,\n exitCode,\n truncated: stdout.length >= max || stderr.length >= max,\n error,\n };\n}\n","import * as path from 'node:path';\r\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\r\nimport { safeResolve } from './_util.js';\nimport { spawnStream } from './_spawn-stream.js';\n\r\ninterface TestInput {\r\n files?: string | string[];\r\n runner?: 'vitest' | 'jest' | 'mocha' | 'auto';\r\n watch?: boolean;\r\n coverage?: boolean;\r\n cwd?: string;\r\n grep?: string;\r\n timeout?: number;\r\n}\r\n\r\ninterface TestOutput {\r\n runner: string;\r\n exit_code: number;\r\n tests_run: number;\r\n passed: number;\r\n failed: number;\r\n duration_ms: number;\r\n output: string;\r\n truncated: boolean;\r\n}\r\n\r\nexport const testTool: Tool<TestInput, TestOutput> = {\r\n name: 'test',\r\n description:\r\n 'Run tests with vitest, jest, or mocha. Returns pass/fail counts and output.',\r\n usageHint:\r\n 'Set `files` for specific tests. `watch` enables watch mode. `coverage` generates coverage report. `grep` filters by name.',\r\n permission: 'confirm',\r\n mutating: false,\r\n timeoutMs: 120_000,\r\n inputSchema: {\r\n type: 'object',\r\n properties: {\r\n files: {\r\n type: 'string',\r\n description: 'Test files: single path, comma-separated list, or glob (e.g. \"**/*.test.ts\")',\r\n },\r\n runner: {\r\n type: 'string',\r\n enum: ['vitest', 'jest', 'mocha', 'auto'],\r\n description: 'Test runner (default: auto-detect)',\r\n },\r\n watch: { type: 'boolean', description: 'Run in watch mode (default: false)' },\r\n coverage: { type: 'boolean', description: 'Generate coverage report (default: false)' },\r\n cwd: { type: 'string', description: 'Working directory (default: cwd)' },\r\n grep: { type: 'string', description: 'Filter tests by name pattern (default: none)' },\r\n timeout: { type: 'integer', description: 'Test timeout in ms (default: 30000)' },\r\n },\r\n },\r\n async execute(input, ctx, opts) {\r\n let final: TestOutput | undefined;\r\n for await (const ev of testTool.executeStream!(input, ctx, opts)) {\r\n if (ev.type === 'final') final = ev.output;\r\n }\r\n if (!final) throw new Error('test: stream ended without final event');\r\n return final;\r\n },\r\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<TestOutput>> {\r\n const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;\r\n const runner = input.runner ?? 'auto';\r\n\r\n const detected = runner === 'auto' ? await detectRunner(cwd) : runner;\r\n if (!detected) {\r\n yield {\r\n type: 'final',\r\n output: {\r\n runner: 'none',\r\n exit_code: 1,\r\n tests_run: 0,\r\n passed: 0,\r\n failed: 0,\r\n duration_ms: 0,\r\n output: 'No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)',\r\n truncated: false,\r\n },\r\n };\r\n return;\r\n }\r\n\r\n yield { type: 'log', text: `Running ${detected}…`, data: { runner: detected } };\r\n\r\n const start = Date.now();\r\n const args = buildArgs(detected, input);\r\n\r\n const result = yield* spawnStream({\r\n cmd: detected,\r\n args,\r\n cwd,\r\n signal: opts.signal,\r\n maxBytes: 200_000,\r\n });\r\n const duration = Date.now() - start;\r\n\r\n yield { type: 'final', output: parseResult(detected, result, duration) };\r\n },\r\n};\r\n\r\nasync function detectRunner(cwd: string): Promise<string | null> {\r\n const { stat } = await import('node:fs/promises');\r\n const candidates = ['vitest.config.ts', 'jest.config.js', '.mocharc.json'];\r\n for (const f of candidates) {\r\n try {\r\n await stat(path.join(cwd, f));\r\n if (f.includes('vitest')) return 'vitest';\r\n if (f.includes('jest')) return 'jest';\r\n if (f.includes('mocha')) return 'mocha';\r\n } catch {\r\n // continue\r\n }\r\n }\r\n return 'vitest';\r\n}\r\n\r\nfunction buildArgs(runner: string, input: TestInput): string[] {\r\n const args: string[] = [];\r\n const timeout = input.timeout ?? 30000;\r\n\r\n switch (runner) {\r\n case 'vitest':\r\n args.push('run', '--reporter=verbose');\r\n if (input.watch) { args[1] = ''; args.push('watch'); }\r\n if (input.coverage) args.push('--coverage');\r\n if (input.grep) args.push('--testNamePattern', input.grep);\r\n args.push('--testTimeout', String(timeout));\r\n break;\r\n case 'jest':\r\n args.push('--verbose');\r\n if (input.watch) args.push('--watch');\r\n if (input.coverage) args.push('--coverage');\r\n if (input.grep) args.push('--testPathPattern', input.grep);\r\n args.push('--testTimeout', String(timeout));\r\n break;\r\n case 'mocha':\r\n args.push('--reporter', 'spec');\r\n if (input.grep) args.push('--grep', input.grep);\r\n args.push('--timeout', String(timeout));\r\n break;\r\n }\r\n\r\n if (input.files) {\r\n const files = Array.isArray(input.files) ? input.files : input.files.split(',');\r\n args.push('--', ...files.map((f) => f.trim()));\r\n }\r\n\r\n return args;\r\n}\r\n\r\nfunction parseResult(\r\n runner: string,\r\n result: { stdout: string; stderr: string; exitCode: number; truncated: boolean; error?: string },\r\n duration: number,\r\n): TestOutput {\r\n const out = result.stdout + result.stderr;\r\n\r\n let tests_run = 0;\r\n let passed = 0;\r\n let failed = 0;\r\n\r\n if (runner === 'vitest') {\r\n const passedMatch = out.match(/(\\d+) passed/);\r\n const failedMatch = out.match(/(\\d+) failed/);\r\n if (passedMatch?.[1]) passed = Number.parseInt(passedMatch[1], 10);\r\n if (failedMatch?.[1]) failed = Number.parseInt(failedMatch[1], 10);\r\n tests_run = passed + failed;\r\n } else if (runner === 'jest') {\r\n const suitesMatch = out.match(/Test Suites:\\s+(\\d+)\\s+total/);\r\n const passedMatch = out.match(/Tests:\\s+(\\d+)\\s+passed/);\r\n const failedMatch = out.match(/Tests:\\s+(\\d+)\\s+failed/);\r\n tests_run = Number.parseInt(suitesMatch?.[1] ?? '0', 10);\r\n passed = Number.parseInt(passedMatch?.[1] ?? '0', 10);\r\n failed = Number.parseInt(failedMatch?.[1] ?? '0', 10);\r\n }\r\n\r\n return {\r\n runner,\r\n exit_code: result.exitCode,\r\n tests_run,\r\n passed,\r\n failed,\r\n duration_ms: duration,\r\n output: result.stdout || result.error || '',\r\n truncated: result.truncated,\r\n };\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/_spawn-stream.ts","../src/_util.ts","../src/test.ts"],"names":["resolve","path2"],"mappings":";;;;;AA6BA,gBAAuB,YACrB,IAAA,EACsD;AACtD,EAAA,MAAM,GAAA,GAAM,KAAK,QAAY;AAC7B,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,IAAc,CAAA,GAAI,IAAA;AACvC,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,OAAA,GAAU,EAAA;AACd,EAAA,IAAI,KAAA;AAEJ,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,KAAK,IAAA,EAAM;AAAA,IACvC,KAAK,IAAA,CAAK,GAAA;AAAA,IACV,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,KAAK,aAAA,EAAc;AAAA,IACnB,KAAA,EAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAM;AAAA,GACjC,CAAA;AAGD,EAAA,MAAM,QAAiB,EAAC;AACxB,EAAA,IAAI,MAAA;AACJ,EAAA,MAAM,OAAO,MAAM;AACjB,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,CAAA,GAAI,MAAA;AACV,MAAA,MAAA,GAAS,MAAA;AACT,MAAA,CAAA,EAAE;AAAA,IACJ;AAAA,EACF,CAAA;AAEA,EAAA,KAAA,CAAM,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAM;AAC9B,IAAA,MAAM,CAAA,GAAI,EAAE,QAAA,EAAS;AACrB,IAAA,IAAI,MAAA,CAAO,MAAA,GAAS,GAAA,EAAK,MAAA,IAAU,CAAA;AACnC,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,GAAG,CAAA;AACnC,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAM;AAC9B,IAAA,MAAM,CAAA,GAAI,EAAE,QAAA,EAAS;AACrB,IAAA,IAAI,MAAA,CAAO,MAAA,GAAS,GAAA,EAAK,MAAA,IAAU,CAAA;AACnC,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,GAAG,CAAA;AACnC,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,OAAA,EAAS,CAAC,CAAA,KAAM;AACvB,IAAA,KAAA,GAAQ,CAAA,CAAE,OAAA;AACV,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,SAAS,IAAA,EAAM,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,OAAA,EAAS,CAAC,IAAA,KAAS;AAC1B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,MAAM,EAAA,EAAI,IAAA,EAAM,IAAA,IAAQ,CAAA,EAAG,CAAA;AACvD,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AAED,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,WAAS;AACP,IAAA,OAAO,KAAA,CAAM,WAAW,CAAA,EAAG;AACzB,MAAA,MAAM,IAAI,OAAA,CAAc,CAACA,QAAAA,KAAY;AACnC,QAAA,MAAA,GAASA,QAAAA;AAAA,MACX,CAAC,CAAA;AAAA,IACH;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,EAAM;AAC1B,IAAA,IAAI,KAAA,CAAM,SAAS,OAAA,EAAS;AAG1B,MAAA,IAAI,CAAC,WAAA,EAAa,QAAA,GAAW,KAAA,CAAM,IAAA,IAAQ,CAAA;AAC3C,MAAA;AAAA,IACF;AACA,IAAA,IAAI,KAAA,CAAM,SAAS,OAAA,EAAS;AAC1B,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,QAAA,GAAW,CAAA;AAEX,MAAA;AAAA,IACF;AACA,IAAA,OAAA,IAAW,KAAA,CAAM,IAAA;AACjB,IAAA,IAAI,OAAA,CAAQ,UAAU,OAAA,EAAS;AAC7B,MAAA,MAAM,EAAE,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,OAAA,EAAQ;AAC9C,MAAA,OAAA,GAAU,EAAA;AAAA,IACZ;AAAA,EACF;AACA,EAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,IAAA,MAAM,EAAE,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,OAAA,EAAQ;AAAA,EAChD;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA,EAAW,MAAA,CAAO,MAAA,IAAU,GAAA,IAAO,OAAO,MAAA,IAAU,GAAA;AAAA,IACpD;AAAA,GACF;AACF;AClHO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAY,IAAA,CAAA,UAAA,CAAW,KAAK,CAAA,GAAS,IAAA,CAAA,SAAA,CAAU,KAAK,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACrF;AAEO,SAAS,gBAAA,CAAiB,SAAiB,GAAA,EAAsB;AACtE,EAAA,MAAM,IAAA,GAAY,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AACzC,EAAA,MAAM,MAAA,GAAc,aAAQ,OAAO,CAAA;AACnC,EAAA,MAAM,GAAA,GAAW,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AACtC,EAAA,IAAI,IAAI,UAAA,CAAW,IAAI,CAAA,IAAU,IAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,OAAO,CAAA,2BAAA,EAA8B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AACtD;;;ACOO,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EAAa,6EAAA;AAAA,EACb,SAAA,EACE,2HAAA;AAAA,EACF,UAAA,EAAY,SAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,IAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,QAAA,EAAU,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA,QACxC,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,oCAAA,EAAqC;AAAA,MAC5E,QAAA,EAAU,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,2CAAA,EAA4C;AAAA,MACtF,GAAA,EAAK,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,kCAAA,EAAmC;AAAA,MACvE,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,8CAAA,EAA+C;AAAA,MACpF,OAAA,EAAS,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,qCAAA;AAAsC;AACjF,GACF;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,IAAI,KAAA;AACJ,IAAA,WAAA,MAAiB,MAAM,QAAA,CAAS,aAAA,CAAe,KAAA,EAAO,GAAA,EAAK,IAAI,CAAA,EAAG;AAChE,MAAA,IAAI,EAAA,CAAG,IAAA,KAAS,OAAA,EAAS,KAAA,GAAQ,EAAA,CAAG,MAAA;AAAA,IACtC;AACA,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,wCAAwC,CAAA;AACpE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAO,aAAA,CAAc,KAAA,EAAO,GAAA,EAAK,IAAA,EAAmD;AAClF,IAAA,MAAM,GAAA,GAAM,MAAM,GAAA,GAAM,WAAA,CAAY,MAAM,GAAA,EAAK,GAAG,IAAI,GAAA,CAAI,GAAA;AAC1D,IAAA,MAAM,MAAA,GAAS,MAAM,MAAA,IAAU,MAAA;AAE/B,IAAA,MAAM,WAAW,MAAA,KAAW,MAAA,GAAS,MAAM,YAAA,CAAa,GAAG,CAAA,GAAI,MAAA;AAC/D,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,OAAA;AAAA,QACN,MAAA,EAAQ;AAAA,UACN,MAAA,EAAQ,MAAA;AAAA,UACR,SAAA,EAAW,CAAA;AAAA,UACX,SAAA,EAAW,CAAA;AAAA,UACX,MAAA,EAAQ,CAAA;AAAA,UACR,MAAA,EAAQ,CAAA;AAAA,UACR,WAAA,EAAa,CAAA;AAAA,UACb,MAAA,EAAQ,wEAAA;AAAA,UACR,SAAA,EAAW;AAAA;AACb,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA,QAAA,EAAW,QAAQ,CAAA,MAAA,CAAA,EAAK,IAAA,EAAM,EAAE,MAAA,EAAQ,QAAA,EAAS,EAAE;AAE9E,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AACvB,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,QAAA,EAAU,KAAK,CAAA;AAEtC,IAAA,MAAM,MAAA,GAAS,OAAO,WAAA,CAAY;AAAA,MAChC,GAAA,EAAK,QAAA;AAAA,MACL,IAAA;AAAA,MACA,GAAA;AAAA,MACA,QAAQ,IAAA,CAAK,MAAA;AAAA,MACb,QAAA,EAAU;AAAA,KACX,CAAA;AACD,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAE9B,IAAA,MAAM,EAAE,MAAM,OAAA,EAAS,MAAA,EAAQ,YAAY,QAAA,EAAU,MAAA,EAAQ,QAAQ,CAAA,EAAE;AAAA,EACzE;AACF;AAEA,eAAe,aAAa,GAAA,EAAqC;AAC/D,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,OAAO,aAAkB,CAAA;AAChD,EAAA,MAAM,UAAA,GAAa,CAAC,kBAAA,EAAoB,gBAAA,EAAkB,eAAe,CAAA;AACzE,EAAA,KAAA,MAAW,KAAK,UAAA,EAAY;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAUC,IAAA,CAAA,IAAA,CAAK,GAAA,EAAK,CAAC,CAAC,CAAA;AAC5B,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,QAAA;AACjC,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,MAAA;AAC/B,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,OAAO,CAAA,EAAG,OAAO,OAAA;AAAA,IAClC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,SAAA,CAAU,QAAgB,KAAA,EAA4B;AAC7D,EAAA,MAAM,OAAiB,EAAC;AACxB,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,IAAW,GAAA;AAEjC,EAAA,QAAQ,MAAA;AAAQ,IACd,KAAK,QAAA;AACH,MAAA,IAAA,CAAK,IAAA,CAAK,OAAO,oBAAoB,CAAA;AACrC,MAAA,IAAI,MAAM,KAAA,EAAO;AACf,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,EAAA;AACV,QAAA,IAAA,CAAK,KAAK,OAAO,CAAA;AAAA,MACnB;AACA,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA;AAC1C,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,mBAAA,EAAqB,MAAM,IAAI,CAAA;AACzD,MAAA,IAAA,CAAK,IAAA,CAAK,eAAA,EAAiB,MAAA,CAAO,OAAO,CAAC,CAAA;AAC1C,MAAA;AAAA,IACF,KAAK,MAAA;AACH,MAAA,IAAA,CAAK,KAAK,WAAW,CAAA;AACrB,MAAA,IAAI,KAAA,CAAM,KAAA,EAAO,IAAA,CAAK,IAAA,CAAK,SAAS,CAAA;AACpC,MAAA,IAAI,KAAA,CAAM,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,YAAY,CAAA;AAC1C,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,mBAAA,EAAqB,MAAM,IAAI,CAAA;AACzD,MAAA,IAAA,CAAK,IAAA,CAAK,eAAA,EAAiB,MAAA,CAAO,OAAO,CAAC,CAAA;AAC1C,MAAA;AAAA,IACF,KAAK,OAAA;AACH,MAAA,IAAA,CAAK,IAAA,CAAK,cAAc,MAAM,CAAA;AAC9B,MAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,IAAI,CAAA;AAC9C,MAAA,IAAA,CAAK,IAAA,CAAK,WAAA,EAAa,MAAA,CAAO,OAAO,CAAC,CAAA;AACtC,MAAA;AAAA;AAGJ,EAAA,IAAI,MAAM,KAAA,EAAO;AACf,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAA,GAAI,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,GAAG,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,GAAG,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA;AAAA,EAC/C;AAEA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,WAAA,CACP,MAAA,EACA,MAAA,EACA,QAAA,EACY;AACZ,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,MAAA,GAAS,MAAA,CAAO,MAAA;AAEnC,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,IAAI,MAAA,GAAS,CAAA;AAEb,EAAA,IAAI,WAAW,QAAA,EAAU;AACvB,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,cAAc,CAAA;AAC5C,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,cAAc,CAAA;AAC5C,IAAA,IAAI,WAAA,GAAc,CAAC,CAAA,EAAG,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA,EAAG,EAAE,CAAA;AACjE,IAAA,IAAI,WAAA,GAAc,CAAC,CAAA,EAAG,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA,EAAG,EAAE,CAAA;AACjE,IAAA,SAAA,GAAY,MAAA,GAAS,MAAA;AAAA,EACvB,CAAA,MAAA,IAAW,WAAW,MAAA,EAAQ;AAC5B,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,8BAA8B,CAAA;AAC5D,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,yBAAyB,CAAA;AACvD,IAAA,MAAM,WAAA,GAAc,GAAA,CAAI,KAAA,CAAM,yBAAyB,CAAA;AACvD,IAAA,SAAA,GAAY,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AACvD,IAAA,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AACpD,IAAA,MAAA,GAAS,OAAO,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA,IAAK,KAAK,EAAE,CAAA;AAAA,EACtD;AAEA,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,WAAW,MAAA,CAAO,QAAA;AAAA,IAClB,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA,EAAa,QAAA;AAAA,IACb,MAAA,EAAQ,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,KAAA,IAAS,EAAA;AAAA,IACzC,WAAW,MAAA,CAAO;AAAA,GACpB;AACF","file":"test.js","sourcesContent":["import { spawn } from 'node:child_process';\nimport { buildChildEnv } from '@wrongstack/core';\nimport type { ToolProgressEvent } from '@wrongstack/core';\n\nexport interface SpawnStreamResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n truncated: boolean;\n error?: string;\n}\n\nexport interface SpawnStreamOptions {\n cmd: string;\n args: string[];\n cwd: string;\n signal: AbortSignal;\n maxBytes?: number;\n /** Bytes of new stdout/stderr to accumulate before yielding a `partial_output` event. */\n flushBytes?: number;\n}\n\n/**\n * Spawn a child process and yield `partial_output` progress events as\n * stdout/stderr arrive (batched by byte threshold), then return the full\n * buffered result. Shared between install/lint/format/typecheck/test/audit\n * so the TUI live tail sees consistent progress regardless of which tool\n * is running.\n */\nexport async function* spawnStream(\n opts: SpawnStreamOptions,\n): AsyncGenerator<ToolProgressEvent, SpawnStreamResult> {\n const max = opts.maxBytes ?? 200_000;\n const flushAt = opts.flushBytes ?? 4 * 1024;\n let stdout = '';\n let stderr = '';\n let pending = '';\n let error: string | undefined;\n\n const child = spawn(opts.cmd, opts.args, {\n cwd: opts.cwd,\n signal: opts.signal,\n env: buildChildEnv(),\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n type Chunk = { kind: 'out' | 'err' | 'close' | 'error'; data: string; code?: number };\n const queue: Chunk[] = [];\n let waiter: (() => void) | undefined;\n const wake = () => {\n if (waiter) {\n const w = waiter;\n waiter = undefined;\n w();\n }\n };\n\n child.stdout?.on('data', (c) => {\n const s = c.toString();\n if (stdout.length < max) stdout += s;\n queue.push({ kind: 'out', data: s });\n wake();\n });\n child.stderr?.on('data', (c) => {\n const s = c.toString();\n if (stderr.length < max) stderr += s;\n queue.push({ kind: 'err', data: s });\n wake();\n });\n child.on('error', (e) => {\n error = e.message;\n queue.push({ kind: 'error', data: e.message });\n wake();\n });\n child.on('close', (code) => {\n queue.push({ kind: 'close', data: '', code: code ?? 0 });\n wake();\n });\n\n let exitCode = 0;\n let spawnFailed = false;\n for (;;) {\n while (queue.length === 0) {\n await new Promise<void>((resolve) => {\n waiter = resolve;\n });\n }\n const chunk = queue.shift()!;\n if (chunk.kind === 'close') {\n // If we already saw a spawn error (ENOENT etc.), keep exitCode=1\n // rather than the negative platform code Node fabricates.\n if (!spawnFailed) exitCode = chunk.code ?? 0;\n break;\n }\n if (chunk.kind === 'error') {\n spawnFailed = true;\n exitCode = 1;\n // close usually follows\n continue;\n }\n pending += chunk.data;\n if (pending.length >= flushAt) {\n yield { type: 'partial_output', text: pending };\n pending = '';\n }\n }\n if (pending.length > 0) {\n yield { type: 'partial_output', text: pending };\n }\n\n return {\n stdout,\n stderr,\n exitCode,\n truncated: stdout.length >= max || stderr.length >= max,\n error,\n };\n}\n","import * as path from 'node:path';\nimport type { Context } from '@wrongstack/core';\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const root = path.resolve(ctx.projectRoot);\n const target = path.resolve(absPath);\n const rel = path.relative(root, target);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\n }\n return target;\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n","import * as path from 'node:path';\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { spawnStream } from './_spawn-stream.js';\nimport { safeResolve } from './_util.js';\n\ninterface TestInput {\n files?: string | string[];\n runner?: 'vitest' | 'jest' | 'mocha' | 'auto';\n watch?: boolean;\n coverage?: boolean;\n cwd?: string;\n grep?: string;\n timeout?: number;\n}\n\ninterface TestOutput {\n runner: string;\n exit_code: number;\n tests_run: number;\n passed: number;\n failed: number;\n duration_ms: number;\n output: string;\n truncated: boolean;\n}\n\nexport const testTool: Tool<TestInput, TestOutput> = {\n name: 'test',\n description: 'Run tests with vitest, jest, or mocha. Returns pass/fail counts and output.',\n usageHint:\n 'Set `files` for specific tests. `watch` enables watch mode. `coverage` generates coverage report. `grep` filters by name.',\n permission: 'confirm',\n mutating: false,\n timeoutMs: 120_000,\n inputSchema: {\n type: 'object',\n properties: {\n files: {\n type: 'string',\n description: 'Test files: single path, comma-separated list, or glob (e.g. \"**/*.test.ts\")',\n },\n runner: {\n type: 'string',\n enum: ['vitest', 'jest', 'mocha', 'auto'],\n description: 'Test runner (default: auto-detect)',\n },\n watch: { type: 'boolean', description: 'Run in watch mode (default: false)' },\n coverage: { type: 'boolean', description: 'Generate coverage report (default: false)' },\n cwd: { type: 'string', description: 'Working directory (default: cwd)' },\n grep: { type: 'string', description: 'Filter tests by name pattern (default: none)' },\n timeout: { type: 'integer', description: 'Test timeout in ms (default: 30000)' },\n },\n },\n async execute(input, ctx, opts) {\n let final: TestOutput | undefined;\n for await (const ev of testTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('test: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<TestOutput>> {\n const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;\n const runner = input.runner ?? 'auto';\n\n const detected = runner === 'auto' ? await detectRunner(cwd) : runner;\n if (!detected) {\n yield {\n type: 'final',\n output: {\n runner: 'none',\n exit_code: 1,\n tests_run: 0,\n passed: 0,\n failed: 0,\n duration_ms: 0,\n output: 'No test runner found (vitest.config.ts, jest.config.js, .mocharc.json)',\n truncated: false,\n },\n };\n return;\n }\n\n yield { type: 'log', text: `Running ${detected}…`, data: { runner: detected } };\n\n const start = Date.now();\n const args = buildArgs(detected, input);\n\n const result = yield* spawnStream({\n cmd: detected,\n args,\n cwd,\n signal: opts.signal,\n maxBytes: 200_000,\n });\n const duration = Date.now() - start;\n\n yield { type: 'final', output: parseResult(detected, result, duration) };\n },\n};\n\nasync function detectRunner(cwd: string): Promise<string | null> {\n const { stat } = await import('node:fs/promises');\n const candidates = ['vitest.config.ts', 'jest.config.js', '.mocharc.json'];\n for (const f of candidates) {\n try {\n await stat(path.join(cwd, f));\n if (f.includes('vitest')) return 'vitest';\n if (f.includes('jest')) return 'jest';\n if (f.includes('mocha')) return 'mocha';\n } catch {\n // continue\n }\n }\n return 'vitest';\n}\n\nfunction buildArgs(runner: string, input: TestInput): string[] {\n const args: string[] = [];\n const timeout = input.timeout ?? 30000;\n\n switch (runner) {\n case 'vitest':\n args.push('run', '--reporter=verbose');\n if (input.watch) {\n args[1] = '';\n args.push('watch');\n }\n if (input.coverage) args.push('--coverage');\n if (input.grep) args.push('--testNamePattern', input.grep);\n args.push('--testTimeout', String(timeout));\n break;\n case 'jest':\n args.push('--verbose');\n if (input.watch) args.push('--watch');\n if (input.coverage) args.push('--coverage');\n if (input.grep) args.push('--testPathPattern', input.grep);\n args.push('--testTimeout', String(timeout));\n break;\n case 'mocha':\n args.push('--reporter', 'spec');\n if (input.grep) args.push('--grep', input.grep);\n args.push('--timeout', String(timeout));\n break;\n }\n\n if (input.files) {\n const files = Array.isArray(input.files) ? input.files : input.files.split(',');\n args.push('--', ...files.map((f) => f.trim()));\n }\n\n return args;\n}\n\nfunction parseResult(\n runner: string,\n result: { stdout: string; stderr: string; exitCode: number; truncated: boolean; error?: string },\n duration: number,\n): TestOutput {\n const out = result.stdout + result.stderr;\n\n let tests_run = 0;\n let passed = 0;\n let failed = 0;\n\n if (runner === 'vitest') {\n const passedMatch = out.match(/(\\d+) passed/);\n const failedMatch = out.match(/(\\d+) failed/);\n if (passedMatch?.[1]) passed = Number.parseInt(passedMatch[1], 10);\n if (failedMatch?.[1]) failed = Number.parseInt(failedMatch[1], 10);\n tests_run = passed + failed;\n } else if (runner === 'jest') {\n const suitesMatch = out.match(/Test Suites:\\s+(\\d+)\\s+total/);\n const passedMatch = out.match(/Tests:\\s+(\\d+)\\s+passed/);\n const failedMatch = out.match(/Tests:\\s+(\\d+)\\s+failed/);\n tests_run = Number.parseInt(suitesMatch?.[1] ?? '0', 10);\n passed = Number.parseInt(passedMatch?.[1] ?? '0', 10);\n failed = Number.parseInt(failedMatch?.[1] ?? '0', 10);\n }\n\n return {\n runner,\n exit_code: result.exitCode,\n tests_run,\n passed,\n failed,\n duration_ms: duration,\n output: result.stdout || result.error || '',\n truncated: result.truncated,\n };\n}\n"]}
package/dist/todo.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/todo.ts"],"names":[],"mappings":";AAWO,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EAAa,wDAAA;AAAA,EACb,SAAA,EACE,uMAAA;AAAA,EACF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,OAAA;AAAA,QACN,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,QAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACV,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,YACrB,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,YAC1B,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,MAAM,CAAC,SAAA,EAAW,aAAA,EAAe,WAAW,CAAA,EAAE;AAAA,YACxE,UAAA,EAAY,EAAE,IAAA,EAAM,QAAA;AAAS,WAC/B;AAAA,UACA,QAAA,EAAU,CAAC,IAAA,EAAM,SAAA,EAAW,QAAQ;AAAA;AACtC;AACF,KACF;AAAA,IACA,QAAA,EAAU,CAAC,OAAO;AAAA,GACpB;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK;AACxB,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAA,EAAG;AAChC,MAAA,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,IAChD;AACA,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAqB,OAAA,CAAQ,CAAA,EAAG,EAAA,IAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAClF,IAAA,MAAM,aAAa,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,aAAa,CAAA;AACjE,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AAEzB,MAAA,IAAI,cAAA,GAAiB,KAAA;AACrB,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,IAAA,CAAK,WAAW,aAAA,EAAe;AACjC,UAAA,IAAI,cAAA,OAAqB,MAAA,GAAS,SAAA;AAClC,UAAA,cAAA,GAAiB,IAAA;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA,CAAI,KAAA,CAAM,aAAa,KAAK,CAAA;AAC5B,IAAA,OAAO;AAAA,MACL,OAAO,KAAA,CAAM,MAAA;AAAA,MACb,WAAA,EAAa,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,aAAa,CAAA,CAAE;AAAA,KAC/D;AAAA,EACF;AACF","file":"todo.js","sourcesContent":["import type { Tool, TodoItem } from '@wrongstack/core';\n\ninterface TodoInput {\n todos: TodoItem[];\n}\n\ninterface TodoOutput {\n count: number;\n in_progress: number;\n}\n\nexport const todoTool: Tool<TodoInput, TodoOutput> = {\n name: 'todo',\n description: 'Replace the current todo list with a new set of items.',\n usageHint:\n 'Use for multi-step tasks. Replace the full list on each call. At most ONE task may be in_progress at a time. Items have id, content, status (pending|in_progress|completed), and optional activeForm.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 1_000,\n inputSchema: {\n type: 'object',\n properties: {\n todos: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n content: { type: 'string' },\n status: { type: 'string', enum: ['pending', 'in_progress', 'completed'] },\n activeForm: { type: 'string' },\n },\n required: ['id', 'content', 'status'],\n },\n },\n },\n required: ['todos'],\n },\n async execute(input, ctx) {\n if (!Array.isArray(input?.todos)) {\n throw new Error('todo: todos must be an array');\n }\n const items = input.todos.filter((t): t is TodoItem => Boolean(t?.id && t.content));\n const inProgress = items.filter((t) => t.status === 'in_progress');\n if (inProgress.length > 1) {\n // Keep only the first as in_progress, mark rest pending\n let seenInProgress = false;\n for (const item of items) {\n if (item.status === 'in_progress') {\n if (seenInProgress) item.status = 'pending';\n seenInProgress = true;\n }\n }\n }\n ctx.state.replaceTodos(items);\n return {\n count: items.length,\n in_progress: items.filter((t) => t.status === 'in_progress').length,\n };\n },\n};\n"]}
1
+ {"version":3,"sources":["../src/todo.ts"],"names":[],"mappings":";AAWO,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EAAa,wDAAA;AAAA,EACb,SAAA,EACE,uMAAA;AAAA,EACF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,OAAA;AAAA,QACN,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,QAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACV,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,YACrB,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,YAC1B,MAAA,EAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,MAAM,CAAC,SAAA,EAAW,aAAA,EAAe,WAAW,CAAA,EAAE;AAAA,YACxE,UAAA,EAAY,EAAE,IAAA,EAAM,QAAA;AAAS,WAC/B;AAAA,UACA,QAAA,EAAU,CAAC,IAAA,EAAM,SAAA,EAAW,QAAQ;AAAA;AACtC;AACF,KACF;AAAA,IACA,QAAA,EAAU,CAAC,OAAO;AAAA,GACpB;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK;AACxB,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAA,EAAG;AAChC,MAAA,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,IAChD;AACA,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAqB,OAAA,CAAQ,CAAA,EAAG,EAAA,IAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAClF,IAAA,MAAM,aAAa,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,aAAa,CAAA;AACjE,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AAEzB,MAAA,IAAI,cAAA,GAAiB,KAAA;AACrB,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,IAAA,CAAK,WAAW,aAAA,EAAe;AACjC,UAAA,IAAI,cAAA,OAAqB,MAAA,GAAS,SAAA;AAClC,UAAA,cAAA,GAAiB,IAAA;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AACA,IAAA,GAAA,CAAI,KAAA,CAAM,aAAa,KAAK,CAAA;AAC5B,IAAA,OAAO;AAAA,MACL,OAAO,KAAA,CAAM,MAAA;AAAA,MACb,WAAA,EAAa,MAAM,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,aAAa,CAAA,CAAE;AAAA,KAC/D;AAAA,EACF;AACF","file":"todo.js","sourcesContent":["import type { TodoItem, Tool } from '@wrongstack/core';\n\ninterface TodoInput {\n todos: TodoItem[];\n}\n\ninterface TodoOutput {\n count: number;\n in_progress: number;\n}\n\nexport const todoTool: Tool<TodoInput, TodoOutput> = {\n name: 'todo',\n description: 'Replace the current todo list with a new set of items.',\n usageHint:\n 'Use for multi-step tasks. Replace the full list on each call. At most ONE task may be in_progress at a time. Items have id, content, status (pending|in_progress|completed), and optional activeForm.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 1_000,\n inputSchema: {\n type: 'object',\n properties: {\n todos: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n content: { type: 'string' },\n status: { type: 'string', enum: ['pending', 'in_progress', 'completed'] },\n activeForm: { type: 'string' },\n },\n required: ['id', 'content', 'status'],\n },\n },\n },\n required: ['todos'],\n },\n async execute(input, ctx) {\n if (!Array.isArray(input?.todos)) {\n throw new Error('todo: todos must be an array');\n }\n const items = input.todos.filter((t): t is TodoItem => Boolean(t?.id && t.content));\n const inProgress = items.filter((t) => t.status === 'in_progress');\n if (inProgress.length > 1) {\n // Keep only the first as in_progress, mark rest pending\n let seenInProgress = false;\n for (const item of items) {\n if (item.status === 'in_progress') {\n if (seenInProgress) item.status = 'pending';\n seenInProgress = true;\n }\n }\n }\n ctx.state.replaceTodos(items);\n return {\n count: items.length,\n in_progress: items.filter((t) => t.status === 'in_progress').length,\n };\n },\n};\n"]}
package/dist/tool-help.js CHANGED
@@ -40,14 +40,16 @@ var toolHelpTool = {
40
40
  return {
41
41
  tool: tool.name,
42
42
  help: formatToolHelp(tool, format, includeExamples),
43
- tools: [{
44
- name: tool.name,
45
- description: tool.description,
46
- usageHint: tool.usageHint ?? "",
47
- inputSchema: tool.inputSchema,
48
- permission: tool.permission,
49
- mutating: tool.mutating
50
- }],
43
+ tools: [
44
+ {
45
+ name: tool.name,
46
+ description: tool.description,
47
+ usageHint: tool.usageHint ?? "",
48
+ inputSchema: tool.inputSchema,
49
+ permission: tool.permission,
50
+ mutating: tool.mutating
51
+ }
52
+ ],
51
53
  total: 1
52
54
  };
53
55
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tool-help.ts"],"names":[],"mappings":"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","file":"tool-help.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface ToolHelpInput {\n tool?: string;\n format?: 'short' | 'full' | 'markdown';\n include_examples?: boolean;\n}\n\ninterface ToolHelpOutput {\n tool?: string;\n help: string;\n tools: {\n name: string;\n description: string;\n usageHint: string;\n inputSchema: unknown;\n permission: string;\n mutating: boolean;\n }[];\n total: number;\n}\n\nexport const toolHelpTool: Tool<ToolHelpInput, ToolHelpOutput> = {\n name: 'tool_help',\n description:\n 'Get help and usage information for a specific tool or list all available tools.',\n usageHint:\n 'Set `tool` for specific help. Omit to list all tools. `format`: short (one-liner), full (schema), markdown (formatted).',\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n inputSchema: {\n type: 'object',\n properties: {\n tool: {\n type: 'string',\n description: 'Tool name to get help for (omit for all tools)',\n },\n format: {\n type: 'string',\n enum: ['short', 'full', 'markdown'],\n description: 'Output format (default: short)',\n },\n include_examples: {\n type: 'boolean',\n description: 'Include usage examples in output (default: false)',\n },\n },\n },\n async execute(input, ctx) {\n const format = input.format ?? 'short';\n const includeExamples = input.include_examples ?? false;\n\n if (input.tool) {\n const tool = ctx.tools.find((t: Tool) => t.name === input.tool);\n if (!tool) {\n return {\n tool: input.tool,\n help: `No tool found with name \"${input.tool}\"`,\n tools: [],\n total: 0,\n };\n }\n\n return {\n tool: tool.name,\n help: formatToolHelp(tool, format, includeExamples),\n tools: [{\n name: tool.name,\n description: tool.description,\n usageHint: tool.usageHint ?? '',\n inputSchema: tool.inputSchema,\n permission: tool.permission,\n mutating: tool.mutating,\n }],\n total: 1,\n };\n }\n\n const allTools = ctx.tools.map((t: Tool) => ({\n name: t.name,\n description: t.description,\n usageHint: t.usageHint ?? '',\n inputSchema: format === 'full' ? t.inputSchema : undefined,\n permission: t.permission,\n mutating: t.mutating,\n }));\n\n return {\n help: format === 'markdown' ? formatAllToolsMarkdown(allTools) : formatAllToolsShort(allTools),\n tools: allTools,\n total: allTools.length,\n };\n },\n};\n\nfunction formatToolHelp(tool: Tool, format: string, includeExamples: boolean): string {\n const lines: string[] = [];\n\n if (format === 'short') {\n lines.push(`${tool.name}: ${tool.description}`);\n if (tool.usageHint) lines.push(`Hint: ${tool.usageHint}`);\n return lines.join('\\n');\n }\n\n if (format === 'markdown') {\n lines.push(`## ${tool.name}`);\n lines.push('');\n lines.push(tool.description);\n lines.push('');\n lines.push('**Permission:** ' + tool.permission);\n lines.push('**Mutating:** ' + (tool.mutating ? 'yes' : 'no'));\n if (tool.usageHint) {\n lines.push('');\n lines.push('### Usage Hint');\n lines.push(tool.usageHint);\n }\n if (includeExamples && tool.inputSchema) {\n lines.push('');\n lines.push('### Input Schema');\n lines.push('```json');\n lines.push(JSON.stringify(tool.inputSchema, null, 2));\n lines.push('```');\n }\n return lines.join('\\n');\n }\n\n lines.push(`Tool: ${tool.name}`);\n lines.push(`Description: ${tool.description}`);\n lines.push(`Permission: ${tool.permission}`);\n lines.push(`Mutating: ${tool.mutating}`);\n if (tool.usageHint) lines.push(`Usage: ${tool.usageHint}`);\n if (format === 'full' && tool.inputSchema) {\n lines.push('Schema: ' + JSON.stringify(tool.inputSchema, null, 2));\n }\n return lines.join('\\n');\n}\n\nfunction formatAllToolsShort(tools: { name: string; description: string }[]): string {\n return tools\n .map((t) => ` ${t.name.padEnd(16)} ${t.description}`)\n .join('\\n');\n}\n\nfunction formatAllToolsMarkdown(tools: { name: string; description: string; usageHint: string }[]): string {\n const lines: string[] = ['## Available Tools', ''];\n lines.push('| Tool | Description |');\n lines.push('|------|-------------|');\n for (const t of tools) {\n lines.push(`| \\`${t.name}\\` | ${t.description} |`);\n }\n return lines.join('\\n');\n}"]}
1
+ {"version":3,"sources":["../src/tool-help.ts"],"names":[],"mappings":"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","file":"tool-help.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface ToolHelpInput {\n tool?: string;\n format?: 'short' | 'full' | 'markdown';\n include_examples?: boolean;\n}\n\ninterface ToolHelpOutput {\n tool?: string;\n help: string;\n tools: {\n name: string;\n description: string;\n usageHint: string;\n inputSchema: unknown;\n permission: string;\n mutating: boolean;\n }[];\n total: number;\n}\n\nexport const toolHelpTool: Tool<ToolHelpInput, ToolHelpOutput> = {\n name: 'tool_help',\n description: 'Get help and usage information for a specific tool or list all available tools.',\n usageHint:\n 'Set `tool` for specific help. Omit to list all tools. `format`: short (one-liner), full (schema), markdown (formatted).',\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n inputSchema: {\n type: 'object',\n properties: {\n tool: {\n type: 'string',\n description: 'Tool name to get help for (omit for all tools)',\n },\n format: {\n type: 'string',\n enum: ['short', 'full', 'markdown'],\n description: 'Output format (default: short)',\n },\n include_examples: {\n type: 'boolean',\n description: 'Include usage examples in output (default: false)',\n },\n },\n },\n async execute(input, ctx) {\n const format = input.format ?? 'short';\n const includeExamples = input.include_examples ?? false;\n\n if (input.tool) {\n const tool = ctx.tools.find((t: Tool) => t.name === input.tool);\n if (!tool) {\n return {\n tool: input.tool,\n help: `No tool found with name \"${input.tool}\"`,\n tools: [],\n total: 0,\n };\n }\n\n return {\n tool: tool.name,\n help: formatToolHelp(tool, format, includeExamples),\n tools: [\n {\n name: tool.name,\n description: tool.description,\n usageHint: tool.usageHint ?? '',\n inputSchema: tool.inputSchema,\n permission: tool.permission,\n mutating: tool.mutating,\n },\n ],\n total: 1,\n };\n }\n\n const allTools = ctx.tools.map((t: Tool) => ({\n name: t.name,\n description: t.description,\n usageHint: t.usageHint ?? '',\n inputSchema: format === 'full' ? t.inputSchema : undefined,\n permission: t.permission,\n mutating: t.mutating,\n }));\n\n return {\n help:\n format === 'markdown' ? formatAllToolsMarkdown(allTools) : formatAllToolsShort(allTools),\n tools: allTools,\n total: allTools.length,\n };\n },\n};\n\nfunction formatToolHelp(tool: Tool, format: string, includeExamples: boolean): string {\n const lines: string[] = [];\n\n if (format === 'short') {\n lines.push(`${tool.name}: ${tool.description}`);\n if (tool.usageHint) lines.push(`Hint: ${tool.usageHint}`);\n return lines.join('\\n');\n }\n\n if (format === 'markdown') {\n lines.push(`## ${tool.name}`);\n lines.push('');\n lines.push(tool.description);\n lines.push('');\n lines.push('**Permission:** ' + tool.permission);\n lines.push('**Mutating:** ' + (tool.mutating ? 'yes' : 'no'));\n if (tool.usageHint) {\n lines.push('');\n lines.push('### Usage Hint');\n lines.push(tool.usageHint);\n }\n if (includeExamples && tool.inputSchema) {\n lines.push('');\n lines.push('### Input Schema');\n lines.push('```json');\n lines.push(JSON.stringify(tool.inputSchema, null, 2));\n lines.push('```');\n }\n return lines.join('\\n');\n }\n\n lines.push(`Tool: ${tool.name}`);\n lines.push(`Description: ${tool.description}`);\n lines.push(`Permission: ${tool.permission}`);\n lines.push(`Mutating: ${tool.mutating}`);\n if (tool.usageHint) lines.push(`Usage: ${tool.usageHint}`);\n if (format === 'full' && tool.inputSchema) {\n lines.push('Schema: ' + JSON.stringify(tool.inputSchema, null, 2));\n }\n return lines.join('\\n');\n}\n\nfunction formatAllToolsShort(tools: { name: string; description: string }[]): string {\n return tools.map((t) => ` ${t.name.padEnd(16)} ${t.description}`).join('\\n');\n}\n\nfunction formatAllToolsMarkdown(\n tools: { name: string; description: string; usageHint: string }[],\n): string {\n const lines: string[] = ['## Available Tools', ''];\n lines.push('| Tool | Description |');\n lines.push('|------|-------------|');\n for (const t of tools) {\n lines.push(`| \\`${t.name}\\` | ${t.description} |`);\n }\n return lines.join('\\n');\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tool-search.ts"],"names":[],"mappings":";AAqBO,IAAM,cAAA,GAA0D;AAAA,EACrE,IAAA,EAAM,aAAA;AAAA,EACN,WAAA,EACE,wFAAA;AAAA,EACF,SAAA,EACE,qJAAA;AAAA,EACF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,OAAA;AAAA,QACN,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QACxB,WAAA,EAAa;AAAA,OACf;AAAA,MACA,UAAA,EAAY;AAAA,QACV,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,MAAA,EAAQ,SAAA,EAAW,MAAM,CAAA;AAAA,QAChC,WAAA,EAAa;AAAA,OACf;AAAA,MACA,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa,yCAAA;AAAA,QACb,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAS;AAAA;AACX;AACF,GACF;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK;AACxB,IAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,IAAI,GAAG,CAAA;AAC7C,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAClB,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,EAAO,WAAA,EAAY,IAAK,EAAA;AAE5C,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAY;AACzC,MAAA,IAAI,SAAS,CAAC,CAAA,CAAE,IAAA,CAAK,WAAA,GAAc,QAAA,CAAS,KAAK,CAAA,IAAK,CAAC,EAAE,WAAA,CAAY,WAAA,EAAY,CAAE,QAAA,CAAS,KAAK,CAAA,EAAG;AAClG,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,IAAI,KAAA,CAAM,UAAA,IAAc,CAAA,CAAE,UAAA,KAAe,MAAM,UAAA,EAAY;AACzD,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,IAAI,OAAO,KAAA,CAAM,QAAA,KAAa,aAAa,CAAA,CAAE,QAAA,KAAa,MAAM,QAAA,EAAU;AACxE,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,MAAM,OAAA,GAAU,SAAS,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,MAAa;AAAA,MACzD,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,aAAa,CAAA,CAAE,WAAA;AAAA,MACf,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,UAAU,CAAA,CAAE;AAAA,KACd,CAAE,CAAA;AAEF,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAA;AAAA,MACP,OAAO,QAAA,CAAS,MAAA;AAAA,MAChB,SAAA,EAAW,SAAS,MAAA,GAAS;AAAA,KAC/B;AAAA,EACF;AACF","file":"tool-search.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\r\n\r\ninterface ToolSearchInput {\r\n query?: string;\r\n tags?: string[];\r\n permission?: 'auto' | 'confirm' | 'deny';\r\n mutating?: boolean;\r\n limit?: number;\r\n}\r\n\r\ninterface ToolSearchOutput {\r\n tools: {\r\n name: string;\r\n description: string;\r\n permission: string;\r\n mutating: boolean;\r\n }[];\r\n total: number;\r\n truncated: boolean;\r\n}\r\n\r\nexport const toolSearchTool: Tool<ToolSearchInput, ToolSearchOutput> = {\r\n name: 'tool_search',\r\n description:\r\n 'Search available tools by name, description, tags, permission level, or mutating flag.',\r\n usageHint:\r\n 'Set `query` for keyword search. `tags` to filter by category. `permission` to filter by required permission. `mutating` to filter by mutating flag.',\r\n permission: 'auto',\r\n mutating: false,\r\n timeoutMs: 1_000,\r\n inputSchema: {\r\n type: 'object',\r\n properties: {\r\n query: {\r\n type: 'string',\r\n description: 'Search query for tool name or description',\r\n },\r\n tags: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n description: 'Filter by tags (e.g. \"filesystem\", \"network\", \"dev\")',\r\n },\r\n permission: {\r\n type: 'string',\r\n enum: ['auto', 'confirm', 'deny'],\r\n description: 'Filter by required permission level',\r\n },\r\n mutating: {\r\n type: 'boolean',\r\n description: 'Filter by mutating flag (true=filters that modify, false=read-only)',\r\n },\r\n limit: {\r\n type: 'integer',\r\n description: 'Maximum results to return (default: 20)',\r\n minimum: 1,\r\n maximum: 100,\r\n },\r\n },\r\n },\r\n async execute(input, ctx) {\r\n const limit = Math.min(input.limit ?? 20, 100);\r\n const tools = ctx.tools;\r\n const query = input.query?.toLowerCase() ?? '';\r\n\r\n const filtered = tools.filter((t: Tool) => {\r\n if (query && !t.name.toLowerCase().includes(query) && !t.description.toLowerCase().includes(query)) {\r\n return false;\r\n }\r\n if (input.permission && t.permission !== input.permission) {\r\n return false;\r\n }\r\n if (typeof input.mutating === 'boolean' && t.mutating !== input.mutating) {\r\n return false;\r\n }\r\n return true;\r\n });\r\n\r\n const results = filtered.slice(0, limit).map((t: Tool) => ({\r\n name: t.name,\r\n description: t.description,\r\n permission: t.permission,\r\n mutating: t.mutating,\r\n }));\r\n\r\n return {\r\n tools: results,\r\n total: filtered.length,\r\n truncated: filtered.length > limit,\r\n };\r\n },\r\n};"]}
1
+ {"version":3,"sources":["../src/tool-search.ts"],"names":[],"mappings":";AAqBO,IAAM,cAAA,GAA0D;AAAA,EACrE,IAAA,EAAM,aAAA;AAAA,EACN,WAAA,EACE,wFAAA;AAAA,EACF,SAAA,EACE,qJAAA;AAAA,EACF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,OAAA;AAAA,QACN,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QACxB,WAAA,EAAa;AAAA,OACf;AAAA,MACA,UAAA,EAAY;AAAA,QACV,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,MAAA,EAAQ,SAAA,EAAW,MAAM,CAAA;AAAA,QAChC,WAAA,EAAa;AAAA,OACf;AAAA,MACA,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa,yCAAA;AAAA,QACb,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAS;AAAA;AACX;AACF,GACF;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK;AACxB,IAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,IAAI,GAAG,CAAA;AAC7C,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA;AAClB,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,EAAO,WAAA,EAAY,IAAK,EAAA;AAE5C,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAY;AACzC,MAAA,IACE,SACA,CAAC,CAAA,CAAE,IAAA,CAAK,WAAA,GAAc,QAAA,CAAS,KAAK,CAAA,IACpC,CAAC,EAAE,WAAA,CAAY,WAAA,EAAY,CAAE,QAAA,CAAS,KAAK,CAAA,EAC3C;AACA,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,IAAI,KAAA,CAAM,UAAA,IAAc,CAAA,CAAE,UAAA,KAAe,MAAM,UAAA,EAAY;AACzD,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,IAAI,OAAO,KAAA,CAAM,QAAA,KAAa,aAAa,CAAA,CAAE,QAAA,KAAa,MAAM,QAAA,EAAU;AACxE,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA;AAED,IAAA,MAAM,OAAA,GAAU,SAAS,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,MAAa;AAAA,MACzD,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,aAAa,CAAA,CAAE,WAAA;AAAA,MACf,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,UAAU,CAAA,CAAE;AAAA,KACd,CAAE,CAAA;AAEF,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAA;AAAA,MACP,OAAO,QAAA,CAAS,MAAA;AAAA,MAChB,SAAA,EAAW,SAAS,MAAA,GAAS;AAAA,KAC/B;AAAA,EACF;AACF","file":"tool-search.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface ToolSearchInput {\n query?: string;\n tags?: string[];\n permission?: 'auto' | 'confirm' | 'deny';\n mutating?: boolean;\n limit?: number;\n}\n\ninterface ToolSearchOutput {\n tools: {\n name: string;\n description: string;\n permission: string;\n mutating: boolean;\n }[];\n total: number;\n truncated: boolean;\n}\n\nexport const toolSearchTool: Tool<ToolSearchInput, ToolSearchOutput> = {\n name: 'tool_search',\n description:\n 'Search available tools by name, description, tags, permission level, or mutating flag.',\n usageHint:\n 'Set `query` for keyword search. `tags` to filter by category. `permission` to filter by required permission. `mutating` to filter by mutating flag.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 1_000,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query for tool name or description',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'Filter by tags (e.g. \"filesystem\", \"network\", \"dev\")',\n },\n permission: {\n type: 'string',\n enum: ['auto', 'confirm', 'deny'],\n description: 'Filter by required permission level',\n },\n mutating: {\n type: 'boolean',\n description: 'Filter by mutating flag (true=filters that modify, false=read-only)',\n },\n limit: {\n type: 'integer',\n description: 'Maximum results to return (default: 20)',\n minimum: 1,\n maximum: 100,\n },\n },\n },\n async execute(input, ctx) {\n const limit = Math.min(input.limit ?? 20, 100);\n const tools = ctx.tools;\n const query = input.query?.toLowerCase() ?? '';\n\n const filtered = tools.filter((t: Tool) => {\n if (\n query &&\n !t.name.toLowerCase().includes(query) &&\n !t.description.toLowerCase().includes(query)\n ) {\n return false;\n }\n if (input.permission && t.permission !== input.permission) {\n return false;\n }\n if (typeof input.mutating === 'boolean' && t.mutating !== input.mutating) {\n return false;\n }\n return true;\n });\n\n const results = filtered.slice(0, limit).map((t: Tool) => ({\n name: t.name,\n description: t.description,\n permission: t.permission,\n mutating: t.mutating,\n }));\n\n return {\n tools: results,\n total: filtered.length,\n truncated: filtered.length > limit,\n };\n },\n};\n"]}
package/dist/tool-use.js CHANGED
@@ -4,7 +4,7 @@ var toolUseTool = {
4
4
  description: "Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.",
5
5
  usageHint: "Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.",
6
6
  permission: "confirm",
7
- mutating: false,
7
+ mutating: true,
8
8
  timeoutMs: 6e4,
9
9
  inputSchema: {
10
10
  type: "object",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tool-use.ts"],"names":[],"mappings":";AAeO,IAAM,WAAA,GAAiD;AAAA,EAC5D,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EACE,2GAAA;AAAA,EACF,SAAA,EACE,gGAAA;AAAA,EACF,UAAA,EAAY,SAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA,QAAA,EAAU,CAAC,MAAM;AAAA,GACnB;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AAEvB,IAAA,IAAI,CAAC,OAAO,IAAA,EAAM;AAChB,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,iCAAA;AAAA,QACP,WAAA,EAAa;AAAA,OACf;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,IAAI,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAY,CAAA,CAAE,IAAA,KAAS,KAAA,CAAM,IAAI,CAAA;AAC9D,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,CAAA,gBAAA,EAAmB,KAAA,CAAM,IAAI,CAAA,WAAA,CAAA;AAAA,QACpC,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAKA,IAAA,IAAI,IAAA,CAAK,eAAe,MAAA,EAAQ;AAC9B,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,CAAA,gBAAA,EAAmB,KAAA,CAAM,IAAI,CAAA,qBAAA,CAAA;AAAA,QACpC,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAWA,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,MAAM,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA,EAAO,KAAK,IAAI,CAAA;AACxD,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,IAAA;AAAA,QACT,MAAA;AAAA,QACA,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF,SAAS,CAAA,EAAG;AACV,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,OAAO,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AAAA,QAChD,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAAA,EACF;AACF","file":"tool-use.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface ToolUseInput {\n tool: string;\n input: Record<string, unknown>;\n}\n\ninterface ToolUseOutput {\n tool: string;\n success: boolean;\n result?: unknown;\n error?: string;\n executionMs: number;\n}\n\nexport const toolUseTool: Tool<ToolUseInput, ToolUseOutput> = {\n name: 'tool_use',\n description:\n 'Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.',\n usageHint:\n 'Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.',\n permission: 'confirm',\n mutating: false,\n timeoutMs: 60_000,\n inputSchema: {\n type: 'object',\n properties: {\n tool: {\n type: 'string',\n description: 'Exact name of the tool to execute',\n },\n input: {\n type: 'object',\n description: 'Input parameters for the tool',\n },\n },\n required: ['tool'],\n },\n async execute(input, ctx, opts) {\n const start = Date.now();\n\n if (!input?.tool) {\n return {\n tool: 'unknown',\n success: false,\n error: 'tool_use: tool name is required',\n executionMs: 0,\n };\n }\n\n const tool = ctx.tools.find((t: Tool) => t.name === input.tool);\n if (!tool) {\n return {\n tool: input.tool,\n success: false,\n error: `tool_use: tool \"${input.tool}\" not found`,\n executionMs: Date.now() - start,\n };\n }\n\n // `deny` is a hard policy gate — bypassing it through a meta-tool\n // would defeat the whole point of the permission system. Keep this\n // check even though the outer `tool_use` already requires `confirm`.\n if (tool.permission === 'deny') {\n return {\n tool: input.tool,\n success: false,\n error: `tool_use: tool \"${input.tool}\" is denied by policy`,\n executionMs: Date.now() - start,\n };\n }\n\n // Note: inner `permission === 'confirm'` is intentionally NOT short-\n // circuited here. The outer `tool_use` itself has `permission: 'confirm'`,\n // so the user already saw the full args (including which inner tool will\n // run, and with what input) before approving the meta-call. Duplicating\n // the check inside execute() turned every confirm-tool dispatch through\n // `tool_use` into a hard failure — the model would see \"requires\n // confirmation\" with no way to proceed, even after the user said yes.\n // `batch_tool_use` already follows this same model.\n\n try {\n const result = await tool.execute(input.input, ctx, opts);\n return {\n tool: input.tool,\n success: true,\n result,\n executionMs: Date.now() - start,\n };\n } catch (e) {\n return {\n tool: input.tool,\n success: false,\n error: e instanceof Error ? e.message : String(e),\n executionMs: Date.now() - start,\n };\n }\n },\n};"]}
1
+ {"version":3,"sources":["../src/tool-use.ts"],"names":[],"mappings":";AAeO,IAAM,WAAA,GAAiD;AAAA,EAC5D,IAAA,EAAM,UAAA;AAAA,EACN,WAAA,EACE,2GAAA;AAAA,EACF,SAAA,EACE,gGAAA;AAAA,EACF,UAAA,EAAY,SAAA;AAAA,EACZ,QAAA,EAAU,IAAA;AAAA,EACV,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA,QAAA,EAAU,CAAC,MAAM;AAAA,GACnB;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,EAAI;AAEvB,IAAA,IAAI,CAAC,OAAO,IAAA,EAAM;AAChB,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,iCAAA;AAAA,QACP,WAAA,EAAa;AAAA,OACf;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,IAAI,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAY,CAAA,CAAE,IAAA,KAAS,KAAA,CAAM,IAAI,CAAA;AAC9D,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,CAAA,gBAAA,EAAmB,KAAA,CAAM,IAAI,CAAA,WAAA,CAAA;AAAA,QACpC,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAKA,IAAA,IAAI,IAAA,CAAK,eAAe,MAAA,EAAQ;AAC9B,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,KAAA,EAAO,CAAA,gBAAA,EAAmB,KAAA,CAAM,IAAI,CAAA,qBAAA,CAAA;AAAA,QACpC,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAWA,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,MAAM,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA,EAAO,KAAK,IAAI,CAAA;AACxD,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,IAAA;AAAA,QACT,MAAA;AAAA,QACA,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF,SAAS,CAAA,EAAG;AACV,MAAA,OAAO;AAAA,QACL,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAA,EAAS,KAAA;AAAA,QACT,OAAO,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AAAA,QAChD,WAAA,EAAa,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,OAC5B;AAAA,IACF;AAAA,EACF;AACF","file":"tool-use.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface ToolUseInput {\n tool: string;\n input: Record<string, unknown>;\n}\n\ninterface ToolUseOutput {\n tool: string;\n success: boolean;\n result?: unknown;\n error?: string;\n executionMs: number;\n}\n\nexport const toolUseTool: Tool<ToolUseInput, ToolUseOutput> = {\n name: 'tool_use',\n description:\n 'Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.',\n usageHint:\n 'Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.',\n permission: 'confirm',\n mutating: true,\n timeoutMs: 60_000,\n inputSchema: {\n type: 'object',\n properties: {\n tool: {\n type: 'string',\n description: 'Exact name of the tool to execute',\n },\n input: {\n type: 'object',\n description: 'Input parameters for the tool',\n },\n },\n required: ['tool'],\n },\n async execute(input, ctx, opts) {\n const start = Date.now();\n\n if (!input?.tool) {\n return {\n tool: 'unknown',\n success: false,\n error: 'tool_use: tool name is required',\n executionMs: 0,\n };\n }\n\n const tool = ctx.tools.find((t: Tool) => t.name === input.tool);\n if (!tool) {\n return {\n tool: input.tool,\n success: false,\n error: `tool_use: tool \"${input.tool}\" not found`,\n executionMs: Date.now() - start,\n };\n }\n\n // `deny` is a hard policy gate — bypassing it through a meta-tool\n // would defeat the whole point of the permission system. Keep this\n // check even though the outer `tool_use` already requires `confirm`.\n if (tool.permission === 'deny') {\n return {\n tool: input.tool,\n success: false,\n error: `tool_use: tool \"${input.tool}\" is denied by policy`,\n executionMs: Date.now() - start,\n };\n }\n\n // Note: inner `permission === 'confirm'` is intentionally NOT short-\n // circuited here. The outer `tool_use` itself has `permission: 'confirm'`,\n // so the user already saw the full args (including which inner tool will\n // run, and with what input) before approving the meta-call. Duplicating\n // the check inside execute() turned every confirm-tool dispatch through\n // `tool_use` into a hard failure — the model would see \"requires\n // confirmation\" with no way to proceed, even after the user said yes.\n // `batch_tool_use` already follows this same model.\n\n try {\n const result = await tool.execute(input.input, ctx, opts);\n return {\n tool: input.tool,\n success: true,\n result,\n executionMs: Date.now() - start,\n };\n } catch (e) {\n return {\n tool: input.tool,\n success: false,\n error: e instanceof Error ? e.message : String(e),\n executionMs: Date.now() - start,\n };\n }\n },\n};\n"]}
package/dist/tree.js CHANGED
@@ -19,7 +19,15 @@ function safeResolve(input, ctx) {
19
19
  }
20
20
 
21
21
  // src/tree.ts
22
- var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverage", "__pycache__"];
22
+ var DEFAULT_IGNORE = [
23
+ "node_modules",
24
+ ".git",
25
+ "dist",
26
+ "build",
27
+ ".next",
28
+ "coverage",
29
+ "__pycache__"
30
+ ];
23
31
  var treeTool = {
24
32
  name: "tree",
25
33
  description: "Display directory structure as an ASCII tree. Shows files and folders with indentation.",
package/dist/tree.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/_util.ts","../src/tree.ts"],"names":["path2"],"mappings":";;;;AAGO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAY,IAAA,CAAA,UAAA,CAAW,KAAK,CAAA,GAAS,IAAA,CAAA,SAAA,CAAU,KAAK,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACrF;AAEO,SAAS,gBAAA,CAAiB,SAAiB,GAAA,EAAsB;AACtE,EAAA,MAAM,IAAA,GAAY,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AACzC,EAAA,MAAM,MAAA,GAAc,aAAQ,OAAO,CAAA;AACnC,EAAA,MAAM,GAAA,GAAW,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AACtC,EAAA,IAAI,IAAI,UAAA,CAAW,IAAI,CAAA,IAAU,IAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,OAAO,CAAA,2BAAA,EAA8B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AACtD;;;ACdA,IAAM,cAAA,GAAiB,CAAC,cAAA,EAAgB,MAAA,EAAQ,QAAQ,OAAA,EAAS,OAAA,EAAS,YAAY,aAAa,CAAA;AAoB5F,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,WAAA,EACE,yFAAA;AAAA,EACF,SAAA,EACE,kKAAA;AAAA,EACF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,IAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,+BAAA,EAAgC;AAAA,MACrE,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa,iDAAA;AAAA,QACb,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAS;AAAA,OACX;AAAA,MACA,IAAA,EAAM,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,0CAAA,EAA2C;AAAA,MAChF,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,OAAA;AAAA,QACN,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QACxB,WAAA,EAAa;AAAA,OACf;AAAA,MACA,UAAA,EAAY;AAAA,QACV,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,SAAA,EAAW;AAAA,QACT,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,WAAA,EAAa;AAAA,QACX,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf;AACF,GACF;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,IAAI,KAAA;AACJ,IAAA,WAAA,MAAiB,MAAM,QAAA,CAAS,aAAA,CAAe,KAAA,EAAO,GAAA,EAAK,IAAI,CAAA,EAAG;AAChE,MAAA,IAAI,EAAA,CAAG,IAAA,KAAS,OAAA,EAAS,KAAA,GAAQ,EAAA,CAAG,MAAA;AAAA,IACtC;AACA,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,wCAAwC,CAAA;AACpE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAO,aAAA,CAAc,KAAA,EAAO,GAAA,EAAkD;AAC5E,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,GAAO,WAAA,CAAY,MAAM,IAAA,EAAM,GAAG,IAAI,GAAA,CAAI,GAAA;AACjE,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,IAAS,CAAA;AAChC,IAAA,MAAM,SAAA,GAAY,MAAM,UAAA,IAAc,IAAA;AACtC,IAAA,MAAM,QAAA,GAAW,MAAM,SAAA,IAAa,IAAA;AACpC,IAAA,MAAM,UAAA,GAAa,MAAM,WAAA,IAAe,KAAA;AACxC,IAAA,MAAM,OAAA,mBAAU,IAAI,GAAA,CAAI,CAAC,GAAG,cAAA,EAAgB,GAAI,KAAA,CAAM,OAAA,IAAW,EAAG,CAAC,CAAA;AACrE,IAAA,MAAM,aAAa,KAAA,CAAM,IAAA;AAEzB,IAAA,MAAM,KAAA,GAAkB,CAAC,QAAQ,CAAA;AACjC,IAAA,MAAM,MAAA,GAAS,EAAE,UAAA,EAAY,EAAE,KAAA,EAAO,CAAA,EAAE,EAAG,SAAA,EAAW,EAAE,KAAA,EAAO,CAAA,EAAE,EAAE;AAGnE,IAAA,MAAM,QAA6B,EAAC;AACpC,IAAA,MAAM,WAAA,GAAc,GAAA;AACpB,IAAA,IAAI,gBAAA,GAAmB,CAAA;AAEvB,IAAA,MAAM,eAAe,MAAM;AACzB,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,UAAA,CAAW,KAAA,GAAQ,OAAO,SAAA,CAAU,KAAA;AACxD,MAAA,IAAI,IAAA,GAAO,oBAAoB,WAAA,EAAa;AAC1C,QAAA,KAAA,CAAM,IAAA,CAAK;AAAA,UACT,IAAA,EAAM,QAAA;AAAA,UACN,IAAA,EAAM,GAAG,IAAI,CAAA,QAAA,CAAA;AAAA,UACb,IAAA,EAAM,EAAE,KAAA,EAAO,MAAA,CAAO,WAAW,KAAA,EAAO,IAAA,EAAM,MAAA,CAAO,SAAA,CAAU,KAAA;AAAM,SACtE,CAAA;AACD,QAAA,gBAAA,GAAmB,IAAA;AAAA,MACrB;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,QAAA,EAAU,CAAA,EAAG;AAAA,MACvC,QAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,UAAA;AAAA,MACA,UAAA;AAAA,MACA,KAAA;AAAA,MACA,MAAA,EAAQ,EAAA;AAAA,MACR,MAAA,EAAQ,IAAA;AAAA,MACR,YAAY,MAAA,CAAO,UAAA;AAAA,MACnB,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,UAAA,EAAY;AAAA,KACb,CAAA;AAGD,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,WAAA,CAAY,QAAQ,MAAM;AACxB,MAAA,QAAA,GAAW,IAAA;AAAA,IACb,CAAC,CAAA;AAED,IAAA,OAAO,CAAC,QAAA,IAAY,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AACpC,MAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,QAAA,MAAM,MAAM,KAAA,EAAM;AAAA,MACpB,CAAA,MAAO;AAKL,QAAA,IAAI,SAAA;AACJ,QAAA,MAAM,IAAA,GAAO,IAAI,OAAA,CAAc,CAAC,CAAA,KAAM;AAAE,UAAA,SAAA,GAAY,UAAA,CAAW,GAAG,EAAE,CAAA;AAAA,QAAG,CAAC,CAAA;AACxE,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,CAAQ,KAAK,CAAC,WAAA,EAAa,IAAI,CAAC,CAAA,CAAE,KAAA,CAAM,MAAM,KAAA,CAAS,CAAA;AAAA,QAC/D,CAAA,SAAE;AACA,UAAA,IAAI,SAAA,eAAwB,SAAS,CAAA;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AACA,IAAA,MAAM,WAAA;AAEN,IAAA,MAAM;AAAA,MACJ,IAAA,EAAM,OAAA;AAAA,MACN,MAAA,EAAQ;AAAA,QACN,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA;AAAA,QACrB,WAAA,EAAa,OAAO,UAAA,CAAW,KAAA;AAAA,QAC/B,UAAA,EAAY,OAAO,SAAA,CAAU,KAAA;AAAA,QAC7B,SAAA,EAAW,KAAA;AAAA,QACX,IAAA,EAAM;AAAA;AACR,KACF;AAAA,EACF;AACF;AAiBA,eAAe,OAAA,CACb,GAAA,EACA,KAAA,EACA,IAAA,EACe;AACf,EAAA,MAAM,OAAA,GAAU,MAAS,EAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,EAAE,aAAA,EAAe,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,MAAM,EAAgC,CAAA;AAE3G,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM;AACrC,IAAA,IAAI,CAAC,KAAK,UAAA,IAAc,CAAA,CAAE,KAAK,UAAA,CAAW,GAAG,GAAG,OAAO,KAAA;AACvD,IAAA,IAAI,KAAK,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAE,IAAI,GAAG,OAAO,KAAA;AACrC,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AAED,EAAA,IAAI,QAAQ,CAAA,EAAG;AACb,IAAA,MAAM,QAAA,GAAW,SAAS,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,WAAA,EAAa,CAAA,CAAE,MAAA;AACzD,IAAA,MAAM,SAAA,GAAY,SAAS,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,MAAA,EAAQ,CAAA,CAAE,MAAA;AACrD,IAAA,IAAA,CAAK,UAAU,KAAA,IAAS,QAAA;AACxB,IAAA,IAAA,CAAK,WAAW,KAAA,IAAS,SAAA;AACzB,IAAA,IAAA,CAAK,UAAA,IAAa;AAAA,EACpB;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AACpC,IAAA,IAAI,EAAE,WAAA,EAAY,IAAK,CAAC,CAAA,CAAE,WAAA,IAAe,OAAO,EAAA;AAChD,IAAA,IAAI,CAAC,CAAA,CAAE,WAAA,MAAiB,CAAA,CAAE,WAAA,IAAe,OAAO,CAAA;AAChD,IAAA,OAAO,CAAA,CAAE,IAAA,CAAK,aAAA,CAAc,CAAA,CAAE,IAAI,CAAA;AAAA,EACpC,CAAC,CAAA;AAED,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,KAAA,GAAQ,MAAM,CAAC,CAAA;AACrB,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,MAAM,MAAA,GAAS,CAAA,KAAM,KAAA,CAAM,MAAA,GAAS,CAAA;AACpC,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,GAAS,MAAA,GAAS,WAAA;AACzC,IAAA,MAAM,MAAA,GAAS,SAAS,qBAAA,GAAS,qBAAA;AACjC,IAAA,MAAM,cAAc,KAAA,CAAM,IAAA,IAAQ,KAAA,CAAM,WAAA,KAAgB,GAAA,GAAM,EAAA,CAAA;AAE9D,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,IAAY,KAAA,CAAM,aAAY,EAAG;AAC3C,IAAA,IAAI,CAAC,IAAA,CAAK,SAAA,IAAa,KAAA,CAAM,QAAO,EAAG;AAEvC,IAAA,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,MAAA,GAAS,SAAS,WAAW,CAAA;AAElD,IAAA,IAAI,KAAA,CAAM,aAAY,KAAM,IAAA,CAAK,aAAa,CAAA,IAAK,KAAA,GAAQ,KAAK,QAAA,CAAA,EAAW;AACzE,MAAA,MAAM,WAAA,GAAc,KAAK,MAAA,GAAS,SAAA;AAClC,MAAA,MAAM,QAAaA,IAAA,CAAA,IAAA,CAAK,GAAA,EAAK,MAAM,IAAI,CAAA,EAAG,QAAQ,CAAA,EAAG;AAAA,QACnD,GAAG,IAAA;AAAA,QACH,MAAA,EAAQ,WAAA;AAAA,QACR;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AACF","file":"tree.js","sourcesContent":["import * as path from 'node:path';\nimport type { Context } from '@wrongstack/core';\n\r\nexport function resolvePath(input: string, ctx: Context): string {\r\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\r\n}\r\n\r\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\r\n const root = path.resolve(ctx.projectRoot);\r\n const target = path.resolve(absPath);\r\n const rel = path.relative(root, target);\r\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\r\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\r\n }\r\n return target;\r\n}\r\n\r\nexport function safeResolve(input: string, ctx: Context): string {\r\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\r\n}\r\n\r\nexport function truncateMiddle(s: string, max: number): string {\r\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\r\n const half = Math.floor(max / 2);\r\n return (\r\n s.slice(0, half) +\r\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\r\n s.slice(-half)\r\n );\r\n}\r\n\r\nexport function isBinaryBuffer(buf: Buffer): boolean {\r\n const len = Math.min(buf.length, 8192);\r\n for (let i = 0; i < len; i++) {\r\n if (buf[i] === 0) return true;\r\n }\r\n return false;\r\n}\r\n\r\n","import * as fs from 'node:fs/promises';\r\nimport * as path from 'node:path';\r\nimport type { Tool, ToolProgressEvent, ToolStreamEvent } from '@wrongstack/core';\r\nimport { safeResolve } from './_util.js';\r\n\r\nconst DEFAULT_IGNORE = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '__pycache__'];\r\n\r\ninterface TreeInput {\r\n path?: string;\r\n depth?: number;\r\n glob?: string;\r\n exclude?: string[];\r\n show_files?: boolean;\r\n show_dirs?: boolean;\r\n show_hidden?: boolean;\r\n}\r\n\r\ninterface TreeOutput {\r\n tree: string;\r\n total_files: number;\r\n total_dirs: number;\r\n truncated: boolean;\r\n path: string;\r\n}\r\n\r\nexport const treeTool: Tool<TreeInput, TreeOutput> = {\r\n name: 'tree',\r\n description:\r\n 'Display directory structure as an ASCII tree. Shows files and folders with indentation.',\r\n usageHint:\r\n 'Set `path` (default: cwd). `depth` limits nesting (default: 3). `glob` filters files. `exclude` ignores dirs. `show_files` toggles file listing (default: true).',\r\n permission: 'auto',\r\n mutating: false,\r\n timeoutMs: 15_000,\r\n inputSchema: {\r\n type: 'object',\r\n properties: {\r\n path: { type: 'string', description: 'Root directory (default: cwd)' },\r\n depth: {\r\n type: 'integer',\r\n description: 'Max nesting depth (default: 3, 0 for unlimited)',\r\n minimum: 0,\r\n maximum: 20,\r\n },\r\n glob: { type: 'string', description: 'Filter files matching glob (e.g. \"*.ts\")' },\r\n exclude: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n description: 'Directory names to exclude',\r\n },\r\n show_files: {\r\n type: 'boolean',\r\n description: 'Show files (default: true, false shows dirs only)',\r\n },\r\n show_dirs: {\r\n type: 'boolean',\r\n description: 'Show directories (default: true)',\r\n },\r\n show_hidden: {\r\n type: 'boolean',\r\n description: 'Show hidden files starting with . (default: false)',\r\n },\r\n },\r\n },\r\n async execute(input, ctx, opts) {\r\n let final: TreeOutput | undefined;\r\n for await (const ev of treeTool.executeStream!(input, ctx, opts)) {\r\n if (ev.type === 'final') final = ev.output;\r\n }\r\n if (!final) throw new Error('tree: stream ended without final event');\r\n return final;\r\n },\r\n async *executeStream(input, ctx): AsyncGenerator<ToolStreamEvent<TreeOutput>> {\r\n const basePath = input.path ? safeResolve(input.path, ctx) : ctx.cwd;\r\n const maxDepth = input.depth ?? 3;\r\n const showFiles = input.show_files ?? true;\r\n const showDirs = input.show_dirs ?? true;\r\n const showHidden = input.show_hidden ?? false;\r\n const exclude = new Set([...DEFAULT_IGNORE, ...(input.exclude ?? [])]);\r\n const filterGlob = input.glob;\r\n\r\n const lines: string[] = [basePath];\r\n const totals = { totalFiles: { value: 0 }, totalDirs: { value: 0 } };\r\n\r\n // Walker pushes progress into an async queue; the generator drains it.\r\n const queue: ToolProgressEvent[] = [];\r\n const FLUSH_EVERY = 200; // emit metric every 200 entries seen\r\n let lastEmittedTotal = 0;\r\n\r\n const tickProgress = () => {\r\n const seen = totals.totalFiles.value + totals.totalDirs.value;\r\n if (seen - lastEmittedTotal >= FLUSH_EVERY) {\r\n queue.push({\r\n type: 'metric',\r\n text: `${seen} entries`,\r\n data: { files: totals.totalFiles.value, dirs: totals.totalDirs.value },\r\n });\r\n lastEmittedTotal = seen;\r\n }\r\n };\r\n\r\n const walkPromise = walkDir(basePath, 0, {\r\n maxDepth,\r\n exclude,\r\n showFiles,\r\n showDirs,\r\n showHidden,\r\n filterGlob,\r\n lines,\r\n prefix: '',\r\n isLast: true,\r\n totalFiles: totals.totalFiles,\r\n totalDirs: totals.totalDirs,\r\n onProgress: tickProgress,\r\n });\r\n\r\n // Race the walk against periodic flushes — yield metrics while it runs.\r\n let walkDone = false;\r\n walkPromise.finally(() => {\r\n walkDone = true;\r\n });\r\n\r\n while (!walkDone || queue.length > 0) {\r\n if (queue.length > 0) {\r\n yield queue.shift()!;\r\n } else {\r\n // Race the walk completion against a short tick so we don't busy-\r\n // spin while the producer fills the queue. Previously the\r\n // setTimeout was never cleared when walkPromise won — one stray\r\n // timer per drain iteration accumulated on the event loop.\r\n let pollTimer: ReturnType<typeof setTimeout> | undefined;\r\n const poll = new Promise<void>((r) => { pollTimer = setTimeout(r, 50); });\r\n try {\r\n await Promise.race([walkPromise, poll]).catch(() => undefined);\r\n } finally {\r\n if (pollTimer) clearTimeout(pollTimer);\r\n }\r\n }\r\n }\r\n await walkPromise; // surface any error\r\n\r\n yield {\r\n type: 'final',\r\n output: {\r\n tree: lines.join('\\n'),\r\n total_files: totals.totalFiles.value,\r\n total_dirs: totals.totalDirs.value,\r\n truncated: false,\r\n path: basePath,\r\n },\r\n };\r\n },\r\n};\r\n\r\ninterface WalkOptions {\r\n maxDepth: number;\r\n exclude: Set<string>;\r\n showFiles: boolean;\r\n showDirs: boolean;\r\n showHidden: boolean;\r\n filterGlob?: string;\r\n lines: string[];\r\n prefix: string;\r\n isLast: boolean;\r\n totalFiles: { value: number };\r\n totalDirs: { value: number };\r\n onProgress?: () => void;\r\n}\r\n\r\nasync function walkDir(\r\n dir: string,\r\n depth: number,\r\n opts: WalkOptions,\r\n): Promise<void> {\r\n const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => [] as import('node:fs').Dirent[]);\r\n\r\n const filtered = entries.filter((e) => {\r\n if (!opts.showHidden && e.name.startsWith('.')) return false;\r\n if (opts.exclude.has(e.name)) return false;\r\n return true;\r\n });\r\n\r\n if (depth > 0) {\r\n const dirCount = filtered.filter((e) => e.isDirectory()).length;\r\n const fileCount = filtered.filter((e) => e.isFile()).length;\r\n opts.totalDirs.value += dirCount;\r\n opts.totalFiles.value += fileCount;\r\n opts.onProgress?.();\r\n }\r\n\r\n const items = filtered.sort((a, b) => {\r\n if (a.isDirectory() && !b.isDirectory()) return -1;\r\n if (!a.isDirectory() && b.isDirectory()) return 1;\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const entry = items[i];\r\n if (!entry) continue;\r\n const isLast = i === items.length - 1;\r\n const connector = opts.isLast ? ' ' : '│ ';\r\n const branch = isLast ? '└── ' : '├── ';\r\n const displayName = entry.name + (entry.isDirectory() ? '/' : '');\r\n\r\n if (!opts.showDirs && entry.isDirectory()) continue;\r\n if (!opts.showFiles && entry.isFile()) continue;\r\n\r\n opts.lines.push(opts.prefix + branch + displayName);\r\n\r\n if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {\r\n const childPrefix = opts.prefix + connector;\r\n await walkDir(path.join(dir, entry.name), depth + 1, {\r\n ...opts,\r\n prefix: childPrefix,\r\n isLast,\r\n });\r\n }\r\n }\r\n}"]}
1
+ {"version":3,"sources":["../src/_util.ts","../src/tree.ts"],"names":["path2"],"mappings":"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","file":"tree.js","sourcesContent":["import * as path from 'node:path';\nimport type { Context } from '@wrongstack/core';\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const root = path.resolve(ctx.projectRoot);\n const target = path.resolve(absPath);\n const rel = path.relative(root, target);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\n }\n return target;\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { Tool, ToolProgressEvent, ToolStreamEvent } from '@wrongstack/core';\nimport { safeResolve } from './_util.js';\n\nconst DEFAULT_IGNORE = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n '.next',\n 'coverage',\n '__pycache__',\n];\n\ninterface TreeInput {\n path?: string;\n depth?: number;\n glob?: string;\n exclude?: string[];\n show_files?: boolean;\n show_dirs?: boolean;\n show_hidden?: boolean;\n}\n\ninterface TreeOutput {\n tree: string;\n total_files: number;\n total_dirs: number;\n truncated: boolean;\n path: string;\n}\n\nexport const treeTool: Tool<TreeInput, TreeOutput> = {\n name: 'tree',\n description:\n 'Display directory structure as an ASCII tree. Shows files and folders with indentation.',\n usageHint:\n 'Set `path` (default: cwd). `depth` limits nesting (default: 3). `glob` filters files. `exclude` ignores dirs. `show_files` toggles file listing (default: true).',\n permission: 'auto',\n mutating: false,\n timeoutMs: 15_000,\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Root directory (default: cwd)' },\n depth: {\n type: 'integer',\n description: 'Max nesting depth (default: 3, 0 for unlimited)',\n minimum: 0,\n maximum: 20,\n },\n glob: { type: 'string', description: 'Filter files matching glob (e.g. \"*.ts\")' },\n exclude: {\n type: 'array',\n items: { type: 'string' },\n description: 'Directory names to exclude',\n },\n show_files: {\n type: 'boolean',\n description: 'Show files (default: true, false shows dirs only)',\n },\n show_dirs: {\n type: 'boolean',\n description: 'Show directories (default: true)',\n },\n show_hidden: {\n type: 'boolean',\n description: 'Show hidden files starting with . (default: false)',\n },\n },\n },\n async execute(input, ctx, opts) {\n let final: TreeOutput | undefined;\n for await (const ev of treeTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('tree: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx): AsyncGenerator<ToolStreamEvent<TreeOutput>> {\n const basePath = input.path ? safeResolve(input.path, ctx) : ctx.cwd;\n const maxDepth = input.depth ?? 3;\n const showFiles = input.show_files ?? true;\n const showDirs = input.show_dirs ?? true;\n const showHidden = input.show_hidden ?? false;\n const exclude = new Set([...DEFAULT_IGNORE, ...(input.exclude ?? [])]);\n const filterGlob = input.glob;\n\n const lines: string[] = [basePath];\n const totals = { totalFiles: { value: 0 }, totalDirs: { value: 0 } };\n\n // Walker pushes progress into an async queue; the generator drains it.\n const queue: ToolProgressEvent[] = [];\n const FLUSH_EVERY = 200; // emit metric every 200 entries seen\n let lastEmittedTotal = 0;\n\n const tickProgress = () => {\n const seen = totals.totalFiles.value + totals.totalDirs.value;\n if (seen - lastEmittedTotal >= FLUSH_EVERY) {\n queue.push({\n type: 'metric',\n text: `${seen} entries`,\n data: { files: totals.totalFiles.value, dirs: totals.totalDirs.value },\n });\n lastEmittedTotal = seen;\n }\n };\n\n const walkPromise = walkDir(basePath, 0, {\n maxDepth,\n exclude,\n showFiles,\n showDirs,\n showHidden,\n filterGlob,\n lines,\n prefix: '',\n isLast: true,\n totalFiles: totals.totalFiles,\n totalDirs: totals.totalDirs,\n onProgress: tickProgress,\n });\n\n // Race the walk against periodic flushes — yield metrics while it runs.\n let walkDone = false;\n walkPromise.finally(() => {\n walkDone = true;\n });\n\n while (!walkDone || queue.length > 0) {\n if (queue.length > 0) {\n yield queue.shift()!;\n } else {\n // Race the walk completion against a short tick so we don't busy-\n // spin while the producer fills the queue. Previously the\n // setTimeout was never cleared when walkPromise won — one stray\n // timer per drain iteration accumulated on the event loop.\n let pollTimer: ReturnType<typeof setTimeout> | undefined;\n const poll = new Promise<void>((r) => {\n pollTimer = setTimeout(r, 50);\n });\n try {\n await Promise.race([walkPromise, poll]).catch(() => undefined);\n } finally {\n if (pollTimer) clearTimeout(pollTimer);\n }\n }\n }\n await walkPromise; // surface any error\n\n yield {\n type: 'final',\n output: {\n tree: lines.join('\\n'),\n total_files: totals.totalFiles.value,\n total_dirs: totals.totalDirs.value,\n truncated: false,\n path: basePath,\n },\n };\n },\n};\n\ninterface WalkOptions {\n maxDepth: number;\n exclude: Set<string>;\n showFiles: boolean;\n showDirs: boolean;\n showHidden: boolean;\n filterGlob?: string;\n lines: string[];\n prefix: string;\n isLast: boolean;\n totalFiles: { value: number };\n totalDirs: { value: number };\n onProgress?: () => void;\n}\n\nasync function walkDir(dir: string, depth: number, opts: WalkOptions): Promise<void> {\n const entries = await fs\n .readdir(dir, { withFileTypes: true })\n .catch(() => [] as import('node:fs').Dirent[]);\n\n const filtered = entries.filter((e) => {\n if (!opts.showHidden && e.name.startsWith('.')) return false;\n if (opts.exclude.has(e.name)) return false;\n return true;\n });\n\n if (depth > 0) {\n const dirCount = filtered.filter((e) => e.isDirectory()).length;\n const fileCount = filtered.filter((e) => e.isFile()).length;\n opts.totalDirs.value += dirCount;\n opts.totalFiles.value += fileCount;\n opts.onProgress?.();\n }\n\n const items = filtered.sort((a, b) => {\n if (a.isDirectory() && !b.isDirectory()) return -1;\n if (!a.isDirectory() && b.isDirectory()) return 1;\n return a.name.localeCompare(b.name);\n });\n\n for (let i = 0; i < items.length; i++) {\n const entry = items[i];\n if (!entry) continue;\n const isLast = i === items.length - 1;\n const connector = opts.isLast ? ' ' : '│ ';\n const branch = isLast ? '└── ' : '├── ';\n const displayName = entry.name + (entry.isDirectory() ? '/' : '');\n\n if (!opts.showDirs && entry.isDirectory()) continue;\n if (!opts.showFiles && entry.isFile()) continue;\n\n opts.lines.push(opts.prefix + branch + displayName);\n\n if (entry.isDirectory() && (opts.maxDepth === 0 || depth < opts.maxDepth)) {\n const childPrefix = opts.prefix + connector;\n await walkDir(path.join(dir, entry.name), depth + 1, {\n ...opts,\n prefix: childPrefix,\n isLast,\n });\n }\n }\n}\n"]}
package/dist/typecheck.js CHANGED
@@ -1,22 +1,8 @@
1
1
  import * as path from 'path';
2
2
  import { spawn } from 'child_process';
3
+ import { buildChildEnv } from '@wrongstack/core';
3
4
 
4
5
  // src/typecheck.ts
5
- function resolvePath(input, ctx) {
6
- return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
7
- }
8
- function ensureInsideRoot(absPath, ctx) {
9
- const root = path.resolve(ctx.projectRoot);
10
- const target = path.resolve(absPath);
11
- const rel = path.relative(root, target);
12
- if (rel.startsWith("..") || path.isAbsolute(rel)) {
13
- throw new Error(`Path "${absPath}" is outside project root "${root}"`);
14
- }
15
- return target;
16
- }
17
- function safeResolve(input, ctx) {
18
- return ensureInsideRoot(resolvePath(input, ctx), ctx);
19
- }
20
6
  async function* spawnStream(opts) {
21
7
  const max = opts.maxBytes;
22
8
  const flushAt = opts.flushBytes ?? 4 * 1024;
@@ -27,6 +13,7 @@ async function* spawnStream(opts) {
27
13
  const child = spawn(opts.cmd, opts.args, {
28
14
  cwd: opts.cwd,
29
15
  signal: opts.signal,
16
+ env: buildChildEnv(),
30
17
  stdio: ["ignore", "pipe", "pipe"]
31
18
  });
32
19
  const queue = [];
@@ -94,6 +81,21 @@ async function* spawnStream(opts) {
94
81
  error
95
82
  };
96
83
  }
84
+ function resolvePath(input, ctx) {
85
+ return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);
86
+ }
87
+ function ensureInsideRoot(absPath, ctx) {
88
+ const root = path.resolve(ctx.projectRoot);
89
+ const target = path.resolve(absPath);
90
+ const rel = path.relative(root, target);
91
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
92
+ throw new Error(`Path "${absPath}" is outside project root "${root}"`);
93
+ }
94
+ return target;
95
+ }
96
+ function safeResolve(input, ctx) {
97
+ return ensureInsideRoot(resolvePath(input, ctx), ctx);
98
+ }
97
99
 
98
100
  // src/typecheck.ts
99
101
  var typecheckTool = {