@leing2021/super-pi 0.19.1 → 0.19.2

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 CHANGED
@@ -180,7 +180,7 @@ Single `npm install` output filtered once pays for the entire overhead. Full eva
180
180
 
181
181
  ## Code Scale
182
182
 
183
- ~2500 lines of TypeScript implementing 15 tools, 22 Markdown reference files + 78 rule files driving 10 skills, 162 tests covering all tool logic.
183
+ ~2800 lines of TypeScript implementing 16 tools, 22 Markdown reference files + 78 rule files driving 10 skills, 170 tests covering all tool logic.
184
184
 
185
185
  Not a heavy framework. Each tool has a single responsibility, each skill works independently, and together they form a complete workflow.
186
186
 
@@ -383,6 +383,18 @@ Not a fork. Not a wrapper. Methodologies extracted and rebuilt with Pi's native
383
383
 
384
384
  ## Changelog
385
385
 
386
+ ### 0.19.2 — Evidence-first handoff-lite + docs tracking rule
387
+ - Added `context_handoff` with evidence-first default handoff-lite generation when markdown is omitted.
388
+ - Standardized the shared handoff-lite template across 01-05 workflow handoffs via `skills/references/pipeline-config.md`.
389
+ - Added tests protecting default handoff generation and the shared handoff docs contract.
390
+ - Updated docs tracking so Git only uploads `docs/token-cost-evaluation.md` while other `docs/` artifacts stay local.
391
+
392
+ ### 0.19.1 — Pipeline config + typecheck baseline fix
393
+ - Added shared pipeline config (`skills/references/pipeline-config.md`) for stage model routing via `.pi/settings.json`.
394
+ - Added gated auto-continue rules (`pipeline.autoContinue`) so automation does not skip required approval/review-choice steps.
395
+ - Added explicit README usage examples for `modelStrategy` and `pipeline.autoContinue`.
396
+ - Fixed TypeScript baseline issues so `bunx tsc --noEmit` passes.
397
+
386
398
  ### 0.19.0 — 0.69.0 alignment + learn rename
387
399
  - TypeBox migration: `@sinclair/typebox` → `typebox` (zero old-path imports)
388
400
  - Peer/dev dependency upgrade: pi-coding-agent `0.67.6` → `0.69.0`
