@tianhai/pi-workflow-kit 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +44 -494
  2. package/docs/developer-usage-guide.md +41 -401
  3. package/docs/oversight-model.md +13 -34
  4. package/docs/workflow-phases.md +32 -46
  5. package/extensions/workflow-guard.ts +67 -0
  6. package/package.json +3 -7
  7. package/skills/brainstorming/SKILL.md +16 -67
  8. package/skills/executing-tasks/SKILL.md +26 -227
  9. package/skills/finalizing/SKILL.md +33 -0
  10. package/skills/writing-plans/SKILL.md +23 -132
  11. package/ROADMAP.md +0 -16
  12. package/agents/code-reviewer.md +0 -18
  13. package/agents/config.ts +0 -5
  14. package/agents/implementer.md +0 -26
  15. package/agents/spec-reviewer.md +0 -13
  16. package/agents/worker.md +0 -17
  17. package/docs/plans/2026-04-10-brainstorming-boundary-enforcement-design.md +0 -60
  18. package/docs/plans/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-design.md +0 -56
  19. package/docs/plans/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-implementation.md +0 -196
  20. package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-design.md +0 -185
  21. package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-implementation.md +0 -334
  22. package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-design.md +0 -251
  23. package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-implementation.md +0 -253
  24. package/extensions/constants.ts +0 -15
  25. package/extensions/lib/logging.ts +0 -138
  26. package/extensions/plan-tracker.ts +0 -508
  27. package/extensions/subagent/agents.ts +0 -144
  28. package/extensions/subagent/concurrency.ts +0 -52
  29. package/extensions/subagent/env.ts +0 -47
  30. package/extensions/subagent/index.ts +0 -1181
  31. package/extensions/subagent/lifecycle.ts +0 -25
  32. package/extensions/subagent/timeout.ts +0 -13
  33. package/extensions/workflow-monitor/debug-monitor.ts +0 -98
  34. package/extensions/workflow-monitor/git.ts +0 -31
  35. package/extensions/workflow-monitor/heuristics.ts +0 -58
  36. package/extensions/workflow-monitor/investigation.ts +0 -52
  37. package/extensions/workflow-monitor/reference-tool.ts +0 -42
  38. package/extensions/workflow-monitor/skip-confirmation.ts +0 -19
  39. package/extensions/workflow-monitor/tdd-monitor.ts +0 -137
  40. package/extensions/workflow-monitor/test-runner.ts +0 -37
  41. package/extensions/workflow-monitor/verification-monitor.ts +0 -61
  42. package/extensions/workflow-monitor/warnings.ts +0 -81
  43. package/extensions/workflow-monitor/workflow-handler.ts +0 -363
  44. package/extensions/workflow-monitor/workflow-next-completions.ts +0 -68
  45. package/extensions/workflow-monitor/workflow-next-state.ts +0 -112
  46. package/extensions/workflow-monitor/workflow-tracker.ts +0 -286
  47. package/extensions/workflow-monitor/workflow-transitions.ts +0 -88
  48. package/extensions/workflow-monitor.ts +0 -909
  49. package/skills/dispatching-parallel-agents/SKILL.md +0 -194
  50. package/skills/receiving-code-review/SKILL.md +0 -196
  51. package/skills/systematic-debugging/SKILL.md +0 -170
  52. package/skills/systematic-debugging/condition-based-waiting-example.ts +0 -158
  53. package/skills/systematic-debugging/condition-based-waiting.md +0 -115
  54. package/skills/systematic-debugging/defense-in-depth.md +0 -122
  55. package/skills/systematic-debugging/find-polluter.sh +0 -63
  56. package/skills/systematic-debugging/reference/rationalizations.md +0 -61
  57. package/skills/systematic-debugging/root-cause-tracing.md +0 -169
  58. package/skills/test-driven-development/SKILL.md +0 -266
  59. package/skills/test-driven-development/reference/examples.md +0 -101
  60. package/skills/test-driven-development/reference/rationalizations.md +0 -67
  61. package/skills/test-driven-development/reference/when-stuck.md +0 -33
  62. package/skills/test-driven-development/testing-anti-patterns.md +0 -299
  63. package/skills/using-git-worktrees/SKILL.md +0 -231
