minimal-agent 0.1.9 → 0.3.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.
Files changed (97) hide show
  1. package/README.md +383 -122
  2. package/package.json +19 -12
  3. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +186 -0
  4. package/plugins/ralph-wiggum/commands/ralph-loop.md +6 -16
  5. package/plugins/ralph-wiggum/plugin.js +205 -0
  6. package/plugins/ralph-wiggum/src/goalState.js +260 -0
  7. package/plugins/ralph-wiggum/src/sentinels.js +21 -0
  8. package/plugins/ralph-wiggum/src/stopHookRunner.js +104 -0
  9. package/plugins/ralph-wiggum/src/verificationGate.js +202 -0
  10. package/plugins/workflow-runner/.claude-plugin/plugin.json +5 -0
  11. package/plugins/workflow-runner/commands/workflow.md +15 -0
  12. package/plugins/workflow-runner/commands/workflows.md +8 -0
  13. package/plugins/workflow-runner/plugin.js +36 -0
  14. package/plugins/workflow-runner/src/expressions.js +369 -0
  15. package/plugins/workflow-runner/src/index.js +174 -0
  16. package/plugins/workflow-runner/src/loader.js +183 -0
  17. package/plugins/workflow-runner/src/runner.js +290 -0
  18. package/plugins/workflow-runner/src/stepExecutors/assert.js +28 -0
  19. package/plugins/workflow-runner/src/stepExecutors/llm.js +44 -0
  20. package/plugins/workflow-runner/src/stepExecutors/skill.js +103 -0
  21. package/plugins/workflow-runner/src/stepExecutors/tool.js +35 -0
  22. package/plugins/workflow-runner/src/types.js +59 -0
  23. package/plugins/workflow-runner/src/workflowState.js +46 -0
  24. package/skills/image-gen-openrouter/SKILL.md +121 -0
  25. package/skills/subtitle-srt/SKILL.md +134 -0
  26. package/skills/tts-zh/SKILL.md +137 -0
  27. package/skills/video-compose/SKILL.md +139 -0
  28. package/src/bootstrap/cwdArg.js +22 -0
  29. package/src/bootstrap/workingDir.js +31 -0
  30. package/src/cli/configWizard.js +272 -0
  31. package/src/cli/print.js +192 -0
  32. package/src/config/configFile.js +78 -0
  33. package/src/config.js +118 -0
  34. package/src/context/compact.js +357 -0
  35. package/src/context/microCompactLite.js +151 -0
  36. package/src/context/persistContext.js +109 -0
  37. package/src/context/reactiveCompact.js +121 -0
  38. package/src/context/sessionPath.js +58 -0
  39. package/src/context/snipCompact.js +112 -0
  40. package/src/context/tokenCounter.js +66 -0
  41. package/src/llm/client.js +182 -0
  42. package/src/loop.js +230 -0
  43. package/src/main.js +116 -0
  44. package/src/plugin-sdk.js +24 -0
  45. package/src/plugins/commandRouter.js +169 -0
  46. package/src/plugins/hookEngine.js +258 -0
  47. package/src/plugins/pluginApi.js +23 -0
  48. package/src/plugins/pluginLoader.js +71 -0
  49. package/src/plugins/pluginRunner.js +65 -0
  50. package/src/plugins/transcript.js +171 -0
  51. package/src/prompts/projectInstructions.js +48 -0
  52. package/src/prompts/skillList.js +126 -0
  53. package/src/prompts/system.js +155 -0
  54. package/src/session/runTurn.js +41 -0
  55. package/src/session/sessionState.js +19 -0
  56. package/src/tools/bash/bash.js +352 -0
  57. package/src/tools/bash/semantics.js +85 -0
  58. package/src/tools/bash/warnings.js +98 -0
  59. package/src/tools/edit/edit.js +253 -0
  60. package/src/tools/edit/multi-edit.js +155 -0
  61. package/src/tools/glob/glob.js +97 -0
  62. package/src/tools/grep/grep.js +185 -0
  63. package/src/tools/grep/rgPath.js +173 -0
  64. package/src/tools/index.js +94 -0
  65. package/src/tools/read/read.js +209 -0
  66. package/src/tools/shared/fileState.js +61 -0
  67. package/src/tools/shared/fileUtils.js +281 -0
  68. package/src/tools/shared/schemas.js +16 -0
  69. package/src/tools/types.js +21 -0
  70. package/src/tools/webbrowser/browser.js +55 -0
  71. package/src/tools/webbrowser/webbrowser.js +194 -0
  72. package/src/tools/webfetch/preapproved.js +267 -0
  73. package/src/tools/webfetch/webfetch.js +317 -0
  74. package/src/tools/websearch/websearch.js +161 -0
  75. package/src/tools/write/write.js +125 -0
  76. package/src/types/turndown.d.ts +23 -0
  77. package/src/types.js +16 -0
  78. package/src/ui/App.js +37 -0
  79. package/src/ui/InputBox.js +240 -0
  80. package/src/ui/MessageList.js +28 -0
  81. package/src/ui/Root.js +70 -0
  82. package/src/ui/StatusLine.js +41 -0
  83. package/src/ui/ToolStatus.js +11 -0
  84. package/src/ui/hooks/useChat.js +234 -0
  85. package/src/ui/hooks/usePasteHandler.js +137 -0
  86. package/src/ui/hooks/useTextBuffer.js +55 -0
  87. package/src/ui/hooks/useTokenUsage.js +30 -0
  88. package/src/ui/textBuffer.js +217 -0
  89. package/src/utils/packageRoot.js +37 -0
  90. package/src/utils/resourcePaths.js +49 -0
  91. package/src/utils/zodToJson.js +29 -0
  92. package/workflows/book-review-short.yaml +99 -0
  93. package/workflows/e2e-write-greet.yaml +27 -0
  94. package/workflows/schema.json +74 -0
  95. package/workflows/youtube-shorts.yaml +171 -0
  96. package/dist/main.js +0 -5936
  97. package/plugins/ralph-wiggum/scripts/setup-ralph-loop.sh +0 -203
