pi-soly 1.11.2 → 1.12.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 +60 -14
- package/artifact/index.ts +241 -0
- package/artifact/render.ts +239 -0
- package/artifact/server.ts +384 -0
- package/artifact/session.ts +368 -0
- package/ask/README.md +20 -8
- package/ask/index.ts +100 -36
- package/ask/picker.ts +345 -29
- package/commands.ts +246 -30
- package/config.ts +124 -8
- package/core.ts +48 -236
- package/deck/deck.ts +386 -0
- package/deck/index.ts +113 -0
- package/index.ts +153 -37
- package/iteration.ts +1 -9
- package/mcp/commands.ts +12 -0
- package/mcp/direct-tools.ts +19 -2
- package/mcp/index.ts +144 -2
- package/mcp/init.ts +11 -0
- package/mcp/lifecycle.ts +9 -2
- package/mcp/server-manager.ts +30 -18
- package/mcp/state.ts +7 -0
- package/mcp/tool-cache.ts +135 -0
- package/nudge.ts +20 -3
- package/package.json +10 -3
- package/skills/soly-framework/SKILL.md +64 -37
- package/tool-hints.ts +62 -0
- package/tools.ts +28 -100
- package/util.ts +250 -0
- package/visual/chrome.ts +166 -0
- package/visual/colors.ts +24 -0
- package/visual/data.ts +62 -0
- package/visual/footer.ts +129 -0
- package/visual/format.ts +73 -0
- package/visual/glyphs.ts +35 -0
- package/visual/gradient.ts +122 -0
- package/visual/index.ts +18 -0
- package/visual/list-panel.ts +207 -0
- package/visual/segments.ts +136 -0
- package/visual/style.ts +34 -0
- package/visual/topbar.ts +55 -0
- package/visual/welcome.ts +265 -0
- package/visual/working.ts +66 -0
- package/workflows/execute.ts +17 -7
- package/workflows/index.ts +91 -44
- package/workflows/inspect.ts +23 -26
- package/workflows/migrate.ts +85 -0
- package/workflows/parser.ts +4 -2
- package/workflows/planning.ts +9 -8
- package/workflows/verify.ts +194 -0
- package/workflows-data/discuss-phase.md +10 -6
- package/agents-install.ts +0 -106
- package/ask/prompt.ts +0 -41
- package/ask/tests/prompt.test.ts +0 -54
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: soly-framework
|
|
3
|
+
description: Use when the user invokes soly workflow commands (`/plan`, `/execute`, `/discuss`, `/inspect`, `/pause`, `/resume`, `/quick`, `/soly-init`, `/soly-migrate`, `/soly-status`, `/soly-log`) or asks how to use soly in pi-coding-agent — phases, plans, tasks, intent docs, ROADMAP/STATE state machine, rules, close-out order, and the available soly_* tools. Loaded as the complete reference for managing a soly project end-to-end (init → plan → execute → summary → state update).
|
|
4
|
+
priority: high
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# soly framework
|
|
2
8
|
|
|
3
9
|
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.
|
|
@@ -10,40 +16,49 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
10
16
|
2. **Project state** (`.soly/STATE.md`, `ROADMAP.md`) — where we are, current phase, recent decisions.
|
|
11
17
|
3. **Project rules** (`.soly/rules/`, `~/.soly/rules/`) — how to behave in this project.
|
|
12
18
|
|
|
13
|
-
**Workflow model — phases and
|
|
14
|
-
|
|
15
|
-
- A **phase** is a milestone (e.g. "01-foundation"). Has one or more `PLAN.md` files.
|
|
16
|
-
- A **plan** is one ordered execution unit. Has `<task>` blocks.
|
|
17
|
-
- A **task** is the smallest unit. Has type, description, verify, accept.
|
|
18
|
-
- **Close-out**: production code commits → `SUMMARY.md` → `STATE.md` updated → ROADMAP check.
|
|
19
|
+
**Workflow model — phases and tasks (unified):**
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
- A **phase** is a milestone (e.g. "01-foundation"). It groups **tasks** under `phases/<NN>-<slug>/tasks/`.
|
|
22
|
+
- A **task** is the unit of execution: its own dir with `PLAN.md` (frontmatter: `id`, `kind`, `status`, `depends-on`, optional `feature`) and, when done, `SUMMARY.md`. Tasks run in dependency order.
|
|
23
|
+
- `soly plan <N>` writes a phase's tasks · `soly execute <N>` runs its ready tasks (deps met) in order · `soly verify` reviews.
|
|
24
|
+
- **Close-out** (per task): production commits → task `SUMMARY.md` → flip `status: done` → `STATE.md` / ROADMAP check.
|
|
25
|
+
- Legacy projects (standalone `NN-MM-PLAN.md` files, or a `features/` dir) still load and run; `soly migrate` converts them to this layout.
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|---|---|
|
|
24
|
-
| `/plan [N]` | Generate or update `PLAN.md` for phase N (or current phase) |
|
|
25
|
-
| `/execute [N[.MM]]` | Execute plan(s) in phase N. `N` = all plans. `N.MM` = specific plan. The LLM (you) executes directly. |
|
|
26
|
-
| `/discuss N` | Discussion-driven scoping for phase N — capture decisions before planning |
|
|
27
|
-
| `/inspect` | One-screen summary: position, phases, recent decisions |
|
|
28
|
-
| `/pause` | Save handoff (`HANDOFF.json` + `.continue-here.md`) for later resume |
|
|
29
|
-
| `/resume` | Restore from a paused handoff |
|
|
30
|
-
| `/quick <task>` | One-shot task that doesn't need a full plan — direct dispatch |
|
|
31
|
-
| `/soly` | Project state inspection (alias for `/inspect`) |
|
|
32
|
-
| `/why` | Show what context the LLM's last turn was based on |
|
|
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 |
|
|
27
|
+
## Commands
|
|
37
28
|
|
|
38
|
-
|
|
29
|
+
Workflow verbs are **plain text** — type `soly <verb>` (NOT a slash command):
|
|
39
30
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
| Verb | What it does |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `soly discuss <N>` | Discussion-driven scoping for phase N — interactive, uses the `ask_pro` picker |
|
|
34
|
+
| `soly plan <N>` | Generate `PLAN.md` for phase N (also `<task-id>`, `--new-task`, `--feature`) |
|
|
35
|
+
| `soly execute <N[.MM]>` | Execute a phase / one plan / a task / `--all` / `--feature` |
|
|
36
|
+
| `soly verify [N] [fresh]` | Self-review loop until "No issues found." (max N; `verify stop` to exit; `fresh` = fresh-context) |
|
|
37
|
+
| `soly pause` · `soly compact` | Save a handoff (compact also compresses the session) |
|
|
38
|
+
| `soly resume [N]` | Restore from a handoff |
|
|
39
|
+
| `soly status` · `log` · `diff` · `doctor` · `iterations` · `todos` | Quick read-outs (no LLM round-trip) |
|
|
43
40
|
|
|
44
|
-
**
|
|
41
|
+
**Slash commands** (pi's command surface):
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
| Command | What it does |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `/rules` · `/docs` | Open the rules / intent-docs **modal** (fuzzy list + preview; `e/d/r` enable·disable·reload on rules). A subcommand (`/rules stats`, `/docs stats`, …) prints to chat instead of opening the modal. |
|
|
46
|
+
| `/soly [<sub>]` | Project-state inspection (position, state, plan, roadmap, phases, tasks, …); bare `/soly` opens the picker |
|
|
47
|
+
| `/soly-init` · `/soly-migrate` | Scaffold a project · migrate `.soly/` → `.agents/` |
|
|
48
|
+
| `/why` | What rules + state grounded the last turn |
|
|
49
|
+
| `/rulewizard` | Rule vs .editorconfig vs linter guide |
|
|
50
|
+
|
|
51
|
+
## Delegation
|
|
52
|
+
|
|
53
|
+
`soly plan` and `soly execute` delegate the heavy work to a `worker` subagent
|
|
54
|
+
(via the `subagent(...)` tool from pi-subagents); the parent session keeps the
|
|
55
|
+
close-out (production commits → `SUMMARY.md` → `STATE.md` → then `soly verify`).
|
|
56
|
+
If the `subagent` tool is NOT installed they run **inline** in the main session
|
|
57
|
+
instead — `soly doctor` reports which mode is active. First-party delegation is
|
|
58
|
+
on the roadmap. Other agents are read-only helpers: `oracle` (second opinion),
|
|
59
|
+
`scout` (recon), `reviewer` (adversarial review). `soly discuss` is always
|
|
60
|
+
interactive in the main session (not delegated). Rotors / `Ctrl+Tab` cycling
|
|
61
|
+
were removed in 1.4.0.
|
|
47
62
|
|
|
48
63
|
## File structure
|
|
49
64
|
|
|
@@ -64,10 +79,12 @@ As of 1.4.0, soly no longer ships rotors. No `/rotor` command, no `Ctrl+Tab` cyc
|
|
|
64
79
|
│ │ ├── 01-foundation/
|
|
65
80
|
│ │ │ ├── 01-CONTEXT.md # domain + decisions for phase 1
|
|
66
81
|
│ │ │ ├── 01-RESEARCH.md # what we looked up
|
|
67
|
-
│ │ │
|
|
68
|
-
│ │ │
|
|
69
|
-
│ │ │ ├──
|
|
70
|
-
│ │ │ └──
|
|
82
|
+
│ │ │ └── tasks/ # unified model: one dir per task
|
|
83
|
+
│ │ │ ├── auth-login-a3f9/
|
|
84
|
+
│ │ │ │ ├── PLAN.md # frontmatter: id, kind, status, depends-on
|
|
85
|
+
│ │ │ │ └── SUMMARY.md
|
|
86
|
+
│ │ │ └── auth-token-b1c2/
|
|
87
|
+
│ │ │ └── PLAN.md
|
|
71
88
|
│ │ └── 02-feature-x/
|
|
72
89
|
│ │ └── ...
|
|
73
90
|
│ ├── iterations/ # per-execution context bundles (auto)
|
|
@@ -211,10 +228,22 @@ Once production commits exist, returning without a committed `SUMMARY.md` is an
|
|
|
211
228
|
| `soly_env()` | Detect runtime (package manager, runtimes, services, scripts) |
|
|
212
229
|
| `soly_snippet(path, offset, limit)` | Read bounded line range with line numbers |
|
|
213
230
|
| `soly_doc_search(query, limit)` | Search .md/.html under cwd (prioritizes intent docs) |
|
|
214
|
-
| `soly_intent()` | List 0-point intent docs from `.soly/docs/` |
|
|
215
231
|
| `soly_scratchpad(limit)` | Recent conversation recap (one line per turn) |
|
|
216
|
-
| `ask_pro(questions)` | Multi-question picker (tabbed, single/multi-select, ⭐,
|
|
217
|
-
| `
|
|
232
|
+
| `ask_pro(questions)` | Multi-question picker (tabbed, single/multi-select, ⭐, `preview` w/ code highlight, `allowOther`, `freeText`, `minSelect`/`maxSelect`, `s` to skip, notes) — preferred for structured input |
|
|
233
|
+
| `decision_deck(options)` | Full-screen TUI deck — one card per option with a highlighted code snippet + pros/cons. For design/architecture forks where the choice hinges on the concrete code shape |
|
|
234
|
+
| `html_artifact(title, html, id?, assets?)` | Render HTML (full doc or body fragment) and serve it from a per-session browser gallery SPA (sidebar + iframe + filter + live updates, one stable URL) — soly's "artifacts". `id` updates in place; `assets` writes sibling files. For visual output: example galleries, comparisons, diagrams |
|
|
235
|
+
| `soly_save_discuss_checkpoint(...)` · `soly_finish_discuss(...)` | Save / finalize a `soly discuss` session (writes CONTEXT.md) |
|
|
236
|
+
| `soly_ask_user(...)` | Single-question picker — **deprecated**, prefer `ask_pro` |
|
|
237
|
+
|
|
238
|
+
### Visual & interaction tools — when to reach for each
|
|
239
|
+
|
|
240
|
+
The main system prompt only points at these; the detail is here.
|
|
241
|
+
|
|
242
|
+
**`ask_pro`** — multi-question picker. Use for 1–6 related questions where the user picks concrete answers to move forward (e.g. `soly discuss` scoping). NOT for simple yes/no, a single open-ended prompt, questions already answered, or trivial clarifications — use plain text. Per option, `preview` shows a side panel while focused (put a code/API/config snippet there; fenced ```code is highlighted). `freeText` = optional typed answer; `allowOther` = escape hatch; `minSelect`/`maxSelect` bound multi-select. The user can skip (`s`, returned `(skipped)`) or note (`n`, returned `// note:` — treat as a hard constraint).
|
|
243
|
+
|
|
244
|
+
**`decision_deck`** — full-screen cards (one per option) with a highlighted code snippet + pros/cons. Reach for it when the choice hinges on the concrete code/structure of each option, not a label, and there are 2–6 alternatives worth comparing side-by-side. Prefer `ask_pro` for short-label choices; don't use it for trivial/yes-no.
|
|
245
|
+
|
|
246
|
+
**`html_artifact`** — render HTML served from a per-session browser gallery SPA (sidebar + iframe + filter + live SSE updates, one stable localhost URL). Use when a visual rendered result beats terminal text: example galleries, before/after or side-by-side comparisons, tables, diagrams, a small HTML/CSS/SVG demo. Pass `title` + `html` (a body fragment is fine — it's wrapped in a themed skeleton; put code in `<pre><code>`). Pass `id` to re-render/update an artifact in place (instead of piling up copies); pass `assets: [{path, content, encoding?}]` to write sibling files the HTML references by relative path (e.g. an image or `data.json`). The theme is overridable per project via `.soly/artifact-theme.css`. Keep it self-contained unless you use `assets` — no external/CDN requests. Don't use it for a single snippet (markdown code block) or prose. Mention the returned URL to the user.
|
|
218
247
|
|
|
219
248
|
## Common workflows
|
|
220
249
|
|
|
@@ -247,8 +276,6 @@ You are a data scientist. ...
|
|
|
247
276
|
3. `~/.agents/` — user vendor-neutral (preferred)
|
|
248
277
|
4. `~/.pi/agent/agents/` — user pi native (legacy)
|
|
249
278
|
|
|
250
|
-
`Ctrl+Tab` to see them in the cycle. (Removed in 1.4.0 — use `subagent(...)` directly.)
|
|
251
|
-
|
|
252
279
|
### Add a feature to an existing phase
|
|
253
280
|
|
|
254
281
|
1. `/plan 1.05` (next plan number)
|
package/tool-hints.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// tool-hints.ts — per-turn affordance hints for soly's interactive tools
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Scans the user's prompt for trigger words (bilingual RU/EN) and, when matched,
|
|
6
|
+
// returns a small system-prompt section nudging the LLM to consider the right
|
|
7
|
+
// interactive tool: html_artifact (examples / visual output), decision_deck
|
|
8
|
+
// (comparing options), or ask_pro (clarifying questions).
|
|
9
|
+
//
|
|
10
|
+
// Soft + dynamic: the hint is only injected on turns that actually mention these
|
|
11
|
+
// things (zero token cost otherwise) and explicitly says "only if it helps", so
|
|
12
|
+
// it steers without forcing. Pure: prompt string in, hint string out.
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
export type ToolHint = { artifact: boolean; deck: boolean; ask: boolean };
|
|
16
|
+
|
|
17
|
+
// Trigger words. Stems chosen to catch RU inflections (вариант/варианты/…) and
|
|
18
|
+
// common EN forms. Kept narrow enough to avoid firing on every prompt.
|
|
19
|
+
const TRIGGERS = {
|
|
20
|
+
artifact: [
|
|
21
|
+
/пример/i, /образец/i, /галере/i, /шпаргалк/i, /таблиц/i, /диаграмм/i,
|
|
22
|
+
/визуализ/i, /макет/i, /дашборд/i,
|
|
23
|
+
/\bexamples?\b/i, /\bsamples?\b/i, /\bdemo\b/i, /\bgallery\b/i,
|
|
24
|
+
/cheat\s*sheet/i, /\btables?\b/i, /\bdiagram/i, /\bvisuali[sz]/i,
|
|
25
|
+
/\bmock\s?up/i, /\bdashboard\b/i,
|
|
26
|
+
],
|
|
27
|
+
deck: [
|
|
28
|
+
/вариант/i, /сравн/i, /альтернатив/i, /\bлибо\b/i, /какой подход/i,
|
|
29
|
+
/что лучше/i, /выбрать (?:между|из)/i,
|
|
30
|
+
/\boptions?\b/i, /\bcompare\b/i, /\bcomparison\b/i, /\bvs\.?\b/i,
|
|
31
|
+
/\btrade-?offs?\b/i, /which approach/i, /\bpros and cons\b/i,
|
|
32
|
+
],
|
|
33
|
+
ask: [
|
|
34
|
+
/уточн/i, /несколько вопрос/i, /опросник/i,
|
|
35
|
+
/\bclarif/i, /\ba few questions\b/i,
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Which interactive tools the prompt's wording hints at. */
|
|
40
|
+
export function detectToolHints(prompt: string): ToolHint {
|
|
41
|
+
const has = (res: RegExp[]): boolean => res.some((r) => r.test(prompt));
|
|
42
|
+
return {
|
|
43
|
+
artifact: has(TRIGGERS.artifact),
|
|
44
|
+
deck: has(TRIGGERS.deck),
|
|
45
|
+
ask: has(TRIGGERS.ask),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Build the affordance section, or null when nothing matched. */
|
|
50
|
+
export function buildToolHintSection(h: ToolHint): string | null {
|
|
51
|
+
const bits: string[] = [];
|
|
52
|
+
if (h.artifact)
|
|
53
|
+
bits.push("- Examples / a table / a visual result: **ask the user first** — render it in the browser as an artifact (`html_artifact`), or just as text here? Then proceed accordingly.");
|
|
54
|
+
if (h.deck)
|
|
55
|
+
bits.push("- Comparing options/alternatives: **ask the user first** — show them as a full-screen deck (`decision_deck`, code + pros/cons), or inline as text? Then proceed.");
|
|
56
|
+
if (h.ask)
|
|
57
|
+
bits.push("- Several things to clarify → use `ask_pro` (one batched picker).");
|
|
58
|
+
if (bits.length === 0) return null;
|
|
59
|
+
return `\n## soly — interactive output for this turn\n\n${bits.join(
|
|
60
|
+
"\n",
|
|
61
|
+
)}\nAsk once and briefly (a one-line question is fine). If the content is tiny or the format is obvious, just answer in text.`;
|
|
62
|
+
}
|
package/tools.ts
CHANGED
|
@@ -22,7 +22,6 @@ import { detectEnv, type EnvSummary } from "./env.ts";
|
|
|
22
22
|
import type { SolyConfig } from "./config.ts";
|
|
23
23
|
import { buildDocIndex, searchDocs, readSnippet, stripHtml } from "./docs.ts";
|
|
24
24
|
import { buildScratchpad, SCRATCHPAD_LIMITS } from "./scratchpad.ts";
|
|
25
|
-
import { loadIntentDocs, buildIntentSection, type IntentDoc } from "./intent.ts";
|
|
26
25
|
|
|
27
26
|
const execFileAsync = promisify(execFile);
|
|
28
27
|
|
|
@@ -67,7 +66,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
67
66
|
name: "soly_read",
|
|
68
67
|
label: "soly read",
|
|
69
68
|
description:
|
|
70
|
-
"
|
|
69
|
+
"Read a .soly/ artifact (state, plan, context, research, roadmap, requirements, project, milestone, task). `phase` targets a specific phase (default: current); `taskId` for the task artifact. Returns the file text.",
|
|
71
70
|
parameters: Type.Object({
|
|
72
71
|
artifact: StringEnum([
|
|
73
72
|
"state",
|
|
@@ -190,7 +189,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
190
189
|
name: "soly_log_decision",
|
|
191
190
|
label: "soly log decision",
|
|
192
191
|
description:
|
|
193
|
-
"
|
|
192
|
+
"Append a one-line decision + rationale to the Decisions table in .soly/STATE.md (creates it if missing). For meaningful choices: scope cuts, library picks, trade-offs. `phase` defaults to current.",
|
|
194
193
|
parameters: Type.Object({
|
|
195
194
|
decision: Type.String({ description: "The decision made (one line)." }),
|
|
196
195
|
rationale: Type.String({ description: "Why this decision was made (one line)." }),
|
|
@@ -275,7 +274,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
275
274
|
name: "soly_list_tasks",
|
|
276
275
|
label: "soly list tasks",
|
|
277
276
|
description:
|
|
278
|
-
"
|
|
277
|
+
"List all tasks across features (kind, status, priority, deps). Use before `soly execute <task-id>` / `--all`.",
|
|
279
278
|
parameters: Type.Object({}),
|
|
280
279
|
async execute(_id, _params, _signal, _onUpdate, _ctx) {
|
|
281
280
|
const state = getState();
|
|
@@ -301,7 +300,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
301
300
|
name: "soly_list_phases",
|
|
302
301
|
label: "soly list phases",
|
|
303
302
|
description:
|
|
304
|
-
"
|
|
303
|
+
"List phases with plan count, C/R (context/research) markers, and current-position marker (→). Use before `soly plan <N>` / `execute <N>`.",
|
|
305
304
|
parameters: Type.Object({}),
|
|
306
305
|
async execute(_id, _params, _signal, _onUpdate, _ctx) {
|
|
307
306
|
const state = getState();
|
|
@@ -330,7 +329,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
330
329
|
name: "soly_todos",
|
|
331
330
|
label: "soly todos",
|
|
332
331
|
description:
|
|
333
|
-
"
|
|
332
|
+
"Scan the tree for TODO/FIXME/HACK/XXX/NOTE comments, grouped by file (common source extensions; excludes node_modules/.git/dist/build/.soly). Needs ripgrep on PATH. `paths` overrides root, `limit` caps (default 200).",
|
|
334
333
|
parameters: Type.Object({
|
|
335
334
|
paths: Type.Optional(
|
|
336
335
|
Type.Array(Type.String(), {
|
|
@@ -462,7 +461,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
462
461
|
name: "soly_env",
|
|
463
462
|
label: "soly env",
|
|
464
463
|
description:
|
|
465
|
-
"
|
|
464
|
+
"Detect the project's environment as a one-screen summary: package manager, runtimes, key deps, scripts, services (from compose), and tooling flags (ts/tests/docker/ci). Answers 'what test runner / package manager / is docker used'.",
|
|
466
465
|
parameters: Type.Object({}),
|
|
467
466
|
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
468
467
|
const env = detectEnv(ctx.cwd);
|
|
@@ -500,19 +499,14 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
500
499
|
name: "soly_snippet",
|
|
501
500
|
label: "soly snippet",
|
|
502
501
|
description:
|
|
503
|
-
"
|
|
502
|
+
"Read a bounded line range from a file with line numbers — a specific function/section without the whole file. `offset` 0-indexed, `limit` default 100 (cap 500). For .html, `format=\"stripped\"` removes tags.",
|
|
504
503
|
parameters: Type.Object({
|
|
505
504
|
path: Type.String({ description: "File path (relative to cwd or absolute)." }),
|
|
506
|
-
offset: Type.Optional(
|
|
507
|
-
|
|
508
|
-
),
|
|
509
|
-
limit: Type.Optional(
|
|
510
|
-
Type.Number({ description: "Max lines to return. Default 100, cap 500." }),
|
|
511
|
-
),
|
|
505
|
+
offset: Type.Optional(Type.Number({ description: "0-indexed start line. Default 0." })),
|
|
506
|
+
limit: Type.Optional(Type.Number({ description: "Max lines. Default 100, cap 500." })),
|
|
512
507
|
format: Type.Optional(
|
|
513
508
|
StringEnum(["raw", "stripped"] as const, {
|
|
514
|
-
description:
|
|
515
|
-
"Output format. \"raw\" returns content as-is. \"stripped\" removes HTML tags (for .html files). Default \"raw\".",
|
|
509
|
+
description: '"raw" (default) or "stripped" (remove HTML tags).',
|
|
516
510
|
}),
|
|
517
511
|
),
|
|
518
512
|
}),
|
|
@@ -575,7 +569,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
575
569
|
name: "soly_doc_search",
|
|
576
570
|
label: "soly doc search",
|
|
577
571
|
description:
|
|
578
|
-
"
|
|
572
|
+
"Search .md/.html under cwd for a query (intent docs prioritized, hits tagged [intent]/[phase-intent]/[project]). Use to find docs before loading one with soly_snippet. `limit` default 10 (cap 50).",
|
|
579
573
|
parameters: Type.Object({
|
|
580
574
|
query: Type.String({ description: "Search query (substring, case-insensitive)." }),
|
|
581
575
|
limit: Type.Optional(
|
|
@@ -633,7 +627,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
633
627
|
name: "soly_scratchpad",
|
|
634
628
|
label: "soly scratchpad",
|
|
635
629
|
description:
|
|
636
|
-
"
|
|
630
|
+
"Compact recap of the recent conversation (one line per turn). Use to recover context after a break or brief a sibling subagent. `limit` default 20 user-turns (cap 50).",
|
|
637
631
|
parameters: Type.Object({
|
|
638
632
|
limit: Type.Optional(
|
|
639
633
|
Type.Number({
|
|
@@ -682,51 +676,6 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
682
676
|
},
|
|
683
677
|
});
|
|
684
678
|
|
|
685
|
-
// ============================================================================
|
|
686
|
-
// soly_intent — refresh / list the project's 0-point intent docs
|
|
687
|
-
// ============================================================================
|
|
688
|
-
|
|
689
|
-
pi.registerTool({
|
|
690
|
-
name: "soly_intent",
|
|
691
|
-
label: "soly intent",
|
|
692
|
-
description:
|
|
693
|
-
"list the project's 0-point intent docs from `.soly/docs/` — the business/domain/tech-intent documents the user wrote BEFORE any soly plans. Use before planning, discussing, or executing a phase to ensure alignment with the user's vision. Supports `.md` and `.html` (parsed for `<title>`/`<h1>`/`<meta description>`). Phase-specific docs under `.soly/phases/<N>/docs/` are also included if present. Pass no arguments — this tool always returns the full list.",
|
|
694
|
-
parameters: Type.Object({}),
|
|
695
|
-
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
696
|
-
const docs = loadIntentDocs(ctx.cwd);
|
|
697
|
-
const { section } = buildIntentSection(docs);
|
|
698
|
-
|
|
699
|
-
if (docs.length === 0) {
|
|
700
|
-
return {
|
|
701
|
-
content: [
|
|
702
|
-
{
|
|
703
|
-
type: "text",
|
|
704
|
-
text:
|
|
705
|
-
`soly_intent: no docs found in ${path.join(ctx.cwd, ".soly", "docs")}.\n\n` +
|
|
706
|
-
`Project intent is the user's vision written BEFORE soly plans. If empty, ask the user to drop their intent docs (business glossary, design vision, domain concepts) into .soly/docs/ as .md or .html files, then run soly_intent again.`,
|
|
707
|
-
},
|
|
708
|
-
],
|
|
709
|
-
details: { count: 0 },
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Render the same section that goes into the system prompt, so the
|
|
714
|
-
// model sees a consistent view whether it asks or not.
|
|
715
|
-
const out: string[] = [];
|
|
716
|
-
out.push(`soly_intent: ${docs.length} document(s) in .soly/docs/:`);
|
|
717
|
-
out.push("");
|
|
718
|
-
out.push(section);
|
|
719
|
-
out.push("");
|
|
720
|
-
out.push(
|
|
721
|
-
"To read a specific doc, use soly_snippet(path=\".soly/docs/<name>\", offset=N, limit=M). For HTML files, pass format=\"stripped\" to get plain text.",
|
|
722
|
-
);
|
|
723
|
-
return {
|
|
724
|
-
content: [{ type: "text", text: out.join("\n") }],
|
|
725
|
-
details: { count: docs.length, paths: docs.map((d) => d.relPath) },
|
|
726
|
-
};
|
|
727
|
-
},
|
|
728
|
-
});
|
|
729
|
-
|
|
730
679
|
// ============================================================================
|
|
731
680
|
// soly_ask_user — multiple-choice picker (for `soly discuss` interactive flow)
|
|
732
681
|
// ============================================================================
|
|
@@ -735,29 +684,18 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
735
684
|
name: "soly_ask_user",
|
|
736
685
|
label: "soly ask user",
|
|
737
686
|
description:
|
|
738
|
-
"
|
|
687
|
+
"DEPRECATED — prefer `ask_pro` (multi-question picker). Fallback only. Asks one multiple-choice question via pi's picker; option #1 is the recommended answer (⭐ prefix + a `rationale`). `allowOther` adds a custom-text 'Other…'. Returns the chosen text (or custom string); Esc → cancelled.",
|
|
739
688
|
parameters: Type.Object({
|
|
740
|
-
title: Type.String({
|
|
741
|
-
|
|
742
|
-
}),
|
|
743
|
-
question: Type.String({
|
|
744
|
-
description: "The question to ask the user (one short sentence).",
|
|
745
|
-
}),
|
|
689
|
+
title: Type.String({ description: "Title above the picker." }),
|
|
690
|
+
question: Type.String({ description: "The question (one sentence)." }),
|
|
746
691
|
options: Type.Array(Type.String(), {
|
|
747
|
-
description:
|
|
748
|
-
"2-4 concrete options. Option #1 is the recommended answer (mark with ⭐ prefix in the label).",
|
|
692
|
+
description: "2-4 options; #1 is recommended (⭐ prefix).",
|
|
749
693
|
}),
|
|
750
694
|
rationale: Type.Optional(
|
|
751
|
-
Type.String({
|
|
752
|
-
description:
|
|
753
|
-
"1-2 sentence note explaining why option #1 is recommended. Shown above the picker.",
|
|
754
|
-
}),
|
|
695
|
+
Type.String({ description: "Why #1 is recommended (shown above the picker)." }),
|
|
755
696
|
),
|
|
756
697
|
allowOther: Type.Optional(
|
|
757
|
-
Type.Boolean({
|
|
758
|
-
description:
|
|
759
|
-
"If true, appends an 'Other…' option that opens a text-input dialog for a custom answer. The custom string is returned in place of the option index.",
|
|
760
|
-
}),
|
|
698
|
+
Type.Boolean({ description: "Add a custom-text 'Other…' choice." }),
|
|
761
699
|
),
|
|
762
700
|
}),
|
|
763
701
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
@@ -880,45 +818,35 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
880
818
|
name: "soly_finish_discuss",
|
|
881
819
|
label: "soly finish discuss",
|
|
882
820
|
description:
|
|
883
|
-
"Finalize a `soly discuss <N>` session: write
|
|
821
|
+
"Finalize a `soly discuss <N>` session: write `<phase>-CONTEXT.md` with all decisions and delete the checkpoint. Call AFTER all gray-area questions are answered — not for partial progress (use soly_save_discuss_checkpoint for that).",
|
|
884
822
|
parameters: Type.Object({
|
|
885
|
-
phase_number: Type.Number({
|
|
886
|
-
description: "Phase number being discussed (e.g. 5).",
|
|
887
|
-
}),
|
|
823
|
+
phase_number: Type.Number({ description: "Phase number being discussed." }),
|
|
888
824
|
domain: Type.String({
|
|
889
825
|
description:
|
|
890
|
-
"1-2 paragraphs: what this phase delivers
|
|
826
|
+
"1-2 paragraphs: what this phase delivers (grounded in ROADMAP + intent, no implementation details).",
|
|
891
827
|
}),
|
|
892
828
|
decisions: Type.Array(
|
|
893
829
|
Type.Object({
|
|
894
|
-
category: Type.String({
|
|
895
|
-
description:
|
|
896
|
-
"Category of the decision (e.g. 'Session handling', 'Error responses', 'Token storage').",
|
|
897
|
-
}),
|
|
830
|
+
category: Type.String({ description: "Decision category (e.g. 'Session handling')." }),
|
|
898
831
|
choice: Type.String({ description: "What was chosen." }),
|
|
899
832
|
rationale: Type.Optional(
|
|
900
|
-
Type.String({
|
|
901
|
-
description: "Why this choice — default 'user discretion' if not specified.",
|
|
902
|
-
}),
|
|
833
|
+
Type.String({ description: "Why (default 'user discretion')." }),
|
|
903
834
|
),
|
|
904
835
|
}),
|
|
905
|
-
{ description: "All decisions captured
|
|
836
|
+
{ description: "All decisions captured this round." },
|
|
906
837
|
),
|
|
907
838
|
canonical_refs: Type.Optional(
|
|
908
839
|
Type.Array(Type.String(), {
|
|
909
840
|
description:
|
|
910
|
-
"MANDATORY.
|
|
841
|
+
"MANDATORY. Files the planner needs (intent docs, REQUIREMENTS, contracts), full paths from `.soly/`.",
|
|
911
842
|
}),
|
|
912
843
|
),
|
|
913
844
|
deferred_ideas: Type.Optional(
|
|
914
|
-
Type.Array(Type.String(), {
|
|
915
|
-
description: "Scope-creep items for future phases (or empty array).",
|
|
916
|
-
}),
|
|
845
|
+
Type.Array(Type.String(), { description: "Scope-creep items for future phases." }),
|
|
917
846
|
),
|
|
918
847
|
codebase_context: Type.Optional(
|
|
919
848
|
Type.Array(Type.String(), {
|
|
920
|
-
description:
|
|
921
|
-
"Reusable assets/patterns the planner should know (e.g. 'src/components/Card.tsx — already has rounded/shadow variants, reuse for consistency').",
|
|
849
|
+
description: "Reusable assets/patterns the planner should know (path — what to reuse).",
|
|
922
850
|
}),
|
|
923
851
|
),
|
|
924
852
|
}),
|
|
@@ -1074,7 +1002,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
1074
1002
|
name: "soly_save_discuss_checkpoint",
|
|
1075
1003
|
label: "soly save discuss checkpoint",
|
|
1076
1004
|
description:
|
|
1077
|
-
"Save a partial-progress checkpoint for `soly discuss <N
|
|
1005
|
+
"Save a partial-progress checkpoint for `soly discuss <N>` (call after each decision so a quit doesn't lose progress; the next `soly discuss <N>` resumes from it). When done, call `soly_finish_discuss`.",
|
|
1078
1006
|
parameters: Type.Object({
|
|
1079
1007
|
phase_number: Type.Number({ description: "Phase number being discussed." }),
|
|
1080
1008
|
decisions: Type.Array(
|