minimal-agent 0.1.9 → 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.
- package/README.md +405 -122
- package/dist/main.js +117 -738
- package/package.json +4 -2
- package/plugins/HOW-TO-WRITE-A-PLUGIN.md +186 -0
- package/plugins/ralph-wiggum/commands/ralph-loop.md +6 -16
- package/plugins/ralph-wiggum/plugin.ts +275 -0
- package/plugins/ralph-wiggum/src/goalState.ts +310 -0
- package/plugins/ralph-wiggum/src/sentinels.ts +24 -0
- package/plugins/ralph-wiggum/src/stopHookRunner.ts +136 -0
- package/plugins/ralph-wiggum/src/verificationGate.ts +252 -0
- package/plugins/ralph-wiggum/test/goalState.test.ts +410 -0
- package/plugins/ralph-wiggum/test/verificationGate.test.ts +122 -0
- package/plugins/workflow-runner/.claude-plugin/plugin.json +5 -0
- package/plugins/workflow-runner/commands/workflow.md +15 -0
- package/plugins/workflow-runner/commands/workflows.md +8 -0
- package/plugins/workflow-runner/plugin.ts +42 -0
- package/plugins/workflow-runner/src/expressions.ts +371 -0
- package/plugins/workflow-runner/src/index.ts +194 -0
- package/plugins/workflow-runner/src/loader.ts +193 -0
- package/plugins/workflow-runner/src/runner.ts +313 -0
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +30 -0
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +54 -0
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +115 -0
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +41 -0
- package/plugins/workflow-runner/src/types.ts +183 -0
- package/plugins/workflow-runner/src/workflowState.ts +65 -0
- package/plugins/workflow-runner/test/cli.e2e.test.ts +114 -0
- package/plugins/workflow-runner/test/e2e.test.ts +268 -0
- package/plugins/workflow-runner/test/expressions.test.ts +140 -0
- package/plugins/workflow-runner/test/fixtures/cli-e2e.yaml +27 -0
- package/plugins/workflow-runner/test/fixtures/hello-workflow.yaml +49 -0
- package/plugins/workflow-runner/test/graceful.test.ts +139 -0
- package/plugins/workflow-runner/test/loader.test.ts +216 -0
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +230 -0
- package/plugins/workflow-runner/test/runner.test.ts +511 -0
- package/skills/image-gen-openrouter/SKILL.md +121 -0
- package/skills/subtitle-srt/SKILL.md +134 -0
- package/skills/tts-zh/SKILL.md +137 -0
- package/skills/video-compose/SKILL.md +139 -0
- package/workflows/book-review-short.yaml +99 -0
- package/workflows/e2e-write-greet.yaml +27 -0
- package/workflows/schema.json +74 -0
- package/workflows/youtube-shorts.yaml +171 -0
package/README.md
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
# minimal-agent
|
|
2
2
|
|
|
3
|
-
> 最小化的 AI
|
|
3
|
+
> 最小化的 AI 编程助手生态 —— ReAct CLI + 插件系统 + 可视化 workflow 编辑器
|
|
4
|
+
> 10 个内置工具 · 23 个 Skills · 2 个内置插件 · OpenAI 兼容协议 · Ink TUI · Bun-first
|
|
4
5
|
|
|
5
|
-
一个用于**学习和教学**的 Agent 项目,从 [kakadeai](../) 主仓库的 Claude Code 源码中抽取并简化而来,把"
|
|
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 /
|
|
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
|
|
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
|
|
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 #
|
|
87
|
-
MINIMAL_AGENT_CONTEXT_WINDOW=204800 #
|
|
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
|
-
-
|
|
109
|
-
-
|
|
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
|
|
177
|
+
| `/new` | 清空历史,重建 system prompt;同时清掉当前工作目录下所有 `.minimal-agent*/` 状态目录 |
|
|
117
178
|
| `/compact` | 手动压缩当前对话 |
|
|
118
|
-
| `/config` | 重新配置 LLM provider(LLM 驱动 skill
|
|
119
|
-
| `/
|
|
120
|
-
| `
|
|
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` |
|
|
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
|
-
|
|
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 #
|
|
169
|
-
│ ├── loop.ts #
|
|
170
|
-
│ ├── config.ts #
|
|
171
|
-
│ ├── types.ts #
|
|
172
|
-
│ ├──
|
|
173
|
-
│ ├──
|
|
174
|
-
│ ├──
|
|
175
|
-
│
|
|
176
|
-
│
|
|
177
|
-
│
|
|
178
|
-
│
|
|
179
|
-
|
|
180
|
-
│
|
|
181
|
-
│
|
|
182
|
-
│
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
├──
|
|
186
|
-
├──
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
223
|
-
bun run
|
|
224
|
-
bun
|
|
225
|
-
bun test
|
|
226
|
-
|
|
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
|
-
| 代码量 | 数十万行 |
|
|
544
|
+
| 代码量 | 数十万行 | 数千行 |
|
|
238
545
|
| 主循环 | QueryEngine + 状态机 | 单层 while + AsyncGenerator |
|
|
239
|
-
| Tool 数 | 60+(含 MCP / Agent / Notebook 等) |
|
|
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 报"无法启动浏览器"` |
|
|
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
|
|