psomialbert/tool-smith
GitHub: psomialbert/tool-smith
一个 Agent 无关的 CLI 工具生命周期管理工具,让 AI 编程代理生成的工具以 git 仓库形式经沙盒测试和 PR 审查后被安全采纳到项目中。
Stars: 0 | Forks: 0
# smith (Toolsmith)
Agent 编写的 CLI 作为 **git 仓库**,在每个项目中通过一道**信任闸门**被采纳 —— 这是在 [mise](https://mise.jdx.dev) 之上的一层轻量约定。
适用于任何编程 Agent,而不仅仅是某一个。
一个编写自己工具的 Agent 没有可靠的地方来保存它们:脚本会在某台机器上处于未追踪状态(在下一次会话中丢失),或者每次都被粘贴回上下文中,而拉取别人的工具则是一个 Agent 可能被提示词注入从而触发的供应链事件。smith 将工具变成一个 **git 仓库**(源代码 + 清单 + 自测 + 关于何时使用它的描述),你可以像添加任何依赖项一样,通过**一个可审查的提交**将其**采纳到项目中**。没有任何东西会在未经审查的情况下运行:在工具被采纳之前,它只在沙盒中运行。
循环流程:**编写 → (其自己的仓库) → 采纳(沙盒自测 + vendored,在 PR 中审查) → 加入该项目的 PATH 中。**
**设计上与 Agent 无关。** smith 工具只是项目 PATH 上的一个 CLI,并支持 `--json`,因此任何 Agent 都可以运行它。`smith install` 还会通过刷新 `AGENTS.md` 或 `CLAUDE.md` 中生成的块,*告知*你使用的任何 Agent 这些工具的存在 —— 无需针对特定 Agent 进行设置,也无需手动同步代码片段。Claude Code、Codex、Cursor、Gemini CLI:相同的工具,相同的信任闸门。
那个块就是整个集成过程。采纳一个工具,运行 `smith install`,你现有的 `AGENTS.md` 就会获得这些内容 —— 你自己在它上方的内容保持不变:
```
# 我的项目
My own notes.
## 项目工具 (smith)
These are on PATH for this project. Run `smith lens` for the always-fresh index.
| tool | version | invoke | trigger |
|---|---|---|---|
| calc | v0.1.1 | `calc --json …` | Evaluate an arithmetic/logic expression safely and get a structured result. Use when a session needs to compute a number — precedence, %, //, **, sqrt/log/min/max/floor/ceil/trig, comparisons, and/or/not — without shelling out to python or risking `eval`. |
```
当没有更改时,重新运行 `smith install` 是一个空操作(no-op),因此针对被追踪的文件在 CI 中运行它是安全的。smith 从不*创建* `AGENTS.md`/`CLAUDE.md` —— 它只更新你已有的文件。
## 信任机制如何工作
采纳一个工具 = `smith use` 将其源代码 vendor 到 `.smith/tools//` 下,并将其固定(仓库 + 版本 + 内容哈希)在 `.smith/tools.yaml` 中。这是一个你的团队在 PR 中审查的普通 git diff —— **PR 审查就是信任闸门**,就像审查任何依赖项一样。
- **在采纳前处于沙盒中。** `smith use` 在 vendor 之前会在 OS 沙盒中运行工具的自测(无网络,写入限制在 CWD+TMP,清除环境变量),因此未经验证的代码在采纳时会被限制。
- **基于项目授信。** 一个在经过审查的 `.smith/tools.yaml` 中被 vendor 并固定的工具,*针对该项目*是受信任的,并在 `smith install` 之后加入 PATH(通过 mise)。不是全局的 —— 限定于审查过它的项目,就像 mise 已经按项目限定工具版本一样。
- **发现 ≠ 信任。** `smith search` 通过 GitHub topic 查找仓库;找到的工具只有通过采纳 PR 才会获得信任。
- **完整性。** `smith install` 会根据其固定版本重新计算每个 vendored bundle 的哈希值,如果不匹配则拒绝执行(类似于 go-sum 风格)。克隆项目并运行 `smith install` 的团队成员将获得完全相同的工具。`smith verify --frozen` 将该检查作为独立的 CI 闸门运行(如果发生偏移则以非零值退出)。
完整的威胁模型(smith 防御什么、不防御什么以及如何报告漏洞)请参见 [SECURITY.md](SECURITY.md)。
## 环境要求
- [mise](https://mise.jdx.dev) —— 版本/PATH 管理(smith 对其进行了封装)
- `git`
- 一个用于自测/试用工具的功能正常的 OS 沙盒:macOS Seatbelt(内置)或带有非特权用户 + 网络命名空间的 Linux `bwrap`。smith 在没有沙盒的情况下会**安全关闭**(参见 *Sandbox host requirements*)。
- 用于 `smith search` 的 `gh`(可选)
## 安装
`smith` 是一个单一二进制文件;它在运行时需要 PATH 中有 `mise`。
```
# one-liner (下载适用于您平台的 release binary)
curl -fsSL https://raw.githubusercontent.com/psomialbert/tool-smith/main/scripts/install.sh | sh
make install # from a clone: build + install to ~/.local/bin
```
安装程序通过普通的 https(无身份验证)下载;如果仓库是私有的,它会回退到 `gh`。
所有路径还会将 **cli-creator** 编写技能安装到 `~/.claude/skills/cli-creator/` 中(可通过 `SKILLS_DIR`/`SMITH_SKILLS_DIR` 覆盖),这样编写的 Agent 无需设置即可获取 CLI 契约。预构建的二进制文件(darwin/linux × arm64/amd64)附在每个[发布版本](https://github.com/psomialbert/tool-smith/releases)中。
`smith install` 会单独维护一个项目已采纳工具的索引,使其对无论在那工作的什么 Agent 都是可见的 —— 请参阅下文的 *Using smith with agents*。
## 快速开始
```
# 编写工具
smith new greet --runtime bash --desc "print a greeting"
cd greet && $EDITOR greet selftest.sh SKILL.md
smith publish --public # git + create/push repo, tag v0.1.0, topic-tag smith-tool
# 在项目中,采纳它 — 一个可审查的 commit
smith use ../greet@v0.1.0 # sandboxed self-test, vendor, pin
smith review greet # read the source (TUI); comment lines -> agent report
git add .smith && git commit # the adoption, for your team to review
smith install # realize it onto the project PATH
smith list # what's adopted in this project (+ install status)
greet alice # -> hello, alice
# ...或者采纳一个已发布的现有工具
smith use github:psomialbert/smith-example-calc@v0.1.1 # a worked example
smith search json # discover more by topic
```
## 与 Agent 配合使用 smith
安全模型是劳动分工:**Agent 编写并提出建议**,**人类审查 PR 并采纳**。
**`smith contract`** 会打印编写契约 —— 必需的 CLI 行为、自测规则,以及哪些限制是承重的,哪些只是尚未构建。将任何 Agent 指向它(机器请使用 `smith contract --json`)。它的存在是因为 cli-creator 技能是通过带外方式安装的:一个只持有二进制文件的 Agent 过去除了手动获取此仓库的路径外,没有其他方法可以学习契约,而且那里看起来最具权威性的文件竟然是已废弃的 v1 规范。
其中有两点值得在这里重申,因为如果猜测错误会导致真正的架构偏离:
- **沙盒约束的是自测,而不是工具。** 它仅在采纳时适用。在 `smith install` 之后,工具就是 PATH 上具有你完全权限的普通可执行文件 —— 它可能会下载大文件、启动守护进程、使用本地模型。唯一的要求是 `selftest.sh` 能够证明某些功能在*离线*下是有效的。(值得注意的警告:smith 固定的是你 vendor 的源代码,而不是你的工具以后获取的内容。)
- **重量级系统使用瘦客户端。** 如果实际工作是一个守护进程或常驻模型,请将其作为自己的普通项目发布,并使 smith 工具成为它的瘦 `--json` 客户端。被采纳和审查的是这个客户端。这是受认可的模式,而不是权宜之计。
| Agent 可以 | 人类拥有 |
|---|---|
| `new` (编写), `use`/`upgrade` (提议采纳), `diff`/`review`/`lens`/`contract` (阅读), `search`, `list`, `doctor`, `install` | 审查并 **合并采纳 PR**(信任决定) |
**发现项目工具。** `smith lens` 打印*当前*项目已采纳工具的面向 Agent 的索引 —— 每个工具的触发条件以及如何调用它。它是基于项目的并且始终是最新的(与全局导出的每个工具的技能不同),因此当 Agent 需要当前事实时,它就是应该运行的命令。
`smith install` 还会保持该索引可见,而不需要任何人先运行 `lens`:它会刷新 **`AGENTS.md` / `CLAUDE.md` 中已存在的那一个**(以项目根目录为准)内的一个由 smith 管理的块(工具名称、版本、调用形式、触发条件) —— 块外的内容保持不变。
smith 从不创建这两个文件中的任何一个;如果都不存在,Agent 就没有内容可读,直到有人创建了其中一个 —— `smith doctor --fix` 会创建第一个被选中的 `*-md` 目标(默认为 `AGENTS.md`;只有当 `agents.targets` 排除 `agents-md` 时才创建 `CLAUDE.md`;如果两者都被排除则不创建) —— 或者直接手动添加一个。
重新运行 `smith install` 是幂等的 —— 最新的块不会产生 diff,因此在 CI 中运行它是安全的,不会弄脏被 git 追踪的文件。
`smith install` 写入哪些目标可在 `$SMITH_HOME/config.yaml` 中配置:
```
agents:
targets: [claude, agents-md] # or claude-md; [] disables emission entirely
```
不设置 `agents.targets` 会自动检测:`smith install` 会输出到适用的三个目标中的任何一个(`claude` —— 机器全局的每个工具的技能导出 —— 加上 `agents-md`/`claude-md`,适用于已存在的文件)。将 `targets` 设置为显式列表 —— 包括 `[]` —— 会完全覆盖自动检测;未知的名称是严重的配置错误,而仅仅写入失败的目标只会发出警告 —— `smith install` 仍然会成功完成其余部分。
较旧的 `skills.export: never` 仍然有效,现在它选择退出**所有**输出,而不仅仅是技能目录 —— 它早于 Agent 目标出现,那时该目录是唯一的播发路径,因此它的意思是“不要播发我的工具”。当你想要更精细的划分时,请使用 `agents.targets`(跳过机器全局技能目录,保留项目范围的块);它的优先级高于 `skills.export`。
请注意,`config.yaml` 是**基于机器的**,因此 `targets: []` 选择退出的是*你*,而不是你的仓库 —— 它不能代表 CI 或你的贡献者禁用输出。针对项目范围的控制是文件本身:`*-md` 目标仅在 `AGENTS.md`/`CLAUDE.md` 已存在时才会触发,因此一个不应该携带受管块的项目就不应该拥有该文件。
`smith use` 运行工具的沙盒自测并写入一个可审查的 diff;它本身从不授予权限 —— 只有在采纳被提交/合并并且 `smith install` 运行后,该工具才在 PATH 中可用。
## 运行时
`smith use` 采纳所有四种运行时,始终 vendor 可审查的**源代码**(已提交),从不使用构建的产物:
- **bash / python** —— 脚本就是入口点;被软链接到 PATH 上。
- **go** —— `main.go` 被 vendored;`smith install` 在本地编译它
(按平台,被 git 忽略)。
- **python-uv** —— `.py` 及其哈希锁定的 `.py.lock` 被 vendored(带有哈希的依赖树位于 PR 中);`smith install` 从锁定文件预热一个被 git 忽略的工具本地缓存,并写入一个离线启动器。
对于每种运行时,PR diff 都是可审查的源代码,在团队成员的平台之间是可移植的;构建/预热在安装时在本地进行。
**多个源文件。** 一个工具不限于一个文件 —— 在其清单中声明它由哪些文件组成,每个列出的文件都会被一起进行 vendored、哈希处理和重新验证:
```
sources: [main.go, internal/, templates/]
```
目录会被递归遍历;条目必须保留在仓库内,并且软链接会被拒绝。省略 `sources:`,工具将保持单文件布局,具有字节相同的哈希,因此已经采纳的内容不会发生任何改变。多文件 `go` 工具作为包构建,因此需要一个 `go.mod`(`go mod init `),它会自动加入被审查的 bundle 中 —— 第三方模块在采纳时仍然会安全关闭,因为多文件并不意味着多依赖。
200 个文件 / 2 MiB 的上限保持了审查面的诚实:采纳 PR 是信任闸门,而没人能阅读的工具是没人能担保的工具。
## 第三方 CLI(通过 mise)
在项目中标准化现有的第三方 CLI —— 而不是 smith 工具:
```
smith use --mise ubi:DataDog/pup # also node@20, go:github.com/x/y, aqua:owner/repo
```
这会将工具 + 版本固定在项目提交的 `mise.toml` 中,并将其记录在 `.smith/tools.yaml` 中(`smith list` 将其显示为 `external (mise)`);审查面是 `mise.toml` 的 diff。**没有源代码审查** —— 你信任上游 CLI 和 mise 的获取,就像直接使用 mise 一样。smith 添加了一个按项目审查的固定、统一的 `smith list` 和组织白名单(仍然适用)。团队成员运行 `smith install`(它会运行 `mise install`)来获取它。没有自测,也没有 SKILL 导出 —— 它是一个外部二进制文件,与经过源代码审查的 smith 工具有着明显的区别。
## 沙盒宿主要求
工具在沙盒中自测/运行,因此需要一个功能正常的沙盒:macOS Seatbelt(`sandbox-exec`,内置)或带有**已启用的非特权用户 + 网络命名空间**的 Linux bubblewrap(`bwrap`)。否则 smith 会安全关闭 —— 例如,GitHub Actions 的 Ubuntu 运行器禁止 `--unshare-net` 所需的 netns 设置,因此无法在那里进行沙盒运行。
工具在其清单中声明它需要的任何额外能力:
```
sandbox:
network: true # allow network egress
write: ["~/.cache/mytool"] # extra writable paths beyond CWD+TMP
build: true # python-uv only: allow dependency source builds
# at adopt time (off by default — wheels only)
```
这些在你采纳时会被**显示出来**(`⚠ requests capabilities: …`)以便审查者看到,并在自测期间被**强制执行** —— 工具只获得它声明的内容,没有多余的。请求最小权限;广泛的授权是一个审查标志。
## 布局
每个**项目**:
```
.smith/tools.yaml adopted tools: source, version, content hash, runtime (committed)
.smith/tools// vendored tool source — reviewed in the PR (committed)
.smith/bin/ symlink farm / built binaries, put on PATH (git-ignored)
.smith/cache/ warmed python-uv caches (git-ignored)
mise.local.toml activation: puts .smith/bin on PATH (machine-specific) (git-ignored)
```
`SMITH_HOME`(默认为 `~/.toolsmith`)现在很小 —— 只有生成的沙盒配置文件和config.yaml`(审查 + 技能 + Agent 目标设置)。代码仓库(此目录)是独立的。
## 不在范围内 (v1)
没有公共贡献中心,没有多用户治理/RBAC,没有 GUI,没有自动采纳。提示词注入 -> 恶意软件的风险意味着公共发现是开放的,但*采纳仍然是一个经过审查的决定*。参见 `ROADMAP.md`。
## 组织白名单(可选)
公司可以通过 `$SMITH_HOME/config.yaml` 中的白名单限制可以采纳哪些仓库 —— 即使通过 PR 也不行(按机器配置,因此项目 PR 无法更改它):
```
allowlist:
repos: ["github.com/acme/*", "github.com/acme-tools/lint"]
# file: /etc/smith/allowlist.txt # or a managed file, one pattern per line
```
`smith use` 拒绝(在克隆之前)任何不匹配模式的来源;`smith allowlist` 显示有效的策略。默认(空) = 可以采纳任何内容。对于想要集中控制的组织来说,这是基于项目审查之上的一层策略。
**签名的白名单(防篡改)。** 为了阻止机器的配置静默指向扩大化的列表,请使用 ed25519 密钥对白名单文件进行签名,并固定公钥:
```
smith allowlist keygen # → private key file + a public_key to paste
smith allowlist sign allow.txt --key # → allow.txt.sig
```
```
allowlist:
file: /etc/smith/allow.txt
public_key:
```
为了阻止**回滚**(攻击者提供旧的、有效签名的、更宽泛的列表),在文件中添加一个版本行,并在你每次更改它时对其进行提升 —— smith 记录它已经接受的最高版本,并拒绝较旧的版本:
```
# smith-allowlist-version: 3
github.com/acme/*
```
当设置了 `public_key` 时,smith 会在每次采纳时验证 `allow.txt.sig`,如果文件丢失、无效或被编辑过,则会**安全关闭**(阻止所有采纳)。`smith allowlist verify` 会报告状态。使用你的机群配置分发公钥 + 签名文件;将私钥保留在开发人员机器之外。
## 更新已采纳的工具
```
smith diff # preview: what would change vs the newest version
smith upgrade # re-pin one tool to the newest tag (self-tested)
smith upgrade # ...or every adopted tool → its newest tag
smith upgrade @ # pin an exact version
```
`smith upgrade` 会发现每个工具源仓库的最新 semver 标签,重新运行沙盒自测,并在 `.smith/tools.yaml` 中重新固定 —— 就像最初的采纳一样,是一个可审查的 diff。它会在重新固定时停止(之后运行 `smith install`),以便新源代码在进入 PATH 之前得到审查;如果自测失败,原来的固定版本将保持不变。`smith diff` 首先预览 vendored 源代码的更改(`--stat` 用于摘要,`--json` 用于机器可读的列表)。
(手动重新运行 `smith use @` 仍然有效且是等效的。)
## 健康检查 (smith doctor)
诊断项目 + 机器健康状态:
```
smith doctor # report only (safe, CI-friendly; exits non-zero on errors)
smith doctor --fix # apply safe, reversible repairs
smith doctor --json # machine-readable
```
检查内容:mise 是否存在、沙盒是否正常、废弃的 v1 文件(可修复);并且在一个项目中,检查激活状态、`.gitignore` 覆盖范围、被 git 追踪的 `mise.local.toml`、vendored 源代码偏移、已固定但未安装的工具,以及未经优化的工具 SKILL 触发器。`--fix` 修复激活、`.gitignore`、安装和 v1 残留物;它从不运行 git,也从不通过网络重新获取。
`smith migrate` 自行执行机器级别的 v1→v2 清理 —— 它备份废弃的 `~/.toolsmith` 文件并剥离无用的 `config.yaml` 键(可安全重新运行)。安装程序会自动运行它,并且 `smith doctor --fix` 涵盖了相同的清理工作,因此你很少直接调用它。
## 构建与测试
```
mise exec -- go build -o smith ./cmd/smith
mise exec -- go test ./...
bash scripts/adopt-e2e.sh # full adoption loop, local git repos
```
## 许可证
[Apache-2.0](LICENSE)。
标签:AI编程助手, EVTX分析, SOC Prime, 依赖管理, 命令行工具(CLI), 应用安全, 开发工具, 日志审计, 沙箱环境, 逆向工具