🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

opencode-ralph-loop

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

opencode-ralph-loop - npm Package Compare versions

Comparing version
1.0.9
to
1.0.10
+95
src/commands.ts
// Non-function constants live here (not in index.ts) so opencode's plugin
// loader — which iterates Object.values(module) and rejects anything that
// isn't a function or `{server: fn}` — never sees them as exports of the
// entrypoint. See AGENTS.md gotcha.
export const STATE_FILENAME = "ralph-loop.local.md";
export const STATE_DIR = ".opencode";
export const STATE_PATH = `${STATE_DIR}/${STATE_FILENAME}`;
export interface RalphCommandDef {
description: string;
template: string;
agent: string;
}
// Inline slash-command templates. Mirror what commands/*.md used to be,
// minus the per-file YAML frontmatter. Registered at runtime via the
// `config` hook in index.ts.
export const RALPH_COMMANDS: Record<string, RalphCommandDef> = {
"ralph-loop": {
description: "Start Ralph Loop - auto-continues until task completion",
template: `Start an iterative development loop that automatically continues until the task is complete.
Create the state file in the project directory:
\`\`\`bash
mkdir -p ${STATE_DIR} && cat > ${STATE_PATH} << 'EOF'
---
active: true
iteration: 0
maxIterations: 100
---
$ARGUMENTS
EOF
\`\`\`
Now begin working on the task: **$ARGUMENTS**
When the task is FULLY completed, signal completion by outputting:
\`\`\`
<promise>DONE</promise>
\`\`\`
**IMPORTANT:** ONLY output this when the task is COMPLETELY and VERIFIABLY finished. Do NOT output false promises to escape the loop.
Use \`/cancel-ralph\` to stop early.`,
agent: "build",
},
"cancel-ralph": {
description: "Cancel active Ralph Loop",
template: `Cancel the active Ralph Loop.
\`\`\`bash
if [ -f ${STATE_PATH} ]; then
grep '^iteration:' ${STATE_PATH}
rm -f ${STATE_PATH}
echo "Ralph Loop cancelled."
else
echo "No active Ralph Loop to cancel."
fi
\`\`\`
Report the result to the user.`,
agent: "build",
},
"help": {
description: "Show Ralph Loop plugin help and available commands",
template: `# Ralph Loop Help
## Available Commands
- \`/ralph-loop <task>\` - Start an auto-continuation loop for the given task
- \`/cancel-ralph\` - Stop an active Ralph Loop
## Quick Start
\`\`\`
/ralph-loop Build a REST API with user authentication
\`\`\`
The AI will work on your task and automatically continue until it outputs \`<promise>DONE</promise>\` to signal completion.
## How It Works
1. Creates state file at \`${STATE_PATH}\`
2. Works on task until idle
3. If no \`<promise>DONE</promise>\` found, auto-continues
4. Repeats until complete or max iterations (100) reached
For more details, the AI can use the \`help\` skill.`,
agent: "build",
},
};
+1
-1
{
"name": "opencode-ralph-loop",
"version": "1.0.9",
"version": "1.0.10",
"description": "Minimal Ralph Loop plugin for opencode - auto-continues until task completion",

@@ -5,0 +5,0 @@ "main": "src/index.ts",

@@ -7,2 +7,3 @@ import { tool } from "@opencode-ai/plugin";

import { COMPLETION_TAG } from "./completion.ts";
import { RALPH_COMMANDS, STATE_FILENAME } from "./commands.ts";

@@ -18,4 +19,2 @@ // Types

// Constants
const STATE_FILENAME = "ralph-loop.local.md";
const OPENCODE_CONFIG_DIR = join(homedir(), ".config/opencode");

@@ -35,45 +34,27 @@

// Auto-copy skills and commands to opencode config on first run
function setupSkillsAndCommands(): void {
// Auto-copy skills to opencode config on first run. opencode reads from
// ~/.config/opencode/skills/ (plural). Earlier versions wrote to /skill/
// (singular) — that path is no longer scanned. Slash commands are NOT
// copied here anymore; they self-register via the `config` hook below.
function setupSkills(): void {
const pluginRoot = getPluginRoot();
const skillsDir = join(OPENCODE_CONFIG_DIR, "skill");
const commandsDir = join(OPENCODE_CONFIG_DIR, "command");
const skillsDir = join(OPENCODE_CONFIG_DIR, "skills");
// Copy skills
const pluginSkillsDir = join(pluginRoot, "skills");
if (existsSync(pluginSkillsDir)) {
const skills = ["ralph-loop", "cancel-ralph", "help"];
for (const skill of skills) {
const srcSkillDir = join(pluginSkillsDir, skill);
const destSkillDir = join(skillsDir, skill);
if (!existsSync(pluginSkillsDir)) return;
if (existsSync(srcSkillDir) && !existsSync(destSkillDir)) {
try {
mkdirSync(destSkillDir, { recursive: true });
cpSync(srcSkillDir, destSkillDir, { recursive: true });
} catch {
// Silent fail
}
// Skills directories are co-located with command names; deriving from
// RALPH_COMMANDS keeps the two in lockstep when new commands are added.
for (const skill of Object.keys(RALPH_COMMANDS)) {
const srcSkillDir = join(pluginSkillsDir, skill);
const destSkillDir = join(skillsDir, skill);
if (existsSync(srcSkillDir) && !existsSync(destSkillDir)) {
try {
mkdirSync(destSkillDir, { recursive: true });
cpSync(srcSkillDir, destSkillDir, { recursive: true });
} catch {
// Silent fail
}
}
}
// Copy commands
const pluginCommandsDir = join(pluginRoot, "commands");
if (existsSync(pluginCommandsDir)) {
const commands = ["ralph-loop.md", "cancel-ralph.md", "help.md"];
for (const cmd of commands) {
const srcCmd = join(pluginCommandsDir, cmd);
const destCmd = join(commandsDir, cmd);
if (existsSync(srcCmd) && !existsSync(destCmd)) {
try {
mkdirSync(commandsDir, { recursive: true });
cpSync(srcCmd, destCmd);
} catch {
// Silent fail
}
}
}
}
}

@@ -194,6 +175,18 @@

// Auto-setup skills and commands on first run
setupSkillsAndCommands();
// Auto-setup skills on first run. Slash commands self-register via the
// `config` hook below.
setupSkills();
return {
// Self-register slash commands. opencode's `config` hook fires while
// assembling the runtime config; mutating `input.command` adds entries
// to the command dict, no file copying needed. User-defined entries in
// opencode.json take precedence (we only set absent names).
config: async (input: any) => {
input.command = input.command ?? {};
for (const [name, def] of Object.entries(RALPH_COMMANDS)) {
if (!input.command[name]) input.command[name] = def;
}
},
// Register tools using the @opencode-ai/plugin SDK format.

@@ -200,0 +193,0 @@ // The `args` field (Zod schema shape) is required — opencode calls