minimal-agent 0.8.2 → 0.9.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 +4 -5
- package/package.json +1 -1
- package/plugins/workflow-runner/plugin.js +1 -0
- package/plugins/workflow-runner/src/agentCommon.js +82 -47
- package/plugins/workflow-runner/src/expressions.js +48 -2
- package/plugins/workflow-runner/src/index.js +1 -0
- package/plugins/workflow-runner/src/jsonPayload.js +1 -1
- package/plugins/workflow-runner/src/loader.js +70 -8
- package/plugins/workflow-runner/src/recovery.js +43 -35
- package/plugins/workflow-runner/src/runner.js +143 -34
- package/plugins/workflow-runner/src/stepExecutors/agent.js +64 -35
- package/plugins/workflow-runner/src/stepExecutors/assert.js +14 -2
- package/plugins/workflow-runner/src/stepExecutors/llm.js +48 -107
- package/plugins/workflow-runner/src/stepExecutors/skill.js +50 -39
- package/plugins/workflow-runner/src/stepExecutors/tool.js +20 -7
- package/plugins/workflow-runner/src/stepExecutors/verify.js +11 -3
- package/plugins/workflow-runner/src/taskIdentity.js +141 -0
- package/plugins/workflow-runner/src/types.js +1 -2
- package/plugins/workflow-runner/src/workflowState.js +211 -28
- package/skills/adversarial-review/SKILL.md +0 -1
- package/skills/pptx/scripts/office/__pycache__/soffice.cpython-314.pyc +0 -0
- package/src/askBridge.js +10 -0
- package/src/context/archive.js +4 -0
- package/src/infra/verification.js +25 -13
- package/src/llm/client.js +160 -12
- package/src/loop.js +15 -12
- package/src/plugins/pluginLoader.js +2 -1
- package/src/plugins/pluginRunner.js +9 -21
- package/src/prompts/pluginList.js +5 -5
- package/src/prompts/promptAssembly.js +3 -38
- package/src/prompts/skillList.js +1 -1
- package/src/prompts/system.js +0 -1
- package/src/ui/AskQuestion.js +20 -3
- package/src/ui/InputBox.js +70 -3
- package/src/ui/hooks/useChat.js +13 -0
- package/workflows/adaptive-research.yaml +4 -3
- package/workflows/book-review-short.yaml +5 -3
- package/workflows/e2e-write-greet.yaml +3 -2
- package/workflows/plan.yaml +219 -168
- package/workflows/ralph-loop.yaml +169 -0
- package/workflows/youtube-shorts.yaml +8 -5
- package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
- package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
- package/plugins/ralph-wiggum/README.md +0 -197
- package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
- package/plugins/ralph-wiggum/commands/help.md +0 -137
- package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
- package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
- package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
- package/plugins/ralph-wiggum/plugin.js +0 -280
- package/plugins/ralph-wiggum/src/goalState.js +0 -258
- package/plugins/ralph-wiggum/src/sentinels.js +0 -76
- package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
- package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
- package/plugins/workflow-runner/src/miniReAct.js +0 -223
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
name: ralph-loop
|
|
2
|
+
description: |
|
|
3
|
+
Ralph Wiggum 迭代开发循环——通过重复执行同一目标直到完成。
|
|
4
|
+
|
|
5
|
+
等价于原 ralph-wiggum 插件的 /ralph-loop 命令,完全用声明式工作流实现。
|
|
6
|
+
核心机制:while 循环 + fresh context(每轮重置 history)+ 哨兵协议 + 验证门。
|
|
7
|
+
|
|
8
|
+
用法:/workflow ralph-loop "你的目标" --max-iterations 50 --verify "shell:bun test"
|
|
9
|
+
inputs:
|
|
10
|
+
- name: goal
|
|
11
|
+
type: string
|
|
12
|
+
required: true
|
|
13
|
+
description: "不可变目标描述(如'实现用户认证模块并确保测试通过')"
|
|
14
|
+
- name: max_iterations
|
|
15
|
+
type: number
|
|
16
|
+
default: 50
|
|
17
|
+
description: "最大迭代次数(安全阀,默认 50,硬上限 200)"
|
|
18
|
+
- name: verify_checks
|
|
19
|
+
type: string
|
|
20
|
+
default: ""
|
|
21
|
+
description: "验证门检查项(逗号分隔,如 'shell:bun test,file_exists:src/index.ts')。空则跳过验证门。"
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
# === 初始化阶段变量 ===
|
|
25
|
+
- id: init_phase
|
|
26
|
+
llm: "PLAN"
|
|
27
|
+
capture:
|
|
28
|
+
result: phase
|
|
29
|
+
|
|
30
|
+
# === 初始化教训记录文件(fresh context 下跨轮存活) ===
|
|
31
|
+
- id: init_learnings
|
|
32
|
+
tool: "Write(file_path='.minimal-agent-workflow/learnings.md', content='')"
|
|
33
|
+
onError: continue
|
|
34
|
+
|
|
35
|
+
# ============================================================
|
|
36
|
+
# 主迭代循环(while 模式 + fresh_context)
|
|
37
|
+
# 每轮:构造 prompt → agent 工作 → 哨兵检测 → 阶段推进
|
|
38
|
+
# ============================================================
|
|
39
|
+
- type: loop
|
|
40
|
+
id: main_loop
|
|
41
|
+
max_iterations: "${max_iterations}"
|
|
42
|
+
fresh_context: true
|
|
43
|
+
do:
|
|
44
|
+
# --- 1. 读取上轮教训 ---
|
|
45
|
+
- id: read_learnings
|
|
46
|
+
tool: "Read(file_path='.minimal-agent-workflow/learnings.md')"
|
|
47
|
+
capture:
|
|
48
|
+
text: learnings_text
|
|
49
|
+
onError: continue
|
|
50
|
+
|
|
51
|
+
# --- 2. 构造本轮 prompt(含目标、阶段、通信协议、教训) ---
|
|
52
|
+
- id: build_prompt
|
|
53
|
+
llm: |
|
|
54
|
+
# 不可变目标 (IMMUTABLE GOAL)
|
|
55
|
+
${goal}
|
|
56
|
+
⚠️ 你不能修改或扩大上述目标。
|
|
57
|
+
|
|
58
|
+
# 当前阶段: ${phase.toUpperCase()}
|
|
59
|
+
${phase == 'PLAN' ? '分析任务、拆解步骤、识别风险与未知项;先想清楚再动手。' :
|
|
60
|
+
phase == 'BUILD' ? '按计划实现,一次只聚焦一个子任务,落到具体文件改动。' :
|
|
61
|
+
phase == 'VERIFY' ? '运行测试/检查输出/核对验收标准,用证据确认,别凭感觉。' :
|
|
62
|
+
phase == 'HEAL' ? '已多次验证失败:停止猜测,定位根因再修,避免回归。' :
|
|
63
|
+
'目标已达成。'}
|
|
64
|
+
|
|
65
|
+
# 与循环驱动的通信协议(务必遵守)
|
|
66
|
+
- 任务**真正完成**时输出 `<promise>DONE</promise>`(驱动会运行验证门)
|
|
67
|
+
- 当前方案走不通、要换打法时输出 `<PROMISE>NEED_REPLAN</PROMISE>`(回 PLAN)
|
|
68
|
+
- **目标本身**有问题时输出 `<PROMISE>NEED_GOAL_REVISION</PROMISE>`(干净终止)
|
|
69
|
+
- 踩到的坑用 `<learning>一句话</learning>` 标记(驱动自动存档供后续参考)
|
|
70
|
+
⚠️ 切勿为逃出循环而谎报完成——只有陈述完全为真时才输出完成哨兵。
|
|
71
|
+
|
|
72
|
+
${learnings_text && learnings_text != '' ? '# 关键教训(必须遵守,避免重复踩坑)\n' + learnings_text : ''}
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
本轮任务 (迭代 ${_iter})
|
|
76
|
+
capture:
|
|
77
|
+
result: prompt_text
|
|
78
|
+
|
|
79
|
+
# --- 3. 执行 agent 工作(TAOR 引擎,有工具能力) ---
|
|
80
|
+
- id: work
|
|
81
|
+
agent: "${prompt_text}"
|
|
82
|
+
maxTurns: 15
|
|
83
|
+
capture:
|
|
84
|
+
text: output
|
|
85
|
+
|
|
86
|
+
# --- 4. 提取 learning 并持久化 ---
|
|
87
|
+
- id: extract_learning
|
|
88
|
+
llm: |
|
|
89
|
+
从以下文本中提取所有 <learning>...</learning> 内容,每行一条原样输出。
|
|
90
|
+
如果没有 learning 标签,输出空字符串。
|
|
91
|
+
|
|
92
|
+
文本:
|
|
93
|
+
${output}
|
|
94
|
+
capture:
|
|
95
|
+
text: extracted_learning
|
|
96
|
+
|
|
97
|
+
- id: save_learning
|
|
98
|
+
tool: "Bash(command='cat >> .minimal-agent-workflow/learnings.md << EOF\n${extracted_learning}\nEOF')"
|
|
99
|
+
when: "extracted_learning != ''"
|
|
100
|
+
onError: continue
|
|
101
|
+
|
|
102
|
+
# --- 5. 哵兵检测与分支处理 ---
|
|
103
|
+
|
|
104
|
+
# 5a. 是否包含任何哨兵标签?
|
|
105
|
+
- id: has_sentinel
|
|
106
|
+
type: branch
|
|
107
|
+
condition: "includes(output, '<promise>') || includes(output, '<PROMISE>') || includes(output, '<learning>')"
|
|
108
|
+
then:
|
|
109
|
+
# 有哨兵标签 → 判断具体类型
|
|
110
|
+
|
|
111
|
+
# 5a-i. 完成哨兵?
|
|
112
|
+
- id: check_complete
|
|
113
|
+
type: branch
|
|
114
|
+
condition: |
|
|
115
|
+
includes(lower(output), '<promise>') &&
|
|
116
|
+
(includes(lower(output), 'done') ||
|
|
117
|
+
includes(lower(output), 'complete') ||
|
|
118
|
+
includes(lower(output), 'goal_complete'))
|
|
119
|
+
then:
|
|
120
|
+
# === DONE 哨兵 → 验证门 ===
|
|
121
|
+
- id: run_verify
|
|
122
|
+
type: verify
|
|
123
|
+
checks: "${verify_checks}"
|
|
124
|
+
when: "verify_checks != ''"
|
|
125
|
+
onFail: "验证未通过,回到 BUILD 继续"
|
|
126
|
+
onError: continue
|
|
127
|
+
# 验证通过或无验证门 → 设置完成阶段并跳出循环
|
|
128
|
+
- id: set_done
|
|
129
|
+
llm: "DONE"
|
|
130
|
+
capture:
|
|
131
|
+
result: phase
|
|
132
|
+
- id: exit_success
|
|
133
|
+
type: break
|
|
134
|
+
else:
|
|
135
|
+
# 非 DONE 哨兵 → NEED_REPLAN 或 NEED_GOAL_REVISION
|
|
136
|
+
- id: check_replan
|
|
137
|
+
type: branch
|
|
138
|
+
condition: "includes(output, 'NEED_REPLAN')"
|
|
139
|
+
then:
|
|
140
|
+
# 回到 PLAN
|
|
141
|
+
- id: replan
|
|
142
|
+
llm: "PLAN"
|
|
143
|
+
capture:
|
|
144
|
+
result: phase
|
|
145
|
+
else:
|
|
146
|
+
# NEED_GOAL_REVISION → 终止循环
|
|
147
|
+
- id: goal_revision_exit
|
|
148
|
+
llm: "DONE"
|
|
149
|
+
capture:
|
|
150
|
+
result: phase
|
|
151
|
+
- id: exit_goal_revision
|
|
152
|
+
type: break
|
|
153
|
+
|
|
154
|
+
# 5b. 无哨兵 → 自动阶段推进
|
|
155
|
+
- id: auto_advance
|
|
156
|
+
type: branch
|
|
157
|
+
condition: "phase == 'PLAN' && _iter >= 1"
|
|
158
|
+
then:
|
|
159
|
+
- id: to_build
|
|
160
|
+
llm: "BUILD"
|
|
161
|
+
capture:
|
|
162
|
+
result: phase
|
|
163
|
+
|
|
164
|
+
# === 循环结束后的收尾 ===
|
|
165
|
+
- id: final_summary
|
|
166
|
+
llm: |
|
|
167
|
+
总结本轮 ralph-loop 的执行结果。如果目标是完成的,列出关键产出物。
|
|
168
|
+
如果是因达到迭代上限而退出,说明当前进度和下一步建议。
|
|
169
|
+
maxTurns: 1
|
|
@@ -69,8 +69,10 @@ steps:
|
|
|
69
69
|
content: parse_log
|
|
70
70
|
|
|
71
71
|
- id: assert_script_parsed
|
|
72
|
-
type:
|
|
73
|
-
|
|
72
|
+
type: verify
|
|
73
|
+
checks:
|
|
74
|
+
- 'file_exists:./assets/prompts.txt'
|
|
75
|
+
- 'file_exists:./assets/narrations.txt'
|
|
74
76
|
onFail: "脚本解析失败:./assets/prompts.txt 或 ./assets/narrations.txt 不存在"
|
|
75
77
|
|
|
76
78
|
# ---------- 4. 生成 5 张配图(loop 静态数组 + 每轮先读对应一行 prompt) ----------
|
|
@@ -149,10 +151,11 @@ steps:
|
|
|
149
151
|
skill: video-compose
|
|
150
152
|
input: 'images_glob=./assets/img_*.png audio=./assets/audio.mp3 srt=./assets/subs.srt output=./out/shorts.mp4 width=${inputs.width} height=${inputs.height}'
|
|
151
153
|
|
|
152
|
-
# ---------- 11. 守门 assert ----------
|
|
154
|
+
# ---------- 11. 守门 verify(升级自 assert) ----------
|
|
153
155
|
- id: verify_output
|
|
154
|
-
type:
|
|
155
|
-
|
|
156
|
+
type: verify
|
|
157
|
+
checks:
|
|
158
|
+
- 'file_exists:./out/shorts.mp4'
|
|
156
159
|
onFail: "最终视频 ./out/shorts.mp4 未生成,请检查 video-compose 日志"
|
|
157
160
|
|
|
158
161
|
__meta:
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
# How to Write a minimal-agent Plugin
|
|
2
|
-
|
|
3
|
-
minimal-agent 的插件系统遵循 **drop-in** 原则:把目录扔进 `plugins/`,命令立即可用,**`src/` 一行不改**。
|
|
4
|
-
|
|
5
|
-
## 两种契约
|
|
6
|
-
|
|
7
|
-
### 1. 纯声明式(Anthropic 兼容,零 TS)
|
|
8
|
-
|
|
9
|
-
最小可用插件 = 一个 frontmatter 模板。框架替你拼 prompt 喂给 LLM。
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
plugins/hello-bot/
|
|
13
|
-
├── .claude-plugin/
|
|
14
|
-
│ └── plugin.json
|
|
15
|
-
└── commands/
|
|
16
|
-
└── hi.md
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**`.claude-plugin/plugin.json`**
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"name": "hello-bot",
|
|
24
|
-
"version": "0.1.0",
|
|
25
|
-
"description": "Greets the user"
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
**`commands/hi.md`**
|
|
30
|
-
|
|
31
|
-
```markdown
|
|
32
|
-
---
|
|
33
|
-
description: "Greet the user"
|
|
34
|
-
argument-hint: "<name>"
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
你好 $ARGUMENTS!欢迎使用 minimal-agent。
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**效果**:用户输入 `/hi 世界` → LLM 收到 `你好 世界!欢迎使用 minimal-agent。\n\n用户参数: 世界`。
|
|
41
|
-
|
|
42
|
-
支持的占位符:
|
|
43
|
-
- `$ARGUMENTS` / `${ARGUMENTS}` —— 用户在命令名后的全部文本
|
|
44
|
-
- `${CLAUDE_PLUGIN_ROOT}` —— 该插件目录的绝对路径(用于引用插件资源)
|
|
45
|
-
|
|
46
|
-
### 2. 声明式 + `plugin.ts`(富插件)
|
|
47
|
-
|
|
48
|
-
当模板不够(要跑循环 / 做 TS 校验 / 启动子进程 / 状态机),加一个 `plugin.ts`:
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
plugins/echo-bot/
|
|
52
|
-
├── .claude-plugin/plugin.json
|
|
53
|
-
├── commands/echo.md # 仍要存在!frontmatter 用于 listAvailableCommands
|
|
54
|
-
└── plugin.ts # PluginApi default export
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**`plugin.ts`**
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
import {
|
|
61
|
-
runQuery,
|
|
62
|
-
type PluginApi,
|
|
63
|
-
type LoopEvent,
|
|
64
|
-
} from '../../src/plugin-sdk.ts';
|
|
65
|
-
|
|
66
|
-
const api: PluginApi = {
|
|
67
|
-
async *runCommand(commandName, args, ctx): AsyncGenerator<LoopEvent> {
|
|
68
|
-
if (commandName === 'echo') {
|
|
69
|
-
// 直接 yield 自定义事件
|
|
70
|
-
yield { type: 'text', delta: `你输入了:${args}` };
|
|
71
|
-
yield { type: 'turn_done' };
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 或者改写 prompt 后丢给 LLM
|
|
76
|
-
yield* runQuery(`请把以下内容重复 3 遍:${args}`, {
|
|
77
|
-
provider: ctx.provider,
|
|
78
|
-
history: ctx.history,
|
|
79
|
-
signal: ctx.signal,
|
|
80
|
-
});
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export default api;
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
**框架行为**:命中 `/echo abc` 时,`pluginRunner` 优先调用 `runCommand('echo', 'abc', ctx)`,不再走声明式 fallback。你完全接管事件流。
|
|
88
|
-
|
|
89
|
-
## `plugin-sdk.ts` 暴露的全部 API
|
|
90
|
-
|
|
91
|
-
**唯一稳定 import 路径**:`from '../../src/plugin-sdk.ts'`(相对你的 `plugin.ts`)。
|
|
92
|
-
|
|
93
|
-
| 名称 | 类型 | 用途 |
|
|
94
|
-
|------|------|------|
|
|
95
|
-
| `runQuery(input, opts)` | function | 标准 T-A-O-R 循环,AsyncGenerator |
|
|
96
|
-
| `chat(messages, provider, opts)` | function | 单次非流式 LLM 调用 |
|
|
97
|
-
| `executeTool(name, argsJson, ctx)` | function | 框架级工具调用入口 |
|
|
98
|
-
| `getToolByName(name)` | function | 取 Tool 定义 |
|
|
99
|
-
| `ALL_TOOLS` | const | 所有内置工具数组 |
|
|
100
|
-
| `getWorkingDir()` | function | 当前锁定的工作目录 |
|
|
101
|
-
| `getResourceSearchPaths(name, importMetaUrl)` | function | 双源资源路径(cwd + packageRoot) |
|
|
102
|
-
| `triggerHook(event, payload)` | function | 触发框架级 hook |
|
|
103
|
-
| `createSessionState()` | function | 新建 SessionState |
|
|
104
|
-
| `Message` / `Provider` / `LoopEvent` / `LlmStreamEvent` | type | 协议类型 |
|
|
105
|
-
| `Tool` / `ToolCall` / `ToolResult` | type | 工具协议类型 |
|
|
106
|
-
| `PluginApi` / `PluginContext` | type | 你的 plugin.ts 契约 |
|
|
107
|
-
| `ResourceName` / `HookEventName` / `HookDecision` / `SessionState` | type | 杂项 |
|
|
108
|
-
|
|
109
|
-
**不要**绕开 `plugin-sdk.ts` 直接 import `src/loop.ts` / `src/llm/client.ts` 等内部路径 —— 那些路径不是稳定 API,框架重构时会破坏你的插件。
|
|
110
|
-
|
|
111
|
-
## `PluginContext`
|
|
112
|
-
|
|
113
|
-
`runCommand` 收到的 ctx:
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
interface PluginContext {
|
|
117
|
-
provider: Provider; // 当前 LLM provider 配置
|
|
118
|
-
history: Message[]; // 对话历史(你可以读 / 修改 / 替换)
|
|
119
|
-
signal?: AbortSignal; // 用户 ESC / Ctrl+C 时触发
|
|
120
|
-
sessionState?: SessionState; // 可选共享 session 状态
|
|
121
|
-
maxTurns?: number; // 工具循环最大轮数(runQuery 透传用)
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## 事件 (`LoopEvent`)
|
|
126
|
-
|
|
127
|
-
你的 `runCommand` 必须 yield `LoopEvent`,UI 据此渲染。常用的有:
|
|
128
|
-
|
|
129
|
-
| type | 字段 | 用途 |
|
|
130
|
-
|------|------|------|
|
|
131
|
-
| `text` | `delta: string` | 流式文本片段 |
|
|
132
|
-
| `assistant_message` | (无) | 一次完整 assistant 消息已 push 进 history |
|
|
133
|
-
| `tool_start` | `toolName`, `argsPreview` | 工具开始执行 |
|
|
134
|
-
| `tool_end` | (无) | 工具结束 |
|
|
135
|
-
| `turn_done` | (无) | 整轮交互结束 |
|
|
136
|
-
| `interrupted` | (无) | 用户中断 |
|
|
137
|
-
| `error` | `error: string` | 红色错误提示 |
|
|
138
|
-
| `plugin_progress` | `pluginId`, `current`, `max?`, `message?` | 循环进度条(替代旧的 plugin_start / plugin_iteration) |
|
|
139
|
-
|
|
140
|
-
通常做法:复杂插件直接 `yield* runQuery(...)` 透传所有事件,自己额外 yield `plugin_progress` 做进度展示。
|
|
141
|
-
|
|
142
|
-
## 工作目录与持久化
|
|
143
|
-
|
|
144
|
-
- 状态写到 `getWorkingDir()/.minimal-agent-<your-plugin-id>/` 下,避免污染主 context 目录
|
|
145
|
-
- `/new` 命令会自动清这些 `.minimal-agent*` 目录,你不用管 cleanup
|
|
146
|
-
- 跨平台:Windows 没 bash,hooks/*.sh 静默跳过;其它 .ts 代码完全平台无关
|
|
147
|
-
|
|
148
|
-
## 命令默认值
|
|
149
|
-
|
|
150
|
-
框架**不再**注入命令默认参数(旧版 `COMMAND_DEFAULTS` 已删除)。如果你的命令需要默认值(如 `--max-iterations 50`),自己在 `plugin.ts` 里解析 args 时兜底。这样默认值的语义属于插件契约,不属于框架。
|
|
151
|
-
|
|
152
|
-
## 测试
|
|
153
|
-
|
|
154
|
-
- 把测试放在 `plugins/<id>/test/*.test.ts`
|
|
155
|
-
- `bunfig.toml` 的 `[test].root = ["./test", "./plugins"]` 让 `bun test` 自动发现
|
|
156
|
-
- 测试里 mock LLM / 工具时,用 `mock.module('../../../src/loop.ts', ...)` 等绝对到 src 的相对路径
|
|
157
|
-
|
|
158
|
-
## 参考实现
|
|
159
|
-
|
|
160
|
-
- **声明式 + plugin.ts 简单例**:`plugins/workflow-runner/plugin.ts`(~25 行接管 `/workflow` 与 `/workflows`)
|
|
161
|
-
- **重型循环例**:`plugins/ralph-wiggum/plugin.ts`(~270 行 do-while loop + FSM + sentinel + verification + stop-hook)
|
|
162
|
-
|
|
163
|
-
## Transcript 与 Hook:当前状态
|
|
164
|
-
|
|
165
|
-
框架提供了两套基础设施 —— 都**就绪、有测试、可独立调用**,但**主循环 `src/loop.ts` 不自动触发**:
|
|
166
|
-
|
|
167
|
-
| 模块 | 暴露 | 当前主循环触发? |
|
|
168
|
-
|------|------|------|
|
|
169
|
-
| `src/plugins/transcript.ts` | `initTranscript / appendUserMessage / appendAssistantMessage / appendToolResult` | 否 |
|
|
170
|
-
| `src/plugins/hookEngine.ts` | `loadHooks / triggerHook('UserPromptSubmit' \| 'PreToolUse' \| 'PostToolUse' \| 'SessionStart' \| 'SessionEnd', payload)` | 否 |
|
|
171
|
-
|
|
172
|
-
**插件如何使用**:
|
|
173
|
-
- 想写自己的 JSONL transcript → 在 `runCommand` 内 `import { initTranscript, appendUserMessage } from '../../src/plugin-sdk.ts'` 自己调
|
|
174
|
-
- 想响应 hook 事件 → 在 `runCommand` 内 `triggerHook('PreToolUse', { tool_name, ... })` 自己调;ralph-wiggum 走的就是这条路(vendored stop-hook 执行器在 `plugins/ralph-wiggum/src/stopHookRunner.ts`)
|
|
175
|
-
|
|
176
|
-
**为什么不在主循环自动触发**:当前没有 Anthropic 风格 `hooks/*.sh` 的真实用例驱动;自动触发会改主循环热路径但不解决实际问题。等真有跨插件的全局 hook 需求时再接也不迟。届时只需在 `src/loop.ts` 的 user/assistant/tool 三处 push 点加 `appendXxx` 与 `triggerHook` 调用即可,**不影响插件契约**。
|
|
177
|
-
|
|
178
|
-
## 不支持的 Anthropic 字段
|
|
179
|
-
|
|
180
|
-
minimal-agent 的插件契约是 Anthropic 上游的**子集**。当前未实现的字段:
|
|
181
|
-
|
|
182
|
-
- `mcp-servers/` —— MCP 服务器配置
|
|
183
|
-
- `agents/` —— 子 agent 定义
|
|
184
|
-
- `skills/` 在插件目录下 —— minimal-agent 的 skill 系统是独立顶层目录
|
|
185
|
-
|
|
186
|
-
未实现字段在 plugin.json 中存在不会报错,但被忽略。
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ralph-wiggum",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Implementation of the Ralph Wiggum technique - continuous self-referential AI loops for interactive iterative development. Run Claude in a while-true loop with the same prompt until task completion.",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "Daisy Hollman",
|
|
7
|
-
"email": "daisy@anthropic.com"
|
|
8
|
-
}
|
|
9
|
-
}
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
# Ralph Wiggum Plugin
|
|
2
|
-
|
|
3
|
-
Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in Minimal Agent.
|
|
4
|
-
|
|
5
|
-
## What is Ralph?
|
|
6
|
-
|
|
7
|
-
Ralph is a development methodology based on continuous AI agent loops. As Geoffrey Huntley describes it: **"Ralph is a Bash loop"** - a simple `while true` that repeatedly feeds an AI agent a prompt file, allowing it to iteratively improve its work until completion.
|
|
8
|
-
|
|
9
|
-
The technique is named after Ralph Wiggum from The Simpsons, embodying the philosophy of persistent iteration despite setbacks.
|
|
10
|
-
|
|
11
|
-
### Core Concept
|
|
12
|
-
|
|
13
|
-
This plugin implements Ralph using a **Stop hook** that intercepts Minimal Agent's exit attempts:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# You run ONCE:
|
|
17
|
-
/ralph-loop "Your task description" --completion-promise "DONE"
|
|
18
|
-
|
|
19
|
-
# Then Claude Code automatically:
|
|
20
|
-
# 1. Works on the task
|
|
21
|
-
# 2. Tries to exit
|
|
22
|
-
# 3. Stop hook blocks exit
|
|
23
|
-
# 4. Stop hook feeds the SAME prompt back
|
|
24
|
-
# 5. Repeat until completion
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
The loop happens **inside your current session** - you don't need external bash loops. The Stop hook in `hooks/stop-hook.sh` creates the self-referential feedback loop by blocking normal session exit.
|
|
28
|
-
|
|
29
|
-
This creates a **self-referential feedback loop** where:
|
|
30
|
-
- The prompt never changes between iterations
|
|
31
|
-
- Claude's previous work persists in files
|
|
32
|
-
- Each iteration sees modified files and git history
|
|
33
|
-
- Claude autonomously improves by reading its own past work in files
|
|
34
|
-
|
|
35
|
-
## Quick Start
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
/ralph-loop "Build a REST API for todos. Requirements: CRUD operations, input validation, tests. Output <promise>COMPLETE</promise> when done." --completion-promise "COMPLETE" --max-iterations 50
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Claude will:
|
|
42
|
-
- Implement the API iteratively
|
|
43
|
-
- Run tests and see failures
|
|
44
|
-
- Fix bugs based on test output
|
|
45
|
-
- Iterate until all requirements met
|
|
46
|
-
- Output the completion promise when done
|
|
47
|
-
|
|
48
|
-
## Commands
|
|
49
|
-
|
|
50
|
-
### /ralph-loop
|
|
51
|
-
|
|
52
|
-
Start a Ralph loop in your current session.
|
|
53
|
-
|
|
54
|
-
**Usage:**
|
|
55
|
-
```bash
|
|
56
|
-
/ralph-loop "<prompt>" --max-iterations <n> --completion-promise "<text>"
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
**Options:**
|
|
60
|
-
- `--max-iterations <n>` - Stop after N iterations (default: 50, hard cap: 200)
|
|
61
|
-
- `--completion-promise <text>` - Phrase that signals completion (natively active; default: DONE / COMPLETE / GOAL_COMPLETE)
|
|
62
|
-
- `--verify "<CHECK>"` - Completion gate run after the promise tag. All checks must pass before the loop exits. Repeatable.
|
|
63
|
-
- `shell:<cmd>` — exit code 0 = pass (Windows: `cmd /c`, others: `bash -c`)
|
|
64
|
-
- `file_exists:<path>` — file must exist
|
|
65
|
-
- `file_contains:<file>:<regex>` — file content matches regex
|
|
66
|
-
- `test_count:<min>` — `bun test` pass count ≥ min
|
|
67
|
-
|
|
68
|
-
Example:
|
|
69
|
-
```bash
|
|
70
|
-
/ralph-loop "Implement and test cache layer" \
|
|
71
|
-
--verify "shell:bun test" \
|
|
72
|
-
--verify "file_exists:dist/index.js"
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### /cancel-ralph
|
|
76
|
-
|
|
77
|
-
Cancel the active Ralph loop.
|
|
78
|
-
|
|
79
|
-
**Usage:**
|
|
80
|
-
```bash
|
|
81
|
-
/cancel-ralph
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Prompt Writing Best Practices
|
|
85
|
-
|
|
86
|
-
### 1. Clear Completion Criteria
|
|
87
|
-
|
|
88
|
-
❌ Bad: "Build a todo API and make it good."
|
|
89
|
-
|
|
90
|
-
✅ Good:
|
|
91
|
-
```markdown
|
|
92
|
-
Build a REST API for todos.
|
|
93
|
-
|
|
94
|
-
When complete:
|
|
95
|
-
- All CRUD endpoints working
|
|
96
|
-
- Input validation in place
|
|
97
|
-
- Tests passing (coverage > 80%)
|
|
98
|
-
- README with API docs
|
|
99
|
-
- Output: <promise>COMPLETE</promise>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### 2. Incremental Goals
|
|
103
|
-
|
|
104
|
-
❌ Bad: "Create a complete e-commerce platform."
|
|
105
|
-
|
|
106
|
-
✅ Good:
|
|
107
|
-
```markdown
|
|
108
|
-
Phase 1: User authentication (JWT, tests)
|
|
109
|
-
Phase 2: Product catalog (list/search, tests)
|
|
110
|
-
Phase 3: Shopping cart (add/remove, tests)
|
|
111
|
-
|
|
112
|
-
Output <promise>COMPLETE</promise> when all phases done.
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### 3. Self-Correction
|
|
116
|
-
|
|
117
|
-
❌ Bad: "Write code for feature X."
|
|
118
|
-
|
|
119
|
-
✅ Good:
|
|
120
|
-
```markdown
|
|
121
|
-
Implement feature X following TDD:
|
|
122
|
-
1. Write failing tests
|
|
123
|
-
2. Implement feature
|
|
124
|
-
3. Run tests
|
|
125
|
-
4. If any fail, debug and fix
|
|
126
|
-
5. Refactor if needed
|
|
127
|
-
6. Repeat until all green
|
|
128
|
-
7. Output: <promise>COMPLETE</promise>
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Tag lessons inline with `<learning>…</learning>` — the driver archives them to `learnings.md` automatically, so subsequent iterations inherit the hard-won knowledge without repeating mistakes.
|
|
132
|
-
|
|
133
|
-
### 4. Escape Hatches
|
|
134
|
-
|
|
135
|
-
Always use `--max-iterations` as a safety net to prevent infinite loops on impossible tasks:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
# Recommended: Always set a reasonable iteration limit
|
|
139
|
-
/ralph-loop "Try to implement feature X" --max-iterations 20
|
|
140
|
-
|
|
141
|
-
# In your prompt, include what to do if stuck:
|
|
142
|
-
# "After 15 iterations, if not complete:
|
|
143
|
-
# - Document what's blocking progress
|
|
144
|
-
# - List what was attempted
|
|
145
|
-
# - Suggest alternative approaches"
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Claude can also signal early exit using two distinct tags:
|
|
149
|
-
- `<PROMISE>NEED_REPLAN</PROMISE>` — current approach is unworkable; loop returns to PLAN with the same goal
|
|
150
|
-
- `<PROMISE>NEED_GOAL_REVISION</PROMISE>` — the goal itself is unreachable; loop stops cleanly for human review. Use this when no approach can work, not just the current one.
|
|
151
|
-
|
|
152
|
-
**Note**: The `--completion-promise` uses exact string matching and is natively active (no external stop-hook required). It signals Claude's intent to complete; use `--verify` checks for objective verification of that intent.
|
|
153
|
-
|
|
154
|
-
## Philosophy
|
|
155
|
-
|
|
156
|
-
Ralph embodies several key principles:
|
|
157
|
-
|
|
158
|
-
### 1. Iteration > Perfection
|
|
159
|
-
Don't aim for perfect on first try. Let the loop refine the work.
|
|
160
|
-
|
|
161
|
-
### 2. Failures Are Data
|
|
162
|
-
"Deterministically bad" means failures are predictable and informative. Use them to tune prompts.
|
|
163
|
-
|
|
164
|
-
### 3. Operator Skill Matters
|
|
165
|
-
Success depends on writing good prompts, not just having a good model.
|
|
166
|
-
|
|
167
|
-
### 4. Persistence Wins
|
|
168
|
-
Keep trying until success. The loop handles retry logic automatically.
|
|
169
|
-
|
|
170
|
-
## When to Use Ralph
|
|
171
|
-
|
|
172
|
-
**Good for:**
|
|
173
|
-
- Well-defined tasks with clear success criteria
|
|
174
|
-
- Tasks requiring iteration and refinement (e.g., getting tests to pass)
|
|
175
|
-
- Greenfield projects where you can walk away
|
|
176
|
-
- Tasks with automatic verification (tests, linters)
|
|
177
|
-
|
|
178
|
-
**Not good for:**
|
|
179
|
-
- Tasks requiring human judgment or design decisions
|
|
180
|
-
- One-shot operations
|
|
181
|
-
- Tasks with unclear success criteria
|
|
182
|
-
- Production debugging (use targeted debugging instead)
|
|
183
|
-
|
|
184
|
-
## Real-World Results
|
|
185
|
-
|
|
186
|
-
- Successfully generated 6 repositories overnight in Y Combinator hackathon testing
|
|
187
|
-
- One $50k contract completed for $297 in API costs
|
|
188
|
-
- Created entire programming language ("cursed") over 3 months using this approach
|
|
189
|
-
|
|
190
|
-
## Learn More
|
|
191
|
-
|
|
192
|
-
- Original technique: https://ghuntley.com/ralph/
|
|
193
|
-
- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator
|
|
194
|
-
|
|
195
|
-
## For Help
|
|
196
|
-
|
|
197
|
-
Run `/help` in Claude Code for detailed command reference and examples.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Cancel active Ralph Wiggum loop"
|
|
3
|
-
allowed-tools: ["Bash(test -f .minimal-agent/ralph-loop.local.md:*)", "Bash(rm .minimal-agent/ralph-loop.local.md)", "Read(.minimal-agent/ralph-loop.local.md)"]
|
|
4
|
-
hide-from-slash-command-tool: "true"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Cancel Ralph
|
|
8
|
-
|
|
9
|
-
To cancel the Ralph loop:
|
|
10
|
-
|
|
11
|
-
1. Check if `.minimal-agent/ralph-loop.local.md` exists using Bash: `test -f .minimal-agent/ralph-loop.local.md && echo "EXISTS" || echo "NOT_FOUND"`
|
|
12
|
-
|
|
13
|
-
2. **If NOT_FOUND**: Say "No active Ralph loop found."
|
|
14
|
-
|
|
15
|
-
3. **If EXISTS**:
|
|
16
|
-
- Read `.minimal-agent/ralph-loop.local.md` to get the current iteration number from the `iteration:` field
|
|
17
|
-
- Remove the file using Bash: `rm .minimal-agent/ralph-loop.local.md`
|
|
18
|
-
- Report: "Cancelled Ralph loop (was at iteration N)" where N is the iteration value
|