> ## Documentation Index
> Fetch the complete documentation index at: https://skills.developerdoc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 在 skills 中使用脚本

> 如何在你的 skills 中运行命令，并打包可执行脚本。

skills 可以指示 agent 运行 shell 命令，也可以在 `scripts/` 目录中打包可复用脚本。本指南会介绍一次性命令、自包含且自带依赖的脚本，以及如何为 agent 使用场景设计脚本接口。

## 一次性命令

如果现有包已经能满足你的需求，你可以直接在 `SKILL.md` 指令里引用它，而不一定要单独建立 `scripts/` 目录。很多生态都提供了能在运行时自动解析依赖的工具。

<Tabs sync={false}>
  <Tab title="uvx">
    [uvx](https://docs.astral.sh/uv/guides/tools/) 会在隔离环境中运行 Python 包，并使用非常激进的缓存策略。它随 [uv](https://docs.astral.sh/uv/) 一起提供。

    ```bash theme={null}
    uvx ruff@0.8.0 check .
    uvx black@24.10.0 .
    ```

    * 不随 Python 一起提供，需要额外安装。
    * 很快。缓存策略很积极，因此重复运行几乎是瞬时的。
  </Tab>

  <Tab title="pipx">
    [pipx](https://pipx.pypa.io/) 会在隔离环境中运行 Python 包。可以通过操作系统包管理器安装（`apt install pipx`、`brew install pipx`）。

    ```bash theme={null}
    pipx run 'black==24.10.0' .
    pipx run 'ruff==0.8.0' check .
    ```

    * 不随 Python 一起提供，需要额外安装。
    * 它是 `uvx` 的成熟替代方案。虽然 `uvx` 现在已经成了更常见的推荐选择，但 `pipx` 依然可靠，而且在更多操作系统包管理器里可用。
  </Tab>

  <Tab title="npx">
    [npx](https://docs.npmjs.com/cli/commands/npx) 可以运行 npm 包，并按需下载它们。它随 npm 一起提供，而 npm 又随 Node.js 一起提供。

    ```bash theme={null}
    npx eslint@9 --fix .
    npx create-vite@6 my-app
    ```

    * 随 Node.js 一起提供，不需要额外安装。
    * 会下载包、执行它，并缓存下来供后续运行复用。
    * 使用 `npx package@version` 固定版本，确保可复现。
  </Tab>

  <Tab title="bunx">
    [bunx](https://bun.sh/docs/cli/bunx) 是 Bun 对应的 `npx`。它随 [Bun](https://bun.sh/) 一起提供。

    ```bash theme={null}
    bunx eslint@9 --fix .
    bunx create-vite@6 my-app
    ```

    * 在基于 Bun 的环境中，它是 `npx` 的即插即用替代品。
    * 只有当用户环境里装的是 Bun 而不是 Node.js 时才适合使用。
  </Tab>

  <Tab title="deno run">
    [deno run](https://docs.deno.com/runtime/reference/cli/run/) 可以直接从 URL 或 specifier 运行脚本。它随 [Deno](https://deno.com/) 一起提供。

    ```bash theme={null}
    deno run npm:create-vite@6 my-app
    deno run --allow-read npm:eslint@9 -- --fix .
    ```

    * 访问文件系统或网络时，需要显式声明权限标志（如 `--allow-read`）。
    * 使用 `--` 将 Deno 自己的标志与目标工具的标志分开。
  </Tab>

  <Tab title="go run">
    [go run](https://pkg.go.dev/cmd/go#hdr-Compile_and_run_Go_program) 可以直接编译并运行 Go 包。它内置在 `go` 命令中。

    ```bash theme={null}
    go run golang.org/x/tools/cmd/goimports@v0.28.0 .
    go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run
    ```

    * 内置于 Go，不需要额外工具。
    * 通过固定版本或使用 `@latest`，让命令意图更明确。
  </Tab>
</Tabs>

**在 skills 中编写一次性命令时的建议：**

* **固定版本。** 例如 `npx eslint@9.0.0`，这样命令在不同时间运行时行为一致。
* **把前置条件写进 `SKILL.md`。** 例如“Requires Node.js 18+”，不要默认 agent 所在环境已经具备这些条件。对于运行时级别的要求，请使用 [`compatibility` frontmatter 字段](/specification#compatibility-field)。
* **复杂命令移入脚本。** 当你只是带几个 flag 调用一个工具时，一次性命令很适合；但如果命令已经复杂到第一次就不容易写对，那么放进经过测试的 `scripts/` 脚本会更可靠。

## 从 `SKILL.md` 引用脚本

引用打包文件时，请使用 **相对于 skill 目录根的相对路径**。agent 会自动解析这些路径，不需要绝对路径。

在 `SKILL.md` 中列出有哪些脚本可用，这样 agent 才知道它们存在：

```markdown SKILL.md theme={null}
## Available scripts

- **`scripts/validate.sh`** — Validates configuration files
- **`scripts/process.py`** — Processes input data
```

然后明确指示 agent 去运行它们：

````markdown SKILL.md theme={null}
## Workflow

1. Run the validation script:
   ```bash
   bash scripts/validate.sh "$INPUT_FILE"
   ```

2. Process the results:
   ```bash
   python3 scripts/process.py --input results.json
   ```
````

<Note>
  这种相对路径约定同样适用于 `references/*.md` 这类辅助文件。代码块中的脚本执行路径始终是相对于 **skill 目录根** 的，因为 agent 会从那里执行命令。
</Note>

## 自包含脚本

如果你需要可复用逻辑，可以在 `scripts/` 中打包一个脚本，并让它以内联方式声明自身依赖。这样 agent 只用一条命令就能运行脚本，不需要额外的 manifest 文件，也不用提前执行安装步骤。

有几种语言都支持内联依赖声明：

<Tabs sync={false}>
  <Tab title="Python">
    [PEP 723](https://peps.python.org/pep-0723/) 定义了脚本内联元数据的标准格式。你可以在 `# ///` 标记之间用一个 TOML 代码块声明依赖：

    ```python scripts/extract.py theme={null}
    # /// script
    # dependencies = [
    #   "beautifulsoup4",
    # ]
    # ///

    from bs4 import BeautifulSoup

    html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
    print(BeautifulSoup(html, "html.parser").select_one("p.info").get_text())
    ```

    推荐使用 [uv](https://docs.astral.sh/uv/) 运行：

    ```bash theme={null}
    uv run scripts/extract.py
    ```

    `uv run` 会创建隔离环境、安装声明过的依赖，然后执行脚本。[pipx](https://pipx.pypa.io/)（`pipx run scripts/extract.py`）也支持 PEP 723。

    * 用 [PEP 508](https://peps.python.org/pep-0508/) 规范固定版本，例如：`"beautifulsoup4>=4.12,<5"`。
    * 使用 `requires-python` 约束 Python 版本。
    * 使用 `uv lock --script` 生成 lockfile，以获得完整可复现性。
  </Tab>

  <Tab title="Deno">
    Deno 的 `npm:` 和 `jsr:` import specifier 让每个脚本默认就是自包含的：

    ```typescript scripts/extract.ts theme={null}
    #!/usr/bin/env -S deno run

    import * as cheerio from "npm:cheerio@1.0.0";

    const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
    const $ = cheerio.load(html);
    console.log($("p.info").text());
    ```

    ```bash theme={null}
    deno run scripts/extract.ts
    ```

    * npm 包用 `npm:`，Deno 原生包用 `jsr:`。
    * 版本说明遵循 semver：`@1.0.0` 表示精确版本，`@^1.0.0` 表示兼容版本。
    * 依赖会被全局缓存。使用 `--reload` 可强制重新拉取。
    * 带有原生 addon（node-gyp）的包可能无法工作，通常更适合使用自带预编译二进制的包。
  </Tab>

  <Tab title="Bun">
    当目录树中不存在 `node_modules` 时，Bun 会在运行时自动安装缺失的包。你可以直接在 import 路径中固定版本：

    ```typescript scripts/extract.ts theme={null}
    #!/usr/bin/env bun

    import * as cheerio from "cheerio@1.0.0";

    const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
    const $ = cheerio.load(html);
    console.log($("p.info").text());
    ```

    ```bash theme={null}
    bun run scripts/extract.ts
    ```

    * 不需要 `package.json` 或 `node_modules`。TypeScript 也能原生运行。
    * 包会被全局缓存。第一次运行会下载，之后几乎是瞬时的。
    * 如果目录树中的任意上级目录已经存在 `node_modules`，自动安装会失效，Bun 会退回标准的 Node.js 解析方式。
  </Tab>

  <Tab title="Ruby">
    Ruby 从 2.6 起就随附 Bundler。你可以使用 `bundler/inline` 直接在脚本里声明 gem：

    ```ruby scripts/extract.rb theme={null}
    require 'bundler/inline'

    gemfile do
      source 'https://rubygems.org'
      gem 'nokogiri'
    end

    html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
    doc = Nokogiri::HTML(html)
    puts doc.at_css('p.info').text
    ```

    ```bash theme={null}
    ruby scripts/extract.rb
    ```

    * 显式固定版本（例如 `gem 'nokogiri', '~> 1.16'`），因为这里没有 lockfile。
    * 当前工作目录中的 `Gemfile` 或 `BUNDLE_GEMFILE` 环境变量可能会产生干扰。
  </Tab>
</Tabs>

## 为 agent 使用场景设计脚本

当 agent 运行你的脚本时，它会读取 stdout 和 stderr 来决定下一步怎么做。有几个设计选择会显著影响脚本对 agent 是否友好。

### 避免交互式提示

这是 agent 执行环境里的硬性要求。agent 工作在非交互式 shell 中，不能响应 TTY 提示、密码对话框或确认菜单。任何等待交互输入的脚本都会一直挂起。

应当通过命令行 flag、环境变量或 stdin 接收全部输入：

```
# Bad: hangs waiting for input
$ python scripts/deploy.py
Target environment: _

# Good: clear error with guidance
$ python scripts/deploy.py
Error: --env is required. Options: development, staging, production.
Usage: python scripts/deploy.py --env staging --tag v1.2.3
```

### 用 `--help` 记录用法

`--help` 输出是 agent 学习脚本接口的主要方式。请包含简短描述、可用 flag 和使用示例：

```
Usage: scripts/process.py [OPTIONS] INPUT_FILE

Process input data and produce a summary report.

Options:
  --format FORMAT    Output format: json, csv, table (default: json)
  --output FILE      Write output to FILE instead of stdout
  --verbose          Print progress to stderr

Examples:
  scripts/process.py data.csv
  scripts/process.py --format csv --output report.csv data.csv
```

保持简洁，因为这些输出会和 agent 正在处理的其他内容一起进入上下文窗口。

### 编写有帮助的错误消息

当 agent 拿到一个错误时，这条消息会直接影响它下一次尝试怎么改。一个含糊的 “Error: invalid input” 只会浪费一次轮次。更好的方式是：明确说出哪里错了、期望的是什么、下一步应该怎么试。

```
Error: --format must be one of: json, csv, table.
       Received: "xml"
```

### 使用结构化输出

优先使用结构化格式，例如 JSON、CSV、TSV，而不是自由文本。结构化格式既便于 agent 消费，也便于标准工具（`jq`、`cut`、`awk`）处理，因此更容易组成流水线。

```
# Whitespace-aligned — hard to parse programmatically
NAME          STATUS    CREATED
my-service    running   2025-01-15

# Delimited — unambiguous field boundaries
{"name": "my-service", "status": "running", "created": "2025-01-15"}
```

**把数据和诊断信息分开：** 结构化数据输出到 stdout，进度消息、警告和其他诊断信息输出到 stderr。这样 agent 既能拿到干净、可解析的数据输出，又能在需要时查看诊断信息。

### 更多考虑

* **幂等性。** agent 可能会重试命令。“如果不存在则创建”通常比“创建并在重复时报错”更安全。
* **输入约束。** 遇到有歧义的输入时，不要猜，直接报一个清晰错误。尽量使用枚举值和封闭集合。
* **Dry-run 支持。** 对于破坏性或有状态的操作，`--dry-run` 可以让 agent 先预览将要发生什么。
* **有意义的退出码。** 为不同失败类型使用不同退出码（例如未找到、参数非法、鉴权失败），并在 `--help` 输出中记录这些退出码的含义，让 agent 知道该如何解释它们。
* **安全默认值。** 需要破坏性操作时，要考虑是否应该要求显式确认 flag（例如 `--confirm`、`--force`）或其他与风险级别相匹配的保护措施。
* **可预测的输出规模。** 许多 agent 运行环境会在工具输出超过阈值时自动截断（例如 10 到 30K 字符），这可能导致关键内容丢失。如果你的脚本可能输出很多内容，默认就应该返回摘要或合理限制，并支持像 `--offset` 这样的 flag，让 agent 可以按需请求更多内容。另一种做法是：如果输出很大且不适合分页，就要求 agent 显式传入 `--output`，指定输出文件，或传 `-` 来明确表示要输出到 stdout。
