pi-soly 1.13.4 → 1.14.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 +9 -10
- package/artifact/index.ts +3 -3
- package/artifact/render.ts +2 -2
- package/ask/README.md +3 -1
- package/ask/index.ts +16 -2
- package/codemap.ts +2 -2
- package/commands.ts +40 -67
- package/config.ts +20 -12
- package/core.ts +16 -18
- package/docs.ts +4 -4
- package/index.ts +39 -29
- package/init.ts +7 -7
- package/intent.ts +12 -12
- package/iteration.ts +12 -12
- package/mcp/ext-apps-bridge.ts +76 -0
- package/mcp/index.ts +5 -0
- package/mcp/metadata-cache.ts +1 -1
- package/mcp/tool-metadata.ts +1 -1
- package/mcp/ui-resource-handler.ts +4 -4
- package/mcp/ui-server.ts +1 -1
- package/notification.ts +1 -1
- package/notifications-log.ts +2 -2
- package/nudge.ts +41 -6
- package/package.json +1 -2
- package/skills/soly-framework/SKILL.md +34 -42
- package/status.ts +2 -2
- package/tools.ts +14 -14
- package/util.ts +2 -2
- package/visual/welcome.ts +3 -3
- package/workflows/execute.ts +16 -16
- package/workflows/index.ts +4 -14
- package/workflows/inspect.ts +16 -15
- package/workflows/parser.ts +2 -3
- package/workflows/pause.ts +5 -5
- package/workflows/planning.ts +18 -18
- package/workflows/quick.ts +7 -7
- package/workflows/resume.ts +14 -14
- package/workflows-data/discuss-phase.md +9 -9
- package/workflows-data/execute-phase.md +4 -4
- package/workflows-data/execute-plan.md +8 -8
- package/workflows-data/execute-task.md +7 -7
- package/workflows-data/pause-work.md +18 -18
- package/workflows-data/plan-phase.md +7 -7
- package/workflows-data/plan-task.md +18 -18
- package/migrate.ts +0 -258
- package/workflows/migrate.ts +0 -85
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: soly-framework
|
|
3
|
-
description: Use when the user invokes soly workflow commands (`/plan`, `/execute`, `/discuss`, `/inspect`, `/pause`, `/resume`, `/quick`, `/soly
|
|
3
|
+
description: Use when the user invokes soly workflow commands (`/plan`, `/execute`, `/discuss`, `/inspect`, `/pause`, `/resume`, `/quick`, `/soly init`, `/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
4
|
priority: high
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,9 +12,9 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
12
12
|
|
|
13
13
|
**Mental model — three layers, always in system prompt, in this order:**
|
|
14
14
|
|
|
15
|
-
1. **Project intent** (`.
|
|
16
|
-
2. **Project state** (`.
|
|
17
|
-
3. **Project rules** (`.
|
|
15
|
+
1. **Project intent** (`.agents/docs/`) — the 0-point. What the user wants the app to be. Written BEFORE plans, by humans.
|
|
16
|
+
2. **Project state** (`.agents/STATE.md`, `ROADMAP.md`) — where we are, current phase, recent decisions.
|
|
17
|
+
3. **Project rules** (`.agents/rules/`, `~/.agents/rules/`) — how to behave in this project.
|
|
18
18
|
|
|
19
19
|
**Workflow model — phases and tasks (unified):**
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ The **soly** extension adds project-management workflow to [pi-coding-agent](htt
|
|
|
22
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
23
|
- `soly plan <N>` writes a phase's tasks · `soly execute <N>` runs its ready tasks (deps met) in order · `soly verify` reviews.
|
|
24
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
|
|
25
|
+
- Legacy projects (standalone `NN-MM-PLAN.md` files, or a `features/` dir) still load and run alongside the unified `phases/<N>/tasks/` layout.
|
|
26
26
|
|
|
27
27
|
## Commands
|
|
28
28
|
|
|
@@ -43,8 +43,7 @@ Workflow verbs are **plain text** — type `soly <verb>` (NOT a slash command):
|
|
|
43
43
|
| Command | What it does |
|
|
44
44
|
|---|---|
|
|
45
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/` |
|
|
46
|
+
| `/soly [<sub>]` | Project-state inspection (position, state, plan, roadmap, phases, tasks, …); bare `/soly` opens the picker. `/soly init` scaffolds a new project (template: minimal/web-app/library/cli) |
|
|
48
47
|
| `/why` | What rules + state grounded the last turn |
|
|
49
48
|
| `/rulewizard` | Rule vs .editorconfig vs linter guide |
|
|
50
49
|
|
|
@@ -66,44 +65,37 @@ were removed in 1.4.0.
|
|
|
66
65
|
<project-root>/
|
|
67
66
|
├── AGENTS.md # vendor-neutral agent context (loaded by pi)
|
|
68
67
|
├── agents.md # same as AGENTS.md, lowercase accepted
|
|
69
|
-
├── .
|
|
68
|
+
├── .agents/ # the one home: soly state + vendor-neutral agent config
|
|
69
|
+
│ ├── soly.json # soly config (per-project; ~/.agents/soly.json = global)
|
|
70
70
|
│ ├── ROADMAP.md # phase table
|
|
71
71
|
│ ├── STATE.md # current position + decisions log
|
|
72
72
|
│ ├── docs/ # 0-point intent docs (human-written, locked)
|
|
73
73
|
│ │ ├── vision.md
|
|
74
74
|
│ │ └── architecture.md
|
|
75
|
-
│ ├── rules/ #
|
|
75
|
+
│ ├── rules/ # project rules (version-controlled)
|
|
76
76
|
│ │ ├── code-style.md
|
|
77
77
|
│ │ └── testing.md
|
|
78
78
|
│ ├── phases/
|
|
79
79
|
│ │ ├── 01-foundation/
|
|
80
|
-
│ │ │ ├── 01-CONTEXT.md
|
|
81
|
-
│ │ │ ├── 01-RESEARCH.md
|
|
82
|
-
│ │ │ └── tasks/
|
|
80
|
+
│ │ │ ├── 01-CONTEXT.md # domain + decisions for phase 1
|
|
81
|
+
│ │ │ ├── 01-RESEARCH.md # what we looked up
|
|
82
|
+
│ │ │ └── tasks/ # unified model: one dir per task
|
|
83
83
|
│ │ │ ├── auth-login-a3f9/
|
|
84
|
-
│ │ │ │ ├── PLAN.md
|
|
84
|
+
│ │ │ │ ├── PLAN.md # frontmatter: id, kind, status, depends-on
|
|
85
85
|
│ │ │ │ └── SUMMARY.md
|
|
86
86
|
│ │ │ └── auth-token-b1c2/
|
|
87
87
|
│ │ │ └── PLAN.md
|
|
88
88
|
│ │ └── 02-feature-x/
|
|
89
89
|
│ │ └── ...
|
|
90
|
+
│ ├── skills/ # project-scoped skills (pi auto-discovers)
|
|
91
|
+
│ │ └── my-skill/SKILL.md
|
|
92
|
+
│ ├── agents/ # project-specific agent definitions
|
|
90
93
|
│ ├── iterations/ # per-execution context bundles (auto)
|
|
91
94
|
│ ├── HANDOFF.json # pause snapshot
|
|
92
95
|
│ └── .continue-here.md # pause resume marker
|
|
93
|
-
├── .agents/ # vendor-neutral agent config (per project)
|
|
94
|
-
│ ├── rules/ # agent rules (loaded with priority 3, after .soly/rules/)
|
|
95
|
-
│ ├── skills/ # project-scoped skills (pi auto-discovers)
|
|
96
|
-
│ │ └── my-skill/
|
|
97
|
-
│ │ └── SKILL.md
|
|
98
|
-
│ ├── docs/ # agent-specific docs (intent-style)
|
|
99
|
-
│ └── agents/ # project-specific agent definitions
|
|
100
96
|
```
|
|
101
97
|
|
|
102
|
-
**
|
|
103
|
-
|
|
104
|
-
- **Use `.soly/`** for soly workflow artifacts (PLAN.md, SUMMARY.md, etc.)
|
|
105
|
-
- **Use `.agents/`** for things other AI tools should also see (rules, skills, agents)
|
|
106
|
-
- **Use `AGENTS.md`** for top-level project-wide agent conventions
|
|
98
|
+
**One home:** everything lives under `.agents/` — soly's workflow artifacts (PLAN.md, SUMMARY.md, phases, STATE), project rules/docs, and the vendor-neutral config (skills, agents) other AI tools also read. Global rules/config live under `~/.agents/`. `AGENTS.md` at the project root carries top-level agent conventions. (Projects from before the rename used `.soly/`; soly no longer reads it — run `mv .soly .agents`.)
|
|
107
99
|
|
|
108
100
|
## Frontmatter conventions
|
|
109
101
|
|
|
@@ -122,9 +114,9 @@ parallelizable: true # can run alongside siblings
|
|
|
122
114
|
# Add OAuth flow
|
|
123
115
|
|
|
124
116
|
## read_first
|
|
125
|
-
- .
|
|
126
|
-
- .
|
|
127
|
-
- .
|
|
117
|
+
- .agents/STATE.md
|
|
118
|
+
- .agents/ROADMAP.md
|
|
119
|
+
- .agents/rules/code-style.md
|
|
128
120
|
|
|
129
121
|
## tasks
|
|
130
122
|
- [ ] **type: implement**, description: Add token refresh logic
|
|
@@ -143,7 +135,7 @@ parallelizable: true # can run alongside siblings
|
|
|
143
135
|
- bun run lint
|
|
144
136
|
|
|
145
137
|
## risks
|
|
146
|
-
- Token storage depends on the encryption scheme (see .
|
|
138
|
+
- Token storage depends on the encryption scheme (see .agents/docs/architecture.md)
|
|
147
139
|
```
|
|
148
140
|
|
|
149
141
|
### SUMMARY.md frontmatter
|
|
@@ -191,15 +183,15 @@ priority: 50 # higher wins on conflict (default: 0)
|
|
|
191
183
|
|
|
192
184
|
## Path discipline (NON-NEGOTIABLE)
|
|
193
185
|
|
|
194
|
-
**All soly-managed files live under `.
|
|
186
|
+
**All soly-managed files live under `.agents/`.** Source code lives in the project's normal source tree.
|
|
195
187
|
|
|
196
188
|
| File kind | Goes to |
|
|
197
189
|
|---|---|
|
|
198
|
-
| `PLAN.md`, `SUMMARY.md`, `CONTEXT.md`, `RESEARCH.md` | `.
|
|
199
|
-
| Intent docs (0-point) | `.
|
|
200
|
-
| Rules | `.
|
|
201
|
-
| Handoff | `.
|
|
202
|
-
| Iteration context | `.
|
|
190
|
+
| `PLAN.md`, `SUMMARY.md`, `CONTEXT.md`, `RESEARCH.md` | `.agents/phases/<NN>-<slug>/` |
|
|
191
|
+
| Intent docs (0-point) | `.agents/docs/` |
|
|
192
|
+
| Rules | `.agents/rules/` (project) or `~/.agents/rules/` (user) |
|
|
193
|
+
| Handoff | `.agents/HANDOFF.json`, `.agents/.continue-here.md` |
|
|
194
|
+
| Iteration context | `.agents/iterations/` (auto-generated) |
|
|
203
195
|
| Production code, tests | project's normal `src/`, `tests/`, `app/`, etc. |
|
|
204
196
|
|
|
205
197
|
Use absolute paths (or paths starting with `$SOLY_DIR`) when calling tools. Never bare relative names that could land in cwd.
|
|
@@ -243,14 +235,14 @@ The main system prompt only points at these; the detail is here.
|
|
|
243
235
|
|
|
244
236
|
**`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
237
|
|
|
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 `.
|
|
238
|
+
**`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 `.agents/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.
|
|
247
239
|
|
|
248
240
|
## Common workflows
|
|
249
241
|
|
|
250
242
|
### Start a new project
|
|
251
243
|
|
|
252
|
-
1. `soly init` (or manually create `.
|
|
253
|
-
2. Write 1-3 intent docs in `.
|
|
244
|
+
1. `soly init` (or manually create `.agents/`, `docs/`, `rules/`)
|
|
245
|
+
2. Write 1-3 intent docs in `.agents/docs/`
|
|
254
246
|
3. Optionally write `AGENTS.md` (or `agents.md`) at project root with project conventions
|
|
255
247
|
4. Create `ROADMAP.md` with phase table
|
|
256
248
|
5. `/plan 1` to start the first phase
|
|
@@ -291,7 +283,7 @@ You are a data scientist. ...
|
|
|
291
283
|
|
|
292
284
|
If `/execute` complains about illegal partial state:
|
|
293
285
|
|
|
294
|
-
1. `cat .
|
|
286
|
+
1. `cat .agents/iterations/<latest>.md` — see what the last run did
|
|
295
287
|
2. Check if `SUMMARY.md` exists for the last plan
|
|
296
288
|
3. If yes, finish close-out: update `STATE.md` + `ROADMAP.md`
|
|
297
289
|
4. If no, either commit the SUMMARY or revert the production commits
|
|
@@ -302,8 +294,8 @@ Call `soly_read(artifact: "state")` and `soly_read(artifact: "roadmap")` first.
|
|
|
302
294
|
|
|
303
295
|
## Don'ts
|
|
304
296
|
|
|
305
|
-
- ❌ Edit `.
|
|
297
|
+
- ❌ Edit `.agents/rules/` files you didn't write — those are project invariants
|
|
306
298
|
- ❌ Skip the SUMMARY — illegal partial state
|
|
307
299
|
- ❌ 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.
|
|
308
|
-
- ❌ Edit `.
|
|
309
|
-
- ❌ Put intent docs anywhere other than `.
|
|
300
|
+
- ❌ Edit `.agents/phases/*/PLAN.md` after `status: in_progress` — create a new plan
|
|
301
|
+
- ❌ Put intent docs anywhere other than `.agents/docs/`
|
package/status.ts
CHANGED
|
@@ -56,8 +56,8 @@ export function formatStatus(
|
|
|
56
56
|
lines.push(" Project state");
|
|
57
57
|
lines.push(" ─────────────");
|
|
58
58
|
if (!state.exists) {
|
|
59
|
-
lines.push(" no .agents/
|
|
60
|
-
lines.push(" → run /soly
|
|
59
|
+
lines.push(" no .agents/ found in cwd");
|
|
60
|
+
lines.push(" → run /soly init to scaffold");
|
|
61
61
|
} else {
|
|
62
62
|
lines.push(` milestone: ${state.milestone ?? "—"}`);
|
|
63
63
|
lines.push(` current: ${state.currentPosition ?? "ready"}`);
|
package/tools.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
5
|
// Registers three tools the LLM can call:
|
|
6
|
-
// - soly_read — read any .
|
|
6
|
+
// - soly_read — read any .agents/ artifact (state/plan/roadmap/...)
|
|
7
7
|
// - soly_log_decision — append a row to STATE.md Decisions table
|
|
8
8
|
// - soly_list_phases — list all phases with markers
|
|
9
9
|
//
|
|
10
|
-
// All paths are relative to <cwd>/.
|
|
10
|
+
// All paths are relative to <cwd>/.agents/ (the soly layout — NOT .planning/).
|
|
11
11
|
// =============================================================================
|
|
12
12
|
|
|
13
13
|
import * as fs from "node:fs";
|
|
@@ -17,7 +17,7 @@ import { promisify } from "node:util";
|
|
|
17
17
|
import { Type } from "typebox";
|
|
18
18
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
19
19
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
|
-
import { readIfExists, splitFrontmatter, atomicWriteFileSync, type SolyState } from "./core.ts";
|
|
20
|
+
import { readIfExists, splitFrontmatter, atomicWriteFileSync, solyDirFor, type SolyState } from "./core.ts";
|
|
21
21
|
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";
|
|
@@ -66,7 +66,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
66
66
|
name: "soly_read",
|
|
67
67
|
label: "soly read",
|
|
68
68
|
description:
|
|
69
|
-
"Read a .
|
|
69
|
+
"Read a .agents/ 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.",
|
|
70
70
|
parameters: Type.Object({
|
|
71
71
|
artifact: StringEnum([
|
|
72
72
|
"state",
|
|
@@ -129,7 +129,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
129
129
|
if (!task) {
|
|
130
130
|
return {
|
|
131
131
|
content: [
|
|
132
|
-
{ type: "text", text: `soly: task ${taskId} not found in .
|
|
132
|
+
{ type: "text", text: `soly: task ${taskId} not found in .agents/features/*/tasks/` },
|
|
133
133
|
],
|
|
134
134
|
details: { error: "task_not_found", taskId },
|
|
135
135
|
};
|
|
@@ -189,7 +189,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
189
189
|
name: "soly_log_decision",
|
|
190
190
|
label: "soly log decision",
|
|
191
191
|
description:
|
|
192
|
-
"Append a one-line decision + rationale to the Decisions table in .
|
|
192
|
+
"Append a one-line decision + rationale to the Decisions table in .agents/STATE.md (creates it if missing). For meaningful choices: scope cuts, library picks, trade-offs. `phase` defaults to current.",
|
|
193
193
|
parameters: Type.Object({
|
|
194
194
|
decision: Type.String({ description: "The decision made (one line)." }),
|
|
195
195
|
rationale: Type.String({ description: "Why this decision was made (one line)." }),
|
|
@@ -329,7 +329,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
329
329
|
name: "soly_todos",
|
|
330
330
|
label: "soly todos",
|
|
331
331
|
description:
|
|
332
|
-
"Scan the tree for TODO/FIXME/HACK/XXX/NOTE comments, grouped by file (common source extensions; excludes node_modules/.git/dist/build/.
|
|
332
|
+
"Scan the tree for TODO/FIXME/HACK/XXX/NOTE comments, grouped by file (common source extensions; excludes node_modules/.git/dist/build/.agents). Needs ripgrep on PATH. `paths` overrides root, `limit` caps (default 200).",
|
|
333
333
|
parameters: Type.Object({
|
|
334
334
|
paths: Type.Optional(
|
|
335
335
|
Type.Array(Type.String(), {
|
|
@@ -356,7 +356,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
356
356
|
"--glob=!node_modules/**",
|
|
357
357
|
"--glob=!dist/**",
|
|
358
358
|
"--glob=!build/**",
|
|
359
|
-
"--glob=!.
|
|
359
|
+
"--glob=!.agents/**",
|
|
360
360
|
"--glob=!coverage/**",
|
|
361
361
|
"-tts",
|
|
362
362
|
"-tjs",
|
|
@@ -838,7 +838,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
838
838
|
canonical_refs: Type.Optional(
|
|
839
839
|
Type.Array(Type.String(), {
|
|
840
840
|
description:
|
|
841
|
-
"MANDATORY. Files the planner needs (intent docs, REQUIREMENTS, contracts), full paths from `.
|
|
841
|
+
"MANDATORY. Files the planner needs (intent docs, REQUIREMENTS, contracts), full paths from `.agents/`.",
|
|
842
842
|
}),
|
|
843
843
|
),
|
|
844
844
|
deferred_ideas: Type.Optional(
|
|
@@ -852,10 +852,10 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
852
852
|
}),
|
|
853
853
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
854
854
|
// Locate phase dir
|
|
855
|
-
const solyDir =
|
|
855
|
+
const solyDir = solyDirFor(ctx.cwd);
|
|
856
856
|
if (!fs.existsSync(solyDir)) {
|
|
857
857
|
return {
|
|
858
|
-
content: [{ type: "text", text: "soly_finish_discuss: no .
|
|
858
|
+
content: [{ type: "text", text: "soly_finish_discuss: no .agents/ in cwd" }],
|
|
859
859
|
details: { error: "no_soly" },
|
|
860
860
|
};
|
|
861
861
|
}
|
|
@@ -877,7 +877,7 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
877
877
|
content: [
|
|
878
878
|
{
|
|
879
879
|
type: "text",
|
|
880
|
-
text: `soly_finish_discuss: phase ${phaseNum} not found in .
|
|
880
|
+
text: `soly_finish_discuss: phase ${phaseNum} not found in .agents/phases/`,
|
|
881
881
|
},
|
|
882
882
|
],
|
|
883
883
|
details: { error: "no_phase", phase: phaseNum },
|
|
@@ -1021,11 +1021,11 @@ export function registerTools(pi: ExtensionAPI, deps: ToolsDeps): void {
|
|
|
1021
1021
|
),
|
|
1022
1022
|
}),
|
|
1023
1023
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1024
|
-
const solyDir =
|
|
1024
|
+
const solyDir = solyDirFor(ctx.cwd);
|
|
1025
1025
|
const phasesRoot = path.join(solyDir, "phases");
|
|
1026
1026
|
if (!fs.existsSync(phasesRoot)) {
|
|
1027
1027
|
return {
|
|
1028
|
-
content: [{ type: "text", text: "soly_save_discuss_checkpoint: no .
|
|
1028
|
+
content: [{ type: "text", text: "soly_save_discuss_checkpoint: no .agents/phases/ in cwd" }],
|
|
1029
1029
|
details: { error: "no_phases" },
|
|
1030
1030
|
};
|
|
1031
1031
|
}
|
package/util.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { ProgressInfo, RuleFrontmatter } from "./core.ts";
|
|
|
16
16
|
// Frontmatter parsers
|
|
17
17
|
// ============================================================================
|
|
18
18
|
|
|
19
|
-
// Simple parser for .
|
|
19
|
+
// Simple parser for .agents/rules/ frontmatter.
|
|
20
20
|
export function parseRuleFrontmatter(raw: string): {
|
|
21
21
|
meta: RuleFrontmatter;
|
|
22
22
|
body: string;
|
|
@@ -68,7 +68,7 @@ export function parseRuleFrontmatter(raw: string): {
|
|
|
68
68
|
return { meta, body };
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// YAML-ish parser for .
|
|
71
|
+
// YAML-ish parser for .agents/STATE.md. Handles 2-level nested objects (for `progress:`).
|
|
72
72
|
export function parseStateFrontmatter(yaml: string): {
|
|
73
73
|
meta: Record<string, unknown>;
|
|
74
74
|
progress: ProgressInfo;
|
package/visual/welcome.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// soly adds plans · state · rules · workflows · ask_pro picker
|
|
11
11
|
// project v1.12 · plan 2/5 — auth refactor · ≡ 4 rules · 2 docs
|
|
12
12
|
// next → /execute
|
|
13
|
-
// start here /soly
|
|
13
|
+
// start here /soly init · /plan · /soly · /why · /rules stats
|
|
14
14
|
// recent 1.11.2 … · 1.10.0 …
|
|
15
15
|
//
|
|
16
16
|
// pi is explicitly credited (engine) with soly as the framework on top. The
|
|
@@ -54,7 +54,7 @@ export const SOLY_ART: readonly string[] = [
|
|
|
54
54
|
const TAGLINE = "the project framework · running on pi, the coding engine";
|
|
55
55
|
const SOLY_ADDS = "plans · state · rules · workflows · ask_pro picker";
|
|
56
56
|
const START_HERE: ReadonlyArray<[string, string]> = [
|
|
57
|
-
["/soly
|
|
57
|
+
["/soly init", "scaffold .agents/ in a new project"],
|
|
58
58
|
["/plan", "plan the current phase"],
|
|
59
59
|
["/soly", "state picker · /why · /rules stats"],
|
|
60
60
|
];
|
|
@@ -144,7 +144,7 @@ function bannerLines(opts: WelcomeOpts): string[] {
|
|
|
144
144
|
|
|
145
145
|
/** The "project" row value, depending on whether a soly project exists. */
|
|
146
146
|
function projectValue(input: WelcomeInput, styler: ChromeStyler): string {
|
|
147
|
-
if (!input.hasProject) return styler.dim("no soly project here → /soly
|
|
147
|
+
if (!input.hasProject) return styler.dim("no soly project here → /soly init to scaffold");
|
|
148
148
|
const bits = [styler.fg("accent", `v${input.version}`)];
|
|
149
149
|
if (input.phaseLabel) bits.push(input.phaseLabel);
|
|
150
150
|
if (input.rulesActive > 0) bits.push(`≡ ${input.rulesActive}`);
|
package/workflows/execute.ts
CHANGED
|
@@ -73,7 +73,7 @@ export function buildExecuteTransform(
|
|
|
73
73
|
return {
|
|
74
74
|
handled: true,
|
|
75
75
|
transformedText:
|
|
76
|
-
`soly: no .
|
|
76
|
+
`soly: no .agents/ directory found in cwd (${state.solyDir || "<cwd>"}) — cannot execute phase.\n` +
|
|
77
77
|
`Initialize a soly project first (see soly quickstart) before running "soly execute".`,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
@@ -101,7 +101,7 @@ export function buildExecuteTransform(
|
|
|
101
101
|
return {
|
|
102
102
|
handled: true,
|
|
103
103
|
transformedText:
|
|
104
|
-
`soly execute: task ${target.taskId} not found in .
|
|
104
|
+
`soly execute: task ${target.taskId} not found in .agents/features/*/tasks/.\n` +
|
|
105
105
|
`Known tasks: ${state.tasks.map((t) => t.id).join(", ") || "(none)"}\n` +
|
|
106
106
|
`Tip: use the \`soly_list_tasks\` tool to see all available tasks.`,
|
|
107
107
|
};
|
|
@@ -155,7 +155,7 @@ export function buildExecuteTransform(
|
|
|
155
155
|
const featureDir = path.dirname(path.dirname(task.dir));
|
|
156
156
|
|
|
157
157
|
// Write per-iteration context bundle (B2 of the soly design).
|
|
158
|
-
// Worker reads this file first; no need to chase 6+ .
|
|
158
|
+
// Worker reads this file first; no need to chase 6+ .agents/ files.
|
|
159
159
|
const iter = writeIterationContext({
|
|
160
160
|
solyDir: state.solyDir,
|
|
161
161
|
projectRoot,
|
|
@@ -179,7 +179,7 @@ export function buildExecuteTransform(
|
|
|
179
179
|
**Iteration context file written:** \`${iter.relPath}\` (${iter.tokens} tokens, ${iter.bytes} bytes)
|
|
180
180
|
The worker reads this file first — it contains intent, STATE, ROADMAP (n/a for tasks), the feature README, prior task SUMMARYs, and the current task PLAN.
|
|
181
181
|
|
|
182
|
-
**0-POINT CHECK.** Worker must re-read .
|
|
182
|
+
**0-POINT CHECK.** Worker must re-read .agents/docs/ (intent) and .agents/features/${task.feature}/README.md before implementing.
|
|
183
183
|
|
|
184
184
|
Launch a single subagent for this work. Do NOT do the work inline.
|
|
185
185
|
|
|
@@ -206,12 +206,12 @@ Soly dir: ${state.solyDir}
|
|
|
206
206
|
Feature dir: ${featureDir}
|
|
207
207
|
Task dir: ${task.dir}
|
|
208
208
|
|
|
209
|
-
**0-POINT CHECK — read .
|
|
209
|
+
**0-POINT CHECK — read .agents/docs/ first.**
|
|
210
210
|
These are the project's INTENT (business context, design vision). Re-read them before implementing. If you find a conflict between intent and PLAN.md, flag it instead of silently choosing one.
|
|
211
211
|
|
|
212
|
-
**Follow the worker self-audit gate (see .
|
|
212
|
+
**Follow the worker self-audit gate (see .agents/rules/process/worker-audit.md):**
|
|
213
213
|
1. Run \`dotnet build\` (or relevant build) — 0 warnings
|
|
214
|
-
2. Cross-check diff against .
|
|
214
|
+
2. Cross-check diff against .agents/rules/coding/*
|
|
215
215
|
3. Invoke \`analyzer-coach\` skill for any rule gaps
|
|
216
216
|
4. Loop until clean (max 3 iterations)
|
|
217
217
|
5. Commit (production-code commit(s))
|
|
@@ -224,10 +224,10 @@ ${workflow}
|
|
|
224
224
|
|
|
225
225
|
Hard rules:
|
|
226
226
|
- Do not skip the close-out order: production commits -> SUMMARY commit -> status: done.
|
|
227
|
-
- Do not modify any .
|
|
227
|
+
- Do not modify any .agents/rules/ files.
|
|
228
228
|
- Do not run subagents yourself.
|
|
229
229
|
- Do not start a task whose \`depends-on:\` lists tasks that are not \`done\`.
|
|
230
|
-
- PATH DISCIPLINE: all files YOU create must live under \`.
|
|
230
|
+
- PATH DISCIPLINE: all files YOU create must live under \`.agents/\` (iteration, handoff, etc.) or under the project's source dirs. Never write to the project root.
|
|
231
231
|
- Return: changed files, commands run with exit codes, validation evidence, surprises, decisions needing parent approval.
|
|
232
232
|
- Interactive-only rules are NOT in scope for you: ${interactiveRules.length > 0 ? interactiveRules.join(", ") : "(none)"}.
|
|
233
233
|
\`
|
|
@@ -248,7 +248,7 @@ When the subagent completes, synthesize the result. Do not re-execute its work.
|
|
|
248
248
|
handled: true,
|
|
249
249
|
transformedText:
|
|
250
250
|
target.kind === "all"
|
|
251
|
-
? `soly execute --all: no tasks found in .
|
|
251
|
+
? `soly execute --all: no tasks found in .agents/features/*/tasks/.`
|
|
252
252
|
: `soly execute --feature ${target.feature}: no tasks found for that feature.`,
|
|
253
253
|
};
|
|
254
254
|
}
|
|
@@ -280,7 +280,7 @@ When the subagent completes, synthesize the result. Do not re-execute its work.
|
|
|
280
280
|
return {
|
|
281
281
|
handled: true,
|
|
282
282
|
transformedText:
|
|
283
|
-
`soly execute: phase ${target.phase} not found in .
|
|
283
|
+
`soly execute: phase ${target.phase} not found in .agents/phases/.\n` +
|
|
284
284
|
`Known phases: ${state.phases.map((p) => p.number).join(", ") || "(none)"}`,
|
|
285
285
|
};
|
|
286
286
|
}
|
|
@@ -358,8 +358,8 @@ The iteration context file lists all plans (their frontmatter) in section 6, gro
|
|
|
358
358
|
**Iteration context file written:** \`${iter.relPath}\` (${iter.tokens} tokens, ${iter.bytes} bytes)
|
|
359
359
|
The worker reads this file first — it contains intent, STATE, ROADMAP row for this phase, phase CONTEXT, phase RESEARCH, prior SUMMARYs, ${isPlanLevel ? "and the current PLAN" : "and all PLAN frontmatter summaries"}, and (for exec) the Critical Anti-Patterns from .continue-here.md.
|
|
360
360
|
|
|
361
|
-
**0-POINT CHECK — worker must read .
|
|
362
|
-
These are the project's INTENT docs. The worker is about to implement tasks; if the implementation diverges from intent, it will be wrong even if the tests pass. Have the worker re-read .
|
|
361
|
+
**0-POINT CHECK — worker must read .agents/docs/ first.**
|
|
362
|
+
These are the project's INTENT docs. The worker is about to implement tasks; if the implementation diverges from intent, it will be wrong even if the tests pass. Have the worker re-read .agents/docs/ (and any intent docs linked from PLAN.md) before each plan.
|
|
363
363
|
|
|
364
364
|
${scopeBlock}
|
|
365
365
|
|
|
@@ -389,7 +389,7 @@ Project root: ${projectRoot}
|
|
|
389
389
|
Soly dir: ${state.solyDir}
|
|
390
390
|
Phase dir: ${phase.dir}
|
|
391
391
|
|
|
392
|
-
**0-POINT CHECK — read .
|
|
392
|
+
**0-POINT CHECK — read .agents/docs/ first.**
|
|
393
393
|
These are the project's INTENT (business context, design vision). Re-read them before implementing each plan. If you find a conflict between intent and PLAN.md, flag it instead of silently choosing one.
|
|
394
394
|
|
|
395
395
|
Follow the workflow below VERBATIM — these are the user-approved soly instructions, not suggestions.
|
|
@@ -400,9 +400,9 @@ ${workflow}
|
|
|
400
400
|
|
|
401
401
|
Hard rules:
|
|
402
402
|
- Do not skip the close-out order: production commits -> SUMMARY commit -> STATE/ROADMAP update.
|
|
403
|
-
- Do not modify any .
|
|
403
|
+
- Do not modify any .agents/rules/ files.
|
|
404
404
|
- Do not run subagents yourself.
|
|
405
|
-
- PATH DISCIPLINE: all files YOU create must live under \`.
|
|
405
|
+
- PATH DISCIPLINE: all files YOU create must live under \`.agents/\` (e.g. .agents/iterations/, .agents/phases/<slug>/, .agents/HANDOFF.json) or under the project's source dirs. Never write PLAN/SUMMARY/CONTEXT/RESEARCH/iteration files to the project root.
|
|
406
406
|
- Return: changed files, commands run with exit codes, validation evidence, surprises, and any decisions needing parent approval.
|
|
407
407
|
- Interactive-only rules are NOT in scope for you: ${interactiveRules.length > 0 ? interactiveRules.join(", ") : "(none)"}. They describe how the user-facing conversation should go, not how to execute work.
|
|
408
408
|
\`
|
package/workflows/index.ts
CHANGED
|
@@ -24,7 +24,6 @@ import { showStatus, showLog, showDiff } from "./quick.ts";
|
|
|
24
24
|
import { showDoctor, showIterations, showDiffIterations, showPhaseDelete, showTodos } from "./inspect.ts";
|
|
25
25
|
import { buildPlanTransform, buildDiscussTransform } from "./planning.ts";
|
|
26
26
|
import { createVerifyLoop, type VerifyState } from "./verify.ts";
|
|
27
|
-
import { buildMigrateTransform } from "./migrate.ts";
|
|
28
27
|
import type { ContextManager } from "../context-manager.ts";
|
|
29
28
|
import type { SolyState } from "../core.js";
|
|
30
29
|
import type { SolyConfig } from "../config.js";
|
|
@@ -145,12 +144,6 @@ export function registerWorkflows(pi: ExtensionAPI, deps: WorkflowsDeps): void {
|
|
|
145
144
|
return { action: "transform", text: result.transformedText };
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
if (cmd.verb === "migrate") {
|
|
149
|
-
const result = buildMigrateTransform(state);
|
|
150
|
-
if (!result.handled || !result.transformedText) return;
|
|
151
|
-
return { action: "transform", text: result.transformedText };
|
|
152
|
-
}
|
|
153
|
-
|
|
154
147
|
if (cmd.verb === "verify") {
|
|
155
148
|
const sub = (cmd.args[0] ?? "").toLowerCase();
|
|
156
149
|
if (sub === "stop" || sub === "off") {
|
|
@@ -179,15 +172,12 @@ Lifecycle:
|
|
|
179
172
|
Quick info (no LLM round-trip):
|
|
180
173
|
status — position + progress + phases
|
|
181
174
|
log [N] — last N decisions from STATE.md
|
|
182
|
-
diff — git status + uncommitted .
|
|
175
|
+
diff — git status + uncommitted .agents/ changes
|
|
183
176
|
doctor — health check (missing files, broken refs, stale iterations)
|
|
184
177
|
iterations [N] — recent iteration bundles
|
|
185
178
|
todos — pi-todo live list
|
|
186
179
|
phase delete <N> — soft-delete a phase
|
|
187
180
|
|
|
188
|
-
Maintenance:
|
|
189
|
-
migrate — convert a legacy layout (NN-PLAN files / features/) to phases/<N>/tasks/
|
|
190
|
-
|
|
191
181
|
State inspection lives on the slash form — \`/soly <sub>\`:
|
|
192
182
|
position · state · plan · roadmap · progress · phases · tasks · task <id> ·
|
|
193
183
|
features · milestone · context · research · config · reload
|
|
@@ -249,7 +239,7 @@ State inspection lives on the slash form — \`/soly <sub>\`:
|
|
|
249
239
|
return {
|
|
250
240
|
action: "transform",
|
|
251
241
|
text: `soly phase — usage:
|
|
252
|
-
soly phase delete <N> — soft-delete phase N (move to .
|
|
242
|
+
soly phase delete <N> — soft-delete phase N (move to .agents/phases/.trash/)
|
|
253
243
|
soly phase list — list all phases (same as /soly phases)
|
|
254
244
|
soly phase <N> — alias for "soly plan <N>" (route through planner)`,
|
|
255
245
|
};
|
|
@@ -265,8 +255,8 @@ State inspection lives on the slash form — \`/soly <sub>\`:
|
|
|
265
255
|
pendingCompact = false;
|
|
266
256
|
ctx.compact({
|
|
267
257
|
customInstructions:
|
|
268
|
-
"Session was paused via `soly compact`. Handoff files are in .
|
|
269
|
-
"and .
|
|
258
|
+
"Session was paused via `soly compact`. Handoff files are in .agents/HANDOFF.json " +
|
|
259
|
+
"and .agents/.continue-here.md. Preserve milestone/phase/plan position and key " +
|
|
270
260
|
"decisions in the summary. Drop implementation-detail noise.",
|
|
271
261
|
onComplete: () => {
|
|
272
262
|
ctx.ui.notify("soly: session compacted. Use `soly resume` to pick up.", "info");
|
package/workflows/inspect.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
+
import { solyDirFor } from "../core.js";
|
|
9
10
|
import type { SolyState } from "../core.js";
|
|
10
11
|
import type { SolyConfig } from "../config.js";
|
|
11
12
|
|
|
@@ -28,11 +29,11 @@ interface DoctorCheck {
|
|
|
28
29
|
export function showDoctor(_cmd: unknown, state: SolyState, ui: InspectUI, config: SolyConfig, activeTools: string[] = []): void {
|
|
29
30
|
const checks: DoctorCheck[] = [];
|
|
30
31
|
|
|
31
|
-
// 1. .
|
|
32
|
+
// 1. .agents/ exists
|
|
32
33
|
checks.push({
|
|
33
|
-
name: ".
|
|
34
|
+
name: ".agents/ directory",
|
|
34
35
|
status: state.exists ? "pass" : "fail",
|
|
35
|
-
detail: state.exists ? state.solyDir : "no .
|
|
36
|
+
detail: state.exists ? state.solyDir : "no .agents/ found in cwd",
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
// 2. STATE.md exists + has frontmatter
|
|
@@ -177,13 +178,13 @@ export function showDoctor(_cmd: unknown, state: SolyState, ui: InspectUI, confi
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
// 9. .
|
|
181
|
+
// 9. .agents/rules/ exists if state says rules are loaded
|
|
181
182
|
if (state.exists) {
|
|
182
183
|
const rulesDir = path.join(state.solyDir, "rules");
|
|
183
184
|
checks.push({
|
|
184
|
-
name: ".
|
|
185
|
+
name: ".agents/rules/ directory",
|
|
185
186
|
status: fs.existsSync(rulesDir) ? "pass" : "warn",
|
|
186
|
-
detail: fs.existsSync(rulesDir) ? "present" : "no rules directory — soly will fall back to ~/.
|
|
187
|
+
detail: fs.existsSync(rulesDir) ? "present" : "no rules directory — soly will fall back to ~/.agents/rules/",
|
|
187
188
|
});
|
|
188
189
|
}
|
|
189
190
|
|
|
@@ -220,7 +221,7 @@ export function showDoctor(_cmd: unknown, state: SolyState, ui: InspectUI, confi
|
|
|
220
221
|
name: "project layout",
|
|
221
222
|
status: legacy ? "info" : "pass",
|
|
222
223
|
detail: legacy
|
|
223
|
-
? "legacy plans/features detected —
|
|
224
|
+
? "legacy plans/features detected — still supported alongside the unified phases/<N>/tasks/ model"
|
|
224
225
|
: "unified model (phase = group of tasks)",
|
|
225
226
|
});
|
|
226
227
|
}
|
|
@@ -258,7 +259,7 @@ function pluralDays(n: number): string {
|
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
// ---------------------------------------------------------------------------
|
|
261
|
-
// soly todos — read .
|
|
262
|
+
// soly todos — read .agents/todos.json or .pi-todos.json and show as a notify.
|
|
262
263
|
// Mirrors what pi-todo would render in the footer, so the user can see
|
|
263
264
|
// todos even when pi-todo extension isn't loaded (e.g. just-installed).
|
|
264
265
|
// ---------------------------------------------------------------------------
|
|
@@ -266,7 +267,7 @@ function pluralDays(n: number): string {
|
|
|
266
267
|
/** Try to find a todo file in cwd. Returns the path or null. */
|
|
267
268
|
function findTodosFile(cwd: string): string | null {
|
|
268
269
|
const candidates = [
|
|
269
|
-
path.join(cwd, "
|
|
270
|
+
path.join(solyDirFor(cwd), "todos.json"),
|
|
270
271
|
path.join(cwd, ".pi-todos.json"),
|
|
271
272
|
];
|
|
272
273
|
for (const c of candidates) {
|
|
@@ -281,13 +282,13 @@ export function showTodos(
|
|
|
281
282
|
ui: InspectUI,
|
|
282
283
|
): void {
|
|
283
284
|
if (!state.exists) {
|
|
284
|
-
ui.notify("soly todos: no .
|
|
285
|
+
ui.notify("soly todos: no .agents/ directory in cwd", "error");
|
|
285
286
|
return;
|
|
286
287
|
}
|
|
287
288
|
const file = findTodosFile(state.solyDir);
|
|
288
289
|
if (!file) {
|
|
289
290
|
ui.notify(
|
|
290
|
-
"soly todos: no todo file found. Install the `pi-todo` extension or write `.
|
|
291
|
+
"soly todos: no todo file found. Install the `pi-todo` extension or write `.agents/todos.json` manually.",
|
|
291
292
|
"info",
|
|
292
293
|
);
|
|
293
294
|
return;
|
|
@@ -329,7 +330,7 @@ export function showIterations(
|
|
|
329
330
|
limitDefault: number = 10,
|
|
330
331
|
): void {
|
|
331
332
|
if (!state.exists) {
|
|
332
|
-
ui.notify("soly iterations: no .
|
|
333
|
+
ui.notify("soly iterations: no .agents/ directory in cwd", "error");
|
|
333
334
|
return;
|
|
334
335
|
}
|
|
335
336
|
const iterDir = path.join(state.solyDir, "iterations");
|
|
@@ -398,7 +399,7 @@ export function showDiffIterations(
|
|
|
398
399
|
ui: InspectUI,
|
|
399
400
|
): void {
|
|
400
401
|
if (!state.exists) {
|
|
401
|
-
ui.notify("soly diff iterations: no .
|
|
402
|
+
ui.notify("soly diff iterations: no .agents/ directory in cwd", "error");
|
|
402
403
|
return;
|
|
403
404
|
}
|
|
404
405
|
const iterDir = path.join(state.solyDir, "iterations");
|
|
@@ -456,7 +457,7 @@ export function showPhaseDelete(
|
|
|
456
457
|
ui: InspectUI,
|
|
457
458
|
): void {
|
|
458
459
|
if (!state.exists) {
|
|
459
|
-
ui.notify("soly phase delete: no .
|
|
460
|
+
ui.notify("soly phase delete: no .agents/ directory in cwd", "error");
|
|
460
461
|
return;
|
|
461
462
|
}
|
|
462
463
|
if (cmd.args.length < 1) {
|
|
@@ -492,7 +493,7 @@ export function showPhaseDelete(
|
|
|
492
493
|
out.push(`✓ Phase ${phaseNum} (${phase.name}) moved to .trash/`);
|
|
493
494
|
out.push(` ${phase.dir} → ${dest}`);
|
|
494
495
|
out.push("");
|
|
495
|
-
out.push("To restore: `mv` it back to .
|
|
496
|
+
out.push("To restore: `mv` it back to .agents/phases/");
|
|
496
497
|
out.push("To permanently delete: `rm -rf " + dest + "`");
|
|
497
498
|
ui.notify(out.join("\n"), "info");
|
|
498
499
|
}
|