pi-soly 1.16.1 → 1.16.3

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/config.ts CHANGED
@@ -272,9 +272,13 @@ function deepMerge(base: SolyConfig, over: RawConfig): SolyConfig {
272
272
  merged.editor.command = over.editor.command;
273
273
  }
274
274
  if (over.plan && typeof over.plan.defaultBranchPrefix === "string") {
275
- // Sanitize: allow only kebab-case words (no slashes, dots, etc.).
276
- // Empty string is allowed and means "no prefix".
277
- const sanitized = over.plan.defaultBranchPrefix.replace(/[^a-z0-9-]/gi, "");
275
+ // Sanitize: lowercase, then allow only kebab-case chars (no
276
+ // slashes/dots/spaces). Empty string is allowed and means
277
+ // "no prefix". We do NOT warn on slash/uppercase — the user
278
+ // probably meant a different prefix shape; silently normalizing
279
+ // keeps configs from being rejected for cosmetic reasons.
280
+ const lowercased = over.plan.defaultBranchPrefix.toLowerCase();
281
+ const sanitized = lowercased.replace(/[^a-z0-9-]/g, "");
278
282
  const collapsed = sanitized.replace(/-+/g, "-").replace(/^-|-$/g, "");
279
283
  merged.plan.defaultBranchPrefix = collapsed;
280
284
  }
