Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "ralphex",
"interface": {
"displayName": "Ralphex"
},
"plugins": [
{
"name": "ralphex",
"source": {
"source": "local",
"path": "./plugins/ralphex"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
2 changes: 1 addition & 1 deletion .claude-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Users can install via the plugin marketplace:

## Versioning

The `version` field in both JSON files is automatically updated during releases by `scripts/internal/update-plugin-version.sh`, triggered by goreleaser.
The plugin version is independent from the ralphex CLI version. When distributed skill payload changes, maintainers bump all Claude, Codex, and portable manifests together with `make bump-plugin-version VERSION=<version>` before merging. Releases do not mutate plugin manifests.

## Marketplace Structure

Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "ralphex",
"source": "./",
"description": "Autonomous plan execution with Claude Code - task execution, monitoring, and plan creation",
"version": "0.20.0",
"version": "0.20.2",
"author": {
"name": "umputun"
}
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ralphex",
"version": "0.20.0",
"version": "0.20.2",
"description": "Autonomous plan execution with Claude Code - task execution, monitoring, and plan creation",
"author": {
"name": "umputun",
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
- name: checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false

- name: set up go 1.26
Expand All @@ -25,6 +26,16 @@ jobs:
go-version: "1.26"
id: go

- name: set up uv
uses: astral-sh/setup-uv@v9.0.0
with:
version: "0.12.3"

- name: validate portable plugin and version policy
run: make test-plugin PLUGIN_VERSION_BASE="$PLUGIN_VERSION_BASE"
env:
PLUGIN_VERSION_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}

- name: build and test
run: |
go test -race -timeout=100s -covermode=atomic -coverprofile=$GITHUB_WORKSPACE/profile.cov_tmp ./...
Expand Down
4 changes: 0 additions & 4 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
version: 2

before:
hooks:
- ./scripts/internal/update-plugin-version.sh {{ .Tag }}

builds:
- id: ralphex
main: ./cmd/ralphex
Expand Down
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ Autonomous plan execution with Claude Code - Go rewrite of ralph.py.

## LLM Documentation

See @llms.txt for usage instructions and Claude Code integration commands.
See @llms.txt for usage instructions and Claude Code/Codex integration commands.

## Build Commands

```bash
make build # build binary to .bin/ralphex
make test # run tests with coverage
make test-plugin # validate portable Codex plugin (requires uv; resolves PyYAML)
make lint # run golangci-lint
make fmt # format code
```
Expand All @@ -33,6 +34,7 @@ pkg/config/ # configuration loading, defaults, prompts, agents
pkg/executor/ # claude and codex CLI execution
pkg/git/ # git operations (external git CLI)
pkg/input/ # terminal input collector (fzf/fallback, draft review)
plugins/ralphex/ # portable Codex plugin and workflow skills
pkg/notify/ # notification delivery (telegram, email, slack, webhook, custom)
pkg/plan/ # plan file selection, parsing, and manipulation
pkg/processor/ # pipeline coordinator, prompt rendering, executor policy, signal wrappers
Expand Down Expand Up @@ -508,7 +510,7 @@ If you're an AI agent preparing a contribution, complete this checklist:

## Workflow Rules

- **Plugin version**: bump `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` versions on release if skill files (`assets/claude/`) changed since last plugin version bump
- **Plugin version**: bump `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `plugins/ralphex/.codex-plugin/plugin.json`, and `plugins/ralphex/plugin.json` on release if skill files under `assets/claude/` or `plugins/ralphex/skills/` changed since the last plugin version bump
- **CHANGELOG**: Never modify during development - updates are part of release process only
- **Version sections**: Never add entries to existing version sections - versions are immutable once released
- **Linter warnings**: Add exclusions to `.golangci.yml` instead of `_, _ =` prefixes for fmt.Fprintf/Fprintln
Expand Down
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ test:
go tool cover -func=coverage_no_mocks.out
rm coverage.out coverage_no_mocks.out

test-plugin:
uv run --with pyyaml==6.0.3 python scripts/validate-portable-plugin.py
uv run --with pyyaml==6.0.3 python scripts/validate-portable-plugin_test.py
python3 scripts/validate-skill-contracts_test.py
python3 scripts/internal/check-plugin-version_test.py
./scripts/internal/update-plugin-version_test.sh
python3 scripts/internal/check-plugin-version.py --base "$(PLUGIN_VERSION_BASE)"

bump-plugin-version:
@test -n "$(VERSION)" || (echo "Usage: make bump-plugin-version VERSION=<version>" >&2; exit 1)
./scripts/internal/update-plugin-version.sh "$(VERSION)"

lint:
golangci-lint run --max-issues-per-linter=0 --max-same-issues=0

Expand Down Expand Up @@ -102,4 +114,4 @@ docker-build-go: docker-build
docker-run:
./scripts/ralphex-dk.sh $(ARGS)

.PHONY: all build test lint fmt race version e2e-setup e2e e2e-ui e2e-prep e2e-review e2e-codex prep_site docker-build docker-build-go docker-run
.PHONY: all build test test-plugin bump-plugin-version lint fmt race version e2e-setup e2e e2e-ui e2e-prep e2e-review e2e-codex prep_site docker-build docker-build-go docker-run
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,22 @@ The `/ralphex` command runs ralphex in the background and provides status update

> **Note:** ralphex automatically strips the `CLAUDECODE` env var from child processes, allowing it to run from inside Claude Code. However, running from a standalone terminal is still recommended for the best experience. If the nested session error is somehow encountered, ralphex detects it via error pattern matching and exits gracefully.

## Codex Integration (Optional)

Ralphex also provides a skills-only Codex plugin. The standalone CLI behavior is unchanged.

```bash
codex plugin marketplace add umputun/ralphex
codex plugin add ralphex@ralphex
```

After installation, start a new Codex conversation and use `/skills` to discover the four workflows:

- `$ralphex` launches and monitors an existing plan.
- `$ralphex-plan` creates a structured implementation plan.
- `$ralphex-adopt` converts an existing specification or task list into a Ralphex plan.
- `$ralphex-update` merges updated defaults into customized Ralphex configuration.

## For LLMs

See [llms.txt](llms.txt) for LLM-optimized documentation.
Expand Down
3 changes: 2 additions & 1 deletion assets/claude/skills/ralphex-adopt/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: ralphex-adopt
description: Convert plans from various source formats (OpenSpec, spec-kit, GitHub/GitLab issues with checklists, generic task-lists, free-form markdown) into ralphex-format plans in docs/plans/. Triggers on "ralphex-adopt", "adopt plan", "convert plan to ralphex", "import plan as ralphex".
allowed-tools: [Bash, Read, Write, Glob, Grep, AskUserQuestion]
allowed-tools: Bash Read Write Glob Grep AskUserQuestion
---

# ralphex-adopt - Convert Plans Into ralphex Format
Expand Down
1 change: 1 addition & 0 deletions assets/claude/skills/ralphex-plan/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
name: ralphex-plan
description: Create structured implementation plan in docs/plans/
---

Expand Down
3 changes: 2 additions & 1 deletion assets/claude/skills/ralphex-update/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: ralphex-update
description: Smart-merge updated ralphex defaults into customized prompts/agents
allowed-tools: [Bash, Read, Write, Glob, AskUserQuestion]
allowed-tools: Bash Read Write Glob AskUserQuestion
---

# ralphex-update - Smart Prompt Merging
Expand Down
118 changes: 88 additions & 30 deletions assets/claude/skills/ralphex/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
name: ralphex
description: Run ralphex autonomous plan execution with progress monitoring
argument-hint: 'optional plan file path'
allowed-tools: [Bash, Read, AskUserQuestion, TaskOutput, Glob]
allowed-tools: Bash Read AskUserQuestion TaskOutput Glob
---

# ralphex - Autonomous Plan Execution
Expand All @@ -27,33 +28,52 @@ Use AskUserQuestion to confirm installation method, then guide through it. **Do
## Step 1: Check for Plan Argument

Check `$ARGUMENTS` for optional plan file path:
- if argument provided: validate file exists using Read tool, skip plan selection in Step 3
- if no argument: will ask for plan selection in Step 3
- if argument provided: validate file exists using Read tool, skip plan selection in Step 4
- if no argument: will ask for plan selection in Step 4

## Step 2: Ask Execution Mode
Treat the selected plan as a path, never as an option. If its text begins with `-`, resolve it to an absolute path with safely quoted Bash and validate that exact resolved file again. If safe normalization is unavailable or ambiguous, reject the path and ask for an explicit `./...` or absolute path.

## Step 2: Ask Executor

Use AskUserQuestion:
- header: "Executor"
- question: "Which executor should ralphex use?"
- options:
- label: "Configured (Recommended)"
description: "Use ralphex's effective config; Claude Code is the default when executor is unset"
- label: "Codex"
description: "Add --codex; codex runs tasks, internal reviews, and finalize while external review is skipped"

`--codex` is the first-class Codex executor flag. It is not the same as the deprecated `--codex-only` alias for `--external-only`. Never combine `--codex` with `--external-only` or `--codex-only`.

## Step 3: Ask Execution Mode

Use AskUserQuestion:
- header: "Mode"
- question: "Which execution mode should ralphex use?"
- options:
- label: "Full (Recommended)"
description: "Task execution + Claude review + Codex loop + final Claude review"
- label: "Review"
description: "Skip tasks, run full review pipeline (Claude + Codex + Claude)"
- label: "Codex-only"
description: "Skip tasks and first Claude review, run only Codex loop"
- label: "Full pipeline (Recommended)"
description: "Run tasks, internal reviews, and finalize; configured external review runs only with a non-Codex executor"
- label: "Review pipeline"
description: "Add --review; review current-branch changes and allow agents to fix and commit findings"
- label: "External review"
description: "Add --external-only; skip tasks and first internal review, then fix findings and run the post-external review"

If Codex executor was selected, do not offer "External review" because first-class `--codex` skips that phase and the flags are incompatible. Offer only "Full pipeline" and "Review pipeline".

If "Configured" executor and "External review" are selected, inspect the effective ralphex config before proceeding. Respect `RALPHEX_CONFIG_DIR` when set and local `.ralphex/config` overrides. If the effective config contains `executor = codex`, do not launch an incompatible command; ask the user to choose Full/Review with Codex or change the config explicitly.

## Step 3: Plan Selection (if no argument provided)
## Step 4: Plan Selection (if no argument provided)

**If Full mode selected:**
**If Full pipeline selected:**
- Use Glob: `docs/plans/*.md` (excludes completed/)
- Plan is REQUIRED
- **IMPORTANT**: Glob returns oldest-first, so REVERSE the list to get most recent first
- Build AskUserQuestion with up to 4 most recent plans
- First option (most recent) should have "(Recommended)" suffix
- User MUST select one

**If Review or Codex-only mode selected:**
**If Review pipeline or External review mode selected:**
- Use Glob: `docs/plans/**/*.md` (includes completed/ for context)
- Plan is OPTIONAL
- **IMPORTANT**: Glob returns oldest-first, so REVERSE the list to get most recent first
Expand All @@ -62,7 +82,7 @@ Use AskUserQuestion:
- "None" option description: "Review existing changes without a plan file"
- If user selects "None", run without plan file

## Step 4: Ask Max Iterations
## Step 5: Ask Max Iterations

Use AskUserQuestion:
- header: "Iterations"
Expand All @@ -75,36 +95,74 @@ Use AskUserQuestion:
- label: "100"
description: "Large plans with many tasks"

## Step 5: Launch ralphex in Background
## Step 6: Fail-Closed Launch Preflight

### Repository-local executable overrides (every mode)

Read `.ralphex/config` directly when it exists. Reject the launch if the file is unreadable, malformed, changes while being inspected, or contains any active non-empty assignment for:

- `claude_command`
- `codex_command`
- `custom_review_script`
- `vcs_command`

These values select executables or scripts that the background run would invoke. Do not offer a proceed/override choice. Report the blocking keys and values, then stop. If the file passes, record its content hash (or an equivalent exact-content snapshot); if it is absent, record that exact absence. Use this baseline for the immediate pre-launch revalidation in Step 7.

### Review checkout (Review pipeline and External review only)

Both review modes operate on the current checkout. Their review agents can edit files and create commits while fixing findings. Before any ralphex process starts:

1. Resolve the named current branch with `git symbolic-ref --quiet --short HEAD`. Detached HEAD or an unresolved/empty branch is a hard failure.
2. Require `git status --porcelain=v1` to be empty, including staged, tracked, and untracked changes.
3. Resolve the exact base ref ralphex will use: repo-local `default_branch`, then global ralphex config, then the repository's remote/default-branch evidence. If the effective base is missing, conflicting, or ambiguous, stop.
4. Verify the base resolves to a commit and the current named branch is not that default branch.
5. Require a non-empty committed `base...HEAD` file diff. An uncommitted diff, an ahead commit with no file delta, or an empty diff does not qualify.
6. On any failure, report the observed branch, base, status, and diff condition, then stop. Do not offer "Proceed anyway", switch branches, stash, commit, or modify the checkout.
7. Record the exact branch, resolved base commit, clean-status result, and committed-diff evidence for the immediate pre-launch revalidation in Step 7.

## Step 7: Launch ralphex in Background

Build and run the command:

```bash
ralphex \
[--review] # if user selected "Review" mode
[--codex-only] # if user selected "Codex-only" mode
[--codex] # only if user selected Codex executor
[--review] # only if user selected Review pipeline
[--external-only] # only if user selected External review
[--max-iterations N] # from user selection (25, 50, or 100)
[plan-file] # from argument OR plan selection (omit if "None" selected)
-- '<normalized-plan-file>' # append both only when a plan was selected
```

Run using Bash tool with `run_in_background: true`. **Save the task_id from the response** - needed for status checks later.
The executor and mode flags are alternatives; include only the flags selected above. `--` must immediately precede the positional plan path. Omit both `--` and the plan path when no plan was selected. POSIX-single-quote the normalized plan path, escaping every embedded single quote with the shell sequence `'"'"'`. Never concatenate an unquoted path or use `eval`.

**Determine progress filename** based on mode and plan selection:
- Full mode + plan: `.ralphex/progress/progress-{plan-stem}.txt`
- Review mode + plan: `.ralphex/progress/progress-{plan-stem}-review.txt`
- Codex-only + plan: `.ralphex/progress/progress-{plan-stem}-codex.txt`
- External review + plan: `.ralphex/progress/progress-{plan-stem}-codex.txt`
- Full mode + no plan: `.ralphex/progress/progress.txt`
- Review mode + no plan: `.ralphex/progress/progress-review.txt`
- Codex-only + no plan: `.ralphex/progress/progress-codex.txt`
- External review + no plan: `.ralphex/progress/progress-codex.txt`

Where `{plan-stem}` is the plan filename without extension (e.g., `fix-bugs` from `fix-bugs.md`).

## Step 6: Confirm Launch
Before launch, record whether this progress file exists and capture its current content hash or equivalent file identity/size evidence. Also record the launch time.

Immediately before spawning the process, repeat every applicable Step 6 check:

1. Re-read `.ralphex/config`; require the same safe content/hash and no executable-bearing override.
2. For Review pipeline or External review, require the same named branch and resolved base commit, a still-clean status, and a still-non-empty committed `base...HEAD` diff.
3. If anything changed or cannot be revalidated, stop without launching. Do not reuse the earlier result.

Only after this second gate passes, run using Bash with `run_in_background: true`. **Save the task_id from the response** - needed for status checks later.

## Step 8: Confirm Launch

1. Wait 10-15 seconds for initialization
2. Read last 20 lines of progress file: `tail -20 [progress-filename]`
3. Confirm ralphex started by checking for "Plan:", "Branch:", "Started:" lines
4. Report launch confirmation:
2. Use TaskOutput with `block: false` to read process liveness or completed exit status.
3. Verify the progress file was created or changed after the recorded launch baseline. Read the new/current last 20 lines with safely quoted Bash: `tail -n 20 -- '<progress-filename>'`.
4. Confirm a live launch only when TaskOutput reports it running and the current launch produced fresh progress evidence. Existing `Plan:`, `Branch:`, or `Started:` headers alone are not proof.
5. If it exited non-zero, report launch failure with the exit code and fresh progress tail. If it exited zero before confirmation, report that it already completed rather than saying it is running. If process state or fresh progress cannot be verified, report launch as unconfirmed.
6. For a live confirmed task, report:

```
ralphex started. Task ID: [task_id]
Expand All @@ -115,21 +173,21 @@ Mode: [mode from progress file]
Progress file: [progress-filename]

Manual monitoring:
tail -f [progress-filename] # live stream
tail -50 [progress-filename] # recent activity
tail -f -- '<progress-filename>' # live stream
tail -n 50 -- '<progress-filename>' # recent activity

ralphex runs autonomously (can take hours). Process continues if you close this conversation.
Ask "check ralphex" to get status update.
```

**STOP HERE after reporting launch status. Do not continue monitoring automatically.**

## Step 7: Progress Check (only on explicit user request)
## Step 9: Progress Check (only on explicit user request)

If user explicitly asks "check ralphex", "ralphex status", or "how is ralphex doing":

1. Use TaskOutput tool with `block: false` to check process status (use task_id from Step 5)
2. Read last 40 lines of progress file (use filename from Step 5)
1. Use TaskOutput tool with `block: false` to check process status (use task_id from Step 7)
2. Read last 40 lines of progress file (use filename from Step 7)

**If process still running:**
- Report current phase from progress file:
Expand Down
Loading