@zhanngning/hecode 0.7.0 → 1.0.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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../node_modules/@anthropic-ai/sdk/src/tools/agent-toolset/node.ts","../node_modules/@anthropic-ai/sdk/src/helpers/beta/json-schema.ts","../node_modules/@anthropic-ai/sdk/src/tools/agent-toolset/fs-util.ts","../node_modules/@anthropic-ai/sdk/src/tools/agent-toolset/skills.ts"],"sourcesContent":["/**\n * Node implementation of the `agent_toolset_20260401` tools — `bash`, `read`,\n * `write`, `edit`, `glob`, `grep` — plus the workdir/skills\n * {@link AgentToolContext}.\n *\n * This mirrors `@anthropic-ai/sdk/tools/memory/node`: it is the explicit,\n * Node-only entry point for these implementations. Importing it pulls in\n * `node:child_process`, `node:fs`, etc., so it is kept separate from the rest of\n * the SDK — depending on it is an opt-in.\n *\n * **Node 22+ is required** for this module: the `glob` tool uses the native\n * `fs.glob`, added in Node 22. The rest of the SDK still supports Node 18+; only\n * the agent toolset has this requirement.\n *\n * The result of {@link betaAgentToolset20260401} is a plain `BetaRunnableTool[]`;\n * hand it to any tool runner — `client.beta.messages.toolRunner({ …, tools })`\n * for the Messages API, or `client.beta.sessions.events.toolRunner({ …, tools })`\n * for a managed-agents session:\n *\n * ```ts\n * import { betaAgentToolset20260401 } from '@anthropic-ai/sdk/tools/agent-toolset/node';\n *\n * const tools = betaAgentToolset20260401({ workdir: '/work' });\n * const tools2 = betaAgentToolset20260401({ workdir: '/work' }).filter((t) => t.name !== 'bash');\n * ```\n *\n * Trust model: the file tools confine to `workdir` (symlink-aware) and are safe\n * without a sandbox; `bash` is unrestricted and should run inside one. See\n * {@link AgentToolContext}.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as fssync from 'node:fs';\nimport * as path from 'node:path';\nimport * as cp from 'node:child_process';\nimport * as crypto from 'node:crypto';\nimport * as readline from 'node:readline';\nimport type { Anthropic } from '../../client';\nimport { AnthropicError } from '../../core/error';\nimport type { BetaRunnableTool } from '../../lib/tools/BetaRunnableTool';\nimport { ToolError } from '../../lib/tools/ToolError';\nimport { betaTool } from '../../helpers/beta/json-schema';\nimport { promiseWithResolvers } from '../../internal/utils/promise';\nimport { atomicWriteFile, confineToRoot, DIR_CREATE_MODE, fsErrorMessage } from './fs-util';\n\nexport { setupSkills, resolveSkillVersion, extractSkillArchive } from './skills';\n\nconst BASH_OUTPUT_LIMIT = 100 * 1024;\nconst BASH_DEFAULT_TIMEOUT_MS = 120_000;\n// Default size cap for the read/edit tools (both load the whole file into\n// memory) when AgentToolContext.maxFileBytes is unset. The reject-vs-truncate\n// behaviour remains a separate question pending CMA validation.\nconst DEFAULT_MAX_FILE_BYTES = 256 * 1024;\nconst GREP_OUTPUT_LIMIT = 100 * 1024;\nconst GREP_MAX_LINE_LENGTH = 2000;\nconst GLOB_RESULT_LIMIT = 200;\n\nconst ANSI_RE = /\\x1b\\[[0-9;?]*[ -/]*[@-~]/g;\n\n// `fs.glob` is Node 22+. `@types/node` may still target an older line, so the\n// surface this module uses is typed locally rather than relying on the ambient\n// declaration. At runtime this module requires Node 22 (see the file header).\ntype GlobFn = (\n pattern: string,\n options: {\n cwd?: string;\n withFileTypes?: boolean;\n exclude?: (entry: fssync.Dirent) => boolean;\n },\n) => AsyncIterable<fssync.Dirent>;\nconst fsGlob = (fs as unknown as { glob: GlobFn }).glob;\n\nfunction resolveMaxBytes(configured: number | null | undefined): number | null {\n return configured === undefined ? DEFAULT_MAX_FILE_BYTES : configured;\n}\n\n/**\n * Workdir + path-policy for the agent toolset.\n *\n * Trust model — two tiers:\n *\n * - The file tools ({@link betaReadTool}, {@link betaWriteTool},\n * {@link betaEditTool}, {@link betaGlobTool}, {@link betaGrepTool}) confine to\n * `workdir` unless `unrestrictedPaths` is set. {@link resolvePath}\n * canonicalizes the target (resolving every symlink, including the leaf)\n * before the check *and* returns that canonical path for the operation, so a\n * symlink inside the workdir that points outside it neither passes the check\n * nor gets followed afterwards — this is a real boundary, not a lexical hint\n * (modulo the residual TOCTOU noted on {@link resolvePath}).\n * - {@link betaBashTool} runs an unrestricted `/bin/bash` and cannot be\n * confined. Run it — and, for defense in depth, the whole toolset — inside a\n * sandbox the host controls (e.g. a self-hosted environment runner).\n */\nexport interface AgentToolContext {\n /** Base directory for resolving relative tool paths. */\n workdir: string;\n /**\n * When `false` (default), the file tools reject absolute paths and paths\n * that escape `workdir` (symlinks resolved). Does **not** constrain\n * {@link betaBashTool}.\n */\n unrestrictedPaths?: boolean;\n /**\n * Anthropic client. Optional — the bare toolset needs no client; it is only\n * used by `setupSkills`, which (together with {@link AgentToolContext.sessionId})\n * fetches the session's resolved agent and downloads each of its skills into\n * `{workdir}/skills/<name>/`.\n */\n client?: Anthropic;\n /** Session whose agent's skills `setupSkills` should download. */\n sessionId?: string;\n /**\n * Optional environment for the bash subprocess. When unset, the bash tool\n * inherits the process environment with the runner's `ANTHROPIC_*`\n * credentials scrubbed. When provided, it FULLY REPLACES that default\n * environment — the mapping is used verbatim and is NOT merged with or added\n * to the scrubbed process environment. To keep the defaults plus extra vars,\n * build the combined mapping yourself before passing it.\n */\n env?: NodeJS.ProcessEnv;\n /**\n * Size cap for the `read` and `edit` tools, which both load the whole file into\n * memory. `undefined` (default) uses the built-in 256 KiB cap; a positive number\n * sets a custom cap; `null` disables the cap entirely. Disabling it reintroduces\n * the OOM risk on a model-controlled path, so pass `null` only when the sandbox\n * can absorb arbitrarily large files. The non-regular-file (FIFO/device) guard\n * always applies regardless of this value.\n */\n maxFileBytes?: number | null;\n}\n\n/**\n * Returns the `agent_toolset_20260401` implementations bound to `ctx`. The\n * result is a plain array of `BetaRunnableTool`; filter or extend it before\n * handing it to a tool runner:\n *\n * ```ts\n * const tools = [...betaAgentToolset20260401(ctx), myCustomTool];\n * const tools = betaAgentToolset20260401(ctx).filter((t) => t.name !== 'grep');\n * ```\n *\n * Concurrency note: `client.beta.sessions.events.toolRunner` dispatches a\n * session's tool calls serially (the sessions API delivers one `agent.tool_use`\n * at a time). `client.beta.messages.toolRunner` runs a turn's `tool.run` calls\n * via `Promise.all`. The toolset below is safe under either model —\n * {@link betaBashTool} serializes its persistent shell internally and the FS\n * tools are independent per call — but {@link betaEditTool}/{@link betaWriteTool}\n * cannot synchronize concurrent writes to the *same* file across processes, so a\n * multi-edit turn touching one path is still subject to inherent FS lost-update\n * races. Custom tools that close over mutable state should do their own queueing.\n */\nexport function betaAgentToolset20260401(ctx: AgentToolContext): BetaRunnableTool[] {\n return [\n betaBashTool(ctx),\n betaReadTool(ctx),\n betaWriteTool(ctx),\n betaEditTool(ctx),\n betaGlobTool(ctx),\n betaGrepTool(ctx),\n ];\n}\n\n/**\n * Resolve `p` relative to `ctx.workdir`. Unless `unrestrictedPaths` is set,\n * absolute inputs are rejected and the **canonical** path is returned — every\n * symlink in `p` (including the leaf, even a dangling one) is resolved before\n * the workdir check, and the resolved path is what the tool then operates on, so\n * a symlink inside the workdir that points outside it can neither pass the check\n * nor be followed afterwards. See the trust model on {@link AgentToolContext}.\n *\n * Residual TOCTOU: a component could still be swapped for a symlink between this\n * call and the eventual `fs` operation. Closing that fully needs per-component\n * `O_NOFOLLOW`/`openat`, which Node does not expose ergonomically; the same\n * residual exposure exists in `tools/memory/node` and is why a sandbox is still\n * recommended for the toolset as a whole.\n */\nexport function resolvePath(ctx: AgentToolContext, p: string): Promise<string> {\n return confineToRoot(ctx.workdir, p, { allowOutside: ctx.unrestrictedPaths ?? false });\n}\n\n// ---- bash ----------------------------------------------------------------\n\n/**\n * Build the environment for the spawned bash shell. The runner process holds\n * Anthropic credentials in `ANTHROPIC_*` env vars — the API key, the auth token,\n * and the per-work session token among them. `bash` runs an unrestricted shell,\n * so any command the agent runs could read those straight out of `process.env`;\n * strip the whole `ANTHROPIC_*` namespace from the child's environment.\n * Everything else (PATH, HOME, locale, …) is passed through unchanged.\n *\n * Passing an explicit `env` to {@link AgentToolContext} does NOT add to this\n * default — it FULLY REPLACES it. The provided mapping becomes the entire bash\n * environment verbatim; nothing here is merged in, so callers who want the\n * scrubbed process environment plus extras must build that mapping themselves.\n */\nfunction scrubbedShellEnv(): NodeJS.ProcessEnv {\n const env: NodeJS.ProcessEnv = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (key.startsWith('ANTHROPIC_')) continue;\n env[key] = value;\n }\n return env;\n}\n\n/**\n * A persistent /bin/bash process. State (cwd, env, background jobs) survives\n * across exec() calls. Uses pipes rather than a PTY so input is never echoed.\n */\nexport class BashSession {\n #proc: cp.ChildProcessWithoutNullStreams;\n #buf = '';\n #truncated = false;\n #closed = false;\n // While a command is in flight, the resolver to fire once its sentinel lands\n // in `#buf` (or once the shell dies). Event-driven: no polling loop.\n #waiting: { sentinel: string; resolve: () => void } | null = null;\n\n constructor(dir: string, env: NodeJS.ProcessEnv = scrubbedShellEnv()) {\n this.#proc = cp.spawn('/bin/bash', ['--noprofile', '--norc'], {\n cwd: dir,\n // `env` is the full base environment (the scrubbed process env by\n // default, or the verbatim replacement from `AgentToolContext.env`).\n // PS1/PS2/TERM are shell-control settings BashSession always applies so\n // the pipe-based sentinel exec parsing works — not part of the\n // user-facing environment.\n env: { ...env, PS1: '', PS2: '', TERM: 'dumb' },\n stdio: ['pipe', 'pipe', 'pipe'],\n detached: true,\n });\n this.#proc.stdout.setEncoding('utf8');\n this.#proc.stderr.setEncoding('utf8');\n this.#proc.stdout.on('data', (d: string) => this.#append(d));\n this.#proc.stderr.on('data', (d: string) => this.#append(d));\n this.#proc.once('close', () => {\n this.#closed = true;\n // Wake any in-flight exec so it fails fast instead of waiting for its deadline.\n const w = this.#waiting;\n this.#waiting = null;\n w?.resolve();\n });\n }\n\n /** Whether the underlying shell process has exited. */\n get closed(): boolean {\n return this.#closed;\n }\n\n // Cap the buffer during accumulation so a command that streams unboundedly\n // can't OOM the runner. Keeps the tail so the sentinel stays detectable.\n // Also resolves the in-flight exec the instant its sentinel is buffered.\n #append(d: string): void {\n this.#buf += d;\n if (this.#buf.length > BASH_OUTPUT_LIMIT) {\n this.#buf = this.#buf.slice(this.#buf.length - BASH_OUTPUT_LIMIT);\n this.#truncated = true;\n }\n if (this.#waiting && this.#buf.indexOf(this.#waiting.sentinel) >= 0) {\n const w = this.#waiting;\n this.#waiting = null;\n w.resolve();\n }\n }\n\n async exec(\n command: string,\n opts: { timeoutMs?: number; signal?: AbortSignal | null | undefined } = {},\n ): Promise<{ output: string; exitCode: number }> {\n if (this.#closed) {\n throw new AnthropicError('bash session terminated');\n }\n const timeoutMs = opts.timeoutMs ?? BASH_DEFAULT_TIMEOUT_MS;\n const signal = opts.signal;\n if (signal?.aborted) {\n throw new AnthropicError('bash command aborted');\n }\n this.#buf = '';\n this.#truncated = false;\n // Per-call nonce so a command that prints a fixed marker can't spoof the\n // exit-code framing. The `''` split keeps the literal out of what we write\n // to stdin — only the shell's printf reassembles it.\n const sentinel = `__ANT_CMD_${crypto.randomUUID()}_DONE__`;\n const sentinelSplit = `${sentinel.slice(0, 8)}''${sentinel.slice(8)}`;\n // </dev/null: a stdin-reading command (`cat`, `read`) gets EOF instead of\n // blocking on the shared pipe until the timeout.\n const wrapped = `{ ${command}\\n} </dev/null 2>&1; printf '\\\\n${sentinelSplit}%d\\\\n' $?\\n`;\n this.#proc.stdin.write(wrapped);\n\n if (this.#buf.indexOf(sentinel) < 0) {\n // Park until the sentinel lands, the deadline passes, the caller aborts,\n // or the shell dies — whichever comes first. `#append` (and the `close`\n // handler) resolve `sentinelSeen`; the deadline / abort reject.\n const { promise: sentinelSeen, resolve } = promiseWithResolvers<void>();\n this.#waiting = { sentinel, resolve };\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n try {\n await Promise.race([\n sentinelSeen,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new AnthropicError(`bash command timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n }),\n new Promise<never>((_, reject) => {\n if (!signal) return;\n onAbort = () => reject(new AnthropicError('bash command aborted'));\n signal.addEventListener('abort', onAbort, { once: true });\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n if (onAbort && signal) signal.removeEventListener('abort', onAbort);\n this.#waiting = null;\n }\n }\n\n const idx = this.#buf.indexOf(sentinel);\n if (idx < 0) {\n // The shell closed (or was killed) before emitting the sentinel.\n throw new AnthropicError('bash session terminated');\n }\n const tail = this.#buf.slice(idx + sentinel.length);\n const m = tail.match(/^(-?\\d+)/);\n const exitCode = m ? parseInt(m[1]!, 10) : -1;\n let out = this.#buf.slice(0, idx).replace(ANSI_RE, '').replace(/\\n+$/, '');\n if (this.#truncated) {\n out = `[output truncated]\\n${out}`;\n }\n return { output: out, exitCode };\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n const w = this.#waiting;\n this.#waiting = null;\n w?.resolve();\n this.#proc.stdout.destroy();\n this.#proc.stderr.destroy();\n this.#proc.stdin.destroy();\n try {\n // Negative PID targets the process group so foreground jobs (e.g. a\n // hung sleep) die with the shell.\n process.kill(-this.#proc.pid!, 'SIGKILL');\n } catch {\n this.#proc.kill('SIGKILL');\n }\n this.#proc.unref();\n }\n}\n\nexport function betaBashTool(ctx: AgentToolContext): BetaRunnableTool {\n let session: BashSession | undefined;\n // Concurrent run() callers chain onto this promise so writes to the shared\n // shell's stdin can't interleave (which would corrupt the sentinel-match\n // exit-code parsing in BashSession.exec). Each call replaces `tail` with a\n // promise that resolves only after its own exec settles.\n let tail: Promise<unknown> = Promise.resolve();\n return betaTool({\n name: 'bash',\n description: 'Run a bash command in a persistent shell. State (cwd, env vars) persists across calls.',\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string', description: 'The command to run' },\n restart: { type: 'boolean', description: 'Restart the persistent shell before running' },\n timeout_ms: { type: 'integer', description: 'Per-call timeout in milliseconds' },\n },\n },\n run: async ({ command, restart, timeout_ms }, context) => {\n const prev = tail;\n const gate = promiseWithResolvers<void>();\n tail = gate.promise;\n // Swallow prior rejections — earlier callers got their own error path;\n // we just need to wait for the shell to be free.\n try {\n await prev;\n } catch {\n // ignore\n }\n try {\n if (restart) {\n session?.close();\n session = undefined;\n }\n if (!command) {\n if (restart) return 'bash session restarted';\n throw new ToolError('bash: command is required');\n }\n session ??= new BashSession(ctx.workdir, ctx.env);\n try {\n const { output, exitCode } = await session.exec(command, {\n timeoutMs: timeout_ms ?? BASH_DEFAULT_TIMEOUT_MS,\n signal: context?.signal,\n });\n if (exitCode !== 0) throw new ToolError(output || `exit ${exitCode}`);\n return output;\n } catch (e) {\n if (e instanceof ToolError) throw e;\n // Timeout, abort, or terminated: the still-running command will emit\n // a stale sentinel, so discard this session and let the next call\n // start fresh.\n session.close();\n session = undefined;\n throw new ToolError(`bash: ${e instanceof Error ? e.message : String(e)}`);\n }\n } finally {\n gate.resolve();\n }\n },\n close: () => {\n session?.close();\n session = undefined;\n },\n });\n}\n\n// ---- fs ------------------------------------------------------------------\n\nexport function betaReadTool(ctx: AgentToolContext): BetaRunnableTool {\n return betaTool({\n name: 'read',\n description: 'Read a UTF-8 text file relative to the workdir.',\n inputSchema: {\n type: 'object',\n properties: {\n file_path: { type: 'string' },\n view_range: {\n type: 'array',\n items: { type: 'integer' },\n description: '[start_line, end_line] 1-indexed inclusive',\n },\n },\n required: ['file_path'],\n },\n run: async ({ file_path, view_range }) => {\n if (!file_path) throw new ToolError('read: file_path is required');\n const abs = await resolvePath(ctx, file_path);\n let data: string;\n try {\n // stat() before any open(): the size cap stops a multi-GB file from\n // OOM'ing the runner, and isFile() rejects FIFOs/devices/dirs without\n // opening them (open() on an unconnected FIFO blocks indefinitely).\n const st = await fs.stat(abs);\n if (!st.isFile()) {\n throw new ToolError(`read: ${file_path} is not a regular file`);\n }\n const limit = resolveMaxBytes(ctx.maxFileBytes);\n if (limit !== null && st.size > limit) {\n throw new ToolError(\n `read: ${file_path} is ${st.size} bytes, exceeds ${limit}-byte limit. ` +\n 'Use bash (head/tail/sed) to read a slice.',\n );\n }\n data = await fs.readFile(abs, 'utf8');\n } catch (e) {\n if (e instanceof ToolError) throw e;\n throw new ToolError(`read: ${fsErrorMessage(e, file_path)}`);\n }\n if (!view_range) return data;\n if (view_range.length !== 2) throw new ToolError('read: view_range must be [start_line, end_line]');\n const [startLine, endLine] = view_range as [number, number];\n const lines = data.split('\\n');\n const start = Math.max(0, startLine - 1);\n const end = endLine > 0 ? endLine : lines.length;\n return lines.slice(start, end).join('\\n');\n },\n });\n}\n\nexport function betaWriteTool(ctx: AgentToolContext): BetaRunnableTool {\n return betaTool({\n name: 'write',\n description: 'Write a UTF-8 text file relative to the workdir, creating parent directories as needed.',\n inputSchema: {\n type: 'object',\n properties: { file_path: { type: 'string' }, content: { type: 'string' } },\n required: ['file_path', 'content'],\n },\n run: async ({ file_path, content }) => {\n if (!file_path) throw new ToolError('write: file_path is required');\n const abs = await resolvePath(ctx, file_path);\n try {\n await fs.mkdir(path.dirname(abs), { recursive: true, mode: DIR_CREATE_MODE });\n await atomicWriteFile(abs, content ?? '');\n } catch (e) {\n throw new ToolError(`write: ${fsErrorMessage(e, file_path)}`);\n }\n return `wrote ${Buffer.byteLength(content ?? '')} bytes to ${file_path}`;\n },\n });\n}\n\nexport function betaEditTool(ctx: AgentToolContext): BetaRunnableTool {\n return betaTool({\n name: 'edit',\n description:\n 'Replace old_string with new_string in a file. old_string must be unique unless replace_all.',\n inputSchema: {\n type: 'object',\n properties: {\n file_path: { type: 'string' },\n old_string: { type: 'string' },\n new_string: { type: 'string' },\n replace_all: { type: 'boolean' },\n },\n required: ['file_path', 'old_string', 'new_string'],\n },\n run: async ({ file_path, old_string, new_string, replace_all }) => {\n if (!file_path) throw new ToolError('edit: file_path is required');\n if (!old_string) throw new ToolError('edit: old_string is required');\n const abs = await resolvePath(ctx, file_path);\n let data: string;\n try {\n // stat() before any open() — same guard as `read`: the size cap stops a\n // multi-GB file from OOM'ing the runner, and isFile() rejects\n // FIFOs/devices/dirs without opening them (open() on an unconnected FIFO\n // blocks indefinitely). The edit path is model-controlled, so it needs\n // the same bound `read` already has.\n const st = await fs.stat(abs);\n if (!st.isFile()) {\n throw new ToolError(`edit: ${file_path} is not a regular file`);\n }\n const limit = resolveMaxBytes(ctx.maxFileBytes);\n if (limit !== null && st.size > limit) {\n throw new ToolError(\n `edit: ${file_path} is ${st.size} bytes, exceeds ${limit}-byte limit. ` +\n 'Use bash (sed/awk) to edit a large file.',\n );\n }\n data = await fs.readFile(abs, 'utf8');\n } catch (e) {\n if (e instanceof ToolError) throw e;\n throw new ToolError(`edit: ${fsErrorMessage(e, file_path)}`);\n }\n const count = data.split(old_string).length - 1;\n if (count === 0) throw new ToolError(`edit: old_string not found in ${file_path}`);\n let updated: string;\n if (replace_all) {\n updated = data.split(old_string).join(new_string);\n } else {\n if (count > 1)\n throw new ToolError(`edit: old_string appears ${count} times in ${file_path} (must be unique)`);\n // Callback form so `$&`/`$1`/`` $` `` in new_string are inserted\n // literally instead of expanded as replacement patterns.\n updated = data.replace(old_string, () => new_string);\n }\n try {\n await atomicWriteFile(abs, updated);\n } catch (e) {\n throw new ToolError(`edit: write: ${fsErrorMessage(e, file_path)}`);\n }\n return `edited ${file_path} (${replace_all ? count : 1} replacement(s))`;\n },\n });\n}\n\n// ---- search --------------------------------------------------------------\n\nexport function betaGlobTool(ctx: AgentToolContext): BetaRunnableTool {\n return betaTool({\n name: 'glob',\n description:\n 'Match files under the workdir against a glob pattern. Results are mtime-sorted, newest first.',\n inputSchema: {\n type: 'object',\n properties: {\n pattern: { type: 'string' },\n path: { type: 'string', description: 'Directory to search in. Defaults to the workdir.' },\n },\n required: ['pattern'],\n },\n run: async ({ pattern, path: searchPath }) => {\n if (!pattern) throw new ToolError('glob: pattern is required');\n let root = path.resolve(ctx.workdir);\n let pat = pattern;\n if (path.isAbsolute(pattern)) {\n if (!ctx.unrestrictedPaths) throw new ToolError('glob: absolute pattern not permitted');\n root = path.parse(pattern).root;\n pat = path.relative(root, pattern);\n } else if (searchPath) {\n root = await resolvePath(ctx, searchPath);\n }\n // A `..` in the *pattern itself* (e.g. `../../*`) walks `fs.glob` out of\n // the search root — this is separate from the `searchPath` confinement\n // above, which only covers the path argument. Reject it outright when the\n // toolset is confined.\n if (!ctx.unrestrictedPaths && pat.split(/[\\\\/]/).includes('..')) {\n throw new ToolError('glob: \"..\" is not permitted in the pattern');\n }\n const matches: { path: string; mtime: number }[] = [];\n try {\n // Native `fs.glob` (Node 22+). `exclude` prunes the noisy dirs the\n // legacy walker skipped; only regular files are collected.\n for await (const entry of fsGlob(pat, {\n cwd: root,\n withFileTypes: true,\n exclude: (d) => d.name === '.git' || d.name === 'node_modules',\n })) {\n if (!entry.isFile()) continue;\n const full = path.join(entry.parentPath, entry.name);\n // Defense in depth: drop any match that resolved outside the search\n // root (e.g. via a symlinked directory in the tree) when confined.\n if (!ctx.unrestrictedPaths && !isWithin(root, full)) continue;\n let mtime = 0;\n try {\n mtime = (await fs.stat(full)).mtimeMs;\n } catch {\n // unreadable — keep it in the list with mtime 0\n }\n matches.push({ path: full, mtime });\n }\n } catch (e) {\n throw new ToolError(`glob: ${e instanceof Error ? e.message : String(e)}`);\n }\n if (matches.length === 0) return 'no matches';\n matches.sort((a, b) => b.mtime - a.mtime);\n return matches\n .slice(0, GLOB_RESULT_LIMIT)\n .map((m) => m.path)\n .join('\\n');\n },\n });\n}\n\nexport function betaGrepTool(ctx: AgentToolContext): BetaRunnableTool {\n return betaTool({\n name: 'grep',\n description: 'Search file contents for a regex. Uses ripgrep if available, otherwise a built-in walker.',\n inputSchema: {\n type: 'object',\n properties: { pattern: { type: 'string' }, path: { type: 'string' } },\n required: ['pattern'],\n },\n run: async ({ pattern, path: p }, context) => {\n if (!pattern) throw new ToolError('grep: pattern is required');\n let searchPath = path.resolve(ctx.workdir);\n if (p) searchPath = await resolvePath(ctx, p);\n const rg = await findRg();\n return rg ?\n runRipgrep(rg, pattern, searchPath, context?.signal)\n : runWalkGrep(pattern, searchPath, context?.signal);\n },\n });\n}\n\nfunction runRipgrep(\n rg: string,\n pattern: string,\n searchPath: string,\n signal?: AbortSignal | null | undefined,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = cp.spawn(rg, ['-n', '--no-heading', '-e', pattern, '--', searchPath], {\n ...(signal ? { signal } : {}),\n });\n let out = '';\n let errOut = '';\n let truncated = false;\n proc.stdout.on('data', (d) => {\n if (truncated) return;\n out += d;\n if (out.length > GREP_OUTPUT_LIMIT) {\n truncated = true;\n out = out.slice(0, GREP_OUTPUT_LIMIT);\n proc.kill('SIGKILL');\n }\n });\n proc.stderr.on('data', (d) => (errOut += d));\n proc.on('close', (code) => {\n if (signal?.aborted) return reject(new ToolError('grep: aborted'));\n if (truncated) return resolve(out + `\\n[output truncated at ${GREP_OUTPUT_LIMIT} bytes]`);\n if (code === 0) return resolve(out);\n if (code === 1) return resolve('no matches');\n reject(new ToolError(`grep: rg failed: ${errOut || `exit ${code}`}`));\n });\n proc.on('error', (e) => {\n if (signal?.aborted) return reject(new ToolError('grep: aborted'));\n reject(new ToolError(`grep: rg failed: ${e.message}`));\n });\n });\n}\n\nasync function runWalkGrep(\n pattern: string,\n root: string,\n signal?: AbortSignal | null | undefined,\n): Promise<string> {\n let re: RegExp;\n try {\n re = new RegExp(pattern);\n } catch (e) {\n throw new ToolError(`grep: invalid regex: ${e instanceof Error ? e.message : String(e)}`);\n }\n const hits: string[] = [];\n let budget = GREP_OUTPUT_LIMIT;\n const push = (line: string): boolean => {\n budget -= line.length + 1;\n if (budget < 0) {\n hits.push(`[output truncated at ${GREP_OUTPUT_LIMIT} bytes]`);\n return false;\n }\n hits.push(line);\n return true;\n };\n const stat = await fs.stat(root).catch(() => null);\n if (stat?.isFile()) {\n await grepFile(root, re, push);\n } else {\n await walk(root, '', (rel) => grepFile(path.join(root, rel), re, push), signal);\n }\n if (signal?.aborted) throw new ToolError('grep: aborted');\n if (hits.length === 0) return 'no matches';\n return hits.join('\\n');\n}\n\nasync function grepFile(file: string, re: RegExp, push: (line: string) => boolean): Promise<boolean> {\n const stream = fssync.createReadStream(file, { encoding: 'utf8' });\n const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });\n let i = 0;\n try {\n for await (const line of rl) {\n i++;\n // Cap line length: `pattern` is model-supplied and JS regexes backtrack,\n // so a pathological pattern against a very long line is a ReDoS.\n if (line.length > GREP_MAX_LINE_LENGTH) continue;\n if (re.test(line) && !push(`${file}:${i}:${line}`)) return false;\n }\n } catch {\n // unreadable / binary\n } finally {\n stream.destroy();\n }\n return true;\n}\n\n// ---- utils ---------------------------------------------------------------\n\n/** True when `p` is `root` itself or lexically contained within it. */\nfunction isWithin(root: string, p: string): boolean {\n const rel = path.relative(root, p);\n return rel === '' || (!rel.startsWith('..' + path.sep) && rel !== '..' && !path.isAbsolute(rel));\n}\n\nconst WALK_MAX_DEPTH = 40;\nconst WALK_MAX_ENTRIES = 50_000;\n\n/**\n * Bounded recursive walk. `fn` may return `false` to abort. Only real\n * directories are descended into and only real files are handed to `fn` —\n * symlinks (and devices/fifos/sockets) are skipped entirely so a symlink inside\n * the root cannot be followed out of it.\n */\nasync function walk(\n root: string,\n rel: string,\n fn: (rel: string) => boolean | void | Promise<boolean | void>,\n signal?: AbortSignal | null | undefined,\n): Promise<void> {\n let remaining = WALK_MAX_ENTRIES;\n async function inner(rel: string, depth: number): Promise<boolean> {\n if (depth > WALK_MAX_DEPTH) return true;\n if (signal?.aborted) return false;\n let entries: fssync.Dirent[];\n try {\n entries = await fs.readdir(path.join(root, rel), { withFileTypes: true });\n } catch {\n return true;\n }\n for (const e of entries) {\n if (e.name === '.git' || e.name === 'node_modules') continue;\n if (remaining-- <= 0) return false;\n if (signal?.aborted) return false;\n const childRel = rel ? path.join(rel, e.name) : e.name;\n if (e.isDirectory()) {\n if (!(await inner(childRel, depth + 1))) return false;\n } else if (e.isFile()) {\n if ((await fn(childRel)) === false) return false;\n }\n // Symlinks, devices, fifos and sockets are intentionally skipped.\n }\n return true;\n }\n await inner(rel, 0);\n}\n\nasync function findRg(): Promise<string | null> {\n const dirs = (process.env['PATH'] ?? '').split(path.delimiter);\n for (const d of dirs) {\n const candidate = path.join(d, 'rg');\n try {\n await fs.access(candidate, fssync.constants.X_OK);\n return candidate;\n } catch {\n // not here\n }\n }\n return null;\n}\n","import { FromSchema, JSONSchema } from 'json-schema-to-ts';\nimport { Promisable, BetaRunnableTool, BetaToolRunContext } from '../../lib/tools/BetaRunnableTool';\nimport { BetaToolResultContentBlockParam } from '../../resources/beta';\nimport { AutoParseableBetaOutputFormat } from '../../lib/beta-parser';\nimport { AnthropicError } from '../..';\nimport { transformJSONSchema } from '../../lib/transform-json-schema';\n\ntype NoInfer<T> = T extends infer R ? R : never;\n\n/**\n * Creates a Tool with a provided JSON schema that can be passed\n * to the `.toolRunner()` method. The schema is used to automatically validate\n * the input arguments for the tool.\n */\nexport function betaTool<const Schema extends Exclude<JSONSchema, boolean> & { type: 'object' }>(options: {\n name: string;\n inputSchema: Schema;\n description: string;\n run: (\n args: NoInfer<FromSchema<Schema>>,\n context?: BetaToolRunContext,\n ) => Promisable<string | Array<BetaToolResultContentBlockParam>>;\n /**\n * Optional cleanup hook for tools that hold process-level resources (e.g. a\n * persistent shell). `client.beta.sessions.events.toolRunner` calls it once\n * when iteration ends.\n */\n close?: () => void | Promise<void>;\n}): BetaRunnableTool<NoInfer<FromSchema<Schema>>> {\n if (options.inputSchema.type !== 'object') {\n throw new Error(\n `JSON schema for tool \"${options.name}\" must be an object, but got ${options.inputSchema.type}`,\n );\n }\n\n return {\n type: 'custom',\n name: options.name,\n input_schema: options.inputSchema,\n description: options.description,\n run: options.run,\n parse: (content: unknown) => content as FromSchema<Schema>,\n ...(options.close ? { close: options.close } : {}),\n } as any;\n}\n\n/**\n * Creates a JSON schema output format object from the given JSON schema.\n * If this is passed to the `.parse()` method then the response message will contain a\n * `.parsed_output` property that is the result of parsing the content with the given JSON schema.\n *\n */\nexport function betaJSONSchemaOutputFormat<\n const Schema extends Exclude<JSONSchema, boolean> & { type: 'object' },\n>(\n jsonSchema: Schema,\n options?: {\n transform?: boolean;\n },\n): AutoParseableBetaOutputFormat<NoInfer<FromSchema<Schema>>> {\n if (jsonSchema.type !== 'object') {\n throw new Error(`JSON schema for tool must be an object, but got ${jsonSchema.type}`);\n }\n\n const transform = options?.transform ?? true;\n if (transform) {\n // todo: doing this is arguably necessary, but it does change the schema the user passed in\n // so I'm not sure how we should handle that\n jsonSchema = transformJSONSchema(jsonSchema) as Schema;\n }\n\n return {\n type: 'json_schema',\n schema: {\n ...jsonSchema,\n },\n parse: (content) => {\n try {\n return JSON.parse(content);\n } catch (error) {\n throw new AnthropicError(`Failed to parse structured output: ${error}`);\n }\n },\n };\n}\n","/**\n * Shared, Node-only filesystem helpers for the agent toolset's file tools:\n * path confinement (symlink-aware), an atomic write, and language-independent\n * error messages. Kept out of `node.ts` so the tool implementations stay focused\n * and these helpers can be reused by every file tool.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { ToolError } from '../../lib/tools/ToolError';\n\n/** Mode for directories the file tools create — not world-writable under a 0 umask. */\nexport const DIR_CREATE_MODE = 0o755;\n/** Mode for files the file tools create. */\nexport const FILE_CREATE_MODE = 0o644;\n\n/** `realpath` `p`, or return `p` unchanged when it cannot be resolved. */\nasync function realpathOrSelf(p: string): Promise<string> {\n try {\n return await fs.realpath(p);\n } catch {\n return p;\n }\n}\n\n/**\n * Fully resolve `abs`: `realpath` the longest existing ancestor and re-append\n * the rest, but never re-append a component that is itself a symlink — read the\n * link and continue from its target instead. This handles paths being created\n * (write/edit) without letting a symlink leaf (e.g. a dangling one pointing\n * outside a confinement root) slip through unresolved.\n */\nexport async function canonicalize(abs: string): Promise<string> {\n const tail: string[] = [];\n let prefix = abs;\n for (;;) {\n let real: string;\n try {\n real = await fs.realpath(prefix);\n } catch {\n let isLink = false;\n try {\n isLink = (await fs.lstat(prefix)).isSymbolicLink();\n } catch {\n /* prefix truly doesn't exist (ENOENT) — fall through and walk up */\n }\n if (isLink) {\n // Resolve the symlink ourselves and retry; `tail` (the part below it)\n // still applies to the link's target.\n prefix = path.resolve(path.dirname(prefix), await fs.readlink(prefix));\n continue;\n }\n const parent = path.dirname(prefix);\n if (parent === prefix) return abs; // walked past the FS root without a hit\n tail.push(path.basename(prefix));\n prefix = parent;\n continue;\n }\n return tail.length ? path.join(real, ...tail.reverse()) : real;\n }\n}\n\n/**\n * Resolve `p` and confine it to `root`.\n *\n * Unless `allowOutside` is set, absolute inputs are rejected and the\n * **canonical** path is returned — every symlink in `p` (including the leaf,\n * even a dangling one) is resolved before the confinement check, and the\n * resolved path is what the caller then operates on, so a symlink inside `root`\n * that points outside it can neither pass the check nor be followed afterwards.\n *\n * Residual TOCTOU: a component could still be swapped for a symlink between this\n * call and the eventual `fs` operation. Closing that fully needs per-component\n * `O_NOFOLLOW`/`openat`, which Node does not expose ergonomically; this is why a\n * sandbox is still recommended for the toolset as a whole.\n */\nexport async function confineToRoot(\n root: string,\n p: string,\n opts?: { allowOutside?: boolean },\n): Promise<string> {\n const allowOutside = opts?.allowOutside ?? false;\n if (path.isAbsolute(p)) {\n if (!allowOutside) {\n throw new ToolError(`absolute path ${JSON.stringify(p)} not permitted`);\n }\n return path.resolve(p);\n }\n const realRoot = await realpathOrSelf(path.resolve(root));\n const abs = path.resolve(realRoot, p);\n if (allowOutside) return abs;\n const real = await canonicalize(abs);\n const rootSep = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep;\n if (real !== realRoot && !real.startsWith(rootSep)) {\n throw new ToolError(`path ${JSON.stringify(p)} escapes workdir`);\n }\n return real;\n}\n\n/**\n * Atomically write `content` to `targetPath`: write a sibling temp file, fsync\n * it, then rename over the target. The rename is atomic on most filesystems, so\n * a crash mid-write never leaves the target half-written.\n */\nexport async function atomicWriteFile(targetPath: string, content: string): Promise<void> {\n const dir = path.dirname(targetPath);\n const tempPath = path.join(dir, `.tmp-${process.pid}-${randomUUID()}`);\n let handle: fs.FileHandle | undefined;\n try {\n handle = await fs.open(tempPath, 'wx', FILE_CREATE_MODE);\n await handle.writeFile(content, 'utf-8');\n await handle.sync();\n await handle.close();\n handle = undefined;\n await fs.rename(tempPath, targetPath);\n } catch (err) {\n if (handle) await handle.close().catch(() => {});\n await fs.unlink(tempPath).catch(() => {});\n throw err;\n }\n}\n\n/**\n * Map a thrown filesystem error to a consistent, language-independent message,\n * so the model sees the same wording regardless of the runtime (Node's raw\n * `ENOENT: no such file...` text would otherwise leak through). Falls back to\n * the raw error message for codes we don't special-case.\n */\nexport function fsErrorMessage(err: unknown, file: string): string {\n const code = (err as { code?: string } | null)?.code;\n switch (code) {\n case 'ENOENT':\n return `${file}: no such file or directory`;\n case 'EACCES':\n case 'EPERM':\n return `${file}: permission denied`;\n case 'ENOTDIR':\n return `${file}: not a directory`;\n case 'EISDIR':\n return `${file}: is a directory`;\n case 'ELOOP':\n return `${file}: too many levels of symbolic links`;\n case 'ENAMETOOLONG':\n return `${file}: file name too long`;\n case 'ENOSPC':\n return `${file}: no space left on device`;\n case 'EMFILE':\n case 'ENFILE':\n return `${file}: too many open files`;\n default:\n return `${file}: ${err instanceof Error ? err.message : String(err)}`;\n }\n}\n","/**\n * Node-only skill plumbing for the agent toolset: downloading a session\n * agent's skills into the workdir and extracting the archives. Kept in its own\n * file because it is a distinct concern from the tool implementations in\n * `node.ts` — distinct enough, and large enough, to review on its own.\n */\n\nimport * as fs from 'node:fs/promises';\nimport * as fssync from 'node:fs';\nimport * as path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { Anthropic } from '../../client';\nimport { AnthropicError } from '../../core/error';\nimport { loggerFor } from '../../internal/utils/log';\nimport { DIR_CREATE_MODE } from './fs-util';\nimport type { AgentToolContext } from './node';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Download the session agent's skills into `{ctx.workdir}/skills/<name>/`.\n *\n * No-op (returns a no-op cleanup) unless both `ctx.client` and `ctx.sessionId`\n * are set. Looks up the session's resolved agent and, for each skill, fetches\n * its files via `client.beta.skills.versions.download` and extracts the archive\n * (a zip or tar.* archive) into a directory named after the skill. A failure on\n * one skill is logged and does not block the others. Call this before starting\n * the session tool runner (e.g. right after the bash session / workdir is\n * ready).\n *\n * Returns a cleanup function that removes the skill directories this call\n * created — call it once the work item is done so downloaded skills do not\n * accumulate in the workdir across sessions.\n */\nexport async function setupSkills(ctx: AgentToolContext): Promise<() => Promise<void>> {\n const { client, sessionId } = ctx;\n if (!client || !sessionId) return async () => {};\n const log = loggerFor(client);\n const session = await client.beta.sessions.retrieve(sessionId);\n const skillsRoot = path.resolve(ctx.workdir, 'skills');\n const created: string[] = [];\n for (const skill of session.agent.skills) {\n try {\n const versionId = await resolveSkillVersion(client, skill.skill_id, skill.version);\n const version = await client.beta.skills.versions.retrieve(versionId, { skill_id: skill.skill_id });\n // The directory is the skill's name, reduced to a single safe path\n // component so a hostile name can't escape `skillsRoot`.\n let dirname = path.basename(version.name.trim());\n if (dirname === '' || dirname === '.' || dirname === '..') dirname = skill.skill_id;\n const dest = path.resolve(skillsRoot, dirname);\n if (dest !== skillsRoot && !dest.startsWith(skillsRoot + path.sep)) {\n log.warn('skill name escapes the skills dir; skipping', {\n component: 'agent-tool-context',\n name: version.name,\n });\n continue;\n }\n const resp = await client.beta.skills.versions.download(versionId, { skill_id: skill.skill_id });\n await fs.rm(dest, { recursive: true, force: true });\n await fs.mkdir(dest, { recursive: true, mode: DIR_CREATE_MODE });\n created.push(dest);\n await extractSkillArchive(resp, dest);\n log.info('downloaded skill', {\n component: 'agent-tool-context',\n skill_id: skill.skill_id,\n version: versionId,\n dest,\n });\n } catch (e) {\n log.warn('failed to download skill', {\n component: 'agent-tool-context',\n skill_id: skill.skill_id,\n error: String(e),\n });\n }\n }\n return async () => {\n for (const dest of created) {\n await fs.rm(dest, { recursive: true, force: true }).catch((e) => {\n log.warn('failed to clean up skill', { component: 'agent-tool-context', dest, error: String(e) });\n });\n }\n };\n}\n\n/**\n * Resolve `version` to the concrete numeric timestamp the\n * `/v1/skills/{id}/versions/{version}` endpoints require — `session.agent.skills[].version`\n * can be an alias such as `\"latest\"`, which those endpoints reject. Numeric\n * versions pass through unchanged.\n */\nexport async function resolveSkillVersion(\n client: Anthropic,\n skillId: string,\n version: string,\n): Promise<string> {\n if (/^\\d+$/.test(version)) return version;\n let newest: string | undefined;\n for await (const v of client.beta.skills.versions.list(skillId)) {\n if (/^\\d+$/.test(v.version) && (newest === undefined || BigInt(v.version) > BigInt(newest))) {\n newest = v.version;\n }\n }\n if (newest === undefined) {\n throw new AnthropicError(\n `skill ${JSON.stringify(skillId)} has no concrete version to resolve ${JSON.stringify(\n version,\n )} against`,\n );\n }\n return newest;\n}\n\n/** Reject archive members that are absolute or contain a `..` component. */\nfunction assertSafeMemberNames(names: string): void {\n for (const raw of names.split('\\n')) {\n const entry = raw.trim();\n if (!entry) continue;\n if (path.isAbsolute(entry) || entry.split(/[\\\\/]/).includes('..')) {\n throw new AnthropicError(`refusing to extract unsafe archive member: ${entry}`);\n }\n }\n}\n\n/**\n * Reject archives that contain anything other than regular files and\n * directories. The type char is the first byte of each `ls`-style line emitted\n * by `tar -tvf` / `unzip -Z`: `-` file, `d` dir, `l` symlink, `h` hardlink,\n * `b`/`c` device, `p` fifo, `s` socket. A symlink/hardlink member is how an\n * archive escapes its extraction dir even when no name contains `..`.\n */\nfunction assertNoSpecialMembers(verboseListing: string): void {\n for (const line of verboseListing.split('\\n')) {\n const type = line.trimStart()[0];\n if (type === 'l' || type === 'h' || type === 'b' || type === 'c' || type === 'p' || type === 's') {\n throw new AnthropicError('refusing to extract archive with symlink/hardlink/device member');\n }\n }\n}\n\n/**\n * Run an archive CLI (`unzip` for zip archives, `tar` for everything else),\n * returning its stdout. Both binaries must be on `PATH`; a missing one would\n * otherwise surface as an opaque `ENOENT` spawn failure, so it is turned into a\n * clear, specific error naming the missing command.\n */\nasync function runArchiveTool(cmd: 'unzip' | 'tar', args: string[]): Promise<string> {\n try {\n const { stdout } = await execFileAsync(cmd, args);\n return stdout;\n } catch (e) {\n if (e != null && typeof e === 'object' && (e as { code?: unknown }).code === 'ENOENT') {\n throw new AnthropicError(\n `skill extraction requires the \\`${cmd}\\` command, but it was not found on PATH`,\n );\n }\n throw e;\n }\n}\n\n/**\n * The single top-level directory shared by every entry in a newline-separated\n * archive listing, or `''` if entries don't all live under one common\n * directory. Skill bundles are packaged wrapped in one directory named after\n * the skill (e.g. `pdf/SKILL.md`, `pdf/scripts/...`); the extractor strips it\n * so contents land directly in the skill's dir instead of a redundant nested\n * `<skill>/<skill>/` level. A flat or multi-root archive yields `''`.\n */\nfunction archiveTopDir(listing: string): string {\n let top: string | undefined;\n let nested = false;\n for (const raw of listing.split('\\n')) {\n // Drop `.` / empty segments so a `./pdf/...`-style listing (e.g. from\n // `tar -C dir .`) is treated the same as `pdf/...`.\n const parts = raw\n .trim()\n .split('/')\n .filter((p) => p !== '' && p !== '.');\n if (parts.length === 0) continue;\n const first = parts[0]!;\n if (top === undefined) top = first;\n else if (first !== top) return '';\n if (parts.length > 1) nested = true;\n }\n return top !== undefined && nested ? top : '';\n}\n\n/**\n * Extract a skill download (a zip or tar.* archive) into `dest`. Streams the\n * response body straight to a temp file beside `dest` (so the whole archive is\n * never buffered in memory — skills can contain large binaries), then shells out\n * to `unzip`/`tar` — consistent with the rest of the toolset, which already\n * invokes `bash` and `rg`. Both `unzip` and `tar` must be available on `PATH`; a\n * missing binary surfaces as a clear error (see {@link runArchiveTool}). Refuses\n * any member that would escape `dest` (zip-slip / tar-slip), including\n * symlink/hardlink members: skill archives come from the API, but skills can be\n * third-party.\n *\n * The skill bundle's single wrapper directory is stripped: the archive is\n * extracted into a staging dir and the wrapper's contents are promoted into\n * `dest`, so files land at `dest/SKILL.md` rather than a doubled\n * `dest/<skill>/SKILL.md` (`unzip` has no `--strip-components`, so this is\n * done uniformly by staging + promote rather than per-tool flags).\n */\nexport async function extractSkillArchive(resp: Response, dest: string): Promise<void> {\n const tmp = path.join(dest, `.skill-archive-${process.pid}-${Date.now()}`);\n if (!resp.body) {\n throw new AnthropicError('skill download response had no body');\n }\n await pipeline(\n Readable.fromWeb(resp.body as Parameters<typeof Readable.fromWeb>[0]),\n fssync.createWriteStream(tmp),\n );\n const stage = path.join(path.dirname(dest), `.skill-stage-${process.pid}-${Date.now()}`);\n try {\n // Sniff the first bytes: zip archives start with \"PK\\x03\\x04\"; treat\n // anything else as a tar.* archive (`tar -xf` autodetects gzip/bzip2/xz).\n const head = await readHead(tmp, 4);\n const isZip =\n head.length >= 4 && head[0] === 0x50 && head[1] === 0x4b && head[2] === 0x03 && head[3] === 0x04;\n const archiveCmd = isZip ? 'unzip' : 'tar';\n // List first, validate, then extract — `tar`/`unzip` will happily write a\n // `../` member (or follow a symlink member) outside `-C`/`-d` otherwise.\n const listing = await runArchiveTool(archiveCmd, isZip ? ['-Z1', tmp] : ['-tf', tmp]);\n assertSafeMemberNames(listing);\n assertNoSpecialMembers(await runArchiveTool(archiveCmd, isZip ? ['-Z', tmp] : ['-tvf', tmp]));\n const top = archiveTopDir(listing);\n await fs.mkdir(stage, { recursive: true, mode: DIR_CREATE_MODE });\n await runArchiveTool(archiveCmd, isZip ? ['-oq', tmp, '-d', stage] : ['-xf', tmp, '-C', stage]);\n // Promote the wrapper's contents (or the staged tree itself, if the\n // archive wasn't wrapped) into the already-created empty `dest`. `stage`\n // is a sibling of `dest`, so each rename stays on one filesystem.\n const srcRoot = top ? path.join(stage, top) : stage;\n for (const entry of await fs.readdir(srcRoot)) {\n await fs.rename(path.join(srcRoot, entry), path.join(dest, entry));\n }\n } finally {\n await fs.rm(tmp, { force: true });\n await fs.rm(stage, { recursive: true, force: true });\n }\n}\n\n/** Read the first `n` bytes of `file`. */\nasync function readHead(file: string, n: number): Promise<Buffer> {\n const handle = await fs.open(file, 'r');\n try {\n const buf = Buffer.alloc(n);\n const { bytesRead } = await handle.read(buf, 0, n, 0);\n return buf.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n"],"mappings":"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","names":["fs","fssync","path","fs","path","dirname","resolve","stat","rel"]}
@@ -1,8 +0,0 @@
1
- import {
2
- Orchestrator
3
- } from "./chunk-I4W33CWB.js";
4
- import "./chunk-5WRI5ZAA.js";
5
- export {
6
- Orchestrator
7
- };
8
- //# sourceMappingURL=orchestrator-2GXELO2X.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,69 +0,0 @@
1
- # Code Reviewer
2
-
3
- trigger: review, code review, 审查, 代码审查, check code, 检查代码
4
-
5
- priority: 8
6
-
7
- You are an expert code reviewer. Analyze code for quality, bugs, and improvements.
8
-
9
- ## Review Checklist
10
-
11
- ### Correctness
12
- - Logic errors and bugs
13
- - Edge cases not handled
14
- - Off-by-one errors
15
- - Null/undefined handling
16
- - Race conditions
17
-
18
- ### Code Quality
19
- - Naming conventions
20
- - Function complexity
21
- - Code duplication (DRY)
22
- - Single responsibility
23
- - Proper abstractions
24
-
25
- ### Performance
26
- - Unnecessary computations
27
- - Memory leaks
28
- - N+1 queries
29
- - Inefficient algorithms
30
- - Missing caching
31
-
32
- ### Security
33
- - Input validation
34
- - SQL injection
35
- - XSS vulnerabilities
36
- - Authentication/Authorization
37
- - Sensitive data exposure
38
-
39
- ### Maintainability
40
- - Code readability
41
- - Documentation
42
- - Test coverage
43
- - Error handling
44
- - Logging
45
-
46
- ## Output Format
47
-
48
- ```markdown
49
- ## Code Review Summary
50
-
51
- ### Critical Issues
52
- - [Issue 1]: Description and fix
53
-
54
- ### Warnings
55
- - [Warning 1]: Description and suggestion
56
-
57
- ### Suggestions
58
- - [Suggestion 1]: Improvement idea
59
-
60
- ### Positive Notes
61
- - Good practice observed
62
- ```
63
-
64
- ## Workflow
65
- 1. Read the code thoroughly
66
- 2. Check against review checklist
67
- 3. Provide specific line references
68
- 4. Suggest concrete fixes
69
- 5. Highlight positive patterns
@@ -1,11 +0,0 @@
1
- # Coding Assistant
2
-
3
- trigger: code, fix, debug, refactor, implement, write, create, build, test
4
-
5
- You are a skilled software engineer. Follow these principles:
6
- - Write clean, minimal code
7
- - Follow existing project conventions
8
- - Run tests to verify changes
9
- - Be concise in explanations
10
- - Use tools to read files before editing them
11
- - Verify builds pass after changes
@@ -1,77 +0,0 @@
1
- # Content Creator
2
-
3
- trigger: content, 内容, 文章, article, blog, 博客, 公众号, wechat, 写作, write
4
-
5
- priority: 6
6
-
7
- You are a content creation expert. Create professional content for various platforms.
8
-
9
- ## Content Types
10
-
11
- ### Articles & Blogs
12
- - Technical blog posts
13
- - Tutorial guides
14
- - Industry analysis
15
- - Product reviews
16
-
17
- ### Social Media
18
- - WeChat articles (公众号)
19
- - Twitter/X posts
20
- - LinkedIn articles
21
- - Xiaohongshu notes (小红书)
22
-
23
- ### Marketing
24
- - Product descriptions
25
- - Email newsletters
26
- - Landing page copy
27
- - Ad copy
28
-
29
- ## Writing Principles
30
-
31
- ### Structure
32
- 1. **Hook** - Grab attention in first paragraph
33
- 2. **Problem** - Identify the pain point
34
- 3. **Solution** - Provide value
35
- 4. **Evidence** - Support with data/examples
36
- 5. **CTA** - Clear call to action
37
-
38
- ### Style
39
- - Clear and concise
40
- - Active voice
41
- - Short paragraphs
42
- - Use subheadings
43
- - Include visuals descriptions
44
-
45
- ## Platform Optimization
46
-
47
- ### WeChat (公众号)
48
- - Title: 15-25 characters
49
- - Summary: 50-100 characters
50
- - Use images every 3-5 paragraphs
51
- - End with interaction prompt
52
-
53
- ### Twitter/X
54
- - Hook in first tweet
55
- - Thread format for long content
56
- - Use hashtags strategically
57
- - Engage with replies
58
-
59
- ### Technical Blog
60
- - Code examples
61
- - Step-by-step tutorials
62
- - Include prerequisites
63
- - Provide source links
64
-
65
- ## Output Formats
66
- - Markdown
67
- - HTML
68
- - Plain text
69
- - Platform-specific format
70
-
71
- ## Workflow
72
- 1. Understand target audience
73
- 2. Research topic
74
- 3. Create outline
75
- 4. Write content
76
- 5. Optimize for platform
77
- 6. Review and refine
@@ -1,76 +0,0 @@
1
- # Data Analyst
2
-
3
- trigger: data, 数据, analysis, 分析, chart, 图表, visualization, 可视化, statistics, 统计
4
-
5
- priority: 5
6
-
7
- You are a data analysis expert. Analyze data and create visualizations.
8
-
9
- ## Analysis Types
10
-
11
- ### Descriptive Statistics
12
- - Mean, median, mode
13
- - Standard deviation
14
- - Percentiles
15
- - Distribution analysis
16
-
17
- ### Trend Analysis
18
- - Time series
19
- - Growth rates
20
- - Seasonality
21
- - Forecasting
22
-
23
- ### Comparative Analysis
24
- - Year-over-year
25
- - Period-over-period
26
- - Benchmarking
27
- - Segmentation
28
-
29
- ## Visualization Types
30
-
31
- ### Charts
32
- - Line charts (trends)
33
- - Bar charts (comparison)
34
- - Pie charts (composition)
35
- - Scatter plots (correlation)
36
- - Heatmaps (patterns)
37
-
38
- ### Data Tables
39
- - Summary tables
40
- - Pivot tables
41
- - Cross-tabulation
42
- - Ranking tables
43
-
44
- ## Tools & Libraries
45
-
46
- ### Python
47
- - pandas (data manipulation)
48
- - matplotlib (basic charts)
49
- - seaborn (statistical viz)
50
- - plotly (interactive)
51
-
52
- ### JavaScript
53
- - D3.js (custom visualizations)
54
- - Chart.js (simple charts)
55
- - Echarts (business charts)
56
-
57
- ## Output Formats
58
- - PNG/SVG images
59
- - Interactive HTML
60
- - CSV/Excel exports
61
- - PDF reports
62
-
63
- ## Best Practices
64
- - Choose right chart type
65
- - Label clearly
66
- - Use consistent colors
67
- - Include data source
68
- - Add context/insights
69
-
70
- ## Workflow
71
- 1. Understand the question
72
- 2. Collect/clean data
73
- 3. Explore patterns
74
- 4. Create visualizations
75
- 5. Draw conclusions
76
- 6. Present findings
@@ -1,63 +0,0 @@
1
- # Documentation Generator
2
-
3
- trigger: docs, documentation, 文档, readme, api doc, 注释, comment, generate docs
4
-
5
- priority: 7
6
-
7
- You are a technical documentation expert. Create clear, comprehensive documentation.
8
-
9
- ## Documentation Types
10
-
11
- ### README
12
- - Project description
13
- - Installation instructions
14
- - Usage examples
15
- - Configuration
16
- - Contributing guidelines
17
-
18
- ### API Documentation
19
- - Endpoint descriptions
20
- - Request/Response formats
21
- - Authentication
22
- - Error codes
23
- - Examples
24
-
25
- ### Code Comments
26
- - Function descriptions
27
- - Parameter explanations
28
- - Return values
29
- - Usage examples
30
- - Edge cases
31
-
32
- ### Inline Documentation
33
- - Complex logic explanation
34
- - Algorithm descriptions
35
- - Business rule documentation
36
- - TODO/FIXME notes
37
-
38
- ## Best Practices
39
-
40
- ### Writing Style
41
- - Use clear, concise language
42
- - Include code examples
43
- - Keep documentation updated
44
- - Use consistent formatting
45
-
46
- ### Structure
47
- - Start with overview
48
- - Progress from simple to complex
49
- - Include troubleshooting
50
- - Add cross-references
51
-
52
- ## Output Formats
53
- - Markdown
54
- - JSDoc / TSDoc
55
- - OpenAPI / Swagger
56
- - Docstrings
57
-
58
- ## Workflow
59
- 1. Understand the code/project
60
- 2. Identify the audience
61
- 3. Choose appropriate format
62
- 4. Write documentation
63
- 5. Review and refine
@@ -1,67 +0,0 @@
1
- # Code Explainer
2
-
3
- trigger: explain, 解释, 说明, 理解, understand, what does, how does, 什么意思
4
-
5
- priority: 5
6
-
7
- You are a code explanation expert. Make complex code easy to understand.
8
-
9
- ## Explanation Levels
10
-
11
- ### Beginner
12
- - Use simple analogies
13
- - Explain step by step
14
- - Avoid jargon
15
- - Use visual aids (diagrams)
16
-
17
- ### Intermediate
18
- - Focus on patterns
19
- - Explain trade-offs
20
- - Compare alternatives
21
- - Link to concepts
22
-
23
- ### Expert
24
- - Deep dive into implementation
25
- - Performance implications
26
- - Architecture decisions
27
- - Edge cases
28
-
29
- ## Explanation Structure
30
-
31
- 1. **Summary** - One sentence overview
32
- 2. **Purpose** - Why this code exists
33
- 3. **How** - Step-by-step breakdown
34
- 4. **Key Concepts** - Important patterns used
35
- 5. **Examples** - Usage scenarios
36
- 6. **Gotchas** - Common pitfalls
37
-
38
- ## Visual Aids
39
-
40
- ### Flowcharts
41
- ```
42
- Start → Process → Decision → End
43
- ```
44
-
45
- ### Data Flow
46
- ```
47
- Input → Transform → Output
48
- ```
49
-
50
- ### Call Graph
51
- ```
52
- main() → helper() → util()
53
- ```
54
-
55
- ## Best Practices
56
- - Start with the big picture
57
- - Use concrete examples
58
- - Highlight non-obvious behavior
59
- - Link to documentation
60
- - Suggest improvements
61
-
62
- ## Workflow
63
- 1. Read the code thoroughly
64
- 2. Identify the audience
65
- 3. Choose explanation depth
66
- 4. Use appropriate format
67
- 5. Verify understanding
@@ -1,51 +0,0 @@
1
- # 金融分析技能
2
-
3
- trigger: finance, 金融, 股票分析, 财务分析, 研究报告, 股票, 股价
4
-
5
- ## 功能
6
-
7
- 分析上市公司财务数据,生成研究报告。
8
-
9
- ## 使用方法
10
-
11
- 用户可以说:
12
- - "分析贵州茅台"
13
- - "生成腾讯的研究报告"
14
- - "分析股票 600519"
15
- - "查看比亚迪股价"
16
-
17
- ## 工具
18
-
19
- 使用 `finance_analyze` 工具,参数:
20
- - company: 公司名称
21
- - code: 股票代码
22
- - market: 市场(A股/HK)
23
-
24
- ## 输出
25
-
26
- 生成包含以下内容的报告:
27
- 1. 公司概况(股票代码、市场、行业)
28
- 2. 价格信息(当前价格、涨跌幅、成交量)
29
- 3. 技术分析(均线、RSI、趋势信号)
30
- 4. 近期走势
31
- 5. 投资建议
32
-
33
- ## 数据源
34
-
35
- 使用 akshare 获取免费金融数据:
36
- - A股数据:沪深交易所
37
- - 港股数据:港交所
38
-
39
- ## 示例
40
-
41
- ```
42
- 用户: 分析贵州茅台
43
- 助手: 我来分析贵州茅台的财务数据...
44
- [调用 finance_analyze 工具,参数: { company: "贵州茅台", code: "600519", market: "A" }]
45
- ```
46
-
47
- ## 注意事项
48
-
49
- - 数据仅供参考,不构成投资建议
50
- - 数据来源为免费公开数据
51
- - 历史数据最近30天