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
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**The project management framework for [pi-coding-agent](https://github.com/nicobailon/pi-coding-agent).**
|
|
6
6
|
|
|
7
|
-
Plans · State · Subagents · Multi-question picker ·
|
|
7
|
+
Plans · State · Subagents · Multi-question picker · Live task list.
|
|
8
8
|
|
|
9
9
|
One `npm install`. Zero config. Pure magic.
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ That's it. Restart pi, and you have:
|
|
|
30
30
|
|
|
31
31
|
- **A complete project management engine** — plans, state, subagent-driven execution
|
|
32
32
|
- **A multi-question picker** — `ask_pro` tool for the LLM
|
|
33
|
-
- **
|
|
33
|
+
- **Live task list + notifications** — see nudge + deprecation warnings as framed Box widgets
|
|
34
34
|
- **A live task list** — `todo_update` tool renders in the footer
|
|
35
35
|
- **7 soly agents** installed on first run
|
|
36
36
|
|
|
@@ -43,7 +43,7 @@ That's it. Restart pi, and you have:
|
|
|
43
43
|
| Write your own planning workflow | `/plan`, `/execute`, `/resume`, `/inspect` — ready |
|
|
44
44
|
| Manually dispatch subagents | `useSolyWorkerSubagents: true` — automatic routing |
|
|
45
45
|
| 3 different packages for pickers/tasks/agents | One package, one config, one install |
|
|
46
|
-
|
|
|
46
|
+
| Mode selection (oracle/scout/reviewer) | LLM picks via `subagent(...)` based on task brief |
|
|
47
47
|
| Re-invent the state machine | `.soly/STATE.md` + auto-managed phases |
|
|
48
48
|
|
|
49
49
|
---
|
|
@@ -56,7 +56,7 @@ GSD-inspired planning and execution. State is the source of truth, not vibes.
|
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
/plan # generate PLAN.md for the current phase
|
|
59
|
-
/execute #
|
|
59
|
+
/execute # execute plan directly (LLM does the work)
|
|
60
60
|
/resume # pick up a paused session
|
|
61
61
|
/inspect # show current state summary
|
|
62
62
|
/discuss 3 # talk through decisions before planning phase 3
|
|
@@ -94,30 +94,6 @@ ask_pro({
|
|
|
94
94
|
})
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
### 🎛 Rotor Switcher
|
|
98
|
-
|
|
99
|
-
Footer pill that's always there. `Ctrl+Tab` to cycle. No popup, no friction.
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
[model] · ⚡ worker · todos 2/5: write tests ← footer, always visible
|
|
103
|
-
[model] ▶ 🐢 oracle · todos 2/5: write tests ← after one Ctrl+Tab
|
|
104
|
-
[model] · 🔍 scout · todos 2/5: write tests ← after two
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Agents:
|
|
108
|
-
- `worker` — default, full read+write
|
|
109
|
-
- `oracle` — read-only decision advisor
|
|
110
|
-
- `scout` — codebase reconnaissance
|
|
111
|
-
- `researcher` — external docs, ecosystem
|
|
112
|
-
- `planner` — architecture and decomposition
|
|
113
|
-
- `context-builder` — hands off context to other agents
|
|
114
|
-
- `reviewer` — adversarial code review, read-only
|
|
115
|
-
- `delegate` — chains agents together
|
|
116
|
-
|
|
117
|
-
### 📝 Live Task List
|
|
118
|
-
|
|
119
|
-
`todo_update` tool — renders in the footer as `todos 2/5: current action`.
|
|
120
|
-
|
|
121
97
|
The LLM can update its own task list mid-turn. You watch progress without re-asking.
|
|
122
98
|
|
|
123
99
|
```ts
|
|
@@ -171,18 +147,18 @@ todo_update({
|
|
|
171
147
|
▼
|
|
172
148
|
┌──────────────────┐
|
|
173
149
|
│ switch/ │
|
|
174
|
-
│
|
|
150
|
+
│ (no rotors — │
|
|
175
151
|
│ │
|
|
176
152
|
│ Ctrl+Tab │
|
|
177
153
|
│ footer pill │
|
|
178
|
-
│
|
|
154
|
+
│ removed 1.4) │
|
|
179
155
|
└────────┬─────────┘
|
|
180
156
|
│
|
|
181
157
|
▼
|
|
182
158
|
┌──────────────────┐
|
|
183
159
|
│ 7 soly agents │
|
|
184
160
|
│ │
|
|
185
|
-
│
|
|
161
|
+
│ worker (cycle) │
|
|
186
162
|
│ soly-debugger │
|
|
187
163
|
│ soly-tester │
|
|
188
164
|
│ soly-reviewer │
|
|
@@ -196,7 +172,7 @@ todo_update({
|
|
|
196
172
|
|
|
197
173
|
## 📚 Documentation
|
|
198
174
|
|
|
199
|
-
- **Slash commands** — `/plan`, `/execute`, `/resume`, `/inspect`, `/discuss <N>`, `/quick <task
|
|
175
|
+
- **Slash commands** — `/plan`, `/execute`, `/resume`, `/inspect`, `/discuss <N>`, `/quick <task>`
|
|
200
176
|
- **Tools** — `ask_pro(question[])` and `todo_update(todo[])`
|
|
201
177
|
- **Events** — `session_start`, `before_agent_start`, `message_end`, `tool_call`, `tool_result`
|
|
202
178
|
- **State files** — `.soly/STATE.md`, `.soly/ROADMAP.md`, `.soly/phases/<N>-<slug>/<N>-PLAN.md`
|
package/agents-install.ts
CHANGED
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
// assets-install.ts — Idempotent install of soly-managed user assets
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// Soly ships
|
|
5
|
+
// Soly ships one kind of user-scope asset:
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
// The single `soly-manager` subagent (mode-switching executor).
|
|
9
|
-
//
|
|
10
|
-
// 2. Skills → `~/.pi/agent/skills/<name>/`
|
|
7
|
+
// Skills → `~/.pi/agent/skills/<name>/`
|
|
11
8
|
// The `soly-framework` skill — framework documentation the LLM
|
|
12
|
-
// loads on demand via the read tool.
|
|
9
|
+
// loads on demand via the read tool. This is the LLM's only
|
|
10
|
+
// "helper" for soly — pi doesn't need a separate subagent layer
|
|
11
|
+
// for plan execution (the LLM in the main session does it).
|
|
13
12
|
//
|
|
14
|
-
// pi discovers
|
|
15
|
-
// copy our shipped
|
|
13
|
+
// pi discovers the skill from `~/.pi/agent/`, so on first session_start
|
|
14
|
+
// we copy our shipped file there.
|
|
16
15
|
//
|
|
17
16
|
// IDEMPOTENT: if the target file already exists (user may have customized
|
|
18
17
|
// it), we do NOT overwrite. This is one-way "first install wins".
|
|
@@ -22,41 +21,19 @@ import * as fs from "node:fs";
|
|
|
22
21
|
import * as os from "node:os";
|
|
23
22
|
import * as path from "node:path";
|
|
24
23
|
|
|
25
|
-
/** soly agent files bundled with the extension. */
|
|
26
|
-
const SHIPPED_AGENTS = [
|
|
27
|
-
"soly-manager.md",
|
|
28
|
-
] as const;
|
|
29
|
-
|
|
30
24
|
/** soly skills bundled with the extension. Each entry is a directory
|
|
31
25
|
* under `skills/` containing a SKILL.md. */
|
|
32
26
|
const SHIPPED_SKILLS = [
|
|
33
27
|
"soly-framework",
|
|
34
28
|
] as const;
|
|
35
29
|
|
|
36
|
-
/** Where pi looks for user
|
|
37
|
-
* testability (otherwise we'd always write to the real user home).
|
|
38
|
-
* Agent files live directly in the dir (no `agents/` subfolder):
|
|
39
|
-
* ~/.agents/reviewer.md (NOT ~/.agents/agents/reviewer.md)
|
|
40
|
-
* This keeps the path clean and matches the project-level convention. */
|
|
41
|
-
function userAgentsDirs(): string[] {
|
|
42
|
-
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
43
|
-
return [
|
|
44
|
-
path.join(home, ".agents"), // vendor-neutral (preferred)
|
|
45
|
-
path.join(home, ".pi", "agent", "agents"), // pi native (legacy)
|
|
46
|
-
];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Where pi looks for user skills. */
|
|
30
|
+
/** Where pi looks for user skills. Respects HOME/USERPROFILE for
|
|
31
|
+
* testability (otherwise we'd always write to the real user home). */
|
|
50
32
|
function userSkillsDir(): string {
|
|
51
33
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
52
34
|
return path.join(home, ".pi", "agent", "skills");
|
|
53
35
|
}
|
|
54
36
|
|
|
55
|
-
/** Where this soly extension's `agents/` directory lives. */
|
|
56
|
-
function shippedAgentsDir(extensionRoot: string): string {
|
|
57
|
-
return path.join(extensionRoot, "agents");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
37
|
/** Where this soly extension's `skills/` directory lives. */
|
|
61
38
|
function shippedSkillsDir(extensionRoot: string): string {
|
|
62
39
|
return path.join(extensionRoot, "skills");
|
|
@@ -68,19 +45,7 @@ export interface InstallResult {
|
|
|
68
45
|
errors: string[];
|
|
69
46
|
}
|
|
70
47
|
|
|
71
|
-
/** Copy a
|
|
72
|
-
function copyIfMissing(from: string, to: string): "installed" | "skipped" | "error" {
|
|
73
|
-
if (!fs.existsSync(from)) return "error";
|
|
74
|
-
if (fs.existsSync(to)) return "skipped";
|
|
75
|
-
try {
|
|
76
|
-
fs.copyFileSync(from, to);
|
|
77
|
-
return "installed";
|
|
78
|
-
} catch {
|
|
79
|
-
return "error";
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Recursively copy a directory tree if destination doesn't exist. Idempotent. */
|
|
48
|
+
/** Copy a directory tree if destination doesn't exist. Idempotent. */
|
|
84
49
|
function copyDirIfMissing(from: string, to: string): "installed" | "skipped" | "error" {
|
|
85
50
|
if (!fs.existsSync(from)) return "error";
|
|
86
51
|
if (fs.existsSync(to)) return "skipped";
|
|
@@ -93,40 +58,6 @@ function copyDirIfMissing(from: string, to: string): "installed" | "skipped" | "
|
|
|
93
58
|
}
|
|
94
59
|
}
|
|
95
60
|
|
|
96
|
-
/** Install shipped soly agents to `~/.agents/` (vendor-neutral,
|
|
97
|
-
* preferred). Legacy `~/.pi/agent/agents/` copies are left alone —
|
|
98
|
-
* `discoverUserRotors` reads both, so old installs still work. */
|
|
99
|
-
export function installSolyAgents(extensionRoot: string): InstallResult {
|
|
100
|
-
const result: InstallResult = { installed: [], skipped: [], errors: [] };
|
|
101
|
-
const src = shippedAgentsDir(extensionRoot);
|
|
102
|
-
|
|
103
|
-
if (!fs.existsSync(src)) return result; // dev mode no-op
|
|
104
|
-
|
|
105
|
-
// Try vendor-neutral first, then fall back to pi's native dir.
|
|
106
|
-
let dst: string | null = null;
|
|
107
|
-
for (const candidate of userAgentsDirs()) {
|
|
108
|
-
try {
|
|
109
|
-
fs.mkdirSync(candidate, { recursive: true });
|
|
110
|
-
dst = candidate;
|
|
111
|
-
break;
|
|
112
|
-
} catch (err) {
|
|
113
|
-
result.errors.push(`mkdir ${candidate}: ${(err as Error).message}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (!dst) return result;
|
|
117
|
-
|
|
118
|
-
for (const name of SHIPPED_AGENTS) {
|
|
119
|
-
const from = path.join(src, name);
|
|
120
|
-
const to = path.join(dst, name);
|
|
121
|
-
const r = copyIfMissing(from, to);
|
|
122
|
-
if (r === "installed") result.installed.push(name);
|
|
123
|
-
else if (r === "skipped") result.skipped.push(name);
|
|
124
|
-
else result.errors.push(`missing source: ${from}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
61
|
/** Install shipped soly skills to `~/.pi/agent/skills/`. Idempotent. */
|
|
131
62
|
export function installSolySkills(extensionRoot: string): InstallResult {
|
|
132
63
|
const result: InstallResult = { installed: [], skipped: [], errors: [] };
|
|
@@ -147,33 +78,15 @@ export function installSolySkills(extensionRoot: string): InstallResult {
|
|
|
147
78
|
return result;
|
|
148
79
|
}
|
|
149
80
|
|
|
150
|
-
/** Install all soly assets (
|
|
81
|
+
/** Install all soly assets (skills only — agents are not shipped). */
|
|
151
82
|
export function installSolyAssets(extensionRoot: string): {
|
|
152
|
-
agents: InstallResult;
|
|
153
83
|
skills: InstallResult;
|
|
154
84
|
} {
|
|
155
85
|
return {
|
|
156
|
-
agents: installSolyAgents(extensionRoot),
|
|
157
86
|
skills: installSolySkills(extensionRoot),
|
|
158
87
|
};
|
|
159
88
|
}
|
|
160
89
|
|
|
161
|
-
/** Check which shipped soly agents are present across all user agent
|
|
162
|
-
* homes. A file counts as "installed" if it's in ANY of the dirs. */
|
|
163
|
-
export function checkSolyAgentsInstalled(extensionRoot: string): {
|
|
164
|
-
installed: string[];
|
|
165
|
-
missing: string[];
|
|
166
|
-
} {
|
|
167
|
-
const installed: string[] = [];
|
|
168
|
-
const missing: string[] = [];
|
|
169
|
-
for (const name of SHIPPED_AGENTS) {
|
|
170
|
-
const present = userAgentsDirs().some((dir) => fs.existsSync(path.join(dir, name)));
|
|
171
|
-
if (present) installed.push(name);
|
|
172
|
-
else missing.push(name);
|
|
173
|
-
}
|
|
174
|
-
return { installed, missing };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
90
|
/** Check which shipped soly skills are present in the user dir. */
|
|
178
91
|
export function checkSolySkillsInstalled(extensionRoot: string): {
|
|
179
92
|
installed: string[];
|
package/commands.ts
CHANGED
|
@@ -31,6 +31,10 @@ import {
|
|
|
31
31
|
type SolyState,
|
|
32
32
|
} from "./core.ts";
|
|
33
33
|
import type { SolyConfig } from "./config.ts";
|
|
34
|
+
import { migrateSolyDir } from "./migrate.js";
|
|
35
|
+
import { initSolyProject } from "./init.js";
|
|
36
|
+
import { readNotifications, formatNotifications } from "./notifications-log.js";
|
|
37
|
+
import { formatStatus } from "./status.js";
|
|
34
38
|
|
|
35
39
|
/** Minimum ui surface the command handlers actually need. */
|
|
36
40
|
export interface CommandUI {
|
|
@@ -332,6 +336,48 @@ What must the LLM do?
|
|
|
332
336
|
});
|
|
333
337
|
|
|
334
338
|
// ============================================================================
|
|
339
|
+
// /soly migrate — move .soly/ → .agents/ atomically
|
|
340
|
+
// ============================================================================
|
|
341
|
+
pi.registerCommand("soly-migrate", {
|
|
342
|
+
description:
|
|
343
|
+
"migrate project state from .soly/ to .agents/ (atomic rename, validates result, suggests git commit)",
|
|
344
|
+
handler: async (args, ctx) => {
|
|
345
|
+
const ui: CommandUI = {
|
|
346
|
+
notify: (t, k) => ctx.ui.notify(t, k ?? "info"),
|
|
347
|
+
select: async (label, options) => {
|
|
348
|
+
const result = await ctx.ui.select(label, options);
|
|
349
|
+
return result === undefined ? null : options.indexOf(result);
|
|
350
|
+
},
|
|
351
|
+
confirm: (title, message) => ctx.ui.confirm(title, message),
|
|
352
|
+
};
|
|
353
|
+
await migrateSolyDir(ctx.cwd, ui, { autoYes: args.includes("--yes") });
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// /soly init — scaffold new project
|
|
358
|
+
// ============================================================================
|
|
359
|
+
pi.registerCommand("soly-init", {
|
|
360
|
+
description:
|
|
361
|
+
"scaffold a new soly project (interactive: pick template — minimal/web-app/library/cli)",
|
|
362
|
+
handler: async (args, ctx) => {
|
|
363
|
+
const ui: CommandUI = {
|
|
364
|
+
notify: (t, k) => ctx.ui.notify(t, k ?? "info"),
|
|
365
|
+
select: async (label, options) => {
|
|
366
|
+
const result = await ctx.ui.select(label, options);
|
|
367
|
+
return result === undefined ? null : options.indexOf(result);
|
|
368
|
+
},
|
|
369
|
+
confirm: (title, message) => ctx.ui.confirm(title, message),
|
|
370
|
+
input: (label, placeholder) => ctx.ui.input(label, placeholder),
|
|
371
|
+
};
|
|
372
|
+
// Parse args: --template=X, --yes, --name=X
|
|
373
|
+
const template = (args.match(/--template[= ](\S+)/)?.[1] as
|
|
374
|
+
| "minimal" | "web-app" | "library" | "cli" | undefined) ?? undefined;
|
|
375
|
+
const autoYes = args.includes("--yes");
|
|
376
|
+
const projectName = args.match(/--name[= ](\S+)/)?.[1];
|
|
377
|
+
await initSolyProject(ctx.cwd, ui, { template, autoYes, projectName });
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
|
|
335
381
|
// /soly
|
|
336
382
|
// ============================================================================
|
|
337
383
|
|
|
@@ -372,8 +418,8 @@ What must the LLM do?
|
|
|
372
418
|
};
|
|
373
419
|
const subcommands: Record<string, SolySub> = {
|
|
374
420
|
// `agent` subcommand REMOVED — moved to the separate `pi-switch`
|
|
375
|
-
// extension
|
|
376
|
-
// Soly no longer owns
|
|
421
|
+
// extension (rotor switcher removed in 1.4.0).
|
|
422
|
+
// Soly no longer owns a rotor switcher.
|
|
377
423
|
config: {
|
|
378
424
|
description: "show merged config (per-project + global + defaults); edit .soly/config.json or ~/.soly/config.json",
|
|
379
425
|
run: () => {
|
package/config.ts
CHANGED
|
@@ -35,12 +35,10 @@ export interface SolyConfig {
|
|
|
35
35
|
preferAskPro: boolean;
|
|
36
36
|
/** When soly pause is invoked, also auto-save HANDOFF.json (currently always true; knob for future). */
|
|
37
37
|
autoCheckpointOnPause: boolean;
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* specialized subagent since the workflow template already
|
|
43
|
-
* contains soly instructions. */
|
|
38
|
+
/** DEPRECATED in 1.3.0. Soly no longer ships a subagent. The LLM
|
|
39
|
+
* executes plans directly using the slash commands + the
|
|
40
|
+
* soly-framework skill. This option is kept as a no-op for
|
|
41
|
+
* backward compat with old config files. */
|
|
44
42
|
useSolyWorkerSubagents: boolean;
|
|
45
43
|
};
|
|
46
44
|
display: {
|
package/index.ts
CHANGED
|
@@ -66,7 +66,7 @@ import { loadIntentDocs, buildIntentSection, loadInlineIntentBodies, type Intent
|
|
|
66
66
|
|
|
67
67
|
// Built-in sub-features (merged from former pi-asked, pi-agented packages):
|
|
68
68
|
import piAskExtension from "./ask/index.ts";
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
|
|
71
71
|
export default function solyExtension(pi: ExtensionAPI) {
|
|
72
72
|
// ============================================================================
|
|
@@ -82,18 +82,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
82
82
|
let sessionCwd = "";
|
|
83
83
|
|
|
84
84
|
// ============================================================================
|
|
85
|
-
// Rotor switcher (Shift+Tab cycles through available rotors)
|
|
86
|
-
// ============================================================================
|
|
87
|
-
|
|
88
85
|
// ============================================================================
|
|
89
|
-
// Rotor switcher: REMOVED. The rotor cycler is now owned by the
|
|
90
|
-
// separate `pi-switch` extension (footer pill + Ctrl+Tab + /rotor slash).
|
|
91
|
-
// Soly owns a single subagent (soly-manager.md) and the auto-install on
|
|
92
|
-
// opt-in. Workflows read the current agent from
|
|
93
|
-
// globalThis.__PI_SWITCH_AGENT__ (set by pi-switch).
|
|
94
|
-
// ============================================================================
|
|
95
|
-
|
|
96
|
-
// Config (per-project + global + defaults). Refreshed on session_start
|
|
97
86
|
// and on each session_start (the LLM can call /soly config to view).
|
|
98
87
|
let activeConfig: SolyConfig = DEFAULT_CONFIG;
|
|
99
88
|
const getActiveConfig = (): SolyConfig => activeConfig;
|
|
@@ -261,7 +250,6 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
261
250
|
const hint = buildNextHint(state);
|
|
262
251
|
const hintGroup = hint ? `${"\x1b[2m"}${hint}${"\x1b[0m"}` : "";
|
|
263
252
|
|
|
264
|
-
// Agent badge — owned by pi-switch extension (header bar + status line).
|
|
265
253
|
// Soly doesn't render the agent badge itself.
|
|
266
254
|
const agentGroup = "";
|
|
267
255
|
|
|
@@ -324,18 +312,6 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
324
312
|
getConfig: getActiveConfig,
|
|
325
313
|
});
|
|
326
314
|
|
|
327
|
-
// ============================================================================
|
|
328
|
-
// Agent switcher: Ctrl+Shift+A cycles through available subagents.
|
|
329
|
-
// (Shift+Tab is taken by pi's thinking-level cycler; Ctrl+Shift+A is unused
|
|
330
|
-
// and mnemonic for "A"gent.)
|
|
331
|
-
// ============================================================================
|
|
332
|
-
// Agent switcher REMOVED — moved to the separate `pi-switch` extension.
|
|
333
|
-
// Soly no longer owns Ctrl+Tab, the footer pill, or /rotor slash.
|
|
334
|
-
// The current agent is read by soly workflows from
|
|
335
|
-
// globalThis.__PI_SWITCH_AGENT__ (set by pi-switch), with a fallback
|
|
336
|
-
// to "worker" if pi-switch isn't installed.
|
|
337
|
-
// ============================================================================
|
|
338
|
-
|
|
339
315
|
registerWorkflows(pi, {
|
|
340
316
|
getState: () => state,
|
|
341
317
|
getInteractiveRules: () =>
|
|
@@ -354,7 +330,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
354
330
|
pi.on("session_start", async (event, ctx) => {
|
|
355
331
|
// Deprecation warning: if the project still uses `.soly/`, nudge the
|
|
356
332
|
// user toward `.agents/`. One-time per session.
|
|
357
|
-
if (isLegacySolyDir(ctx.cwd)) {
|
|
333
|
+
if (activeConfig.agent.useSolyWorkerSubagents && isLegacySolyDir(ctx.cwd)) {
|
|
358
334
|
notifyDeprecation(
|
|
359
335
|
ctx.ui,
|
|
360
336
|
`.soly/ (legacy)`,
|
|
@@ -405,21 +381,22 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
405
381
|
}
|
|
406
382
|
}
|
|
407
383
|
|
|
408
|
-
// Auto-install soly user-scope assets (soly-
|
|
409
|
-
//
|
|
410
|
-
// `agent.useSolyWorkerSubagents`
|
|
411
|
-
//
|
|
384
|
+
// Auto-install soly user-scope assets (soly-framework skill only,
|
|
385
|
+
// since 1.3.0 we don't ship a subagent) to ~/.pi/agent/skills/ on
|
|
386
|
+
// first run. Opt-in via config `agent.useSolyWorkerSubagents`
|
|
387
|
+
// (kept for backward compat, now a no-op for the skill install).
|
|
388
|
+
// Idempotent — respects any existing user-customized copies.
|
|
412
389
|
if (activeConfig.agent.useSolyWorkerSubagents) {
|
|
413
390
|
const extRoot = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, "$1"));
|
|
414
391
|
const assets = installSolyAssets(extRoot);
|
|
415
|
-
const installed =
|
|
392
|
+
const installed = assets.skills.installed.map((s) => `skill:${s}`);
|
|
416
393
|
if (installed.length > 0) {
|
|
417
394
|
ctx.ui.notify(
|
|
418
|
-
`soly: installed (${installed.join(", ")}) —
|
|
395
|
+
`soly: installed (${installed.join(", ")}) — pi will discover on next session`,
|
|
419
396
|
"info",
|
|
420
397
|
);
|
|
421
398
|
}
|
|
422
|
-
for (const e of
|
|
399
|
+
for (const e of assets.skills.errors) {
|
|
423
400
|
ctx.ui.notify(`soly: install error — ${e}`, "warning");
|
|
424
401
|
}
|
|
425
402
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-soly",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -38,10 +38,8 @@
|
|
|
38
38
|
"scratchpad.ts",
|
|
39
39
|
"tools.ts",
|
|
40
40
|
"agents-install.ts",
|
|
41
|
-
"agents",
|
|
42
41
|
"ask",
|
|
43
42
|
"skills",
|
|
44
|
-
"switch",
|
|
45
43
|
"workflows",
|
|
46
44
|
"workflows-data"
|
|
47
45
|
],
|