@@ -1,286 +0,0 @@
1
- import type { SessionEntry } from "@mariozechner/pi-coding-agent";
2
-
3
- export const WORKFLOW_PHASES = ["brainstorm", "plan", "execute", "finalize"] as const;
4
-
5
- export type Phase = (typeof WORKFLOW_PHASES)[number];
6
- export type PhaseStatus = "pending" | "active" | "complete" | "skipped";
7
-
8
- export interface WorkflowTrackerState {
9
- phases: Record<Phase, PhaseStatus>;
10
- currentPhase: Phase | null;
11
- artifacts: Record<Phase, string | null>;
12
- prompted: Record<Phase, boolean>;
13
- }
14
-
15
- export type TransitionBoundary =
16
- | "design_reviewable"
17
- | "plan_reviewable"
18
- | "design_committed"
19
- | "plan_ready"
20
- | "execution_complete";
21
-
22
- export function computeBoundaryToPrompt(state: WorkflowTrackerState): TransitionBoundary | null {
23
- // Reviewable: current phase has its deliverable artifact but hasn't been
24
- // user-confirmed as complete. Prompt the user to review before moving on.
25
- if (
26
- state.currentPhase === "brainstorm" &&
27
- state.artifacts.brainstorm &&
28
- state.phases.brainstorm === "active" &&
29
- !state.prompted.brainstorm
30
- ) {
31
- return "design_reviewable";
32
- }
33
- if (state.currentPhase === "plan" && state.artifacts.plan && state.phases.plan === "active" && !state.prompted.plan) {
34
- return "plan_reviewable";
35
- }
36
-
37
- // Committed: phase is complete but user hasn't been prompted for
38
- // transition options yet (e.g. phases completed via skip-confirmation
39
- // "mark complete" or execute phase auto-completing on all tasks terminal).
40
- if (state.phases.brainstorm === "complete" && !state.prompted.brainstorm) {
41
- return "design_committed";
42
- }
43
- if (state.phases.plan === "complete" && !state.prompted.plan) {
44
- return "plan_ready";
45
- }
46
- if (state.phases.execute === "complete" && !state.prompted.execute) {
47
- return "execution_complete";
48
- }
49
- return null;
50
- }
51
-
52
- function cloneState(state: WorkflowTrackerState): WorkflowTrackerState {
53
- return JSON.parse(JSON.stringify(state)) as WorkflowTrackerState;
54
- }
55
-
56
- function emptyState(): WorkflowTrackerState {
57
- const phases = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, "pending"])) as Record<Phase, PhaseStatus>;
58
-
59
- const artifacts = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, null])) as Record<Phase, string | null>;
60
-
61
- const prompted = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, false])) as Record<Phase, boolean>;
62
-
63
- return { phases, currentPhase: null, artifacts, prompted };
64
- }
65
-
66
- export const WORKFLOW_TRACKER_ENTRY_TYPE = "workflow_tracker_state";
67
-
68
- export function parseSkillName(line: string): string | null {
69
- const slashMatch = line.match(/^\s*\/skill:([^\s]+)/);
70
- const xmlMatch = line.match(/<skill\s+name="([^"]+)"/);
71
- return slashMatch?.[1] ?? xmlMatch?.[1] ?? null;
72
- }
73
-
74
- export const SKILL_TO_PHASE: Record<string, Phase> = {
75
- brainstorming: "brainstorm",
76
- "writing-plans": "plan",
77
- "using-git-worktrees": "plan", // pre-execute worktree setup belongs to plan
78
- "executing-tasks": "execute",
79
- "systematic-debugging": "execute", // used within execute phase
80
- "dispatching-parallel-agents": "execute", // used within execute phase
81
- "test-driven-development": "execute", // makes TDD skill phase-aware
82
- "receiving-code-review": "finalize", // post-PR external review
83
- };
84
-
85
- export function resolveSkillPhase(skill: string, state: WorkflowTrackerState | null | undefined): Phase | null {
86
- if (skill === "executing-tasks") {
87
- if (state?.currentPhase === "finalize" || state?.phases.execute === "complete") {
88
- return "finalize";
89
- }
90
- return "execute";
91
- }
92
-
93
- return SKILL_TO_PHASE[skill] ?? null;
94
- }
95
-
96
- const PLANS_DIR_RE = /^docs\/plans\//;
97
- const DESIGN_RE = /-design\.md$/;
98
- const IMPLEMENTATION_RE = /-implementation\.md$/;
99
-
100
- export class WorkflowTracker {
101
- private state: WorkflowTrackerState = emptyState();
102
-
103
- getState(): WorkflowTrackerState {
104
- return cloneState(this.state);
105
- }
106
-
107
- setState(state: WorkflowTrackerState): void {
108
- this.state = cloneState(state);
109
- }
110
-
111
- reset(): void {
112
- this.state = emptyState();
113
- }
114
-
115
- advanceTo(phase: Phase): boolean {
116
- const current = this.state.currentPhase;
117
- const nextIdx = WORKFLOW_PHASES.indexOf(phase);
118
-
119
- if (current) {
120
- const curIdx = WORKFLOW_PHASES.indexOf(current);
121
- if (nextIdx === curIdx) {
122
- // Same-phase navigation is a no-op. This prevents accidental resets
123
- // when plan_tracker init is called while already in execute, or when
124
- // a skill is re-invoked during its own phase.
125
- return false;
126
- }
127
- if (nextIdx < curIdx) {
128
- // Backward navigation = intentional new task. Reset everything.
129
- this.reset();
130
- // Fall through to activate the target phase below.
131
- } else {
132
- // Forward advance: do NOT auto-complete the current phase.
133
- // Phase completion requires explicit user confirmation via
134
- // boundary prompts or skip-confirmation "mark complete".
135
- // However, refuse to jump over unresolved intermediate phases.
136
- for (let i = curIdx + 1; i < nextIdx; i++) {
137
- const intermediate = WORKFLOW_PHASES[i]!;
138
- const status = this.state.phases[intermediate];
139
- if (status !== "complete" && status !== "skipped") {
140
- // Can't advance past an unresolved intermediate phase.
141
- return false;
142
- }
143
- }
144
- }
145
- }
146
-
147
- this.state.currentPhase = phase;
148
- if (this.state.phases[phase] === "pending") {
149
- this.state.phases[phase] = "active";
150
- }
151
-
152
- return true;
153
- }
154
-
155
- skipPhase(phase: Phase): boolean {
156
- const status = this.state.phases[phase];
157
- if (status !== "pending" && status !== "active") return false;
158
- this.state.phases[phase] = "skipped";
159
- return true;
160
- }
161
-
162
- skipPhases(phases: Phase[]): boolean {
163
- let changed = false;
164
- for (const p of phases) changed = this.skipPhase(p) || changed;
165
- return changed;
166
- }
167
-
168
- completeCurrent(): boolean {
169
- const phase = this.state.currentPhase;
170
- if (!phase) return false;
171
- return this.completePhase(phase);
172
- }
173
-
174
- completePhase(phase: Phase): boolean {
175
- if (this.state.phases[phase] === "complete") return false;
176
- this.state.phases[phase] = "complete";
177
- return true;
178
- }
179
-
180
- recordArtifact(phase: Phase, path: string): boolean {
181
- if (this.state.artifacts[phase] === path) return false;
182
- this.state.artifacts[phase] = path;
183
- return true;
184
- }
185
-
186
- markPrompted(phase: Phase): boolean {
187
- if (this.state.prompted[phase]) return false;
188
- this.state.prompted[phase] = true;
189
- return true;
190
- }
191
-
192
- onInputText(text: string): boolean {
193
- const lines = text.split(/\r?\n/);
194
- let changed = false;
195
-
196
- for (const line of lines) {
197
- const skill = parseSkillName(line);
198
- if (!skill) continue;
199
- const phase = resolveSkillPhase(skill, this.state);
200
- if (!phase) continue;
201
- // Guard against backward navigation: skills shared across phases (e.g. executing-tasks
202
- // covers both execute and finalize) must not reset state when re-invoked in a later phase.
203
- const currentIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
204
- const targetIdx = WORKFLOW_PHASES.indexOf(phase);
205
- if (targetIdx <= currentIdx) continue;
206
- if (this.advanceTo(phase)) changed = true;
207
- }
208
-
209
- return changed;
210
- }
211
-
212
- onSkillFileRead(path: string): boolean {
213
- const match = path.match(/\/skills\/([^/]+)\/SKILL\.md$/);
214
- if (!match) return false;
215
- const phase = resolveSkillPhase(match[1], this.state);
216
- if (!phase) return false;
217
- // Guard against backward navigation: some skills (e.g. executing-tasks) serve
218
- // multiple phases. Re-reading their SKILL.md during a later phase (e.g. finalize)
219
- // must not reset workflow state. Rely on plan_tracker init or explicit /workflow-reset
220
- // to restart from scratch.
221
- const currentIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
222
- const targetIdx = WORKFLOW_PHASES.indexOf(phase);
223
- if (targetIdx <= currentIdx) return false;
224
- return this.advanceTo(phase);
225
- }
226
-
227
- onFileWritten(path: string): boolean {
228
- if (!PLANS_DIR_RE.test(path)) return false;
229
-
230
- if (DESIGN_RE.test(path)) {
231
- // Only advance if we haven't already passed the brainstorm phase.
232
- // Writing a design doc during plan/execute/finalize (e.g., updating
233
- // the plan) must NOT reset workflow state.
234
- const curIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
235
- if (curIdx > WORKFLOW_PHASES.indexOf("brainstorm")) {
236
- return this.recordArtifact("brainstorm", path);
237
- }
238
- let changed = false;
239
- changed = this.recordArtifact("brainstorm", path) || changed;
240
- // Activate brainstorm phase but do NOT auto-complete.
241
- // User confirms completion via the reviewable boundary prompt at agent_end.
242
- changed = this.advanceTo("brainstorm") || changed;
243
- return changed;
244
- }
245
-
246
- if (IMPLEMENTATION_RE.test(path)) {
247
- // Only advance if we haven't already passed the plan phase.
248
- const curIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
249
- if (curIdx > WORKFLOW_PHASES.indexOf("plan")) {
250
- return this.recordArtifact("plan", path);
251
- }
252
- let changed = false;
253
- changed = this.recordArtifact("plan", path) || changed;
254
- // Activate plan phase but do NOT auto-complete.
255
- // User confirms completion via the reviewable boundary prompt at agent_end.
256
- changed = this.advanceTo("plan") || changed;
257
- return changed;
258
- }
259
-
260
- return false;
261
- }
262
-
263
- onPlanTrackerInit(): boolean {
264
- // Guard: don't advance if already in execute (prevents accidental resets).
265
- // Also refuse to jump over unresolved phases (e.g., plan still active).
266
- if (this.state.currentPhase === "execute") return false;
267
- return this.advanceTo("execute");
268
- }
269
-
270
- static reconstructFromBranch(branch: SessionEntry[]): WorkflowTrackerState | null {
271
- let last: WorkflowTrackerState | null = null;
272
-
273
- for (const entry of branch) {
274
- if (entry.type !== "custom") continue;
275
- // biome-ignore lint/suspicious/noExplicitAny: pi SDK session entry type
276
- if ((entry as any).customType !== WORKFLOW_TRACKER_ENTRY_TYPE) continue;
277
- // biome-ignore lint/suspicious/noExplicitAny: pi SDK session entry type
278
- const data = (entry as any).data as WorkflowTrackerState | undefined;
279
- if (data && typeof data === "object") {
280
- last = cloneState(data);
281
- }
282
- }
283
-
284
- return last;
285
- }
286
- }
@@ -1,88 +0,0 @@
1
- import type { Phase, TransitionBoundary } from "./workflow-tracker";
2
-
3
- export type TransitionChoice = "next" | "fresh" | "skip" | "revise" | "discuss";
4
-
5
- export interface TransitionPrompt {
6
- boundary: TransitionBoundary;
7
- title: string;
8
- nextPhase: Phase;
9
- artifactPath: string | null;
10
- options: { choice: TransitionChoice; label: string }[];
11
- }
12
-
13
- const BASE_OPTIONS: TransitionPrompt["options"] = [
14
- { choice: "next", label: "Next step (this session)" },
15
- { choice: "fresh", label: "Fresh session → next step" },
16
- { choice: "skip", label: "Skip" },
17
- { choice: "discuss", label: "Discuss" },
18
- ];
19
-
20
- // Reviewable options: shown when a phase has its artifact but hasn't
21
- // been user-confirmed as complete. Includes explicit approval + revision.
22
- const REVIEWABLE_OPTIONS: TransitionPrompt["options"] = [
23
- { choice: "next", label: "✓ Looks good, next step (this session)" },
24
- { choice: "fresh", label: "✓ Looks good, fresh session → next step" },
25
- { choice: "skip", label: "Skip phase" },
26
- { choice: "revise", label: "✗ Needs more work" },
27
- { choice: "discuss", label: "Discuss" },
28
- ];
29
-
30
- export function getTransitionPrompt(boundary: TransitionBoundary, artifactPath: string | null): TransitionPrompt {
31
- switch (boundary) {
32
- // Reviewable: phase has artifact but user hasn't confirmed completion
33
- case "design_reviewable":
34
- return {
35
- boundary,
36
- title: `Design written${artifactPath ? `: ${artifactPath}` : ""}. Ready to proceed?`,
37
- nextPhase: "plan",
38
- artifactPath,
39
- options: REVIEWABLE_OPTIONS,
40
- };
41
- case "plan_reviewable":
42
- return {
43
- boundary,
44
- title: `Plan written${artifactPath ? `: ${artifactPath}` : ""}. Ready to proceed?`,
45
- nextPhase: "execute",
46
- artifactPath,
47
- options: REVIEWABLE_OPTIONS,
48
- };
49
- // Committed: phase already complete, user chooses how to proceed
50
- case "design_committed":
51
- return {
52
- boundary,
53
- title: "Design committed. What next?",
54
- nextPhase: "plan",
55
- artifactPath,
56
- options: BASE_OPTIONS,
57
- };
58
- case "plan_ready":
59
- return {
60
- boundary,
61
- title: "Plan ready. What next?",
62
- nextPhase: "execute",
63
- artifactPath,
64
- options: BASE_OPTIONS,
65
- };
66
- case "execution_complete":
67
- return {
68
- boundary,
69
- title: "All tasks complete. What next?",
70
- nextPhase: "finalize",
71
- artifactPath,
72
- options: BASE_OPTIONS,
73
- };
74
- default:
75
- return {
76
- boundary,
77
- title: "What next?",
78
- nextPhase: "plan",
79
- artifactPath,
80
- options: BASE_OPTIONS,
81
- };
82
- }
83
- }
84
-
85
- /** Whether a boundary represents a phase that still needs user confirmation. */
86
- export function isReviewableBoundary(boundary: TransitionBoundary): boolean {
87
- return boundary === "design_reviewable" || boundary === "plan_reviewable";
88
- }