🔶 Show HN: Aict —— 输出 XML/JSON 的 Unix 核心工具集,专为 AI Agent 构建
英文标题:🔶 Show HN: Aict – Unix coreutils that output XML/JSON, built for AI agents
aict 重新实现了 33 个 Unix 工具,默认输出结构化的 XML/JSON,使 AI Agent 无需再解析人类可读的纯文本。它提供带标签字段、绝对路径、Unix 时间戳以及自动语言/MIME 检测的输出,并通过内置 MCP 服务器让 Claude 等助手原生调用。尽管单次输出的 token 数量比 GNU 工具高出若干倍,但能减少多轮调用和解析歧义,在真实 Agent 评测中可减少约 46% 的输出 token 并提升准确性。该项目已开源,支持 macOS、Linux 和部分 Windows 功能,适合希望为 AI Agent 构建更可靠工具链的开发者。
533.5 KB
132.9 KB
中文
AI Agent 运行 ls、grep、cat,拿到的却是人类可读的纯文本。然后它们还得消耗 token 去解析列位置、猜测字段宽度,处理不一致的格式。这既脆弱又浪费。
-rw-r--r-- 1 user staff 2048 Apr 6 10:00 main.go ← 哪一列是文件大小?
-rw-r--r-- 1 user staff 1024 Apr 6 10:00 utils.go ← 这是什么语言?
drwxr-xr-x 5 user staff 160 Apr 6 10:00 internal ← 这是目录吗?
aict 用 结构化输出 重新实现了 33 个 Unix 工具,Agent 可以直接读取 —— 无需再解析。
$ aict ls src/
<ls timestamp="1746123456" total_entries="3">
<file name="main.go" path="src/main.go" absolute="/project/src/main.go"
size_bytes="2048" size_human="2.0K" language="go" mime="text/x-go"
binary="false" executable="false" modified="1746120000" modified_ago_s="3456"/>
<file name="utils.go" path="src/utils.go" absolute="/project/src/utils.go"
size_bytes="1024" size_human="1.0K" language="go" mime="text/x-go"
binary="false" executable="false" modified="1746120000" modified_ago_s="3456"/>
<directory name="internal" path="src/internal" modified="1746120000"/>
</ls>
每个字段都有标签。路径始终是绝对路径。时间戳是 Unix 整数。语言和 MIME 类型自动检测 —— 完全不需要解析。
brew tap synseqack/aict
brew install aict
这会安装 aict 以及 bash 和 zsh 的 shell 补全脚本。内置 MCP 服务器:aict mcp。
go install github.com/synseqack/aict@latest
git clone https://github.com/synseqack/aict
cd aict
go build -o aict .
验证安装:
aict --help应列出所有可用工具。
# 默认:XML 输出(最适合 AI Agent)
aict ls src/
aict grep "func" . -r
aict cat main.go
aict diff old.go new.go
# JSON 输出
aict ls src/ --json
# 纯文本(与原始 Unix 工具相同)
aict ls src/ --plain
# 全局启用 XML 输出(所有 aict 调用)
export AICT_XML=1
横跨 6 个类别的 33 个工具。每个工具都支持 --xml(默认)、--json 和 --plain。
| 类别 | 工具 |
|---|---|
| 文件检查 | cat head tail file stat wc |
| 搜索与比较 | ls find grep diff |
| 路径工具 | realpath basename dirname pwd |
| 文本处理 | sort uniq cut tr sed awk |
| 数据与归档 | jq tar |
| 系统与环境 | env system ps df du checksums md5sum sha1sum sha256sum |
附加:git(status, diff, log, ls-files, blame) · completions(bash/zsh/fish) · doctor(自诊断)
所有工具遵循相同的约定:
| 字段 | 约定 |
|---|---|
| 路径 | 始终为绝对路径(absolute 属性) |
| 时间戳 | Unix 纪元整数 + _ago_s 伴侣字段 |
| 大小 | 字节数(size_bytes) + 人类可读(size_human) |
| 布尔值 | "true" / "false" 字符串 |
| 错误 | <error code="" msg=""/> 元素 — 从不输出到 stderr |
| 空结果 | 有效的 XML,包含零计数,绝不报错 |
aict mcp 通过 stdio 传输将所有工具暴露为可调用的 MCP 函数。AI 助手可以原生调用它们 —— 无需 shell 包装。MCP 服务器是主二进制文件的一个子命令。
配置 Claude Desktop (~/.config/claude/claude_desktop_config.json):
{
"mcpServers": {
"aict": {
"command": "aict",
"args": ["mcp"]
}
}
}
如果 aict 不在 PATH 中,请使用其完整路径:
{
"mcpServers": {
"aict": {
"command": "/usr/local/bin/aict",
"args": ["mcp"]
}
}
}
添加到 ~/.claude.json:
{
"mcpServers": {
"aict": {
"command": "aict",
"args": ["mcp"]
}
}
}
连接后,Claude Code 可以将 ls、grep、diff 及其他所有工具作为原生函数调用,并使用类型化参数和结构化 JSON 结果。
先说真实数据:aict 的输出每个任务的 token 消耗比精简的 GNU 输出高出 1.1–7.8 倍(使用 tiktoken o200k_base 测量)。但这些 token 换来了更少的往返次数和零解析歧义。当纯文本需要 2–4 次链式调用时(如 ls 之后再用 file 检测语言,find 之后对每个结果再 stat),aict 一次调用即可完成。
| 任务 | GNU | aict | Token(GNU → aict) |
|---|---|---|---|
| 列出目录(含大小/类型/语言) | 2 次调用 | 1 次调用 | 246 → 820 · 3.3× |
| 读取文件 + 行数 + 类型 | 3 次调用 | 1 次调用 | 173 → 274 · 1.6× |
查找 .go 文件(含大小/修改时间) |
4 次调用 | 1 次调用 | 47 → 367 · 7.8× |
| Grep 并返回文件/行号/上下文 | 1 次调用 | 1 次调用 | 141 → 326 · 2.3× |
| 带变更类型的 Diff | 1 次调用 | 1 次调用 | 167 → 192 · 1.2× |
每次额外的纯文本调用都是一次完整的 Agent 回合——模型推理、工具调用开销以及中间输出最终都会进入上下文窗口,而上表中的 token 数均未计入这些开销。而且就在这个基准测试中,file(1) 将一个 Go 源文件误判为“C source”;而 aict 将其正确标记为 go。
在一次真实 Agent 评测中(使用 opencode,每条工具链相同任务运行 3 次),配备 aict 的 Agent 生成的输出 token 约减少 46%(中位数 265 vs 487),且 3/3 次均正确——而配备 GNU 工具链的 Agent 在某次运行中因信任 file(1) 的语言检测而产生了一份错误报告。两种方法的详细说明请参见 benchmarks/TOKENS.md;可通过 go run ./cmd/tokenbench 复现。
当你只需要原始内容时,请使用 --plain。
aict 用一些速度换取了语义丰富度(语言检测、MIME 类型、绝对路径)。这些开销是特意保留的。每次调用的启动成本约为 3.6 毫秒。
| 工具 | GNU | --plain |
--xml |
备注 |
|---|---|---|---|---|
diff(1000 行) |
0.9 ms | 1.9 ms · 2.1× | 2.1 ms · 2.4× | ✅ Myers O(ND) |
wc(100k 行) |
6.1 ms | 16 ms · 2.6× | 17 ms · 2.7× | ✅ |
awk(10k 行) |
4.1 ms | 12 ms · 2.9× | 11 ms · 2.6× | ✅ |
sed(10k 行) |
3.3 ms | 14 ms · 4.2× | 16 ms · 4.9× | ✅ |
find(深层树) |
1.9 ms | 13 ms · 6.8× | 15 ms · 8.0× | ✅ |
ls(1000 个文件) |
4.0 ms | 51 ms · 12.9× | 70 ms · 17.7× | 每个文件的 MIME+语言检测 |
cat(100k 行) |
1.4 ms | 24 ms · 16.4× | 31 ms · 21.6× | 逐行扫描 + 编码检测 |
grep(100k 行) |
1.3 ms | 119 ms · 88× | 130 ms · 96× | Go regexp 与 GNU SIMD |
数据为 Linux/amd64 下 5 次运行的中位数。方法参见 benchmarks/,可使用 make bench 复现。
当你只需要原始内容时,请使用 --plain 跳过富化处理。
为什么默认使用 XML 而非 JSON?
XML 属性在上下文窗口中更紧凑。<file size="1024" lang="go"/> 比 {"size":1024,"lang":"go"} 更短。如果你更喜欢 JSON,请使用 --json——结构完全相同。
为什么不直接将 GNU 工具管道到 jq?
ls、cat、stat、find、diff 和 wc 并不输出 JSON。jq 无法处理它们。aict 为整个工具链提供了结构化输出,而不仅仅是 grep。(aict 也自带 jq,用于通过路径表达式查询 JSON 文件。)
与 ripgrep 相比如何?
ripgrep 在纯搜索方面要快得多。aict 的 grep 增加了语言检测、MIME 类型以及与所有其他工具一致的输出格式。在对速度要求极高的搜索场景中使用 ripgrep;当 Agent 需要结构化上下文时使用 aict。
与 eza / lsd 相比如何?
eza 和 lsd 是面向人类的更好 ls——具有出色的色彩和格式。aict 输出的是数据结构,而非格式化的表格。它们解决的是不同的问题。
能在 Windows 上运行吗?
ls、cat、stat、wc、find、diff、grep、head、tail、sort、uniq、cut、tr、sed、awk、jq、tar、checksums、df 和路径工具可以在 Windows 上运行。system 适用于 Linux/macOS;ps 仅适用于 Linux(它读取 /proc)。在不支持的平台上,会返回结构化的 <error> 元素,而绝不会崩溃。
在沙盒环境中运行是否安全?
是的。aict 严格只读。无网络请求(MIME 检测使用 Go 标准库,而非 HTTP)。无遥测。无数据收集。它仅读取你显式传递的路径。
它有多少依赖?
只有一个:官方的 MCP Go SDK,仅供 aict mcp 子命令使用。所有 33 个工具以及每个内部包都是纯 Go 标准库——这作为硬性约束写入 AGENTS.md。
欢迎提交错误报告、功能请求和 PR。有关指南、代码风格和工具实现模式,请参阅 CONTRIBUTING.md。
标记为 good first issue 的 Issue 是良好的起点。
MIT —— 由 AI 工具为 AI 工具而构建。
English / 原文
AI agents run ls, grep, and cat and get back human-readable plaintext. Then they spend tokens parsing column positions, guessing field widths, and handling inconsistent formats. This is fragile and wasteful.
-rw-r--r-- 1 user staff 2048 Apr 6 10:00 main.go ← which column is size?
-rw-r--r-- 1 user staff 1024 Apr 6 10:00 utils.go ← what's the language?
drwxr-xr-x 5 user staff 160 Apr 6 10:00 internal ← is this a directory?
aict reimplements 33 Unix tools with structured output the agent can read directly — no parsing required.
$ aict ls src/
<ls timestamp="1746123456" total_entries="3">
<file name="main.go" path="src/main.go" absolute="/project/src/main.go"
size_bytes="2048" size_human="2.0K" language="go" mime="text/x-go"
binary="false" executable="false" modified="1746120000" modified_ago_s="3456"/>
<file name="utils.go" path="src/utils.go" absolute="/project/src/utils.go"
size_bytes="1024" size_human="1.0K" language="go" mime="text/x-go"
binary="false" executable="false" modified="1746120000" modified_ago_s="3456"/>
<directory name="internal" path="src/internal" modified="1746120000"/>
</ls>
Every field is labeled. Paths are always absolute. Timestamps are Unix integers. Language and MIME type are detected automatically — zero parsing needed.
brew tap synseqack/aict
brew install aict
This installs aict plus shell completions for bash and zsh. The MCP server is built in: aict mcp.
go install github.com/synseqack/aict@latest
git clone https://github.com/synseqack/aict
cd aict
go build -o aict .
Verify install:
aict --helpshould list all available tools.
# Default: XML output (best for AI agents)
aict ls src/
aict grep "func" . -r
aict cat main.go
aict diff old.go new.go
# JSON output
aict ls src/ --json
# Plain text (same as the original Unix tools)
aict ls src/ --plain
# Enable XML globally for all aict calls
export AICT_XML=1
33 tools across 6 categories. Every tool supports --xml (default), --json, and --plain.
| Category | Tools |
|---|---|
| File inspection | cat head tail file stat wc |
| Search & compare | ls find grep diff |
| Path utilities | realpath basename dirname pwd |
| Text processing | sort uniq cut tr sed awk |
| Data & archives | jq tar |
| System & environment | env system ps df du checksums md5sum sha1sum sha256sum |
Additional: git (status, diff, log, ls-files, blame) · completions (bash/zsh/fish) · doctor (self-diagnostic)
All tools follow the same conventions:
| Field | Convention |
|---|---|
| Paths | Always absolute (absolute attr) |
| Timestamps | Unix epoch integers + _ago_s companion |
| Sizes | Bytes (size_bytes) + human-readable (size_human) |
| Booleans | "true" / "false" strings |
| Errors | <error code="" msg=""/> elements — never stderr |
| Empty results | Valid XML with zero counts, never an error |
aict mcp exposes all tools as callable MCP functions via stdio transport. AI assistants call them natively — no shell wrapping needed. The MCP server is a subcommand of the main binary.
Configure Claude Desktop (~/.config/claude/claude_desktop_config.json):
{
"mcpServers": {
"aict": {
"command": "aict",
"args": ["mcp"]
}
}
}
If aict is not in PATH, use its full path:
{
"mcpServers": {
"aict": {
"command": "/usr/local/bin/aict",
"args": ["mcp"]
}
}
}
Add to ~/.claude.json:
{
"mcpServers": {
"aict": {
"command": "aict",
"args": ["mcp"]
}
}
}
Once connected, Claude Code can call ls, grep, diff, and all other tools as native functions with typed arguments and structured JSON results.
Honest numbers first: aict output costs 1.1–7.8× more tokens per task than terse GNU output (measured with tiktoken o200k_base). What those tokens buy: fewer round-trips and zero parsing ambiguity. Where plaintext needs 2–4 chained calls (ls then file for languages, find then stat per hit), aict answers in one.
| Task | GNU | aict | Tokens (GNU → aict) |
|---|---|---|---|
| List dir with size/type/language | 2 calls | 1 call | 246 → 820 · 3.3× |
| Read file + line count + type | 3 calls | 1 call | 173 → 274 · 1.6× |
Find .go files with size/mtime |
4 calls | 1 call | 47 → 367 · 7.8× |
| Grep with file/line/context | 1 call | 1 call | 141 → 326 · 2.3× |
| Diff with change types | 1 call | 1 call | 167 → 192 · 1.2× |
Every extra plaintext call is a full agent turn — model inference, tool-call overhead, and intermediate output all land in the context window anyway, none of which the token counts above include. And in this very benchmark, file(1) misidentified a Go source file as "C source"; aict labeled it go.
In a live agent eval (opencode, same task 3× per toolchain), the aict-equipped agent generated ~46% fewer output tokens (median 265 vs 487) and was correct 3/3 — the GNU-equipped agent shipped a flawed report in the run where it trusted file(1)'s language detection. See benchmarks/TOKENS.md for both methodologies; reproduce with go run ./cmd/tokenbench.
Use --plain when you only need raw content.
aict trades some speed for semantic richness (language detection, MIME typing, absolute paths). The overhead is intentional. Startup cost is ~3.6 ms per invocation.
| Tool | GNU | --plain |
--xml |
Notes |
|---|---|---|---|---|
diff (1000 lines) |
0.9 ms | 1.9 ms · 2.1× | 2.1 ms · 2.4× | ✅ Myers O(ND) |
wc (100k lines) |
6.1 ms | 16 ms · 2.6× | 17 ms · 2.7× | ✅ |
awk (10k lines) |
4.1 ms | 12 ms · 2.9× | 11 ms · 2.6× | ✅ |
sed (10k lines) |
3.3 ms | 14 ms · 4.2× | 16 ms · 4.9× | ✅ |
find (deep tree) |
1.9 ms | 13 ms · 6.8× | 15 ms · 8.0× | ✅ |
ls (1000 files) |
4.0 ms | 51 ms · 12.9× | 70 ms · 17.7× | MIME+lang detection per file |
cat (100k lines) |
1.4 ms | 24 ms · 16.4× | 31 ms · 21.6× | line-by-line scan + encoding detect |
grep (100k lines) |
1.3 ms | 119 ms · 88× | 130 ms · 96× | Go regexp vs GNU SIMD |
Medians from 5 runs on Linux/amd64. See benchmarks/ for methodology and make bench to reproduce.
Use --plain to skip enrichment when you only need raw content.
Why XML and not JSON by default?
XML attributes are denser in a context window. <file size="1024" lang="go"/> is shorter than {"size":1024,"lang":"go"}. Use --json if you prefer JSON — the structure is identical.
Why not pipe GNU tools tojq?
ls, cat, stat, find, diff, and wc don't output JSON. jq can't help with them. aict provides structured output for the entire toolchain, not just grep. (aict also ships its own jq for querying JSON files with path expressions.)
How does this compare to ripgrep?
ripgrep is much faster for pure search. aict grep adds language detection, MIME type, and a consistent output format shared with every other tool. Use ripgrep for speed-critical search; use aict when the agent needs structured context.
How does this compare to eza / lsd?
eza and lsd are better ls for humans — great colors and formatting. aict outputs data structures, not formatted tables. They're solving different problems.
Does it work on Windows?
ls, cat, stat, wc, find, diff, grep, head, tail, sort, uniq, cut, tr, sed, awk, jq, tar, checksums, df, and path utilities work on Windows. system is Linux/macOS; ps is Linux-only (it reads /proc). Unsupported platforms get a structured <error> element, never a crash.
Is this safe to run in a sandboxed environment?
Yes. aict is strictly read-only. No network requests (MIME detection uses the Go stdlib, not HTTP). No telemetry. No data collection. It only reads paths you explicitly pass to it.
How many dependencies does it have?
One: the official MCP Go SDK, used only by the aict mcp subcommand. All 33 tools and every internal package are pure Go standard library — enforced as a hard constraint in AGENTS.md.
Bug reports, feature requests, and PRs are welcome. See CONTRIBUTING.md for guidelines, code style, and the tool implementation pattern.
Issues tagged good first issue are a good place to start.
MIT — built entirely by AI tools, for AI tools.