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