mirror of
https://github.com/rot13maxi/opencode-ralph.git
synced 2026-05-27 14:27:40 +00:00
Initial commit: Ralph Wiggum plugin for OpenCode
This commit is contained in:
commit
d8f58ffc3c
6 changed files with 677 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Ralph loop state file (should not be committed)
|
||||
ralph-loop.local.md
|
||||
225
README.md
Normal file
225
README.md
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
# Ralph Wiggum Plugin for OpenCode
|
||||
|
||||
Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in OpenCode.
|
||||
|
||||
Ported from the [Claude Code Ralph Plugin](https://github.com/anthropics/claude-code/tree/main/plugins/ralph-wiggum).
|
||||
|
||||
## What is Ralph?
|
||||
|
||||
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.
|
||||
|
||||
The technique is named after Ralph Wiggum from The Simpsons, embodying the philosophy of persistent iteration despite setbacks.
|
||||
|
||||
### Core Concept
|
||||
|
||||
This plugin implements Ralph using OpenCode's event system to intercept session idle states:
|
||||
|
||||
```bash
|
||||
# You run ONCE:
|
||||
/ralph-loop "Your task description" --completion-promise "DONE"
|
||||
|
||||
# Then OpenCode automatically:
|
||||
# 1. Works on the task
|
||||
# 2. Finishes responding
|
||||
# 3. Plugin intercepts idle state
|
||||
# 4. Plugin feeds the SAME prompt back
|
||||
# 5. Repeat until completion
|
||||
```
|
||||
|
||||
The loop happens **inside your current session** - you don't need external bash loops. The plugin creates the self-referential feedback loop by intercepting the session idle event.
|
||||
|
||||
This creates a **self-referential feedback loop** where:
|
||||
- The prompt never changes between iterations
|
||||
- The AI's previous work persists in files
|
||||
- Each iteration sees modified files and git history
|
||||
- The AI autonomously improves by reading its own past work in files
|
||||
|
||||
## Installation
|
||||
|
||||
Clone or copy this repo, then symlink or copy to your global OpenCode config:
|
||||
|
||||
```bash
|
||||
# Clone the repo
|
||||
git clone https://github.com/anthropics/opencode-ralph.git
|
||||
cd opencode-ralph
|
||||
|
||||
# Symlink to global config (recommended - updates automatically)
|
||||
ln -s "$(pwd)/plugin/ralph.ts" ~/.config/opencode/plugin/ralph.ts
|
||||
ln -s "$(pwd)/command/ralph-loop.md" ~/.config/opencode/command/ralph-loop.md
|
||||
ln -s "$(pwd)/command/cancel-ralph.md" ~/.config/opencode/command/cancel-ralph.md
|
||||
ln -s "$(pwd)/command/ralph-help.md" ~/.config/opencode/command/ralph-help.md
|
||||
|
||||
# Or copy files directly
|
||||
cp plugin/* ~/.config/opencode/plugin/
|
||||
cp command/* ~/.config/opencode/command/
|
||||
```
|
||||
|
||||
For project-level installation, copy to `.opencode/`:
|
||||
|
||||
```bash
|
||||
cp plugin/* /path/to/your/project/.opencode/plugin/
|
||||
cp command/* /path/to/your/project/.opencode/command/
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
/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
|
||||
```
|
||||
|
||||
The AI will:
|
||||
- Implement the API iteratively
|
||||
- Run tests and see failures
|
||||
- Fix bugs based on test output
|
||||
- Iterate until all requirements met
|
||||
- Output the completion promise when done
|
||||
|
||||
## Commands
|
||||
|
||||
### /ralph-loop
|
||||
|
||||
Start a Ralph loop in your current session.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
/ralph-loop "<prompt>" --max-iterations <n> --completion-promise "<text>"
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--max-iterations <n>` - Stop after N iterations (default: unlimited)
|
||||
- `--completion-promise <text>` - Phrase that signals completion
|
||||
|
||||
### /cancel-ralph
|
||||
|
||||
Cancel the active Ralph loop.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
/cancel-ralph
|
||||
```
|
||||
|
||||
### /ralph-help
|
||||
|
||||
Get detailed help about the Ralph technique and commands.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
/ralph-help
|
||||
```
|
||||
|
||||
## Prompt Writing Best Practices
|
||||
|
||||
### 1. Clear Completion Criteria
|
||||
|
||||
Bad: "Build a todo API and make it good."
|
||||
|
||||
Good:
|
||||
```markdown
|
||||
Build a REST API for todos.
|
||||
|
||||
When complete:
|
||||
- All CRUD endpoints working
|
||||
- Input validation in place
|
||||
- Tests passing (coverage > 80%)
|
||||
- README with API docs
|
||||
- Output: <promise>COMPLETE</promise>
|
||||
```
|
||||
|
||||
### 2. Incremental Goals
|
||||
|
||||
Bad: "Create a complete e-commerce platform."
|
||||
|
||||
Good:
|
||||
```markdown
|
||||
Phase 1: User authentication (JWT, tests)
|
||||
Phase 2: Product catalog (list/search, tests)
|
||||
Phase 3: Shopping cart (add/remove, tests)
|
||||
|
||||
Output <promise>COMPLETE</promise> when all phases done.
|
||||
```
|
||||
|
||||
### 3. Self-Correction
|
||||
|
||||
Bad: "Write code for feature X."
|
||||
|
||||
Good:
|
||||
```markdown
|
||||
Implement feature X following TDD:
|
||||
1. Write failing tests
|
||||
2. Implement feature
|
||||
3. Run tests
|
||||
4. If any fail, debug and fix
|
||||
5. Refactor if needed
|
||||
6. Repeat until all green
|
||||
7. Output: <promise>COMPLETE</promise>
|
||||
```
|
||||
|
||||
### 4. Escape Hatches
|
||||
|
||||
Always use `--max-iterations` as a safety net to prevent infinite loops:
|
||||
|
||||
```bash
|
||||
# Recommended: Always set a reasonable iteration limit
|
||||
/ralph-loop "Try to implement feature X" --max-iterations 20
|
||||
```
|
||||
|
||||
## Philosophy
|
||||
|
||||
Ralph embodies several key principles:
|
||||
|
||||
### 1. Iteration > Perfection
|
||||
Don't aim for perfect on first try. Let the loop refine the work.
|
||||
|
||||
### 2. Failures Are Data
|
||||
"Deterministically bad" means failures are predictable and informative. Use them to tune prompts.
|
||||
|
||||
### 3. Operator Skill Matters
|
||||
Success depends on writing good prompts, not just having a good model.
|
||||
|
||||
### 4. Persistence Wins
|
||||
Keep trying until success. The loop handles retry logic automatically.
|
||||
|
||||
## When to Use Ralph
|
||||
|
||||
**Good for:**
|
||||
- Well-defined tasks with clear success criteria
|
||||
- Tasks requiring iteration and refinement (e.g., getting tests to pass)
|
||||
- Greenfield projects where you can walk away
|
||||
- Tasks with automatic verification (tests, linters)
|
||||
|
||||
**Not good for:**
|
||||
- Tasks requiring human judgment or design decisions
|
||||
- One-shot operations
|
||||
- Tasks with unclear success criteria
|
||||
- Production debugging (use targeted debugging instead)
|
||||
|
||||
## Files
|
||||
|
||||
- `plugin/ralph.ts` - Main plugin that handles the loop logic
|
||||
- `command/ralph-loop.md` - Command to start a Ralph loop
|
||||
- `command/cancel-ralph.md` - Command to cancel the loop
|
||||
- `command/ralph-help.md` - Help documentation
|
||||
|
||||
## State File
|
||||
|
||||
The plugin stores loop state in `ralph-loop.local.md` in your project root:
|
||||
|
||||
```markdown
|
||||
---
|
||||
active: true
|
||||
iteration: 5
|
||||
max_iterations: 20
|
||||
completion_promise: "DONE"
|
||||
started_at: "2024-01-15T10:30:00Z"
|
||||
---
|
||||
|
||||
Your prompt text here...
|
||||
```
|
||||
|
||||
Add this file to `.gitignore` to avoid committing loop state.
|
||||
|
||||
## Learn More
|
||||
|
||||
- Original technique: https://ghuntley.com/ralph/
|
||||
- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator
|
||||
- Claude Code plugin: https://github.com/anthropics/claude-code/tree/main/plugins/ralph-wiggum
|
||||
28
command/cancel-ralph.md
Normal file
28
command/cancel-ralph.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
description: Cancel the active Ralph Wiggum loop
|
||||
---
|
||||
|
||||
# Cancel Ralph Loop
|
||||
|
||||
To cancel the Ralph loop, perform these steps:
|
||||
|
||||
1. Check if the Ralph state file exists at `ralph-loop.local.md`
|
||||
|
||||
2. If the file does NOT exist:
|
||||
- Report: "No active Ralph loop found."
|
||||
|
||||
3. If the file EXISTS:
|
||||
- Read the file to get the current iteration number from the `iteration:` field in the frontmatter
|
||||
- Delete the file `ralph-loop.local.md`
|
||||
- Report: "Cancelled Ralph loop (was at iteration N)" where N is the iteration value
|
||||
|
||||
Execute:
|
||||
```bash
|
||||
if [ -f ralph-loop.local.md ]; then
|
||||
ITERATION=$(grep '^iteration:' ralph-loop.local.md | sed 's/iteration: *//')
|
||||
rm ralph-loop.local.md
|
||||
echo "Cancelled Ralph loop (was at iteration $ITERATION)"
|
||||
else
|
||||
echo "No active Ralph loop found."
|
||||
fi
|
||||
```
|
||||
126
command/ralph-help.md
Normal file
126
command/ralph-help.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
description: Explain the Ralph Wiggum technique and available commands
|
||||
---
|
||||
|
||||
# Ralph Wiggum Plugin Help
|
||||
|
||||
Please explain the following to the user:
|
||||
|
||||
## What is the Ralph Wiggum Technique?
|
||||
|
||||
The Ralph Wiggum technique is an iterative development methodology based on continuous AI loops, pioneered by Geoffrey Huntley.
|
||||
|
||||
**Core concept:**
|
||||
```bash
|
||||
while :; do
|
||||
cat PROMPT.md | opencode --continue
|
||||
done
|
||||
```
|
||||
|
||||
The same prompt is fed to the AI repeatedly. The "self-referential" aspect comes from the AI seeing its own previous work in the files and git history, not from feeding output back as input.
|
||||
|
||||
**Each iteration:**
|
||||
1. AI receives the SAME prompt
|
||||
2. Works on the task, modifying files
|
||||
3. Completes its response
|
||||
4. Plugin intercepts idle state and feeds the same prompt again
|
||||
5. AI sees its previous work in the files
|
||||
6. Iteratively improves until completion
|
||||
|
||||
The technique is described as "deterministically bad in an undeterministic world" - failures are predictable, enabling systematic improvement through prompt tuning.
|
||||
|
||||
## Available Commands
|
||||
|
||||
### /ralph-loop <PROMPT> [OPTIONS]
|
||||
|
||||
Start a Ralph loop in your current session.
|
||||
|
||||
**Usage:**
|
||||
```
|
||||
/ralph-loop "Refactor the cache layer" --max-iterations 20
|
||||
/ralph-loop "Add tests" --completion-promise "TESTS COMPLETE"
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--max-iterations <n>` - Max iterations before auto-stop
|
||||
- `--completion-promise <text>` - Promise phrase to signal completion
|
||||
|
||||
**How it works:**
|
||||
1. Creates `.opencode/ralph-loop.local.md` state file
|
||||
2. You work on the task
|
||||
3. When you finish responding, the plugin intercepts
|
||||
4. Same prompt fed back
|
||||
5. You see your previous work
|
||||
6. Continues until promise detected or max iterations
|
||||
|
||||
---
|
||||
|
||||
### /cancel-ralph
|
||||
|
||||
Cancel an active Ralph loop (removes the loop state file).
|
||||
|
||||
**Usage:**
|
||||
```
|
||||
/cancel-ralph
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Checks for active loop state file
|
||||
- Removes `.opencode/ralph-loop.local.md`
|
||||
- Reports cancellation with iteration count
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Completion Promises
|
||||
|
||||
To signal completion, the AI must output a `<promise>` tag:
|
||||
|
||||
```
|
||||
<promise>TASK COMPLETE</promise>
|
||||
```
|
||||
|
||||
The plugin looks for this specific tag. Without it (or `--max-iterations`), Ralph runs infinitely.
|
||||
|
||||
### Self-Reference Mechanism
|
||||
|
||||
The "loop" doesn't mean the AI talks to itself. It means:
|
||||
- Same prompt repeated
|
||||
- AI's work persists in files
|
||||
- Each iteration sees previous attempts
|
||||
- Builds incrementally toward goal
|
||||
|
||||
## Example
|
||||
|
||||
### Interactive Bug Fix
|
||||
|
||||
```
|
||||
/ralph-loop "Fix the token refresh logic in auth.ts. Output <promise>FIXED</promise> when all tests pass." --completion-promise "FIXED" --max-iterations 10
|
||||
```
|
||||
|
||||
You'll see Ralph:
|
||||
- Attempt fixes
|
||||
- Run tests
|
||||
- See failures
|
||||
- Iterate on solution
|
||||
- In your current session
|
||||
|
||||
## When to Use Ralph
|
||||
|
||||
**Good for:**
|
||||
- Well-defined tasks with clear success criteria
|
||||
- Tasks requiring iteration and refinement
|
||||
- Iterative development with self-correction
|
||||
- Greenfield projects
|
||||
|
||||
**Not good for:**
|
||||
- Tasks requiring human judgment or design decisions
|
||||
- One-shot operations
|
||||
- Tasks with unclear success criteria
|
||||
- Debugging production issues (use targeted debugging instead)
|
||||
|
||||
## Learn More
|
||||
|
||||
- Original technique: https://ghuntley.com/ralph/
|
||||
- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator
|
||||
80
command/ralph-loop.md
Normal file
80
command/ralph-loop.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
description: Start a Ralph Wiggum loop for iterative development
|
||||
---
|
||||
|
||||
# Ralph Loop Command
|
||||
|
||||
You are starting a Ralph Wiggum loop. This is an iterative development technique where you work on the same task repeatedly, seeing your previous work in files and git history.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
Execute the following steps to initialize the Ralph loop:
|
||||
|
||||
1. Parse the arguments from: `$ARGUMENTS`
|
||||
|
||||
Arguments format: `<PROMPT> [--max-iterations N] [--completion-promise TEXT]`
|
||||
|
||||
- Extract the main prompt (everything that isn't a flag or flag value)
|
||||
- Extract `--max-iterations` value if provided (default: 0 for unlimited)
|
||||
- Extract `--completion-promise` value if provided (default: null)
|
||||
|
||||
2. Create the state file at `ralph-loop.local.md` (in the project root) with this exact format:
|
||||
|
||||
```markdown
|
||||
---
|
||||
active: true
|
||||
iteration: 1
|
||||
max_iterations: <MAX_ITERATIONS_VALUE>
|
||||
completion_promise: <COMPLETION_PROMISE_VALUE_OR_null>
|
||||
started_at: "<CURRENT_ISO_TIMESTAMP>"
|
||||
---
|
||||
|
||||
<THE_PROMPT_TEXT>
|
||||
```
|
||||
|
||||
3. Output the activation message:
|
||||
|
||||
```
|
||||
Ralph loop activated!
|
||||
|
||||
Iteration: 1
|
||||
Max iterations: <N or "unlimited">
|
||||
Completion promise: <TEXT or "none (runs forever)">
|
||||
|
||||
The Ralph plugin will now monitor for session idle events. When you complete
|
||||
your response, the same prompt will be fed back to continue the loop.
|
||||
|
||||
To stop the loop:
|
||||
- Output <promise>YOUR_PROMISE</promise> if a completion promise is set
|
||||
- Wait for max iterations to be reached
|
||||
- Run /cancel-ralph to cancel manually
|
||||
```
|
||||
|
||||
4. If a completion promise is set, display this critical warning:
|
||||
|
||||
```
|
||||
CRITICAL - Ralph Loop Completion Promise
|
||||
|
||||
To complete this loop, output this EXACT text:
|
||||
<promise>YOUR_PROMISE_HERE</promise>
|
||||
|
||||
STRICT REQUIREMENTS:
|
||||
- Use <promise> XML tags EXACTLY as shown above
|
||||
- The statement MUST be completely and unequivocally TRUE
|
||||
- Do NOT output false statements to exit the loop
|
||||
- Do NOT lie even if you think you should exit
|
||||
|
||||
IMPORTANT: Even if you believe you're stuck or the task is impossible,
|
||||
you MUST NOT output a false promise. The loop continues until the
|
||||
promise is GENUINELY TRUE.
|
||||
```
|
||||
|
||||
5. Now begin working on the task from the prompt. The Ralph plugin will automatically continue feeding you the same prompt when you complete your response.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
/ralph-loop Build a REST API for todos --completion-promise "DONE" --max-iterations 20
|
||||
/ralph-loop Fix the auth bug --max-iterations 10
|
||||
/ralph-loop Refactor the cache layer
|
||||
```
|
||||
216
plugin/ralph.ts
Normal file
216
plugin/ralph.ts
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import type { Plugin } from "@opencode-ai/plugin"
|
||||
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from "fs"
|
||||
import { join } from "path"
|
||||
|
||||
/**
|
||||
* Ralph Wiggum Plugin for OpenCode
|
||||
*
|
||||
* Implementation of the Ralph Wiggum technique - continuous self-referential AI loops
|
||||
* for iterative development. Named after Ralph Wiggum from The Simpsons, embodying
|
||||
* the philosophy of persistent iteration despite setbacks.
|
||||
*
|
||||
* Core concept: Feed the same prompt repeatedly, letting the AI see its previous work
|
||||
* in files and git history, creating a self-referential feedback loop.
|
||||
*
|
||||
* Based on: https://ghuntley.com/ralph/
|
||||
*/
|
||||
|
||||
interface RalphState {
|
||||
active: boolean
|
||||
iteration: number
|
||||
maxIterations: number
|
||||
completionPromise: string | null
|
||||
startedAt: string
|
||||
prompt: string
|
||||
}
|
||||
|
||||
const STATE_FILE = "ralph-loop.local.md"
|
||||
|
||||
function parseRalphState(directory: string): RalphState | null {
|
||||
const statePath = join(directory, STATE_FILE)
|
||||
|
||||
if (!existsSync(statePath)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const content = readFileSync(statePath, "utf-8")
|
||||
|
||||
// Parse YAML frontmatter
|
||||
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
|
||||
if (!frontmatterMatch) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [, frontmatter, prompt] = frontmatterMatch
|
||||
|
||||
// Parse frontmatter values
|
||||
const getValue = (key: string): string | null => {
|
||||
const match = frontmatter.match(new RegExp(`^${key}:\\s*(.*)$`, "m"))
|
||||
if (!match) return null
|
||||
// Remove surrounding quotes if present
|
||||
return match[1].replace(/^["'](.*)["']$/, "$1")
|
||||
}
|
||||
|
||||
const active = getValue("active") === "true"
|
||||
const iteration = parseInt(getValue("iteration") || "1", 10)
|
||||
const maxIterations = parseInt(getValue("max_iterations") || "0", 10)
|
||||
const completionPromise = getValue("completion_promise")
|
||||
const startedAt = getValue("started_at") || new Date().toISOString()
|
||||
|
||||
return {
|
||||
active,
|
||||
iteration,
|
||||
maxIterations,
|
||||
completionPromise: completionPromise === "null" ? null : completionPromise,
|
||||
startedAt,
|
||||
prompt: prompt.trim(),
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function writeRalphState(directory: string, state: RalphState): void {
|
||||
const statePath = join(directory, STATE_FILE)
|
||||
|
||||
|
||||
|
||||
const completionPromiseYaml =
|
||||
state.completionPromise === null ? "null" : `"${state.completionPromise}"`
|
||||
|
||||
const content = `---
|
||||
active: ${state.active}
|
||||
iteration: ${state.iteration}
|
||||
max_iterations: ${state.maxIterations}
|
||||
completion_promise: ${completionPromiseYaml}
|
||||
started_at: "${state.startedAt}"
|
||||
---
|
||||
|
||||
${state.prompt}
|
||||
`
|
||||
|
||||
writeFileSync(statePath, content, "utf-8")
|
||||
}
|
||||
|
||||
function deleteRalphState(directory: string): boolean {
|
||||
const statePath = join(directory, STATE_FILE)
|
||||
|
||||
if (existsSync(statePath)) {
|
||||
unlinkSync(statePath)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function checkCompletionPromise(text: string, promise: string): boolean {
|
||||
// Extract text from <promise> tags
|
||||
const promiseMatch = text.match(/<promise>([\s\S]*?)<\/promise>/)
|
||||
if (!promiseMatch) return false
|
||||
|
||||
// Normalize whitespace and compare
|
||||
const promiseText = promiseMatch[1].trim().replace(/\s+/g, " ")
|
||||
return promiseText === promise
|
||||
}
|
||||
|
||||
export const RalphPlugin: Plugin = async ({ directory, client, $ }) => {
|
||||
return {
|
||||
/**
|
||||
* Handle session idle event - this is when the AI has finished responding
|
||||
* and would normally wait for user input. In Ralph mode, we intercept this
|
||||
* to continue the loop.
|
||||
*/
|
||||
event: async ({ event }) => {
|
||||
if (event.type !== "session.idle") return
|
||||
|
||||
const state = parseRalphState(directory)
|
||||
if (!state || !state.active) return
|
||||
|
||||
// Get the last assistant message to check for completion
|
||||
// We need to check if the completion promise was output
|
||||
if (state.completionPromise) {
|
||||
try {
|
||||
// Use the SDK to get session messages
|
||||
const session = await client.session.get({ id: event.properties.sessionID })
|
||||
if (session.messages && session.messages.length > 0) {
|
||||
// Find the last assistant message
|
||||
const lastAssistantMsg = [...session.messages]
|
||||
.reverse()
|
||||
.find((m) => m.role === "assistant")
|
||||
|
||||
if (lastAssistantMsg) {
|
||||
// Extract text content from message parts
|
||||
const textContent = lastAssistantMsg.parts
|
||||
?.filter((p: any) => p.type === "text")
|
||||
.map((p: any) => p.text)
|
||||
.join("\n")
|
||||
|
||||
if (textContent && checkCompletionPromise(textContent, state.completionPromise)) {
|
||||
// Completion promise detected - stop the loop
|
||||
deleteRalphState(directory)
|
||||
await client.app.log({
|
||||
service: "ralph-plugin",
|
||||
level: "info",
|
||||
message: `Ralph loop completed: detected <promise>${state.completionPromise}</promise>`,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// If we can't check messages, continue the loop
|
||||
await client.app.log({
|
||||
service: "ralph-plugin",
|
||||
level: "warn",
|
||||
message: `Could not check for completion promise: ${err}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check max iterations
|
||||
if (state.maxIterations > 0 && state.iteration >= state.maxIterations) {
|
||||
deleteRalphState(directory)
|
||||
await client.app.log({
|
||||
service: "ralph-plugin",
|
||||
level: "info",
|
||||
message: `Ralph loop stopped: max iterations (${state.maxIterations}) reached`,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Continue the loop - increment iteration and feed prompt back
|
||||
const nextIteration = state.iteration + 1
|
||||
writeRalphState(directory, {
|
||||
...state,
|
||||
iteration: nextIteration,
|
||||
})
|
||||
|
||||
// Build the continuation message
|
||||
let systemMsg = `Ralph iteration ${nextIteration}`
|
||||
if (state.completionPromise) {
|
||||
systemMsg += ` | To stop: output <promise>${state.completionPromise}</promise> (ONLY when TRUE)`
|
||||
} else if (state.maxIterations > 0) {
|
||||
systemMsg += ` / ${state.maxIterations}`
|
||||
} else {
|
||||
systemMsg += ` | No completion promise set - loop runs until cancelled`
|
||||
}
|
||||
|
||||
// Log the iteration
|
||||
await client.app.log({
|
||||
service: "ralph-plugin",
|
||||
level: "info",
|
||||
message: systemMsg,
|
||||
})
|
||||
|
||||
// Append the prompt back to continue the session
|
||||
// The prompt includes a marker showing the iteration
|
||||
const continuationPrompt = `[${systemMsg}]\n\n${state.prompt}`
|
||||
|
||||
// Use session.send to continue the conversation
|
||||
await client.session.send({
|
||||
id: event.properties.sessionID,
|
||||
text: continuationPrompt,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue