@leing2021/super-pi 0.24.0 → 0.25.1
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 +14 -26
- package/extensions/ce-core/index.ts +74 -7
- package/extensions/ce-core/tools/ask-user-question-ui.ts +162 -0
- package/extensions/ce-core/tools/ask-user-question.ts +84 -5
- package/package.json +3 -1
- package/skills/02-plan/references/ceo-review-mode.md +1 -1
- package/skills/08-help/SKILL.md +0 -46
- package/skills/08-help/references/workflow-sequence.md +0 -135
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**Turn your AI coding agent into a reliable engineer.**
|
|
10
10
|
|
|
11
|
-
Super Pi is a Pi-native engineering workflow layer: it adds stage discipline, durable artifacts, TDD gates, checkpoints, review, and learning loops on top of your coding agent.
|
|
11
|
+
Super Pi is a Pi-native engineering workflow layer: it adds stage discipline, durable artifacts, TDD gates, checkpoints, review, and learning loops on top of your coding agent.
|
|
12
12
|
|
|
13
13
|
Install, describe what you want to build, then keep saying "continue." Super Pi drives the full loop:
|
|
14
14
|
|
|
@@ -25,10 +25,9 @@ pi install npm:@leing2021/super-pi
|
|
|
25
25
|
- **Five-step loop** — brainstorm → plan → work → review → learn, with automatic skill routing
|
|
26
26
|
- **Checkpoint resume** — interrupted? Resume from the exact unit you left off
|
|
27
27
|
- **TDD enforcement** — every unit follows RED → GREEN → REFACTOR with hard gates
|
|
28
|
-
- **Controlled subagents** — inline execution by default; subagents are limited to bounded, non-interactive leaf tasks
|
|
29
28
|
- **Evidence-first review** — auto-assigned reviewers across five axes, autofix loop
|
|
30
29
|
- **Knowledge compounding** — solved problems become searchable solution artifacts
|
|
31
|
-
- **Token-efficient** — ~
|
|
30
|
+
- **Token-efficient** — ~4,200 tokens new-conversation overhead; progressive loading
|
|
32
31
|
|
|
33
32
|
---
|
|
34
33
|
|
|
@@ -45,7 +44,7 @@ You: I want to build a CLI tool that helps indie devs find early users
|
|
|
45
44
|
|
|
46
45
|
→ 01-brainstorm: structured discovery → requirements artifact
|
|
47
46
|
→ 02-plan: TDD-gated implementation units → plan artifact
|
|
48
|
-
→ 03-work: inline
|
|
47
|
+
→ 03-work: inline execution, checkpoint resume
|
|
49
48
|
→ 04-review: five-axis findings, autofix loop
|
|
50
49
|
→ 05-learn: knowledge compounding
|
|
51
50
|
|
|
@@ -73,22 +72,11 @@ You: /skill:03-work docs/plans/plan.md
|
|
|
73
72
|
|-------|-------------|-----------|
|
|
74
73
|
| **01-brainstorm** | Structured multi-round discovery, domain vocabulary persistence | `brainstorm_dialog` |
|
|
75
74
|
| **02-plan** | TDD-gated implementation units, optional CEO Review | `plan_diff` |
|
|
76
|
-
| **03-work** | Inline
|
|
75
|
+
| **03-work** | Inline execution, checkpoint resume, strict TDD, stop-the-line | `session_checkpoint`, `task_splitter` |
|
|
77
76
|
| **04-review** | Auto-assigned reviewers, five-axis findings, autofix loop | `review_router` |
|
|
78
77
|
| **05-learn** | Pattern extraction → searchable solution artifacts | `pattern_extractor` |
|
|
79
78
|
| **06-next** | Next-step recommendation + workflow status | `workflow_state` |
|
|
80
79
|
| **07-worktree** | Isolated git worktree development | `worktree_manager` |
|
|
81
|
-
| **08-help** | Phase 1 skill explainer | — |
|
|
82
|
-
|
|
83
|
-
### Optional: External subagent delegation
|
|
84
|
-
|
|
85
|
-
For generic child agent delegation (background jobs, parallel audits, scout/worker/oracle agents), install the community extension `pi-subagents` separately. Super Pi does not require it.
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
pi install npm:pi-subagents
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Super Pi focuses on inline workflow execution. Use subagents only when you need true process isolation or background runs.
|
|
92
80
|
|
|
93
81
|
### Model & Thinking Routing
|
|
94
82
|
|
|
@@ -121,7 +109,7 @@ Super Pi is not a fork or wrapper. It extracts useful methods from the projects
|
|
|
121
109
|
| Project | What Super Pi adopted |
|
|
122
110
|
|---------|------------------------|
|
|
123
111
|
| [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) | "Use when" skill trigger conditions, source-driven verification, stop-the-line hard gate, anti-rationalization, and the five-axis review baseline. Adopted as embedded micro-patterns only — no new skills, tools, commands, or agents. |
|
|
124
|
-
| [everything-claude-code](https://github.com/affaan-m/everything-claude-code) |
|
|
112
|
+
| [everything-claude-code](https://github.com/affaan-m/everything-claude-code) | Checkpoint resume, continuous learning loops, and token-conscious agent workflow design. |
|
|
125
113
|
| [humanlayer/12-factor-agents](https://github.com/humanlayer/12-factor-agents) | Context window ownership, compacting resolved errors, retry caps, and pre-fetching obvious prerequisites. Adopted as lightweight context hygiene rules inside the existing Phase 1 pipeline. |
|
|
126
114
|
| [superpowers](https://github.com/obra/superpowers) | Strict TDD gates, design checklists, review discipline, and the idea that agents need hard gates instead of gentle suggestions. |
|
|
127
115
|
| [compound-engineering-plugin](https://github.com/EveryInc/compound-engineering-plugin) | The five-step think → plan → build → review → learn loop and the knowledge-compounding backbone. |
|
|
@@ -157,13 +145,13 @@ All reviewers evaluate changes across: **correctness, readability, architecture,
|
|
|
157
145
|
|
|
158
146
|
## Token Cost
|
|
159
147
|
|
|
160
|
-
New conversation overhead: **~
|
|
148
|
+
New conversation overhead: **~4,130 tokens** (2.1% of 200K context).
|
|
161
149
|
|
|
162
150
|
| Component | Tokens |
|
|
163
151
|
|-----------|--------|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
| Skill inlining (per invocation) | ~
|
|
152
|
+
| 17 skill registrations | ~1,710 |
|
|
153
|
+
| 22 tool schemas | ~2,420 |
|
|
154
|
+
| Skill inlining (per invocation) | ~300–1,200 |
|
|
167
155
|
|
|
168
156
|
Progressive loading: only needed skills loaded on-demand.
|
|
169
157
|
|
|
@@ -195,11 +183,11 @@ Commit everything to git — these files are the project's traceable memory.
|
|
|
195
183
|
|
|
196
184
|
| Component | Count |
|
|
197
185
|
|-----------|------:|
|
|
198
|
-
| Skills |
|
|
199
|
-
| Tools |
|
|
200
|
-
| Rules |
|
|
201
|
-
| TypeScript lines | ~4,
|
|
202
|
-
| Tests |
|
|
186
|
+
| Skills | 7 |
|
|
187
|
+
| Tools | 12 CE + 10 Pi built-in |
|
|
188
|
+
| Rules | 78 |
|
|
189
|
+
| TypeScript lines | ~4,100 |
|
|
190
|
+
| Tests | 180 (727 assertions) |
|
|
203
191
|
|
|
204
192
|
Rules in `rules/` cover 11 common topics + language-specific sets (TypeScript, Rust, Go, Python, Java, Kotlin, C++, C#, Dart, Swift, Perl, PHP). Project-level overrides take priority.
|
|
205
193
|
|
|
@@ -3,7 +3,8 @@ import path from "node:path"
|
|
|
3
3
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"
|
|
4
4
|
import { Type } from "typebox"
|
|
5
5
|
import { createArtifactHelperTool, type ArtifactType } from "./tools/artifact-helper"
|
|
6
|
-
import { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
6
|
+
import { createAskUserQuestionTool, CUSTOM_SENTINEL } from "./tools/ask-user-question"
|
|
7
|
+
import { createAskUserQuestionCustomFactory } from "./tools/ask-user-question-ui"
|
|
7
8
|
import { createWorkflowStateTool } from "./tools/workflow-state"
|
|
8
9
|
import { createWorktreeManagerTool } from "./tools/worktree-manager"
|
|
9
10
|
import { createReviewRouterTool } from "./tools/review-router"
|
|
@@ -26,6 +27,70 @@ const PIPELINE_STAGE_KEYS = new Set([
|
|
|
26
27
|
"05-learn",
|
|
27
28
|
])
|
|
28
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Module-level promise chain that serializes interactive `ask_user_question`
|
|
32
|
+
* UI calls within a single Pi process.
|
|
33
|
+
*
|
|
34
|
+
* Why: `@earendil-works/pi-coding-agent` renders extension selectors via a
|
|
35
|
+
* singleton field (`extensionSelector` / `extensionInput` in
|
|
36
|
+
* `interactive-mode.js`). When two selector calls overlap, the second
|
|
37
|
+
* overwrites the first, and the first promise never resolves — surfacing to
|
|
38
|
+
* the agent as a silent `No result provided`. See
|
|
39
|
+
* `docs/bug/ask-user-question-parallel-call-silent-failure.md`.
|
|
40
|
+
*
|
|
41
|
+
* This guard ensures at most one selector/input is on screen at a time.
|
|
42
|
+
*/
|
|
43
|
+
let askUserQuestionExclusiveChain: Promise<unknown> = Promise.resolve()
|
|
44
|
+
|
|
45
|
+
function runAskUserQuestionExclusive<T>(task: () => Promise<T>): Promise<T> {
|
|
46
|
+
const run = askUserQuestionExclusiveChain.then(task, task)
|
|
47
|
+
// Keep the chain robust: a rejected run must not poison subsequent calls.
|
|
48
|
+
askUserQuestionExclusiveChain = run.then(
|
|
49
|
+
() => undefined,
|
|
50
|
+
() => undefined,
|
|
51
|
+
)
|
|
52
|
+
return run
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build the UI adapter for `ask_user_question`.
|
|
57
|
+
*
|
|
58
|
+
* In interactive TUI mode with `ctx.ui.custom` available, route `select`
|
|
59
|
+
* through the scrollable custom dialog so long questions/options stay readable
|
|
60
|
+
* (see `docs/bug/ask-user-question-long-text-not-scrollable.md`). Otherwise
|
|
61
|
+
* fall back to the built-in `ctx.ui.select()`.
|
|
62
|
+
*/
|
|
63
|
+
function buildAskUserQuestionUi(ctx: any): import("./tools/ask-user-question").AskUserQuestionUi {
|
|
64
|
+
const useCustom = ctx?.mode === "tui" && typeof ctx?.ui?.custom === "function"
|
|
65
|
+
const input = async (question: string) => (await ctx.ui.input(question)) ?? null
|
|
66
|
+
const selectFallback = async (question: string, options: string[]) =>
|
|
67
|
+
(await ctx.ui.select(question, options)) ?? null
|
|
68
|
+
|
|
69
|
+
if (!useCustom) {
|
|
70
|
+
return { input, select: selectFallback }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const select = async (question: string, options: string[]): Promise<string | null> => {
|
|
74
|
+
// The custom sentinel is appended last (if present) and matches
|
|
75
|
+
// `CUSTOM_SENTINEL` or a `CUSTOM_SENTINEL (#n)` disambiguation suffix.
|
|
76
|
+
const last = options.length > 0 ? options[options.length - 1] : null
|
|
77
|
+
const customLabel = last !== null
|
|
78
|
+
&& (last === CUSTOM_SENTINEL || last.startsWith(`${CUSTOM_SENTINEL} (#`))
|
|
79
|
+
? last
|
|
80
|
+
: null
|
|
81
|
+
const factory = createAskUserQuestionCustomFactory({
|
|
82
|
+
question,
|
|
83
|
+
displayOptions: options,
|
|
84
|
+
customLabel,
|
|
85
|
+
})
|
|
86
|
+
const selection = (await ctx.ui.custom(factory)) as
|
|
87
|
+
{ selectedLabel: string | null } | null | undefined
|
|
88
|
+
return selection?.selectedLabel ?? null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { input, select }
|
|
92
|
+
}
|
|
93
|
+
|
|
29
94
|
interface StrategySettings {
|
|
30
95
|
modelStrategy?: Record<string, string>
|
|
31
96
|
thinkingStrategy?: Record<string, string>
|
|
@@ -399,6 +464,11 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
399
464
|
name: askUserQuestion.name,
|
|
400
465
|
label: "Ask User Question",
|
|
401
466
|
description: "Ask the user a structured question with optional choices and custom answers.",
|
|
467
|
+
promptSnippet: "Ask the user a structured question with optional choices and custom answers",
|
|
468
|
+
promptGuidelines: [
|
|
469
|
+
"Call ask_user_question one at a time, never two or more in the same assistant message: Pi renders selectors on a shared singleton, so parallel ask_user_question calls silently fail with 'No result provided'.",
|
|
470
|
+
"For ask_user_question, keep the question concise and put long analysis in prior text or a file first; ask_user_question normalizes long options to short labels but the full answer is still returned to you.",
|
|
471
|
+
],
|
|
402
472
|
parameters: askUserQuestionParams,
|
|
403
473
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
404
474
|
if (!ctx.hasUI) {
|
|
@@ -409,17 +479,14 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
409
479
|
}
|
|
410
480
|
}
|
|
411
481
|
|
|
412
|
-
const result = await askUserQuestion.execute(
|
|
482
|
+
const result = await runAskUserQuestionExclusive(() => askUserQuestion.execute(
|
|
413
483
|
{
|
|
414
484
|
question: params.question,
|
|
415
485
|
options: params.options,
|
|
416
486
|
allowCustom: params.allowCustom,
|
|
417
487
|
},
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
select: async (question, options) => (await ctx.ui.select(question, options)) ?? null,
|
|
421
|
-
},
|
|
422
|
-
)
|
|
488
|
+
buildAskUserQuestionUi(ctx),
|
|
489
|
+
))
|
|
423
490
|
|
|
424
491
|
const contentText = result.answer === null
|
|
425
492
|
? "User cancelled."
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Container,
|
|
3
|
+
Key,
|
|
4
|
+
matchesKey,
|
|
5
|
+
truncateToWidth,
|
|
6
|
+
wrapTextWithAnsi,
|
|
7
|
+
} from "@earendil-works/pi-tui"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Result shape returned by the scrollable ask_user_question custom UI.
|
|
11
|
+
*
|
|
12
|
+
* - `selectedLabel`: the normalized display label the user picked (may be the
|
|
13
|
+
* custom sentinel), or `null` when cancelled.
|
|
14
|
+
* - The caller maps `selectedLabel` back to the original full option string.
|
|
15
|
+
*/
|
|
16
|
+
export interface AskUserQuestionSelection {
|
|
17
|
+
selectedLabel: string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface AskUserQuestionCustomOptions {
|
|
21
|
+
question: string
|
|
22
|
+
displayOptions: string[]
|
|
23
|
+
customLabel: string | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Maximum number of option/question rows rendered before the viewport scrolls.
|
|
28
|
+
* Keeps the dialog inside typical terminal heights while still being readable.
|
|
29
|
+
*/
|
|
30
|
+
const MAX_VISIBLE_ROWS = 10
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Render the question body as wrapped lines so long questions stay readable
|
|
34
|
+
* inside the scrollable custom dialog.
|
|
35
|
+
*/
|
|
36
|
+
function renderQuestionLines(question: string, width: number, fg: (color: string, text: string) => string): string[] {
|
|
37
|
+
const lines = wrapTextWithAnsi(question, width)
|
|
38
|
+
return lines.map((line) => truncateToWidth(fg("text", line), width))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Scrollable selector component for `ask_user_question`.
|
|
43
|
+
*
|
|
44
|
+
* Renders a wrapped (multi-line) question and a list of single-line options.
|
|
45
|
+
* Handles:
|
|
46
|
+
* - up/down navigation with scroll offset
|
|
47
|
+
* - Enter to confirm
|
|
48
|
+
* - Escape / cancel keybinding to cancel
|
|
49
|
+
* - optional "Other" custom entry label
|
|
50
|
+
*
|
|
51
|
+
* Built to work inside Pi's `ctx.ui.custom()` factory contract:
|
|
52
|
+
* `(tui, theme, keybindings, done) => Component`.
|
|
53
|
+
*/
|
|
54
|
+
export class AskUserQuestionSelector extends Container {
|
|
55
|
+
private readonly options: string[]
|
|
56
|
+
private readonly question: string
|
|
57
|
+
private readonly customLabel: string | null
|
|
58
|
+
private readonly theme: { fg: (color: string, text: string) => string }
|
|
59
|
+
private readonly done: (result: AskUserQuestionSelection) => void
|
|
60
|
+
private selectedIndex = 0
|
|
61
|
+
private scrollOffset = 0
|
|
62
|
+
private resolved = false
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
opts: AskUserQuestionCustomOptions,
|
|
66
|
+
theme: { fg: (color: string, text: string) => string },
|
|
67
|
+
done: (result: AskUserQuestionSelection) => void,
|
|
68
|
+
) {
|
|
69
|
+
super()
|
|
70
|
+
this.options = opts.displayOptions
|
|
71
|
+
this.question = opts.question
|
|
72
|
+
this.customLabel = opts.customLabel
|
|
73
|
+
this.theme = theme
|
|
74
|
+
this.done = done
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Resolve a selected display label into the result handed to `done()`. */
|
|
78
|
+
private commit(index: number): void {
|
|
79
|
+
if (this.resolved) return
|
|
80
|
+
this.resolved = true
|
|
81
|
+
const selected = this.options[index] ?? null
|
|
82
|
+
this.done({ selectedLabel: selected })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
handleInput(data: string): void {
|
|
86
|
+
if (matchesKey(data, Key.up) || data === "k") {
|
|
87
|
+
this.selectedIndex = Math.max(0, this.selectedIndex - 1)
|
|
88
|
+
this.scrollOffset = Math.min(this.scrollOffset, this.selectedIndex)
|
|
89
|
+
} else if (matchesKey(data, Key.down) || data === "j") {
|
|
90
|
+
this.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1)
|
|
91
|
+
const maxStart = Math.max(0, this.selectedIndex - MAX_VISIBLE_ROWS + 1)
|
|
92
|
+
this.scrollOffset = Math.max(this.scrollOffset, maxStart)
|
|
93
|
+
} else if (matchesKey(data, Key.enter) || data === "\n") {
|
|
94
|
+
this.commit(this.selectedIndex)
|
|
95
|
+
} else if (matchesKey(data, Key.escape)) {
|
|
96
|
+
if (this.resolved) return
|
|
97
|
+
this.resolved = true
|
|
98
|
+
this.done({ selectedLabel: null })
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render(width: number): string[] {
|
|
103
|
+
const lines: string[] = []
|
|
104
|
+
const fg = this.theme.fg.bind(this.theme)
|
|
105
|
+
const innerWidth = Math.max(20, width - 2)
|
|
106
|
+
|
|
107
|
+
lines.push(truncateToWidth(fg("accent", "─".repeat(width)), width))
|
|
108
|
+
for (const line of renderQuestionLines(this.question, innerWidth, fg)) {
|
|
109
|
+
lines.push(` ${line}`)
|
|
110
|
+
}
|
|
111
|
+
lines.push("")
|
|
112
|
+
|
|
113
|
+
const visible = this.options.slice(
|
|
114
|
+
this.scrollOffset,
|
|
115
|
+
this.scrollOffset + MAX_VISIBLE_ROWS,
|
|
116
|
+
)
|
|
117
|
+
for (let i = 0; i < visible.length; i++) {
|
|
118
|
+
const optionIndex = this.scrollOffset + i
|
|
119
|
+
const isSelected = optionIndex === this.selectedIndex
|
|
120
|
+
const isCustom = this.customLabel !== null && visible[i] === this.customLabel
|
|
121
|
+
const prefix = isSelected ? fg("accent", "→ ") : " "
|
|
122
|
+
const marker = isCustom ? fg("muted", " ✎") : ""
|
|
123
|
+
lines.push(truncateToWidth(`${prefix}${fg(isSelected ? "accent" : "text", visible[i])}${marker}`, width))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.options.length > MAX_VISIBLE_ROWS) {
|
|
127
|
+
lines.push(
|
|
128
|
+
truncateToWidth(
|
|
129
|
+
fg("muted", ` (${this.selectedIndex + 1}/${this.options.length})`),
|
|
130
|
+
width,
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
lines.push("")
|
|
136
|
+
lines.push(
|
|
137
|
+
truncateToWidth(
|
|
138
|
+
fg("dim", " ↑↓ navigate · Enter select · Esc cancel"),
|
|
139
|
+
width,
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
lines.push(truncateToWidth(fg("accent", "─".repeat(width)), width))
|
|
143
|
+
return lines
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Build the factory passed to `ctx.ui.custom()`.
|
|
149
|
+
*
|
|
150
|
+
* Returns either a normalized display label (the user picked an option / custom
|
|
151
|
+
* sentinel) or `null` when the user cancelled.
|
|
152
|
+
*/
|
|
153
|
+
export function createAskUserQuestionCustomFactory(opts: AskUserQuestionCustomOptions) {
|
|
154
|
+
return (
|
|
155
|
+
_tui: unknown,
|
|
156
|
+
theme: { fg: (color: string, text: string) => string },
|
|
157
|
+
_keybindings: unknown,
|
|
158
|
+
done: (result: AskUserQuestionSelection) => void,
|
|
159
|
+
): AskUserQuestionSelector => {
|
|
160
|
+
return new AskUserQuestionSelector(opts, theme, done)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -14,7 +14,76 @@ export interface AskUserQuestionResult {
|
|
|
14
14
|
mode: "input" | "select" | "custom" | "cancelled"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/** Sentinel label used to offer a free-text custom answer. */
|
|
18
|
+
export const CUSTOM_SENTINEL = "Other"
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Maximum display width for a normalized option label. Long labels overflow the
|
|
22
|
+
* selector row in the built-in `ctx.ui.select()` renderer (see
|
|
23
|
+
* `docs/bug/ask-user-question-long-options-truncated.md`), so labels are kept
|
|
24
|
+
* to a single line and truncated to this width.
|
|
25
|
+
*/
|
|
26
|
+
export const MAX_OPTION_LABEL_WIDTH = 60
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build the single-line display label for one option.
|
|
30
|
+
*
|
|
31
|
+
* Rules:
|
|
32
|
+
* - Take only the first line (drop embedded `\n`).
|
|
33
|
+
* - Trim surrounding whitespace.
|
|
34
|
+
* - Truncate to {@link MAX_OPTION_LABEL_WIDTH} with an ellipsis when needed.
|
|
35
|
+
*/
|
|
36
|
+
export function toOptionDisplayLabel(option: string): string {
|
|
37
|
+
const firstLine = option.split("\n", 1)[0] ?? ""
|
|
38
|
+
const trimmed = firstLine.trim()
|
|
39
|
+
if (trimmed.length <= MAX_OPTION_LABEL_WIDTH) {
|
|
40
|
+
return trimmed
|
|
41
|
+
}
|
|
42
|
+
return trimmed.slice(0, MAX_OPTION_LABEL_WIDTH - 1) + "…"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build display labels for all options, disambiguating collisions with a
|
|
47
|
+
* numeric suffix so the selector never shows two identical rows.
|
|
48
|
+
*
|
|
49
|
+
* @returns A map from display label back to the original full option string.
|
|
50
|
+
* When collisions exist, labels become `<label> (#<n>)`.
|
|
51
|
+
*/
|
|
52
|
+
export function normalizeQuestionOptions(options: string[]): Map<string, string> {
|
|
53
|
+
const labelToOriginal = new Map<string, string>()
|
|
54
|
+
const labelCounts = new Map<string, number>()
|
|
55
|
+
|
|
56
|
+
for (const original of options) {
|
|
57
|
+
const baseLabel = toOptionDisplayLabel(original)
|
|
58
|
+
const count = (labelCounts.get(baseLabel) ?? 0) + 1
|
|
59
|
+
labelCounts.set(baseLabel, count)
|
|
60
|
+
|
|
61
|
+
// Start from the count-based suffix, then keep appending (#n) until we find
|
|
62
|
+
// a label that is truly unused. This handles pathological inputs where an
|
|
63
|
+
// option's own text already ends in `(#k)` and would otherwise collide.
|
|
64
|
+
let label = count === 1 ? baseLabel : `${baseLabel} (#${count})`
|
|
65
|
+
let dedup = count
|
|
66
|
+
while (labelToOriginal.has(label)) {
|
|
67
|
+
dedup += 1
|
|
68
|
+
label = `${baseLabel} (#${dedup})`
|
|
69
|
+
}
|
|
70
|
+
labelToOriginal.set(label, original)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return labelToOriginal
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Choose a display label for the custom-answer sentinel that never collides. */
|
|
77
|
+
export function resolveCustomSentinelLabel(labelToOriginal: Map<string, string>): string {
|
|
78
|
+
if (!labelToOriginal.has(CUSTOM_SENTINEL)) {
|
|
79
|
+
return CUSTOM_SENTINEL
|
|
80
|
+
}
|
|
81
|
+
let index = 2
|
|
82
|
+
while (labelToOriginal.has(`${CUSTOM_SENTINEL} (#${index})`)) {
|
|
83
|
+
index += 1
|
|
84
|
+
}
|
|
85
|
+
return `${CUSTOM_SENTINEL} (#${index})`
|
|
86
|
+
}
|
|
18
87
|
|
|
19
88
|
export function createAskUserQuestionTool() {
|
|
20
89
|
return {
|
|
@@ -33,21 +102,31 @@ export function createAskUserQuestionTool() {
|
|
|
33
102
|
}
|
|
34
103
|
|
|
35
104
|
const allowCustom = input.allowCustom ?? true
|
|
36
|
-
const
|
|
37
|
-
const
|
|
105
|
+
const labelToOriginal = normalizeQuestionOptions(options)
|
|
106
|
+
const customLabel = allowCustom
|
|
107
|
+
? resolveCustomSentinelLabel(labelToOriginal)
|
|
108
|
+
: null
|
|
109
|
+
const displayOptions = customLabel
|
|
110
|
+
? [...labelToOriginal.keys(), customLabel]
|
|
111
|
+
: [...labelToOriginal.keys()]
|
|
112
|
+
|
|
113
|
+
const selected = await ui.select(input.question, displayOptions)
|
|
38
114
|
|
|
39
115
|
if (selected === null) {
|
|
40
116
|
return { answer: null, mode: "cancelled" }
|
|
41
117
|
}
|
|
42
118
|
|
|
43
|
-
if (allowCustom && selected ===
|
|
119
|
+
if (allowCustom && customLabel && selected === customLabel) {
|
|
44
120
|
const customAnswer = await ui.input("Your answer")
|
|
45
121
|
return customAnswer === null
|
|
46
122
|
? { answer: null, mode: "cancelled" }
|
|
47
123
|
: { answer: customAnswer, mode: "custom" }
|
|
48
124
|
}
|
|
49
125
|
|
|
50
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
answer: labelToOriginal.get(selected) ?? selected,
|
|
128
|
+
mode: "select",
|
|
129
|
+
}
|
|
51
130
|
},
|
|
52
131
|
}
|
|
53
132
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leing2021/super-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Pi-native Compound Engineering package for iterative development workflows",
|
|
@@ -36,10 +36,12 @@
|
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
|
39
|
+
"@earendil-works/pi-tui": ">=0.74.0",
|
|
39
40
|
"typebox": ">=1.0.0"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@earendil-works/pi-coding-agent": "^0.76.0",
|
|
44
|
+
"@earendil-works/pi-tui": "^0.76.0",
|
|
43
45
|
"bun-types": "^1.3.12"
|
|
44
46
|
},
|
|
45
47
|
"pi": {
|
|
@@ -23,7 +23,7 @@ Re-examine the plan's assumptions:
|
|
|
23
23
|
- What is the actual user/business outcome? Is the plan the most direct path?
|
|
24
24
|
- What happens if we do nothing? Real pain or hypothetical?
|
|
25
25
|
|
|
26
|
-
Present findings as premises the user must agree with. Use `ask_user_question` for each.
|
|
26
|
+
Present findings as premises the user must agree with. Use `ask_user_question` for each — **one at a time, sequentially** (never two `ask_user_question` calls in the same assistant message; Pi selectors are serialized and parallel calls silently fail).
|
|
27
27
|
|
|
28
28
|
### 2. Dream State Mapping
|
|
29
29
|
|
package/skills/08-help/SKILL.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: 08-help
|
|
3
|
-
description: "Explain Phase 1 skills and their connections. Use when learning the workflow or deciding which skill applies."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Help
|
|
7
|
-
|
|
8
|
-
Use this skill when the user asks how to use the package, which workflow step comes next, or which Compound Engineering skill fits the current task.
|
|
9
|
-
|
|
10
|
-
## Core principle
|
|
11
|
-
|
|
12
|
-
**Explain the smallest useful next step** — do not force the full sequence.
|
|
13
|
-
|
|
14
|
-
## Trigger conditions
|
|
15
|
-
|
|
16
|
-
| User asks | Use this skill |
|
|
17
|
-
|---|---|
|
|
18
|
-
| How to use the package | Help with skill selection |
|
|
19
|
-
| Which step comes next | Recommend via workflow state |
|
|
20
|
-
| Which skill fits | Match task to skill |
|
|
21
|
-
|
|
22
|
-
## Skill mapping
|
|
23
|
-
|
|
24
|
-
| Skill | When to use |
|
|
25
|
-
|---|---|
|
|
26
|
-
| `01-brainstorm` | Ambiguous request, new idea, requirements discovery |
|
|
27
|
-
| `02-plan` | Requirements clear, turn into implementation units |
|
|
28
|
-
| `03-work` | Plan ready, controlled execution |
|
|
29
|
-
| `04-review` | After code changes, structured findings |
|
|
30
|
-
| `05-learn` | After solving, capture reusable learning |
|
|
31
|
-
| `06-next` | Check status or get next step recommendation |
|
|
32
|
-
| `07-worktree` | Isolated feature development |
|
|
33
|
-
|
|
34
|
-
## Workflow sequence
|
|
35
|
-
|
|
36
|
-
See `references/workflow-sequence.md` for detailed flow, mode variants, and output formats.
|
|
37
|
-
|
|
38
|
-
**Quick reference:**
|
|
39
|
-
1. `01-brainstorm` → clarify problem
|
|
40
|
-
2. `02-plan` → break into units (optional CEO/Strict review)
|
|
41
|
-
3. `03-work` → execute
|
|
42
|
-
4. `04-review` → inspect (optional Browser QA)
|
|
43
|
-
5. `05-learn` → capture learnings
|
|
44
|
-
6. `06-next` → check status anytime
|
|
45
|
-
|
|
46
|
-
Before finishing this skill, apply the completion checklist in [shared pipeline instructions](../references/pipeline-config.md).
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Workflow Sequence
|
|
2
|
-
|
|
3
|
-
## Full CE pipeline
|
|
4
|
-
|
|
5
|
-
1. `01-brainstorm` → clarify the problem
|
|
6
|
-
2. `02-plan` → break into units (optionally + CEO Review / Strict Review)
|
|
7
|
-
3. `03-work` → execute the plan
|
|
8
|
-
4. `04-review` → inspect changes (optionally + Browser QA / QA + Regression Tests)
|
|
9
|
-
5. `05-learn` → save key learnings
|
|
10
|
-
|
|
11
|
-
Use `06-next` at any point to see where you are and what to do next.
|
|
12
|
-
Use `07-worktree` for isolated feature development on a separate branch.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## Phase 1 skill guide
|
|
17
|
-
|
|
18
|
-
### 01-brainstorm
|
|
19
|
-
**Use when:** request is ambiguous, needs requirements discovery, or user has a new idea.
|
|
20
|
-
|
|
21
|
-
**Three modes:**
|
|
22
|
-
- **CE Brainstorm** — standard requirements discovery for feature additions
|
|
23
|
-
- **Startup Diagnostic** — YC-style forcing questions for founders/intrapreneurs
|
|
24
|
-
- **Builder Mode** — generative brainstorming for side projects/hackathons/learning
|
|
25
|
-
|
|
26
|
-
**Key outputs:**
|
|
27
|
-
- Requirements document in `docs/brainstorms/`
|
|
28
|
-
- Explicit user approval before handoff
|
|
29
|
-
|
|
30
|
-
### 02-plan
|
|
31
|
-
**Use when:** requirements are clear enough to turn into implementation units.
|
|
32
|
-
|
|
33
|
-
**Optional reviews after planning:**
|
|
34
|
-
- **CEO Review** — challenge premises, check dream-state alignment
|
|
35
|
-
- **Strict Review** — CEO Review + error maps, failure modes, test diagrams
|
|
36
|
-
|
|
37
|
-
**Key outputs:**
|
|
38
|
-
- Plan artifact in `docs/plans/`
|
|
39
|
-
- Implementation units following TDD RED→GREEN→REFACTOR
|
|
40
|
-
|
|
41
|
-
### 03-work
|
|
42
|
-
**Use when:** plan is ready or task is tightly scoped.
|
|
43
|
-
|
|
44
|
-
**Execution mode:**
|
|
45
|
-
- Inline execution for all units in the current session
|
|
46
|
-
|
|
47
|
-
**Advanced:** For external child agent delegation, install `pi-subagents` separately.
|
|
48
|
-
|
|
49
|
-
**Key outputs:**
|
|
50
|
-
- Completion report with verification evidence
|
|
51
|
-
- Checkpoint state for resume
|
|
52
|
-
|
|
53
|
-
### 04-review
|
|
54
|
-
**Use when:** after code changes to produce structured findings.
|
|
55
|
-
|
|
56
|
-
**Optional extensions:**
|
|
57
|
-
- **Browser QA** — find visual/functional bugs with agent-browser
|
|
58
|
-
- **Browser QA + Regression Tests** — also generate automated coverage
|
|
59
|
-
|
|
60
|
-
**Key outputs:**
|
|
61
|
-
- Structured findings per `references/findings-schema.md`
|
|
62
|
-
- Optional QA report with fix commits
|
|
63
|
-
|
|
64
|
-
### 05-learn
|
|
65
|
-
**Use when:** after solving a problem to capture reusable learning.
|
|
66
|
-
|
|
67
|
-
**Key outputs:**
|
|
68
|
-
- Solution artifact in `docs/solutions/` or global `~/.pi/agent/docs/solutions/`
|
|
69
|
-
- Handoff-lite for workflow closure
|
|
70
|
-
|
|
71
|
-
### 06-next
|
|
72
|
-
**Use when:** user wants to know what to run next, or wants full status.
|
|
73
|
-
|
|
74
|
-
**Two modes:**
|
|
75
|
-
- **Default** — recommend one next step with reason
|
|
76
|
-
- **Verbose** (`--verbose` or "show status") — full artifact details + recent history
|
|
77
|
-
|
|
78
|
-
### 07-worktree
|
|
79
|
-
**Use when:** need isolated git worktree development.
|
|
80
|
-
|
|
81
|
-
**Use for:** large, risky, or parallel feature work.
|
|
82
|
-
|
|
83
|
-
**Flow:** create → execute via 03-work → merge → cleanup
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
## Skill routing decision tree
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
Is the request clear and specific?
|
|
91
|
-
├── No → 01-brainstorm (clarify requirements)
|
|
92
|
-
└── Yes → Is there a plan?
|
|
93
|
-
├── No → 02-plan (create implementation units)
|
|
94
|
-
└── Yes → Is work complete?
|
|
95
|
-
├── No → 03-work (execute)
|
|
96
|
-
└── Yes → Has it been reviewed?
|
|
97
|
-
├── No → 04-review (structured findings)
|
|
98
|
-
└── Yes → Is there a learning worth capturing?
|
|
99
|
-
├── Yes → 05-learn
|
|
100
|
-
└── No → Check status with 06-next
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Output format for help responses
|
|
106
|
-
|
|
107
|
-
When explaining skills to users, keep it concise:
|
|
108
|
-
|
|
109
|
-
**Short form** (one skill recommendation):
|
|
110
|
-
```
|
|
111
|
-
Next step: `/skill:02-plan`
|
|
112
|
-
|
|
113
|
-
Reason: You have a clear idea. Let's turn it into implementation units.
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
**Medium form** (context + recommendation):
|
|
117
|
-
```
|
|
118
|
-
Status: Requirements captured in docs/brainstorms/2024-01-idea.md
|
|
119
|
-
|
|
120
|
-
Next step: `/skill:02-plan`
|
|
121
|
-
|
|
122
|
-
Why: Requirements exist but no implementation plan yet.
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
**Verbose form** (full sequence):
|
|
126
|
-
```
|
|
127
|
-
Workflow sequence:
|
|
128
|
-
1. ✅ Brainstorm: requirements captured
|
|
129
|
-
2. ⏳ Plan: ready to create
|
|
130
|
-
3. ⏸ Work: waiting for plan
|
|
131
|
-
4. ⏸ Review: waiting for work
|
|
132
|
-
5. ⏸ Learn: waiting for review
|
|
133
|
-
|
|
134
|
-
Recommended: `/skill:02-plan` to turn requirements into implementation units.
|
|
135
|
-
```
|