@@ -0,0 +1,357 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises"
2
+ import { existsSync } from "node:fs"
3
+ import path from "node:path"
4
+ import { normalizeSlug } from "../utils/name-utils"
5
+
6
+ export type ContextHealth = "good" | "watch" | "heavy" | "critical"
7
+
8
+ export interface ContextHandoffInput {
9
+ operation: "save" | "load" | "latest" | "status"
10
+ repoRoot: string
11
+ currentStage?: string
12
+ nextStage?: string
13
+ contextHealth?: ContextHealth
14
+ activeFiles?: string[]
15
+ blocker?: string
16
+ verification?: string
17
+ artifacts?: Record<string, string | undefined>
18
+ handoffMarkdown?: string
19
+ handoffPath?: string
20
+ }
21
+
22
+ export interface ContextStateEntry {
23
+ currentStage: string
24
+ nextStage?: string
25
+ contextHealth: ContextHealth
26
+ latestHandoffPath?: string
27
+ latestDatedHandoffPath?: string
28
+ activeFiles: string[]
29
+ blocker?: string
30
+ verification?: string
31
+ artifacts: Record<string, string | undefined>
32
+ recommendNewSession: boolean
33
+ updatedAt: string
34
+ }
35
+
36
+ export interface ContextHandoffResult {
37
+ operation: string
38
+ found?: boolean
39
+ path?: string
40
+ latestPath?: string
41
+ currentStage?: string
42
+ nextStage?: string
43
+ contextHealth?: ContextHealth
44
+ activeFiles?: string[]
45
+ blocker?: string
46
+ verification?: string
47
+ artifacts?: Record<string, string | undefined>
48
+ recommendNewSession?: boolean
49
+ handoffMarkdown?: string
50
+ updatedAt?: string
51
+ }
52
+
53
+ function ceDir(repoRoot: string): string {
54
+ return path.join(repoRoot, ".context", "compound-engineering")
55
+ }
56
+
57
+ function handoffDir(repoRoot: string): string {
58
+ return path.join(ceDir(repoRoot), "handoffs")
59
+ }
60
+
61
+ function stateFilePath(repoRoot: string): string {
62
+ return path.join(ceDir(repoRoot), "context-state.json")
63
+ }
64
+
65
+ function latestHandoffPath(repoRoot: string): string {
66
+ return path.join(handoffDir(repoRoot), "latest.md")
67
+ }
68
+
69
+ function toRepoRelative(repoRoot: string, filePath: string): string {
70
+ return path.relative(repoRoot, filePath)
71
+ }
72
+
73
+ function resolveRepoPath(repoRoot: string, filePath: string): string {
74
+ return path.isAbsolute(filePath) ? filePath : path.join(repoRoot, filePath)
75
+ }
76
+
77
+ function stageSlug(value?: string): string {
78
+ if (!value || value.trim().length === 0) return "unknown"
79
+ return normalizeSlug(value)
80
+ }
81
+
82
+ function buildDatedHandoffPath(repoRoot: string, currentStage?: string, nextStage?: string): string {
83
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
84
+ const fileName = `${timestamp}-${stageSlug(currentStage)}-to-${stageSlug(nextStage)}.md`
85
+ return path.join(handoffDir(repoRoot), fileName)
86
+ }
87
+
88
+ function computeRecommendNewSession(currentStage?: string, nextStage?: string, contextHealth: ContextHealth = "watch"): boolean {
89
+ const isCrossPhase = Boolean(currentStage && nextStage && currentStage !== nextStage)
90
+ const isHeavy = contextHealth === "heavy" || contextHealth === "critical"
91
+ return isCrossPhase && isHeavy
92
+ }
93
+
94
+ function formatBullets(items: string[]): string {
95
+ if (items.length === 0) return "- N/A"
96
+ return items.map(item => `- ${item}`).join("\n")
97
+ }
98
+
99
+ function formatArtifacts(artifacts: Record<string, string | undefined>): string {
100
+ const lines = Object.entries(artifacts)
101
+ .filter(([, value]) => Boolean(value && value.trim().length > 0))
102
+ .map(([key, value]) => `- ${key}: ${value}`)
103
+
104
+ if (lines.length === 0) return "- N/A"
105
+ return lines.join("\n")
106
+ }
107
+
108
+ function buildDefaultHandoffMarkdown(input: {
109
+ currentStage: string
110
+ nextStage?: string
111
+ activeFiles: string[]
112
+ artifacts: Record<string, string | undefined>
113
+ blocker?: string
114
+ verification?: string
115
+ }): string {
116
+ const currentTask = input.nextStage
117
+ ? `Continue from ${input.currentStage} to ${input.nextStage}.`
118
+ : `Continue ${input.currentStage}.`
119
+
120
+ const hotContext = formatBullets(input.activeFiles.slice(0, 5))
121
+
122
+ const verifiedFacts = formatBullets([
123
+ `Current stage: ${input.currentStage}`,
124
+ `Next stage: ${input.nextStage ?? "N/A"}`,
125
+ ])
126
+
127
+ const activeFiles = formatBullets(input.activeFiles.slice(0, 5))
128
+ const artifacts = formatArtifacts(input.artifacts)
129
+ const blocker = input.blocker ?? "N/A"
130
+ const verification = input.verification ?? "Not run"
131
+ const nextMinimalStep = input.nextStage ? `/skill:${input.nextStage}` : "N/A"
132
+
133
+ return [
134
+ "## Current Task",
135
+ currentTask,
136
+ "",
137
+ "## Hot Context",
138
+ hotContext,
139
+ "",
140
+ "## Verified Facts",
141
+ verifiedFacts,
142
+ "",
143
+ "## Active Files",
144
+ activeFiles,
145
+ "",
146
+ "## Artifacts",
147
+ artifacts,
148
+ "",
149
+ "## Current Blocker",
150
+ `- ${blocker}`,
151
+ "",
152
+ "## Verification",
153
+ `- ${verification}`,
154
+ "",
155
+ "## Do Not Repeat",
156
+ "- Do not reload full history unless the handoff lacks required evidence.",
157
+ "",
158
+ "## Next Minimal Step",
159
+ `- ${nextMinimalStep}`,
160
+ "",
161
+ ].join("\n")
162
+ }
163
+
164
+ async function readState(repoRoot: string): Promise<ContextStateEntry | null> {
165
+ const filePath = stateFilePath(repoRoot)
166
+ if (!existsSync(filePath)) return null
167
+
168
+ try {
169
+ const content = await readFile(filePath, "utf8")
170
+ return JSON.parse(content) as ContextStateEntry
171
+ } catch {
172
+ return null
173
+ }
174
+ }
175
+
176
+ async function writeState(repoRoot: string, state: ContextStateEntry): Promise<void> {
177
+ const filePath = stateFilePath(repoRoot)
178
+ await mkdir(path.dirname(filePath), { recursive: true })
179
+ await writeFile(filePath, JSON.stringify(state, null, 2), "utf8")
180
+ }
181
+
182
+ export function createContextHandoffTool() {
183
+ return {
184
+ name: "context_handoff",
185
+ async execute(input: ContextHandoffInput): Promise<ContextHandoffResult> {
186
+ switch (input.operation) {
187
+ case "save":
188
+ return save(input)
189
+ case "load":
190
+ return load(input)
191
+ case "latest":
192
+ return latest(input)
193
+ case "status":
194
+ return status(input)
195
+ default:
196
+ throw new Error(`Unknown operation: ${input.operation}`)
197
+ }
198
+ },
199
+ }
200
+ }
201
+
202
+ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
203
+ const currentStage = input.currentStage ?? "unknown"
204
+ const nextStage = input.nextStage
205
+ const contextHealth = input.contextHealth ?? "watch"
206
+ const activeFiles = input.activeFiles ?? []
207
+ const blocker = input.blocker
208
+ const verification = input.verification
209
+ const artifacts = input.artifacts ?? {}
210
+ const handoffMarkdown = input.handoffMarkdown?.trim().length
211
+ ? input.handoffMarkdown
212
+ : buildDefaultHandoffMarkdown({
213
+ currentStage,
214
+ nextStage,
215
+ activeFiles,
216
+ artifacts,
217
+ blocker,
218
+ verification,
219
+ })
220
+
221
+ const recommendNewSession = computeRecommendNewSession(currentStage, nextStage, contextHealth)
222
+ const latestPath = latestHandoffPath(input.repoRoot)
223
+ const datedPath = buildDatedHandoffPath(input.repoRoot, currentStage, nextStage)
224
+ const relativeLatestPath = toRepoRelative(input.repoRoot, latestPath)
225
+ const relativeDatedPath = toRepoRelative(input.repoRoot, datedPath)
226
+
227
+ await mkdir(path.dirname(latestPath), { recursive: true })
228
+ await writeFile(latestPath, handoffMarkdown, "utf8")
229
+ await writeFile(datedPath, handoffMarkdown, "utf8")
230
+
231
+ const state: ContextStateEntry = {
232
+ currentStage,
233
+ nextStage,
234
+ contextHealth,
235
+ latestHandoffPath: relativeLatestPath,
236
+ latestDatedHandoffPath: relativeDatedPath,
237
+ activeFiles,
238
+ blocker,
239
+ verification,
240
+ artifacts,
241
+ recommendNewSession,
242
+ updatedAt: new Date().toISOString(),
243
+ }
244
+
245
+ await writeState(input.repoRoot, state)
246
+
247
+ return {
248
+ operation: "save",
249
+ found: true,
250
+ path: relativeDatedPath,
251
+ latestPath: relativeLatestPath,
252
+ currentStage,
253
+ nextStage,
254
+ contextHealth,
255
+ activeFiles,
256
+ blocker,
257
+ verification,
258
+ artifacts,
259
+ recommendNewSession,
260
+ updatedAt: state.updatedAt,
261
+ }
262
+ }
263
+
264
+ async function load(input: ContextHandoffInput): Promise<ContextHandoffResult> {
265
+ const state = await readState(input.repoRoot)
266
+ if (!state) {
267
+ return {
268
+ operation: "load",
269
+ found: false,
270
+ contextHealth: "watch",
271
+ recommendNewSession: false,
272
+ }
273
+ }
274
+
275
+ const targetPath = input.handoffPath ?? state.latestHandoffPath ?? latestHandoffPath(input.repoRoot)
276
+ const absoluteTargetPath = resolveRepoPath(input.repoRoot, targetPath)
277
+ let markdown = ""
278
+ if (existsSync(absoluteTargetPath)) {
279
+ markdown = await readFile(absoluteTargetPath, "utf8")
280
+ }
281
+
282
+ return {
283
+ operation: "load",
284
+ found: true,
285
+ path: targetPath,
286
+ latestPath: state.latestHandoffPath,
287
+ currentStage: state.currentStage,
288
+ nextStage: state.nextStage,
289
+ contextHealth: state.contextHealth,
290
+ activeFiles: state.activeFiles,
291
+ blocker: state.blocker,
292
+ verification: state.verification,
293
+ artifacts: state.artifacts,
294
+ recommendNewSession: state.recommendNewSession,
295
+ handoffMarkdown: markdown,
296
+ updatedAt: state.updatedAt,
297
+ }
298
+ }
299
+
300
+ async function latest(input: ContextHandoffInput): Promise<ContextHandoffResult> {
301
+ const state = await readState(input.repoRoot)
302
+ if (!state || !state.latestHandoffPath) {
303
+ return {
304
+ operation: "latest",
305
+ found: false,
306
+ contextHealth: "watch",
307
+ recommendNewSession: false,
308
+ }
309
+ }
310
+
311
+ return {
312
+ operation: "latest",
313
+ found: true,
314
+ path: state.latestDatedHandoffPath,
315
+ latestPath: state.latestHandoffPath,
316
+ currentStage: state.currentStage,
317
+ nextStage: state.nextStage,
318
+ contextHealth: state.contextHealth,
319
+ activeFiles: state.activeFiles,
320
+ blocker: state.blocker,
321
+ verification: state.verification,
322
+ artifacts: state.artifacts,
323
+ recommendNewSession: state.recommendNewSession,
324
+ updatedAt: state.updatedAt,
325
+ }
326
+ }
327
+
328
+ async function status(input: ContextHandoffInput): Promise<ContextHandoffResult> {
329
+ const state = await readState(input.repoRoot)
330
+
331
+ if (!state) {
332
+ return {
333
+ operation: "status",
334
+ found: false,
335
+ contextHealth: "watch",
336
+ recommendNewSession: false,
337
+ activeFiles: [],
338
+ artifacts: {},
339
+ }
340
+ }
341
+
342
+ return {
343
+ operation: "status",
344
+ found: true,
345
+ path: state.latestDatedHandoffPath,
346
+ latestPath: state.latestHandoffPath,
347
+ currentStage: state.currentStage,
348
+ nextStage: state.nextStage,
349
+ contextHealth: state.contextHealth,
350
+ activeFiles: state.activeFiles,
351
+ blocker: state.blocker,
352
+ verification: state.verification,
353
+ artifacts: state.artifacts,
354
+ recommendNewSession: state.recommendNewSession,
355
+ updatedAt: state.updatedAt,
356
+ }
357
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leing2021/super-pi",
3
- "version": "0.19.1",
3
+ "version": "0.19.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Pi-native Compound Engineering package for iterative development workflows",
@@ -6,3 +6,6 @@ When the requirements artifact is ready:
6
6
  2. Summarize the recommended direction in 2-3 bullets.
7
7
  3. Recommend `02-plan` as the next step.
8
8
  4. If key ambiguity remains, say so before handing off.
9
+ 5. Provide `🧠 Context Status` (health, handoff path, active files, new-session recommendation).
10
+ 6. Save/mention handoff-lite path under `.context/compound-engineering/handoffs/` using the shared `Handoff-lite template` in `skills/references/pipeline-config.md`.
11
+ 7. Recommend new session only when cross-phase + health is heavy/critical, and include a copyable prompt.
@@ -6,3 +6,6 @@ When the plan is ready:
6
6
  2. Summarize the main implementation units.
7
7
  3. Recommend `03-work` as the next step.
8
8
  4. Call out any remaining assumptions or open risks.
9
+ 5. Provide `🧠 Context Status` (health, handoff path, active files, new-session recommendation).
10
+ 6. Save/mention handoff-lite path under `.context/compound-engineering/handoffs/` using the shared `Handoff-lite template` in `skills/references/pipeline-config.md`.
11
+ 7. Recommend new session only when cross-phase + health is heavy/critical, and include a copyable prompt.
@@ -6,3 +6,7 @@ When execution reaches a meaningful checkpoint:
6
6
  2. Report the latest verification results.
7
7
  3. Recommend `04-review` as the next step.
8
8
  4. Mention any remaining implementation risk.
9
+ 5. Include checkpoint fields: `activeFiles`, `currentUnit`, `blocker`, `verification`, `contextTiers`, `handoffPath`.
10
+ 6. Provide `🧠 Context Status` (health, handoff path, active files, new-session recommendation).
11
+ 7. Save/mention handoff-lite path under `.context/compound-engineering/handoffs/` using the shared `Handoff-lite template` in `skills/references/pipeline-config.md`.
12
+ 8. Recommend new session only when cross-phase + health is heavy/critical, and include a copyable prompt.
@@ -8,6 +8,8 @@ When the review is complete:
8
8
  4. After autofix, report what was changed and whether re-review confirms the fix.
9
9
  5. Recommend `05-learn` when the review surfaced a reusable learning or newly solved problem.
10
10
  6. Mention any relevant plan or solution artifacts referenced during review.
11
+ 7. Provide `🧠 Context Status` (health, handoff path, active files, new-session recommendation).
12
+ 8. Save/mention handoff-lite path under `.context/compound-engineering/handoffs/` using the shared `Handoff-lite template` in `skills/references/pipeline-config.md`.
11
13
 
12
14
  ## Autofix loop
13
15
 
@@ -32,5 +32,7 @@ See [shared pipeline instructions](../references/pipeline-config.md) for model r
32
32
  5. Choose the correct category using `references/category-map.md`.
33
33
  6. Write or update the solution artifact under `docs/solutions/<category>/`.
34
34
  7. Mention how future `02-plan` and `04-review` runs should benefit from the new learning.
35
+ 8. Include `🧠 Context Status` (health, handoff path, active files, new-session recommendation) for workflow closure.
36
+ 9. Save/mention handoff-lite path under `.context/compound-engineering/handoffs/` using the shared `Handoff-lite template` in `skills/references/pipeline-config.md`.
35
37
 
36
38
  Before finishing this skill, apply the completion checklist in [shared pipeline instructions](../references/pipeline-config.md).
@@ -18,9 +18,9 @@ Run this checklist before normal skill workflow:
18
18
  5. If `targetModel` exists and differs from the current model, run `/model <targetModel>`.
19
19
  6. If switching fails, continue with current model and mention the failure once.
20
20
 
21
- ## End of skill: status + optional auto-continue
21
+ ## End of skill: status + context + optional auto-continue
22
22
 
23
- Before final completion, always output this block (replace placeholders with real values, never output angle-bracket placeholders literally):
23
+ Before final completion, always output these blocks (replace placeholders with real values, never output angle-bracket placeholders literally):
24
24
 
25
25
  ```
26
26
  ---
@@ -29,6 +29,13 @@ Before final completion, always output this block (replace placeholders with rea
29
29
  - Output: <main artifact path or N/A>
30
30
  - Next: <next skill command or Completed>
31
31
  ---
32
+
33
+ 🧠 Context Status
34
+ - Health: good | watch | heavy | critical
35
+ - Handoff: <path or N/A>
36
+ - Active: <1-5 active files or N/A>
37
+ - New session: recommended | not needed
38
+ ---
32
39
  ```
33
40
 
34
41
  Next step mapping:
@@ -47,3 +54,84 @@ Then read `.pi/settings.json` → `pipeline.autoContinue`:
47
54
  - `04-review`: optional QA choice is resolved (A/B/C flow completed) and user confirmed to proceed.
48
55
  - Any unclear/ambiguous state: do not auto-continue; stop and ask.
49
56
  - When gates are satisfied, automatically trigger the mapped next skill command.
57
+
58
+ ### Handoff-lite template
59
+
60
+ When a stage produces or updates handoff-lite, use this evidence-first structure and keep it concise (target <= 1500 tokens):
61
+
62
+ ```md
63
+ ## Current Task
64
+
65
+ ## Hot Context
66
+ - 1-5 must-know facts for the next step
67
+
68
+ ## Verified Facts
69
+ - already validated facts (do not re-prove)
70
+
71
+ ## Active Files
72
+ - 1-5 file paths only
73
+
74
+ ## Artifacts
75
+ - requirements: <path or N/A>
76
+ - plan: <path or N/A>
77
+ - checkpoint: <path or N/A>
78
+ - proof: <path or N/A>
79
+
80
+ ## Current Blocker
81
+ - <blocker or N/A>
82
+
83
+ ## Verification
84
+ - <latest command + result or Not run>
85
+
86
+ ## Do Not Repeat
87
+ - what should not be re-read/re-run unless needed
88
+
89
+ ## Next Minimal Step
90
+ - exact next command/action
91
+ ```
92
+
93
+ Rules:
94
+ - Use `N/A` instead of inventing facts.
95
+ - Keep broad history in artifact paths, not expanded narrative.
96
+ - If `context_handoff` is unavailable, manually write this shape to `.context/compound-engineering/handoffs/latest.md` and mention the path.
97
+
98
+ ### New-session recommendation rule
99
+
100
+ Recommend a new session only when both are true:
101
+
102
+ 1. Phase is changing (`Current` != next stage)
103
+ 2. Context health is `heavy` or `critical`
104
+
105
+ When recommending a new session, include a directly copyable prompt:
106
+
107
+ ```md
108
+ ## 建议新开 Session
109
+
110
+ 原因:当前 `<current stage>` 已完成,下一步将进入 `<next stage>`。当前窗口已经包含较多已完成阶段上下文,继续执行会降低 Token ROI,并增加旧上下文干扰后续判断的风险。
111
+
112
+ 建议:新开一个窗口,把下面这段 Prompt 复制进去即可继续。
113
+
114
+ ```text
115
+ 继续这个 Super Pi workflow,不要重新开始。
116
+
117
+ Repo: <repo path>
118
+
119
+ 请先读取:
120
+ - <latest plan/requirements artifact>
121
+ - <latest handoff-lite path>
122
+ - <latest checkpoint path or summary>
123
+
124
+ 然后继续:
125
+ - 执行 <next skill command>
126
+
127
+ 上下文策略:
128
+ - hot: 仅保留当前执行必须文件(1-5)
129
+ - warm: 通过 artifact path 按需回读
130
+ - cold: 不进入当前窗口,除非明确需要
131
+
132
+ 核心原则:
133
+ - 不重复已完成阶段
134
+ - 优先验证当前阶段输出
135
+ - 控制 token,保持高 ROI
136
+ ```
137
+ ```