@tianhai/pi-workflow-kit 0.5.1 → 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 (62) 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 -59
  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/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-design.md +0 -56
  18. package/docs/plans/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-implementation.md +0 -196
  19. package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-design.md +0 -185
  20. package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-implementation.md +0 -334
  21. package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-design.md +0 -251
  22. package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-implementation.md +0 -253
  23. package/extensions/constants.ts +0 -15
  24. package/extensions/lib/logging.ts +0 -138
  25. package/extensions/plan-tracker.ts +0 -502
  26. package/extensions/subagent/agents.ts +0 -144
  27. package/extensions/subagent/concurrency.ts +0 -52
  28. package/extensions/subagent/env.ts +0 -47
  29. package/extensions/subagent/index.ts +0 -1181
  30. package/extensions/subagent/lifecycle.ts +0 -25
  31. package/extensions/subagent/timeout.ts +0 -13
  32. package/extensions/workflow-monitor/debug-monitor.ts +0 -98
  33. package/extensions/workflow-monitor/git.ts +0 -31
  34. package/extensions/workflow-monitor/heuristics.ts +0 -58
  35. package/extensions/workflow-monitor/investigation.ts +0 -52
  36. package/extensions/workflow-monitor/reference-tool.ts +0 -42
  37. package/extensions/workflow-monitor/skip-confirmation.ts +0 -19
  38. package/extensions/workflow-monitor/tdd-monitor.ts +0 -137
  39. package/extensions/workflow-monitor/test-runner.ts +0 -37
  40. package/extensions/workflow-monitor/verification-monitor.ts +0 -61
  41. package/extensions/workflow-monitor/warnings.ts +0 -81
  42. package/extensions/workflow-monitor/workflow-handler.ts +0 -358
  43. package/extensions/workflow-monitor/workflow-next-completions.ts +0 -68
  44. package/extensions/workflow-monitor/workflow-next-state.ts +0 -112
  45. package/extensions/workflow-monitor/workflow-tracker.ts +0 -253
  46. package/extensions/workflow-monitor/workflow-transitions.ts +0 -55
  47. package/extensions/workflow-monitor.ts +0 -872
  48. package/skills/dispatching-parallel-agents/SKILL.md +0 -194
  49. package/skills/receiving-code-review/SKILL.md +0 -196
  50. package/skills/systematic-debugging/SKILL.md +0 -170
  51. package/skills/systematic-debugging/condition-based-waiting-example.ts +0 -158
  52. package/skills/systematic-debugging/condition-based-waiting.md +0 -115
  53. package/skills/systematic-debugging/defense-in-depth.md +0 -122
  54. package/skills/systematic-debugging/find-polluter.sh +0 -63
  55. package/skills/systematic-debugging/reference/rationalizations.md +0 -61
  56. package/skills/systematic-debugging/root-cause-tracing.md +0 -169
  57. package/skills/test-driven-development/SKILL.md +0 -266
  58. package/skills/test-driven-development/reference/examples.md +0 -101
  59. package/skills/test-driven-development/reference/rationalizations.md +0 -67
  60. package/skills/test-driven-development/reference/when-stuck.md +0 -33
  61. package/skills/test-driven-development/testing-anti-patterns.md +0 -299
  62. package/skills/using-git-worktrees/SKILL.md +0 -231