package/nudge.ts CHANGED
@@ -169,7 +169,15 @@ export function buildNudgeSection(
169
169
  - \`soly verify\` — self-review loop until clean
170
170
  - \`soly status\` — current position + progress (no LLM round-trip)
171
171
 
172
- **You may scaffold a new plan yourself** when the user asks for a new piece of work and the scope is clear (or the user just confirmed). Propose the slug AND the full branch name first via ask_pro — offer the default \`${prefix ? prefix + "/" : ""}<slug>\` plus the alternatives \`fix/<slug>\`, \`chore/<slug>\`, or just \`<slug>\` (no prefix). Pick whatever fits the work, then type \`soly new …\` in your next output. Don't ask for trivial one-liners or when the user already said "go". Skip only for a genuine one-off that doesn't deserve a plan branch.`
172
+ **You may scaffold a new plan yourself** when the user asks for a new piece of work and the scope is clear (or the user just confirmed). Propose the slug AND the full branch name first via ask_pro — offer the default \`${prefix ? prefix + "/" : ""}<slug>\` plus the alternatives \`fix/<slug>\`, \`chore/<slug>\`, or just \`<slug>\` (no prefix). Pick whatever fits the work, then type \`soly new …\` in your next output. Don't ask for trivial one-liners or when the user already said "go". Skip only for a genuine one-off that doesn't deserve a plan branch.
173
+
174
+ **STUDY THE REPO before scaffolding or fleshing out a plan.** Before you write or edit any plan, use the available tools to understand the area you're about to touch:
175
+ - \`soly_snippet(path, offset, limit)\` and \`soly_doc_search(query)\` for bounded reads of the relevant files (don't \`read\` the whole tree — that's why these tools exist).
176
+ - The \`## project layout\` section in this system prompt for a directory overview.
177
+ - \`.agents/docs/\` (intent) for *why* the project is the way it is.
178
+ - \`git log --oneline -20\` for recent context (existing patterns, prior decisions).
179
+
180
+ What to extract: where similar features live, the naming/file-layout conventions, how errors are handled, how tests are written, and any constraints you missed in the prompt. Surface ambiguities as \`ask_pro\` questions BEFORE writing the plan — a plan that knows "the tests go in __tests__/" beats a plan that picks an arbitrary location and rewrites.`
173
181
  : "";
174
182
 
175
183
  // Confirm-before-coding gate: for non-trivial implementation, pull the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-soly",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
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",
@@ -181,6 +181,8 @@ The worker reads this file first — it contains intent, STATE, ROADMAP (n/a for
181
181
 
182
182
  **0-POINT CHECK.** Worker must re-read .agents/docs/ (intent) and .agents/features/${task.feature}/README.md before implementing.
183
183
 
184
+ **STUDY THE REPO.** The worker MUST use \`soly_snippet(path, offset, limit)\`, \`soly_doc_search(query)\`, and \`## project layout\` from the system prompt to understand the area before changing any file. Read at least: the module being modified, one similar feature for the pattern, and the corresponding test file. Do NOT change code based on assumptions about how the existing code works.
185
+
184
186
  Launch a single subagent for this work. Do NOT do the work inline.
185
187
 
186
188
  subagent({
@@ -274,9 +276,12 @@ When the subagent completes, synthesize the result. Do not re-execute its work.
274
276
  };
275
277
  }
276
278
 
277
- // === PLAN MODE (new dual-mode: `<type>/<name>` plans live under .agents/plans/<name>/) ===
279
+ // === PLAN MODE (new dual-mode: `<type>/<name>` plans live under .agents/plans/<prefix>-<name>/) ===
278
280
  if (target.kind === "plan") {
279
- const planDirAbs = `${state.solyDir}/plans/${target.name}`;
281
+ // Plan dir is always flattened: `<prefix>-<name>` if a prefix is
282
+ // present, else just `<name>`.
283
+ const dirSlug = target.prefix ? `${target.prefix}-${target.name}` : target.name;
284
+ const planDirAbs = `${state.solyDir}/plans/${dirSlug}`;
280
285
  const planFile = `${planDirAbs}/PLAN.md`;
281
286
  let planBody: string;
282
287
  try {
@@ -323,6 +328,8 @@ ${planBody.slice(0, 4000)}${planBody.length > 4000 ? "\n…(truncated)" : ""}
323
328
 
324
329
  **0-POINT CHECK.** Worker must re-read .agents/docs/ (intent) before implementing.
325
330
 
331
+ **STUDY THE REPO.** Worker MUST use \`soly_snippet(path, offset, limit)\`, \`soly_doc_search(query)\`, and \`## project layout\` from the system prompt to map the area before editing. Read at least: the module(s) the plan touches, one adjacent feature for the convention, and any test files in the same area. Do NOT edit code on assumptions about how existing code is structured.
332
+
326
333
  Launch a single subagent to execute the plan. Do NOT do the work inline.
327
334
 
328
335
  subagent({
@@ -241,7 +241,7 @@ function parseNewTaskFlag(
241
241
  */
242
242
  export type ExecuteTarget =
243
243
  | { kind: "phase"; phase: number; plan: number | null; raw: string }
244
- | { kind: "plan"; name: string; raw: string }
244
+ | { kind: "plan"; name: string; prefix: string | null; raw: string }
245
245
  | { kind: "task"; taskId: string; raw: string }
246
246
  | { kind: "all"; raw: string }
247
247
  | { kind: "feature"; feature: string; raw: string };
@@ -287,7 +287,7 @@ export function describeExecuteTarget(args: string[]): ExecuteTarget | null {
287
287
 
288
288
  const plan = parsePlanName(target);
289
289
  if (!("error" in plan)) {
290
- return { kind: "plan", name: plan.name, raw };
290
+ return { kind: "plan", name: plan.name, prefix: plan.prefix, raw };
291
291
  }
292
292
 
293
293
  const phase = parsePhaseShape(target);
@@ -313,7 +313,7 @@ export function describeExecuteTarget(args: string[]): ExecuteTarget | null {
313
313
  */
314
314
  export type PlanTarget =
315
315
  | { kind: "phase"; phase: number; raw: string }
316
- | { kind: "plan"; name: string; raw: string }
316
+ | { kind: "plan"; name: string; prefix: string | null; raw: string }
317
317
  | { kind: "task"; taskId: string; raw: string }
318
318
  | { kind: "new-task"; slug: string; feature: string; raw: string }
319
319
  | { kind: "feature"; feature: string; raw: string };
@@ -361,7 +361,7 @@ export function describePlanTarget(args: string[]): PlanTarget | null {
361
361
 
362
362
  const plan = parsePlanName(target);
363
363
  if (!("error" in plan)) {
364
- return { kind: "plan", name: plan.name, raw };
364
+ return { kind: "plan", name: plan.name, prefix: plan.prefix, raw };
365
365
  }
366
366
 
367
367
  // Plan target only matches plain N (no .MM — plan is per-phase, executed
@@ -91,9 +91,13 @@ export function buildPlanTransform(cmd: SolyCommand, state: SolyState): Planning
91
91
  };
92
92
  }
93
93
 
94
- // === PLAN MODE (new dual-mode: `<type>/<name>` plans live under .agents/plans/<name>/) ===
94
+ // === PLAN MODE (new dual-mode: `<type>/<name>` plans live under .agents/plans/<prefix>-<name>/) ===
95
95
  if (target.kind === "plan") {
96
- const planDirAbs = `${state.solyDir}/plans/${target.name}`;
96
+ // Plan dir is always flattened: `<prefix>-<name>` if a prefix is
97
+ // present (user typed `<prefix>/<slug>` or the project default
98
+ // applied), else just `<name>`.
99
+ const dirSlug = target.prefix ? `${target.prefix}-${target.name}` : target.name;
100
+ const planDirAbs = `${state.solyDir}/plans/${dirSlug}`;
97
101
  const planFile = `${planDirAbs}/PLAN.md`;
98
102
  let planBody: string;
99
103
  try {
@@ -114,6 +118,8 @@ ${planBody.slice(0, 4000)}${planBody.length > 4000 ? "\n…(truncated)" : ""}
114
118
 
115
119
  **0-POINT CHECK.** Re-read .agents/docs/ (intent) before fleshing out the plan.
116
120
 
121
+ **STUDY THE REPO.** Before writing PLAN.md, use \`soly_snippet(path, offset, limit)\` and \`soly_doc_search(query)\` to understand the area this plan will touch. Look at how similar features are implemented in this codebase (naming, file layout, error handling, test convention), and check \`## project layout\` from the system prompt for orientation. Extract enough context that the plan names concrete files and conventions, not abstractions. Surface ambiguities as \`ask_pro\` questions BEFORE writing PLAN.md.
122
+
117
123
  If ask_pro is available, use it ONCE to gather goal / steps / acceptance criteria (freeText questions, batched). Then write PLAN.md with the answers. If ask_pro is NOT available, write a sensible stub plan and tell the user to refine it via \`soly plan ${target.raw}\` again.
118
124
 
119
125
  Hard rules:
@@ -514,10 +520,11 @@ export function buildDiscussTransform(
514
520
  };
515
521
  }
516
522
 
517
- // Plan mode: `<type>/<name>`
523
+ // Plan mode: `<slug>` or `<prefix>/<slug>`
518
524
  const planParsed = parsePlanName(arg);
519
525
  if (!("error" in planParsed)) {
520
- const planDirAbs = `${state.solyDir}/plans/${planParsed.name}`;
526
+ const dirSlug = planParsed.prefix ? `${planParsed.prefix}-${planParsed.name}` : planParsed.name;
527
+ const planDirAbs = `${state.solyDir}/plans/${dirSlug}`;
521
528
  const planFile = `${planDirAbs}/PLAN.md`;
522
529
  let planBody: string;
523
530
  try {
@@ -538,6 +545,8 @@ ${planBody.slice(0, 4000)}${planBody.length > 4000 ? "\n…(truncated)" : ""}
538
545
 
539
546
  **0-POINT CHECK.** Re-read .agents/docs/ (intent) before discussing.
540
547
 
548
+ **STUDY THE REPO.** Use \`soly_snippet(path, offset, limit)\`, \`soly_doc_search(query)\`, and \`## project layout\` from the system prompt to understand the area the plan touches. The discussion should resolve real ambiguities in the existing code, not invent new ones. If the plan body mentions files you haven't read, read them first.
549
+
541
550
  Use \`soly_finish_discuss\` to capture the discussion outcome. The flow:
542
551
  1. Read the plan above.
543
552
  2. Identify ambiguities / open questions / scope clarifications.