readme cleanup

This commit is contained in:
2026-05-31 08:46:59 -04:00
parent 3c01652b90
commit 9f90ed4252
5 changed files with 509 additions and 675 deletions

View File

@@ -54,7 +54,7 @@ Task IDs are zero-padded strings (`"01"`, `"02"`, etc.). The parser prepends `0`
## Command routing ## Command routing
`/ralpi` with no args → plan. First token looks like a path (`@path`, `./path`, `.md`, etc.) → run. Otherwise dispatches to subcommand (`run`, `plan`, `status`, `resume`, `next`, `reset`). `/ralpi` with no args → plan. First token looks like a path (`@path`, `./path`, `.md`, etc.) → run. Otherwise dispatches to subcommand (`run`, `plan`, `resume`, `reset`).
## Config ## Config

117
README.md
View File

@@ -4,75 +4,33 @@ Execute tasks from task files using DAG-based dependency resolution with persist
## Features ## Features
- **DAG-based execution**: Tasks are ordered by dependencies using Kahn's algorithm
- **Parallel batching**: Independent tasks in each batch can run concurrently - **Parallel batching**: Independent tasks in each batch can run concurrently
- **Persistent progress**: Execution state saved to `.ralpi/progress.json` - **Persistent progress**: Execution state saved to `.ralpi/progress.json`
- **Reflection system**: Each task produces a reflection for downstream tasks - **Reflection system**: Each task produces a reflection for downstream tasks
- **Retry with backoff**: Failed tasks retry with exponential backoff - **Retry with backoff**: Failed tasks retry with exponential backoff
- **Multiple formats**: Supports Fio README, simple checkboxes, and YAML - **Multiple formats**: Supports simple checkboxes, and YAML
- **Chat progress**: Real-time progress messages in Pi chat via `pi.sendMessage`
- **Tool usage tracking**: Detects and reports tool usage (read, write, edit, bash) from task execution - **Tool usage tracking**: Detects and reports tool usage (read, write, edit, bash) from task execution
- **Git commit capture**: Captures git commit messages and generates summaries per task
- **Configurable timeouts**: Task-level timeouts via meta blocks, with global fallback - **Configurable timeouts**: Task-level timeouts via meta blocks, with global fallback
- **Session saving**: Saves full task output for expandable session review - **Session saving**: Saves full task output for expandable session review
- **Resume auto-discovery**: Automatically finds and resumes interrupted execution - **Resume auto-discovery**: Automatically finds and resumes interrupted execution
- **Custom message renderer**: Compact UI labels with expandable details in Pi TUI
## Usage ## Usage
``` ```
/ralpi plan [task-file] # Show execution plan /ralpi [task-file] # Execute all tasks
/ralpi run [task-file] # Execute all tasks /ralpi plan # Alias to /task-manager to plan new tasks
/ralpi status [task-file] # Show current progress /ralpi resume # Resume paused execution
/ralpi resume [task-file] # Resume paused execution /ralpi reset [task-file] # Reset progress and .ralpi directory - does not modify PRD
/ralpi next [task-file] # Execute next batch only
/ralpi reset [task-file] # Reset all progress
``` ```
## Task File Formats ## Task File Formats
### Fio README Format ### Highly recommended to use the task-manager prompt for prd construction, it's output pairs perfectly
```markdown
# Project Title # Project Title
## Tasks ## Tasks
- [ ] 01 — Setup project structure -> `tasks/01-setup.md`
- [ ] 02 — Implement auth -> `tasks/02-auth.md`
- [ ] 03 — Build API -> `tasks/03-api.md`
## Dependencies
1 -> 2,3
2 -> 3
```
#### Supported Dependency Formats
The parser supports two dependency declaration styles in the `## Dependencies` section:
**Arrow Notation** (recommended):
```
1 -> 2,3,4
5 -> 6
```
This means: "Task 1 must complete before tasks 2, 3, and 4 can start."
**Natural Language**:
```
13 depends on 17, 18, 19, 20
14 depends on 13, 15, 16
```
This means: "Task 13 depends on tasks 17, 18, 19, and 20."
**Parallel Groups** (informational only):
```
1, 2, 3, 4 can be done in parallel
5, 6, 7, 8 can be done in parallel
```
Note: These lines are ignored by the parser. Use explicit dependencies to control execution order.
### Simple Checkbox Format ### Simple Checkbox Format
```markdown ```markdown
@@ -96,9 +54,45 @@ tasks:
depends_on: ["01"] depends_on: ["01"]
``` ```
## Dependencies
### Arrow Notation (recommended):
1 -> 2,3,4
5 -> 6
This means: "Task 1 must complete before tasks 2, 3, and 4 can start."
### Natural Language:
13 depends on 17, 18, 19, 20
14 depends on 13, 15, 16
This means: "Task 13 depends on tasks 17, 18, 19, and 20."
### Parallel Groups (informational only):
1, 2, 3, 4 can be done in parallel
5, 6, 7, 8 can be done in parallel
Note: These lines are ignored by the parser. Use explicit dependencies to control execution order.
## Configuration ## Configuration
Create config files. Both are optional: ### Task-Level Timeout
You can set a timeout for individual tasks using a meta block in the task file:
```markdown
- [ ] 01: Setup project structure
timeout: 10m
```
Supported formats: `10m` (minutes), `600s` (seconds), `3600000` (milliseconds)
### Config files
| Scope | Path | | Scope | Path |
|-------|------| |-------|------|
@@ -115,9 +109,6 @@ prompts:
projectContext: "Additional context for all tasks" projectContext: "Additional context for all tasks"
``` ```
> ralpi deliberately does **not** set timeouts or retries — those are inherited
> from Pi's own settings. Tasks run until they complete or Pi's own flow stops them.
>
> `execution.models` uses slot-aware round-robin: with 3 models and 2 concurrent > `execution.models` uses slot-aware round-robin: with 3 models and 2 concurrent
> tasks, only the first two models are used. The third model is only touched when > tasks, only the first two models are used. The third model is only touched when
> a third concurrent task starts. Freed model slots are reused before new ones > a third concurrent task starts. Freed model slots are reused before new ones
@@ -127,28 +118,6 @@ prompts:
> the task automatically cycles to the next model in the list without counting it > the task automatically cycles to the next model in the list without counting it
> as a task failure. Each model is tried once before the task is marked as failed. > as a task failure. Each model is tried once before the task is marked as failed.
The keys mirror the nested structure of `RalpiConfig` in `src/types.ts`.
### Precedence (highest wins)
| Priority | Source |
|----------|--------|
| **1st** | In-memory overrides (`model`, `thinkingLevel` from parent Pi session) |
| **2nd** | `./.ralpi/config.yaml` — project-level |
| **3rd** | `~/.pi/ralpi/config.yaml` — global, shared across projects |
| **4th** | `DEFAULT_CONFIG` in `src/types.ts` |
### Task-Level Timeout
You can set a timeout for individual tasks using a meta block in the task file:
```markdown
- [ ] 01: Setup project structure
timeout: 10m
```
Supported formats: `10m` (minutes), `600s` (seconds), `3600000` (milliseconds)
## State Files ## State Files
- `.ralpi/progress.json` - Execution progress - `.ralpi/progress.json` - Execution progress

