pi-soly 1.13.5 → 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 +8 -9
- 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 +34 -63
- package/config.ts +20 -12
- package/core.ts +16 -18
- package/docs.ts +4 -4
- package/index.ts +20 -21
- package/init.ts +7 -7
- package/intent.ts +12 -12
- package/iteration.ts +12 -12
- 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
package/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// index.ts — Main soly extension entry point
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// Loads .
|
|
5
|
+
// Loads .agents/rules/ and .agents/ project state into the agent's system
|
|
6
6
|
// prompt, and registers:
|
|
7
7
|
// - slash commands /rules /soly /rulewizard /why
|
|
8
8
|
// - LLM tools soly_read soly_log_decision soly_list_phases
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
rulesApplicableToFiles,
|
|
43
43
|
STATUS_ID,
|
|
44
44
|
solyDirFor,
|
|
45
|
+
SOLY_DIRNAME,
|
|
45
46
|
isLegacySolyDir,
|
|
46
47
|
buildNextHint,
|
|
47
48
|
buildDriftReminder,
|
|
@@ -94,7 +95,7 @@ const SOLY_TOOLS_POINTER = `
|
|
|
94
95
|
## soly — interactive & visual tools
|
|
95
96
|
|
|
96
97
|
When terminal text isn't the best medium, reach for these (details + when-NOT in the soly-framework skill):
|
|
97
|
-
- \`ask_pro\` — multi-question picker (single/multi-select, free-text, per-option previews).
|
|
98
|
+
- \`ask_pro\` — multi-question picker (single/multi-select, free-text, per-option previews); every options question always offers a free-text "Other…" so the user can answer in their own words.
|
|
98
99
|
- \`decision_deck\` — full-screen cards comparing design options by their concrete code shape.
|
|
99
100
|
- \`html_artifact\` — render HTML to a self-contained, browseable per-project gallery.`;
|
|
100
101
|
|
|
@@ -178,7 +179,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
178
179
|
let codeMap: CodeMap | null = null;
|
|
179
180
|
let lastCodeMapSection = "";
|
|
180
181
|
|
|
181
|
-
// Project intent (zero-point docs from .
|
|
182
|
+
// Project intent (zero-point docs from .agents/docs/) — always loaded
|
|
182
183
|
let intentDocs: IntentDoc[] = [];
|
|
183
184
|
let lastIntentSection = "";
|
|
184
185
|
|
|
@@ -237,14 +238,14 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
237
238
|
/**
|
|
238
239
|
* Persistent storage of rule mtimes from the previous session, so we can
|
|
239
240
|
* show a "rules changed since last session" diff at startup.
|
|
240
|
-
* Stored in <solyDir
|
|
241
|
-
* <homedir>/.
|
|
241
|
+
* Stored in <solyDir>/rule-mtimes.json (project) or
|
|
242
|
+
* <homedir>/.agents/rule-mtimes.json (global fallback).
|
|
242
243
|
*/
|
|
243
244
|
let lastSessionMtimes: Record<string, number> = {};
|
|
244
245
|
const mtimeStorePath = (): string => {
|
|
245
246
|
const base = state.solyDir && fs.existsSync(state.solyDir)
|
|
246
247
|
? state.solyDir
|
|
247
|
-
: path.join(os.homedir(),
|
|
248
|
+
: path.join(os.homedir(), SOLY_DIRNAME);
|
|
248
249
|
try {
|
|
249
250
|
fs.mkdirSync(base, { recursive: true });
|
|
250
251
|
} catch {}
|
|
@@ -293,7 +294,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
293
294
|
// Soly doesn't render the agent badge itself.
|
|
294
295
|
const agentGroup = "";
|
|
295
296
|
|
|
296
|
-
// Cross-extension: show pi-todo progress if either .
|
|
297
|
+
// Cross-extension: show pi-todo progress if either .agents/todos.json
|
|
297
298
|
// (soly-integration mode) OR .pi-todos.json (standalone mode) exists.
|
|
298
299
|
// Cheap (one stat + one small JSON read); cached only for the
|
|
299
300
|
// lifetime of one updateStatus call.
|
|
@@ -407,32 +408,30 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
407
408
|
// ============================================================================
|
|
408
409
|
|
|
409
410
|
pi.on("session_start", async (event, ctx) => {
|
|
410
|
-
//
|
|
411
|
-
// user
|
|
411
|
+
// Legacy detection: a project with only a `.soly/` dir (no `.agents/`)
|
|
412
|
+
// is now invisible to soly — warn the user to rename it. soly no longer
|
|
413
|
+
// reads `.soly/`. One-time per session.
|
|
412
414
|
if (isLegacySolyDir(ctx.cwd)) {
|
|
413
415
|
notifyDeprecation(
|
|
414
416
|
ctx.ui,
|
|
415
|
-
`.soly/ (legacy)`,
|
|
416
|
-
`.agents
|
|
417
|
-
`Run \`mv .soly .agents\`
|
|
417
|
+
`.soly/ (legacy, no longer read)`,
|
|
418
|
+
`.agents/`,
|
|
419
|
+
`Run \`mv .soly .agents\` — soly now reads only \`.agents/\`.`,
|
|
418
420
|
);
|
|
419
421
|
}
|
|
420
422
|
|
|
421
423
|
// Rules sources (priority order, higher wins on relPath collision).
|
|
422
|
-
// Project rules always beat global rules.
|
|
424
|
+
// Project rules always beat global rules. `.agents/rules.local/` is
|
|
423
425
|
// gitignored — for personal overrides on top of the project's rules.
|
|
424
|
-
//
|
|
425
|
-
// (same role as the old .claude/rules/).
|
|
426
|
+
// `.agents/rules/` is the vendor-neutral project-level convention.
|
|
426
427
|
ruleSources = [
|
|
427
|
-
{ dir: path.join(ctx.cwd, ".
|
|
428
|
-
{ dir: path.join(ctx.cwd, ".soly", "rules"), source: "project-soly", sourceLabel: "soly", priority: 4 },
|
|
428
|
+
{ dir: path.join(ctx.cwd, ".agents", "rules.local"), source: "project-agents", sourceLabel: "local", priority: 5 },
|
|
429
429
|
{ dir: path.join(ctx.cwd, ".agents", "rules"), source: "project-agents", sourceLabel: "agents", priority: 3 },
|
|
430
|
-
{ dir: path.join(os.homedir(), ".soly", "rules"), source: "global-soly", sourceLabel: "soly", priority: 2 },
|
|
431
430
|
{ dir: path.join(os.homedir(), ".agents", "rules"), source: "global-agents", sourceLabel: "agents", priority: 1 },
|
|
432
431
|
];
|
|
433
432
|
refreshRules();
|
|
434
433
|
|
|
435
|
-
// Project state — soly owns .
|
|
434
|
+
// Project state — soly owns .agents/ at the project root
|
|
436
435
|
state.solyDir = solyDirFor(ctx.cwd);
|
|
437
436
|
refreshState();
|
|
438
437
|
|
|
@@ -499,7 +498,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
499
498
|
lastCodeMapSection = "";
|
|
500
499
|
}
|
|
501
500
|
|
|
502
|
-
// Load project intent (zero-point docs from .
|
|
501
|
+
// Load project intent (zero-point docs from .agents/docs/) — always
|
|
503
502
|
refreshIntent();
|
|
504
503
|
|
|
505
504
|
// Start hot-reload watcher on rules dirs
|
|
@@ -570,7 +569,7 @@ export default function solyExtension(pi: ExtensionAPI) {
|
|
|
570
569
|
);
|
|
571
570
|
}
|
|
572
571
|
} else {
|
|
573
|
-
ctx.ui.notify("soly rules: none found in .
|
|
572
|
+
ctx.ui.notify("soly rules: none found in .agents/rules.local, .agents/rules, or ~/.agents/rules", "info");
|
|
574
573
|
}
|
|
575
574
|
|
|
576
575
|
if (state.exists) {
|
package/init.ts
CHANGED
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
// "cli" — adds command/, flags/, shell-compat/ example rules
|
|
28
28
|
//
|
|
29
29
|
// Usage:
|
|
30
|
-
// /soly
|
|
31
|
-
// /soly
|
|
32
|
-
// /soly
|
|
30
|
+
// /soly init # interactive: pick template
|
|
31
|
+
// /soly init minimal # no prompts
|
|
32
|
+
// /soly init web-app --yes
|
|
33
33
|
// =============================================================================
|
|
34
34
|
|
|
35
35
|
import * as fs from "node:fs";
|
|
@@ -208,7 +208,7 @@ export async function initSolyProject(
|
|
|
208
208
|
// Preconditions
|
|
209
209
|
if (fs.existsSync(agentsDir) || fs.existsSync(path.join(cwd, ".soly"))) {
|
|
210
210
|
ui.notify(
|
|
211
|
-
`soly
|
|
211
|
+
`soly init: project already initialized (found ${SOLY_DIRNAME}/ or .soly/). ` +
|
|
212
212
|
`Aborting to avoid overwriting.`,
|
|
213
213
|
"error",
|
|
214
214
|
);
|
|
@@ -223,7 +223,7 @@ export async function initSolyProject(
|
|
|
223
223
|
["minimal", "web-app", "library", "cli"],
|
|
224
224
|
);
|
|
225
225
|
if (!pick) {
|
|
226
|
-
ui.notify("soly
|
|
226
|
+
ui.notify("soly init: cancelled", "info");
|
|
227
227
|
return { created: false, template: null, projectName };
|
|
228
228
|
}
|
|
229
229
|
template = pick as InitTemplate;
|
|
@@ -237,7 +237,7 @@ export async function initSolyProject(
|
|
|
237
237
|
`Create .agents/ structure in:\n ${cwd}\n\nProject name: ${projectName}`,
|
|
238
238
|
);
|
|
239
239
|
if (!ok) {
|
|
240
|
-
ui.notify("soly
|
|
240
|
+
ui.notify("soly init: cancelled", "info");
|
|
241
241
|
return { created: false, template: null, projectName };
|
|
242
242
|
}
|
|
243
243
|
}
|
|
@@ -289,7 +289,7 @@ export async function initSolyProject(
|
|
|
289
289
|
created.push(`.agents/${extra.file}`);
|
|
290
290
|
}
|
|
291
291
|
ui.notify(
|
|
292
|
-
`soly
|
|
292
|
+
`soly init: done (${template}). Created:\n - ${created.join("\n - ")}\n\n` +
|
|
293
293
|
`Next:\n 1. Edit \`.agents/docs/vision.md\`\n 2. \`/plan 1\` to start the first phase`,
|
|
294
294
|
"info",
|
|
295
295
|
);
|
package/intent.ts
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
// intent.ts — Project intent loader (the "0 point" of every soly project)
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// `.
|
|
5
|
+
// `.agents/docs/` is the zero-point of the project: documents written BEFORE
|
|
6
6
|
// any soly plans, research, or code. It holds the user's vision, business
|
|
7
7
|
// context, and architectural intent. Other soly artifacts (STATE, PLANS,
|
|
8
8
|
// RESEARCH) flow FROM this input.
|
|
9
9
|
//
|
|
10
10
|
// Supported files: `.md` (full text) and `.html` (parsed for title + preview).
|
|
11
|
-
// Nested directories are supported (e.g. `.
|
|
11
|
+
// Nested directories are supported (e.g. `.agents/docs/api/auth.md`).
|
|
12
12
|
//
|
|
13
|
-
// Convention: any document in `.
|
|
13
|
+
// Convention: any document in `.agents/docs/` is loaded into the system
|
|
14
14
|
// prompt as "project intent". This is separate from:
|
|
15
|
-
// - rules (`.
|
|
16
|
-
// - state (`.
|
|
15
|
+
// - rules (`.agents/rules/`) — how to behave
|
|
16
|
+
// - state (`.agents/STATE.md`, ROADMAP.md) — where we are
|
|
17
17
|
// - planning (PLAN.md, CONTEXT.md) — what to do next
|
|
18
18
|
//
|
|
19
|
-
// Optional: `.
|
|
19
|
+
// Optional: `.agents/phases/<N>/docs/` is also scanned if the directory exists
|
|
20
20
|
// (for backward compat / phase-specific intent). Not required.
|
|
21
21
|
// =============================================================================
|
|
22
22
|
|
|
23
23
|
import * as fs from "node:fs";
|
|
24
24
|
import * as path from "node:path";
|
|
25
|
-
import { formatTok, resolveImports } from "./core.ts";
|
|
25
|
+
import { formatTok, resolveImports, solyDirFor } from "./core.ts";
|
|
26
26
|
import { extractTitleAndPreview, stripHtml } from "./html.ts";
|
|
27
27
|
|
|
28
28
|
const DOC_EXTS = new Set([".md", ".html", ".htm"]);
|
|
@@ -148,15 +148,15 @@ function walkIntentDir(
|
|
|
148
148
|
|
|
149
149
|
export function loadIntentDocs(cwd: string, currentPhaseNumber?: number): IntentDoc[] {
|
|
150
150
|
const out: IntentDoc[] = [];
|
|
151
|
-
const docsRoot = path.join(cwd, "
|
|
151
|
+
const docsRoot = path.join(solyDirFor(cwd), "docs");
|
|
152
152
|
walkIntentDir(docsRoot, "", undefined, out);
|
|
153
153
|
|
|
154
154
|
// Optional: phase-specific docs (only if current phase is known)
|
|
155
155
|
if (currentPhaseNumber != null) {
|
|
156
156
|
// We don't know the slug here without re-walking phases; scan any
|
|
157
|
-
// directory in .
|
|
157
|
+
// directory in .agents/phases/ whose name starts with the phase number.
|
|
158
158
|
// Skip if no phases dir exists.
|
|
159
|
-
const phasesRoot = path.join(cwd, "
|
|
159
|
+
const phasesRoot = path.join(solyDirFor(cwd), "phases");
|
|
160
160
|
if (fs.existsSync(phasesRoot)) {
|
|
161
161
|
try {
|
|
162
162
|
const phaseEntries = fs.readdirSync(phasesRoot, { withFileTypes: true });
|
|
@@ -196,7 +196,7 @@ export function buildIntentSection(intent: IntentDoc[]): IntentSection {
|
|
|
196
196
|
return { hasContent: false, section: "" };
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
const lines: string[] = ["", "## project intent (from .
|
|
199
|
+
const lines: string[] = ["", "## project intent (from .agents/docs/)", ""];
|
|
200
200
|
lines.push(
|
|
201
201
|
"These documents are the **0 point** of this project — the user's vision, business context, and design intent, written BEFORE any soly plans. Read them first when planning, discussing, or executing. If implementation diverges from intent, fix one or the other — don't let drift compound.",
|
|
202
202
|
);
|
|
@@ -403,7 +403,7 @@ export function formatIntentStats(stats: IntentStats): string {
|
|
|
403
403
|
lines.push(``);
|
|
404
404
|
}
|
|
405
405
|
if (stats.totalDocs === 0) {
|
|
406
|
-
lines.push(`No intent docs found in .
|
|
406
|
+
lines.push(`No intent docs found in .agents/docs/ or ~/.agents/docs/`);
|
|
407
407
|
}
|
|
408
408
|
return lines.join("\n");
|
|
409
409
|
}
|
package/iteration.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// iteration.ts — Per-iteration context bundle (B2 of the soly design)
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// Writes a self-contained .md file under `.
|
|
5
|
+
// Writes a self-contained .md file under `.agents/iterations/` that bundles
|
|
6
6
|
// everything a worker needs for ONE iteration of a plan / task / phase:
|
|
7
7
|
// intent docs, STATE.md, ROADMAP row, phase CONTEXT, phase RESEARCH, up
|
|
8
8
|
// to 3 prior SUMMARYs, and the current PLAN.md (for exec).
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
// the worker's task string itself, so the worker has the most critical
|
|
19
19
|
// bits available even before opening the file.
|
|
20
20
|
//
|
|
21
|
-
// All worktree writes go to `.
|
|
22
|
-
// `.
|
|
21
|
+
// All worktree writes go to `.agents/iterations/` (under the project's
|
|
22
|
+
// `.agents/`), never to the project root. See workflow markdown templates
|
|
23
23
|
// for the hard rule.
|
|
24
24
|
// =============================================================================
|
|
25
25
|
|
|
@@ -160,7 +160,7 @@ export function findRecentSummaries(dir: string, n: number): string[] {
|
|
|
160
160
|
|
|
161
161
|
/** Find a phase's `<NN>-CONTEXT.md` path (or null).
|
|
162
162
|
* Convention: filename starts with the padded phase number, not the full
|
|
163
|
-
* slug. e.g. `.
|
|
163
|
+
* slug. e.g. `.agents/phases/05-auth/05-CONTEXT.md`, not `05-auth-CONTEXT.md`. */
|
|
164
164
|
export function findPhaseContextPath(phaseDir: string): string | null {
|
|
165
165
|
const slug = path.basename(phaseDir);
|
|
166
166
|
const numMatch = slug.match(/^(\d+)/);
|
|
@@ -192,7 +192,7 @@ export function findContinueHerePath(phaseDir: string): string | null {
|
|
|
192
192
|
|
|
193
193
|
function loadIntentSummary(solyDir: string, maxTokens: number): string {
|
|
194
194
|
const docsRoot = path.join(solyDir, "docs");
|
|
195
|
-
if (!fs.existsSync(docsRoot)) return "_(no \`.
|
|
195
|
+
if (!fs.existsSync(docsRoot)) return "_(no \`.agents/docs/\` directory — drop your 0-point docs there)_";
|
|
196
196
|
|
|
197
197
|
const files: string[] = [];
|
|
198
198
|
for (const e of fs.readdirSync(docsRoot, { withFileTypes: true })) {
|
|
@@ -206,7 +206,7 @@ function loadIntentSummary(solyDir: string, maxTokens: number): string {
|
|
|
206
206
|
files.push(full);
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
|
-
if (files.length === 0) return "_(no \`.md\` files in \`.
|
|
209
|
+
if (files.length === 0) return "_(no \`.md\` files in \`.agents/docs/\`)_";
|
|
210
210
|
|
|
211
211
|
files.sort();
|
|
212
212
|
const out: string[] = [];
|
|
@@ -223,7 +223,7 @@ function loadIntentSummary(solyDir: string, maxTokens: number): string {
|
|
|
223
223
|
.join(" ");
|
|
224
224
|
const chunk = `- \`${relPath}\`: ${preview.slice(0, 240)}\n`;
|
|
225
225
|
if (usedChars + chunk.length > maxChars) {
|
|
226
|
-
out.push(`\n_(truncated at ${maxTokens} tokens; full files in \`.
|
|
226
|
+
out.push(`\n_(truncated at ${maxTokens} tokens; full files in \`.agents/docs/\`)_\n`);
|
|
227
227
|
break;
|
|
228
228
|
}
|
|
229
229
|
out.push(chunk);
|
|
@@ -370,7 +370,7 @@ function loadAntiPatterns(phaseDir: string, maxTokens: number): string {
|
|
|
370
370
|
|
|
371
371
|
function loadFeatureReadme(feature: string, solyDir: string, maxTokens: number): string {
|
|
372
372
|
const readme = path.join(solyDir, "features", feature, "README.md");
|
|
373
|
-
if (!fs.existsSync(readme)) return `_(no \`.
|
|
373
|
+
if (!fs.existsSync(readme)) return `_(no \`.agents/features/${feature}/README.md\`)_`;
|
|
374
374
|
return truncate(readIfExists(readme) ?? "", maxTokens);
|
|
375
375
|
}
|
|
376
376
|
|
|
@@ -508,7 +508,7 @@ export function buildIterationContent(input: IterationInput): string {
|
|
|
508
508
|
sections.push(title);
|
|
509
509
|
sections.push("");
|
|
510
510
|
sections.push(`**Generated**: ${generatedAt}`);
|
|
511
|
-
sections.push(`**Soly dir**: \`${rel(projectRoot, input.solyDir) || ".
|
|
511
|
+
sections.push(`**Soly dir**: \`${rel(projectRoot, input.solyDir) || ".agents"}\``);
|
|
512
512
|
if (input.taskId) {
|
|
513
513
|
sections.push(`**Task**: \`${input.taskId}\` in feature \`${input.feature ?? "?"}\``);
|
|
514
514
|
} else if (input.phaseNumber != null) {
|
|
@@ -524,10 +524,10 @@ export function buildIterationContent(input: IterationInput): string {
|
|
|
524
524
|
sections.push("");
|
|
525
525
|
|
|
526
526
|
// ---- Section 0: Intent ----
|
|
527
|
-
sections.push("## 0. Project Intent (from `.
|
|
527
|
+
sections.push("## 0. Project Intent (from `.agents/docs/`)");
|
|
528
528
|
sections.push("");
|
|
529
529
|
sections.push(
|
|
530
|
-
`_Source: \`.
|
|
530
|
+
`_Source: \`.agents/docs/\` (full files in \`.agents/docs/\`, see also \`soly_intent\` tool)_`,
|
|
531
531
|
);
|
|
532
532
|
sections.push("");
|
|
533
533
|
sections.push(loadIntentSummary(input.solyDir, SECTION_BUDGETS.intent));
|
|
@@ -548,7 +548,7 @@ export function buildIterationContent(input: IterationInput): string {
|
|
|
548
548
|
|
|
549
549
|
// ---- Section 1: State (only if there's a project state) ----
|
|
550
550
|
if (input.kind !== "pause") {
|
|
551
|
-
sections.push("## 1. Project State (`.
|
|
551
|
+
sections.push("## 1. Project State (`.agents/STATE.md` — Current Position + Decisions)");
|
|
552
552
|
sections.push("");
|
|
553
553
|
sections.push(`_Source: \`${rel(projectRoot, path.join(input.solyDir, "STATE.md"))}\`_`);
|
|
554
554
|
sections.push("");
|
package/notification.ts
CHANGED
|
@@ -168,7 +168,7 @@ export function notifyDeprecation(
|
|
|
168
168
|
|
|
169
169
|
// ---------------------------------------------------------------------------
|
|
170
170
|
// Plain-text fallback (no widget, just framed Unicode text via notify).
|
|
171
|
-
// Used for the .soly/
|
|
171
|
+
// Used for the legacy .soly/ detection banner and other places that don't have
|
|
172
172
|
// access to a UI context yet (CLI startup, print mode, etc.)
|
|
173
173
|
// ---------------------------------------------------------------------------
|
|
174
174
|
|
package/notifications-log.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// notifications-log.ts — Persistent record of soly notifications
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// Appends every nudge/deprecation to a JSONL file at .agents
|
|
5
|
+
// Appends every nudge/deprecation to a JSONL file at .agents/notifications.log
|
|
6
6
|
// (inside the project dir; safe to commit if the user wants a public audit
|
|
7
7
|
// trail, or .gitignore if not). One line per notification, JSON-encoded.
|
|
8
8
|
//
|
|
@@ -33,7 +33,7 @@ export interface NotificationEntry {
|
|
|
33
33
|
/** Where the log file lives. Respects HOME for tests. */
|
|
34
34
|
export function logFilePath(cwd: string, home?: string): string {
|
|
35
35
|
const h = home ?? process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();
|
|
36
|
-
return path.join(cwd, SOLY_DIRNAME, "
|
|
36
|
+
return path.join(cwd, SOLY_DIRNAME, "notifications.log");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** Append a single entry to the log. Creates parent dirs as needed. */
|
package/nudge.ts
CHANGED
|
@@ -82,9 +82,42 @@ export function classifyTaskHeuristics(prompt: string): TaskHeuristics {
|
|
|
82
82
|
return { nonTrivial, researchHeavy, mentions, suggestedAngles };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Confirm-before-coding gate
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
/** How hard soly pushes the LLM to clarify before writing code.
|
|
90
|
+
* - "scope" — batch the substantive decisions (placement, pattern, scope,
|
|
91
|
+
* interface) via ask_pro, then wait. Strongest.
|
|
92
|
+
* - "ask" — lighter: one "ready to implement, or discuss?" question.
|
|
93
|
+
* - "off" — no gate. */
|
|
94
|
+
export type ConfirmLevel = "off" | "ask" | "scope";
|
|
95
|
+
|
|
96
|
+
/** Normalize the config value (boolean back-compat) to a confirm level.
|
|
97
|
+
* `true` → "scope" (strongest), `false` / absent → "off". */
|
|
98
|
+
export function confirmLevelOf(v: boolean | ConfirmLevel | undefined): ConfirmLevel {
|
|
99
|
+
if (v === true) return "scope";
|
|
100
|
+
if (v === "ask" || v === "scope") return v;
|
|
101
|
+
return "off";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// "scope": pull the decisions only the user can make BEFORE coding, as one
|
|
105
|
+
// batched ask_pro call — placement, pattern, scope, interface — instead of
|
|
106
|
+
// guessing and editing files on assumptions.
|
|
107
|
+
const SCOPE_DIRECTIVE = `**Scope it with me before you code.** For non-trivial work, do NOT start writing or editing files on assumptions about decisions only I can make. First surface them as a single \`ask_pro\` batch (2–5 questions in one call), covering the dimensions that actually matter for this task — typically:
|
|
108
|
+
- **Placement** — where should this live? (which file / module / layer; extend an existing unit or add a new one)
|
|
109
|
+
- **Architecture / pattern** — which approach? (follow an existing pattern in the codebase vs introduce a new one; reuse a dependency vs hand-roll)
|
|
110
|
+
- **Scope** — what's in vs out for this change? (defer adjacent work to its own task)
|
|
111
|
+
- **Interface** — the shape callers see (API / CLI / signature / return), when it's a genuine fork
|
|
112
|
+
- **Data / state** — schema, storage, or state-shape decisions, when relevant
|
|
113
|
+
Give each question 2–4 concrete options with a ⭐ recommended default + one-line rationale, and add \`allowOther\` so I can steer freely. Wait for my answers before touching files. Skip only for trivial fixes (typo / rename / one-liner) or when I've already decided ("just do it", "go", or a follow-up turn in an already-scoped task). Prefer 2–3 sharp questions over a long list — ask what changes the result, not ceremony.`;
|
|
114
|
+
|
|
115
|
+
// "ask": lighter — a single "ready, or discuss first?" confirmation.
|
|
116
|
+
const ASK_DIRECTIVE = `**Confirm before coding.** Don't jump straight into writing/editing code. First state your understanding and intended approach in 1–3 sentences and list anything still open, then ask via the \`ask_pro\` picker whether to proceed — one question with options like "Go — implement now", "Discuss / refine the approach", "Adjust scope first" (add an \`allowOther\` for a free-text steer). Wait for the choice before touching files. Skip only for trivial fixes, or when the user already said to proceed ("just do it", "go", "yes").`;
|
|
117
|
+
|
|
85
118
|
export function buildNudgeSection(
|
|
86
119
|
heuristics: TaskHeuristics,
|
|
87
|
-
opts: { hasProject?: boolean; confirmBeforeCode?: boolean } = {},
|
|
120
|
+
opts: { hasProject?: boolean; confirmBeforeCode?: boolean | ConfirmLevel } = {},
|
|
88
121
|
): string {
|
|
89
122
|
// Always-on rules (cheap to add, high signal):
|
|
90
123
|
// - Don't dive in on non-trivial tasks without a brief check
|
|
@@ -112,14 +145,16 @@ export function buildNudgeSection(
|
|
|
112
145
|
// model toward the workflow lifecycle instead of ad-hoc edits.
|
|
113
146
|
const workflowPoint =
|
|
114
147
|
opts.hasProject && heuristics.nonTrivial
|
|
115
|
-
? `\n\n4. **Route project work through the soly workflow.** For phase/task work in \`.
|
|
148
|
+
? `\n\n4. **Route project work through the soly workflow.** For phase/task work in \`.agents/\`, prefer the lifecycle over ad-hoc edits: \`soly discuss <N>\` (scope) → \`soly plan <N>\` (write tasks) → \`soly execute <N>\` (do them) → \`soly verify\` (review). Run \`soly status\` to see where you are. Skip only for a genuine one-off.`
|
|
116
149
|
: "";
|
|
117
150
|
|
|
118
|
-
// Confirm-before-coding gate: for non-trivial implementation,
|
|
119
|
-
//
|
|
151
|
+
// Confirm-before-coding gate: for non-trivial implementation, pull the
|
|
152
|
+
// open decisions out of the user before editing files. Strength is config-
|
|
153
|
+
// driven ("scope" = batch the substantive questions; "ask" = one go/discuss).
|
|
154
|
+
const confirmLevel = confirmLevelOf(opts.confirmBeforeCode);
|
|
120
155
|
const confirmBlock =
|
|
121
|
-
|
|
122
|
-
? `\n\n
|
|
156
|
+
confirmLevel !== "off" && heuristics.nonTrivial
|
|
157
|
+
? `\n\n ${confirmLevel === "scope" ? SCOPE_DIRECTIVE : ASK_DIRECTIVE}`
|
|
123
158
|
: "";
|
|
124
159
|
|
|
125
160
|
return `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-soly",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Project management + workflow framework for pi-coding-agent. Plans, state, MANDATORY rules, self-review, multi-question picker, native footer + welcome chrome — one npm install, zero config. The LLM drives execution and delegates to a worker subagent when available.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
"notifications-log.ts",
|
|
59
59
|
"status.ts",
|
|
60
60
|
"init.ts",
|
|
61
|
-
"migrate.ts",
|
|
62
61
|
"codemap.ts",
|
|
63
62
|
"state.ts",
|
|
64
63
|
"hotreload.ts"
|
|
@@ -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/`
|