diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e16a7b --- /dev/null +++ b/README.md @@ -0,0 +1,148 @@ +# pi Skills + +This directory contains custom skills for [pi](https://github.com/earendil-works/pi), a coding agent harness. Each skill adds specialized capabilities that extend what pi can do. + +## Installed Skills + +| Skill | Description | +|-------|-------------| +| [phased-execution](./phased-execution/) | Runs phased build pipelines in isolated subprocesses | +| [find-skills](./find-skills/) | Discovers and installs new skills | +| [gog](./gog/) | Google Workspace CLI (Gmail, Calendar, Drive, Contacts, Sheets, Docs) | + +## How Skills Work + +A skill is a directory containing at minimum a `SKILL.md` file. When pi loads, it reads all `SKILL.md` files in this directory and makes their instructions available to the agent. The `description` field in each skill's front matter tells pi when to activate that skill. + +Skills can include: +- **Scripts** — shell scripts, Node modules, or any executable code +- **Assets** — templates, prompts, validation gates, etc. +- **Additional docs** — referenced via relative paths from `SKILL.md` + +## Adding a New Skill + +1. Create a directory under `~/.pi/agent/skills//` +2. Add a `SKILL.md` with front matter and instructions +3. Optionally add scripts, assets, or subdirectories +4. Restart pi (or the skill will be picked up on next load) + +### SKILL.md Format + +```markdown +--- +name: my-skill +description: What this skill does — when pi should activate it +--- + +# My Skill + +Instructions for the agent go here. +``` + +## Cloning Skills from Other Sources + +To install a skill from a git repo or another location, you can clone it directly into this directory: + +```bash +git clone https://github.com/user/my-skill ~/.pi/agent/skills/my-skill +``` + +Or symlink for development: + +```bash +ln -s /path/to/my-skill ~/.pi/agent/skills/my-skill +``` + +--- + +## phased-execution + +Runs phased build pipelines in isolated subprocesses. Ported from [opencode](https://github.com/opencode-ai/opencode)'s `next-phase` / `auto-phase` commands. + +### Overview + +This skill orchestrates a project's development as a sequence of phases, each executed by the LLM in its own fresh pi process. Phases are managed via files — not chat context — so even very long-running pipelines don't bloat your session history. + +Each phase: +1. Reads the master plan (`.agent/PLAN.md`) and any already-completed phases +2. Implements all tasks from its phase file +3. Runs tests, linting, and coverage checks via `.agent/validate.sh` +4. Retries up to `MAX_FIX_ATTEMPTS` times on failure (resuming the same session) +5. Moves to `complete/` only when everything passes + +### Directory Structure + +``` +/ +├── .agent/ +│ ├── PLAN.md # Master plan; LOCKED DECISIONS are binding +│ ├── validate.sh # Quality gate (created from skill template on first run) +│ ├── phases/ +│ │ ├── todo/ # Pending phases: 01_name.md, 02_name.md, … +│ │ └── complete/ # Finished phases +│ ├── reports/ # Per-phase executor reports, stderr, validation logs +│ └── phase-sessions/ # Resumable child pi sessions +``` + +### Usage + +From within a project that has `.agent/phases/todo/`: + +**Run the next pending phase:** +```bash +cd /path/to/project +bash ~/.pi/agent/skills/phased-execution/scripts/run-phase.sh +``` + +**Run a specific phase:** +```bash +bash ~/.pi/agent/skills/phased-execution/scripts/run-phase.sh 03_api.md +``` + +**Run the entire pipeline** (all pending phases, in order, stopping at first failure): +```bash +bash ~/.pi/agent/skills/phased-execution/scripts/auto-phase.sh +``` + +Re-running `auto-phase.sh` after a failure continues where it stopped. + +### Configuration + +Environment variables passed to the scripts control behavior: + +| Variable | Default | Meaning | +|----------|---------|---------| +| `MAX_FIX_ATTEMPTS` | `3` | Fixer retries per phase | +| `PHASE_MODEL` | session default | Model for child executors (e.g. `anthropic/claude-sonnet-4-5`) | +| `PHASE_THINKING` | session default | Thinking level for child executors | +| `PHASE_COMMIT` | `0` | `1` = auto-git-commit after each passing phase | +| `PI_TRUST` | `0` | `1` = pass `--approve` to children (load project `.pi/` settings) | +| `FRESH_FIX` | `0` | `1` = fixer retries start fresh instead of resuming the failed session | +| `QUIET` | `0` | `1` = suppress live progress display (reports still written) | + +Example: +```bash +PHASE_MODEL=anthropic/claude-sonnet-4-5 PHASE_COMMIT=1 bash ~/.pi/agent/skills/phased-execution/scripts/auto-phase.sh +``` + +### How It Works + +1. `run-phase.sh` / `auto-phase.sh` finds the project root (walks up from `PWD` for `.agent/phases/todo/`) +2. For each phase, it spawns a **separate pi subprocess** with `--mode json` to capture structured output +3. `progress.mjs` streams live progress (tool calls, assistant text) to the terminal and writes the final report to `.agent/reports/` +4. After the child exits, `.agent/validate.sh` is run as the quality gate +5. On success: phase moves to `complete/`, report is printed +6. On failure: up to `MAX_FIX_ATTEMPTS` fixer retries (resuming the same session), then the phase stays in `todo/` + +### After a Run + +- **Success**: The executor report is printed at the end of the script output. Phases in `.agent/phases/complete/` are already done. +- **Failure**: Point to `.agent/reports/.a*.{md,err,validate}` for logs. Resume with: + ```bash + pi --session-dir .agent/phase-sessions -c + ``` + Or re-run the script to retry automatically. + +### Creating Phases + +Phase files are typically created by prompt templates like `/to-phase`, `/audit-create`, `/new-project`, and `/new-python-*`. Each phase file in `todo/` should list concrete tasks the executor must complete, including testing & quality criteria. diff --git a/phased-execution/scripts/progress.mjs b/phased-execution/scripts/progress.mjs index 4d9c9f0..627d672 100644 --- a/phased-execution/scripts/progress.mjs +++ b/phased-execution/scripts/progress.mjs @@ -117,7 +117,6 @@ function handle(ev) { if (text) { lastAssistantText = text; out(wrap(text, 2)); - out(""); } if (m.stopReason === "error") { sawError = true;