@tianhai/pi-workflow-kit 0.4.1 → 0.5.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 +29 -5
- package/docs/developer-usage-guide.md +3 -4
- package/docs/plans/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-design.md +56 -0
- package/docs/plans/completed/2026-04-09-cleanup-legacy-state-and-enforce-think-phases-implementation.md +196 -0
- package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-design.md +185 -0
- package/docs/plans/completed/2026-04-09-workflow-next-autocomplete-implementation.md +334 -0
- package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-design.md +251 -0
- package/docs/plans/completed/2026-04-09-workflow-next-handoff-state-implementation.md +253 -0
- package/extensions/constants.ts +6 -0
- package/extensions/plan-tracker.ts +7 -1
- package/extensions/subagent/index.ts +73 -8
- package/extensions/workflow-monitor/workflow-next-completions.ts +68 -0
- package/extensions/workflow-monitor/workflow-next-state.ts +112 -0
- package/extensions/workflow-monitor/workflow-tracker.ts +28 -6
- package/extensions/workflow-monitor.ts +64 -77
- package/package.json +1 -1
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# /workflow-next Autocomplete Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **REQUIRED SUB-SKILL:** Use the executing-tasks skill to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add argument autocomplete to `/workflow-next` for workflow phases and workflow-specific plan artifacts without changing command execution, validation, or session creation behavior.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Add a small stateless helper module under `extensions/workflow-monitor/` that parses the raw argument prefix and returns `AutocompleteItem[] | null` from pi's `getArgumentCompletions` hook. Keep `extensions/workflow-monitor.ts` as the command registration entry point and leave the existing `/workflow-next` handler logic unchanged so autocomplete stays UX-only.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, Node.js `fs`/`path`, pi extension command API, Vitest
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Reference design artifact: `docs/plans/2026-04-09-workflow-next-autocomplete-design.md`
|
|
14
|
+
|
|
15
|
+
### Task 1: Add phase autocomplete for `/workflow-next`
|
|
16
|
+
|
|
17
|
+
**Type:** code
|
|
18
|
+
**TDD scenario:** New feature — full TDD cycle
|
|
19
|
+
|
|
20
|
+
**Files:**
|
|
21
|
+
- Create: `extensions/workflow-monitor/workflow-next-completions.ts`
|
|
22
|
+
- Modify: `extensions/workflow-monitor.ts:1-38`
|
|
23
|
+
- Modify: `extensions/workflow-monitor.ts:813-851`
|
|
24
|
+
- Test: `tests/extension/workflow-monitor/workflow-next-command.test.ts:1-102`
|
|
25
|
+
|
|
26
|
+
**Step 1: Write the failing test**
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
function getWorkflowNextCommand() {
|
|
30
|
+
let command: any;
|
|
31
|
+
const fakePi: any = {
|
|
32
|
+
on() {},
|
|
33
|
+
registerTool() {},
|
|
34
|
+
appendEntry() {},
|
|
35
|
+
registerCommand(name: string, opts: any) {
|
|
36
|
+
if (name === "workflow-next") command = opts;
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
workflowMonitorExtension(fakePi);
|
|
41
|
+
expect(command).toBeTruthy();
|
|
42
|
+
return command;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test("returns all workflow phases when no args are typed", async () => {
|
|
46
|
+
const command = getWorkflowNextCommand();
|
|
47
|
+
const items = await command.getArgumentCompletions("");
|
|
48
|
+
expect(items).toEqual([
|
|
49
|
+
{ value: "brainstorm", label: "brainstorm" },
|
|
50
|
+
{ value: "plan", label: "plan" },
|
|
51
|
+
{ value: "execute", label: "execute" },
|
|
52
|
+
{ value: "finalize", label: "finalize" },
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("keeps suggesting phases until the first arg is an exact valid phase", async () => {
|
|
57
|
+
const command = getWorkflowNextCommand();
|
|
58
|
+
const items = await command.getArgumentCompletions("pla docs/plans/");
|
|
59
|
+
expect(items).toEqual([{ value: "plan", label: "plan" }]);
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Step 2: Run test to verify it fails**
|
|
64
|
+
|
|
65
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts -t "returns all workflow phases when no args are typed"`
|
|
66
|
+
Expected: FAIL with `command.getArgumentCompletions is not a function` or an equivalent assertion failure because `/workflow-next` does not expose completions yet.
|
|
67
|
+
|
|
68
|
+
**Step 3: Write minimal implementation**
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// extensions/workflow-monitor/workflow-next-completions.ts
|
|
72
|
+
import type { AutocompleteItem } from "@mariozechner/pi-tui";
|
|
73
|
+
|
|
74
|
+
const WORKFLOW_NEXT_PHASES = ["brainstorm", "plan", "execute", "finalize"] as const;
|
|
75
|
+
|
|
76
|
+
export async function getWorkflowNextCompletions(prefix: string): Promise<AutocompleteItem[] | null> {
|
|
77
|
+
const normalized = prefix.replace(/^\s+/, "");
|
|
78
|
+
const [firstToken = ""] = normalized.split(/\s+/, 1);
|
|
79
|
+
const completingFirstArg = normalized.length === 0 || !/\s/.test(normalized);
|
|
80
|
+
|
|
81
|
+
if (completingFirstArg || !WORKFLOW_NEXT_PHASES.includes(firstToken as (typeof WORKFLOW_NEXT_PHASES)[number])) {
|
|
82
|
+
const phasePrefix = completingFirstArg ? normalized : firstToken;
|
|
83
|
+
const items = WORKFLOW_NEXT_PHASES.filter((phase) => phase.startsWith(phasePrefix)).map((phase) => ({
|
|
84
|
+
value: phase,
|
|
85
|
+
label: phase,
|
|
86
|
+
}));
|
|
87
|
+
return items.length > 0 ? items : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// extensions/workflow-monitor.ts
|
|
94
|
+
pi.registerCommand("workflow-next", {
|
|
95
|
+
description: "Start a fresh session for the next workflow phase (optionally referencing an artifact path)",
|
|
96
|
+
getArgumentCompletions: getWorkflowNextCompletions,
|
|
97
|
+
async handler(args, ctx) {
|
|
98
|
+
// existing handler body stays unchanged
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Step 4: Run test to verify it passes**
|
|
104
|
+
|
|
105
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts -t "workflow phases"`
|
|
106
|
+
Expected: PASS for the new phase-autocomplete assertions and PASS for the existing kickoff/validation tests.
|
|
107
|
+
|
|
108
|
+
**Step 5: Commit**
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git add tests/extension/workflow-monitor/workflow-next-command.test.ts extensions/workflow-monitor.ts extensions/workflow-monitor/workflow-next-completions.ts
|
|
112
|
+
git commit -m "feat: add workflow-next phase autocomplete"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Task 2: Add `plan` artifact suggestions for design docs
|
|
116
|
+
|
|
117
|
+
**Type:** code
|
|
118
|
+
**TDD scenario:** New feature — full TDD cycle
|
|
119
|
+
|
|
120
|
+
**Files:**
|
|
121
|
+
- Modify: `extensions/workflow-monitor/workflow-next-completions.ts`
|
|
122
|
+
- Test: `tests/extension/workflow-monitor/workflow-next-command.test.ts:1-102`
|
|
123
|
+
|
|
124
|
+
**Step 1: Write the failing test**
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import * as fs from "node:fs";
|
|
128
|
+
import * as path from "node:path";
|
|
129
|
+
import { withTempCwd } from "./test-helpers";
|
|
130
|
+
|
|
131
|
+
test("suggests only design artifacts for plan phase", async () => {
|
|
132
|
+
const tempDir = withTempCwd();
|
|
133
|
+
const plansDir = path.join(tempDir, "docs", "plans");
|
|
134
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
135
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-design.md"), "");
|
|
136
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-implementation.md"), "");
|
|
137
|
+
|
|
138
|
+
const command = getWorkflowNextCommand();
|
|
139
|
+
const items = await command.getArgumentCompletions("plan ");
|
|
140
|
+
|
|
141
|
+
expect(items).toEqual([
|
|
142
|
+
{
|
|
143
|
+
value: "docs/plans/2026-04-09-alpha-design.md",
|
|
144
|
+
label: "docs/plans/2026-04-09-alpha-design.md",
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("filters plan artifact suggestions by typed prefix", async () => {
|
|
150
|
+
const tempDir = withTempCwd();
|
|
151
|
+
const plansDir = path.join(tempDir, "docs", "plans");
|
|
152
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
153
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-design.md"), "");
|
|
154
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-beta-design.md"), "");
|
|
155
|
+
|
|
156
|
+
const command = getWorkflowNextCommand();
|
|
157
|
+
const items = await command.getArgumentCompletions("plan docs/plans/2026-04-09-al");
|
|
158
|
+
|
|
159
|
+
expect(items).toEqual([
|
|
160
|
+
{
|
|
161
|
+
value: "docs/plans/2026-04-09-alpha-design.md",
|
|
162
|
+
label: "docs/plans/2026-04-09-alpha-design.md",
|
|
163
|
+
},
|
|
164
|
+
]);
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Step 2: Run test to verify it fails**
|
|
169
|
+
|
|
170
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts -t "design artifacts for plan phase"`
|
|
171
|
+
Expected: FAIL because phase completion works, but second-argument artifact completion still returns `null`.
|
|
172
|
+
|
|
173
|
+
**Step 3: Write minimal implementation**
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import * as fs from "node:fs";
|
|
177
|
+
import * as path from "node:path";
|
|
178
|
+
import type { AutocompleteItem } from "@mariozechner/pi-tui";
|
|
179
|
+
|
|
180
|
+
function listPlanArtifacts(suffix: string, typedPrefix: string): AutocompleteItem[] | null {
|
|
181
|
+
const plansDir = path.join(process.cwd(), "docs", "plans");
|
|
182
|
+
if (!fs.existsSync(plansDir)) return null;
|
|
183
|
+
|
|
184
|
+
const items = fs
|
|
185
|
+
.readdirSync(plansDir)
|
|
186
|
+
.filter((name) => name.endsWith(suffix))
|
|
187
|
+
.map((name) => path.join("docs", "plans", name))
|
|
188
|
+
.filter((relPath) => relPath.startsWith(typedPrefix))
|
|
189
|
+
.map((relPath) => ({ value: relPath, label: relPath }));
|
|
190
|
+
|
|
191
|
+
return items.length > 0 ? items : null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function getWorkflowNextCompletions(prefix: string): Promise<AutocompleteItem[] | null> {
|
|
195
|
+
// keep task-1 phase behavior
|
|
196
|
+
const match = prefix.replace(/^\s+/, "").match(/^(\S+)(?:\s+(.*))?$/);
|
|
197
|
+
const phase = match?.[1] ?? "";
|
|
198
|
+
const artifactPrefix = match?.[2] ?? "";
|
|
199
|
+
const startingSecondArg = /\s$/.test(prefix) || artifactPrefix.length > 0;
|
|
200
|
+
|
|
201
|
+
if (phase === "plan" && startingSecondArg) {
|
|
202
|
+
return listPlanArtifacts("-design.md", artifactPrefix);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return previousPhaseLogic(prefix);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Step 4: Run test to verify it passes**
|
|
210
|
+
|
|
211
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts -t "plan"`
|
|
212
|
+
Expected: PASS for the new `plan` artifact tests and PASS for the phase completion tests from Task 1.
|
|
213
|
+
|
|
214
|
+
**Step 5: Commit**
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
git add tests/extension/workflow-monitor/workflow-next-command.test.ts extensions/workflow-monitor/workflow-next-completions.ts
|
|
218
|
+
git commit -m "feat: add workflow-next plan artifact autocomplete"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Task 3: Complete execute/finalize artifact mapping and quiet failure behavior
|
|
222
|
+
|
|
223
|
+
**Type:** code
|
|
224
|
+
**TDD scenario:** New feature — full TDD cycle
|
|
225
|
+
|
|
226
|
+
**Files:**
|
|
227
|
+
- Modify: `extensions/workflow-monitor/workflow-next-completions.ts`
|
|
228
|
+
- Test: `tests/extension/workflow-monitor/workflow-next-command.test.ts:1-102`
|
|
229
|
+
|
|
230
|
+
**Step 1: Write the failing test**
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
test("suggests only implementation artifacts for execute and finalize", async () => {
|
|
234
|
+
const tempDir = withTempCwd();
|
|
235
|
+
const plansDir = path.join(tempDir, "docs", "plans");
|
|
236
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
237
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-design.md"), "");
|
|
238
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-implementation.md"), "");
|
|
239
|
+
|
|
240
|
+
const command = getWorkflowNextCommand();
|
|
241
|
+
|
|
242
|
+
await expect(command.getArgumentCompletions("execute ")).resolves.toEqual([
|
|
243
|
+
{
|
|
244
|
+
value: "docs/plans/2026-04-09-alpha-implementation.md",
|
|
245
|
+
label: "docs/plans/2026-04-09-alpha-implementation.md",
|
|
246
|
+
},
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
await expect(command.getArgumentCompletions("finalize ")).resolves.toEqual([
|
|
250
|
+
{
|
|
251
|
+
value: "docs/plans/2026-04-09-alpha-implementation.md",
|
|
252
|
+
label: "docs/plans/2026-04-09-alpha-implementation.md",
|
|
253
|
+
},
|
|
254
|
+
]);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("returns null for brainstorm artifact completion", async () => {
|
|
258
|
+
const command = getWorkflowNextCommand();
|
|
259
|
+
await expect(command.getArgumentCompletions("brainstorm ")).resolves.toBeNull();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("returns null when docs/plans is missing or has no matching files", async () => {
|
|
263
|
+
withTempCwd();
|
|
264
|
+
const command = getWorkflowNextCommand();
|
|
265
|
+
await expect(command.getArgumentCompletions("execute ")).resolves.toBeNull();
|
|
266
|
+
|
|
267
|
+
const plansDir = path.join(process.cwd(), "docs", "plans");
|
|
268
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
269
|
+
fs.writeFileSync(path.join(plansDir, "2026-04-09-alpha-design.md"), "");
|
|
270
|
+
await expect(command.getArgumentCompletions("execute ")).resolves.toBeNull();
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Step 2: Run test to verify it fails**
|
|
275
|
+
|
|
276
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts -t "implementation artifacts for execute and finalize"`
|
|
277
|
+
Expected: FAIL because only `plan` artifact completion exists and `execute`/`finalize` still return `null`.
|
|
278
|
+
|
|
279
|
+
**Step 3: Write minimal implementation**
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
const ARTIFACT_SUFFIX_BY_PHASE = {
|
|
283
|
+
brainstorm: null,
|
|
284
|
+
plan: "-design.md",
|
|
285
|
+
execute: "-implementation.md",
|
|
286
|
+
finalize: "-implementation.md",
|
|
287
|
+
} as const;
|
|
288
|
+
|
|
289
|
+
function listArtifactsForPhase(phase: keyof typeof ARTIFACT_SUFFIX_BY_PHASE, typedPrefix: string) {
|
|
290
|
+
const suffix = ARTIFACT_SUFFIX_BY_PHASE[phase];
|
|
291
|
+
if (!suffix) return null;
|
|
292
|
+
|
|
293
|
+
const plansDir = path.join(process.cwd(), "docs", "plans");
|
|
294
|
+
if (!fs.existsSync(plansDir)) return null;
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const items = fs
|
|
298
|
+
.readdirSync(plansDir)
|
|
299
|
+
.filter((name) => name.endsWith(suffix))
|
|
300
|
+
.map((name) => path.join("docs", "plans", name))
|
|
301
|
+
.filter((relPath) => relPath.startsWith(typedPrefix))
|
|
302
|
+
.map((relPath) => ({ value: relPath, label: relPath }));
|
|
303
|
+
|
|
304
|
+
return items.length > 0 ? items : null;
|
|
305
|
+
} catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function getWorkflowNextCompletions(prefix: string): Promise<AutocompleteItem[] | null> {
|
|
311
|
+
// keep task-1 phase completion logic for empty / partial / invalid first tokens
|
|
312
|
+
// switch to artifact mode only after an exact valid phase plus second-arg context
|
|
313
|
+
if (phaseIsExactlyValid && startingSecondArg) {
|
|
314
|
+
return listArtifactsForPhase(phase, artifactPrefix);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return phaseSuggestionsOrNull;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Step 4: Run test to verify it passes**
|
|
322
|
+
|
|
323
|
+
Run: `npx vitest run tests/extension/workflow-monitor/workflow-next-command.test.ts`
|
|
324
|
+
Expected: PASS for all `/workflow-next` tests, including kickoff behavior, invalid-phase validation, phase autocomplete, artifact autocomplete, and quiet-null cases.
|
|
325
|
+
|
|
326
|
+
Then run: `npm test`
|
|
327
|
+
Expected: PASS across the full Vitest suite with no regressions in the workflow monitor extension.
|
|
328
|
+
|
|
329
|
+
**Step 5: Commit**
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
git add tests/extension/workflow-monitor/workflow-next-command.test.ts extensions/workflow-monitor/workflow-next-completions.ts
|
|
333
|
+
git commit -m "feat: finish workflow-next artifact autocomplete"
|
|
334
|
+
```
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# /workflow-next Handoff State Design
|
|
2
|
+
|
|
3
|
+
Date: 2026-04-09
|
|
4
|
+
Status: approved
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
Fix `/workflow-next` so that, for the same feature, a fresh handoff session preserves prior completed workflow history instead of showing earlier phases as `pending` again.
|
|
9
|
+
|
|
10
|
+
The command should become a strict forward-only handoff for the immediate next phase. It must not allow same-phase handoff, backward handoff, or direct jumps across multiple phases.
|
|
11
|
+
|
|
12
|
+
## Problem
|
|
13
|
+
|
|
14
|
+
Today `/workflow-next` creates a new session and pre-fills the editor with the next skill, but it does not explicitly seed the new session with a derived workflow state for the same feature.
|
|
15
|
+
|
|
16
|
+
As a result, the new session may start from an empty workflow tracker state:
|
|
17
|
+
|
|
18
|
+
- `brainstorm: pending`
|
|
19
|
+
- `plan: pending`
|
|
20
|
+
- `execute: pending`
|
|
21
|
+
- `finalize: pending`
|
|
22
|
+
|
|
23
|
+
When the prefilled skill is then detected, the tracker advances only to the requested phase. Earlier phases remain `pending`, even when they were already completed in the previous session.
|
|
24
|
+
|
|
25
|
+
## Goals
|
|
26
|
+
|
|
27
|
+
- Preserve prior completed workflow phases across `/workflow-next` handoff for the same feature.
|
|
28
|
+
- Preserve earlier-phase artifact paths and prompted flags.
|
|
29
|
+
- Keep TDD, debug, and verification state fresh in the new session.
|
|
30
|
+
- Make `/workflow-next` a strict immediate-next handoff command.
|
|
31
|
+
- Reject invalid handoffs before creating a new session.
|
|
32
|
+
- Rename the persisted state file to reflect current naming.
|
|
33
|
+
|
|
34
|
+
## Non-goals
|
|
35
|
+
|
|
36
|
+
- Allow arbitrary phase switching.
|
|
37
|
+
- Allow skipping phases through `/workflow-next`.
|
|
38
|
+
- Carry over TDD/debug/verification runtime state into the new session.
|
|
39
|
+
- Change the existing slash-command UX beyond stricter validation and correct state seeding.
|
|
40
|
+
|
|
41
|
+
## Decisions
|
|
42
|
+
|
|
43
|
+
### 1. Preserve derived workflow-only state
|
|
44
|
+
|
|
45
|
+
When `/workflow-next <phase>` is used for the same feature, the new session will receive a derived workflow snapshot.
|
|
46
|
+
|
|
47
|
+
Rules:
|
|
48
|
+
|
|
49
|
+
- all phases before the requested phase are `complete`
|
|
50
|
+
- the requested phase is `active`
|
|
51
|
+
- all phases after the requested phase are `pending`
|
|
52
|
+
- `currentPhase` is the requested phase
|
|
53
|
+
- earlier-phase artifacts are preserved
|
|
54
|
+
- earlier-phase prompted flags are preserved
|
|
55
|
+
|
|
56
|
+
This snapshot is derived from the current workflow state, not reconstructed from filenames alone.
|
|
57
|
+
|
|
58
|
+
### 2. Do not preserve execution-local monitor state
|
|
59
|
+
|
|
60
|
+
The new session must start with fresh:
|
|
61
|
+
|
|
62
|
+
- TDD state
|
|
63
|
+
- debug state
|
|
64
|
+
- verification state
|
|
65
|
+
|
|
66
|
+
Only workflow lineage is preserved.
|
|
67
|
+
|
|
68
|
+
### 3. `/workflow-next` is immediate-next only
|
|
69
|
+
|
|
70
|
+
Allowed transitions:
|
|
71
|
+
|
|
72
|
+
- `brainstorm -> plan`
|
|
73
|
+
- `plan -> execute`
|
|
74
|
+
- `execute -> finalize`
|
|
75
|
+
|
|
76
|
+
Only when the current phase status is exactly `complete`.
|
|
77
|
+
|
|
78
|
+
Disallowed transitions:
|
|
79
|
+
|
|
80
|
+
- same-phase handoff
|
|
81
|
+
- backward handoff
|
|
82
|
+
- direct jumps such as `brainstorm -> execute` or `plan -> finalize`
|
|
83
|
+
- moving forward when the current phase is `pending`, `active`, or `skipped`
|
|
84
|
+
|
|
85
|
+
Skipped phases do not qualify for `/workflow-next`.
|
|
86
|
+
|
|
87
|
+
### 4. Hard-fail invalid handoffs
|
|
88
|
+
|
|
89
|
+
Invalid requests must show an error and stop before opening a new session.
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
|
|
93
|
+
- `Cannot hand off to execute from brainstorm. /workflow-next only supports the immediate next phase.`
|
|
94
|
+
- `Cannot hand off to plan because brainstorm is not complete.`
|
|
95
|
+
- `Cannot hand off to plan from plan. Use /workflow-reset for a new task or continue in this session.`
|
|
96
|
+
|
|
97
|
+
### 5. Rename persisted state file
|
|
98
|
+
|
|
99
|
+
Rename the local state file from:
|
|
100
|
+
|
|
101
|
+
- `.pi/superpowers-state.json`
|
|
102
|
+
|
|
103
|
+
To:
|
|
104
|
+
|
|
105
|
+
- `.pi/workflow-kit-state.json`
|
|
106
|
+
|
|
107
|
+
Migration behavior:
|
|
108
|
+
|
|
109
|
+
- reconstruction first checks `.pi/workflow-kit-state.json`
|
|
110
|
+
- if absent, it falls back to `.pi/superpowers-state.json`
|
|
111
|
+
- persistence writes only `.pi/workflow-kit-state.json`
|
|
112
|
+
|
|
113
|
+
This preserves compatibility for existing users while moving to clearer naming.
|
|
114
|
+
|
|
115
|
+
## Proposed implementation
|
|
116
|
+
|
|
117
|
+
## Helper module
|
|
118
|
+
|
|
119
|
+
Add a small helper module under `extensions/workflow-monitor/`, for example:
|
|
120
|
+
|
|
121
|
+
- `workflow-next-state.ts`
|
|
122
|
+
|
|
123
|
+
Responsibilities:
|
|
124
|
+
|
|
125
|
+
### `validateNextWorkflowPhase(currentState, requestedPhase)`
|
|
126
|
+
|
|
127
|
+
Input:
|
|
128
|
+
|
|
129
|
+
- current workflow state
|
|
130
|
+
- requested target phase
|
|
131
|
+
|
|
132
|
+
Behavior:
|
|
133
|
+
|
|
134
|
+
- require a current phase to exist
|
|
135
|
+
- require the requested phase to be the immediate next phase
|
|
136
|
+
- require `currentState.phases[currentState.currentPhase] === "complete"`
|
|
137
|
+
- return either success or a precise error message
|
|
138
|
+
|
|
139
|
+
### `deriveWorkflowHandoffState(currentState, requestedPhase)`
|
|
140
|
+
|
|
141
|
+
Input:
|
|
142
|
+
|
|
143
|
+
- current workflow state
|
|
144
|
+
- requested target phase
|
|
145
|
+
|
|
146
|
+
Behavior:
|
|
147
|
+
|
|
148
|
+
- produce a new workflow snapshot for the handoff session
|
|
149
|
+
- mark prior phases `complete`
|
|
150
|
+
- mark the requested phase `active`
|
|
151
|
+
- leave later phases `pending`
|
|
152
|
+
- preserve artifacts and prompted flags for earlier phases
|
|
153
|
+
- set `currentPhase` to the requested phase
|
|
154
|
+
|
|
155
|
+
## `/workflow-next` handler changes
|
|
156
|
+
|
|
157
|
+
Update `extensions/workflow-monitor.ts` so the handler:
|
|
158
|
+
|
|
159
|
+
1. parses `phase` and optional artifact path
|
|
160
|
+
2. validates the phase value against the known set
|
|
161
|
+
3. reads `handler.getWorkflowState()`
|
|
162
|
+
4. calls `validateNextWorkflowPhase(...)`
|
|
163
|
+
5. if invalid, notifies with an error and returns
|
|
164
|
+
6. creates the new session
|
|
165
|
+
7. seeds the new session with a fresh snapshot containing:
|
|
166
|
+
- derived `workflow`
|
|
167
|
+
- default `tdd`
|
|
168
|
+
- default `debug`
|
|
169
|
+
- default `verification`
|
|
170
|
+
8. pre-fills the editor text as today
|
|
171
|
+
|
|
172
|
+
The prefilled text remains useful, but the session no longer depends on skill detection to reconstruct earlier history.
|
|
173
|
+
|
|
174
|
+
## Persistence flow
|
|
175
|
+
|
|
176
|
+
### Current
|
|
177
|
+
|
|
178
|
+
State is persisted in two places:
|
|
179
|
+
|
|
180
|
+
- session custom entry (`superpowers_state`)
|
|
181
|
+
- local JSON file under `.pi/`
|
|
182
|
+
|
|
183
|
+
### New behavior
|
|
184
|
+
|
|
185
|
+
Keep the same general persistence model, but:
|
|
186
|
+
|
|
187
|
+
- continue using the full snapshot shape for session persistence
|
|
188
|
+
- write the renamed local file
|
|
189
|
+
- allow reconstruction from either the new or legacy filename
|
|
190
|
+
|
|
191
|
+
## Testing
|
|
192
|
+
|
|
193
|
+
Add or update tests for:
|
|
194
|
+
|
|
195
|
+
### Workflow-next validation
|
|
196
|
+
|
|
197
|
+
- allows `brainstorm -> plan` only when `brainstorm` is `complete`
|
|
198
|
+
- allows `plan -> execute` only when `plan` is `complete`
|
|
199
|
+
- allows `execute -> finalize` only when `execute` is `complete`
|
|
200
|
+
- rejects same-phase handoff
|
|
201
|
+
- rejects backward handoff
|
|
202
|
+
- rejects direct jumps
|
|
203
|
+
- rejects handoff when current phase is `active`
|
|
204
|
+
- rejects handoff when current phase is `pending`
|
|
205
|
+
- rejects handoff when current phase is `skipped`
|
|
206
|
+
|
|
207
|
+
### Derived state
|
|
208
|
+
|
|
209
|
+
- preserves prior completed phases in the new session
|
|
210
|
+
- preserves artifacts for earlier phases
|
|
211
|
+
- preserves prompted flags for earlier phases
|
|
212
|
+
- marks requested phase `active`
|
|
213
|
+
- leaves later phases `pending`
|
|
214
|
+
- resets TDD/debug/verification state in the new session
|
|
215
|
+
|
|
216
|
+
### State-file migration
|
|
217
|
+
|
|
218
|
+
- `getStateFilePath()` returns `.pi/workflow-kit-state.json`
|
|
219
|
+
- reconstruction reads the new file when present
|
|
220
|
+
- reconstruction falls back to `.pi/superpowers-state.json` when the new file is absent
|
|
221
|
+
- persistence writes only the new filename
|
|
222
|
+
|
|
223
|
+
## Risks and mitigations
|
|
224
|
+
|
|
225
|
+
### Risk: session seeding API constraints
|
|
226
|
+
|
|
227
|
+
Depending on the pi session API, the new session may not directly expose a way to append a custom state entry before user submission.
|
|
228
|
+
|
|
229
|
+
Mitigation:
|
|
230
|
+
|
|
231
|
+
- if direct session seeding is supported, use it
|
|
232
|
+
- otherwise encode the derived workflow state into the handoff path using the existing persistence/reconstruction mechanism with minimal, well-scoped changes
|
|
233
|
+
- verify behavior with an integration test around `/workflow-next`
|
|
234
|
+
|
|
235
|
+
### Risk: invalidating existing expectations
|
|
236
|
+
|
|
237
|
+
Tests and current behavior explicitly allow advancing to a later phase from empty state without backfilling earlier phases.
|
|
238
|
+
|
|
239
|
+
Mitigation:
|
|
240
|
+
|
|
241
|
+
- limit the stricter semantics to `/workflow-next`
|
|
242
|
+
- keep generic workflow tracker behavior unchanged unless a separate design chooses otherwise
|
|
243
|
+
|
|
244
|
+
## Acceptance criteria
|
|
245
|
+
|
|
246
|
+
- Using `/workflow-next` for the immediate next phase of the same feature preserves prior completed phases in the fresh session.
|
|
247
|
+
- Earlier completed phases do not regress to `pending` in the new session.
|
|
248
|
+
- Artifacts and prompted flags for earlier phases are preserved.
|
|
249
|
+
- TDD/debug/verification state is fresh in the new session.
|
|
250
|
+
- Same-phase, backward, and direct-jump handoffs are rejected.
|
|
251
|
+
- The local state file is renamed to `.pi/workflow-kit-state.json` with fallback support for the legacy filename.
|