@wrongstack/tools 0.2.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/audit.js +1 -0
- package/dist/audit.js.map +1 -1
- package/dist/bash.js +1 -0
- package/dist/bash.js.map +1 -1
- package/dist/batch-tool-use.js +1 -0
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/builtin.js +58 -8
- package/dist/builtin.js.map +1 -1
- package/dist/diff.js +1 -0
- package/dist/diff.js.map +1 -1
- package/dist/document.js +1 -0
- package/dist/document.js.map +1 -1
- package/dist/edit.js +1 -0
- package/dist/edit.js.map +1 -1
- package/dist/exec.js +5 -4
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js +5 -1
- package/dist/fetch.js.map +1 -1
- package/dist/format.js +1 -0
- package/dist/format.js.map +1 -1
- package/dist/git.js +3 -2
- package/dist/git.js.map +1 -1
- package/dist/glob.js +1 -0
- package/dist/glob.js.map +1 -1
- package/dist/grep.js +18 -1
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +69 -9
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -0
- package/dist/install.js.map +1 -1
- package/dist/json.js +1 -0
- package/dist/json.js.map +1 -1
- package/dist/lint.js +1 -0
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +1 -0
- package/dist/logs.js.map +1 -1
- package/dist/memory.js +2 -0
- package/dist/memory.js.map +1 -1
- package/dist/mode.js +1 -0
- package/dist/mode.js.map +1 -1
- package/dist/outdated.js +1 -0
- package/dist/outdated.js.map +1 -1
- package/dist/pack.d.ts +9 -0
- package/dist/pack.js +4219 -0
- package/dist/pack.js.map +1 -0
- package/dist/patch.js +1 -0
- package/dist/patch.js.map +1 -1
- package/dist/read.js +1 -0
- package/dist/read.js.map +1 -1
- package/dist/replace.js +1 -0
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js +1 -0
- package/dist/scaffold.js.map +1 -1
- package/dist/search.js +1 -0
- package/dist/search.js.map +1 -1
- package/dist/test.js +1 -0
- package/dist/test.js.map +1 -1
- package/dist/todo.js +1 -0
- package/dist/todo.js.map +1 -1
- package/dist/tool-help.js +1 -0
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-search.js +1 -0
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-use.js +1 -0
- package/dist/tool-use.js.map +1 -1
- package/dist/tree.js +1 -0
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.js +1 -0
- package/dist/typecheck.js.map +1 -1
- package/dist/write.js +1 -0
- package/dist/write.js.map +1 -1
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ pnpm add @wrongstack/tools @wrongstack/core
|
|
|
21
21
|
| `edit` | `confirm` | yes | str_replace; requires prior `read`; FAT-aware mtime tolerance |
|
|
22
22
|
| `replace` | `confirm` | yes | Regex replace across files; symlink-skipped + realpath-revalidated |
|
|
23
23
|
| `glob` | `auto` | no | Glob pattern matching |
|
|
24
|
-
| `grep` | `auto` | no | rg-backed;
|
|
24
|
+
| `grep` | `auto` | no | rg-backed; ReDoS-guarded regex, default ignores, accurate count totals |
|
|
25
25
|
| `tree` | `auto` | no | Project tree; clears its polling timer (no leaks) |
|
|
26
26
|
| `patch` | `confirm` | yes | GNU-patch diff applier; targets pre-validated against `projectRoot` |
|
|
27
27
|
| `diff` | `auto` | no | Git diff against HEAD |
|
package/dist/audit.js
CHANGED
|
@@ -100,6 +100,7 @@ function safeResolve(input, ctx) {
|
|
|
100
100
|
// src/audit.ts
|
|
101
101
|
var auditTool = {
|
|
102
102
|
name: "audit",
|
|
103
|
+
category: "Package Management",
|
|
103
104
|
description: "Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.",
|
|
104
105
|
usageHint: "Set `level` to filter minimum severity. `fix` attempts auto-fix. `packages` checks specific packages.",
|
|
105
106
|
permission: "confirm",
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/_spawn-stream.ts","../src/_util.ts","../src/audit.ts"],"names":["resolve"],"mappings":"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","file":"audit.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 type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { spawnStream } from './_spawn-stream.js';\nimport { safeResolve } from './_util.js';\n\ninterface AuditInput {\n cwd?: string;\n level?: 'low' | 'moderate' | 'high' | 'critical';\n fix?: boolean;\n packages?: string | string[];\n}\n\ninterface AuditVulnerability {\n severity: string;\n package: string;\n title: string;\n url: string;\n}\n\ninterface AuditOutput {\n exit_code: number;\n vulnerabilities: AuditVulnerability[];\n total: number;\n summary: string;\n output: string;\n truncated: boolean;\n}\n\nexport const auditTool: Tool<AuditInput, AuditOutput> = {\n name: 'audit',\n description: 'Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.',\n usageHint:\n 'Set `level` to filter minimum severity. `fix` attempts auto-fix. `packages` checks specific packages.',\n permission: 'confirm',\n mutating: false,\n timeoutMs: 60_000,\n inputSchema: {\n type: 'object',\n properties: {\n cwd: { type: 'string', description: 'Working directory (default: cwd)' },\n level: {\n type: 'string',\n enum: ['low', 'moderate', 'high', 'critical'],\n description: 'Minimum severity level to report',\n },\n fix: { type: 'boolean', description: 'Attempt to fix vulnerabilities (default: false)' },\n packages: { type: 'string', description: 'Specific package(s) to audit (comma-separated)' },\n },\n },\n async execute(input, ctx, opts) {\n let final: AuditOutput | undefined;\n for await (const ev of auditTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('audit: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<AuditOutput>> {\n const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;\n const manager = await detectManager(cwd);\n yield { type: 'log', text: `Auditing with ${manager}…`, data: { manager } };\n\n const args = ['audit', '--json'];\n if (input.fix) args.push('--fix');\n if (input.packages) {\n const pkgs = Array.isArray(input.packages) ? input.packages : input.packages.split(',');\n args.push(...pkgs.map((p: string) => p.trim()));\n }\n\n const result = yield* spawnStream({\n cmd: manager,\n args,\n cwd,\n signal: opts.signal,\n maxBytes: 100_000,\n });\n\n yield { type: 'final', output: parseAuditOutput(result.stdout, result.exitCode) };\n },\n};\n\nasync function detectManager(cwd: string): Promise<string> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* */\n }\n return 'npm';\n}\n\nfunction parseAuditOutput(json: string, exitCode: number): AuditOutput {\n if (!json) {\n return {\n exit_code: exitCode,\n vulnerabilities: [],\n total: 0,\n summary: exitCode === 0 ? 'No vulnerabilities found' : 'Audit failed',\n output: '',\n truncated: false,\n };\n }\n\n try {\n const data = JSON.parse(json);\n const advisories: AuditVulnerability[] = [];\n const ads = data.advisories ?? {};\n for (const id of Object.keys(ads)) {\n const adv = ads[id];\n advisories.push({\n severity: adv.severity ?? 'unknown',\n package: adv.module_name ?? id,\n title: adv.title ?? 'Unknown vulnerability',\n url: adv.url ?? '',\n });\n }\n\n const total = advisories.length;\n const summary =\n total === 0\n ? 'No vulnerabilities found'\n : `Found ${total} vulnerabilities: ${advisories.filter((a) => a.severity === 'critical').length} critical, ${advisories.filter((a) => a.severity === 'high').length} high`;\n\n return {\n exit_code: exitCode,\n vulnerabilities: advisories,\n total,\n summary,\n output: json,\n truncated: json.length >= 100_000,\n };\n } catch {\n return {\n exit_code: exitCode,\n vulnerabilities: [],\n total: 0,\n summary: 'Could not parse audit output',\n output: json,\n truncated: false,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/_spawn-stream.ts","../src/_util.ts","../src/audit.ts"],"names":["resolve"],"mappings":"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","file":"audit.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 type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { spawnStream } from './_spawn-stream.js';\nimport { safeResolve } from './_util.js';\n\ninterface AuditInput {\n cwd?: string;\n level?: 'low' | 'moderate' | 'high' | 'critical';\n fix?: boolean;\n packages?: string | string[];\n}\n\ninterface AuditVulnerability {\n severity: string;\n package: string;\n title: string;\n url: string;\n}\n\ninterface AuditOutput {\n exit_code: number;\n vulnerabilities: AuditVulnerability[];\n total: number;\n summary: string;\n output: string;\n truncated: boolean;\n}\n\nexport const auditTool: Tool<AuditInput, AuditOutput> = {\n name: 'audit',\n category: 'Package Management',\n description: 'Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.',\n usageHint:\n 'Set `level` to filter minimum severity. `fix` attempts auto-fix. `packages` checks specific packages.',\n permission: 'confirm',\n mutating: false,\n timeoutMs: 60_000,\n inputSchema: {\n type: 'object',\n properties: {\n cwd: { type: 'string', description: 'Working directory (default: cwd)' },\n level: {\n type: 'string',\n enum: ['low', 'moderate', 'high', 'critical'],\n description: 'Minimum severity level to report',\n },\n fix: { type: 'boolean', description: 'Attempt to fix vulnerabilities (default: false)' },\n packages: { type: 'string', description: 'Specific package(s) to audit (comma-separated)' },\n },\n },\n async execute(input, ctx, opts) {\n let final: AuditOutput | undefined;\n for await (const ev of auditTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('audit: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<AuditOutput>> {\n const cwd = input.cwd ? safeResolve(input.cwd, ctx) : ctx.cwd;\n const manager = await detectManager(cwd);\n yield { type: 'log', text: `Auditing with ${manager}…`, data: { manager } };\n\n const args = ['audit', '--json'];\n if (input.fix) args.push('--fix');\n if (input.packages) {\n const pkgs = Array.isArray(input.packages) ? input.packages : input.packages.split(',');\n args.push(...pkgs.map((p: string) => p.trim()));\n }\n\n const result = yield* spawnStream({\n cmd: manager,\n args,\n cwd,\n signal: opts.signal,\n maxBytes: 100_000,\n });\n\n yield { type: 'final', output: parseAuditOutput(result.stdout, result.exitCode) };\n },\n};\n\nasync function detectManager(cwd: string): Promise<string> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* */\n }\n return 'npm';\n}\n\nfunction parseAuditOutput(json: string, exitCode: number): AuditOutput {\n if (!json) {\n return {\n exit_code: exitCode,\n vulnerabilities: [],\n total: 0,\n summary: exitCode === 0 ? 'No vulnerabilities found' : 'Audit failed',\n output: '',\n truncated: false,\n };\n }\n\n try {\n const data = JSON.parse(json);\n const advisories: AuditVulnerability[] = [];\n const ads = data.advisories ?? {};\n for (const id of Object.keys(ads)) {\n const adv = ads[id];\n advisories.push({\n severity: adv.severity ?? 'unknown',\n package: adv.module_name ?? id,\n title: adv.title ?? 'Unknown vulnerability',\n url: adv.url ?? '',\n });\n }\n\n const total = advisories.length;\n const summary =\n total === 0\n ? 'No vulnerabilities found'\n : `Found ${total} vulnerabilities: ${advisories.filter((a) => a.severity === 'critical').length} critical, ${advisories.filter((a) => a.severity === 'high').length} high`;\n\n return {\n exit_code: exitCode,\n vulnerabilities: advisories,\n total,\n summary,\n output: json,\n truncated: json.length >= 100_000,\n };\n } catch {\n return {\n exit_code: exitCode,\n vulnerabilities: [],\n total: 0,\n summary: 'Could not parse audit output',\n output: json,\n truncated: false,\n };\n }\n}\n"]}
|
package/dist/bash.js
CHANGED
|
@@ -19,6 +19,7 @@ var STREAM_FLUSH_INTERVAL_MS = 200;
|
|
|
19
19
|
var STREAM_FLUSH_BYTES = 4 * 1024;
|
|
20
20
|
var bashTool = {
|
|
21
21
|
name: "bash",
|
|
22
|
+
category: "Shell",
|
|
22
23
|
description: "Run a shell command. stdout and stderr are merged.",
|
|
23
24
|
usageHint: "Runs via `bash -c` (or `cmd /c` on Windows). Cwd is the project root. Default timeout 30s. Output truncated from the middle if oversized. Use for git, npm, builds, tests.",
|
|
24
25
|
permission: "confirm",
|
package/dist/bash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/_util.ts","../src/bash.ts"],"names":["resolve"],"mappings":"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","file":"bash.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 { spawn } from 'node:child_process';\nimport * as os from 'node:os';\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { stripAnsi } from '@wrongstack/core';\nimport { buildChildEnv } from './_env.js';\nimport { truncateMiddle } from './_util.js';\n\ninterface BashInput {\n command: string;\n timeout_ms?: number;\n background?: boolean;\n}\n\ninterface BashOutput {\n output: string;\n exit_code: number | null;\n timed_out: boolean;\n pid?: number;\n}\n\nconst MAX_OUTPUT = 32_768;\nconst DEFAULT_TIMEOUT = 30_000;\n// Flush partial_output every 200ms or when 4 KiB accumulates — whichever\n// comes first. Smaller batches make the TUI feel responsive; larger ones\n// keep EventBus traffic reasonable on chatty processes.\nconst STREAM_FLUSH_INTERVAL_MS = 200;\nconst STREAM_FLUSH_BYTES = 4 * 1024;\n\nexport const bashTool: Tool<BashInput, BashOutput> = {\n name: 'bash',\n description: 'Run a shell command. stdout and stderr are merged.',\n usageHint:\n 'Runs via `bash -c` (or `cmd /c` on Windows). Cwd is the project root. Default timeout 30s. Output truncated from the middle if oversized. Use for git, npm, builds, tests.',\n permission: 'confirm',\n mutating: true,\n // Trust rules match on the literal `command` string. Without subjectKey\n // the policy heuristic would have done the same here, but declaring it\n // explicitly removes the implicit cross-tool aliasing.\n subjectKey: 'command',\n timeoutMs: 30_000,\n maxOutputBytes: MAX_OUTPUT,\n estimatedDurationMs: 3_000,\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string' },\n timeout_ms: { type: 'integer' },\n background: { type: 'boolean' },\n },\n required: ['command'],\n },\n async execute(input, ctx, opts) {\n let final: BashOutput | undefined;\n for await (const ev of bashTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('bash: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<BashOutput>> {\n if (!input?.command) throw new Error('bash: command is required');\n const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT, 600_000));\n\n const isWin = os.platform() === 'win32';\n const shell = isWin\n ? (process.env['COMSPEC'] ?? 'cmd.exe')\n : (process.env['SHELL'] ?? '/bin/bash');\n const args = isWin ? ['/c', input.command] : ['-c', input.command];\n\n const env = buildChildEnv(ctx.session?.id);\n\n // On POSIX we put the shell in its own process group so that timeout /\n // abort can kill the entire group with `process.kill(-pid)`. Otherwise\n // `bash -c \"sleep 9999 & disown\"` would leave the grandchild running.\n // `detached: true` is also reused for the user-facing background mode;\n // we always want detached on POSIX, only on Windows is it tied to the\n // explicit background flag.\n const detached = isWin ? !!input.background : true;\n const child = spawn(shell, args, {\n cwd: ctx.projectRoot,\n env,\n stdio: input.background ? 'ignore' : ['ignore', 'pipe', 'pipe'],\n detached,\n signal: opts.signal,\n });\n\n if (input.background) {\n const pid = child.pid;\n if (typeof pid === 'number') child.unref();\n yield {\n type: 'final',\n output: {\n output: `[background] pid=${pid ?? 'unknown'}`,\n exit_code: null,\n timed_out: false,\n pid,\n },\n };\n return;\n }\n\n let buf = '';\n let pending = '';\n let timedOut = false;\n const timers: NodeJS.Timeout[] = [];\n const timer = setTimeout(() => {\n timedOut = true;\n if (isWin) {\n try {\n child.kill();\n } catch {\n /* ignore */\n }\n } else {\n try {\n // Kill the process group, not just the shell — pid is positive,\n // group id is the negated pid. Without this a runaway grandchild\n // ('sleep 9999 & disown') survives bash termination.\n if (typeof child.pid === 'number') {\n try {\n process.kill(-child.pid, 'SIGTERM');\n } catch {\n child.kill('SIGTERM');\n }\n } else {\n child.kill('SIGTERM');\n }\n const killTimer = setTimeout(() => {\n try {\n if (typeof child.pid === 'number') {\n try {\n process.kill(-child.pid, 'SIGKILL');\n } catch {\n child.kill('SIGKILL');\n }\n } else {\n child.kill('SIGKILL');\n }\n } catch {\n /* ignore */\n }\n }, 2000);\n timers.push(killTimer);\n } catch {\n /* ignore */\n }\n }\n }, timeoutMs);\n timers.push(timer);\n timer.unref?.();\n\n // Bridge the EventEmitter-style child to an async iterator. We push\n // chunks into a queue and let the generator pull them; this lets us\n // yield 'partial_output' events to the executor at flush boundaries.\n type Chunk =\n | { kind: 'data'; text: string }\n | { kind: 'end'; code: number | null }\n | { kind: 'error'; err: Error };\n const queue: Chunk[] = [];\n let resolveNext: ((c: Chunk) => void) | null = null;\n const push = (c: Chunk) => {\n if (resolveNext) {\n const r = resolveNext;\n resolveNext = null;\n r(c);\n } else {\n queue.push(c);\n }\n };\n const next = (): Promise<Chunk> =>\n new Promise((resolve) => {\n const c = queue.shift();\n if (c) resolve(c);\n else resolveNext = resolve;\n });\n\n let lastFlush = Date.now();\n const flush = () => {\n if (pending.length === 0) return null;\n const text = pending;\n pending = '';\n lastFlush = Date.now();\n return text;\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n buf += text;\n pending += text;\n push({ kind: 'data', text });\n });\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n buf += text;\n pending += text;\n push({ kind: 'data', text });\n });\n child.on('error', (err) => {\n for (const t of timers) clearTimeout(t);\n push({ kind: 'error', err });\n });\n child.on('close', (code) => {\n for (const t of timers) clearTimeout(t);\n push({ kind: 'end', code });\n });\n\n try {\n while (true) {\n const c = await next();\n if (c.kind === 'error') throw c.err;\n if (c.kind === 'end') {\n const remainder = flush();\n if (remainder !== null) {\n yield { type: 'partial_output', text: remainder };\n }\n const cleaned = stripAnsi(buf).replace(/\\r\\n?/g, '\\n');\n yield {\n type: 'final',\n output: {\n output: truncateMiddle(cleaned, MAX_OUTPUT),\n exit_code: c.code,\n timed_out: timedOut,\n },\n };\n return;\n }\n // Decide whether to flush. Time-based OR size-based to keep latency\n // low for slow-emitting commands without overwhelming the TUI for\n // chatty ones.\n const now = Date.now();\n if (pending.length >= STREAM_FLUSH_BYTES || now - lastFlush >= STREAM_FLUSH_INTERVAL_MS) {\n const text = flush();\n if (text) yield { type: 'partial_output', text };\n }\n }\n } finally {\n for (const t of timers) clearTimeout(t);\n }\n },\n};\n\n// Re-export types so consumers can narrow on stream events.\nexport type { BashInput, BashOutput };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/_util.ts","../src/bash.ts"],"names":["resolve"],"mappings":"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","file":"bash.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 { spawn } from 'node:child_process';\nimport * as os from 'node:os';\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { stripAnsi } from '@wrongstack/core';\nimport { buildChildEnv } from './_env.js';\nimport { truncateMiddle } from './_util.js';\n\ninterface BashInput {\n command: string;\n timeout_ms?: number;\n background?: boolean;\n}\n\ninterface BashOutput {\n output: string;\n exit_code: number | null;\n timed_out: boolean;\n pid?: number;\n}\n\nconst MAX_OUTPUT = 32_768;\nconst DEFAULT_TIMEOUT = 30_000;\n// Flush partial_output every 200ms or when 4 KiB accumulates — whichever\n// comes first. Smaller batches make the TUI feel responsive; larger ones\n// keep EventBus traffic reasonable on chatty processes.\nconst STREAM_FLUSH_INTERVAL_MS = 200;\nconst STREAM_FLUSH_BYTES = 4 * 1024;\n\nexport const bashTool: Tool<BashInput, BashOutput> = {\n name: 'bash',\n category: 'Shell',\n description: 'Run a shell command. stdout and stderr are merged.',\n usageHint:\n 'Runs via `bash -c` (or `cmd /c` on Windows). Cwd is the project root. Default timeout 30s. Output truncated from the middle if oversized. Use for git, npm, builds, tests.',\n permission: 'confirm',\n mutating: true,\n // Trust rules match on the literal `command` string. Without subjectKey\n // the policy heuristic would have done the same here, but declaring it\n // explicitly removes the implicit cross-tool aliasing.\n subjectKey: 'command',\n timeoutMs: 30_000,\n maxOutputBytes: MAX_OUTPUT,\n estimatedDurationMs: 3_000,\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string' },\n timeout_ms: { type: 'integer' },\n background: { type: 'boolean' },\n },\n required: ['command'],\n },\n async execute(input, ctx, opts) {\n let final: BashOutput | undefined;\n for await (const ev of bashTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('bash: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<BashOutput>> {\n if (!input?.command) throw new Error('bash: command is required');\n const timeoutMs = Math.max(1, Math.min(input.timeout_ms ?? DEFAULT_TIMEOUT, 600_000));\n\n const isWin = os.platform() === 'win32';\n const shell = isWin\n ? (process.env['COMSPEC'] ?? 'cmd.exe')\n : (process.env['SHELL'] ?? '/bin/bash');\n const args = isWin ? ['/c', input.command] : ['-c', input.command];\n\n const env = buildChildEnv(ctx.session?.id);\n\n // On POSIX we put the shell in its own process group so that timeout /\n // abort can kill the entire group with `process.kill(-pid)`. Otherwise\n // `bash -c \"sleep 9999 & disown\"` would leave the grandchild running.\n // `detached: true` is also reused for the user-facing background mode;\n // we always want detached on POSIX, only on Windows is it tied to the\n // explicit background flag.\n const detached = isWin ? !!input.background : true;\n const child = spawn(shell, args, {\n cwd: ctx.projectRoot,\n env,\n stdio: input.background ? 'ignore' : ['ignore', 'pipe', 'pipe'],\n detached,\n signal: opts.signal,\n });\n\n if (input.background) {\n const pid = child.pid;\n if (typeof pid === 'number') child.unref();\n yield {\n type: 'final',\n output: {\n output: `[background] pid=${pid ?? 'unknown'}`,\n exit_code: null,\n timed_out: false,\n pid,\n },\n };\n return;\n }\n\n let buf = '';\n let pending = '';\n let timedOut = false;\n const timers: NodeJS.Timeout[] = [];\n const timer = setTimeout(() => {\n timedOut = true;\n if (isWin) {\n try {\n child.kill();\n } catch {\n /* ignore */\n }\n } else {\n try {\n // Kill the process group, not just the shell — pid is positive,\n // group id is the negated pid. Without this a runaway grandchild\n // ('sleep 9999 & disown') survives bash termination.\n if (typeof child.pid === 'number') {\n try {\n process.kill(-child.pid, 'SIGTERM');\n } catch {\n child.kill('SIGTERM');\n }\n } else {\n child.kill('SIGTERM');\n }\n const killTimer = setTimeout(() => {\n try {\n if (typeof child.pid === 'number') {\n try {\n process.kill(-child.pid, 'SIGKILL');\n } catch {\n child.kill('SIGKILL');\n }\n } else {\n child.kill('SIGKILL');\n }\n } catch {\n /* ignore */\n }\n }, 2000);\n timers.push(killTimer);\n } catch {\n /* ignore */\n }\n }\n }, timeoutMs);\n timers.push(timer);\n timer.unref?.();\n\n // Bridge the EventEmitter-style child to an async iterator. We push\n // chunks into a queue and let the generator pull them; this lets us\n // yield 'partial_output' events to the executor at flush boundaries.\n type Chunk =\n | { kind: 'data'; text: string }\n | { kind: 'end'; code: number | null }\n | { kind: 'error'; err: Error };\n const queue: Chunk[] = [];\n let resolveNext: ((c: Chunk) => void) | null = null;\n const push = (c: Chunk) => {\n if (resolveNext) {\n const r = resolveNext;\n resolveNext = null;\n r(c);\n } else {\n queue.push(c);\n }\n };\n const next = (): Promise<Chunk> =>\n new Promise((resolve) => {\n const c = queue.shift();\n if (c) resolve(c);\n else resolveNext = resolve;\n });\n\n let lastFlush = Date.now();\n const flush = () => {\n if (pending.length === 0) return null;\n const text = pending;\n pending = '';\n lastFlush = Date.now();\n return text;\n };\n\n child.stdout?.on('data', (chunk) => {\n const text = chunk.toString();\n buf += text;\n pending += text;\n push({ kind: 'data', text });\n });\n child.stderr?.on('data', (chunk) => {\n const text = chunk.toString();\n buf += text;\n pending += text;\n push({ kind: 'data', text });\n });\n child.on('error', (err) => {\n for (const t of timers) clearTimeout(t);\n push({ kind: 'error', err });\n });\n child.on('close', (code) => {\n for (const t of timers) clearTimeout(t);\n push({ kind: 'end', code });\n });\n\n try {\n while (true) {\n const c = await next();\n if (c.kind === 'error') throw c.err;\n if (c.kind === 'end') {\n const remainder = flush();\n if (remainder !== null) {\n yield { type: 'partial_output', text: remainder };\n }\n const cleaned = stripAnsi(buf).replace(/\\r\\n?/g, '\\n');\n yield {\n type: 'final',\n output: {\n output: truncateMiddle(cleaned, MAX_OUTPUT),\n exit_code: c.code,\n timed_out: timedOut,\n },\n };\n return;\n }\n // Decide whether to flush. Time-based OR size-based to keep latency\n // low for slow-emitting commands without overwhelming the TUI for\n // chatty ones.\n const now = Date.now();\n if (pending.length >= STREAM_FLUSH_BYTES || now - lastFlush >= STREAM_FLUSH_INTERVAL_MS) {\n const text = flush();\n if (text) yield { type: 'partial_output', text };\n }\n }\n } finally {\n for (const t of timers) clearTimeout(t);\n }\n },\n};\n\n// Re-export types so consumers can narrow on stream events.\nexport type { BashInput, BashOutput };\n"]}
|
package/dist/batch-tool-use.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/batch-tool-use.ts
|
|
2
2
|
var batchToolUseTool = {
|
|
3
3
|
name: "batch_tool_use",
|
|
4
|
+
category: "Meta",
|
|
4
5
|
description: "Execute multiple tool calls in sequence or parallel. Returns all results.",
|
|
5
6
|
usageHint: "Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).",
|
|
6
7
|
permission: "confirm",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/batch-tool-use.ts"],"names":[],"mappings":"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","file":"batch-tool-use.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface BatchToolUseInput {\n calls: {\n tool: string;\n input: Record<string, unknown>;\n }[];\n stop_on_error?: boolean;\n parallel?: boolean;\n}\n\ninterface BatchToolUseOutput {\n results: {\n tool: string;\n success: boolean;\n result?: unknown;\n error?: string;\n executionMs: number;\n }[];\n total: number;\n succeeded: number;\n failed: number;\n stop_on_error: boolean;\n}\n\nexport const batchToolUseTool: Tool<BatchToolUseInput, BatchToolUseOutput> = {\n name: 'batch_tool_use',\n description: 'Execute multiple tool calls in sequence or parallel. Returns all results.',\n usageHint:\n 'Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).',\n permission: 'confirm',\n mutating: true,\n timeoutMs: 120_000,\n inputSchema: {\n type: 'object',\n properties: {\n calls: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n tool: { type: 'string' },\n input: { type: 'object' },\n },\n required: ['tool'],\n },\n description: 'Array of tool calls to execute',\n },\n stop_on_error: {\n type: 'boolean',\n description: 'Stop execution on first error (default: false)',\n },\n parallel: {\n type: 'boolean',\n description: 'Execute calls in parallel (default: true)',\n },\n },\n required: ['calls'],\n },\n async execute(input, ctx, opts) {\n if (!input?.calls || input.calls.length === 0) {\n return {\n results: [],\n total: 0,\n succeeded: 0,\n failed: 0,\n stop_on_error: false,\n };\n }\n\n const results: BatchToolUseOutput['results'] = [];\n let succeeded = 0;\n let failed = 0;\n\n if (input.parallel !== false) {\n const promises = input.calls.map(async (call) => executeSingle(call, ctx, opts));\n const allResults = await Promise.all(promises);\n results.push(...allResults);\n succeeded = allResults.filter((r) => r.success).length;\n failed = allResults.filter((r) => !r.success).length;\n } else {\n for (const call of input.calls) {\n const result = await executeSingle(call, ctx, opts);\n results.push(result);\n if (result.success) {\n succeeded++;\n } else {\n failed++;\n if (input.stop_on_error) break;\n }\n }\n }\n\n return {\n results,\n total: input.calls.length,\n succeeded,\n failed,\n stop_on_error: input.stop_on_error ?? false,\n };\n },\n};\n\nasync function executeSingle(\n call: { tool: string; input: Record<string, unknown> },\n ctx: import('@wrongstack/core').Context,\n opts: { signal: AbortSignal },\n): Promise<BatchToolUseOutput['results'][0]> {\n const start = Date.now();\n const tool = ctx.tools.find((t: Tool) => t.name === call.tool);\n\n if (!tool) {\n return {\n tool: call.tool,\n success: false,\n error: `tool \"${call.tool}\" not found`,\n executionMs: Date.now() - start,\n };\n }\n\n try {\n const result = await tool.execute(call.input, ctx, opts);\n return {\n tool: call.tool,\n success: true,\n result,\n executionMs: Date.now() - start,\n };\n } catch (e) {\n return {\n tool: call.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/batch-tool-use.ts"],"names":[],"mappings":"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","file":"batch-tool-use.js","sourcesContent":["import type { Tool } from '@wrongstack/core';\n\ninterface BatchToolUseInput {\n calls: {\n tool: string;\n input: Record<string, unknown>;\n }[];\n stop_on_error?: boolean;\n parallel?: boolean;\n}\n\ninterface BatchToolUseOutput {\n results: {\n tool: string;\n success: boolean;\n result?: unknown;\n error?: string;\n executionMs: number;\n }[];\n total: number;\n succeeded: number;\n failed: number;\n stop_on_error: boolean;\n}\n\nexport const batchToolUseTool: Tool<BatchToolUseInput, BatchToolUseOutput> = {\n name: 'batch_tool_use',\n category: 'Meta',\n description: 'Execute multiple tool calls in sequence or parallel. Returns all results.',\n usageHint:\n 'Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).',\n permission: 'confirm',\n mutating: true,\n timeoutMs: 120_000,\n inputSchema: {\n type: 'object',\n properties: {\n calls: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n tool: { type: 'string' },\n input: { type: 'object' },\n },\n required: ['tool'],\n },\n description: 'Array of tool calls to execute',\n },\n stop_on_error: {\n type: 'boolean',\n description: 'Stop execution on first error (default: false)',\n },\n parallel: {\n type: 'boolean',\n description: 'Execute calls in parallel (default: true)',\n },\n },\n required: ['calls'],\n },\n async execute(input, ctx, opts) {\n if (!input?.calls || input.calls.length === 0) {\n return {\n results: [],\n total: 0,\n succeeded: 0,\n failed: 0,\n stop_on_error: false,\n };\n }\n\n const results: BatchToolUseOutput['results'] = [];\n let succeeded = 0;\n let failed = 0;\n\n if (input.parallel !== false) {\n const promises = input.calls.map(async (call) => executeSingle(call, ctx, opts));\n const allResults = await Promise.all(promises);\n results.push(...allResults);\n succeeded = allResults.filter((r) => r.success).length;\n failed = allResults.filter((r) => !r.success).length;\n } else {\n for (const call of input.calls) {\n const result = await executeSingle(call, ctx, opts);\n results.push(result);\n if (result.success) {\n succeeded++;\n } else {\n failed++;\n if (input.stop_on_error) break;\n }\n }\n }\n\n return {\n results,\n total: input.calls.length,\n succeeded,\n failed,\n stop_on_error: input.stop_on_error ?? false,\n };\n },\n};\n\nasync function executeSingle(\n call: { tool: string; input: Record<string, unknown> },\n ctx: import('@wrongstack/core').Context,\n opts: { signal: AbortSignal },\n): Promise<BatchToolUseOutput['results'][0]> {\n const start = Date.now();\n const tool = ctx.tools.find((t: Tool) => t.name === call.tool);\n\n if (!tool) {\n return {\n tool: call.tool,\n success: false,\n error: `tool \"${call.tool}\" not found`,\n executionMs: Date.now() - start,\n };\n }\n\n try {\n const result = await tool.execute(call.input, ctx, opts);\n return {\n tool: call.tool,\n success: true,\n result,\n executionMs: Date.now() - start,\n };\n } catch (e) {\n return {\n tool: call.tool,\n success: false,\n error: e instanceof Error ? e.message : String(e),\n executionMs: Date.now() - start,\n };\n }\n}\n"]}
|
package/dist/builtin.js
CHANGED
|
@@ -125,6 +125,7 @@ function isBinaryBuffer(buf) {
|
|
|
125
125
|
// src/audit.ts
|
|
126
126
|
var auditTool = {
|
|
127
127
|
name: "audit",
|
|
128
|
+
category: "Package Management",
|
|
128
129
|
description: "Run npm/pnpm security audit. Returns vulnerabilities sorted by severity.",
|
|
129
130
|
usageHint: "Set `level` to filter minimum severity. `fix` attempts auto-fix. `packages` checks specific packages.",
|
|
130
131
|
permission: "confirm",
|
|
@@ -238,6 +239,7 @@ var STREAM_FLUSH_INTERVAL_MS = 200;
|
|
|
238
239
|
var STREAM_FLUSH_BYTES = 4 * 1024;
|
|
239
240
|
var bashTool = {
|
|
240
241
|
name: "bash",
|
|
242
|
+
category: "Shell",
|
|
241
243
|
description: "Run a shell command. stdout and stderr are merged.",
|
|
242
244
|
usageHint: "Runs via `bash -c` (or `cmd /c` on Windows). Cwd is the project root. Default timeout 30s. Output truncated from the middle if oversized. Use for git, npm, builds, tests.",
|
|
243
245
|
permission: "confirm",
|
|
@@ -417,6 +419,7 @@ var bashTool = {
|
|
|
417
419
|
// src/batch-tool-use.ts
|
|
418
420
|
var batchToolUseTool = {
|
|
419
421
|
name: "batch_tool_use",
|
|
422
|
+
category: "Meta",
|
|
420
423
|
description: "Execute multiple tool calls in sequence or parallel. Returns all results.",
|
|
421
424
|
usageHint: "Set `calls` array with tool names and inputs. `stop_on_error` halts on first failure. `parallel` runs concurrently (default: true).",
|
|
422
425
|
permission: "confirm",
|
|
@@ -518,6 +521,7 @@ async function executeSingle(call, ctx, opts) {
|
|
|
518
521
|
}
|
|
519
522
|
var diffTool = {
|
|
520
523
|
name: "diff",
|
|
524
|
+
category: "Filesystem",
|
|
521
525
|
description: "Show differences between files, commits, or branches. Supports staged vs working tree.",
|
|
522
526
|
usageHint: "Use `files` for file paths, `a`/`b` for commit refs, `staged` for git index. `mode`: unified (default), stat, side-by-side.",
|
|
523
527
|
permission: "auto",
|
|
@@ -634,6 +638,7 @@ function formatUnified(lines, context) {
|
|
|
634
638
|
}
|
|
635
639
|
var documentTool = {
|
|
636
640
|
name: "document",
|
|
641
|
+
category: "Project",
|
|
637
642
|
description: "Generate or update documentation comments for functions, classes, and types. Supports JSDoc, TSDoc, and block comments.",
|
|
638
643
|
usageHint: "Set `target` for what to document. `files` for paths. `style` for comment format. `overwrite` replaces existing docs.",
|
|
639
644
|
permission: "confirm",
|
|
@@ -782,6 +787,7 @@ function processFile(content, absPath, style, overwrite, target) {
|
|
|
782
787
|
}
|
|
783
788
|
var editTool = {
|
|
784
789
|
name: "edit",
|
|
790
|
+
category: "Filesystem",
|
|
785
791
|
description: "Make a surgical edit by replacing exact text. Fails if `old_string` is not unique unless `replace_all` is true.",
|
|
786
792
|
usageHint: "Always `read` the file first. `old_string` must be an EXACT match (whitespace included). If multiple matches exist, either narrow `old_string` with more context or set `replace_all: true`.",
|
|
787
793
|
permission: "confirm",
|
|
@@ -891,9 +897,9 @@ function findSimilarity(haystack, needle) {
|
|
|
891
897
|
return line;
|
|
892
898
|
}
|
|
893
899
|
var ALLOWED_COMMANDS = {
|
|
894
|
-
node: ["--version", "-
|
|
895
|
-
npm: ["--version", "init", "install", "test", "
|
|
896
|
-
pnpm: ["--version", "init", "install", "add", "remove", "
|
|
900
|
+
node: ["--version", "-r", "--input-type=module"],
|
|
901
|
+
npm: ["--version", "init", "install", "test", "list", "pkg", "doctor"],
|
|
902
|
+
pnpm: ["--version", "init", "install", "add", "remove", "list"],
|
|
897
903
|
npx: ["--version"],
|
|
898
904
|
git: [
|
|
899
905
|
"--version",
|
|
@@ -921,7 +927,7 @@ var ALLOWED_COMMANDS = {
|
|
|
921
927
|
mv: [],
|
|
922
928
|
rm: ["-rf"],
|
|
923
929
|
touch: [],
|
|
924
|
-
bun: ["--version", "
|
|
930
|
+
bun: ["--version", "add", "init"],
|
|
925
931
|
tsc: ["--version", "--noEmit", "--project"],
|
|
926
932
|
vitest: ["--version", "run", "--coverage"],
|
|
927
933
|
biome: ["--version", "lint", "format", "check"],
|
|
@@ -938,6 +944,7 @@ var MAX_OUTPUT2 = 2e5;
|
|
|
938
944
|
var TIMEOUT_MS = 3e4;
|
|
939
945
|
var execTool = {
|
|
940
946
|
name: "exec",
|
|
947
|
+
category: "Shell",
|
|
941
948
|
description: "Restricted shell that only runs pre-approved commands with constrained arguments. Safer alternative to `bash`.",
|
|
942
949
|
usageHint: "Set `command` (must be in allowlist). `args` passed through. For arbitrary shell access use the `bash` tool instead.",
|
|
943
950
|
permission: "confirm",
|
|
@@ -1083,6 +1090,7 @@ async function fetchWithRedirectLimit(url, maxRedirects, signal) {
|
|
|
1083
1090
|
}
|
|
1084
1091
|
var fetchTool = {
|
|
1085
1092
|
name: "fetch",
|
|
1093
|
+
category: "Network",
|
|
1086
1094
|
description: "Fetch the contents of a URL. HTML is converted to markdown by default.",
|
|
1087
1095
|
usageHint: "HTTPS only by default. Localhost and RFC1918 ranges blocked unless WRONGSTACK_FETCH_ALLOW_PRIVATE=1. Max 5 redirects, 20s timeout, 128KB cap.",
|
|
1088
1096
|
permission: "confirm",
|
|
@@ -1314,7 +1322,10 @@ function htmlToMarkdown(html) {
|
|
|
1314
1322
|
});
|
|
1315
1323
|
s = s.replace(/<(strong|b)[^>]*>([\s\S]*?)<\/\1>/gi, "**$2**");
|
|
1316
1324
|
s = s.replace(/<(em|i)[^>]*>([\s\S]*?)<\/\1>/gi, "*$2*");
|
|
1317
|
-
s = s.replace(/<a [^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi,
|
|
1325
|
+
s = s.replace(/<a [^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi, (_m, href, text) => {
|
|
1326
|
+
const safe = /^(https?|ftps?):\/\//i.test(href);
|
|
1327
|
+
return safe ? `[${text}](${href})` : text;
|
|
1328
|
+
});
|
|
1318
1329
|
s = s.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, (_m, c) => "\n```\n" + stripTags(c) + "\n```\n");
|
|
1319
1330
|
s = s.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, "`$1`");
|
|
1320
1331
|
s = s.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "- $1\n");
|
|
@@ -1331,6 +1342,7 @@ function stripTags(s) {
|
|
|
1331
1342
|
// src/format.ts
|
|
1332
1343
|
var formatTool = {
|
|
1333
1344
|
name: "format",
|
|
1345
|
+
category: "Code Quality",
|
|
1334
1346
|
description: "Format files with biome or prettier. Use `check` to verify without modifying.",
|
|
1335
1347
|
usageHint: "Set `files` (glob or comma-separated). `check` only validates. `fixer` forces tool.",
|
|
1336
1348
|
permission: "confirm",
|
|
@@ -1429,6 +1441,7 @@ var TIMEOUT_MS3 = 3e4;
|
|
|
1429
1441
|
var MAX_OUTPUT3 = 1e5;
|
|
1430
1442
|
var gitTool = {
|
|
1431
1443
|
name: "git",
|
|
1444
|
+
category: "Git",
|
|
1432
1445
|
description: "Run git commands. Wraps common operations: status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset.",
|
|
1433
1446
|
usageHint: "Prefer built-in subcommands over raw args. `command` is required. `message` for commits. `branch` for checkout/branch. `files` for status/diff. `format` for log.",
|
|
1434
1447
|
permission: "confirm",
|
|
@@ -1530,11 +1543,11 @@ function buildArgs(input) {
|
|
|
1530
1543
|
...files.length ? ["--", ...files] : []
|
|
1531
1544
|
];
|
|
1532
1545
|
case "branch":
|
|
1533
|
-
return input.branch ? ["branch", input.branch] : ["branch"];
|
|
1546
|
+
return input.branch ? ["branch", ...input.branch.startsWith("-") ? [] : [input.branch]] : ["branch"];
|
|
1534
1547
|
case "checkout":
|
|
1535
1548
|
return [
|
|
1536
1549
|
"checkout",
|
|
1537
|
-
...input.branch ? [input.branch] : [],
|
|
1550
|
+
...input.branch ? ["--", input.branch] : [],
|
|
1538
1551
|
...files.length ? ["--", ...files] : []
|
|
1539
1552
|
];
|
|
1540
1553
|
case "stash":
|
|
@@ -1593,6 +1606,7 @@ function runGit2(args, cwd, signal) {
|
|
|
1593
1606
|
var DEFAULT_IGNORE = ["node_modules", ".git", "dist", "build", ".next", "coverage", ".turbo"];
|
|
1594
1607
|
var globTool = {
|
|
1595
1608
|
name: "glob",
|
|
1609
|
+
category: "Filesystem",
|
|
1596
1610
|
description: "Find files matching a glob pattern. Returns paths sorted by mtime (newest first).",
|
|
1597
1611
|
usageHint: "Examples: `**/*.ts`, `src/**/*.test.ts`, `*.json`. Common dirs (node_modules, .git, dist) are ignored by default. Returns up to 1000 paths.",
|
|
1598
1612
|
permission: "auto",
|
|
@@ -1709,6 +1723,7 @@ function capSubject(line) {
|
|
|
1709
1723
|
var DEFAULT_IGNORE2 = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
|
|
1710
1724
|
var grepTool = {
|
|
1711
1725
|
name: "grep",
|
|
1726
|
+
category: "Search",
|
|
1712
1727
|
description: "Search file contents with a regex. Uses ripgrep when available.",
|
|
1713
1728
|
usageHint: 'Pattern is regex. Use `output_mode: "content"` for matched lines, `"files_with_matches"` for paths, `"count"` for tallies. `glob` filters files (e.g. `*.ts`).',
|
|
1714
1729
|
permission: "auto",
|
|
@@ -1741,6 +1756,10 @@ var grepTool = {
|
|
|
1741
1756
|
const base = input.path ? safeResolve(input.path, ctx) : ctx.cwd;
|
|
1742
1757
|
const mode = input.output_mode ?? "content";
|
|
1743
1758
|
const limit = Math.max(1, Math.min(input.limit ?? 200, 2e3));
|
|
1759
|
+
const validation = compileUserRegex(input.pattern, input.case_insensitive ? "i" : "");
|
|
1760
|
+
if (!validation.ok) {
|
|
1761
|
+
throw new Error(`grep: ${validation.reason}`);
|
|
1762
|
+
}
|
|
1744
1763
|
const rgAvailable = await detectRg(opts.signal);
|
|
1745
1764
|
if (rgAvailable) {
|
|
1746
1765
|
try {
|
|
@@ -1774,11 +1793,15 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
1774
1793
|
args.push("-n");
|
|
1775
1794
|
if (input.context_lines) args.push("-C", String(input.context_lines));
|
|
1776
1795
|
}
|
|
1796
|
+
for (const ignored of DEFAULT_IGNORE2) {
|
|
1797
|
+
args.push("--glob", `!${ignored}/**`, "--glob", `!**/${ignored}/**`);
|
|
1798
|
+
}
|
|
1777
1799
|
if (input.glob) args.push("--glob", input.glob);
|
|
1778
1800
|
args.push("--", input.pattern, base);
|
|
1779
1801
|
const matches = [];
|
|
1780
1802
|
let buf = "";
|
|
1781
1803
|
let totalLines = 0;
|
|
1804
|
+
let totalCount = 0;
|
|
1782
1805
|
let batchSinceFlush = 0;
|
|
1783
1806
|
const FLUSH_AT = 16;
|
|
1784
1807
|
const MAX_BUF_BYTES = 1e6;
|
|
@@ -1835,6 +1858,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
1835
1858
|
for (const line of ready.split("\n")) {
|
|
1836
1859
|
if (!line) continue;
|
|
1837
1860
|
totalLines++;
|
|
1861
|
+
if (mode === "count") totalCount += parseRgCountLine(line);
|
|
1838
1862
|
if (matches.length < limit) {
|
|
1839
1863
|
matches.push(line);
|
|
1840
1864
|
pendingBatch.push(line);
|
|
@@ -1855,6 +1879,7 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
1855
1879
|
for (const line of buf.split("\n")) {
|
|
1856
1880
|
if (!line) continue;
|
|
1857
1881
|
totalLines++;
|
|
1882
|
+
if (mode === "count") totalCount += parseRgCountLine(line);
|
|
1858
1883
|
if (matches.length < limit) {
|
|
1859
1884
|
matches.push(line);
|
|
1860
1885
|
pendingBatch.push(line);
|
|
@@ -1873,12 +1898,18 @@ async function* runRgStream(input, base, mode, limit, signal) {
|
|
|
1873
1898
|
type: "final",
|
|
1874
1899
|
output: {
|
|
1875
1900
|
matches,
|
|
1876
|
-
count: totalLines,
|
|
1901
|
+
count: mode === "count" ? totalCount : totalLines,
|
|
1877
1902
|
truncated: totalLines > limit || bufOverflow,
|
|
1878
1903
|
used: "rg"
|
|
1879
1904
|
}
|
|
1880
1905
|
};
|
|
1881
1906
|
}
|
|
1907
|
+
function parseRgCountLine(line) {
|
|
1908
|
+
const idx = line.lastIndexOf(":");
|
|
1909
|
+
if (idx === -1) return 0;
|
|
1910
|
+
const n = Number.parseInt(line.slice(idx + 1), 10);
|
|
1911
|
+
return Number.isFinite(n) ? n : 0;
|
|
1912
|
+
}
|
|
1882
1913
|
async function runNative(input, base, mode, limit, signal) {
|
|
1883
1914
|
const flags = input.case_insensitive ? "i" : "";
|
|
1884
1915
|
const compiled = compileUserRegex(input.pattern, flags);
|
|
@@ -1955,6 +1986,7 @@ async function runNative(input, base, mode, limit, signal) {
|
|
|
1955
1986
|
// src/install.ts
|
|
1956
1987
|
var installTool = {
|
|
1957
1988
|
name: "install",
|
|
1989
|
+
category: "Package Management",
|
|
1958
1990
|
description: "Install npm packages. Detects pnpm/npm/yarn and uses the right package manager.",
|
|
1959
1991
|
usageHint: "Set `packages` to install. `save` as dependency type. `global` for global install. `dry_run` to preview.",
|
|
1960
1992
|
permission: "confirm",
|
|
@@ -2048,6 +2080,7 @@ async function detectPackageManager(cwd) {
|
|
|
2048
2080
|
}
|
|
2049
2081
|
var jsonTool = {
|
|
2050
2082
|
name: "json",
|
|
2083
|
+
category: "Data",
|
|
2051
2084
|
description: "Parse, query, and validate JSON/JSON5/YAML. Use `query` with JMESPath-like paths to extract values.",
|
|
2052
2085
|
usageHint: 'Provide `file` path or `data` string. `query` supports dot notation (e.g. "results[0].name"). `format` outputs in specified format.',
|
|
2053
2086
|
permission: "auto",
|
|
@@ -2170,6 +2203,7 @@ function toYaml(data, indent = 0) {
|
|
|
2170
2203
|
// src/lint.ts
|
|
2171
2204
|
var lintTool = {
|
|
2172
2205
|
name: "lint",
|
|
2206
|
+
category: "Code Quality",
|
|
2173
2207
|
description: "Run a linter on files. Auto-detects biome, eslint, or tslint. Use `fix` to auto-fix issues.",
|
|
2174
2208
|
usageHint: "Set `files` (glob or comma-separated). `fix` applies corrections. `linter` forces specific tool.",
|
|
2175
2209
|
permission: "confirm",
|
|
@@ -2259,6 +2293,7 @@ async function detectLinter(cwd) {
|
|
|
2259
2293
|
}
|
|
2260
2294
|
var logsTool = {
|
|
2261
2295
|
name: "logs",
|
|
2296
|
+
category: "Logs",
|
|
2262
2297
|
description: "Stream or fetch logs from a service or file. Supports Docker, systemd, or plain log files.",
|
|
2263
2298
|
usageHint: "Set `service` for Docker/systemd, `path` for file. `lines` limits output. `stream` for tail -f behavior. `filter` regex filters lines.",
|
|
2264
2299
|
permission: "confirm",
|
|
@@ -2436,6 +2471,7 @@ function parseLine(line) {
|
|
|
2436
2471
|
}
|
|
2437
2472
|
var outdatedTool = {
|
|
2438
2473
|
name: "outdated",
|
|
2474
|
+
category: "Package Management",
|
|
2439
2475
|
description: "Check for outdated npm packages. Shows current, wanted, and latest versions.",
|
|
2440
2476
|
usageHint: "Set `check` to filter specific packages. `format` as list or table. `include_deprecated` shows deprecated packages.",
|
|
2441
2477
|
permission: "auto",
|
|
@@ -2547,6 +2583,7 @@ function parseOutdatedOutput(json, exitCode) {
|
|
|
2547
2583
|
}
|
|
2548
2584
|
var patchTool = {
|
|
2549
2585
|
name: "patch",
|
|
2586
|
+
category: "Filesystem",
|
|
2550
2587
|
description: "Apply a unified diff patch to files. Writes .orig and .rej files on failure.",
|
|
2551
2588
|
usageHint: "Set `patch` (the diff text). `directory` defaults to cwd. `strip` removes leading path components. `dry_run` previews.",
|
|
2552
2589
|
permission: "confirm",
|
|
@@ -2655,6 +2692,7 @@ function extractPatchedFiles(output) {
|
|
|
2655
2692
|
}
|
|
2656
2693
|
var planTool = {
|
|
2657
2694
|
name: "plan",
|
|
2695
|
+
category: "Session",
|
|
2658
2696
|
description: "Inspect or edit the strategic plan board for this session. Plans persist across resume (unlike todos). Use this to lay out the multi-step approach before diving in, then mark steps in_progress/done as the work proceeds.",
|
|
2659
2697
|
usageHint: "Set action to one of: show | add | start | done | remove | clear. Pass `title` for add. Pass `target` (item id, 1-based index, or title substring) for start/done/remove. Always returns the formatted plan plus open/total counts.",
|
|
2660
2698
|
permission: "auto",
|
|
@@ -2753,6 +2791,7 @@ function mkResult(plan, ok, message) {
|
|
|
2753
2791
|
var MAX_BYTES2 = 5 * 1024 * 1024;
|
|
2754
2792
|
var readTool = {
|
|
2755
2793
|
name: "read",
|
|
2794
|
+
category: "Filesystem",
|
|
2756
2795
|
description: "Read the contents of a file. Lines are 1-indexed and prefixed with line numbers.",
|
|
2757
2796
|
usageHint: "Read a file before editing it. Returns lines numbered like ` 1\u2192content`. Use `offset` and `limit` for large files (default reads up to 2000 lines).",
|
|
2758
2797
|
permission: "auto",
|
|
@@ -2805,6 +2844,7 @@ var readTool = {
|
|
|
2805
2844
|
var DEFAULT_IGNORE3 = ["node_modules", ".git", "dist", "build", ".next", "coverage"];
|
|
2806
2845
|
var replaceTool = {
|
|
2807
2846
|
name: "replace",
|
|
2847
|
+
category: "Transform",
|
|
2808
2848
|
description: "Batch replace a pattern across multiple files matched by glob. Returns diff for each modified file.",
|
|
2809
2849
|
usageHint: 'Use `glob` for broad patterns (e.g. "**/*.ts"). Set `dry_run: true` to preview without modifying. `files` can be a single path, comma-separated list, or glob pattern.',
|
|
2810
2850
|
permission: "confirm",
|
|
@@ -3084,6 +3124,7 @@ describe('{{Name}}', () => {
|
|
|
3084
3124
|
};
|
|
3085
3125
|
var scaffoldTool = {
|
|
3086
3126
|
name: "scaffold",
|
|
3127
|
+
category: "Project",
|
|
3087
3128
|
description: "Generate boilerplate code from built-in templates or paths. Creates package.json, source files, tests.",
|
|
3088
3129
|
usageHint: "Set `template` (npm-package, cli-tool, react-component) and `name`. `vars` for template variables. `dry_run` preview.",
|
|
3089
3130
|
permission: "confirm",
|
|
@@ -3172,6 +3213,7 @@ var MAX_RESULTS = 50;
|
|
|
3172
3213
|
var TIMEOUT_MS4 = 15e3;
|
|
3173
3214
|
var searchTool = {
|
|
3174
3215
|
name: "search",
|
|
3216
|
+
category: "Search",
|
|
3175
3217
|
description: "Search the web for information. Returns title, URL, and snippet for each result.",
|
|
3176
3218
|
usageHint: "Set `num_results` (1-50, default 10). Use `source` to pick engine: duckduckgo (default), google, bing.",
|
|
3177
3219
|
permission: "confirm",
|
|
@@ -3380,6 +3422,7 @@ function stripTags2(html) {
|
|
|
3380
3422
|
}
|
|
3381
3423
|
var testTool = {
|
|
3382
3424
|
name: "test",
|
|
3425
|
+
category: "Code Quality",
|
|
3383
3426
|
description: "Run tests with vitest, jest, or mocha. Returns pass/fail counts and output.",
|
|
3384
3427
|
usageHint: "Set `files` for specific tests. `watch` enables watch mode. `coverage` generates coverage report. `grep` filters by name.",
|
|
3385
3428
|
permission: "confirm",
|
|
@@ -3527,6 +3570,7 @@ function parseResult(runner, result, duration) {
|
|
|
3527
3570
|
// src/todo.ts
|
|
3528
3571
|
var todoTool = {
|
|
3529
3572
|
name: "todo",
|
|
3573
|
+
category: "Session",
|
|
3530
3574
|
description: "Replace the current todo list with a new set of items.",
|
|
3531
3575
|
usageHint: "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.",
|
|
3532
3576
|
permission: "auto",
|
|
@@ -3577,6 +3621,7 @@ var todoTool = {
|
|
|
3577
3621
|
// src/tool-help.ts
|
|
3578
3622
|
var toolHelpTool = {
|
|
3579
3623
|
name: "tool_help",
|
|
3624
|
+
category: "Meta",
|
|
3580
3625
|
description: "Get help and usage information for a specific tool or list all available tools.",
|
|
3581
3626
|
usageHint: "Set `tool` for specific help. Omit to list all tools. `format`: short (one-liner), full (schema), markdown (formatted).",
|
|
3582
3627
|
permission: "auto",
|
|
@@ -3698,6 +3743,7 @@ function formatAllToolsMarkdown(tools) {
|
|
|
3698
3743
|
// src/tool-search.ts
|
|
3699
3744
|
var toolSearchTool = {
|
|
3700
3745
|
name: "tool_search",
|
|
3746
|
+
category: "Meta",
|
|
3701
3747
|
description: "Search available tools by name, description, tags, permission level, or mutating flag.",
|
|
3702
3748
|
usageHint: "Set `query` for keyword search. `tags` to filter by category. `permission` to filter by required permission. `mutating` to filter by mutating flag.",
|
|
3703
3749
|
permission: "auto",
|
|
@@ -3765,6 +3811,7 @@ var toolSearchTool = {
|
|
|
3765
3811
|
// src/tool-use.ts
|
|
3766
3812
|
var toolUseTool = {
|
|
3767
3813
|
name: "tool_use",
|
|
3814
|
+
category: "Meta",
|
|
3768
3815
|
description: "Execute a specific tool by name with given input. Useful when the agent knows exactly which tool to call.",
|
|
3769
3816
|
usageHint: "Set `tool` with exact tool name and `input` with the tool parameters. Returns result or error.",
|
|
3770
3817
|
permission: "confirm",
|
|
@@ -3840,6 +3887,7 @@ var DEFAULT_IGNORE4 = [
|
|
|
3840
3887
|
];
|
|
3841
3888
|
var treeTool = {
|
|
3842
3889
|
name: "tree",
|
|
3890
|
+
category: "Filesystem",
|
|
3843
3891
|
description: "Display directory structure as an ASCII tree. Shows files and folders with indentation.",
|
|
3844
3892
|
usageHint: "Set `path` (default: cwd). `depth` limits nesting (default: 3). `glob` filters files. `exclude` ignores dirs. `show_files` toggles file listing (default: true).",
|
|
3845
3893
|
permission: "auto",
|
|
@@ -3994,6 +4042,7 @@ async function walkDir(dir, depth, opts) {
|
|
|
3994
4042
|
}
|
|
3995
4043
|
var typecheckTool = {
|
|
3996
4044
|
name: "typecheck",
|
|
4045
|
+
category: "Code Quality",
|
|
3997
4046
|
description: "Run TypeScript type checking with `tsc --noEmit`. Checks for type errors without compiling.",
|
|
3998
4047
|
usageHint: "Set `project` for tsconfig path (default: nearest). `strict` enables strictest flags. `all` checks all projects in workspace.",
|
|
3999
4048
|
permission: "confirm",
|
|
@@ -4073,6 +4122,7 @@ async function findTsConfig(cwd) {
|
|
|
4073
4122
|
}
|
|
4074
4123
|
var writeTool = {
|
|
4075
4124
|
name: "write",
|
|
4125
|
+
category: "Filesystem",
|
|
4076
4126
|
description: "Write or overwrite a file. For existing files, prefer `edit` over `write`.",
|
|
4077
4127
|
usageHint: "Use `write` for new files or full replacements. For partial edits use `edit`. Existing files must have been `read` first in this session.",
|
|
4078
4128
|
permission: "confirm",
|