@leing2021/super-pi 0.23.2 → 0.23.4
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/extensions/ce-core/index.ts +11 -5
- package/extensions/ce-core/tools/compaction-optimizer.ts +1 -9
- package/extensions/ce-core/tools/context-handoff.ts +306 -1
- package/package.json +1 -1
- package/skills/02-plan/SKILL.md +8 -7
- package/skills/03-work/SKILL.md +13 -11
- package/skills/04-review/SKILL.md +9 -8
- package/skills/06-next/SKILL.md +5 -3
- package/skills/06-next/references/recommendation-logic.md +46 -1
- package/skills/references/pipeline-config.md +28 -1
|
@@ -310,6 +310,7 @@ const contextHandoffParams = Type.Object({
|
|
|
310
310
|
Type.Literal("load"),
|
|
311
311
|
Type.Literal("latest"),
|
|
312
312
|
Type.Literal("status"),
|
|
313
|
+
Type.Literal("validate"),
|
|
313
314
|
], { description: "Handoff operation" }),
|
|
314
315
|
repoRoot: Type.String({ description: "Repository root" }),
|
|
315
316
|
currentStage: Type.Optional(Type.String({ description: "Current pipeline stage (e.g. 02-plan)" })),
|
|
@@ -331,6 +332,7 @@ const contextHandoffParams = Type.Object({
|
|
|
331
332
|
openDecisions: Type.Optional(Type.Array(Type.String(), { description: "Pending decisions that affect next steps" })),
|
|
332
333
|
recentlyAccessedFiles: Type.Optional(Type.Array(Type.String(), { description: "Files recently read or edited (defaults to activeFiles)" })),
|
|
333
334
|
compressionRisk: Type.Optional(Type.Array(Type.String(), { description: "Context compression risks to watch for" })),
|
|
335
|
+
activeRules: Type.Optional(Type.Array(Type.String(), { description: "1-5 must-know rules for continuation (TDD gates, constraints, do-not-repeat)" })),
|
|
334
336
|
})
|
|
335
337
|
|
|
336
338
|
const patternExtractorParams = Type.Object({
|
|
@@ -393,18 +395,21 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
393
395
|
if (thinkingStrategy) {
|
|
394
396
|
const targetThinking = thinkingStrategy[stageKey] ?? thinkingStrategy.default
|
|
395
397
|
if (targetThinking) {
|
|
396
|
-
const levelMap: Record<string, "
|
|
398
|
+
const levelMap: Record<string, ReturnType<ExtensionAPI["getThinkingLevel"]>> = {
|
|
399
|
+
off: "off",
|
|
400
|
+
minimal: "minimal",
|
|
397
401
|
low: "low",
|
|
398
402
|
medium: "medium",
|
|
399
403
|
high: "high",
|
|
404
|
+
xhigh: "xhigh",
|
|
400
405
|
"0": "low",
|
|
401
406
|
"1": "medium",
|
|
402
407
|
"2": "high",
|
|
403
408
|
}
|
|
404
409
|
const normalized = levelMap[targetThinking.toLowerCase()] ?? "medium"
|
|
405
|
-
const currentLevel =
|
|
410
|
+
const currentLevel = pi.getThinkingLevel()
|
|
406
411
|
if (currentLevel !== normalized) {
|
|
407
|
-
|
|
412
|
+
pi.setThinkingLevel(normalized)
|
|
408
413
|
if (ctx.hasUI) {
|
|
409
414
|
ctx.ui.notify(`Switched thinking level for ${stageKey}: ${normalized}`, "info")
|
|
410
415
|
}
|
|
@@ -748,7 +753,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
748
753
|
pi.registerTool({
|
|
749
754
|
name: contextHandoff.name,
|
|
750
755
|
label: "Context Handoff",
|
|
751
|
-
description: "Manage cross-stage context handoffs with evidence-first templates. Supports save (write handoff + state), load (read handoff + state), latest (read latest dated handoff),
|
|
756
|
+
description: "Manage cross-stage context handoffs with evidence-first templates. Supports save (write handoff + state), load (read handoff + state), latest (read latest dated handoff), status (read current state), and validate (check continuation readiness with deterministic probes).",
|
|
752
757
|
parameters: contextHandoffParams,
|
|
753
758
|
async execute(_toolCallId, params) {
|
|
754
759
|
const result = await contextHandoff.execute({
|
|
@@ -768,6 +773,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
768
773
|
openDecisions: params.openDecisions,
|
|
769
774
|
recentlyAccessedFiles: params.recentlyAccessedFiles,
|
|
770
775
|
compressionRisk: params.compressionRisk,
|
|
776
|
+
activeRules: params.activeRules,
|
|
771
777
|
})
|
|
772
778
|
|
|
773
779
|
return {
|
|
@@ -887,4 +893,4 @@ export {
|
|
|
887
893
|
export { normalizeSlug } from "./utils/name-utils"
|
|
888
894
|
export { filterBashOutput } from "./tools/bash-output-filter"
|
|
889
895
|
export { filterReadOutput } from "./tools/read-output-filter"
|
|
890
|
-
export { COMPACTION_FOCUS_INSTRUCTIONS
|
|
896
|
+
export { COMPACTION_FOCUS_INSTRUCTIONS } from "./tools/compaction-optimizer"
|
|
@@ -27,12 +27,4 @@ export const COMPACTION_FOCUS_INSTRUCTIONS = `Additional focus for this summary:
|
|
|
27
27
|
6. If any tests were run, summarize results by: file, pass/fail count, and specific failure messages
|
|
28
28
|
7. Note any blocked items and their exact error state`
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
* Additional instructions for turn-prefix summaries (split turns).
|
|
32
|
-
* These are more concise since the turn suffix is retained.
|
|
33
|
-
*/
|
|
34
|
-
export const TURN_PREFIX_FOCUS_INSTRUCTIONS = `Focus the turn-prefix summary on:
|
|
35
|
-
- What the user originally asked for
|
|
36
|
-
- Key decisions made in the prefix
|
|
37
|
-
- Exact file paths and identifiers needed to understand the retained suffix
|
|
38
|
-
Skip reasoning details — only keep actionable context.`
|
|
30
|
+
|
|
@@ -5,8 +5,23 @@ import { normalizeSlug } from "../utils/name-utils"
|
|
|
5
5
|
|
|
6
6
|
export type ContextHealth = "good" | "watch" | "heavy" | "critical"
|
|
7
7
|
|
|
8
|
+
export type ContextHandoffRecommendedAction = "continue" | "save_handoff" | "fill_required_context"
|
|
9
|
+
|
|
10
|
+
export interface ContextHandoffValidationCheck {
|
|
11
|
+
name: string
|
|
12
|
+
passed: boolean
|
|
13
|
+
reason: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ContextHandoffValidationProbes {
|
|
17
|
+
recall: boolean
|
|
18
|
+
continuation: boolean
|
|
19
|
+
artifact: boolean
|
|
20
|
+
decision: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
export interface ContextHandoffInput {
|
|
9
|
-
operation: "save" | "load" | "latest" | "status"
|
|
24
|
+
operation: "save" | "load" | "latest" | "status" | "validate"
|
|
10
25
|
repoRoot: string
|
|
11
26
|
currentStage?: string
|
|
12
27
|
nextStage?: string
|
|
@@ -22,6 +37,7 @@ export interface ContextHandoffInput {
|
|
|
22
37
|
openDecisions?: string[]
|
|
23
38
|
recentlyAccessedFiles?: string[]
|
|
24
39
|
compressionRisk?: string[]
|
|
40
|
+
activeRules?: string[]
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
export interface ContextStateEntry {
|
|
@@ -39,6 +55,7 @@ export interface ContextStateEntry {
|
|
|
39
55
|
openDecisions: string[]
|
|
40
56
|
recentlyAccessedFiles: string[]
|
|
41
57
|
compressionRisk: string[]
|
|
58
|
+
activeRules: string[]
|
|
42
59
|
recommendNewSession: boolean
|
|
43
60
|
updatedAt: string
|
|
44
61
|
}
|
|
@@ -62,7 +79,15 @@ export interface ContextHandoffResult {
|
|
|
62
79
|
openDecisions?: string[]
|
|
63
80
|
recentlyAccessedFiles?: string[]
|
|
64
81
|
compressionRisk?: string[]
|
|
82
|
+
activeRules?: string[]
|
|
65
83
|
updatedAt?: string
|
|
84
|
+
// Validation fields
|
|
85
|
+
ok?: boolean
|
|
86
|
+
probes?: ContextHandoffValidationProbes
|
|
87
|
+
checks?: ContextHandoffValidationCheck[]
|
|
88
|
+
missing?: string[]
|
|
89
|
+
warnings?: string[]
|
|
90
|
+
recommendedAction?: ContextHandoffRecommendedAction
|
|
66
91
|
}
|
|
67
92
|
|
|
68
93
|
function ceDir(repoRoot: string): string {
|
|
@@ -132,6 +157,7 @@ function buildDefaultHandoffMarkdown(input: {
|
|
|
132
157
|
openDecisions: string[]
|
|
133
158
|
recentlyAccessedFiles: string[]
|
|
134
159
|
compressionRisk: string[]
|
|
160
|
+
activeRules: string[]
|
|
135
161
|
}): string {
|
|
136
162
|
const currentTask = input.nextStage
|
|
137
163
|
? `Continue from ${input.currentStage} to ${input.nextStage}.`
|
|
@@ -172,6 +198,9 @@ function buildDefaultHandoffMarkdown(input: {
|
|
|
172
198
|
"## Active Files",
|
|
173
199
|
activeFiles,
|
|
174
200
|
"",
|
|
201
|
+
"## Active Rules",
|
|
202
|
+
formatBullets(input.activeRules),
|
|
203
|
+
"",
|
|
175
204
|
"## Recently Accessed Files",
|
|
176
205
|
formatBullets(input.recentlyAccessedFiles),
|
|
177
206
|
"",
|
|
@@ -223,6 +252,7 @@ function normalizeStateEntry(raw: unknown): ContextStateEntry | null {
|
|
|
223
252
|
? toStringArray(state.recentlyAccessedFiles)
|
|
224
253
|
: activeFiles.slice(0, 5),
|
|
225
254
|
compressionRisk: toStringArray(state.compressionRisk),
|
|
255
|
+
activeRules: toStringArray(state.activeRules),
|
|
226
256
|
recommendNewSession: typeof state.recommendNewSession === "boolean" ? state.recommendNewSession : false,
|
|
227
257
|
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : new Date(0).toISOString(),
|
|
228
258
|
}
|
|
@@ -268,6 +298,8 @@ export function createContextHandoffTool() {
|
|
|
268
298
|
return latest(input)
|
|
269
299
|
case "status":
|
|
270
300
|
return status(input)
|
|
301
|
+
case "validate":
|
|
302
|
+
return validate(input)
|
|
271
303
|
default:
|
|
272
304
|
throw new Error(`Unknown operation: ${input.operation}`)
|
|
273
305
|
}
|
|
@@ -290,6 +322,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
|
290
322
|
? input.recentlyAccessedFiles
|
|
291
323
|
: activeFiles.slice(0, 5)
|
|
292
324
|
const compressionRisk = input.compressionRisk ?? []
|
|
325
|
+
const activeRules = input.activeRules ?? []
|
|
293
326
|
|
|
294
327
|
const handoffMarkdown = input.handoffMarkdown?.trim().length
|
|
295
328
|
? input.handoffMarkdown
|
|
@@ -305,6 +338,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
|
305
338
|
openDecisions,
|
|
306
339
|
recentlyAccessedFiles,
|
|
307
340
|
compressionRisk,
|
|
341
|
+
activeRules,
|
|
308
342
|
})
|
|
309
343
|
|
|
310
344
|
const recommendNewSession = computeRecommendNewSession(currentStage, nextStage, contextHealth)
|
|
@@ -332,6 +366,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
|
332
366
|
openDecisions,
|
|
333
367
|
recentlyAccessedFiles,
|
|
334
368
|
compressionRisk,
|
|
369
|
+
activeRules,
|
|
335
370
|
recommendNewSession,
|
|
336
371
|
updatedAt: new Date().toISOString(),
|
|
337
372
|
}
|
|
@@ -355,6 +390,7 @@ async function save(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
|
355
390
|
openDecisions,
|
|
356
391
|
recentlyAccessedFiles,
|
|
357
392
|
compressionRisk,
|
|
393
|
+
activeRules,
|
|
358
394
|
recommendNewSession,
|
|
359
395
|
updatedAt: state.updatedAt,
|
|
360
396
|
}
|
|
@@ -395,6 +431,7 @@ async function load(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
|
395
431
|
openDecisions: state.openDecisions,
|
|
396
432
|
recentlyAccessedFiles: state.recentlyAccessedFiles,
|
|
397
433
|
compressionRisk: state.compressionRisk,
|
|
434
|
+
activeRules: state.activeRules,
|
|
398
435
|
recommendNewSession: state.recommendNewSession,
|
|
399
436
|
handoffMarkdown: markdown,
|
|
400
437
|
updatedAt: state.updatedAt,
|
|
@@ -429,11 +466,278 @@ async function latest(input: ContextHandoffInput): Promise<ContextHandoffResult>
|
|
|
429
466
|
openDecisions: state.openDecisions,
|
|
430
467
|
recentlyAccessedFiles: state.recentlyAccessedFiles,
|
|
431
468
|
compressionRisk: state.compressionRisk,
|
|
469
|
+
activeRules: state.activeRules,
|
|
432
470
|
recommendNewSession: state.recommendNewSession,
|
|
433
471
|
updatedAt: state.updatedAt,
|
|
434
472
|
}
|
|
435
473
|
}
|
|
436
474
|
|
|
475
|
+
const PLACEHOLDER_VALUES = new Set(["n/a", "na", "not run", "none", "", "-"])
|
|
476
|
+
const PLACEHOLDER_PREFIXES = ["- n/a", "- na", "- not run", "- none", "- "]
|
|
477
|
+
|
|
478
|
+
function isPlaceholder(text: string): boolean {
|
|
479
|
+
const trimmed = text.trim().toLowerCase()
|
|
480
|
+
if (PLACEHOLDER_VALUES.has(trimmed)) return true
|
|
481
|
+
for (const prefix of PLACEHOLDER_PREFIXES) {
|
|
482
|
+
if (trimmed === prefix) return true
|
|
483
|
+
}
|
|
484
|
+
return false
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function isMeaningfulText(value?: string): boolean {
|
|
488
|
+
return Boolean(value && !isPlaceholder(value))
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function toPublicHandoffPath(repoRoot: string, filePath: string): string {
|
|
492
|
+
return path.isAbsolute(filePath) ? toRepoRelative(repoRoot, filePath) : filePath
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function extractSection(markdown: string, heading: string): string {
|
|
496
|
+
const lines = markdown.split("\n")
|
|
497
|
+
const sectionLines: string[] = []
|
|
498
|
+
let inSection = false
|
|
499
|
+
|
|
500
|
+
for (const line of lines) {
|
|
501
|
+
if (line.startsWith("## ")) {
|
|
502
|
+
if (inSection) break
|
|
503
|
+
if (line.slice(3).trim() === heading) {
|
|
504
|
+
inSection = true
|
|
505
|
+
}
|
|
506
|
+
continue
|
|
507
|
+
}
|
|
508
|
+
if (inSection) {
|
|
509
|
+
sectionLines.push(line)
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return sectionLines.join("\n").trim()
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function sectionHasMeaningfulContent(markdown: string, heading: string): boolean {
|
|
517
|
+
const section = extractSection(markdown, heading)
|
|
518
|
+
if (!section) return false
|
|
519
|
+
|
|
520
|
+
const lines = section.split("\n")
|
|
521
|
+
for (const line of lines) {
|
|
522
|
+
const trimmed = line.trim()
|
|
523
|
+
if (!trimmed) continue
|
|
524
|
+
if (!isPlaceholder(trimmed)) return true
|
|
525
|
+
}
|
|
526
|
+
return false
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function hasMeaningfulArray(arr: string[]): boolean {
|
|
530
|
+
return arr.some(item => isMeaningfulText(item))
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function hasMeaningfulRecord(rec: Record<string, string | undefined>): boolean {
|
|
534
|
+
return Object.values(rec).some(isMeaningfulText)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function isMeaningfulStage(value?: string): boolean {
|
|
538
|
+
return Boolean(value && value.trim().length > 0 && value !== "unknown" && !isPlaceholder(value))
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async function validate(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
542
|
+
const checks: ContextHandoffValidationCheck[] = []
|
|
543
|
+
const missing: string[] = []
|
|
544
|
+
const warnings: string[] = []
|
|
545
|
+
|
|
546
|
+
// Read state
|
|
547
|
+
const state = await readState(input.repoRoot)
|
|
548
|
+
const hasState = state !== null
|
|
549
|
+
|
|
550
|
+
// Read markdown
|
|
551
|
+
let markdown = ""
|
|
552
|
+
let hasMarkdown = false
|
|
553
|
+
let handoffFile = ""
|
|
554
|
+
|
|
555
|
+
if (input.handoffPath) {
|
|
556
|
+
const absolutePath = resolveRepoPath(input.repoRoot, input.handoffPath)
|
|
557
|
+
if (existsSync(absolutePath)) {
|
|
558
|
+
markdown = await readFile(absolutePath, "utf8")
|
|
559
|
+
hasMarkdown = markdown.trim().length > 0
|
|
560
|
+
handoffFile = toPublicHandoffPath(input.repoRoot, input.handoffPath)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!hasMarkdown && state?.latestHandoffPath) {
|
|
565
|
+
const absolutePath = resolveRepoPath(input.repoRoot, state.latestHandoffPath)
|
|
566
|
+
if (existsSync(absolutePath)) {
|
|
567
|
+
markdown = await readFile(absolutePath, "utf8")
|
|
568
|
+
hasMarkdown = markdown.trim().length > 0
|
|
569
|
+
handoffFile = toPublicHandoffPath(input.repoRoot, state.latestHandoffPath)
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (!hasMarkdown) {
|
|
574
|
+
const latestPath = latestHandoffPath(input.repoRoot)
|
|
575
|
+
if (existsSync(latestPath)) {
|
|
576
|
+
markdown = await readFile(latestPath, "utf8")
|
|
577
|
+
hasMarkdown = markdown.trim().length > 0
|
|
578
|
+
handoffFile = toRepoRelative(input.repoRoot, latestPath)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const found = hasState || hasMarkdown
|
|
583
|
+
|
|
584
|
+
checks.push({
|
|
585
|
+
name: "state_exists",
|
|
586
|
+
passed: hasState,
|
|
587
|
+
reason: hasState ? "Found context-state.json" : "No context-state.json found",
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
checks.push({
|
|
591
|
+
name: "handoff_exists",
|
|
592
|
+
passed: hasMarkdown,
|
|
593
|
+
reason: hasMarkdown ? "Found handoff markdown" : "No handoff markdown found",
|
|
594
|
+
})
|
|
595
|
+
|
|
596
|
+
// --- Recall probe ---
|
|
597
|
+
const hasCurrentTruth = state ? hasMeaningfulArray(state.currentTruth) : false
|
|
598
|
+
const hasCurrentStage = state ? isMeaningfulStage(state.currentStage) : false
|
|
599
|
+
const hasCurrentTaskSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Current Task")
|
|
600
|
+
|
|
601
|
+
const recallPass = hasCurrentTruth || (hasCurrentStage && (hasMarkdown || (state?.nextStage !== undefined))) || hasCurrentTaskSection
|
|
602
|
+
|
|
603
|
+
checks.push({
|
|
604
|
+
name: "recall_current_truth",
|
|
605
|
+
passed: hasCurrentTruth,
|
|
606
|
+
reason: hasCurrentTruth ? `Found ${state!.currentTruth.length} current truth entries` : "No current truth entries",
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
checks.push({
|
|
610
|
+
name: "recall_current_task",
|
|
611
|
+
passed: hasCurrentTaskSection,
|
|
612
|
+
reason: hasCurrentTaskSection ? "Current Task section has meaningful content" : "Current Task section is missing or placeholder",
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
checks.push({
|
|
616
|
+
name: "recall_current_stage",
|
|
617
|
+
passed: hasCurrentStage,
|
|
618
|
+
reason: hasCurrentStage ? `Current stage: ${state!.currentStage}` : "No meaningful current stage",
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
if (!recallPass) {
|
|
622
|
+
missing.push("recall: current task or goal evidence")
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// --- Continuation probe ---
|
|
626
|
+
// Tightened: only actionable next-step evidence passes
|
|
627
|
+
const hasNextStage = state ? isMeaningfulStage(state.nextStage) : false
|
|
628
|
+
const hasNextMinimalStep = hasMarkdown && sectionHasMeaningfulContent(markdown, "Next Minimal Step")
|
|
629
|
+
|
|
630
|
+
const continuationPass = hasNextMinimalStep || hasNextStage
|
|
631
|
+
|
|
632
|
+
checks.push({
|
|
633
|
+
name: "continuation_next_stage",
|
|
634
|
+
passed: hasNextStage,
|
|
635
|
+
reason: hasNextStage ? `Next stage: ${state!.nextStage}` : "No meaningful next stage",
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
checks.push({
|
|
639
|
+
name: "continuation_next_minimal_step",
|
|
640
|
+
passed: hasNextMinimalStep,
|
|
641
|
+
reason: hasNextMinimalStep ? "Next Minimal Step has meaningful content" : "Next Minimal Step is missing or placeholder",
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
// Diagnostic checks (do not affect continuation pass)
|
|
645
|
+
const hasBlocker = state ? Boolean(state.blocker && !isPlaceholder(state.blocker)) : false
|
|
646
|
+
const hasVerification = state ? Boolean(state.verification && !isPlaceholder(state.verification)) : false
|
|
647
|
+
const hasVerificationSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Verification")
|
|
648
|
+
|
|
649
|
+
checks.push({
|
|
650
|
+
name: "continuation_blocker",
|
|
651
|
+
passed: hasBlocker,
|
|
652
|
+
reason: hasBlocker ? `Blocker: ${state!.blocker}` : "No blocker information",
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
checks.push({
|
|
656
|
+
name: "continuation_verification",
|
|
657
|
+
passed: hasVerification || hasVerificationSection,
|
|
658
|
+
reason: (hasVerification || hasVerificationSection) ? "Verification evidence present" : "No verification information",
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
if (!continuationPass) {
|
|
662
|
+
missing.push("continuation: next minimal step or next stage evidence")
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// --- Artifact probe ---
|
|
666
|
+
const hasActiveFiles = state ? hasMeaningfulArray(state.activeFiles) : false
|
|
667
|
+
const hasRecentlyAccessed = state ? hasMeaningfulArray(state.recentlyAccessedFiles) : false
|
|
668
|
+
const hasArtifacts = state ? hasMeaningfulRecord(state.artifacts) : false
|
|
669
|
+
const hasActiveFilesSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Active Files")
|
|
670
|
+
const hasRecentlyAccessedSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Recently Accessed Files")
|
|
671
|
+
const hasArtifactsSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Artifacts")
|
|
672
|
+
|
|
673
|
+
const artifactPass = hasActiveFiles || hasRecentlyAccessed || hasArtifacts
|
|
674
|
+
|| hasActiveFilesSection || hasRecentlyAccessedSection || hasArtifactsSection
|
|
675
|
+
|
|
676
|
+
checks.push({
|
|
677
|
+
name: "artifact_active_files",
|
|
678
|
+
passed: hasActiveFiles || hasActiveFilesSection,
|
|
679
|
+
reason: (hasActiveFiles || hasActiveFilesSection) ? "Active files present" : "No active files",
|
|
680
|
+
})
|
|
681
|
+
|
|
682
|
+
if (!artifactPass) {
|
|
683
|
+
warnings.push("artifact: active files or artifacts are missing")
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// --- Decision probe ---
|
|
687
|
+
const hasOpenDecisions = state ? hasMeaningfulArray(state.openDecisions) : false
|
|
688
|
+
const hasInvalidatedAssumptions = state ? hasMeaningfulArray(state.invalidatedAssumptions) : false
|
|
689
|
+
const hasOpenDecisionsSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Open Decisions")
|
|
690
|
+
const hasInvalidatedSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Invalidated Assumptions")
|
|
691
|
+
const hasCurrentTruthSection = hasMarkdown && sectionHasMeaningfulContent(markdown, "Current Truth")
|
|
692
|
+
|
|
693
|
+
const decisionPass = hasOpenDecisions || hasInvalidatedAssumptions || hasCurrentTruth
|
|
694
|
+
|| hasOpenDecisionsSection || hasInvalidatedSection || hasCurrentTruthSection
|
|
695
|
+
|
|
696
|
+
checks.push({
|
|
697
|
+
name: "decision_open_decisions",
|
|
698
|
+
passed: hasOpenDecisions || hasOpenDecisionsSection,
|
|
699
|
+
reason: (hasOpenDecisions || hasOpenDecisionsSection) ? "Open decisions present" : "No open decisions",
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
if (!decisionPass) {
|
|
703
|
+
warnings.push("decision: decisions or invalidated assumptions are missing")
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// --- ok derivation ---
|
|
707
|
+
const ok = recallPass && continuationPass
|
|
708
|
+
|
|
709
|
+
// --- recommended action ---
|
|
710
|
+
let recommendedAction: ContextHandoffRecommendedAction
|
|
711
|
+
if (!found) {
|
|
712
|
+
recommendedAction = "save_handoff"
|
|
713
|
+
} else if (!ok) {
|
|
714
|
+
recommendedAction = "fill_required_context"
|
|
715
|
+
} else {
|
|
716
|
+
recommendedAction = "continue"
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
operation: "validate",
|
|
721
|
+
found,
|
|
722
|
+
ok,
|
|
723
|
+
path: handoffFile || undefined,
|
|
724
|
+
probes: {
|
|
725
|
+
recall: recallPass,
|
|
726
|
+
continuation: continuationPass,
|
|
727
|
+
artifact: artifactPass,
|
|
728
|
+
decision: decisionPass,
|
|
729
|
+
},
|
|
730
|
+
checks,
|
|
731
|
+
missing,
|
|
732
|
+
warnings,
|
|
733
|
+
recommendedAction,
|
|
734
|
+
currentStage: state?.currentStage,
|
|
735
|
+
nextStage: state?.nextStage,
|
|
736
|
+
contextHealth: state?.contextHealth,
|
|
737
|
+
updatedAt: state?.updatedAt,
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
437
741
|
async function status(input: ContextHandoffInput): Promise<ContextHandoffResult> {
|
|
438
742
|
const state = await readState(input.repoRoot)
|
|
439
743
|
|
|
@@ -465,6 +769,7 @@ async function status(input: ContextHandoffInput): Promise<ContextHandoffResult>
|
|
|
465
769
|
openDecisions: state.openDecisions,
|
|
466
770
|
recentlyAccessedFiles: state.recentlyAccessedFiles,
|
|
467
771
|
compressionRisk: state.compressionRisk,
|
|
772
|
+
activeRules: state.activeRules,
|
|
468
773
|
recommendNewSession: state.recommendNewSession,
|
|
469
774
|
updatedAt: state.updatedAt,
|
|
470
775
|
}
|
package/package.json
CHANGED
package/skills/02-plan/SKILL.md
CHANGED
|
@@ -39,13 +39,14 @@ Every unit follows **RED → GREEN → REFACTOR**:
|
|
|
39
39
|
|
|
40
40
|
## Planning flow
|
|
41
41
|
|
|
42
|
-
1.
|
|
43
|
-
2.
|
|
44
|
-
3.
|
|
45
|
-
4.
|
|
46
|
-
5. If
|
|
47
|
-
6.
|
|
48
|
-
7.
|
|
42
|
+
1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles` and `blocker` as starting point. If not found, proceed normally (new project).
|
|
43
|
+
2. Read relevant brainstorm from `docs/brainstorms/`
|
|
44
|
+
3. Run solution search (keywords → grep frontmatter → read top 3)
|
|
45
|
+
4. Gather repository context
|
|
46
|
+
5. If plan exists: use `plan_diff` `compare` → review with user → `patch`
|
|
47
|
+
6. If no plan: write new plan under `docs/plans/` using `references/plan-template.md`
|
|
48
|
+
7. Structure work using `references/implementation-unit-template.md`
|
|
49
|
+
8. Verify every unit follows TDD gates
|
|
49
50
|
|
|
50
51
|
## Optional: CEO Review
|
|
51
52
|
|
package/skills/03-work/SKILL.md
CHANGED
|
@@ -41,16 +41,18 @@ Every step follows **RED → GREEN → REFACTOR**:
|
|
|
41
41
|
|
|
42
42
|
## Workflow
|
|
43
43
|
|
|
44
|
-
1.
|
|
45
|
-
2.
|
|
46
|
-
3.
|
|
47
|
-
4.
|
|
48
|
-
5.
|
|
49
|
-
6.
|
|
50
|
-
7.
|
|
51
|
-
8.
|
|
52
|
-
9.
|
|
53
|
-
10.
|
|
54
|
-
11.
|
|
44
|
+
1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles`, `blocker`, `verification`, `activeRules` as starting point. If not found, proceed normally.
|
|
45
|
+
2. Detect input type (plan path vs bare prompt)
|
|
46
|
+
3. Read implementation units if plan path
|
|
47
|
+
4. Load `session_checkpoint` to skip completed units
|
|
48
|
+
5. Use `task_splitter` for dependency analysis
|
|
49
|
+
6. Execute: **inline mode** by default, `ce_parallel_subagent` for independent units
|
|
50
|
+
7. Follow TDD per unit: RED → minimal code → GREEN → refactor → unit-level **verification**
|
|
51
|
+
8. Record progress via `references/progress-update-format.md`
|
|
52
|
+
9. Save `session_checkpoint` after each unit
|
|
53
|
+
10. On failure: `session_checkpoint` `fail` → `retry` → follow strategy
|
|
54
|
+
11. Provide completion report (see `references/completion-report.md`)
|
|
55
|
+
12. **Save handoff**: `context_handoff save` with current stage, next stage, activeFiles, blocker, verification, activeRules
|
|
56
|
+
13. Handoff to `04-review` using `references/handoff.md`
|
|
55
57
|
|
|
56
58
|
Before finishing this skill, apply the completion checklist in [shared pipeline instructions](../references/pipeline-config.md).
|
|
@@ -46,14 +46,15 @@ Code review is **technical evaluation**, not social performance:
|
|
|
46
46
|
|
|
47
47
|
## Workflow
|
|
48
48
|
|
|
49
|
-
1.
|
|
50
|
-
2.
|
|
51
|
-
3.
|
|
52
|
-
4.
|
|
53
|
-
5.
|
|
54
|
-
6.
|
|
55
|
-
7.
|
|
56
|
-
8.
|
|
49
|
+
1. **Load context**: consume latest handoff before any broad file reads — `context_handoff load` or read `.context/compound-engineering/handoffs/latest.md`. If found, use `activeFiles`, `artifacts.plan` as starting point. If not found, proceed normally.
|
|
50
|
+
2. Determine diff scope from branch or explicit target
|
|
51
|
+
3. Collect stats (files, insertions, deletions) → call `review_router`
|
|
52
|
+
4. Read matching plan artifact
|
|
53
|
+
5. Run solution search
|
|
54
|
+
6. Apply each reviewer persona from `review_router`
|
|
55
|
+
7. Merge into structured findings
|
|
56
|
+
8. Verify each finding against codebase
|
|
57
|
+
9. Apply autofixes, re-run tests, re-review if needed
|
|
57
58
|
|
|
58
59
|
## Optional: QA Test Mode
|
|
59
60
|
|
package/skills/06-next/SKILL.md
CHANGED
|
@@ -22,8 +22,9 @@ Use this skill when the user wants to know what to run next in the Compound Engi
|
|
|
22
22
|
When the user asks "what should I do next?", "continue", or runs `/skill:06-next`:
|
|
23
23
|
|
|
24
24
|
1. Call `workflow_state` with the repo root
|
|
25
|
-
2.
|
|
26
|
-
3.
|
|
25
|
+
2. Inspect `workflow_state.context` first — apply **context-first priority chain** from `references/recommendation-logic.md` (health → blocker → recommendNewSession → nextStage → mismatch → fallback)
|
|
26
|
+
3. If no context signals trigger, fall back to artifact-count rules
|
|
27
|
+
4. Return: skill name, reason (1-2 lines), brief workflow state summary
|
|
27
28
|
|
|
28
29
|
### Verbose mode: "full status"
|
|
29
30
|
|
|
@@ -31,7 +32,8 @@ When the user asks "show status", "what's the current state", or uses `--verbose
|
|
|
31
32
|
|
|
32
33
|
1. Call `workflow_state` with the repo root
|
|
33
34
|
2. Call `session_history` with `latest` operation
|
|
34
|
-
3.
|
|
35
|
+
3. Include context health assessment from `workflow_state.context` in the status report
|
|
36
|
+
4. Return: latest artifacts (path + summary), status of each phase, context health, recommended next step
|
|
35
37
|
|
|
36
38
|
## Artifact locations
|
|
37
39
|
|
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
# Recommendation logic
|
|
2
2
|
|
|
3
|
-
Apply these rules in order. Return the first match.
|
|
3
|
+
Apply these rules in strict priority order. Return the first match.
|
|
4
|
+
|
|
5
|
+
## Context-first priority chain
|
|
6
|
+
|
|
7
|
+
`06-next` must call `workflow_state` and inspect `workflow_state.context` **before** applying artifact-count rules. Context health and runtime state override stale artifact counts.
|
|
8
|
+
|
|
9
|
+
Field mapping: the requirement uses `context.health` as a conceptual alias. Use the actual field name `workflow_state.context.contextHealth`.
|
|
10
|
+
|
|
11
|
+
### Priority 1: Critical context health
|
|
12
|
+
|
|
13
|
+
If `context.contextHealth` is `"critical"`:
|
|
14
|
+
- Recommend: save a `context_handoff` with full handoff-lite template, then open a **new session**.
|
|
15
|
+
- Reason: Current session context is critically inflated. Continuing will degrade decision quality and waste tokens.
|
|
16
|
+
- Output: handoff-save guidance + copyable new-session prompt with repo path and artifact references.
|
|
17
|
+
|
|
18
|
+
### Priority 2: Active blocker
|
|
19
|
+
|
|
20
|
+
If `context.blocker` exists and is not `"N/A"` or a placeholder:
|
|
21
|
+
- Recommend: return to the current stage (`context.currentStage`) and resolve the blocker before advancing.
|
|
22
|
+
- Reason: A known blocker exists in the current pipeline stage.
|
|
23
|
+
- Output: `/skill:<currentStage>` with blocker description.
|
|
24
|
+
|
|
25
|
+
### Priority 3: New session recommended
|
|
26
|
+
|
|
27
|
+
If `context.recommendNewSession === true`:
|
|
28
|
+
- Recommend: open a new Pi session.
|
|
29
|
+
- Reason: The previous stage determined that context health + phase transition warrant a fresh session.
|
|
30
|
+
- Output: copyable new-session prompt referencing `context.latestHandoffPath`, artifacts, and next stage.
|
|
31
|
+
|
|
32
|
+
### Priority 4: Explicit next stage
|
|
33
|
+
|
|
34
|
+
If `context.nextStage` exists, is meaningful, and differs from `context.currentStage`:
|
|
35
|
+
- Recommend: `/skill:<context.nextStage>`.
|
|
36
|
+
- Reason: The previous stage explicitly requested this transition.
|
|
37
|
+
- Output: recommended skill command.
|
|
38
|
+
|
|
39
|
+
### Priority 5: Stage mismatch detection
|
|
40
|
+
|
|
41
|
+
If `context.currentStage` does not match the most recent artifact state (e.g. a plan exists but context says `"01-brainstorm"`):
|
|
42
|
+
- Recommend: the skill that matches the most recent artifact, or ask the user to clarify.
|
|
43
|
+
- Reason: Runtime context and artifact state are out of sync.
|
|
44
|
+
- Output: suggested correction with explanation of the mismatch.
|
|
45
|
+
|
|
46
|
+
### Priority 6: Fallback — artifact-count rules
|
|
47
|
+
|
|
48
|
+
If none of the above context rules triggered, fall back to the artifact-count rules below.
|
|
4
49
|
|
|
5
50
|
## Rule 1: No brainstorm artifacts
|
|
6
51
|
|
|
@@ -18,7 +18,34 @@ Supported `modelStrategy` formats in `.pi/settings.json`:
|
|
|
18
18
|
- Full reference: `"02-plan": "anthropic/claude-opus-4-1"`
|
|
19
19
|
- Bare model id (reuses current provider): `"02-plan": "claude-opus-4-1"`
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Start of skill: context loading
|
|
22
|
+
|
|
23
|
+
Before reading any project files or running repository-wide scans, load the most recent handoff:
|
|
24
|
+
|
|
25
|
+
1. Try `context_handoff load` or `context_handoff latest` first.
|
|
26
|
+
2. Fallback: `read .context/compound-engineering/handoffs/latest.md` if tool is unavailable.
|
|
27
|
+
3. **Handoff found?** Consume its `activeFiles`, `blocker`, `verification`, `currentTruth`, `activeRules` before any broad project reads.
|
|
28
|
+
4. **No handoff?** Proceed normally — this is a new project or first run. Do not block.
|
|
29
|
+
|
|
30
|
+
Core principle: **consume handoff before broad project file reads** — a single handoff read (~500 tokens) avoids 5-10 project file scans (~5K-10K tokens).
|
|
31
|
+
|
|
32
|
+
## End of skill: save handoff + status + context
|
|
33
|
+
|
|
34
|
+
Every Phase 1 skill (02-plan through 05-learn) must save context handoff at completion:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
context_handoff save
|
|
38
|
+
currentStage: <stageKey>
|
|
39
|
+
nextStage: <next stage>
|
|
40
|
+
contextHealth: good | watch | heavy | critical
|
|
41
|
+
activeFiles: [1-5 currently active paths]
|
|
42
|
+
blocker: <blocker or N/A>
|
|
43
|
+
verification: <latest command + result>
|
|
44
|
+
activeRules: [1-5 rules critical for continuation]
|
|
45
|
+
currentTruth: [validated truths]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If `context_handoff` is unavailable, manually write the Handoff-lite template to `.context/compound-engineering/handoffs/latest.md`.
|
|
22
49
|
|
|
23
50
|
Before final completion, always output these blocks (replace placeholders with real values, never output angle-bracket placeholders literally):
|
|
24
51
|
|