@@ -1,253 +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 = "design_committed" | "plan_ready" | "execution_complete";
16
-
17
- export function computeBoundaryToPrompt(state: WorkflowTrackerState): TransitionBoundary | null {
18
- if (state.phases.brainstorm === "complete" && !state.prompted.brainstorm) {
19
- return "design_committed";
20
- }
21
- if (state.phases.plan === "complete" && !state.prompted.plan) {
22
- return "plan_ready";
23
- }
24
- if (state.phases.execute === "complete" && !state.prompted.execute) {
25
- return "execution_complete";
26
- }
27
- return null;
28
- }
29
-
30
- function cloneState(state: WorkflowTrackerState): WorkflowTrackerState {
31
- return JSON.parse(JSON.stringify(state)) as WorkflowTrackerState;
32
- }
33
-
34
- function emptyState(): WorkflowTrackerState {
35
- const phases = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, "pending"])) as Record<Phase, PhaseStatus>;
36
-
37
- const artifacts = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, null])) as Record<Phase, string | null>;
38
-
39
- const prompted = Object.fromEntries(WORKFLOW_PHASES.map((p) => [p, false])) as Record<Phase, boolean>;
40
-
41
- return { phases, currentPhase: null, artifacts, prompted };
42
- }
43
-
44
- export const WORKFLOW_TRACKER_ENTRY_TYPE = "workflow_tracker_state";
45
-
46
- export function parseSkillName(line: string): string | null {
47
- const slashMatch = line.match(/^\s*\/skill:([^\s]+)/);
48
- const xmlMatch = line.match(/<skill\s+name="([^"]+)"/);
49
- return slashMatch?.[1] ?? xmlMatch?.[1] ?? null;
50
- }
51
-
52
- export const SKILL_TO_PHASE: Record<string, Phase> = {
53
- brainstorming: "brainstorm",
54
- "writing-plans": "plan",
55
- "using-git-worktrees": "plan", // pre-execute worktree setup belongs to plan
56
- "executing-tasks": "execute",
57
- "systematic-debugging": "execute", // used within execute phase
58
- "dispatching-parallel-agents": "execute", // used within execute phase
59
- "test-driven-development": "execute", // makes TDD skill phase-aware
60
- "receiving-code-review": "finalize", // post-PR external review
61
- };
62
-
63
- export function resolveSkillPhase(skill: string, state: WorkflowTrackerState | null | undefined): Phase | null {
64
- if (skill === "executing-tasks") {
65
- if (state?.currentPhase === "finalize" || state?.phases.execute === "complete") {
66
- return "finalize";
67
- }
68
- return "execute";
69
- }
70
-
71
- return SKILL_TO_PHASE[skill] ?? null;
72
- }
73
-
74
- const PLANS_DIR_RE = /^docs\/plans\//;
75
- const DESIGN_RE = /-design\.md$/;
76
- const IMPLEMENTATION_RE = /-implementation\.md$/;
77
-
78
- export class WorkflowTracker {
79
- private state: WorkflowTrackerState = emptyState();
80
-
81
- getState(): WorkflowTrackerState {
82
- return cloneState(this.state);
83
- }
84
-
85
- setState(state: WorkflowTrackerState): void {
86
- this.state = cloneState(state);
87
- }
88
-
89
- reset(): void {
90
- this.state = emptyState();
91
- }
92
-
93
- advanceTo(phase: Phase): boolean {
94
- const current = this.state.currentPhase;
95
- const nextIdx = WORKFLOW_PHASES.indexOf(phase);
96
-
97
- if (current) {
98
- const curIdx = WORKFLOW_PHASES.indexOf(current);
99
- if (nextIdx <= curIdx) {
100
- // Backward or same-phase navigation = new task. Reset everything.
101
- this.reset();
102
- // Fall through to activate the target phase below.
103
- } else {
104
- // Forward advance: auto-complete the current phase.
105
- if (this.state.phases[current] === "active") {
106
- this.state.phases[current] = "complete";
107
- }
108
- }
109
- }
110
-
111
- for (const p of WORKFLOW_PHASES) {
112
- if (p !== phase && this.state.phases[p] === "active") {
113
- this.state.phases[p] = "complete";
114
- }
115
- }
116
-
117
- this.state.currentPhase = phase;
118
- if (this.state.phases[phase] === "pending") {
119
- this.state.phases[phase] = "active";
120
- }
121
-
122
- return true;
123
- }
124
-
125
- skipPhase(phase: Phase): boolean {
126
- const status = this.state.phases[phase];
127
- if (status !== "pending" && status !== "active") return false;
128
- this.state.phases[phase] = "skipped";
129
- return true;
130
- }
131
-
132
- skipPhases(phases: Phase[]): boolean {
133
- let changed = false;
134
- for (const p of phases) changed = this.skipPhase(p) || changed;
135
- return changed;
136
- }
137
-
138
- completeCurrent(): boolean {
139
- const phase = this.state.currentPhase;
140
- if (!phase) return false;
141
- if (this.state.phases[phase] === "complete") return false;
142
- this.state.phases[phase] = "complete";
143
- return true;
144
- }
145
-
146
- recordArtifact(phase: Phase, path: string): boolean {
147
- if (this.state.artifacts[phase] === path) return false;
148
- this.state.artifacts[phase] = path;
149
- return true;
150
- }
151
-
152
- markPrompted(phase: Phase): boolean {
153
- if (this.state.prompted[phase]) return false;
154
- this.state.prompted[phase] = true;
155
- return true;
156
- }
157
-
158
- onInputText(text: string): boolean {
159
- const lines = text.split(/\r?\n/);
160
- let changed = false;
161
-
162
- for (const line of lines) {
163
- const skill = parseSkillName(line);
164
- if (!skill) continue;
165
- const phase = resolveSkillPhase(skill, this.state);
166
- if (!phase) continue;
167
- // Guard against backward navigation: skills shared across phases (e.g. executing-tasks
168
- // covers both execute and finalize) must not reset state when re-invoked in a later phase.
169
- const currentIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
170
- const targetIdx = WORKFLOW_PHASES.indexOf(phase);
171
- if (targetIdx <= currentIdx) continue;
172
- if (this.advanceTo(phase)) changed = true;
173
- }
174
-
175
- return changed;
176
- }
177
-
178
- onSkillFileRead(path: string): boolean {
179
- const match = path.match(/\/skills\/([^/]+)\/SKILL\.md$/);
180
- if (!match) return false;
181
- const phase = resolveSkillPhase(match[1], this.state);
182
- if (!phase) return false;
183
- // Guard against backward navigation: some skills (e.g. executing-tasks) serve
184
- // multiple phases. Re-reading their SKILL.md during a later phase (e.g. finalize)
185
- // must not reset workflow state. Rely on plan_tracker init or explicit /workflow-reset
186
- // to restart from scratch.
187
- const currentIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
188
- const targetIdx = WORKFLOW_PHASES.indexOf(phase);
189
- if (targetIdx <= currentIdx) return false;
190
- return this.advanceTo(phase);
191
- }
192
-
193
- onFileWritten(path: string): boolean {
194
- if (!PLANS_DIR_RE.test(path)) return false;
195
-
196
- if (DESIGN_RE.test(path)) {
197
- // Only advance if we haven't already passed the brainstorm phase.
198
- // Writing a design doc during plan/execute/finalize (e.g., updating
199
- // the plan) must NOT reset workflow state.
200
- const curIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
201
- if (curIdx > WORKFLOW_PHASES.indexOf("brainstorm")) {
202
- return this.recordArtifact("brainstorm", path);
203
- }
204
- let changed = false;
205
- changed = this.recordArtifact("brainstorm", path) || changed;
206
- // Activating and immediately completing: the design doc is the
207
- // deliverable that signals brainstorm is done. Do NOT mark prompted
208
- // so the agent_end boundary prompt still fires to offer session handoff.
209
- changed = this.advanceTo("brainstorm") || changed;
210
- changed = this.completeCurrent() || changed;
211
- return changed;
212
- }
213
-
214
- if (IMPLEMENTATION_RE.test(path)) {
215
- // Only advance if we haven't already passed the plan phase.
216
- const curIdx = this.state.currentPhase ? WORKFLOW_PHASES.indexOf(this.state.currentPhase) : -1;
217
- if (curIdx > WORKFLOW_PHASES.indexOf("plan")) {
218
- return this.recordArtifact("plan", path);
219
- }
220
- let changed = false;
221
- changed = this.recordArtifact("plan", path) || changed;
222
- // Activating and immediately completing: the implementation plan
223
- // is the deliverable that signals plan phase is done. Do NOT mark
224
- // prompted so the agent_end boundary prompt still fires.
225
- changed = this.advanceTo("plan") || changed;
226
- changed = this.completeCurrent() || changed;
227
- return changed;
228
- }
229
-
230
- return false;
231
- }
232
-
233
- onPlanTrackerInit(): boolean {
234
- return this.advanceTo("execute");
235
- }
236
-
237
- static reconstructFromBranch(branch: SessionEntry[]): WorkflowTrackerState | null {
238
- let last: WorkflowTrackerState | null = null;
239
-
240
- for (const entry of branch) {
241
- if (entry.type !== "custom") continue;
242
- // biome-ignore lint/suspicious/noExplicitAny: pi SDK session entry type
243
- if ((entry as any).customType !== WORKFLOW_TRACKER_ENTRY_TYPE) continue;
244
- // biome-ignore lint/suspicious/noExplicitAny: pi SDK session entry type
245
- const data = (entry as any).data as WorkflowTrackerState | undefined;
246
- if (data && typeof data === "object") {
247
- last = cloneState(data);
248
- }
249
- }
250
-
251
- return last;
252
- }
253
- }
@@ -1,55 +0,0 @@
1
- import type { Phase, TransitionBoundary } from "./workflow-tracker";
2
-
3
- export type TransitionChoice = "next" | "fresh" | "skip" | "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
- export function getTransitionPrompt(boundary: TransitionBoundary, artifactPath: string | null): TransitionPrompt {
21
- switch (boundary) {
22
- case "design_committed":
23
- return {
24
- boundary,
25
- title: "Design committed. What next?",
26
- nextPhase: "plan",
27
- artifactPath,
28
- options: BASE_OPTIONS,
29
- };
30
- case "plan_ready":
31
- return {
32
- boundary,
33
- title: "Plan ready. What next?",
34
- nextPhase: "execute",
35
- artifactPath,
36
- options: BASE_OPTIONS,
37
- };
38
- case "execution_complete":
39
- return {
40
- boundary,
41
- title: "All tasks complete. What next?",
42
- nextPhase: "finalize",
43
- artifactPath,
44
- options: BASE_OPTIONS,
45
- };
46
- default:
47
- return {
48
- boundary,
49
- title: "What next?",
50
- nextPhase: "plan",
51
- artifactPath,
52
- options: BASE_OPTIONS,
53
- };
54
- }
55
- }