1034
index.ts

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"task-runner", "task-runner",
"dag", "dag",
"task-manager", "task-manager",
"ralpi-loop", "ralph-loop",
"prd" "prd"
], ],
"author": "Michael Freno", "author": "Michael Freno",
@@ -54,14 +54,6 @@
"@earendil-works/pi-coding-agent": "*", "@earendil-works/pi-coding-agent": "*",
"@earendil-works/pi-tui": "*" "@earendil-works/pi-tui": "*"
}, },
"peerDependenciesMeta": {
"@earendil-works/pi-coding-agent": {
"optional": true
},
"@earendil-works/pi-tui": {
"optional": true
}
},
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },

View File

@@ -296,8 +296,8 @@ export async function runTask(
} }
if (!output.success) { if (!output.success) {
sendChatMessage?.(`${taskHeader}${output.error}`); // Failure reporting is handled by the caller (executeTask) to avoid
ctx.ui.notify(`Task ${task.id} failed: ${output.error}`, "error"); // duplicate messages when model failover or retry cycling is active.
return { return {
success: false, success: false,
error: output.error, error: output.error,
@@ -433,6 +433,7 @@ export async function executeBatch(
roundRobin?.release(task.id); roundRobin?.release(task.id);
const errorMsg = error instanceof Error ? error.message : String(error); const errorMsg = error instanceof Error ? error.message : String(error);
progress.markFailed(task.id, errorMsg); progress.markFailed(task.id, errorMsg);
sendChatMessage?.(`${task.id} · ${task.title}${errorMsg}`);
ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error"); ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error");
break; break;
} }
@@ -555,6 +556,7 @@ async function executeBatchParallel(
roundRobin?.release(task.id); roundRobin?.release(task.id);
const errorMsg = error instanceof Error ? error.message : String(error); const errorMsg = error instanceof Error ? error.message : String(error);
progress.markFailed(task.id, errorMsg); progress.markFailed(task.id, errorMsg);
sendChatMessage?.(`${task.id} · ${task.title}${errorMsg}`);
ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error"); ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error");
}), }),
}); });
@@ -658,9 +660,8 @@ async function executeTask(
// release() would put the slot in freeSlots, then assign() // release() would put the slot in freeSlots, then assign()
// would pick it right back up, getting stuck on the same model. // would pick it right back up, getting stuck on the same model.
modelAttempt++; modelAttempt++;
ctx.ui.notify( sendChatMessage?.(
`Task ${task.id}: model failed, trying next (${modelAttempt + 1}/${maxModelAttempts}): ${result.error}`, `~ ${task.id} · ${task.title} trying model ${modelAttempt + 1}/${maxModelAttempts} (previous: ${result.error})`,
"warning",
); );
break; // exit retry loop, cycle to next model break; // exit retry loop, cycle to next model
} }
@@ -668,9 +669,8 @@ async function executeTask(
// No more models — use normal retry logic // No more models — use normal retry logic
if (retries < maxRetries) { if (retries < maxRetries) {
retries = progress.incrementRetry(task.id); retries = progress.incrementRetry(task.id);
ctx.ui.notify( sendChatMessage?.(
`Retrying task ${task.id} (${retries}/${maxRetries}): ${result.error}`, `~ ${task.id} · ${task.title} — retrying (${retries}/${maxRetries}): ${result.error}`,
"warning",
); );
// Exponential backoff // Exponential backoff
@@ -679,6 +679,7 @@ async function executeTask(
} else { } else {
// Max retries exceeded // Max retries exceeded
progress.markFailed(task.id, result.error || "Unknown error"); progress.markFailed(task.id, result.error || "Unknown error");
sendChatMessage?.(`${task.id} · ${task.title}${result.error}`);
ctx.ui.notify( ctx.ui.notify(
`Task ${task.id} failed after ${maxRetries} retries: ${ `Task ${task.id} failed after ${maxRetries} retries: ${
result.error || "Unknown error" result.error || "Unknown error"
@@ -691,6 +692,7 @@ async function executeTask(
roundRobin?.release(task.id); roundRobin?.release(task.id);
const errorMsg = error instanceof Error ? error.message : String(error); const errorMsg = error instanceof Error ? error.message : String(error);
progress.markFailed(task.id, errorMsg); progress.markFailed(task.id, errorMsg);
sendChatMessage?.(`${task.id} · ${task.title}${errorMsg}`);
ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error"); ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error");
return; return;
} }
@@ -703,6 +705,9 @@ async function executeTask(
// All models exhausted — release the slot // All models exhausted — release the slot
roundRobin?.release(task.id); roundRobin?.release(task.id);
progress.markFailed(task.id, "All configured models exhausted"); progress.markFailed(task.id, "All configured models exhausted");
sendChatMessage?.(
`${task.id} · ${task.title} — all ${maxModelAttempts} models exhausted`,
);
ctx.ui.notify( ctx.ui.notify(
`Task ${task.id} failed: all configured models exhausted`, `Task ${task.id} failed: all configured models exhausted`,
"error", "error",