pi-soly 0.6.0 → 0.7.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/agents-install.ts +111 -30
- package/index.ts +11 -10
- package/package.json +2 -1
- package/skills/soly-framework/SKILL.md +320 -0
package/agents-install.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// =============================================================================
|
|
2
|
-
//
|
|
2
|
+
// assets-install.ts — Idempotent install of soly-managed user assets
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// Soly ships
|
|
6
|
-
// switches modes (worker / debugger / tester / reviewer / refactor /
|
|
7
|
-
// documenter / oracle / planner) based on the task brief the parent passes.
|
|
8
|
-
// One agent, one system prompt, all roles.
|
|
5
|
+
// Soly ships two kinds of user-scope assets:
|
|
9
6
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// 1. Subagent configs → `~/.pi/agent/agents/`
|
|
8
|
+
// The single `soly-manager` subagent (mode-switching executor).
|
|
9
|
+
//
|
|
10
|
+
// 2. Skills → `~/.pi/agent/skills/<name>/`
|
|
11
|
+
// The `soly-framework` skill — framework documentation the LLM
|
|
12
|
+
// loads on demand via the read tool.
|
|
13
|
+
//
|
|
14
|
+
// pi discovers both from `~/.pi/agent/`, so on first session_start we
|
|
15
|
+
// copy our shipped files there.
|
|
12
16
|
//
|
|
13
17
|
// IDEMPOTENT: if the target file already exists (user may have customized
|
|
14
18
|
// it), we do NOT overwrite. This is one-way "first install wins".
|
|
@@ -23,34 +27,73 @@ const SHIPPED_AGENTS = [
|
|
|
23
27
|
"soly-manager.md",
|
|
24
28
|
] as const;
|
|
25
29
|
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
30
|
+
/** soly skills bundled with the extension. Each entry is a directory
|
|
31
|
+
* under `skills/` containing a SKILL.md. */
|
|
32
|
+
const SHIPPED_SKILLS = [
|
|
33
|
+
"soly-framework",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
/** Where pi looks for user agents. Respects HOME/USERPROFILE for
|
|
37
|
+
* testability (otherwise we'd always write to the real user home). */
|
|
28
38
|
function userAgentsDir(): string {
|
|
29
39
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
30
40
|
return path.join(home, ".pi", "agent", "agents");
|
|
31
41
|
}
|
|
32
42
|
|
|
43
|
+
/** Where pi looks for user skills. */
|
|
44
|
+
function userSkillsDir(): string {
|
|
45
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
46
|
+
return path.join(home, ".pi", "agent", "skills");
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
/** Where this soly extension's `agents/` directory lives. */
|
|
34
|
-
function
|
|
50
|
+
function shippedAgentsDir(extensionRoot: string): string {
|
|
35
51
|
return path.join(extensionRoot, "agents");
|
|
36
52
|
}
|
|
37
53
|
|
|
54
|
+
/** Where this soly extension's `skills/` directory lives. */
|
|
55
|
+
function shippedSkillsDir(extensionRoot: string): string {
|
|
56
|
+
return path.join(extensionRoot, "skills");
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
export interface InstallResult {
|
|
39
60
|
installed: string[];
|
|
40
61
|
skipped: string[];
|
|
41
62
|
errors: string[];
|
|
42
63
|
}
|
|
43
64
|
|
|
65
|
+
/** Copy a single file if destination doesn't exist. Idempotent. */
|
|
66
|
+
function copyIfMissing(from: string, to: string): "installed" | "skipped" | "error" {
|
|
67
|
+
if (!fs.existsSync(from)) return "error";
|
|
68
|
+
if (fs.existsSync(to)) return "skipped";
|
|
69
|
+
try {
|
|
70
|
+
fs.copyFileSync(from, to);
|
|
71
|
+
return "installed";
|
|
72
|
+
} catch {
|
|
73
|
+
return "error";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Recursively copy a directory tree if destination doesn't exist. Idempotent. */
|
|
78
|
+
function copyDirIfMissing(from: string, to: string): "installed" | "skipped" | "error" {
|
|
79
|
+
if (!fs.existsSync(from)) return "error";
|
|
80
|
+
if (fs.existsSync(to)) return "skipped";
|
|
81
|
+
try {
|
|
82
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
83
|
+
fs.cpSync(from, to, { recursive: true });
|
|
84
|
+
return "installed";
|
|
85
|
+
} catch {
|
|
86
|
+
return "error";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
44
90
|
/** Install shipped soly agents to `~/.pi/agent/agents/`. Idempotent. */
|
|
45
91
|
export function installSolyAgents(extensionRoot: string): InstallResult {
|
|
46
92
|
const result: InstallResult = { installed: [], skipped: [], errors: [] };
|
|
47
|
-
const src =
|
|
93
|
+
const src = shippedAgentsDir(extensionRoot);
|
|
48
94
|
const dst = userAgentsDir();
|
|
49
95
|
|
|
50
|
-
if (!fs.existsSync(src))
|
|
51
|
-
// Development mode or partial install — silently no-op
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
96
|
+
if (!fs.existsSync(src)) return result; // dev mode no-op
|
|
54
97
|
|
|
55
98
|
try {
|
|
56
99
|
fs.mkdirSync(dst, { recursive: true });
|
|
@@ -62,26 +105,46 @@ export function installSolyAgents(extensionRoot: string): InstallResult {
|
|
|
62
105
|
for (const name of SHIPPED_AGENTS) {
|
|
63
106
|
const from = path.join(src, name);
|
|
64
107
|
const to = path.join(dst, name);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
108
|
+
const r = copyIfMissing(from, to);
|
|
109
|
+
if (r === "installed") result.installed.push(name);
|
|
110
|
+
else if (r === "skipped") result.skipped.push(name);
|
|
111
|
+
else result.errors.push(`missing source: ${from}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Install shipped soly skills to `~/.pi/agent/skills/`. Idempotent. */
|
|
118
|
+
export function installSolySkills(extensionRoot: string): InstallResult {
|
|
119
|
+
const result: InstallResult = { installed: [], skipped: [], errors: [] };
|
|
120
|
+
const src = shippedSkillsDir(extensionRoot);
|
|
121
|
+
const dst = userSkillsDir();
|
|
122
|
+
|
|
123
|
+
if (!fs.existsSync(src)) return result; // dev mode no-op
|
|
124
|
+
|
|
125
|
+
for (const name of SHIPPED_SKILLS) {
|
|
126
|
+
const from = path.join(src, name);
|
|
127
|
+
const to = path.join(dst, name);
|
|
128
|
+
const r = copyDirIfMissing(from, to);
|
|
129
|
+
if (r === "installed") result.installed.push(name);
|
|
130
|
+
else if (r === "skipped") result.skipped.push(name);
|
|
131
|
+
else result.errors.push(`missing source: ${from}`);
|
|
80
132
|
}
|
|
81
133
|
|
|
82
134
|
return result;
|
|
83
135
|
}
|
|
84
136
|
|
|
137
|
+
/** Install all soly assets (agents + skills). Combined for convenience. */
|
|
138
|
+
export function installSolyAssets(extensionRoot: string): {
|
|
139
|
+
agents: InstallResult;
|
|
140
|
+
skills: InstallResult;
|
|
141
|
+
} {
|
|
142
|
+
return {
|
|
143
|
+
agents: installSolyAgents(extensionRoot),
|
|
144
|
+
skills: installSolySkills(extensionRoot),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
85
148
|
/** Check which shipped soly agents are present in the user dir. Used by doctor. */
|
|
86
149
|
export function checkSolyAgentsInstalled(extensionRoot: string): {
|
|
87
150
|
installed: string[];
|
|
@@ -99,3 +162,21 @@ export function checkSolyAgentsInstalled(extensionRoot: string): {
|
|
|
99
162
|
}
|
|
100
163
|
return { installed, missing };
|
|
101
164
|
}
|
|
165
|
+
|
|
166
|
+
/** Check which shipped soly skills are present in the user dir. */
|
|
167
|
+
export function checkSolySkillsInstalled(extensionRoot: string): {
|
|
168
|
+
installed: string[];
|
|
169
|
+
missing: string[];
|
|
170
|
+
} {
|
|
171
|
+
const dst = userSkillsDir();
|
|
172
|
+
const installed: string[] = [];
|
|
173
|
+
const missing: string[] = [];
|
|
174
|
+
for (const name of SHIPPED_SKILLS) {
|
|
175
|
+
if (fs.existsSync(path.join(dst, name, "SKILL.md"))) {
|
|
176
|
+
installed.push(name);
|
|
177
|
+
} else {
|
|
178
|
+
missing.push(name);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { installed, missing };
|
|
182
|
+
}
|
package/index.ts
CHANGED
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
type SourceSpec,
|
|
46
46
|
} from "./core.ts";
|
|
47
47
|
import { buildIntegrationsSection } from "./integrations.ts";
|
|
48
|
-
import {
|
|
48
|
+
import { installSolyAssets } from "./agents-install.ts";
|
|
49
49
|
import {
|
|
50
50
|
DEFAULT_CONFIG,
|
|
51
51
|
loadConfig,
|
|
@@ -388,21 +388,22 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
-
// Auto-install soly-manager
|
|
392
|
-
// on first run. Opt-in via config
|
|
393
|
-
// (default false). Idempotent — respects
|
|
394
|
-
// customized copies.
|
|
391
|
+
// Auto-install soly user-scope assets (soly-manager agent + soly-framework
|
|
392
|
+
// skill) to ~/.pi/agent/ on first run. Opt-in via config
|
|
393
|
+
// `agent.useSolyWorkerSubagents` (default false). Idempotent — respects
|
|
394
|
+
// any existing user-customized copies.
|
|
395
395
|
if (activeConfig.agent.useSolyWorkerSubagents) {
|
|
396
396
|
const extRoot = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
|
|
397
|
-
const
|
|
398
|
-
|
|
397
|
+
const assets = installSolyAssets(extRoot);
|
|
398
|
+
const installed = [...assets.agents.installed, ...assets.skills.installed.map((s) => `skill:${s}`)];
|
|
399
|
+
if (installed.length > 0) {
|
|
399
400
|
ctx.ui.notify(
|
|
400
|
-
`soly: installed
|
|
401
|
+
`soly: installed (${installed.join(", ")}) — run \`/subagents-doctor\` to verify`,
|
|
401
402
|
"info",
|
|
402
403
|
);
|
|
403
404
|
}
|
|
404
|
-
for (const e of
|
|
405
|
-
ctx.ui.notify(`soly:
|
|
405
|
+
for (const e of [...assets.agents.errors, ...assets.skills.errors]) {
|
|
406
|
+
ctx.ui.notify(`soly: install error — ${e}`, "warning");
|
|
406
407
|
}
|
|
407
408
|
}
|
|
408
409
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-soly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Project management framework for pi-coding-agent. Workflows, planning, multi-question picker, agent switcher, live task list — one npm install, zero config.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"agents-install.ts",
|
|
41
41
|
"agents",
|
|
42
42
|
"ask",
|
|
43
|
+
"skills",
|
|
43
44
|
"switch",
|
|
44
45
|
"workflows",
|
|
45
46
|
"workflows-data"
|
|
@@ -0,0 +1,320 @@
|
|
|
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
|
+
# soly framework
|
|
7
|
+
|
|
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.
|
|
9
|
+
|
|
10
|
+
## Quick start (read first if new)
|
|
11
|
+
|
|
12
|
+
**Mental model — three layers, always in system prompt, in this order:**
|
|
13
|
+
|
|
14
|
+
1. **Project intent** (`.soly/docs/`) — the 0-point. What the user wants the app to be. Written BEFORE plans, by humans.
|
|
15
|
+
2. **Project state** (`.soly/STATE.md`, `ROADMAP.md`) — where we are, current phase, recent decisions.
|
|
16
|
+
3. **Project rules** (`.soly/rules/`, `~/.soly/rules/`) — how to behave in this project.
|
|
17
|
+
|
|
18
|
+
**Workflow model — phases and plans:**
|
|
19
|
+
|
|
20
|
+
- A **phase** is a milestone (e.g. "01-foundation"). Has one or more `PLAN.md` files.
|
|
21
|
+
- A **plan** is one ordered execution unit. Has `<task>` blocks.
|
|
22
|
+
- A **task** is the smallest unit. Has type, description, verify, accept.
|
|
23
|
+
- **Close-out**: production code commits → `SUMMARY.md` → `STATE.md` updated → ROADMAP check.
|
|
24
|
+
|
|
25
|
+
## Slash commands (interactive mode)
|
|
26
|
+
|
|
27
|
+
| Command | What it does |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `/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. |
|
|
31
|
+
| `/discuss N` | Discussion-driven scoping for phase N — capture decisions before planning |
|
|
32
|
+
| `/inspect` | One-screen summary: position, phases, recent decisions |
|
|
33
|
+
| `/pause` | Save handoff (`HANDOFF.json` + `.continue-here.md`) for later resume |
|
|
34
|
+
| `/resume` | Restore from a paused handoff |
|
|
35
|
+
| `/quick <task>` | One-shot task that doesn't need a full plan — direct dispatch |
|
|
36
|
+
| `/soly` | Project state inspection (alias for `/inspect`) |
|
|
37
|
+
| `/why` | Show what context the LLM's last turn was based on |
|
|
38
|
+
| `/agent [name]` | Switch the current cycle agent (or open picker) |
|
|
39
|
+
|
|
40
|
+
`/soly <verb>` plain-text aliases also work for some verbs (legacy compat).
|
|
41
|
+
|
|
42
|
+
## File structure
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
<project-root>/
|
|
46
|
+
├── .soly/
|
|
47
|
+
│ ├── ROADMAP.md # phase table
|
|
48
|
+
│ ├── STATE.md # current position + decisions log
|
|
49
|
+
│ ├── docs/ # 0-point intent docs (human-written, locked)
|
|
50
|
+
│ │ ├── vision.md
|
|
51
|
+
│ │ └── architecture.md
|
|
52
|
+
│ ├── rules/ # project rules (version-controlled)
|
|
53
|
+
│ │ ├── code-style.md
|
|
54
|
+
│ │ └── testing.md
|
|
55
|
+
│ ├── phases/
|
|
56
|
+
│ │ ├── 01-foundation/
|
|
57
|
+
│ │ │ ├── 01-CONTEXT.md # domain + decisions for phase 1
|
|
58
|
+
│ │ │ ├── 01-RESEARCH.md # what we looked up
|
|
59
|
+
│ │ │ ├── 01-PLAN-01.md # plan 1
|
|
60
|
+
│ │ │ ├── 01-PLAN-01-SUMMARY.md
|
|
61
|
+
│ │ │ ├── 01-PLAN-02.md
|
|
62
|
+
│ │ │ └── 01-PLAN-02-SUMMARY.md
|
|
63
|
+
│ │ └── 02-feature-x/
|
|
64
|
+
│ │ └── ...
|
|
65
|
+
│ ├── iterations/ # per-execution context bundles (auto)
|
|
66
|
+
│ ├── HANDOFF.json # pause snapshot
|
|
67
|
+
│ └── .continue-here.md # pause resume marker
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Frontmatter conventions
|
|
71
|
+
|
|
72
|
+
### PLAN.md frontmatter (required)
|
|
73
|
+
|
|
74
|
+
```markdown
|
|
75
|
+
---
|
|
76
|
+
id: 01-02 # phase-plan, zero-padded
|
|
77
|
+
title: Add OAuth flow
|
|
78
|
+
status: pending # pending | in_progress | done
|
|
79
|
+
phase: 1
|
|
80
|
+
depends-on: [] # other plan ids
|
|
81
|
+
parallelizable: true # can run alongside siblings
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
# Add OAuth flow
|
|
85
|
+
|
|
86
|
+
## read_first
|
|
87
|
+
- .soly/STATE.md
|
|
88
|
+
- .soly/ROADMAP.md
|
|
89
|
+
- .soly/rules/code-style.md
|
|
90
|
+
|
|
91
|
+
## tasks
|
|
92
|
+
- [ ] **type: implement**, description: Add token refresh logic
|
|
93
|
+
- files: src/auth/refresh.ts
|
|
94
|
+
- verify: bun test src/auth/refresh.test.ts
|
|
95
|
+
- accept: Refresh succeeds when token is expired; fails when refresh_token is also expired
|
|
96
|
+
|
|
97
|
+
- [ ] **type: tdd**, description: Write integration test for the auth flow
|
|
98
|
+
- verify: bun test src/auth/
|
|
99
|
+
|
|
100
|
+
- [ ] **type: checkpoint**, description: Pause for human review of UX
|
|
101
|
+
|
|
102
|
+
## verification
|
|
103
|
+
- bun test
|
|
104
|
+
- bun run typecheck
|
|
105
|
+
- bun run lint
|
|
106
|
+
|
|
107
|
+
## risks
|
|
108
|
+
- Token storage depends on the encryption scheme (see .soly/docs/architecture.md)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### SUMMARY.md frontmatter
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+
---
|
|
115
|
+
plan: 01-02
|
|
116
|
+
completed: 2026-06-15
|
|
117
|
+
duration: 47min
|
|
118
|
+
files-touched: 7
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
# Summary
|
|
122
|
+
|
|
123
|
+
## Tasks
|
|
124
|
+
- [x] Add token refresh logic
|
|
125
|
+
- [x] Write integration test
|
|
126
|
+
- [x] Pause for human review
|
|
127
|
+
|
|
128
|
+
## Deviations
|
|
129
|
+
- Refactored `auth/refresh.ts` to use singleton pattern (was factory). Documented in `architecture.md`.
|
|
130
|
+
|
|
131
|
+
## Verification
|
|
132
|
+
- `bun test`: 142 passing
|
|
133
|
+
- `bun run typecheck`: clean
|
|
134
|
+
- `bun run lint`: 0 warnings
|
|
135
|
+
|
|
136
|
+
## Next
|
|
137
|
+
- Phase 02 plan 01: User profile page
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Rules file frontmatter (optional)
|
|
141
|
+
|
|
142
|
+
```markdown
|
|
143
|
+
---
|
|
144
|
+
applyTo: "src/**/*.ts" # glob (optional, default: all)
|
|
145
|
+
priority: 50 # higher wins on conflict (default: 0)
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
# TypeScript style
|
|
149
|
+
|
|
150
|
+
- Strict mode required
|
|
151
|
+
- Never use `any` — use `unknown` and narrow
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Path discipline (NON-NEGOTIABLE)
|
|
155
|
+
|
|
156
|
+
**All soly-managed files live under `.soly/`.** Source code lives in the project's normal source tree.
|
|
157
|
+
|
|
158
|
+
| File kind | Goes to |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `PLAN.md`, `SUMMARY.md`, `CONTEXT.md`, `RESEARCH.md` | `.soly/phases/<NN>-<slug>/` |
|
|
161
|
+
| Intent docs (0-point) | `.soly/docs/` |
|
|
162
|
+
| Rules | `.soly/rules/` (project) or `~/.soly/rules/` (user) |
|
|
163
|
+
| Handoff | `.soly/HANDOFF.json`, `.soly/.continue-here.md` |
|
|
164
|
+
| Iteration context | `.soly/iterations/` (auto-generated) |
|
|
165
|
+
| Production code, tests | project's normal `src/`, `tests/`, `app/`, etc. |
|
|
166
|
+
|
|
167
|
+
Use absolute paths (or paths starting with `$SOLY_DIR`) when calling tools. Never bare relative names that could land in cwd.
|
|
168
|
+
|
|
169
|
+
## Close-out order
|
|
170
|
+
|
|
171
|
+
The only legal sequence for finishing a plan:
|
|
172
|
+
|
|
173
|
+
1. Production code commits (1+)
|
|
174
|
+
2. `SUMMARY.md` committed
|
|
175
|
+
3. `STATE.md` "Current Position" block updated
|
|
176
|
+
4. `ROADMAP.md` phase checkbox updated
|
|
177
|
+
5. `PLAN.md` frontmatter `status: done`
|
|
178
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
181
|
+
## Cycle agents (4 built-in)
|
|
182
|
+
|
|
183
|
+
| Agent | Writes | Use for |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `worker` | ✅ | Generic implementation, full tools |
|
|
186
|
+
| `oracle` | ❌ | Decision-consistency, no file edits |
|
|
187
|
+
| `scout` | ❌ | Codebase recon, read-only |
|
|
188
|
+
| `reviewer` | ❌ | Adversarial code review |
|
|
189
|
+
|
|
190
|
+
Switch with `/agent <name>` or `Ctrl+Tab` (cycles through). Footer pill shows current: `· ⚡ worker` / `▶ 🐢 oracle`.
|
|
191
|
+
|
|
192
|
+
## Subagent: soly-manager (single, mode-switching)
|
|
193
|
+
|
|
194
|
+
Spawn via `subagent({ agent: "soly-manager", task: ... })`. The task brief tells it which mode to be in:
|
|
195
|
+
|
|
196
|
+
| Task brief mentions | Mode |
|
|
197
|
+
|---|---|
|
|
198
|
+
| implement, build, write code, add feature, create | **worker** |
|
|
199
|
+
| debug, bug, fix, crash, error, repro, broken | **debugger** |
|
|
200
|
+
| test, coverage, spec, assert, only modify tests | **tester** |
|
|
201
|
+
| review, audit, adversarial, find bugs, qa | **reviewer** |
|
|
202
|
+
| refactor, simplify, extract, rename, no behavior change | **refactor** |
|
|
203
|
+
| document, readme, jsdoc, comment, intent doc | **documenter** |
|
|
204
|
+
| validate, scope, drift, decision, before committing | **oracle** |
|
|
205
|
+
| plan, design, outline, structure, decompose | **planner** |
|
|
206
|
+
|
|
207
|
+
**soly-manager is ONE agent that switches modes. Don't spawn soly-worker / soly-debugger / etc. — those don't exist anymore.**
|
|
208
|
+
|
|
209
|
+
## Tools the LLM can call
|
|
210
|
+
|
|
211
|
+
| Tool | Purpose |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `soly_read(artifact, phase, taskId)` | Read soly artifacts: STATE, plan, context, research, ROADMAP, requirements, project, milestone, task |
|
|
214
|
+
| `soly_log_decision(decision, rationale, phase)` | Append to STATE.md Decisions table |
|
|
215
|
+
| `soly_list_phases()` | List all phases with plan counts, C/R markers |
|
|
216
|
+
| `soly_list_tasks()` | List all tasks across features (kind, status, priority, deps) |
|
|
217
|
+
| `soly_todos(paths, limit)` | Scan working tree for TODO/FIXME/HACK/XXX/NOTE |
|
|
218
|
+
| `soly_env()` | Detect runtime (package manager, runtimes, services, scripts) |
|
|
219
|
+
| `soly_snippet(path, offset, limit)` | Read bounded line range with line numbers |
|
|
220
|
+
| `soly_doc_search(query, limit)` | Search .md/.html under cwd (prioritizes intent docs) |
|
|
221
|
+
| `soly_intent()` | List 0-point intent docs from `.soly/docs/` |
|
|
222
|
+
| `soly_scratchpad(limit)` | Recent conversation recap (one line per turn) |
|
|
223
|
+
| `ask_pro(questions)` | Multi-question picker (tabbed, single/multi-select, ⭐, Other…) |
|
|
224
|
+
| `todo_update(todos)` | Update task list rendered in footer |
|
|
225
|
+
|
|
226
|
+
## Add a new rule (most common task)
|
|
227
|
+
|
|
228
|
+
Three places, in priority order:
|
|
229
|
+
|
|
230
|
+
1. **Project rule** — `~/.pi/agent/agents/soly/rules/<name>.md` (version-controlled, shared with team)
|
|
231
|
+
2. **User rule** — `~/.soly/rules/<name>.md` (per-user, not committed)
|
|
232
|
+
3. **Phase rule** — `<phase-dir>/<plan>.md.rules/<name>.md` (active only for that plan)
|
|
233
|
+
|
|
234
|
+
Use `/rulewizard` slash command to scaffold a new rule with the right frontmatter.
|
|
235
|
+
|
|
236
|
+
A rule file looks like:
|
|
237
|
+
|
|
238
|
+
```markdown
|
|
239
|
+
---
|
|
240
|
+
applyTo: "src/**/*.ts"
|
|
241
|
+
priority: 50
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
# TypeScript style
|
|
245
|
+
|
|
246
|
+
- Strict mode required
|
|
247
|
+
- Never use `any`
|
|
248
|
+
- Prefer `type` over `interface`
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Add a new intent doc
|
|
252
|
+
|
|
253
|
+
Create a file in `.soly/docs/`:
|
|
254
|
+
|
|
255
|
+
```markdown
|
|
256
|
+
# Architecture
|
|
257
|
+
|
|
258
|
+
## Goal
|
|
259
|
+
|
|
260
|
+
Build a CLI tool that...
|
|
261
|
+
|
|
262
|
+
## Non-obvious constraints
|
|
263
|
+
|
|
264
|
+
- Must work offline (no network calls)
|
|
265
|
+
- Must be a single static binary
|
|
266
|
+
- Must integrate with the existing `~/.config/x` schema
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Intent docs are 0-point — written BEFORE any plan, by humans. They define the "why", not the "how".
|
|
270
|
+
|
|
271
|
+
## Common workflows
|
|
272
|
+
|
|
273
|
+
### Start a new project
|
|
274
|
+
|
|
275
|
+
1. `soly init` (or manually create `.soly/`, `docs/`, `rules/`)
|
|
276
|
+
2. Write 1-3 intent docs in `.soly/docs/`
|
|
277
|
+
3. Create `ROADMAP.md` with phase table
|
|
278
|
+
4. `/plan 1` to start phase 1
|
|
279
|
+
|
|
280
|
+
### Add a feature to an existing phase
|
|
281
|
+
|
|
282
|
+
1. `/plan 1.05` (next plan number)
|
|
283
|
+
2. Edit the generated `PLAN.md`
|
|
284
|
+
3. `/execute 1.05`
|
|
285
|
+
|
|
286
|
+
### Pause a long session
|
|
287
|
+
|
|
288
|
+
1. `/pause` → writes `HANDOFF.json` + `.continue-here.md`
|
|
289
|
+
2. Later, in a new session: `/resume`
|
|
290
|
+
|
|
291
|
+
### Troubleshoot a partial plan
|
|
292
|
+
|
|
293
|
+
If `/execute` complains about illegal partial state:
|
|
294
|
+
|
|
295
|
+
1. `cat .soly/iterations/<latest>.md` — see what the last run did
|
|
296
|
+
2. Check if `SUMMARY.md` exists for the last plan
|
|
297
|
+
3. If yes, finish close-out: update `STATE.md` + `ROADMAP.md`
|
|
298
|
+
4. If no, either commit the SUMMARY or revert the production commits
|
|
299
|
+
|
|
300
|
+
## Where to look for answers
|
|
301
|
+
|
|
302
|
+
- **"What command does X"** → this skill, Slash commands section
|
|
303
|
+
- **"What does PLAN.md look like"** → this skill, Frontmatter section
|
|
304
|
+
- **"How to add a rule"** → this skill, Add a new rule section
|
|
305
|
+
- **"Why did the LLM do Y"** → `/why`
|
|
306
|
+
- **"What context is loaded"** → `soly_read(artifact: "state")` + `soly_doc_search(...)`
|
|
307
|
+
- **"What was the recent conversation"** → `soly_scratchpad()`
|
|
308
|
+
|
|
309
|
+
## Don'ts
|
|
310
|
+
|
|
311
|
+
- ❌ Edit `.soly/rules/` files you didn't write — those are project invariants
|
|
312
|
+
- ❌ Skip the SUMMARY — illegal partial state
|
|
313
|
+
- ❌ Spawn `soly-worker` or `soly-debugger` — use `soly-manager` (mode-switches)
|
|
314
|
+
- ❌ Write rules in code comments — use `.soly/rules/*.md` files
|
|
315
|
+
- ❌ Edit `.soly/phases/*/PLAN.md` after `status: in_progress` — create a new plan
|
|
316
|
+
- ❌ Put intent docs anywhere other than `.soly/docs/`
|
|
317
|
+
|
|
318
|
+
## When in doubt
|
|
319
|
+
|
|
320
|
+
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.
|