package/README.md CHANGED
@@ -1,8 +1,43 @@
1
1
  # minimal-agent
2
2
 
3
- > 最小化的 AI 编程助手 —— 单对话 · 9 个内置工具 · 自动压缩 · OpenAI 兼容协议 · Ink TUI
3
+ > 最小化的 AI 编程助手生态 —— ReAct CLI + 插件系统 + 可视化 workflow 编辑器
4
+ > 10 个内置工具 · 23 个 Skills · 2 个内置插件 · OpenAI 兼容协议 · Ink TUI · Bun-first
4
5
 
5
- 一个用于**学习和教学**的 Agent 项目,从 [kakadeai](../) 主仓库的 Claude Code 源码中抽取并简化而来,把"一个能调工具、能自动压缩上下文、能流式输出的 agent" ~3000 行 TypeScript 内讲清楚。
6
+ 一个用于**学习和教学**的 Agent 项目,从 [kakadeai](../) 主仓库的 Claude Code 源码中抽取并简化而来,把"一个能调工具、能自动压缩上下文、能跑插件、能可视化编排 workflow 的 agent"在合理规模的 TypeScript 里讲清楚。
7
+
8
+ > 项目由**两个独立的 npm 包**组成:`minimal-agent`(主体 ReAct CLI)+ `minimal-workflw`(可视化 workflow 编辑器)。两边只通过 `workflows/*.yaml` 文件耦合,零运行时依赖。
9
+
10
+ ---
11
+
12
+ ## 一图看全
13
+
14
+ ```
15
+ ┌──────────────────────────────────────────────────────────────────┐
16
+ │ minimal-agent (主包) │
17
+ │ │
18
+ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
19
+ │ │ ReAct │ ─→ │ Tools │ │ Plugins/ │ │
20
+ │ │ runQuery│ │ 10 个 │ │ ralph-loop │ │
21
+ │ └─────────┘ └─────────┘ │ workflow-* │ │
22
+ │ ↑ └─────────────┘ │
23
+ │ │ 调用 ↑ │
24
+ │ ↓ │ 读 │
25
+ │ ┌─────────┐ │ │
26
+ │ │ Skills/ │ ─── 23 个 SKILL.md ──────┘ │
27
+ │ └─────────┘ │
28
+ └──────────────────────────────────┬───────────────────────────────┘
29
+
30
+ ↓ 读写 workflows/*.yaml
31
+
32
+ ┌──────────────────────────────────┴───────────────────────────────┐
33
+ │ minimal-workflw (独立编辑器包, editor/) │
34
+ │ │
35
+ │ Bun + Vite + React + React Flow │
36
+ │ ┌────────┐ ┌────────┐ ┌────────┐ │
37
+ │ │ Palette│ │ Canvas │ │Inspector│ │
38
+ │ └────────┘ └────────┘ └────────┘ │
39
+ └──────────────────────────────────────────────────────────────────┘
40
+ ```
6
41
 
7
42
  ---
8
43
 
@@ -11,25 +46,71 @@
11
46
  | 模块 | 说明 |
12
47
  |---|---|
13
48
  | 🧠 主循环 | `runQuery` 用 AsyncGenerator 把"调 LLM → 跑工具 → 喂回去"串成一条流 |
14
- | 🔧 内置工具 | Read / Write / Edit / Glob / Grep / Bash / WebSearch / WebFetch / WebBrowser |
49
+ | 🔧 内置工具 (10) | Read / Edit / MultiEdit / Write / Glob / Grep / Bash / WebSearch / WebFetch / WebBrowser |
15
50
  | 📦 自动压缩 | 接近 context window 时自动摘要历史(auto / micro / reactive 三层) |
16
51
  | 💾 单上下文 | `~/.minimal-agent/last-context.json` 自动保存/恢复,无 session 概念 |
17
52
  | 🎨 双模式 | Ink TUI 交互模式 + `-p` 非交互 CLI 模式(管道友好) |
18
53
  | 🌐 多 provider | 走 OpenAI Chat Completions 协议,配 .env 即可切换 MiniMax / DeepSeek / Kimi / OpenAI |
19
- | 🧰 Skills 系统 | 扫描 `skills/<name>/SKILL.md` frontmatter,按需懒加载完整 prompt |
54
+ | 🧰 Skills 系统 (23) | 扫描 `skills/<name>/SKILL.md` frontmatter,按需懒加载完整 prompt |
55
+ | 🔌 插件系统 | `plugins/<id>/` drop-in 目录,声明式命令 / 富插件双契约;自带 ralph-loop + workflow-runner |
56
+ | 🎬 Workflow 引擎 | YAML DAG,7 种 step 类型(tool / llm / skill / assert / pause / branch / loop) |
57
+ | 🖼️ 可视化编辑器 | 独立 `minimal-workflw` 包,拖拽组装 workflow,节点面板从 `src/tools/` + `skills/` 自动派生 |
20
58
 
21
59
  ---
22
60
 
61
+ > ⚠️ **0.2.0 已发布到 npm 但插件系统不可用**(dynamic import .ts 在 node_modules 下被 Node 拒绝)。请使用 **0.3.0+**:源码 import 统一 `.js` 后缀 + tsc 原地编译,Bun(dev 模式)和 Node(install 模式)共用一份目录布局。
62
+
23
63
  ## 系统要求
24
64
 
