pi-soly 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -32
- package/agents-install.ts +11 -98
- package/commands.ts +48 -2
- package/config.ts +4 -6
- package/index.ts +10 -33
- package/package.json +1 -3
- package/skills/soly-framework/SKILL.md +25 -111
- package/agents/soly-manager.md +0 -124
- package/switch/README.md +0 -104
- package/switch/core.ts +0 -229
- package/switch/index.ts +0 -347
- package/switch/package.json +0 -52
- package/switch/prompt.ts +0 -131
- package/switch/tests/core.test.ts +0 -210
- package/switch/tests/index.test.ts +0 -48
- package/switch/tests/prompt.test.ts +0 -108
- package/switch/tsconfig.json +0 -28
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: soly-framework
|
|
3
|
-
description: Use when the user asks how to do anything with the soly framework for pi — start a new project, plan or execute a phase, pause and resume sessions, add rules, add intent docs, write PLAN.md or SUMMARY.md, troubleshoot issues. Triggers on "how do I", "what's the command for", "soly help", "soly framework", and any practical question about using the soly extension. NOT loaded for generic code questions — only when the user is working with the soly workflow.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# soly framework
|
|
7
2
|
|
|
8
|
-
The **soly** extension adds project-management workflow to [pi-coding-agent](https://github.com/nicobailon/pi-coding-agent): intent docs, ROADMAP/STATE/PHASE state machine, and
|
|
3
|
+
The **soly** extension adds project-management workflow to [pi-coding-agent](https://github.com/nicobailon/pi-coding-agent): intent docs, ROADMAP/STATE/PHASE state machine, and LLM-driven plan execution. This skill is your complete reference for using it.
|
|
9
4
|
|
|
10
5
|
## Quick start (read first if new)
|
|
11
6
|
|
|
@@ -22,12 +17,12 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
22
17
|
- A **task** is the smallest unit. Has type, description, verify, accept.
|
|
23
18
|
- **Close-out**: production code commits → `SUMMARY.md` → `STATE.md` updated → ROADMAP check.
|
|
24
19
|
|
|
25
|
-
##
|
|
20
|
+
## Quick reference — slash commands
|
|
26
21
|
|
|
27
22
|
| Command | What it does |
|
|
28
23
|
|---|---|
|
|
29
24
|
| `/plan [N]` | Generate or update `PLAN.md` for phase N (or current phase) |
|
|
30
|
-
| `/execute [N[.MM]]` |
|
|
25
|
+
| `/execute [N[.MM]]` | Execute plan(s) in phase N. `N` = all plans. `N.MM` = specific plan. The LLM (you) executes directly. |
|
|
31
26
|
| `/discuss N` | Discussion-driven scoping for phase N — capture decisions before planning |
|
|
32
27
|
| `/inspect` | One-screen summary: position, phases, recent decisions |
|
|
33
28
|
| `/pause` | Save handoff (`HANDOFF.json` + `.continue-here.md`) for later resume |
|
|
@@ -35,10 +30,21 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
35
30
|
| `/quick <task>` | One-shot task that doesn't need a full plan — direct dispatch |
|
|
36
31
|
| `/soly` | Project state inspection (alias for `/inspect`) |
|
|
37
32
|
| `/why` | Show what context the LLM's last turn was based on |
|
|
38
|
-
| `/
|
|
33
|
+
| `/soly-init` | Scaffold a new soly project (interactive template picker) |
|
|
34
|
+
| `/soly-migrate` | Move legacy `.soly/` to `.agents/` (atomic) |
|
|
35
|
+
| `/soly-status` | Comprehensive one-screen report |
|
|
36
|
+
| `/soly-log [N]` | Show last N notifications from the log |
|
|
39
37
|
|
|
40
38
|
`/soly <verb>` plain-text aliases also work for some verbs (legacy compat).
|
|
41
39
|
|
|
40
|
+
## No rotors (removed in 1.4.0)
|
|
41
|
+
|
|
42
|
+
As of 1.4.0, soly no longer ships rotors. No `/rotor` command, no `Ctrl+Tab` cycle, no footer pill. The LLM picks the right subagent based on the task brief — use `subagent(...)` with `agent: "worker"` for implementation, `"oracle"` for decisions, `"scout"` for recon, `"reviewer"` for adversarial review.
|
|
43
|
+
|
|
44
|
+
**Why drop them?** Rotors were a UX shortcut (Ctrl+Tab) that pi itself doesn't support well. pi has its own subagent system (`worker`, `oracle`, `scout`, `reviewer`); wrapping it in a "cycle" was over-engineering. The LLM in the main session is the executor; pi's subagents are helpers, not cycle modes.
|
|
45
|
+
|
|
46
|
+
**For soly work**, the LLM does the work itself — it reads PLAN.md, runs commands, commits, writes SUMMARY.md. It does NOT spawn a soly subagent. Use `subagent(...)` only for read-only research (e.g. `agent: "scout"` for "find all files using X").
|
|
47
|
+
|
|
42
48
|
## File structure
|
|
43
49
|
|
|
44
50
|
```
|
|
@@ -69,26 +75,19 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
69
75
|
│ └── .continue-here.md # pause resume marker
|
|
70
76
|
├── .agents/ # vendor-neutral agent config (per project)
|
|
71
77
|
│ ├── rules/ # agent rules (loaded with priority 3, after .soly/rules/)
|
|
72
|
-
│ │ ├── code-style.md
|
|
73
|
-
│ │ └── testing.md
|
|
74
78
|
│ ├── skills/ # project-scoped skills (pi auto-discovers)
|
|
75
79
|
│ │ └── my-skill/
|
|
76
80
|
│ │ └── SKILL.md
|
|
77
81
|
│ ├── docs/ # agent-specific docs (intent-style)
|
|
78
|
-
│ │ └── architecture.md
|
|
79
82
|
│ └── agents/ # project-specific agent definitions
|
|
80
|
-
│ ├── project-reviewer.md
|
|
81
|
-
│ └── data-scientist.md
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
**Two parallel conventions
|
|
85
|
+
**Two parallel conventions:** `.soly/` is soly-specific state. `.agents/` is vendor-neutral agent config. The two coexist:
|
|
85
86
|
|
|
86
|
-
- **Use `.
|
|
87
|
-
- **Use `.
|
|
87
|
+
- **Use `.soly/`** for soly workflow artifacts (PLAN.md, SUMMARY.md, etc.)
|
|
88
|
+
- **Use `.agents/`** for things other AI tools should also see (rules, skills, agents)
|
|
88
89
|
- **Use `AGENTS.md`** for top-level project-wide agent conventions
|
|
89
90
|
|
|
90
|
-
To migrate: `mv .soly .agents` — soly picks up the new location automatically. No data loss.
|
|
91
|
-
|
|
92
91
|
## Frontmatter conventions
|
|
93
92
|
|
|
94
93
|
### PLAN.md frontmatter (required)
|
|
@@ -200,36 +199,6 @@ The only legal sequence for finishing a plan:
|
|
|
200
199
|
|
|
201
200
|
Once production commits exist, returning without a committed `SUMMARY.md` is an **illegal partial-plan state** — the next `/execute` will detect it and refuse to start.
|
|
202
201
|
|
|
203
|
-
## Cycle rotors (4 built-in)
|
|
204
|
-
|
|
205
|
-
| Rotor | Writes | Use for |
|
|
206
|
-
|---|---|---|
|
|
207
|
-
| `worker` | ✅ | Generic implementation, full tools |
|
|
208
|
-
| `oracle` | ❌ | Decision-consistency, no file edits |
|
|
209
|
-
| `scout` | ❌ | Codebase recon, read-only |
|
|
210
|
-
| `reviewer` | ❌ | Adversarial code review |
|
|
211
|
-
|
|
212
|
-
Switch with `/rotor <name>` or `Ctrl+Tab` (cycles through). Footer pill shows current: `· ⚡ worker` / `▶ 🐢 oracle`.
|
|
213
|
-
|
|
214
|
-
**Why "rotors"?** Because they *rotate* — `Ctrl+Tab` cycles through them. The word emphasizes the cycling behavior. Subagents (like `soly-manager`) are still called agents — they're a different concept (spawned for a task, not cycled through).
|
|
215
|
-
|
|
216
|
-
## Subagent: soly-manager (single, mode-switching)
|
|
217
|
-
|
|
218
|
-
Spawn via `subagent({ agent: "soly-manager", task: ... })`. The task brief tells it which mode to be in:
|
|
219
|
-
|
|
220
|
-
| Task brief mentions | Mode |
|
|
221
|
-
|---|---|
|
|
222
|
-
| implement, build, write code, add feature, create | **worker** |
|
|
223
|
-
| debug, bug, fix, crash, error, repro, broken | **debugger** |
|
|
224
|
-
| test, coverage, spec, assert, only modify tests | **tester** |
|
|
225
|
-
| review, audit, adversarial, find bugs, qa | **reviewer** |
|
|
226
|
-
| refactor, simplify, extract, rename, no behavior change | **refactor** |
|
|
227
|
-
| document, readme, jsdoc, comment, intent doc | **documenter** |
|
|
228
|
-
| validate, scope, drift, decision, before committing | **oracle** |
|
|
229
|
-
| plan, design, outline, structure, decompose | **planner** |
|
|
230
|
-
|
|
231
|
-
**soly-manager is ONE agent that switches modes. Don't spawn soly-worker / soly-debugger / etc. — those don't exist anymore.**
|
|
232
|
-
|
|
233
202
|
## Tools the LLM can call
|
|
234
203
|
|
|
235
204
|
| Tool | Purpose |
|
|
@@ -247,51 +216,6 @@ Spawn via `subagent({ agent: "soly-manager", task: ... })`. The task brief tells
|
|
|
247
216
|
| `ask_pro(questions)` | Multi-question picker (tabbed, single/multi-select, ⭐, Other…) |
|
|
248
217
|
| `todo_update(todos)` | Update task list rendered in footer |
|
|
249
218
|
|
|
250
|
-
## Add a new rule (most common task)
|
|
251
|
-
|
|
252
|
-
Three places, in priority order:
|
|
253
|
-
|
|
254
|
-
1. **Project rule** — `~/.pi/agent/agents/soly/rules/<name>.md` (version-controlled, shared with team)
|
|
255
|
-
2. **User rule** — `~/.soly/rules/<name>.md` (per-user, not committed)
|
|
256
|
-
3. **Phase rule** — `<phase-dir>/<plan>.md.rules/<name>.md` (active only for that plan)
|
|
257
|
-
|
|
258
|
-
Use `/rulewizard` slash command to scaffold a new rule with the right frontmatter.
|
|
259
|
-
|
|
260
|
-
A rule file looks like:
|
|
261
|
-
|
|
262
|
-
```markdown
|
|
263
|
-
---
|
|
264
|
-
applyTo: "src/**/*.ts"
|
|
265
|
-
priority: 50
|
|
266
|
-
---
|
|
267
|
-
|
|
268
|
-
# TypeScript style
|
|
269
|
-
|
|
270
|
-
- Strict mode required
|
|
271
|
-
- Never use `any`
|
|
272
|
-
- Prefer `type` over `interface`
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Add a new intent doc
|
|
276
|
-
|
|
277
|
-
Create a file in `.soly/docs/`:
|
|
278
|
-
|
|
279
|
-
```markdown
|
|
280
|
-
# Architecture
|
|
281
|
-
|
|
282
|
-
## Goal
|
|
283
|
-
|
|
284
|
-
Build a CLI tool that...
|
|
285
|
-
|
|
286
|
-
## Non-obvious constraints
|
|
287
|
-
|
|
288
|
-
- Must work offline (no network calls)
|
|
289
|
-
- Must be a single static binary
|
|
290
|
-
- Must integrate with the existing `~/.config/x` schema
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
Intent docs are 0-point — written BEFORE any plan, by humans. They define the "why", not the "how".
|
|
294
|
-
|
|
295
219
|
## Common workflows
|
|
296
220
|
|
|
297
221
|
### Start a new project
|
|
@@ -300,11 +224,11 @@ Intent docs are 0-point — written BEFORE any plan, by humans. They define the
|
|
|
300
224
|
2. Write 1-3 intent docs in `.soly/docs/`
|
|
301
225
|
3. Optionally write `AGENTS.md` (or `agents.md`) at project root with project conventions
|
|
302
226
|
4. Create `ROADMAP.md` with phase table
|
|
303
|
-
5. `/plan 1` to start phase
|
|
227
|
+
5. `/plan 1` to start the first phase
|
|
304
228
|
|
|
305
229
|
### Add project-specific agents
|
|
306
230
|
|
|
307
|
-
Drop a markdown file in `.agents/<name>.md` (project) or `~/.agents/<name>.md` (user):
|
|
231
|
+
Drop a markdown file in `.agents/agents/<name>.md` (project) or `~/.agents/agents/<name>.md` (user):
|
|
308
232
|
|
|
309
233
|
```markdown
|
|
310
234
|
---
|
|
@@ -323,7 +247,7 @@ You are a data scientist. ...
|
|
|
323
247
|
3. `~/.agents/` — user vendor-neutral (preferred)
|
|
324
248
|
4. `~/.pi/agent/agents/` — user pi native (legacy)
|
|
325
249
|
|
|
326
|
-
`Ctrl+Tab` to see them in the cycle.
|
|
250
|
+
`Ctrl+Tab` to see them in the cycle. (Removed in 1.4.0 — use `subagent(...)` directly.)
|
|
327
251
|
|
|
328
252
|
### Add a feature to an existing phase
|
|
329
253
|
|
|
@@ -345,24 +269,14 @@ If `/execute` complains about illegal partial state:
|
|
|
345
269
|
3. If yes, finish close-out: update `STATE.md` + `ROADMAP.md`
|
|
346
270
|
4. If no, either commit the SUMMARY or revert the production commits
|
|
347
271
|
|
|
348
|
-
##
|
|
272
|
+
## When in doubt
|
|
349
273
|
|
|
350
|
-
|
|
351
|
-
- **"What does PLAN.md look like"** → this skill, Frontmatter section
|
|
352
|
-
- **"How to add a rule"** → this skill, Add a new rule section
|
|
353
|
-
- **"Why did the LLM do Y"** → `/why`
|
|
354
|
-
- **"What context is loaded"** → `soly_read(artifact: "state")` + `soly_doc_search(...)`
|
|
355
|
-
- **"What was the recent conversation"** → `soly_scratchpad()`
|
|
274
|
+
Call `soly_read(artifact: "state")` and `soly_read(artifact: "roadmap")` first. The system prompt has the layers, but `soly_read` gives you full content. Then check `soly_doc_search` for any other relevant docs.
|
|
356
275
|
|
|
357
276
|
## Don'ts
|
|
358
277
|
|
|
359
278
|
- ❌ Edit `.soly/rules/` files you didn't write — those are project invariants
|
|
360
279
|
- ❌ Skip the SUMMARY — illegal partial state
|
|
361
|
-
- ❌ Spawn `soly-
|
|
362
|
-
- ❌ Write rules in code comments — use `.soly/rules/*.md` or `.agents/rules/*.md` files
|
|
280
|
+
- ❌ Spawn `soly-manager` / `soly-worker` / etc. — there are no soly subagents (removed in 1.3.0). Use pi's built-in subagents via the parent LLM's `subagent(...)` call.
|
|
363
281
|
- ❌ Edit `.soly/phases/*/PLAN.md` after `status: in_progress` — create a new plan
|
|
364
|
-
- ❌ Put intent docs anywhere other than `.soly/docs/`
|
|
365
|
-
|
|
366
|
-
## When in doubt
|
|
367
|
-
|
|
368
|
-
Call `soly_read(artifact: "state")` and `soly_read(artifact: "roadmap")` first. The system prompt has the layers, but `soly_read` gives you full content. Then check `soly_doc_search` for any other relevant docs.
|
|
282
|
+
- ❌ Put intent docs anywhere other than `.soly/docs/`
|
package/agents/soly-manager.md
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: soly-manager
|
|
3
|
-
description: Soly workflow executor. Handles any soly task end-to-end — plan, execute, debug, test, review, refactor, document. Reads the workflow brief passed by the parent and picks the right role for the task. The single writer/reviewer for soly projects.
|
|
4
|
-
thinking: high
|
|
5
|
-
systemPromptMode: replace
|
|
6
|
-
inheritProjectContext: true
|
|
7
|
-
inheritSkills: false
|
|
8
|
-
tools: read, grep, find, ls, bash, edit, write
|
|
9
|
-
defaultContext: fork
|
|
10
|
-
defaultReads: context.md, plan.md
|
|
11
|
-
defaultProgress: true
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
You are `soly-manager`: the workflow executor for the **soly** project-management extension.
|
|
15
|
-
|
|
16
|
-
The parent agent passes you a task with one of these roles. **Pick the right one based on the task brief, not on your name:**
|
|
17
|
-
|
|
18
|
-
| Task brief mentions | You are in mode | Your job |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| implement, build, write code, add feature, create | **worker** | Write the code, run verification, commit |
|
|
21
|
-
| debug, bug, fix, crash, error, repro, broken | **debugger** | Repro → isolate → fix → regression test |
|
|
22
|
-
| test, coverage, spec, assert, only modify tests | **tester** | Write tests, run full suite, never touch prod |
|
|
23
|
-
| review, audit, adversarial, find bugs, qa | **reviewer** | Read-only review with file:line evidence |
|
|
24
|
-
| refactor, simplify, extract, rename, no behavior change | **refactor** | Behavior-preserving structural change |
|
|
25
|
-
| document, readme, jsdoc, comment, intent doc | **documenter** | Update docs, never change product behavior |
|
|
26
|
-
| validate, scope, drift, decision, before committing | **oracle** | Read-only consistency check, no edits |
|
|
27
|
-
| plan, design, outline, structure, decompose | **planner** | Ordered steps with risks; not code |
|
|
28
|
-
|
|
29
|
-
**You are one agent that switches modes. You are not seven agents.** The system prompt above is your only persona — the task brief tells you which hat to wear.
|
|
30
|
-
|
|
31
|
-
## Soly-aware defaults (apply in every mode)
|
|
32
|
-
|
|
33
|
-
**Path discipline — NON-NEGOTIABLE.** All soly-managed files live under `.soly/`:
|
|
34
|
-
- `PLAN.md`, `CONTEXT.md`, `RESEARCH.md`, `SUMMARY.md` → `.soly/phases/<NN>-<slug>/`
|
|
35
|
-
- iteration files → `.soly/iterations/`
|
|
36
|
-
- handoffs → `.soly/HANDOFF.json`, `.soly/.continue-here.md`
|
|
37
|
-
- rules → `.soly/rules/` (NEVER edit — version-controlled)
|
|
38
|
-
- All other files (source code, tests) → normal project dirs
|
|
39
|
-
|
|
40
|
-
**Close-out order** (when working a plan): production-code commit(s) → SUMMARY commit → `STATUS: done` update.
|
|
41
|
-
Once production commits exist, returning without a committed SUMMARY is an **illegal partial-plan state**.
|
|
42
|
-
|
|
43
|
-
**Frontmatter contract** for `PLAN.md`: `id`, `title`, `status: pending|in_progress|done`, `phase`, `depends-on`, `parallelizable`. Read frontmatter first.
|
|
44
|
-
|
|
45
|
-
**pi-todo integration** (auto-tracks plan sub-tasks if `todo_update` tool is available):
|
|
46
|
-
1. At task start: call `todo_update` with all `status: "pending"`
|
|
47
|
-
2. Set first to `in_progress` before starting
|
|
48
|
-
3. Update as you go: `pending` → `in_progress` → `completed`
|
|
49
|
-
4. Clear list (`todo_update({todos: []})`) after SUMMARY committed
|
|
50
|
-
Skip silently if `todo_update` is not available.
|
|
51
|
-
|
|
52
|
-
## Read first (soly-aware order)
|
|
53
|
-
|
|
54
|
-
1. `.soly/STATE.md` — milestone, current position, recent decisions
|
|
55
|
-
2. `.soly/ROADMAP.md` — overall phase plan
|
|
56
|
-
3. The target `PLAN.md` (the contract) if a plan is in scope
|
|
57
|
-
4. `<phase>-CONTEXT.md` if it exists (honor user decisions)
|
|
58
|
-
5. `<phase>-RESEARCH.md` if it exists (use chosen libs/patterns)
|
|
59
|
-
|
|
60
|
-
**Iteration context file** (if the parent references one) is a pre-aggregated bundle. If given, read that INSTEAD of the individual files.
|
|
61
|
-
|
|
62
|
-
## Mode-specific discipline
|
|
63
|
-
|
|
64
|
-
These are the few hard rules per mode. Follow them or fail loudly.
|
|
65
|
-
|
|
66
|
-
### As worker (implement)
|
|
67
|
-
- Atomic edits only — no speculative scaffolding, no TODO comments
|
|
68
|
-
- Per task: read `<read_first>` → minimal correct change → verify `<acceptance_criteria>` (HARD GATE; log deviation after 2 failed fix attempts) → run `<verification>` → commit with `<type>(${PHASE}-${PLAN}): <summary>`
|
|
69
|
-
- Do NOT edit `.soly/rules/`
|
|
70
|
-
|
|
71
|
-
### As debugger (fix)
|
|
72
|
-
- **Reproduce first.** No fix without a repro. If the user gave a stack trace, build a minimal test that triggers it. If they said "X is broken", find one test case that demonstrates it.
|
|
73
|
-
- **Isolate.** Git blame, grep, bisect. State the root cause in one sentence before fixing.
|
|
74
|
-
- **Fix the cause, not the symptom.** Extra null checks, swallowed errors, type casts mask the bug.
|
|
75
|
-
- **Regression test.** If a test would have caught this, write it. Run the full suite.
|
|
76
|
-
|
|
77
|
-
### As tester
|
|
78
|
-
- **Hard rule:** you can edit `*.test.*`, `*.spec.*`, `tests/`, `__tests__/`, `test/`. You CANNOT edit anything else. If a test fails because of a prod bug, STOP and report — don't "fix" the prod code.
|
|
79
|
-
- Match the project's existing test style. Don't introduce a new style.
|
|
80
|
-
- Test behavior, not implementation. Black-box > white-box.
|
|
81
|
-
|
|
82
|
-
### As reviewer (adversarial)
|
|
83
|
-
- **Read-only.** Do NOT edit files. Do NOT fix bugs. Do NOT commit. You produce a review with file:line evidence; the parent decides what to do.
|
|
84
|
-
- Read spec → read test → read impl → diff them. Where do they disagree?
|
|
85
|
-
- Pick 3-4 relevant review angles (correctness, security, performance, maintainability, soly-style).
|
|
86
|
-
- Specific over vague: "Line 47: SQL injection. Use parameterized query." not "the code is buggy".
|
|
87
|
-
|
|
88
|
-
### As refactor
|
|
89
|
-
- **Behavior preservation is the entire point.** If a test starts failing, you've changed behavior — that's a bug, not a refactor.
|
|
90
|
-
- Smallest possible diff. Run tests after EVERY change.
|
|
91
|
-
- Don't refactor AND fix a bug. Two concerns = unreviewable.
|
|
92
|
-
- If you find a bug, stop, log it, finish the refactor without touching it.
|
|
93
|
-
|
|
94
|
-
### As documenter
|
|
95
|
-
- **You do NOT change product code.** You write READMEs, JSDoc, `.soly/docs/`, ADRs.
|
|
96
|
-
- Update, don't append. If the README has an "Architecture" section, edit in place.
|
|
97
|
-
- Link, don't repeat. 5 lines + a link > 50 lines of pasted explanation.
|
|
98
|
-
- Don't add marketing fluff ("this powerful, elegant framework...").
|
|
99
|
-
|
|
100
|
-
### As oracle (validate)
|
|
101
|
-
- **Read-only.** No edits, no code, no new workflow trees.
|
|
102
|
-
- Check: drift, hidden assumptions, scope creep, missing prerequisites, repeated mistakes, unresolved `depends-on`.
|
|
103
|
-
- Sometimes the answer is "this shouldn't be a soly plan at all" — say so.
|
|
104
|
-
- Output: inherited decisions → drift check → hidden assumptions → missing prereqs → scope check → recommendation → confidence.
|
|
105
|
-
|
|
106
|
-
### As planner
|
|
107
|
-
- Output ordered steps with explicit risks. No code. No "let me also...".
|
|
108
|
-
- Each step: description, depends-on, verification (test or command), acceptance criteria.
|
|
109
|
-
- If the parent asks for a plan, give a plan. Don't drift into implementation.
|
|
110
|
-
|
|
111
|
-
## Returning
|
|
112
|
-
|
|
113
|
-
Your final response should follow this shape:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
Mode: <worker | debugger | tester | reviewer | refactor | documenter | oracle | planner>
|
|
117
|
-
Did: <one-sentence summary of what you did or found>
|
|
118
|
-
Changed files: <list, or "none" for read-only modes>
|
|
119
|
-
Validation: <test/typecheck/build output, or "n/a" for read-only modes>
|
|
120
|
-
Open risks / decisions needing approval: <list, or "none">
|
|
121
|
-
Recommended next step: <one line>
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Be concise. The parent synthesizes, not you.
|
package/switch/README.md
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# pi-switch — generic subagent switcher for pi
|
|
2
|
-
|
|
3
|
-
A tiny pi extension that gives you a **persistent indicator of the current subagent** (footer pill) and lets you **cycle / set / create** agents. Generic — works with any agent in `~/.pi/agent/agents/`.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Footer pill** — always shows current agent with emoji + description
|
|
8
|
-
- **Ctrl+Tab** to cycle to next agent (Shift+Tab is taken by pi's thinking-level cycler)
|
|
9
|
-
- **F2** as fallback for terminals that don't pass Ctrl+Tab through
|
|
10
|
-
- **`/agent`** slash command to show current + available
|
|
11
|
-
- **`/agent <name>`** to set explicitly
|
|
12
|
-
- **`/agent create <name>`** to scaffold a new user agent
|
|
13
|
-
- **`/agent doctor`** to diagnose
|
|
14
|
-
- **`/agent recommend <task>`** to suggest the right agent for a task
|
|
15
|
-
- **Task → agent heuristics** baked into the system prompt so the LLM picks the right agent for the task
|
|
16
|
-
- Persists to `.soly/agent` (if soly project) or `~/.pi-switch/agent` (standalone)
|
|
17
|
-
- Reads user agents from `~/.pi/agent/agents/*.md` on every cycle — drop a file and Ctrl+Tab to see it
|
|
18
|
-
- Silent switch — only the pill updates, chat stays clean
|
|
19
|
-
|
|
20
|
-
## How agents work
|
|
21
|
-
|
|
22
|
-
Agents are markdown files with YAML frontmatter. pi-subagents (and pi-switch) discover them from these locations:
|
|
23
|
-
|
|
24
|
-
| Path | Type | Editable |
|
|
25
|
-
|---|---|---|
|
|
26
|
-
| `~/.pi/agent/npm/node_modules/pi-subagents/agents/*.md` | built-in (worker, oracle, scout, reviewer) | ❌ |
|
|
27
|
-
| `~/.pi/agent/agents/*.md` | user-defined | ✅ |
|
|
28
|
-
| `~/.pi/agent/extensions/pi-soly/agents/*.md` (auto-installed if `useSolyWorkerSubagents: true` in `.soly/config.json`) | soly-manager (mode-switching subagent) | ✅ source |
|
|
29
|
-
|
|
30
|
-
### Frontmatter schema
|
|
31
|
-
|
|
32
|
-
```markdown
|
|
33
|
-
---
|
|
34
|
-
name: my-reviewer # required, unique, [a-zA-Z0-9_-]{1,64}
|
|
35
|
-
description: One-liner shown in picker
|
|
36
|
-
thinking: medium # off | minimal | low | medium | high | xhigh
|
|
37
|
-
systemPromptMode: replace # replace | append
|
|
38
|
-
inheritProjectContext: true
|
|
39
|
-
inheritSkills: false
|
|
40
|
-
tools: read, grep, find, ls, bash, edit, write
|
|
41
|
-
defaultContext: fork # fresh | fork
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
You are `my-reviewer`. The system prompt goes here.
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Create a new agent
|
|
48
|
-
|
|
49
|
-
### Option A: manually
|
|
50
|
-
Drop a markdown file in `~/.pi/agent/agents/<name>.md` (see schema above). Press `Ctrl+Tab` in pi — it joins the cycle.
|
|
51
|
-
|
|
52
|
-
### Option B: via slash command
|
|
53
|
-
```
|
|
54
|
-
/agent create my-debugger
|
|
55
|
-
```
|
|
56
|
-
You'll be prompted for a one-liner description. Then edit the file to specialize the system prompt.
|
|
57
|
-
|
|
58
|
-
## Test agents
|
|
59
|
-
|
|
60
|
-
| Action | How |
|
|
61
|
-
|---|---|
|
|
62
|
-
| See current + available | `/agent` |
|
|
63
|
-
| Cycle | `Ctrl+Tab` (or `F2`) |
|
|
64
|
-
| Set explicitly | `/agent soly-manager` |
|
|
65
|
-
| Diagnose | `/agent doctor` |
|
|
66
|
-
| Recommend for a task | `/agent recommend investigate React Server Components` |
|
|
67
|
-
|
|
68
|
-
The LLM can also auto-pick — see "Task → agent" below.
|
|
69
|
-
|
|
70
|
-
## Task → agent heuristics
|
|
71
|
-
|
|
72
|
-
The LLM's system prompt includes a table mapping task keywords to agents. When the user request matches, the LLM should call `/agent <name>` first, then `subagent({ agent: <name>, ... })`.
|
|
73
|
-
|
|
74
|
-
| Keywords | Agent | Why |
|
|
75
|
-
|---|---|---|
|
|
76
|
-
| scout, scan, map, where is, locate, skim | 🔍 scout | codebase recon |
|
|
77
|
-
| review, audit, check, adversarial, critique, qa | 👀 reviewer | adversarial review |
|
|
78
|
-
| oracle, decision, tradeoff, which approach, drift | 🔮 oracle | decision consistency |
|
|
79
|
-
| implement, build, write code, add feature, debug, fix, test, refactor, document, plan, validate | ⚡ soly-manager | workflow executor, mode-switches from task brief |
|
|
80
|
-
| (anything else) | ⚡ worker | generic implementation |
|
|
81
|
-
|
|
82
|
-
Same keywords in Russian work (изучи, баг, тест, etc.).
|
|
83
|
-
|
|
84
|
-
## Integration with other extensions
|
|
85
|
-
|
|
86
|
-
- **pi-soly** reads `globalThis.__PI_SWITCH_AGENT__` to know which cycle agent is active. Falls back to `"worker"` if pi-switch isn't loaded.
|
|
87
|
-
- **pi-soly** also auto-installs `soly-manager.md` (single mode-switching subagent) to `~/.pi/agent/agents/` when `useSolyWorkerSubagents: true` in `.soly/config.json`.
|
|
88
|
-
|
|
89
|
-
## Files
|
|
90
|
-
|
|
91
|
-
- `core.ts` — agent metadata, discovery, cycling, persistence
|
|
92
|
-
- `prompt.ts` — system-prompt section + task→agent heuristics + `recommendAgent`
|
|
93
|
-
- `index.ts` — footer pill, Ctrl+Tab / F2, `/agent` slash command, `/agent create`/`/agent doctor`/`/agent recommend`
|
|
94
|
-
- `tests/core.test.ts` — tests for core logic
|
|
95
|
-
- `tests/prompt.test.ts` — tests for prompt + recommendAgent
|
|
96
|
-
- `tests/index.test.ts` — tests for slash command parsing
|
|
97
|
-
|
|
98
|
-
## Development
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
cd packages/pi-soly/switch
|
|
102
|
-
bun test # switch tests
|
|
103
|
-
bun run typecheck # tsc --noEmit
|
|
104
|
-
```
|
package/switch/core.ts
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
|
-
// core.ts — Generic subrotor switcher for pi
|
|
3
|
-
// =============================================================================
|
|
4
|
-
//
|
|
5
|
-
// Lets the user pick which subagent the LLM uses (for `subagent(...)` calls
|
|
6
|
-
// in the pi-subagents system, and for any extension that reads the current
|
|
7
|
-
// agent). Generic — works with pi-subagents' built-ins (worker, oracle,
|
|
8
|
-
// scout, ...) AND any user-defined agent in `~/.pi/agent/agents/`.
|
|
9
|
-
//
|
|
10
|
-
// Cycle order (Shift+Tab in pi is taken by thinking-level; we use Ctrl+Tab
|
|
11
|
-
// as the primary shortcut, with F2 as fallback for terminals that don't
|
|
12
|
-
// pass Ctrl+Tab through).
|
|
13
|
-
//
|
|
14
|
-
// Communication with other extensions:
|
|
15
|
-
// - Writes `globalThis.__PI_SWITCH_ROTOR__` (in-process)
|
|
16
|
-
// - Reads/writes `.soly/agent` if it exists (cross-session persistence,
|
|
17
|
-
// shared with soly extension). If no soly project, persists to
|
|
18
|
-
// `~/.pi-switch/agent` instead.
|
|
19
|
-
// =============================================================================
|
|
20
|
-
|
|
21
|
-
import * as fs from "node:fs";
|
|
22
|
-
import * as os from "node:os";
|
|
23
|
-
import * as path from "node:path";
|
|
24
|
-
|
|
25
|
-
/** Default agent used when no override is set. */
|
|
26
|
-
export const DEFAULT_ROTOR = "worker";
|
|
27
|
-
|
|
28
|
-
/** Built-in pi-subagents that we always offer in the cycle. */
|
|
29
|
-
export const BUILTIN_ROTORS: readonly string[] = [
|
|
30
|
-
"worker",
|
|
31
|
-
"oracle",
|
|
32
|
-
"scout",
|
|
33
|
-
"reviewer",
|
|
34
|
-
] as const;
|
|
35
|
-
|
|
36
|
-
/** Visual metadata for every known agent. Used by the rich status badge,
|
|
37
|
-
* the header bar, and the multi-line switch notify. */
|
|
38
|
-
export interface RotorMeta {
|
|
39
|
-
emoji: string;
|
|
40
|
-
shortLabel: string;
|
|
41
|
-
description: string;
|
|
42
|
-
writesFiles: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const ROTOR_META: Record<string, RotorMeta> = {
|
|
46
|
-
worker: { emoji: "\u26a1", shortLabel: "worker", description: "generic implementation, all tools", writesFiles: true },
|
|
47
|
-
oracle: { emoji: "\ud83d\udd2e", shortLabel: "oracle", description: "decision-consistency, no file edits", writesFiles: false },
|
|
48
|
-
scout: { emoji: "\ud83d\udd0d", shortLabel: "scout", description: "codebase recon, read-only", writesFiles: false },
|
|
49
|
-
reviewer: { emoji: "\ud83d\udc40", shortLabel: "reviewer", description: "adversarial code review", writesFiles: false },
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** Get metadata for an agent. Falls back to a neutral entry for unknown. */
|
|
53
|
-
export function getRotorMeta(name: string): RotorMeta {
|
|
54
|
-
return ROTOR_META[name] ?? {
|
|
55
|
-
emoji: "\u2753",
|
|
56
|
-
shortLabel: name.length > 12 ? name.slice(0, 11) + "\u2026" : name,
|
|
57
|
-
description: "user-defined agent",
|
|
58
|
-
writesFiles: true,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Validate an agent name. */
|
|
63
|
-
export function isValidRotorName(name: string): boolean {
|
|
64
|
-
return /^[a-zA-Z0-9_-]{1,64}$/.test(name);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Discover agent `.md` files in user dir. */
|
|
68
|
-
/** All known agent home directories, in priority order (project wins
|
|
69
|
-
* over user-home; user-home `.agents/` wins over pi-native).
|
|
70
|
-
* Project-level `.agents/` is a vendor-neutral per-project
|
|
71
|
-
* convention — same role as `.soly/` or the old `.claude/`.
|
|
72
|
-
* Agent .md files live DIRECTLY in the dir (not in a subfolder):
|
|
73
|
-
* .agents/reviewer.md (NOT .agents/agents/reviewer.md)
|
|
74
|
-
* Honors $HOME / $USERPROFILE for testability. */
|
|
75
|
-
export function rotorHomeDirs(cwd?: string): string[] {
|
|
76
|
-
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
77
|
-
const dirs: string[] = [];
|
|
78
|
-
if (cwd) {
|
|
79
|
-
dirs.push(path.join(cwd, ".agents")); // project (vendor-neutral, preferred)
|
|
80
|
-
dirs.push(path.join(cwd, ".pi", "agent", "agents")); // project (pi native, legacy)
|
|
81
|
-
}
|
|
82
|
-
dirs.push(path.join(home, ".agents")); // user (vendor-neutral)
|
|
83
|
-
dirs.push(path.join(home, ".pi", "agent", "agents")); // user (pi native, legacy)
|
|
84
|
-
return dirs;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Read all agent names from every home dir. Dedupes, first-occurrence
|
|
88
|
-
* wins. If cwd is provided, project dirs are scanned first. */
|
|
89
|
-
export function discoverUserRotors(cwd?: string): string[] {
|
|
90
|
-
const seen = new Set<string>();
|
|
91
|
-
const out: string[] = [];
|
|
92
|
-
for (const dir of rotorHomeDirs(cwd)) {
|
|
93
|
-
if (!fs.existsSync(dir)) continue;
|
|
94
|
-
let entries: string[];
|
|
95
|
-
try {
|
|
96
|
-
entries = fs.readdirSync(dir);
|
|
97
|
-
} catch {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
for (const file of entries) {
|
|
101
|
-
if (!file.endsWith(".md")) continue;
|
|
102
|
-
try {
|
|
103
|
-
const raw = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
104
|
-
const m = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
105
|
-
if (!m) continue;
|
|
106
|
-
const fm = m[1] ?? "";
|
|
107
|
-
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
108
|
-
if (nameMatch) {
|
|
109
|
-
const n = (nameMatch[1] ?? "").trim();
|
|
110
|
-
if (isValidRotorName(n) && !seen.has(n)) {
|
|
111
|
-
seen.add(n);
|
|
112
|
-
out.push(n);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
} catch { /* skip unreadable */ }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return out;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Build the full cycle of available agents. Built-ins first, then
|
|
122
|
-
* project-level agents (if cwd given), then user-home agents.
|
|
123
|
-
* Dedupes while preserving first-occurrence order. */
|
|
124
|
-
export function availableAgents(cwd?: string): string[] {
|
|
125
|
-
const out: string[] = [];
|
|
126
|
-
const seen = new Set<string>();
|
|
127
|
-
const push = (n: string) => {
|
|
128
|
-
if (!seen.has(n)) {
|
|
129
|
-
seen.add(n);
|
|
130
|
-
out.push(n);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
for (const a of BUILTIN_ROTORS) push(a);
|
|
134
|
-
for (const a of discoverUserRotors(cwd)) push(a);
|
|
135
|
-
return out;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Cycle order. */
|
|
139
|
-
export function nextAgent(current: string, cycle: readonly string[]): string {
|
|
140
|
-
if (cycle.length === 0) return DEFAULT_ROTOR;
|
|
141
|
-
const idx = cycle.indexOf(current);
|
|
142
|
-
if (idx < 0) return cycle[0]!;
|
|
143
|
-
return cycle[(idx + 1) % cycle.length]!;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** Parse a user-supplied agent name. */
|
|
147
|
-
export function parseRotorName(raw: string): string | null {
|
|
148
|
-
const n = raw.trim();
|
|
149
|
-
if (!isValidRotorName(n)) return null;
|
|
150
|
-
return n;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/** Short badge: `<emoji> <name>`. Null for default (silent). */
|
|
154
|
-
export function formatAgentBadge(agent: string): string | null {
|
|
155
|
-
if (agent === DEFAULT_ROTOR) return null;
|
|
156
|
-
const meta = getRotorMeta(agent);
|
|
157
|
-
return `${meta.emoji} ${agent}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Multi-line switch notify. */
|
|
161
|
-
export function formatRotorSwitchNotify(prev: string, next: string): string {
|
|
162
|
-
const prevMeta = getRotorMeta(prev);
|
|
163
|
-
const nextMeta = getRotorMeta(next);
|
|
164
|
-
const lines: string[] = [
|
|
165
|
-
"pi-switch agent changed",
|
|
166
|
-
"",
|
|
167
|
-
` ${prevMeta.emoji} ${prev.padEnd(16)} → ${nextMeta.emoji} ${next}`,
|
|
168
|
-
` ${"".padEnd(16)} ${nextMeta.description}`,
|
|
169
|
-
"",
|
|
170
|
-
` writes files: ${nextMeta.writesFiles ? "yes" : "no (read-only)"} · next subagent call uses: ${next}`,
|
|
171
|
-
];
|
|
172
|
-
return lines.join("\n");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** Group agents: built-ins + user-defined. */
|
|
176
|
-
export function groupedAvailableRotors(cwd?: string): Array<{ header: string; agents: string[] }> {
|
|
177
|
-
const all = availableAgents(cwd);
|
|
178
|
-
const groups: Array<{ header: string; agents: string[] }> = [];
|
|
179
|
-
const builtin = all.filter((a) => BUILTIN_ROTORS.includes(a));
|
|
180
|
-
if (builtin.length > 0) groups.push({ header: "built-in", agents: builtin });
|
|
181
|
-
const user = all.filter((a) => !BUILTIN_ROTORS.includes(a));
|
|
182
|
-
if (user.length > 0) groups.push({ header: "user-defined", agents: user });
|
|
183
|
-
return groups;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/** Header line shown above chat. Persistent, dim, single line. */
|
|
187
|
-
export function formatHeaderLine(agent: string): string {
|
|
188
|
-
const meta = getRotorMeta(agent);
|
|
189
|
-
const writeTag = meta.writesFiles ? "" : " \u00b7 read-only";
|
|
190
|
-
return `${meta.emoji} ${agent} \u00b7 ${meta.description}${writeTag} [Ctrl+Tab to cycle]`;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ---------------------------------------------------------------------------
|
|
194
|
-
// Persistence
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
|
-
|
|
197
|
-
/** Where to persist the current agent. Prefers `.soly/agent` if a soly
|
|
198
|
-
* project exists (shared with soly extension). Otherwise `~/.pi-switch/agent`. */
|
|
199
|
-
export function agentFilePath(cwd: string): string {
|
|
200
|
-
const solyAgent = path.join(cwd, ".soly", "agent");
|
|
201
|
-
if (fs.existsSync(path.join(cwd, ".soly"))) return solyAgent;
|
|
202
|
-
// Respect HOME/USERPROFILE for testability (otherwise os.homedir() ignores them on Windows)
|
|
203
|
-
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
204
|
-
const fallbackDir = path.join(home, ".pi-switch");
|
|
205
|
-
fs.mkdirSync(fallbackDir, { recursive: true });
|
|
206
|
-
return path.join(fallbackDir, "agent");
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** Read persisted agent from disk. Returns null if missing/invalid. */
|
|
210
|
-
export function loadAgent(cwd: string): string | null {
|
|
211
|
-
try {
|
|
212
|
-
const file = agentFilePath(cwd);
|
|
213
|
-
if (!fs.existsSync(file)) return null;
|
|
214
|
-
const raw = fs.readFileSync(file, "utf-8").trim();
|
|
215
|
-
if (!isValidRotorName(raw)) return null;
|
|
216
|
-
return raw;
|
|
217
|
-
} catch {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** Write current agent to disk. */
|
|
223
|
-
export function saveAgent(cwd: string, agent: string): void {
|
|
224
|
-
try {
|
|
225
|
-
const file = agentFilePath(cwd);
|
|
226
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
227
|
-
fs.writeFileSync(file, agent + "\n", "utf-8");
|
|
228
|
-
} catch { /* best-effort */ }
|
|
229
|
-
}
|