feat: add cross-tool support for OpenCode and Codex CLI#91
feat: add cross-tool support for OpenCode and Codex CLI#91
Conversation
Spec artifacts: - research.md: feasibility analysis and codebase exploration - requirements.md: user stories and acceptance criteria - design.md: architecture and technical decisions - tasks.md: POC-first implementation plan (25 tasks, 4 phases) Ready for implementation.
…lity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ogression Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The regression test incorrectly flagged the required version bump (3.3.3 to 3.4.0) as a regression. Updated tests 1, 7, and 10 to accept version-only changes in plugin.json, since CLAUDE.md mandates version bumps for any plugin modification. Also commits updated spec index files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR introduces cross-tool support for the Ralph specification workflow, extending it to OpenCode and Codex CLI alongside Claude Code. It adds eight universal SKILL.md documentation files defining each workflow phase, creates OpenCode and Codex adapters with execution logic, establishes a configuration bridge via ralph-config.json, and includes comprehensive test coverage and documentation. Changes
Sequence DiagramssequenceDiagram
participant User
participant Tool as Tool (Claude/OpenCode/Codex)
participant Adapter as Tool Adapter
participant State as State Management<br/>(.ralph-state.json)
participant Spec as Spec Artifacts<br/>(tasks.md, design.md)
User->>Tool: Initiate ralph:start
Tool->>Adapter: Execute skill
Adapter->>State: Initialize state<br/>(phase, taskIndex=0)
Adapter->>Spec: Create research.md
Tool-->>User: Begin Research Phase
loop Workflow Progression (research → implement)
User->>Tool: Continue workflow
Tool->>Adapter: Trigger next phase
Adapter->>State: Read current phase
Adapter->>Spec: Read phase artifact
Adapter->>Spec: Execute phase logic
Adapter->>State: Update phase, set<br/>awaitingApproval
Tool-->>User: Provide phase output
end
User->>Tool: ralph:implement
Tool->>Adapter: Execute implement skill
loop Task Execution (per task)
Adapter->>State: Read taskIndex
Adapter->>Spec: Get current task<br/>from tasks.md
Adapter->>User: Display task details
User->>Spec: Execute task<br/>(Do, Verify, Commit)
Adapter->>State: Advance taskIndex
alt All tasks complete
Adapter->>State: Mark execution complete
Tool-->>User: Spec implementation done
else Tasks remaining
Adapter->>User: Prompt for next task
end
end
sequenceDiagram
participant Claude as Claude Code
participant OpenCode as OpenCode + Hook
participant Codex as Codex CLI
participant Config as ralph-config.json
participant Adapter as Tool Adapter
User->>Config: Provide unified config
Config->>Claude: Claude Code setup<br/>(plugin_dir)
Config->>OpenCode: OpenCode setup<br/>(hooks_dir)
Config->>Codex: Codex setup<br/>(skills_dir)
Claude->>Claude: Uses built-in SKILL.md
Claude-->>User: Execute workflow
OpenCode->>Adapter: Hook triggered<br/>(session.idle)
Adapter->>Adapter: Read .ralph-state.json
Adapter->>Adapter: Detect active spec
alt Execution phase + tasks remain
Adapter->>OpenCode: Inject continuation<br/>prompt + guidance
OpenCode-->>User: Continue task
else All tasks complete
Adapter->>OpenCode: Block with<br/>completion message
end
Codex->>Adapter: Skill discovered<br/>(ralph-implement)
Adapter->>Adapter: Read SKILL.md
Adapter->>Adapter: Execute single task<br/>from tasks.md
Adapter->>Adapter: Update .ralph-state.json
Codex-->>User: Prompt for re-invocation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 19
🤖 Fix all issues with AI agents
In `@adapters/codex/AGENTS.md.template`:
- Line 25: Update the incorrect GitHub URL string
"https://github.com/zachbonfil/smart-ralph" in the AGENTS.md.template line that
references Smart Ralph so the repository owner username is correct; replace
"zachbonfil" with "tzachbon" resulting in
"https://github.com/tzachbon/smart-ralph" to match the rest of the codebase.
In `@adapters/codex/skills/ralph-implement/SKILL.md`:
- Line 331: Replace the double backticks around `.ralph-state.json` with single
backticks in the SKILL.md text that states "Codex CLI does not support hooks,
stop events, or automatic continuation. The state file (``.ralph-state.json``)
acts as the coordination mechanism:" so the filename is rendered correctly as
`.ralph-state.json`.
In `@adapters/config/generate-config.sh`:
- Line 26: The variables PROJECT_ROOT, COMMIT_SPEC, MAX_ITERATIONS, and
MAX_TASK_ITERATIONS are assigned but never used; either remove their assignments
to declutter the script or explicitly mark them for future use by exporting them
or adding a TODO comment; update the generate-config.sh snippet that creates
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" and the lines that assign
COMMIT_SPEC, MAX_ITERATIONS, and MAX_TASK_ITERATIONS so they are either deleted
or changed to export VARIABLE_NAME=# TODO: intended for future use, keeping only
the variables you actually reference elsewhere.
- Around line 339-344: The fallback loop for discovering the latest spec
(iterating design_file and setting latest_spec) does not actually pick the most
recently modified file; update the logic to sort design.md files by modification
time and pick the newest one (e.g., use ls -t "$spec_root"/*/design.md | head
-n1 or use find ... -printf '%T@ %p' | sort -n | tail -n1) then set
latest_spec="$(dirname "<that-most-recent-path>")"; ensure the code references
the existing variables design_file, latest_spec, and spec_root so behavior and
variable names remain consistent.
In `@adapters/opencode/hooks/execution-loop.ts`:
- Around line 302-318: The continuation prompt in the array assigned to reason
contains a Claude-specific phrase "Delegate via Task tool - do NOT implement
yourself"; update the string in that array (the reason constant built near where
specName and state are used) to use tool-agnostic wording such as "Delegate via
the designated task-delegation mechanism - do NOT implement yourself" or "Use
the system's task delegation tool/mechanism - do NOT implement yourself" so it
no longer references Claude-specific concepts; keep the rest of the reason text
unchanged and ensure the edit touches only the literal string in the reason
array that mentions "Task tool" (refer to reason, specName, state,
STATE_FILE_NAME).
- Around line 67-92: getSpecsDirs() and isPluginDisabled() currently read Claude
Code-specific ".claude/ralph-specum.local.md" which is wrong for the OpenCode
adapter; update both functions to prefer a tool-agnostic project config
(ralph-config.json at repo root with a "spec_dirs" array) and then fall back to
an OpenCode-specific config (e.g., ".opencode/ralph-config.json" or a
".opencode" frontmatter file) before using DEFAULT_SPECS_DIR; specifically,
modify getSpecsDirs to: 1) attempt to read and parse ralph-config.json in cwd
and return its spec_dirs if present and valid, 2) if not present, attempt to
read an ".opencode" config (either JSON or frontmatter) for spec dirs, 3) only
if both are missing fallback to DEFAULT_SPECS_DIR; similarly update
isPluginDisabled to check ralph-config.json and .opencode config for the plugin
disabled flag instead of ".claude/ralph-specum.local.md", and keep existing
file-parsing error handling and trimming logic while removing any dependency on
".claude/ralph-specum.local.md".
In `@AGENTS.md`:
- Around line 56-66: The markdown fenced code blocks in AGENTS.md (the directory
listings like the block containing "skills/workflow/" and other blocks showing
"adapters/opencode/" and "adapters/codex/") lack language tags and trigger
MD040; update each triple-backtick fence to include a language tag (use "text")
so they read ```text ... ``` for every fenced block (including those around the
blocks at the other reported ranges ~118-130 and 142-148) while preserving the
exact block contents.
In `@CLAUDE.md`:
- Around line 105-118: The fenced code block in CLAUDE.md that begins with the
directory tree ("adapters/ ...") is missing a language tag and triggers MD040;
update the opening fence from ``` to ```text (or another appropriate language)
so the block is explicitly tagged (locate the block that starts with "adapters/"
in CLAUDE.md and change the opening triple backticks to include the language).
In `@plugins/ralph-specum/scripts/generate-agents-md.sh`:
- Around line 37-59: The --spec-path and --output branches in the
argument-parsing case (inside the while loop handling "$1") assume a following
argument and use "$2" then shift 2; add a guard that verifies a next argument
exists and is not another flag (e.g., test $# -ge 2 and that "$2" does not start
with '-') before assigning SPEC_PATH or OUTPUT_PATH, and if the check fails call
usage or print a clear error and exit; update both branches consistently so the
script (and variables SPEC_PATH/OUTPUT_PATH) won’t trigger an "unbound variable"
under set -u.
- Around line 92-114: The current extract_section logic uses index($0, "## "
target) == 1 which does a prefix match and can capture headings like "##
Architecture Overview" when target is "Architecture"; update the heading check
inside the awk block that uses the heading variable so it only matches an exact
heading (e.g., anchor the pattern to the line start and end), allowing
optionally a trailing space or colon; modify the condition that references
heading/target to use a regex match comparing the full heading line (for example
matching /^##[[:space:]]+target([[:space:]]|:)?$/ using the heading variable)
instead of index(... ) to avoid false positives.
In `@plugins/ralph-specum/skills/workflow/cancel/SKILL.md`:
- Around line 55-58: The markdown output blocks in SKILL.md currently lack
language fences; update each triple-backtick code block (the plain text messages
such as "No spec found at: $SPEC_PATH", "Canceled: $SPEC_NAME", "Canceled and
removed: $SPEC_NAME", "No active execution loop found for spec: $SPEC_NAME",
etc.) to use ```text language tags so they become ```text ... ```; ensure you
apply this change to the other similar blocks referenced (around the other
occurrences noted) so all user-facing output blocks are fenced as text.
In `@plugins/ralph-specum/skills/workflow/design/SKILL.md`:
- Around line 67-80: The outer triple-fenced markdown blocks (the blocks that
start with ```markdown) contain inner fenced blocks like ```mermaid which closes
the outer fence and breaks rendering; fix each occurrence by changing the outer
fence to a four-backtick fence (````markdown ... ````) or use tilde fences (~~~)
so the inner ```mermaid (and other inner ``` blocks) remain intact; search for
the outer "```markdown" blocks and any nested "```mermaid" (and similar nested
fenced blocks) and update those outer fences consistently (also apply the same
fix to the other repeated sections containing nested code fences).
In `@plugins/ralph-specum/skills/workflow/start/SKILL.md`:
- Around line 17-19: The Markdown fenced code blocks in SKILL.md lack language
tags and trigger MD040; update each triple-backtick fence in the shown sections
(the blocks containing the flow line "research -> requirements -> design ->
tasks -> implement" and the subsequent example blocks around specs/,
specs/my-feature/, decision flow, resuming output, start CLI usage examples,
quick-mode rules, and flags examples) by adding a language tag (suggest "text")
after the opening ``` so each block becomes ```text, ensuring all similar blocks
at the other listed ranges (27-33, 37-43, 74-85, 187-191, 224-228, 240-251,
257-259, 265-267, 275-278) are updated consistently to satisfy MD040.
In `@plugins/ralph-specum/skills/workflow/status/SKILL.md`:
- Around line 125-144: The fenced code blocks in SKILL.md (e.g., the sample
block starting with "# Ralph Spec Status", the "Next: Run the requirements
skill..." block, the "## api-auth [packages/api/specs]" block, and other blocks
around lines noted in the review) lack language tags and violate MD040; update
each triple-backtick fence to include a language tag such as ```text or ```md so
they are explicitly labeled (apply to the blocks containing "# Ralph Spec
Status", the example command block "ralph:start my-feature...", the "Next: Run
the requirements skill..." paragraph, and the "## api-auth [packages/api/specs]"
/ "Related: auth-system..." blocks) and ensure every opening ``` has a matching
closing ``` afterward.
In `@specs/.index/index-state.json`:
- Around line 19-23: The "opencode-codex-support" tasks entry is missing the
taskIndex and totalTasks fields; update the JSON object for the entry with name
"opencode-codex-support" to include the same task indexing fields used by other
"tasks" phase entries (add "taskIndex": <number> and "totalTasks": <number> with
the appropriate values consistent with the rest of the phase), ensuring the
object structure matches the other entries (e.g., those for
"implement-ralph-wiggum", "speckit-stop-hook",
"reality-verification-principle").
In `@specs/.index/index.md`:
- Line 19: The index entry for spec "opencode-codex-support" is missing the
task-count in the status column; update the index generator logic that builds
the index row for specs (the code that formats spec rows and the renderer that
produces the "| opencode-codex-support | ./specs | tasks | |" line) to compute
completed/total tasks (16/20) and render it as "16/20 tasks" in the status
column; specifically, locate the function that assembles spec metadata (the spec
row formatter / index row renderer) and add a calculation using the spec's
completedTasks and totalTasks fields, then insert the formatted string into the
fourth column for in-progress specs.
In `@specs/opencode-codex-support/tasks.md`:
- Around line 129-221: Phase 2 lacks a mid-phase quality checkpoint: insert a
new "[VERIFY]" checkpoint immediately after task 2.2 (after the "Create OpenCode
execution loop adapter" block) that validates the state of the work so far; the
checkpoint should confirm adapters/opencode directory exists,
adapters/opencode/hooks/execution-loop.ts is present and TypeScript-valid,
adapters/opencode/README.md exists, and that the AGENTS.md generator script
(plugins/ralph-specum/scripts/generate-agents-md.sh) can read design.md and
produce AGENTS.md (or at minimum the generator file exists and is executable);
label it as a verification task consistent with existing 2.5 format and update
the checklist to include this new verify item.
- Around line 11-127: Insert a new [VERIFY] checkpoint after task 1.3 that
validates the first three SKILL.md files (start, research, requirements) exist
under plugins/ralph-specum/skills/workflow/, contain frontmatter, and are free
of Claude Code-specific references; update the Phase 1 list to include this
checkpoint (e.g., "1.4 [VERIFY] — verify start/research/requirements SKILL.md
existence and tool-agnosticness") and renumber subsequent tasks (design, tasks,
implement, status/cancel) accordingly so every 2–3 tasks has a quality
checkpoint as required by the specs; ensure the verification commands reference
the three paths and grep for the banned tokens ("Task
tool|AskUserQuestion|TeamCreate|allowed-tools|subagent_type") in those files.
In `@tests/test-artifact-portability.sh`:
- Around line 37-47: The aggregate search uses grep -rn with TOOL_KEYWORDS
containing '|' but without extended regex, so the alternation is treated
literally; update the invocation that sets TEMPLATE_MATCHES (the grep command
referencing "$TOOL_KEYWORDS" and "$TEMPLATE_DIR") to use extended regex (add -E)
so the '|' separators work as intended and the aggregate check actually finds
any of the TOOL_KEYWORDS; ensure you keep the existing 2>/dev/null || true
behavior and the variable names TEMPLATE_MATCHES, TOOL_KEYWORDS, and
TEMPLATE_DIR unchanged.
🧹 Nitpick comments (11)
plugins/ralph-specum/skills/workflow/tasks/SKILL.md (1)
124-128: Hardcoded/tmp/state.jsonis a collision risk.If two specs run concurrently (or another process uses the same path), writing to a fixed
/tmp/state.jsoncan clobber data. The same pattern appears in the research and requirements SKILL.md files.Consider using
mktempinstead:Proposed fix
-jq --argjson total "$TOTAL" '.totalTasks = $total | .awaitingApproval = true' "$SPEC_DIR/.ralph-state.json" > /tmp/state.json && mv /tmp/state.json "$SPEC_DIR/.ralph-state.json" +TMP=$(mktemp) && jq --argjson total "$TOTAL" '.totalTasks = $total | .awaitingApproval = true' "$SPEC_DIR/.ralph-state.json" > "$TMP" && mv "$TMP" "$SPEC_DIR/.ralph-state.json"plugins/ralph-specum/skills/workflow/requirements/SKILL.md (1)
147-150: Same/tmp/state.jsoncollision risk as in tasks/SKILL.md.Use
mktempfor the temporary file to avoid concurrent-write collisions.Proposed fix
-jq '.awaitingApproval = true' "$SPEC_DIR/.ralph-state.json" > /tmp/state.json && mv /tmp/state.json "$SPEC_DIR/.ralph-state.json" +TMP=$(mktemp) && jq '.awaitingApproval = true' "$SPEC_DIR/.ralph-state.json" > "$TMP" && mv "$TMP" "$SPEC_DIR/.ralph-state.json"adapters/codex/AGENTS.md.template (1)
35-38: Available Skills table only listsralph-implement.The PR adds 8 SKILL.md files (start, research, requirements, design, tasks, implement, status, cancel), but the template only surfaces
ralph-implement. This may be intentional for Codex (where the user manually drives earlier phases), but consider listing all skills so Codex users can discover them.adapters/opencode/README.md (1)
48-59: Shared config path.claude/ralph-specum.local.mdmay confuse OpenCode-only users.The adapter reuses the Claude Code config path (
.claude/ralph-specum.local.md) for settings. This is documented as intentional for cross-tool config sharing, but OpenCode-only users who have never used Claude Code may find the.claude/directory name misleading. Consider mentioning this rationale briefly in the README (e.g., "This path is shared with the Claude Code plugin so configuration works across both tools").plugins/ralph-specum/skills/workflow/implement/SKILL.md (1)
155-160: Hardcoded/tmp/state.jsontemp path in state-update snippets is fragile.The jq update patterns throughout this document (lines 155-160, 212-214, 322-323, 328-330, 348) all use
/tmp/state.jsonas an intermediate file. This is a predictable path that can cause issues in multi-user or concurrent environments (race conditions, symlink attacks). Since AI agents will follow these snippets literally, consider using a spec-local temp path ormktemp.♻️ Example: use spec-local temp path
jq '.taskIteration += 1 | .globalIteration += 1' \ - "$SPEC_DIR/.ralph-state.json" > /tmp/state.json && \ - mv /tmp/state.json "$SPEC_DIR/.ralph-state.json" + "$SPEC_DIR/.ralph-state.json" > "$SPEC_DIR/.ralph-state.json.tmp" && \ + mv "$SPEC_DIR/.ralph-state.json.tmp" "$SPEC_DIR/.ralph-state.json"tests/test-claude-code-regression.sh (1)
73-94: Glob patterns may break the script if no matching files exist.If
$PLUGIN_DIR/commands/*.mdmatches nothing, bash iterates once with the literal glob string, andhead -1on it will fail — aborting the script underset -e. Same applies toagents/*.mdon line 100. Adding anullglobguard or an existence check would make this more resilient.♻️ Suggested guard
echo "Test 4: Command frontmatter validation..." +shopt -s nullglob for cmd in "$PLUGIN_DIR"/commands/*.md; doOr check before looping:
+CMD_FILES=("$PLUGIN_DIR"/commands/*.md) +if [ ! -e "${CMD_FILES[0]:-}" ]; then + fail "No command .md files found" +else for cmd in "$PLUGIN_DIR"/commands/*.md; do ... done +fiadapters/config/generate-config.sh (1)
96-118:cfgandcfg_boolare identical — consolidate.Both functions have the exact same implementation. This duplication adds maintenance burden without benefit.
♻️ Proposed fix
cfg() { local path="$1" local default="$2" local val val="$(printf '%s' "$CONFIG" | jq -r "$path // empty")" if [ -z "$val" ]; then printf '%s' "$default" else printf '%s' "$val" fi } - -cfg_bool() { - local path="$1" - local default="$2" - local val - val="$(printf '%s' "$CONFIG" | jq -r "$path // empty")" - if [ -z "$val" ]; then - printf '%s' "$default" - else - printf '%s' "$val" - fi -}Then replace all
cfg_boolcalls withcfg.specs/opencode-codex-support/design.md (1)
53-63: Add language identifiers to fenced code blocks.The directory-structure code blocks at lines 53, 115, and 139 have no language specified, triggering markdownlint MD040. Use
textorplaintextto satisfy the linter while conveying they are not executable code.♻️ Example fix
-``` +```text skills/workflow/ start/SKILL.mdAlso applies to: 115-127, 139-145
adapters/opencode/hooks/execution-loop.ts (3)
200-223: Cleanup loop aborts on firststatSync/unlinkSyncfailure, skipping remaining files.If
fs.statSyncorfs.unlinkSyncthrows for one entry (e.g., a race with another process), the outercatchswallows the error and exits the entire loop, leaving other orphaned files uncleaned.Suggested fix: per-file try-catch
for (const entry of entries) { if (/^\.progress-task-\d+\.md$/.test(entry)) { - const fullPath = path.join(specDir, entry); - const stat = fs.statSync(fullPath); - if (now - stat.mtimeMs > cutoffMs) { - fs.unlinkSync(fullPath); + try { + const fullPath = path.join(specDir, entry); + const stat = fs.statSync(fullPath); + if (now - stat.mtimeMs > cutoffMs) { + fs.unlinkSync(fullPath); + } + } catch { + // Ignore per-file cleanup errors (e.g., race condition) } } } - } catch { - // Ignore cleanup errors - }
134-150: Parsed JSON fields are not type-validated — non-numerictaskIndex/totalTaskswould silently corrupt loop logic.
data.taskIndex ?? 0only falls back when the value isnull/undefined. A string like"3"passes through and will cause incorrect arithmetic comparisons (e.g.,"3" >= 20isfalsein JS but"3" >= "20"istrue). Consider coercing withNumber()or adding basic type guards.Suggested fix
return { phase: data.phase ?? "unknown", - taskIndex: data.taskIndex ?? 0, - totalTasks: data.totalTasks ?? 0, - taskIteration: data.taskIteration ?? 1, - globalIteration: data.globalIteration ?? 1, - maxGlobalIterations: data.maxGlobalIterations ?? 100, - maxTaskIterations: data.maxTaskIterations ?? 5, + taskIndex: Number(data.taskIndex) || 0, + totalTasks: Number(data.totalTasks) || 0, + taskIteration: Number(data.taskIteration) || 1, + globalIteration: Number(data.globalIteration) || 1, + maxGlobalIterations: Number(data.maxGlobalIterations) || 100, + maxTaskIterations: Number(data.maxTaskIterations) || 5, recoveryMode: data.recoveryMode ?? false, };
181-195: Entire transcript is read into memory to check the last 500 lines.For large transcripts this is wasteful. Since this is a POC, it's acceptable now, but consider reading only the tail of the file (e.g., last ~50 KB) if transcripts grow large in production.
| # Fallback: find most recently modified design.md | ||
| for design_file in "$spec_root"/*/design.md; do | ||
| if [ -f "$design_file" ]; then | ||
| latest_spec="$(dirname "$design_file")" | ||
| fi | ||
| done |
There was a problem hiding this comment.
Fallback spec discovery doesn't sort by modification time despite the comment.
The comment on line 339 says "find most recently modified design.md", but the glob loop just overwrites latest_spec on each iteration, yielding the last alphabetically — not the most recently modified.
🐛 Proposed fix: use `ls -t` or `find` with `-newer` to sort by mtime
# Fallback: find most recently modified design.md
- for design_file in "$spec_root"/*/design.md; do
- if [ -f "$design_file" ]; then
- latest_spec="$(dirname "$design_file")"
- fi
- done
+ local newest
+ newest="$(find "$spec_root" -maxdepth 2 -name 'design.md' -printf '%T@ %p\n' 2>/dev/null \
+ | sort -rn | head -1 | cut -d' ' -f2-)"
+ if [ -n "$newest" ]; then
+ latest_spec="$(dirname "$newest")"
+ fiNote: -printf is GNU find-specific. On macOS, use stat -f '%m %N' or find ... -exec stat ... instead, depending on portability requirements.
🤖 Prompt for AI Agents
In `@adapters/config/generate-config.sh` around lines 339 - 344, The fallback loop
for discovering the latest spec (iterating design_file and setting latest_spec)
does not actually pick the most recently modified file; update the logic to sort
design.md files by modification time and pick the newest one (e.g., use ls -t
"$spec_root"/*/design.md | head -n1 or use find ... -printf '%T@ %p' | sort -n |
tail -n1) then set latest_spec="$(dirname "<that-most-recent-path>")"; ensure
the code references the existing variables design_file, latest_spec, and
spec_root so behavior and variable names remain consistent.
| function getSpecsDirs(cwd: string): string[] { | ||
| const settingsPath = path.join(cwd, ".claude", "ralph-specum.local.md"); | ||
|
|
||
| if (!fs.existsSync(settingsPath)) { | ||
| return [DEFAULT_SPECS_DIR]; | ||
| } | ||
|
|
||
| try { | ||
| const content = fs.readFileSync(settingsPath, "utf-8"); | ||
| // Extract specs_dirs from YAML frontmatter | ||
| const frontmatter = content.match(/^---\n([\s\S]*?)\n---/); | ||
| if (!frontmatter) return [DEFAULT_SPECS_DIR]; | ||
|
|
||
| const match = frontmatter[1].match(/^specs_dirs:\s*\[([^\]]*)\]/m); | ||
| if (!match) return [DEFAULT_SPECS_DIR]; | ||
|
|
||
| const dirs = match[1] | ||
| .split(",") | ||
| .map((d) => d.trim().replace(/^["']|["']$/g, "")) | ||
| .filter(Boolean); | ||
|
|
||
| return dirs.length > 0 ? dirs : [DEFAULT_SPECS_DIR]; | ||
| } catch { | ||
| return [DEFAULT_SPECS_DIR]; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if ralph-config.schema.json exists and its content
fd "ralph-config" --type fRepository: tzachbon/smart-ralph
Length of output: 104
🏁 Script executed:
# Check what config paths the OpenCode adapter and other adapters use
rg -n "\.claude/" adapters/ --type ts --type sh -A 2 -B 2Repository: tzachbon/smart-ralph
Length of output: 46
🏁 Script executed:
# Search for how other adapters load configuration
rg -n "config|Config" adapters/ --type ts | head -30Repository: tzachbon/smart-ralph
Length of output: 235
🏁 Script executed:
# Check the actual execution-loop.ts file around lines 67-92 and line 157
fd "execution-loop.ts" --type f -x cat -n {}Repository: tzachbon/smart-ralph
Length of output: 13523
🏁 Script executed:
# Read the ralph-config.schema.json to see what fields it defines
cat adapters/config/ralph-config.schema.jsonRepository: tzachbon/smart-ralph
Length of output: 3785
🏁 Script executed:
# Check how other adapters load configuration
find adapters -name "*.ts" -o -name "*.js" | head -5 | xargs ls -laRepository: tzachbon/smart-ralph
Length of output: 155
🏁 Script executed:
# Search for references to .opencode/ or opencode-specific config paths
rg "opencode|\.opencode" --type ts --type jsRepository: tzachbon/smart-ralph
Length of output: 556
🏁 Script executed:
# Check what config is actually used by looking at adapter index or main files
find adapters -name "index.ts" -o -name "index.js" -o -name "*adapter*" | xargs cat | head -100Repository: tzachbon/smart-ralph
Length of output: 93
🏁 Script executed:
# Search for where ralph-config.json is loaded or referenced
rg "ralph-config" --type ts --type js --type json -A 3 -B 1Repository: tzachbon/smart-ralph
Length of output: 627
🏁 Script executed:
# Check if other adapters exist and how they handle configuration
find adapters -type f -name "*.ts" -o -name "*.js" | xargs ls -laRepository: tzachbon/smart-ralph
Length of output: 155
🏁 Script executed:
# Search for references to .claude/ in the entire repo to understand its scope
rg "\.claude" -g "!node_modules" -A 2 -B 2Repository: tzachbon/smart-ralph
Length of output: 50378
🏁 Script executed:
# Check Claude Code plugin configuration to confirm .claude/ is Claude-specific
find . -path "./plugins" -o -path "./plugins/*" | head -20Repository: tzachbon/smart-ralph
Length of output: 1026
OpenCode adapter reads settings from Claude Code-specific path (.claude/).
getSpecsDirs() (lines 67–92) and isPluginDisabled() (line 157) both read configuration from .claude/ralph-specum.local.md. This is a Claude Code plugin convention. An OpenCode adapter must not depend on Claude Code configuration paths—it should read from either tool-agnostic config (ralph-config.json at project root, defining spec_dirs) or OpenCode-specific config (e.g., .opencode/ or a tool-specific section). As written, the adapter only works if users have Claude Code configured alongside OpenCode.
🤖 Prompt for AI Agents
In `@adapters/opencode/hooks/execution-loop.ts` around lines 67 - 92,
getSpecsDirs() and isPluginDisabled() currently read Claude Code-specific
".claude/ralph-specum.local.md" which is wrong for the OpenCode adapter; update
both functions to prefer a tool-agnostic project config (ralph-config.json at
repo root with a "spec_dirs" array) and then fall back to an OpenCode-specific
config (e.g., ".opencode/ralph-config.json" or a ".opencode" frontmatter file)
before using DEFAULT_SPECS_DIR; specifically, modify getSpecsDirs to: 1) attempt
to read and parse ralph-config.json in cwd and return its spec_dirs if present
and valid, 2) if not present, attempt to read an ".opencode" config (either JSON
or frontmatter) for spec dirs, 3) only if both are missing fallback to
DEFAULT_SPECS_DIR; similarly update isPluginDisabled to check ralph-config.json
and .opencode config for the plugin disabled flag instead of
".claude/ralph-specum.local.md", and keep existing file-parsing error handling
and trimming logic while removing any dependency on
".claude/ralph-specum.local.md".
| { | ||
| "name": "opencode-codex-support", | ||
| "path": "./specs/opencode-codex-support", | ||
| "phase": "tasks" | ||
| }, |
There was a problem hiding this comment.
opencode-codex-support entry is missing taskIndex and totalTasks.
Other specs in the "tasks" phase (e.g., implement-ralph-wiggum, speckit-stop-hook, reality-verification-principle) all include taskIndex and totalTasks. This entry should be consistent.
Proposed fix
{
"name": "opencode-codex-support",
"path": "./specs/opencode-codex-support",
- "phase": "tasks"
+ "phase": "tasks",
+ "taskIndex": <current>,
+ "totalTasks": <total>
},🤖 Prompt for AI Agents
In `@specs/.index/index-state.json` around lines 19 - 23, The
"opencode-codex-support" tasks entry is missing the taskIndex and totalTasks
fields; update the JSON object for the entry with name "opencode-codex-support"
to include the same task indexing fields used by other "tasks" phase entries
(add "taskIndex": <number> and "totalTasks": <number> with the appropriate
values consistent with the rest of the phase), ensuring the object structure
matches the other entries (e.g., those for "implement-ralph-wiggum",
"speckit-stop-hook", "reality-verification-principle").
| | Spec | Directory | Phase | Status | | ||
| |------|-----------|-------|--------| | ||
| | add-skills-doc | ./specs | completed | done | | ||
| | opencode-codex-support | ./specs | tasks | | |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there's a .ralph-state.json for this spec that might indicate actual phase
echo "=== Checking .ralph-state.json ==="
find specs/opencode-codex-support -name ".ralph-state.json" 2>/dev/null
if [ -f "specs/opencode-codex-support/.ralph-state.json" ]; then
echo "Content of .ralph-state.json:"
cat specs/opencode-codex-support/.ralph-state.json
fi
# Check the tasks.md to see if all tasks are checked
echo ""
echo "=== Checking task completion ==="
if [ -f "specs/opencode-codex-support/tasks.md" ]; then
echo "Unchecked tasks:"
grep -c '^\- \[ \]' specs/opencode-codex-support/tasks.md || echo "0"
echo "Checked tasks:"
grep -c '^\- \[x\]' specs/opencode-codex-support/tasks.md || echo "0"
else
echo "tasks.md not found"
fi
# Check .progress.md if it exists
echo ""
echo "=== Checking .progress.md ==="
if [ -f "specs/opencode-codex-support/.progress.md" ]; then
head -50 specs/opencode-codex-support/.progress.md
else
echo ".progress.md not found"
fi
# Check the index.md itself to understand structure
echo ""
echo "=== Checking index.md structure ==="
head -30 specs/.index/index.mdRepository: tzachbon/smart-ralph
Length of output: 4804
Spec opencode-codex-support status is missing task count in the index.
The entry shows phase: tasks with an empty status column, while other in-progress specs display task counts (e.g., 27/35 tasks, 9/11 tasks). The spec currently has 16/20 tasks completed with 4 remaining. The index generator should compute and display the task count for this spec's status column.
🤖 Prompt for AI Agents
In `@specs/.index/index.md` at line 19, The index entry for spec
"opencode-codex-support" is missing the task-count in the status column; update
the index generator logic that builds the index row for specs (the code that
formats spec rows and the renderer that produces the "| opencode-codex-support |
./specs | tasks | |" line) to compute completed/total tasks (16/20) and render
it as "16/20 tasks" in the status column; specifically, locate the function that
assembles spec metadata (the spec row formatter / index row renderer) and add a
calculation using the spec's completedTasks and totalTasks fields, then insert
the formatted string into the fourth column for in-progress specs.
| ## Phase 1: Make It Work (POC) | ||
|
|
||
| Focus: Get SKILL.md portability working end-to-end. Validate that a user can discover and follow the Ralph workflow via SKILL.md files in any tool. | ||
|
|
||
| - [x] 1.1 Audit templates and schemas for Claude Code-specific references | ||
| - **Do**: | ||
| 1. Read all files in `plugins/ralph-specum/templates/` and `plugins/ralph-specum/schemas/spec.schema.json` | ||
| 2. Search for Claude Code-specific references: "Task tool", "AskUserQuestion", "TeamCreate", "SendMessage", "Stop hook", "allowed-tools", "subagent_type", "claude", "plugin.json" | ||
| 3. Document any findings in `.progress.md` | ||
| 4. If templates/schemas are already tool-agnostic, note "no changes needed" | ||
| 5. If changes needed, replace tool-specific references with generic alternatives (e.g., "delegate to subagent" instead of "use Task tool") | ||
| - **Files**: `plugins/ralph-specum/templates/*.md`, `plugins/ralph-specum/schemas/spec.schema.json` | ||
| - **Done when**: All templates and schemas contain zero Claude Code-specific tool references | ||
| - **Verify**: `grep -rn "Task tool\|AskUserQuestion\|TeamCreate\|SendMessage\|Stop hook\|subagent_type\|allowed-tools" plugins/ralph-specum/templates/ plugins/ralph-specum/schemas/ | grep -v "^Binary" | wc -l` returns 0 | ||
| - **Commit**: `feat(portability): audit and clean templates for cross-tool compatibility` | ||
| - _Requirements: FR-4, AC-2.1_ | ||
| - _Design: Component B_ | ||
|
|
||
| - [x] 1.2 Create start SKILL.md | ||
| - **Do**: | ||
| 1. Create directory `plugins/ralph-specum/skills/workflow/start/` | ||
| 2. Create `SKILL.md` with progressive disclosure: | ||
| - Level 1: Overview of what start does (detect new vs resume, create spec directory, initialize state) | ||
| - Level 2: Step-by-step instructions (parse name/goal, create directory, write .ralph-state.json, write .progress.md, set .current-spec) | ||
| - Level 3: Advanced options (--quick mode, --fresh, --specs-dir, --commit-spec) | ||
| 3. Use tool-agnostic language throughout (no "Task tool", no "AskUserQuestion") | ||
| 4. Include state file format documentation inline | ||
| 5. Reference spec workflow phases: research -> requirements -> design -> tasks -> implement | ||
| - **Files**: `plugins/ralph-specum/skills/workflow/start/SKILL.md` | ||
| - **Done when**: SKILL.md exists with all 3 disclosure levels, zero Claude Code-specific references | ||
| - **Verify**: `test -f plugins/ralph-specum/skills/workflow/start/SKILL.md && ! grep -q "Task tool\|AskUserQuestion\|TeamCreate\|allowed-tools" plugins/ralph-specum/skills/workflow/start/SKILL.md && echo "PASS"` | ||
| - **Commit**: `feat(skills): add universal start SKILL.md for cross-tool workflow` | ||
| - _Requirements: FR-1, FR-2, AC-1.1, AC-1.2, AC-1.3_ | ||
| - _Design: Component A_ | ||
|
|
||
| - [x] 1.3 Create research SKILL.md | ||
| - **Do**: | ||
| 1. Create directory `plugins/ralph-specum/skills/workflow/research/` | ||
| 2. Create `SKILL.md` with progressive disclosure: | ||
| - Level 1: Purpose of research phase (codebase analysis, external research, feasibility) | ||
| - Level 2: Steps (read goal from .progress.md, explore codebase, search web, write research.md, update state) | ||
| - Level 3: Parallel research notes (tool-specific), output format, research.md template | ||
| 3. Tool-agnostic delegation language: "explore codebase" not "use Explore subagent" | ||
| - **Files**: `plugins/ralph-specum/skills/workflow/research/SKILL.md` | ||
| - **Done when**: SKILL.md provides complete research phase guidance | ||
| - **Verify**: `test -f plugins/ralph-specum/skills/workflow/research/SKILL.md && echo "PASS"` | ||
| - **Commit**: `feat(skills): add universal research SKILL.md` | ||
| - _Requirements: FR-1, FR-2, AC-1.1_ | ||
| - _Design: Component A_ | ||
|
|
||
| - [x] 1.4 Create requirements, design, and tasks SKILL.md files | ||
| - **Do**: | ||
| 1. Create directories: `plugins/ralph-specum/skills/workflow/requirements/`, `design/`, `tasks/` | ||
| 2. Create each SKILL.md with progressive disclosure following the same pattern as research: | ||
| - **requirements/SKILL.md**: User stories, acceptance criteria, FR/NFR tables. Reference requirements.md template. | ||
| - **design/SKILL.md**: Architecture, components, data flow, technical decisions. Reference design.md template. | ||
| - **tasks/SKILL.md**: POC-first 4-phase breakdown, task format (Do/Files/Done when/Verify/Commit). Reference tasks.md template. | ||
| 3. All must be tool-agnostic | ||
| - **Files**: `plugins/ralph-specum/skills/workflow/requirements/SKILL.md`, `plugins/ralph-specum/skills/workflow/design/SKILL.md`, `plugins/ralph-specum/skills/workflow/tasks/SKILL.md` | ||
| - **Done when**: All three SKILL.md files exist with full progressive disclosure | ||
| - **Verify**: `for d in requirements design tasks; do test -f "plugins/ralph-specum/skills/workflow/$d/SKILL.md" || echo "MISSING: $d"; done && echo "PASS"` | ||
| - **Commit**: `feat(skills): add requirements, design, and tasks SKILL.md files` | ||
| - _Requirements: FR-1, FR-2, AC-1.1_ | ||
| - _Design: Component A_ | ||
|
|
||
| - [x] 1.5 Create implement SKILL.md (with Codex-compatible task progression) | ||
| - **Do**: | ||
| 1. Create directory `plugins/ralph-specum/skills/workflow/implement/` | ||
| 2. Create `SKILL.md` with progressive disclosure: | ||
| - Level 1: Purpose of execution (run tasks from tasks.md sequentially) | ||
| - Level 2: Manual task execution loop: | ||
| a. Read .ralph-state.json for taskIndex and totalTasks | ||
| b. Read tasks.md, find task at taskIndex | ||
| c. Execute Do steps, check Done when criteria | ||
| d. Run Verify command | ||
| e. If pass: mark task [x] in tasks.md, update .progress.md, increment taskIndex in state file | ||
| f. If fail: document error, retry or stop | ||
| g. Repeat until taskIndex >= totalTasks | ||
| h. When complete: delete .ralph-state.json, report ALL_TASKS_COMPLETE | ||
| - Level 3: Tool-specific execution modes: | ||
| - Claude Code: Automatic via stop-hook (hands-free) | ||
| - OpenCode: Automatic via JS/TS hooks | ||
| - Codex CLI: Manual re-invocation per task (re-invoke this skill after each task) | ||
| 3. Include state file update instructions using jq | ||
| 4. This is the most critical SKILL.md -- it enables Codex CLI execution | ||
| - **Files**: `plugins/ralph-specum/skills/workflow/implement/SKILL.md` | ||
| - **Done when**: SKILL.md provides complete execution guidance that works without hooks | ||
| - **Verify**: `test -f plugins/ralph-specum/skills/workflow/implement/SKILL.md && grep -q "taskIndex" plugins/ralph-specum/skills/workflow/implement/SKILL.md && echo "PASS"` | ||
| - **Commit**: `feat(skills): add universal implement SKILL.md with hook-free task progression` | ||
| - _Requirements: FR-1, FR-6, AC-1.1, AC-4.1, AC-4.2, AC-4.3_ | ||
| - _Design: Component A, Component E_ | ||
|
|
||
| - [x] 1.6 Create status and cancel SKILL.md files | ||
| - **Do**: | ||
| 1. Create directories: `plugins/ralph-specum/skills/workflow/status/`, `cancel/` | ||
| 2. **status/SKILL.md**: Read all spec directories, show phase/progress/files for each. List available commands. | ||
| 3. **cancel/SKILL.md**: Delete .ralph-state.json, optionally remove spec directory, clear .current-spec. | ||
| 4. Both must be tool-agnostic and reference path resolution patterns | ||
| - **Files**: `plugins/ralph-specum/skills/workflow/status/SKILL.md`, `plugins/ralph-specum/skills/workflow/cancel/SKILL.md` | ||
| - **Done when**: Both SKILL.md files exist with full guidance | ||
| - **Verify**: `test -f plugins/ralph-specum/skills/workflow/status/SKILL.md && test -f plugins/ralph-specum/skills/workflow/cancel/SKILL.md && echo "PASS"` | ||
| - **Commit**: `feat(skills): add status and cancel SKILL.md files` | ||
| - _Requirements: FR-1, AC-1.1_ | ||
| - _Design: Component A_ | ||
|
|
||
| - [ ] 1.7 [VERIFY] POC Checkpoint -- SKILL.md completeness | ||
| - **Do**: | ||
| 1. Verify all 8 SKILL.md files exist under `plugins/ralph-specum/skills/workflow/` | ||
| 2. Verify zero Claude Code-specific references across all SKILL.md files | ||
| 3. Verify each has name and description frontmatter | ||
| 4. Verify progressive disclosure (check for "## Overview", "## Steps", or equivalent headers) | ||
| 5. Verify templates and schemas are tool-agnostic | ||
| - **Verify**: All commands below must pass: | ||
| - `ls plugins/ralph-specum/skills/workflow/*/SKILL.md | wc -l` returns 8 | ||
| - `grep -rl "Task tool\|AskUserQuestion\|TeamCreate\|allowed-tools\|subagent_type" plugins/ralph-specum/skills/workflow/ | wc -l` returns 0 | ||
| - **Done when**: All 8 SKILL.md files exist, all tool-agnostic, all have progressive disclosure | ||
| - **Commit**: `chore(qa): verify SKILL.md completeness checkpoint` |
There was a problem hiding this comment.
Missing quality checkpoints in Phase 1 — gap of 6 tasks before the first [VERIFY].
Phase 1 has tasks 1.1–1.6 (6 implementation tasks) before the checkpoint at 1.7. The guideline requires a quality checkpoint every 2–3 tasks. Insert a [VERIFY] checkpoint after task 1.3 (e.g., verify the first three SKILL.md files exist and are tool-agnostic) to stay within the required cadence.
Suggested checkpoint insertion after line 59
- _Design: Component A_
+- [ ] 1.3b [VERIFY] Mid-POC checkpoint -- first SKILL.md files
+ - **Do**:
+ 1. Verify start, research SKILL.md files exist
+ 2. Verify zero Claude Code-specific references in created files
+ 3. Verify each has frontmatter and progressive disclosure
+ - **Verify**: `ls plugins/ralph-specum/skills/workflow/{start,research}/SKILL.md | wc -l` returns 2
+ - **Done when**: First SKILL.md files pass tool-agnostic and structure checks
+ - **Commit**: `chore(qa): mid-POC SKILL.md checkpoint`
+
- [x] 1.4 Create requirements, design, and tasks SKILL.md filesAs per coding guidelines, specs/**/tasks.md: "Quality checkpoints must be inserted every 2-3 tasks throughout all phases in task specifications." Based on learnings, "Quality checkpoints must be inserted every 2-3 tasks throughout all phases in task specifications."
🤖 Prompt for AI Agents
In `@specs/opencode-codex-support/tasks.md` around lines 11 - 127, Insert a new
[VERIFY] checkpoint after task 1.3 that validates the first three SKILL.md files
(start, research, requirements) exist under
plugins/ralph-specum/skills/workflow/, contain frontmatter, and are free of
Claude Code-specific references; update the Phase 1 list to include this
checkpoint (e.g., "1.4 [VERIFY] — verify start/research/requirements SKILL.md
existence and tool-agnosticness") and renumber subsequent tasks (design, tasks,
implement, status/cancel) accordingly so every 2–3 tasks has a quality
checkpoint as required by the specs; ensure the verification commands reference
the three paths and grep for the banned tokens ("Task
tool|AskUserQuestion|TeamCreate|allowed-tools|subagent_type") in those files.
| ## Phase 2: Refactoring | ||
|
|
||
| After SKILL.md portability validated, add tool-specific adapters and AGENTS.md generation. | ||
|
|
||
| - [x] 2.1 Create AGENTS.md generator | ||
| - **Do**: | ||
| 1. Add AGENTS.md generation logic to the plan-synthesizer agent or as a standalone script | ||
| 2. The generator reads design.md and extracts: | ||
| - Architecture overview | ||
| - Component responsibilities | ||
| - Technical decisions | ||
| - File structure | ||
| - Existing patterns to follow | ||
| 3. Outputs AGENTS.md with sections: Architecture, Coding Conventions, File Structure, Key Decisions | ||
| 4. Generation is optional (controlled by `--generate-agents` flag or config) | ||
| 5. Place generated AGENTS.md at project root (not inside spec directory) | ||
| - **Files**: `plugins/ralph-specum/scripts/generate-agents-md.sh` | ||
| - **Done when**: Script reads design.md and outputs valid AGENTS.md | ||
| - **Verify**: `bash plugins/ralph-specum/scripts/generate-agents-md.sh --spec-path ./specs/opencode-codex-support && test -f AGENTS.md && echo "PASS"` | ||
| - **Commit**: `feat(agents-md): add AGENTS.md generator from design.md` | ||
| - _Requirements: FR-7, AC-5.1, AC-5.2, AC-5.3_ | ||
| - _Design: Component C_ | ||
|
|
||
| - [x] 2.2 Create OpenCode execution loop adapter | ||
| - **Do**: | ||
| 1. Create `adapters/opencode/` directory structure | ||
| 2. Create `adapters/opencode/hooks/execution-loop.ts`: | ||
| - Reads .ralph-state.json on `session.idle` or `tool.execute.after` events | ||
| - If phase=execution and taskIndex < totalTasks: output continuation prompt | ||
| - If taskIndex >= totalTasks: signal completion | ||
| - Mirrors stop-watcher.sh logic in TypeScript | ||
| 3. Create `adapters/opencode/README.md` with setup instructions: | ||
| - How to register the hook in opencode.json | ||
| - How to configure spec directories | ||
| - Example opencode.json snippet | ||
| - **Files**: `adapters/opencode/hooks/execution-loop.ts`, `adapters/opencode/README.md` | ||
| - **Done when**: TypeScript hook file exists with execution loop logic, README has setup instructions | ||
| - **Verify**: `test -f adapters/opencode/hooks/execution-loop.ts && test -f adapters/opencode/README.md && echo "PASS"` | ||
| - **Commit**: `feat(opencode): add execution loop adapter with JS/TS hooks` | ||
| - _Requirements: FR-5, FR-10, AC-3.1, AC-3.2, AC-3.3, AC-3.4_ | ||
| - _Design: Component D_ | ||
|
|
||
| - [x] 2.3 Create Codex CLI adapter | ||
| - **Do**: | ||
| 1. Create `adapters/codex/` directory structure | ||
| 2. Create `adapters/codex/skills/ralph-implement/SKILL.md`: | ||
| - Enhanced implement skill specifically for Codex (no hooks) | ||
| - Reads .ralph-state.json and shows current task with full context | ||
| - Provides explicit "after completing this task, re-invoke this skill for the next task" | ||
| - Includes state file update instructions (jq commands for incrementing taskIndex) | ||
| 3. Create `adapters/codex/AGENTS.md.template`: | ||
| - Template that can be populated from any spec's design.md | ||
| 4. Create `adapters/codex/README.md`: | ||
| - How to set up Ralph skills in Codex CLI | ||
| - How to place SKILL.md files for discovery | ||
| - Workflow walkthrough | ||
| - **Files**: `adapters/codex/skills/ralph-implement/SKILL.md`, `adapters/codex/AGENTS.md.template`, `adapters/codex/README.md` | ||
| - **Done when**: Codex adapter files exist with complete guidance for hook-free execution | ||
| - **Verify**: `test -f adapters/codex/skills/ralph-implement/SKILL.md && test -f adapters/codex/README.md && echo "PASS"` | ||
| - **Commit**: `feat(codex): add Codex CLI adapter with SKILL.md-based execution` | ||
| - _Requirements: FR-6, FR-10, AC-4.1, AC-4.2, AC-4.3_ | ||
| - _Design: Component E_ | ||
|
|
||
| - [x] 2.4 Create configuration bridge | ||
| - **Do**: | ||
| 1. Create `adapters/config/` directory | ||
| 2. Create `adapters/config/ralph-config.schema.json`: | ||
| - Defines tool-agnostic Ralph settings (spec_dirs, default_branch, commit_spec, max_iterations) | ||
| 3. Create `adapters/config/generate-config.sh`: | ||
| - Reads ralph-config.json from project root | ||
| - Generates Claude Code config (validates existing .claude-plugin/ is compatible) | ||
| - Generates OpenCode config (opencode.json plugin entry, .opencode/ directory) | ||
| - Generates Codex config (copies skills, generates AGENTS.md) | ||
| 4. Create `adapters/config/README.md` with usage instructions | ||
| - **Files**: `adapters/config/ralph-config.schema.json`, `adapters/config/generate-config.sh`, `adapters/config/README.md` | ||
| - **Done when**: Config schema defined, generator script creates tool-specific configs | ||
| - **Verify**: `test -f adapters/config/ralph-config.schema.json && test -f adapters/config/generate-config.sh && echo "PASS"` | ||
| - **Commit**: `feat(config): add configuration bridge for multi-tool setup` | ||
| - _Requirements: FR-8, AC-6.1, AC-6.2, AC-6.3, AC-6.4_ | ||
| - _Design: Component F_ | ||
|
|
||
| - [ ] 2.5 [VERIFY] Quality checkpoint -- Adapters and generators | ||
| - **Do**: | ||
| 1. Verify all adapter directories exist (opencode, codex, config) | ||
| 2. Verify AGENTS.md generator works on existing spec | ||
| 3. Verify OpenCode adapter has valid TypeScript | ||
| 4. Verify Codex adapter SKILL.md has task progression guidance | ||
| 5. Verify existing Claude Code plugin is unchanged (compare against HEAD~N) | ||
| - **Verify**: All commands below must pass: | ||
| - `test -d adapters/opencode && test -d adapters/codex && test -d adapters/config` | ||
| - `git diff HEAD -- plugins/ralph-specum/.claude-plugin/plugin.json plugins/ralph-specum/hooks/ | wc -l` returns 0 (no changes to existing plugin core) | ||
| - **Done when**: All adapters exist, Claude Code plugin unchanged | ||
| - **Commit**: `chore(qa): verify adapters and zero-regression checkpoint` |
There was a problem hiding this comment.
Missing quality checkpoint in Phase 2 — gap of 4 tasks before [VERIFY].
Phase 2 has tasks 2.1–2.4 (4 implementation tasks) before the checkpoint at 2.5. Insert a [VERIFY] checkpoint after task 2.2 to comply with the every-2–3-task checkpoint cadence.
Suggested checkpoint insertion after line 169
- _Design: Component D_
+- [ ] 2.2b [VERIFY] Mid-refactor checkpoint -- AGENTS.md and OpenCode adapter
+ - **Do**:
+ 1. Verify AGENTS.md generator produces valid output
+ 2. Verify OpenCode adapter has valid TypeScript structure
+ - **Verify**: `test -f adapters/opencode/hooks/execution-loop.ts && bash plugins/ralph-specum/scripts/generate-agents-md.sh --spec-path ./specs/opencode-codex-support && test -f AGENTS.md && echo "PASS"`
+ - **Done when**: Generator and OpenCode adapter verified
+ - **Commit**: `chore(qa): mid-refactor AGENTS.md and adapter checkpoint`
+
- [x] 2.3 Create Codex CLI adapterAs per coding guidelines, specs/**/tasks.md: "Quality checkpoints must be inserted every 2-3 tasks throughout all phases in task specifications."
🤖 Prompt for AI Agents
In `@specs/opencode-codex-support/tasks.md` around lines 129 - 221, Phase 2 lacks
a mid-phase quality checkpoint: insert a new "[VERIFY]" checkpoint immediately
after task 2.2 (after the "Create OpenCode execution loop adapter" block) that
validates the state of the work so far; the checkpoint should confirm
adapters/opencode directory exists, adapters/opencode/hooks/execution-loop.ts is
present and TypeScript-valid, adapters/opencode/README.md exists, and that the
AGENTS.md generator script (plugins/ralph-specum/scripts/generate-agents-md.sh)
can read design.md and produce AGENTS.md (or at minimum the generator file
exists and is executable); label it as a verification task consistent with
existing 2.5 format and update the checklist to include this new verify item.
| TOOL_KEYWORDS="Task tool|AskUserQuestion|TeamCreate|SendMessage|Stop hook|allowed-tools|subagent_type" | ||
|
|
||
| TEMPLATE_MATCHES=$(grep -rn "$TOOL_KEYWORDS" "$TEMPLATE_DIR"/ 2>/dev/null || true) | ||
| if [ -z "$TEMPLATE_MATCHES" ]; then | ||
| pass "Templates contain zero tool-specific references" | ||
| else | ||
| fail "Templates contain tool-specific references:" | ||
| echo "$TEMPLATE_MATCHES" | while IFS= read -r line; do | ||
| echo " $line" | ||
| done | ||
| fi |
There was a problem hiding this comment.
grep without -E treats | as literal — the aggregate tool-keyword check always passes.
TOOL_KEYWORDS uses | as an OR separator, but grep -rn (line 39) is basic regex, where | is a literal character. This means the search looks for the full string "Task tool|AskUserQuestion|..." verbatim and will never match individual keywords. The test silently passes even when tool-specific references exist.
The per-template check on line 59 correctly uses \| (basic regex alternation in GNU grep), so it would catch issues — but the aggregate check on line 39 is dead.
🐛 Proposed fix: add -E flag for extended regex
-TEMPLATE_MATCHES=$(grep -rn "$TOOL_KEYWORDS" "$TEMPLATE_DIR"/ 2>/dev/null || true)
+TEMPLATE_MATCHES=$(grep -rn -E "$TOOL_KEYWORDS" "$TEMPLATE_DIR"/ 2>/dev/null || true)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| TOOL_KEYWORDS="Task tool|AskUserQuestion|TeamCreate|SendMessage|Stop hook|allowed-tools|subagent_type" | |
| TEMPLATE_MATCHES=$(grep -rn "$TOOL_KEYWORDS" "$TEMPLATE_DIR"/ 2>/dev/null || true) | |
| if [ -z "$TEMPLATE_MATCHES" ]; then | |
| pass "Templates contain zero tool-specific references" | |
| else | |
| fail "Templates contain tool-specific references:" | |
| echo "$TEMPLATE_MATCHES" | while IFS= read -r line; do | |
| echo " $line" | |
| done | |
| fi | |
| TOOL_KEYWORDS="Task tool|AskUserQuestion|TeamCreate|SendMessage|Stop hook|allowed-tools|subagent_type" | |
| TEMPLATE_MATCHES=$(grep -rn -E "$TOOL_KEYWORDS" "$TEMPLATE_DIR"/ 2>/dev/null || true) | |
| if [ -z "$TEMPLATE_MATCHES" ]; then | |
| pass "Templates contain zero tool-specific references" | |
| else | |
| fail "Templates contain tool-specific references:" | |
| echo "$TEMPLATE_MATCHES" | while IFS= read -r line; do | |
| echo " $line" | |
| done | |
| fi |
🤖 Prompt for AI Agents
In `@tests/test-artifact-portability.sh` around lines 37 - 47, The aggregate
search uses grep -rn with TOOL_KEYWORDS containing '|' but without extended
regex, so the alternation is treated literally; update the invocation that sets
TEMPLATE_MATCHES (the grep command referencing "$TOOL_KEYWORDS" and
"$TEMPLATE_DIR") to use extended regex (add -E) so the '|' separators work as
intended and the aggregate check actually finds any of the TOOL_KEYWORDS; ensure
you keep the existing 2>/dev/null || true behavior and the variable names
TEMPLATE_MATCHES, TOOL_KEYWORDS, and TEMPLATE_DIR unchanged.
1. Fix GitHub URL typo (zachbonfil → tzachbon) in AGENTS.md.template 2. Fix double backticks in Codex implement SKILL.md 3. Remove unused variables (PROJECT_ROOT, COMMIT_SPEC, MAX_ITERATIONS, MAX_TASK_ITERATIONS) and duplicate cfg_bool function from generate-config.sh 4. OpenCode adapter now reads ralph-config.json first (tool-agnostic), falls back to .claude/ralph-specum.local.md 5. Add speculative API note to OpenCode adapter header comment 6. Add "Keeping Config in Sync" section to config bridge README 7. Gitignore AGENTS.md (generated file, should not be tracked) 8. Remove duplicate cfg_bool, use cfg everywhere 9. Make test scripts executable 10. Settings template now shows all three tool config paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@adapters/config/generate-config.sh`:
- Around line 57-75: The --config handling in generate-config.sh assumes $2
exists and will fail under set -u if --config is the last arg; before assigning
CONFIG_PATH="$2" in the case branch for --config, add a guard to verify a next
argument exists and is not another option (check that $# -ge 2 and that $2 does
not start with '-') and if the check fails call usage or log_err and exit;
update the --config case around the assignment to CONFIG_PATH and the shift 2 to
only run after the validation.
🧹 Nitpick comments (2)
adapters/config/generate-config.sh (1)
25-25:SCRIPT_DIRis assigned but never referenced.Shellcheck flags this (SC2034). Remove the assignment or use it (e.g., to resolve paths to sibling scripts like
generate-agents-md.sh).adapters/codex/skills/ralph-implement/SKILL.md (1)
153-156: Hardcoded/tmp/ralph-state.jsonin jq examples risks collision.If two specs are processed concurrently (or another user runs the same flow), the shared temp path causes a race. Consider using
mktempor a spec-scoped temp path in the documented patterns.♻️ Suggested pattern
-jq '.taskIteration += 1 | .globalIteration += 1' \ - "$SPEC_DIR/.ralph-state.json" > /tmp/ralph-state.json && \ - mv /tmp/ralph-state.json "$SPEC_DIR/.ralph-state.json" +TMP=$(mktemp) +jq '.taskIteration += 1 | .globalIteration += 1' \ + "$SPEC_DIR/.ralph-state.json" > "$TMP" && \ + mv "$TMP" "$SPEC_DIR/.ralph-state.json"This pattern appears in multiple places (lines 154, 196, 255, 262).
| while [ $# -gt 0 ]; do | ||
| case "$1" in | ||
| --config) | ||
| CONFIG_PATH="$2" | ||
| shift 2 | ||
| ;; | ||
| --dry-run) | ||
| DRY_RUN=true | ||
| shift | ||
| ;; | ||
| --help) | ||
| usage | ||
| ;; | ||
| *) | ||
| log_err "Unknown option: $1" | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| done |
There was a problem hiding this comment.
--config without a following argument will crash under set -u.
If --config is passed as the last argument, $2 is unbound and the script errors out with an unhelpful message. Guard with a check before accessing $2.
🛡️ Proposed fix
--config)
+ if [ $# -lt 2 ]; then
+ log_err "--config requires a path argument"
+ exit 1
+ fi
CONFIG_PATH="$2"
shift 2
;;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| while [ $# -gt 0 ]; do | |
| case "$1" in | |
| --config) | |
| CONFIG_PATH="$2" | |
| shift 2 | |
| ;; | |
| --dry-run) | |
| DRY_RUN=true | |
| shift | |
| ;; | |
| --help) | |
| usage | |
| ;; | |
| *) | |
| log_err "Unknown option: $1" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| while [ $# -gt 0 ]; do | |
| case "$1" in | |
| --config) | |
| if [ $# -lt 2 ]; then | |
| log_err "--config requires a path argument" | |
| exit 1 | |
| fi | |
| CONFIG_PATH="$2" | |
| shift 2 | |
| ;; | |
| --dry-run) | |
| DRY_RUN=true | |
| shift | |
| ;; | |
| --help) | |
| usage | |
| ;; | |
| *) | |
| log_err "Unknown option: $1" | |
| exit 1 | |
| ;; | |
| esac | |
| done |
🤖 Prompt for AI Agents
In `@adapters/config/generate-config.sh` around lines 57 - 75, The --config
handling in generate-config.sh assumes $2 exists and will fail under set -u if
--config is the last arg; before assigning CONFIG_PATH="$2" in the case branch
for --config, add a guard to verify a next argument exists and is not another
option (check that $# -ge 2 and that $2 does not start with '-') and if the
check fails call usage or log_err and exit; update the --config case around the
assignment to CONFIG_PATH and the shift 2 to only run after the validation.
Summary
Tool Support Matrix
Test Plan
Summary by CodeRabbit
Release Notes
New Features
Documentation
Tests
Chores