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.
@@ -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 subagent-driven plan execution. This skill is your complete reference for using it.
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
- ## Slash commands (interactive mode)
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]]` | Dispatch plan(s) to `soly-manager` subagent. `N` = all plans in phase. `N.MM` = specific plan. |
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
- | `/rotor [name]` | Switch the current rotor (or open picker) |
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 (migration in progress):** `.soly/` is the legacy soly state. `.agents/` is the new vendor-neutral home. The two work side-by-side:
85
+ **Two parallel conventions:** `.soly/` is soly-specific state. `.agents/` is vendor-neutral agent config. The two coexist:
85
86
 
86
- - **Use `.agents/`** for new projects (recommended)
87
- - **Use `.soly/`** for legacy projects (still works, will show a deprecation warning)
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 1
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
- ## Where to look for answers
272
+ ## When in doubt
349
273
 
350
- - **"What command does X"** this skill, Slash commands section
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-worker` or `soly-debugger` — use `soly-manager` (mode-switches)
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/` (or `.agents/docs/` for vendor-neutral)
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/`
@@ -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
- }