readme cleanup
This commit is contained in:
@@ -54,7 +54,7 @@ Task IDs are zero-padded strings (`"01"`, `"02"`, etc.). The parser prepends `0`
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
117
README.md
117
README.md
@@ -4,75 +4,33 @@ Execute tasks from task files using DAG-based dependency resolution with persist
|
||||
|
||||
## Features
|
||||
|
||||
- **DAG-based execution**: Tasks are ordered by dependencies using Kahn's algorithm
|
||||
- **Parallel batching**: Independent tasks in each batch can run concurrently
|
||||
- **Persistent progress**: Execution state saved to `.ralpi/progress.json`
|
||||
- **Reflection system**: Each task produces a reflection for downstream tasks
|
||||
- **Retry with backoff**: Failed tasks retry with exponential backoff
|
||||
- **Multiple formats**: Supports Fio README, simple checkboxes, and YAML
|
||||
- **Chat progress**: Real-time progress messages in Pi chat via `pi.sendMessage`
|
||||
- **Multiple formats**: Supports simple checkboxes, and YAML
|
||||
- **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
|
||||
- **Session saving**: Saves full task output for expandable session review
|
||||
- **Resume auto-discovery**: Automatically finds and resumes interrupted execution
|
||||
- **Custom message renderer**: Compact UI labels with expandable details in Pi TUI
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/ralpi plan [task-file] # Show execution plan
|
||||
/ralpi run [task-file] # Execute all tasks
|
||||
/ralpi status [task-file] # Show current progress
|
||||
/ralpi resume [task-file] # Resume paused execution
|
||||
/ralpi next [task-file] # Execute next batch only
|
||||
/ralpi reset [task-file] # Reset all progress
|
||||
/ralpi [task-file] # Execute all tasks
|
||||
/ralpi plan # Alias to /task-manager to plan new tasks
|
||||
/ralpi resume # Resume paused execution
|
||||
/ralpi reset [task-file] # Reset progress and .ralpi directory - does not modify PRD
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
## 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
|
||||
|
||||
```markdown
|
||||
@@ -96,9 +54,45 @@ tasks:
|
||||
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
|
||||
|
||||
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 |
|
||||
|-------|------|
|
||||
@@ -115,9 +109,6 @@ prompts:
|
||||
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
|
||||
> 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
|
||||
@@ -127,28 +118,6 @@ prompts:
|
||||
> 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.
|
||||
|
||||
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
|
||||
|
||||
- `.ralpi/progress.json` - Execution progress
|
||||
|
||||
10
package.json
10
package.json
@@ -8,7 +8,7 @@
|
||||
"task-runner",
|
||||
"dag",
|
||||
"task-manager",
|
||||
"ralpi-loop",
|
||||
"ralph-loop",
|
||||
"prd"
|
||||
],
|
||||
"author": "Michael Freno",
|
||||
@@ -54,14 +54,6 @@
|
||||
"@earendil-works/pi-coding-agent": "*",
|
||||
"@earendil-works/pi-tui": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@earendil-works/pi-coding-agent": {
|
||||
"optional": true
|
||||
},
|
||||
"@earendil-works/pi-tui": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@@ -296,8 +296,8 @@ export async function runTask(
|
||||
}
|
||||
|
||||
if (!output.success) {
|
||||
sendChatMessage?.(`✗ ${taskHeader} — ${output.error}`);
|
||||
ctx.ui.notify(`Task ${task.id} failed: ${output.error}`, "error");
|
||||
// Failure reporting is handled by the caller (executeTask) to avoid
|
||||
// duplicate messages when model failover or retry cycling is active.
|
||||
return {
|
||||
success: false,
|
||||
error: output.error,
|
||||
@@ -433,6 +433,7 @@ export async function executeBatch(
|
||||
roundRobin?.release(task.id);
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
progress.markFailed(task.id, errorMsg);
|
||||
sendChatMessage?.(`✗ ${task.id} · ${task.title} — ${errorMsg}`);
|
||||
ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error");
|
||||
break;
|
||||
}
|
||||
@@ -555,6 +556,7 @@ async function executeBatchParallel(
|
||||
roundRobin?.release(task.id);
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
progress.markFailed(task.id, errorMsg);
|
||||
sendChatMessage?.(`✗ ${task.id} · ${task.title} — ${errorMsg}`);
|
||||
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()
|
||||
// would pick it right back up, getting stuck on the same model.
|
||||
modelAttempt++;
|
||||
ctx.ui.notify(
|
||||
`Task ${task.id}: model failed, trying next (${modelAttempt + 1}/${maxModelAttempts}): ${result.error}`,
|
||||
"warning",
|
||||
sendChatMessage?.(
|
||||
`~ ${task.id} · ${task.title} — trying model ${modelAttempt + 1}/${maxModelAttempts} (previous: ${result.error})`,
|
||||
);
|
||||
break; // exit retry loop, cycle to next model
|
||||
}
|
||||
@@ -668,9 +669,8 @@ async function executeTask(
|
||||
// No more models — use normal retry logic
|
||||
if (retries < maxRetries) {
|
||||
retries = progress.incrementRetry(task.id);
|
||||
ctx.ui.notify(
|
||||
`Retrying task ${task.id} (${retries}/${maxRetries}): ${result.error}`,
|
||||
"warning",
|
||||
sendChatMessage?.(
|
||||
`~ ${task.id} · ${task.title} — retrying (${retries}/${maxRetries}): ${result.error}`,
|
||||
);
|
||||
|
||||
// Exponential backoff
|
||||
@@ -679,6 +679,7 @@ async function executeTask(
|
||||
} else {
|
||||
// Max retries exceeded
|
||||
progress.markFailed(task.id, result.error || "Unknown error");
|
||||
sendChatMessage?.(`✗ ${task.id} · ${task.title} — ${result.error}`);
|
||||
ctx.ui.notify(
|
||||
`Task ${task.id} failed after ${maxRetries} retries: ${
|
||||
result.error || "Unknown error"
|
||||
@@ -691,6 +692,7 @@ async function executeTask(
|
||||
roundRobin?.release(task.id);
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
progress.markFailed(task.id, errorMsg);
|
||||
sendChatMessage?.(`✗ ${task.id} · ${task.title} — ${errorMsg}`);
|
||||
ctx.ui.notify(`Task ${task.id} failed: ${errorMsg}`, "error");
|
||||
return;
|
||||
}
|
||||
@@ -703,6 +705,9 @@ async function executeTask(
|
||||
// All models exhausted — release the slot
|
||||
roundRobin?.release(task.id);
|
||||
progress.markFailed(task.id, "All configured models exhausted");
|
||||
sendChatMessage?.(
|
||||
`✗ ${task.id} · ${task.title} — all ${maxModelAttempts} models exhausted`,
|
||||
);
|
||||
ctx.ui.notify(
|
||||
`Task ${task.id} failed: all configured models exhausted`,
|
||||
"error",
|
||||
|
||||
Reference in New Issue
Block a user