25
- - **Bun ≥ 1.1**(推荐,主运行时);Node.js ≥ 20 需要走 build 流程,见末尾的[发布到 npm](#发布到-npm未来) 章节
65
+ - **Node.js ≥ 20**(npm install 后跑 bin 的运行时)或 **Bun ≥ 1.1**(推荐,dev 模式必需)
26
66
  - 一个 OpenAI 兼容的 LLM API(MiniMax / OpenAI / DeepSeek / Kimi / 自建 vLLM 均可)
27
67
  - *可选*:[Tavily](https://tavily.com) API key(开启 WebSearch)
68
+ - *可选*:[OpenRouter](https://openrouter.ai) API key(用 `image-gen-openrouter` skill 生成图片)
69
+ - *可选*:[MiniMax T2A](https://www.minimaxi.com/) 或 `pip install edge-tts`(用 `tts-zh` skill 做中文配音)
70
+ - *可选*:`ffmpeg` + `ffprobe`(跑视频合成类 workflow)
28
71
  - *可选*:`playwright-core` + chromium(开启 WebBrowser;不装会自动降级到 WebFetch)
29
72
  - *无需安装 ripgrep* —— 项目自带 `vendor/ripgrep/` 6 个平台二进制(arm64/x64 × win32/darwin/linux)
30
73
 
31
74
  ---
32
75
 
76
+ ## 安装
77
+
78
+ ### A. 从 npm 安装(终端用户)
79
+
80
+ ```bash
81
+ # 1. 装主包(ReAct CLI + 内置工具/插件/skills)
82
+ npm install -g minimal-agent
83
+
84
+ # 2. (可选)装可视化 workflow 编辑器
85
+ # ⚠ 当前 0.1 版编辑器 bin 直接吃 .ts,运行需要 Bun ≥1.1 在 PATH 上
86
+ # (主包 minimal-agent 在纯 Node 下完全可用,不依赖 Bun)
87
+ npm install -g minimal-workflw # 或:bun install -g minimal-workflw
88
+
89
+ # 3. 跑起来
90
+ minimal-agent # 启 TUI;首次会弹配置向导
91
+ minimal-workflw ~/my-agent-project # 在指定项目目录里启编辑器
92
+ ```
93
+
94
+ 两个包**独立发版、独立安装**。只想要 ReAct CLI 就装 `minimal-agent`;想拖拽编排 workflow 再加 `minimal-workflw`。两者通过 `<project>/workflows/*.yaml` 文件耦合,没有进程间通信。
95
+
96
+ ### B. 从源码运行(贡献者 / 想读源码)
97
+
98
+ ```bash
99
+ git clone https://github.com/billsoft/kakadeai.git
100
+ cd kakadeai/minimal-agent
101
+ bun install
102
+
103
+ # 主体 ReAct CLI
104
+ bun run start # TUI
105
+ bun run src/main.tsx -p "hi" # 非交互
106
+
107
+ # Workflow 编辑器(另开终端)
108
+ cd editor && bun install
109
+ bun scripts/cli.ts --project .. # 启编辑器并加载父项目的 workflows/
110
+ ```
111
+
112
+ ---
113
+
33
114
  ## 快速开始
34
115
 
35
116
  支持两种配置方式,**任选其一**即可:
@@ -37,21 +118,14 @@
37
118
  ### 方式 A:手写 .env(适合开发者 / `-p` 非交互模式)
38
119
 
39
120
  ```bash
40
- # 1. 安装依赖
41
- bun install
42
-
43
- # 2. 配置 .env(参考 .env.example)
44
121
  cp .env.example .env
45
122
  # 编辑 .env 填入 BASE_URL / API_KEY / MODEL
46
-
47
- # 3. 启动 TUI
48
123
  bun run start
49
124
  ```
50
125
 
51
126
  ### 方式 B:首次启动配置向导(适合零配置上手)
52
127
 
53
128
  ```bash
54
- bun install
55
129
  bun run start # 没检测到任何配置 → 自动弹 Ink 向导
56
130
  ```
57
131
 
@@ -59,20 +133,13 @@ bun run start # 没检测到任何配置 → 自动弹 Ink 向导
59
133
 
60
134
  > 测试不通过或中途 ESC 退出 → **不写文件** → 下次启动继续走向导,直到成功为止。
61
135
 
62
- ### 运行示例
63
-
64
- ```bash
65
- bun run src/main.tsx -p "帮我写个 hello world" # 一次性问答
66
- echo "解释这段代码" | bun run src/main.tsx -p # 管道输入
67
- ```
68
-
69
136
  ### 配置优先级
70
137
 
71
138
  ```
72
139
  process.env > ~/.minimal-agent/config.json > 弹向导
73
140
  ```
74
141
 
75
- env 永远盖过 JSON,**逐字段**生效(可以只用 env 临时覆盖单个字段做调试)。`-p` 模式不会弹向导(没 TTY),缺配置直接 stderr 报错退出。
142
+ env 永远盖过 JSON,**逐字段**生效。`-p` 模式不会弹向导(没 TTY),缺配置直接 stderr 报错退出。
76
143
 
77
144
  ### .env 字段
78
145
 
@@ -83,17 +150,14 @@ MINIMAL_AGENT_API_KEY=your-api-key
83
150
  MINIMAL_AGENT_MODEL=MiniMax-M2.7
84
151
 
85
152
  # 可选
86
- MINIMAL_AGENT_PROVIDER=minimax # 仅作显示用,默认 "env"
87
- MINIMAL_AGENT_CONTEXT_WINDOW=204800 # 模型上下文窗口,默认 128000
153
+ MINIMAL_AGENT_PROVIDER=minimax # 仅显示用,默认 "env"
154
+ MINIMAL_AGENT_CONTEXT_WINDOW=204800 # 上下文窗口,默认 128000
88
155
  TAVILY_API_KEY= # 启用 WebSearch
156
+ OPENROUTER_API_KEY= # 启用 image-gen-openrouter skill
157
+ MINIMAX_API_KEY= # 启用 tts-zh skill(minimax 通道)
158
+ MINIMAX_GROUP_ID= # 同上
89
159
  ```
90
160
 
91
- ### 重新配置
92
-
93
- 在 TUI 内直接输入 `/config`(不带任何其他内容),LLM 会按 `skills/config/SKILL.md` 流程逐项询问后用 Write 工具更新 `~/.minimal-agent/config.json`,提示重启生效。
94
-
95
- > `/config` 是 **LLM 驱动的 skill**(同 `/init`),不是硬编码命令(不同于 `/new` / `/compact`)—— 必须**单独输入 `/config`**,带其他内容(如 `/config 帮我看看`)不会触发。
96
-
97
161
  ---
98
162
 
99
163
  ## 命令行参数
@@ -103,60 +167,179 @@ minimal-agent [选项] [提示...]
103
167
  echo "提示" | minimal-agent [选项]
104
168
 
105
169
  选项:
106
- -p, --print 非交互模式,问答一次后退出
107
- -v, --verbose 显示工具调用 / 压缩详情
108
- -h, --help 帮助
109
- -V, --version 版本号
170
+ -p, --print 非交互模式,问答一次后退出
171
+ -v, --verbose 显示工具调用 / 压缩详情
172
+ -d, --cwd <dir> 指定工作目录(不存在自动创建)
173
+ -h, --help 帮助
174
+ -V, --version 版本号
110
175
  ```
111
176
 
112
177
  TUI 内部支持:
113
178
 
114
179
  | 输入 | 效果 |
115
180
  |---|---|
116
- | `/new` | 清空历史,重建 system prompt(带最新 skills + 项目指令);同时清掉当前工作目录下所有 `.minimal-agent*/` 状态目录 |
181
+ | `/new` | 清空历史,重建 system prompt;同时清掉当前工作目录下所有 `.minimal-agent*/` 状态目录 |
117
182
  | `/compact` | 手动压缩当前对话 |
118
- | `/config` | 重新配置 LLM provider(LLM 驱动 skill,单独输入才触发) |
119
- | `/ralph-loop "<目标>" [--max-iterations N] [--verify ...]` | 复制任务循环(PLAN→BUILD→VERIFY→HEAL→DONE 五阶段 FSM);按当前 cwd 隔离,不同工作目录可并行 |
120
- | `Alt+Enter` | 输入框换行(教学/多行 prompt 友好) |
183
+ | `/config` | 重新配置 LLM provider(LLM 驱动 skill,必须单独输入) |
184
+ | `/init` | LLM 扫一遍项目目录,生成或更新 `minimal-agent.md` |
185
+ | `/ralph-loop "<目标>" [...]` | 复制任务循环(plugin: ralph-wiggum) |
186
+ | `/workflow <name> [--input k=v]` | 执行 workflow yaml(plugin: workflow-runner) |
187
+ | `/workflows` | 列出可用 workflow |
188
+ | `Alt+Enter` | 输入框换行 |
121
189
  | `ESC` / `Ctrl+C` | 中断当前任务 |
122
190
 
123
- ### `/ralph-loop` 速查
124
-
125
- 最小用法:
126
-
127
- ```bash
128
- /ralph-loop "在当前目录创建 hello.txt 内容为 hi" --max-iterations 5 --verify "file_exists:hello.txt"
129
- ```
130
-
131
- 参数:
132
- - `--max-iterations N` — 用户上限,默认 50,硬天花板 200
133
- - `--verify <type>:<args>` — 完成判据,支持四种 type:
134
- - `shell:<cmd>` —— 命令退出码 0 即通过(跨平台 spawn)
135
- - `file_exists:<path>` —— 文件存在即通过(相对路径以当前 cwd 为锚)
136
- - `file_contains:<file>:<regex>` —— 文件内容匹配正则即通过
137
- - `test_count:<N>` —— 跑 `bun test` 并解析 "X pass",X≥N 即通过
138
-
139
- agent 通过输出 `<promise>DONE</promise>` 哨兵声明完成,验证通过才真正退出;声明完成但验证不过会回 BUILD 阶段继续干,连续 3 次失败强转 HEAL 阶段。若 agent 判断方案不可行,可输出 `<PROMISE>NEED_REPLAN</PROMISE>` 强制回 PLAN 重新规划。
140
-
141
- FSM 状态文件落在 `<cwd>/.minimal-agent-ralph-wiggum/`(`goal.md` / `phase.md` / `progress.md` / `learnings.md` / `decisions.md` / `completion.md`),循环结束或 `/new` 时自动清理。Windows / macOS / Linux 都能跑。
142
-
143
191
  ---
144
192
 
145
- ## 内置工具
193
+ ## 内置工具(10 个)
146
194
 
147
195
  | 名称 | 用途 | 实现 |
148
196
  |---|---|---|
149
197
  | `Read` | 读文件(支持 offset / limit / 二进制嗅探) | `node:fs/promises` |
150
198
  | `Write` | 整体写文件(覆盖前要先 Read) | `node:fs/promises` |
151
199
  | `Edit` | 字符串精确替换(old_string 必须唯一) | `node:fs/promises` |
200
+ | `MultiEdit` | 一次性多处替换(原子操作) | `node:fs/promises` |
152
201
  | `Glob` | 文件名模式匹配 | `fast-glob` |
153
202
  | `Grep` | 文件内容搜索 | spawn vendor 内置 ripgrep |
154
203
  | `Bash` | 终端命令执行(黑名单 + 超时双保险) | `node:child_process` |
155
204
  | `WebSearch` | Web 搜索(自动注入当前日期) | Tavily API |
156
205
  | `WebFetch` | 抓网页 → HTML→Markdown(LRU 缓存) | `fetch` + `turndown` |
157
- | `WebBrowser` | 浏览器交互(点击/填表单/截图/JS 渲染) | playwright-core(**可选依赖**) |
206
+ | `WebBrowser` | 浏览器交互(点击 / 填表单 / 截图 / JS 渲染) | playwright-core(**可选依赖**) |
158
207
 
159
- 工具入口:`src/tools/index.ts` 的 `ALL_TOOLS` 数组和 `executeTool()` 函数。
208
+ 工具入口:`src/tools/index.ts` 的 `ALL_TOOLS`。
209
+
210
+ ---
211
+
212
+ ## Skills 系统(23 个)
213
+
214
+ 每个 `skills/<name>/SKILL.md` 都是一段 LLM 子系统提示词 + frontmatter 元数据。框架在每轮组装 system prompt 时**实时扫描**目录注入 `name + description`,完整 body 按需懒加载(用户输入命中 description 触发词时才载入)。**新增 skill 改文件即可,不用改代码。**
215
+
216
+ 当前内置 skills:
217
+
218
+ | 类别 | Skill |
219
+ |---|---|
220
+ | **配置 / 元** | `config` `init` `compact` `simplify` `batch` `skill-creator` `mcp-builder` |
221
+ | **创作** | `algorithmic-art` `canvas-design` `frontend-design` `theme-factory` `web-artifacts-builder` `webapp-testing` |
222
+ | **文档** | `docx` `pdf` `pptx` `xlsx` `diff` `commit` |
223
+ | **多媒体流水线** | `image-gen-openrouter`(OpenRouter Nano Banana 文生图) `tts-zh`(MiniMax + edge-tts 中文配音) `subtitle-srt`(通用 SRT 字幕) `video-compose`(ffmpeg 视频合成) |
224
+
225
+ **双源加载**:cwd 优先,packageRoot fallback。同名(frontmatter::name)时 cwd 覆盖。用户在自己项目里放 `skills/<name>/SKILL.md` 即可扩展或覆盖 npm 包自带的 skill。
226
+
227
+ ---
228
+
229
+ ## 插件系统
230
+
231
+ **硬约束**:`src/plugins/` 只放**框架**(discovery / 命令解析 / plugin.ts 加载 / hook 引擎 / transcript);任何具体插件功能都住在各自的 `plugins/<id>/` 目录内。**新增插件 = 把目录 drop 进 `plugins/`,`src/` 一行不动。**
232
+
233
+ ### 内置插件
234
+
235
+ #### `plugins/ralph-wiggum/` — `/ralph-loop`
236
+
237
+ Karpathy 风格的复制任务循环:PLAN → BUILD → VERIFY → HEAL → DONE 五阶段 FSM,配合 verification gate 和 stop-hook 反馈。
238
+
239
+ ```bash
240
+ /ralph-loop "在当前目录创建 hello.txt 内容为 hi" --max-iterations 5 --verify "file_exists:hello.txt"
241
+ ```
242
+
243
+ `--verify` 支持四种判据:`shell:<cmd>` / `file_exists:<path>` / `file_contains:<file>:<regex>` / `test_count:<N>`。FSM 状态文件落在 `<cwd>/.minimal-agent-ralph-wiggum/`,按当前 cwd 隔离,不同工作目录可并行。
244
+
245
+ #### `plugins/workflow-runner/` — `/workflow` `/workflows`
246
+
247
+ YAML DAG workflow 执行器,支持 7 种 step:
248
+
249
+ | step 类型 | 用途 |
250
+ |---|---|
251
+ | `tool` | 直接调内置工具(Read / Bash / Write …) |
252
+ | `llm` | 单轮 LLM 调用(可 `capture` 输出到变量) |
253
+ | `skill` | LLM 驱动子循环,body 当子 system prompt |
254
+ | `assert` | 表达式断言(`fileExists(...)` / `length(...)` / 比较运算) |
255
+ | `branch` | 条件分支 |
256
+ | `loop` | 循环(`over:` + `as:` + 自动 `${as}_idx`) |
257
+ | `pause` | 等待用户输入 |
258
+
259
+ ```bash
260
+ /workflow youtube-shorts --input topic="猫咪赛博朋克城市探险"
261
+ /workflows # 列出所有 workflow
262
+ ```
263
+
264
+ ### 写自己的插件
265
+
266
+ 参考 `plugins/HOW-TO-WRITE-A-PLUGIN.md`。两种契约任选其一即可生效:
267
+
268
+ - **声明式**(纯 markdown):`plugins/<id>/commands/<cmd>.md` 用 frontmatter + prompt 模板,框架自动注册命令,零 TS 代码。
269
+ - **富插件**:根目录写 `plugin.ts` 导出 `default { runCommand }`,由插件自己 yield `LoopEvent` 流(可调 `runQuery` / 跑循环 / 起子进程)。
270
+
271
+ 插件作者唯一稳定 import 路径:`from '../../src/plugin-sdk.ts'`。
272
+
273
+ ---
274
+
275
+ ## minimal-workflw 可视化编辑器(editor/ 子项目)
276
+
277
+ 独立的 npm 包,运行时与 `minimal-agent` **零耦合**,仅通过 `workflows/*.yaml` + `.editor-cache/manifest.json` 两个文件交换数据。
278
+
279
+ ### 启动方式
280
+
281
+ ```bash
282
+ # 已全局安装时
283
+ minimal-workflw # 当前 cwd 当 minimal-agent 项目根
284
+ minimal-workflw ~/my-project # 指定项目根
285
+ minimal-workflw --refresh-manifest # 强制刷新工具/skill 清单
286
+
287
+ # 从源码跑(在 editor/ 目录下)
288
+ bun scripts/cli.ts --project .. # 一键起后端 + Vite
289
+ bun run dev # 只起 Vite 前端(不带后端文件 IO)
290
+ bun run server # 只起 Bun.serve() 后端
291
+ ```
292
+
293
+ > **为什么是 `bun scripts/cli.ts` 而不是 `bun run dev`?**
294
+ > - `bun run dev` 只起 Vite 前端,UI 能渲染但**没有后端**,文件列表 / 读写 / manifest 端点全是 404
295
+ > - `bun scripts/cli.ts` 是 bin 入口,**同时**起 `Bun.serve()` 文件 IO 后端 + Vite 前端,完整可用
296
+ > - npm 发布后用户执行的 `minimal-workflw` 命令就是这个 cli.ts
297
+
298
+ ### 启动序列(编辑器 CLI 内部)
299
+
300
+ 1. 解析 `--project`(默认 cwd)→ 验证目录存在 + `workflows/` 子目录存在
301
+ 2. 检查 `<project>/.editor-cache/manifest.json` 新鲜度,过期则 spawn `<project>/scripts/export-manifest.ts` 重新生成(脚本缺失时 fallback 到 `editor/public/manifest.fallback.json`)
302
+ 3. 启动 `Bun.serve()` 在 127.0.0.1:5174(文件 IO + manifest 端点)
303
+ 4. 启动 `bunx --bun vite` 在 127.0.0.1:5173
304
+
305
+ ### 解耦红线(D2 红线)
306
+
307
+ - ❌ 编辑器**绝不** import `minimal-agent/src/*` 任何模块
308
+ - ❌ minimal-agent 主程序**绝不**在启动路径里 spawn 编辑器
309
+ - ✅ 接触面只有:`workflows/*.yaml`(双向)+ `.editor-cache/manifest.json`(单向,主项目→编辑器)
310
+
311
+ ---
312
+
313
+ ## 示例:YouTube Shorts 生产流水线
314
+
315
+ `workflows/youtube-shorts.yaml` 是一条端到端示例,演示如何在编辑器里拖出非编程用户场景:
316
+
317
+ ```
318
+ 输入主题
319
+
320
+ LLM 写 5 幕剧本 (JSON)
321
+
322
+ parse_script (jq 拆出 prompts.txt / narrations.txt)
323
+
324
+ loop 5×: image-gen-openrouter ──→ assets/img_0..4.png
325
+
326
+ loop 5×: tts-zh ──→ assets/audio_0..4.mp3
327
+
328
+ probe_durations + merge_audio ──→ assets/audio.mp3
329
+
330
+ subtitle-srt ──→ assets/subs.srt
331
+
332
+ video-compose (ffmpeg) ──→ out/shorts.mp4
333
+ ```
334
+
335
+ 跑起来:
336
+
337
+ ```bash
338
+ export OPENROUTER_API_KEY=sk-or-...
339
+ bun run src/main.tsx -p "/workflow youtube-shorts --input topic='猫咪赛博朋克城市探险'"
340
+ ```
341
+
342
+ 整条流水线**零代码改动** —— 4 个 skill 都是 markdown,1 个 workflow 是 yaml。在 `minimal-workflw` 里打开 `youtube-shorts.yaml`,可视化拖拽改 prompt、换 skill、加节点。
160
343
 
161
344
  ---
162
345
 
@@ -164,31 +347,124 @@ FSM 状态文件落在 `<cwd>/.minimal-agent-ralph-wiggum/`(`goal.md` / `phase
164
347
 
165
348
  ```
166
349
  minimal-agent/
167
- ├── src/
168
- │ ├── main.tsx # 入口:CLI 参数解析 + 模式分发
169
- │ ├── loop.ts # Agent 主循环(runQuery
170
- │ ├── config.ts # .env 加载
171
- │ ├── types.ts # 核心类型(Message / Tool / LoopEvent
172
- │ ├── cli/print.ts # -p 非交互模式
173
- │ ├── llm/client.ts # OpenAI 兼容流式客户端
174
- │ ├── context/ # 压缩 / 持久化 / token 计数
175
- ├── compact.ts # auto + force 压缩
176
- ├── reactiveCompact.ts # 上下文撑爆时的自救
177
- ├── microCompactLite.ts # 工具回填 inline 压缩
178
- └── persistContext.ts # ~/.minimal-agent/last-context.json
179
- ├── prompts/ # system prompt 拼装
180
- ├── system.ts # 主 prompt + 项目指令注入
181
- ├── projectInstructions.ts # 项目根 minimal-agent.md 加载
182
- └── skillList.ts # 扫描 skills/ 生成 skill 列表
183
- ├── tools/ # 9 个内置工具(见上表)
184
- │ └── ui/ # Ink TUI 组件
185
- ├── skills/ # Skill 库(每个子目录一个 SKILL.md)
186
- ├── vendor/ripgrep/ # 自带的 ripgrep 二进制(6 个平台)
350
+ ├── src/ # 主体内核
351
+ │ ├── main.tsx # 入口:CLI 参数 + 模式分发
352
+ │ ├── loop.ts # Agent 主循环 (runQuery)
353
+ │ ├── config.ts # .env + ~/.minimal-agent/config.json
354
+ │ ├── types.ts # 核心类型 (Message / Tool / LoopEvent)
355
+ │ ├── plugin-sdk.ts # 插件作者唯一稳定 import 路径
356
+ │ ├── plugins/ # 插件框架(discovery / loader / router / hooks)
357
+ │ ├── llm/client.ts # OpenAI 兼容流式客户端
358
+ │ ├── context/ # 压缩 / 持久化 / token 计数
359
+ │ ├── prompts/ # system prompt 拼装
360
+ │ ├── tools/ # 10 个内置工具
361
+ │ └── ui/ # Ink TUI 组件
362
+ ├── plugins/ # 实际插件(drop-in)
363
+ │ ├── HOW-TO-WRITE-A-PLUGIN.md
364
+ │ ├── ralph-wiggum/ # /ralph-loop
365
+ │ └── workflow-runner/ # /workflow + /workflows
366
+ ├── skills/ # 23 个 SKILL.md
367
+ ├── workflows/ # YAML DAG 工作流定义
368
+ ├── book-review-short.yaml
369
+ ├── e2e-write-greet.yaml
370
+ │ └── youtube-shorts.yaml
371
+ ├── editor/ # minimal-workflw 独立子包
372
+ │ ├── package.json # name: minimal-workflw, bin: minimal-workflw
373
+ │ ├── scripts/
374
+ │ │ ├── cli.ts # bin 入口(一键起后端+前端)
375
+ │ │ └── server.ts # Bun.serve() 文件 IO + manifest 端点
376
+ │ ├── public/manifest.fallback.json
377
+ │ └── src/ # React + React Flow + Monaco UI
378
+ ├── scripts/
379
+ │ └── export-manifest.ts # 主项目 → editor 的工具/skill 元数据导出
380
+ ├── vendor/ripgrep/ # 自带 ripgrep 二进制(6 个平台)
187
381
  ├── test/ # bun test 用例
188
- ├── examples/ # Ink 教学示例
189
- └── minimal-agent.md # 项目指令(类似 CLAUDE.md,会被自动注入 system prompt)
382
+ └── minimal-agent.md # 项目指令(类似 CLAUDE.md,自动注入 system prompt)
383
+ ```
384
+
385
+ ---
386
+
387
+ ## 发布与安装(npm 工作流)
388
+
389
+ 项目作为**两个独立 npm 包**发布:
390
+
391
+ | 包 | 路径 | 命令 | 职责 |
392
+ |---|---|---|---|
393
+ | `minimal-agent` | 仓库根 | `minimal-agent` | ReAct CLI + 工具 + 插件 + skills |
394
+ | `minimal-workflw` | `editor/` | `minimal-workflw` | 可视化 workflow 编辑器 |
395
+
396
+ > **为什么不打成一个包**?90% 的 ReAct CLI 用户不需要编辑器,编辑器的依赖(React / Vite / @xyflow/react / Monaco 等)有几十 MB,不应该污染主包。两边解耦后用户按需取用,通过 `<project>/workflows/*.yaml` 文件交流,无进程间通信。
397
+
398
+ ### 模块解析模式(dev vs install 通用范式)
399
+
400
+ 主包采用**标准 NodeNext + tsc 原地编译**模式,解决了 npm 包"dev 跑 .ts,install 跑 .js"的通用目录差异问题:
401
+
402
+ - 源码(仓库 / dev)里 import 统一写 `.js` 后缀:`import { x } from './foo.js'`
403
+ - dev 模式跑 `bun run dev`:Bun 把 `'./foo.js'` 透明解析到 `./foo.ts` 源文件,**无需任何构建**
404
+ - publish 前 `tsc --build`:原地输出 `./foo.js`(与 .ts 兄弟位置,路径布局完全一致)
405
+ - `.npmignore` 排除 `*.ts/*.tsx`,tarball 里**只发 .js**
406
+ - install 后 Node 直接吃 .js,Bun 也吃 .js(行为统一)
407
+ - 源码 `plugin.ts` 里 `import '../../src/plugin-sdk.js'` 在 dev / install 两种模式下都指向同一份模块(dev=plugin-sdk.ts,install=plugin-sdk.js),**模块身份天然 singleton**
408
+
409
+ 这是 `execa` / `p-queue` / `ink` 等纯 ESM TypeScript 库的通用做法。
410
+
411
+ ### 用户安装(终端)
412
+
413
+ ```bash
414
+ # 只装主包
415
+ npm install -g minimal-agent
416
+ minimal-agent # 启 ReAct TUI
417
+
418
+ # 加装编辑器
419
+ npm install -g minimal-workflw
420
+ minimal-workflw /path/to/your-project # 启编辑器
421
+ ```
422
+
423
+ ### 开发者发布流程
424
+
425
+ #### 1. 发布主包
426
+
427
+ ```bash
428
+ cd minimal-agent
429
+ npm version patch # 0.3.0 → 0.3.1
430
+ npm publish --dry-run # 验证 tarball(src/*.js + plugins/*.js + skills + workflows + vendor)
431
+ npm publish # prepublishOnly 自动跑 clean + tsc --build
432
+ ```
433
+
434
+ 主包 `package.json` 关键字段:
435
+
436
+ ```json
437
+ {
438
+ "name": "minimal-agent",
439
+ "bin": { "minimal-agent": "src/main.js" },
440
+ "files": ["src", "plugins", "skills", "workflows", "vendor/ripgrep", "README.md", "LICENSE"],
441
+ "engines": { "node": ">=20" },
442
+ "scripts": {
443
+ "build": "tsc --build",
444
+ "clean": "bun scripts/clean-build.ts",
445
+ "prepublishOnly": "bun run clean && bun run build"
446
+ }
447
+ }
448
+ ```
449
+
450
+ `prepublishOnly` 先清旧产物再跑 tsc 原地编译,`.npmignore` 排除源码 .ts/.tsx 让 tarball 里**只有 .js**。`src/main.tsx` 首行 `#!/usr/bin/env node` 由 tsc 编译产物保留,直接作为 bin。
451
+
452
+ #### 2. 发布编辑器包
453
+
454
+ ```bash
455
+ cd minimal-agent/editor
456
+ npm version patch
457
+ bun run build # Vite 把前端打到 editor/dist/
458
+ npm publish --dry-run
459
+ npm publish
190
460
  ```
191
461
 
462
+ 编辑器是独立子项目,Bun 跑 `.ts` 源码作为 bin(`scripts/cli.ts` 首行有 `#!/usr/bin/env bun`),与主包发版策略不同但**目录耦合零依赖**——只通过文件系统读 `<project>/workflows/*.yaml`。
463
+
464
+ #### 3. 版本独立
465
+
466
+ 两个包的 version 字段**互相独立**,不必同步。改主包内核 → 只升主包;改编辑器 UI → 只升编辑器。
467
+
192
468
  ---
193
469
 
194
470
  ## 工作原理(一句话版)
@@ -212,18 +488,27 @@ runQuery (loop.ts)
212
488
  └── continue # 下一轮
213
489
  ```
214
490
 
215
- 详见 `src/loop.ts` 的注释。
491
+ `/workflow` / `/ralph-loop` 这类插件命令在主循环**之前**被路由器拦截,由 `pluginRunner` 分发到对应插件的 `runCommand` 接管。详见 `src/loop.ts` + `src/plugins/pluginRunner.ts`。
216
492
 
217
493
  ---
218
494
 
219
495
  ## 开发命令
220
496
 
221
497
  ```bash
222
- bun run start # 跑 TUI
223
- bun run dev # 热重载
224
- bun test # 跑全部测试(test/*)
225
- bun test test/llm.live.test.ts # 跑单个测试文件
226
- tsc --noEmit # 类型检查
498
+ # 主包
499
+ bun run start # TUI
500
+ bun run dev # 热重载
501
+ bun test # 跑全部测试
502
+ bun test test/llm.live.test.ts # 单个测试文件
503
+ bun run typecheck # tsc --noEmit
504
+ bun run build # tsup 打包
505
+
506
+ # 编辑器
507
+ cd editor
508
+ bun scripts/cli.ts --project .. # 一键起后端 + 前端
509
+ bun run dev # 只起前端
510
+ bun run server # 只起后端
511
+ bun test
227
512
  ```
228
513
 
229
514
  ---
@@ -234,14 +519,16 @@ minimal-agent 是 Claude Code 源码的**教学版**:
234
519
 
235
520
  | 维度 | kakadeai/Claude Code | minimal-agent |
236
521
  |---|---|---|
237
- | 代码量 | 数十万行 | ~3000 |
522
+ | 代码量 | 数十万行 | 数千行 |
238
523
  | 主循环 | QueryEngine + 状态机 | 单层 while + AsyncGenerator |
239
- | Tool 数 | 60+(含 MCP / Agent / Notebook 等) | 9 个内置 |
524
+ | Tool 数 | 60+(含 MCP / Agent / Notebook 等) | 10 个内置 |
240
525
  | Skill 加载 | `registerBundledSkill()` 注册 | 扫描目录 + frontmatter 解析 |
526
+ | Plugin | builtin / bundled plugin registry | drop-in `plugins/<id>/` 目录 |
527
+ | Workflow | / | YAML DAG + 可视化编辑器 |
241
528
  | Provider | Anthropic SDK | OpenAI Chat Completions(多家兼容) |
242
529
  | UI | 完整 Ink 框架 + 主题系统 | 基础 Ink 组件 |
243
530
 
244
- 设计目标是**砍掉一切非必要的抽象**,把"AI agent 是什么"暴露在你眼前。
531
+ 设计目标是**砍掉一切非必要的抽象**,把"AI agent 是什么 / 怎么扩展"暴露在你眼前。
245
532
 
246
533
  ---
247
534
 
@@ -253,39 +540,13 @@ minimal-agent 是 Claude Code 源码的**教学版**:
253
540
  | TUI 启动就弹向导,但我已经写了 .env | 检查 .env 是否在项目根(`bun run` 当前目录),三项变量名拼写无误 |
254
541
  | 向导测试卡 `HTTP 401` / `HTTP 404` | 401 = API key 错;404 = baseURL 没拼 `/v1` 或 model id 不对 |
255
542
  | 改完 `~/.minimal-agent/config.json` 没生效 | 当前进程仍持有旧配置;Ctrl+C 退出后重新 `bun run start` |
256
- | `rg: command not found` | 不该出现——项目自带 ripgrep;若出现说明 vendor 目录未随项目复制 |
257
- | `WebBrowser 报"无法启动浏览器"` | 需要 `npm install playwright-core && npx playwright install chromium`;或改用 WebFetch |
543
+ | `rg: command not found` | 不该出现——项目自带 ripgrep;若出现说明 vendor 目录未随 npm tarball 复制 |
544
+ | `WebBrowser 报"无法启动浏览器"` | `npm install playwright-core && npx playwright install chromium`;或改用 WebFetch |
545
+ | `minimal-workflw` 命令找不到 | 没装编辑器包;`npm install -g minimal-workflw` 即可 |
546
+ | 编辑器空白 / 节点面板没工具 | `.editor-cache/manifest.json` 缺失;`minimal-workflw --refresh-manifest` 强制刷新 |
547
+ | 编辑器看不到自定义 skill | 检查 `<project>/skills/<name>/SKILL.md` frontmatter 是否有 `name` 和 `description` 两个必填字段 |
548
+ | ffmpeg / ffprobe 未找到 | `winget install Gyan.FFmpeg` / `brew install ffmpeg` / `apt install ffmpeg` |
258
549
  | 历史"被吃掉"了 | 上次进程崩溃前未保存;`~/.minimal-agent/last-context.json` 是最后写入的一次 |
259
- | 模型回复说"我没法访问外部 API" | 检查 `MINIMAL_AGENT_BASE_URL` 是否拼了 `/v1`,model id 是否对 |
260
-
261
- ---
262
-
263
- ## 发布到 npm(未来)
264
-
265
- **当前状态:不能直接 `npm install -g minimal-agent`** —— `package.json` 里 `"private": true`,也没有 `bin` 字段和构建产物。
266
-
267
- 如果要发布到 npm 让 Node.js 用户也能用,至少要做:
268
-
269
- 1. **改 `package.json`**
270
- - 去掉 `"private": true`
271
- - 加 `"bin": { "minimal-agent": "dist/main.js" }`
272
- - 加 `"files": ["dist", "vendor", "skills"]`(控制 npm tarball 内容)
273
- - 加 `"engines": { "node": ">=20" }`
274
- 2. **加构建步骤**:用 tsup / esbuild 把 `src/main.tsx` bundle 成单文件 CJS+ESM,输出到 `dist/main.js`
275
- 3. **shebang**:构建产物首行加 `#!/usr/bin/env node`,发布前 `chmod +x`
276
- 4. **`.env` 不打包**:在 `.npmignore` 里排除 `.env*`、`vendor/ripgrep/test/`,依赖**配置向导**让用户首次跑 `npx minimal-agent` 时填配置(这正是本项目 Mode B 的设计目的)
277
- 5. **vendor ripgrep**:6 个平台二进制(~30MB)会进 tarball;如想瘦身可拆成 `optionalDependencies` 按平台下载
278
- 6. **首发流程**:`npm whoami` → `npm publish --dry-run` 验证 tarball → `npm publish`
279
-
280
- 发布后用户体验:
281
-
282
- ```bash
283
- npm install -g minimal-agent
284
- minimal-agent # 没配置 → 弹向导 → 测试通过 → 直接进 TUI
285
- minimal-agent -p "..." # 配置完成后可直接 -p
286
- ```
287
-
288
- **本项目尚未做这步**,本仓库定位为学习/教学版,主要面向 `bun run start` / `bun run dev` 本地运行的用户。
289
550
 
290
551
  ---
291
552