work-kit-cli 0.2.8 → 0.3.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 (86) hide show
  1. package/README.md +13 -13
  2. package/cli/src/commands/bootstrap.ts +39 -13
  3. package/cli/src/commands/cancel.ts +1 -16
  4. package/cli/src/commands/complete.ts +92 -98
  5. package/cli/src/commands/completions.ts +2 -2
  6. package/cli/src/commands/doctor.ts +1 -1
  7. package/cli/src/commands/init.ts +40 -32
  8. package/cli/src/commands/loopback.ts +8 -11
  9. package/cli/src/commands/next.ts +64 -51
  10. package/cli/src/commands/pause-resume.test.ts +142 -0
  11. package/cli/src/commands/pause.ts +34 -0
  12. package/cli/src/commands/report.ts +217 -0
  13. package/cli/src/commands/resume.ts +38 -0
  14. package/cli/src/commands/setup.ts +136 -0
  15. package/cli/src/commands/status.ts +6 -6
  16. package/cli/src/commands/uninstall.ts +8 -3
  17. package/cli/src/commands/workflow.ts +27 -27
  18. package/cli/src/config/agent-map.ts +9 -9
  19. package/cli/src/config/constants.ts +44 -0
  20. package/cli/src/config/loopback-routes.ts +13 -13
  21. package/cli/src/config/project-config.test.ts +127 -0
  22. package/cli/src/config/project-config.ts +106 -0
  23. package/cli/src/config/{phases.ts → workflow.ts} +40 -23
  24. package/cli/src/context/prompt-builder.ts +10 -9
  25. package/cli/src/index.ts +63 -7
  26. package/cli/src/observer/data.ts +64 -56
  27. package/cli/src/observer/renderer.ts +101 -79
  28. package/cli/src/state/helpers.test.ts +28 -28
  29. package/cli/src/state/helpers.ts +37 -25
  30. package/cli/src/state/schema.ts +88 -45
  31. package/cli/src/state/store.ts +92 -7
  32. package/cli/src/state/validators.test.ts +13 -13
  33. package/cli/src/state/validators.ts +3 -4
  34. package/cli/src/utils/colors.ts +2 -0
  35. package/cli/src/utils/json.ts +20 -0
  36. package/cli/src/utils/time.ts +27 -0
  37. package/cli/src/{engine → workflow}/loopbacks.test.ts +2 -2
  38. package/cli/src/workflow/loopbacks.ts +42 -0
  39. package/cli/src/workflow/parallel.ts +64 -0
  40. package/cli/src/workflow/transitions.test.ts +129 -0
  41. package/cli/src/{engine → workflow}/transitions.ts +18 -22
  42. package/package.json +2 -2
  43. package/skills/auto-kit/SKILL.md +22 -22
  44. package/skills/cancel-kit/SKILL.md +4 -4
  45. package/skills/full-kit/SKILL.md +23 -23
  46. package/skills/pause-kit/SKILL.md +25 -0
  47. package/skills/resume-kit/SKILL.md +28 -0
  48. package/skills/wk-bootstrap/SKILL.md +5 -5
  49. package/skills/wk-build/SKILL.md +10 -10
  50. package/skills/wk-build/{stages → steps}/commit.md +1 -1
  51. package/skills/wk-build/{stages → steps}/core.md +3 -3
  52. package/skills/wk-build/{stages → steps}/integration.md +2 -2
  53. package/skills/wk-build/{stages → steps}/migration.md +1 -1
  54. package/skills/wk-build/{stages → steps}/red.md +1 -1
  55. package/skills/wk-build/{stages → steps}/refactor.md +1 -1
  56. package/skills/wk-build/{stages → steps}/setup.md +1 -1
  57. package/skills/wk-build/{stages → steps}/ui.md +1 -1
  58. package/skills/wk-deploy/SKILL.md +6 -6
  59. package/skills/wk-deploy/{stages → steps}/merge.md +1 -1
  60. package/skills/wk-deploy/{stages → steps}/monitor.md +1 -1
  61. package/skills/wk-deploy/{stages → steps}/remediate.md +1 -1
  62. package/skills/wk-plan/SKILL.md +13 -13
  63. package/skills/wk-plan/{stages → steps}/architecture.md +1 -1
  64. package/skills/wk-plan/{stages → steps}/audit.md +2 -2
  65. package/skills/wk-plan/{stages → steps}/blueprint.md +2 -2
  66. package/skills/wk-plan/{stages → steps}/clarify.md +1 -1
  67. package/skills/wk-plan/{stages → steps}/investigate.md +1 -1
  68. package/skills/wk-plan/{stages → steps}/scope.md +1 -1
  69. package/skills/wk-plan/{stages → steps}/sketch.md +1 -1
  70. package/skills/wk-plan/{stages → steps}/ux-flow.md +1 -1
  71. package/skills/wk-review/SKILL.md +10 -10
  72. package/skills/wk-review/{stages → steps}/compliance.md +1 -1
  73. package/skills/wk-review/{stages → steps}/handoff.md +2 -2
  74. package/skills/wk-review/{stages → steps}/performance.md +1 -1
  75. package/skills/wk-review/{stages → steps}/security.md +1 -1
  76. package/skills/wk-review/{stages → steps}/self-review.md +1 -1
  77. package/skills/wk-test/SKILL.md +8 -8
  78. package/skills/wk-test/{stages → steps}/e2e.md +1 -1
  79. package/skills/wk-test/{stages → steps}/validate.md +1 -1
  80. package/skills/wk-test/{stages → steps}/verify.md +1 -1
  81. package/skills/wk-wrap-up/SKILL.md +6 -5
  82. package/skills/wk-wrap-up/steps/summary.md +86 -0
  83. package/cli/src/engine/loopbacks.ts +0 -32
  84. package/cli/src/engine/parallel.ts +0 -60
  85. package/cli/src/engine/transitions.test.ts +0 -129
  86. /package/cli/src/{engine/phases.ts → workflow/gates.ts} +0 -0
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: review
3
- description: "Run the Review phase — 5 sub-stages: Self-Review, Security, Performance, Compliance, Handoff."
3
+ description: "Run the Review phase — 5 steps: Self-Review, Security, Performance, Compliance, Handoff."
4
4
  user-invocable: false
5
5
  allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
6
6
  ---
7
7
 
8
8
  You are the **Senior Reviewer**. Perform multi-dimensional code review before the feature ships.
9
9
 
10
- ## Sub-stages (in order)
10
+ ## Steps (in order)
11
11
 
12
12
  1. **Self-Review** — Check your own diff for obvious issues
13
13
  2. **Security** — OWASP top 10 security review
@@ -17,11 +17,11 @@ You are the **Senior Reviewer**. Perform multi-dimensional code review before th
17
17
 
18
18
  ## Execution
19
19
 
20
- For each sub-stage:
21
- 1. Read the sub-stage file (e.g., `.claude/skills/wk-review/stages/self-review.md`)
20
+ For each step:
21
+ 1. Read the step file (e.g., `.claude/skills/wk-review/steps/self-review.md`)
22
22
  2. Follow its instructions — fix issues directly when possible
23
23
  3. Update `.work-kit/state.md` with findings
24
- 4. Proceed to next sub-stage
24
+ 4. Proceed to next step
25
25
 
26
26
  ## Key Principle
27
27
 
@@ -29,10 +29,10 @@ For each sub-stage:
29
29
 
30
30
  ## Recording
31
31
 
32
- Throughout every sub-stage, update the shared state.md sections:
32
+ Throughout every step, update the shared state.md sections:
33
33
 
34
34
  - **`## Decisions`** — If you make judgment calls during review (e.g., "accepted this deviation because..."), record them.
35
- - **`## Deviations`** — Compliance sub-stage will audit these. If you fix a deviation during review, note that it was resolved.
35
+ - **`## Deviations`** — Compliance step will audit these. If you fix a deviation during review, note that it was resolved.
36
36
 
37
37
  Review findings feed directly into the Handoff decision and the final work-kit log.
38
38
 
@@ -58,9 +58,9 @@ Agent: Compliance ──┘
58
58
  Each sub-agent receives:
59
59
  - The git diff (`git diff main...HEAD`)
60
60
  - The relevant Context Input sections
61
- - Its sub-stage skill file instructions
61
+ - Its step skill file instructions
62
62
 
63
- Each writes its own `### Review: <sub-stage>` section to state.md.
63
+ Each writes its own `### Review: <step>` section to state.md.
64
64
 
65
65
  **Handoff agent** reads all 4 review sections + Test: Final → makes the ship decision.
66
66
 
@@ -83,7 +83,7 @@ Each writes its own `### Review: <sub-stage>` section to state.md.
83
83
  - Approve without checking acceptance criteria status
84
84
  - Rubber-stamp without reading the diff ("looks good" is not a review)
85
85
  - Make changes_requested without specifying exactly what needs to change
86
- - Skip any of the 4 parallel review sub-stages
86
+ - Skip any of the 4 parallel review steps
87
87
 
88
88
  ## Final Output
89
89
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review sub-stage: Compare final code against Blueprint."
2
+ description: "Review step: Compare final code against Blueprint."
3
3
  ---
4
4
 
5
5
  # Compliance Review
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review sub-stage: Finalize PR, make ship/no-ship decision."
2
+ description: "Review step: Finalize PR, make ship/no-ship decision."
3
3
  ---
4
4
 
5
5
  # Handoff
@@ -14,7 +14,7 @@ description: "Review sub-stage: Finalize PR, make ship/no-ship decision."
14
14
  - How to test it
15
15
  - Screenshots if applicable
16
16
  - Any concerns or known limitations
17
- 2. Review all findings from prior review sub-stages
17
+ 2. Review all findings from prior review steps
18
18
  3. Check acceptance criteria status from Test/Validate
19
19
  4. Make the decision: **approved**, **changes_requested**, or **rejected**
20
20
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review sub-stage: Check for performance issues."
2
+ description: "Review step: Check for performance issues."
3
3
  ---
4
4
 
5
5
  # Performance Review
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review sub-stage: OWASP top 10 security review."
2
+ description: "Review step: OWASP top 10 security review."
3
3
  ---
4
4
 
5
5
  # Security Review
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review sub-stage: Review your own diff for obvious issues."
2
+ description: "Review step: Review your own diff for obvious issues."
3
3
  ---
4
4
 
5
5
  # Self-Review
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: test
3
- description: "Run the Test phase — 3 sub-stages: Verify, E2E, Validate."
3
+ description: "Run the Test phase — 3 steps: Verify, E2E, Validate."
4
4
  user-invocable: false
5
5
  allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent
6
6
  ---
7
7
 
8
8
  You are the **QA Lead**. Validate the implementation against the Blueprint and acceptance criteria.
9
9
 
10
- ## Sub-stages (in order)
10
+ ## Steps (in order)
11
11
 
12
12
  1. **Verify** — Run existing test suite, check for regressions
13
13
  2. **E2E** — Test user flows end-to-end
@@ -15,11 +15,11 @@ You are the **QA Lead**. Validate the implementation against the Blueprint and a
15
15
 
16
16
  ## Execution
17
17
 
18
- For each sub-stage:
19
- 1. Read the sub-stage file (e.g., `.claude/skills/wk-test/stages/verify.md`)
18
+ For each step:
19
+ 1. Read the step file (e.g., `.claude/skills/wk-test/steps/verify.md`)
20
20
  2. Follow its instructions
21
21
  3. Update `.work-kit/state.md` with outputs
22
- 4. Proceed to next sub-stage
22
+ 4. Proceed to next step
23
23
 
24
24
  ## Key Principle
25
25
 
@@ -27,7 +27,7 @@ For each sub-stage:
27
27
 
28
28
  ## Recording
29
29
 
30
- Throughout every sub-stage, update the shared state.md sections:
30
+ Throughout every step, update the shared state.md sections:
31
31
 
32
32
  - **`## Criteria`** — Check off criteria as they're verified. Add evidence inline: `- [x] <criterion> — verified by <test name / screenshot / manual check>`.
33
33
  - **`## Decisions`** — If you discover a criterion is untestable or needs reinterpretation, record the decision and why.
@@ -51,7 +51,7 @@ Agent: Verify ──┐
51
51
  Agent: E2E ──┘
52
52
  ```
53
53
 
54
- Each sub-agent reads the same Context Input sections and writes its own `### Test: <sub-stage>` section to state.md.
54
+ Each sub-agent reads the same Context Input sections and writes its own `### Test: <step>` section to state.md.
55
55
 
56
56
  ## Boundaries
57
57
 
@@ -74,7 +74,7 @@ Each sub-agent reads the same Context Input sections and writes its own `### Tes
74
74
 
75
75
  ## Final Output
76
76
 
77
- After all sub-stages are done, append a `### Test: Final` section to state.md. This is what **Review agents read**.
77
+ After all steps are done, append a `### Test: Final` section to state.md. This is what **Review agents read**.
78
78
 
79
79
  ```markdown
80
80
  ### Test: Final
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Test sub-stage: Test user flows end-to-end."
2
+ description: "Test step: Test user flows end-to-end."
3
3
  ---
4
4
 
5
5
  # E2E
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Test sub-stage: Verify every acceptance criterion is satisfied with evidence."
2
+ description: "Test step: Verify every acceptance criterion is satisfied with evidence."
3
3
  ---
4
4
 
5
5
  # Validate
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Test sub-stage: Run existing test suite, check for regressions."
2
+ description: "Test step: Run existing test suite, check for regressions."
3
3
  ---
4
4
 
5
5
  # Verify
@@ -10,18 +10,19 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
10
10
  **Role:** Work Historian
11
11
  **Goal:** Produce a concise, useful summary of what was built and why — then clean up.
12
12
 
13
- ## Instructions
13
+ This phase has **one step**: `wrap-up/summary`. See `.claude/skills/wk-wrap-up/steps/summary.md` for the canonical instructions. The summary you write goes into `.work-kit/summary.md`; the CLI archives it into `.work-kit-tracker/archive/<slug>-<date>/` when you call `work-kit complete wrap-up/summary --outcome done`.
14
14
 
15
- > **Note:** The CLI automatically archives `state.md`, `tracker.json`, and a placeholder `summary.md` into `.work-kit-tracker/archive/<slug>-<date>/` when the last sub-stage completes. It also appends a row to `.work-kit-tracker/index.md`. Your job is to **replace the placeholder summary.md** with a real distilled summary.
15
+ ## Instructions
16
16
 
17
17
  1. **Read the full `.work-kit/state.md`** — every phase output from Plan through the last completed phase
18
18
  2. **Synthesize the summary** — not a copy-paste of state, but a distilled record that a future developer (or agent) would find useful
19
- 3. **Overwrite the summary file** at `.work-kit-tracker/archive/<slug>-<date>/summary.md` on the **main branch** (not the worktree)
20
- 4. **Ask the user** if they want the worktree and branch removed
19
+ 3. **Write `.work-kit/summary.md`** in the format described in the step file
20
+ 4. **Run** `work-kit complete wrap-up/summary --outcome done`
21
+ 5. **Ask the user** if they want the worktree and branch removed
21
22
 
22
23
  ## Summary File Format
23
24
 
24
- Overwrite `.work-kit-tracker/archive/<slug>-<date>/summary.md`:
25
+ Overwrite `.work-kit/summary.md`:
25
26
 
26
27
  ```markdown
27
28
  ---
@@ -0,0 +1,86 @@
1
+ # Step: Summary
2
+
3
+ **Phase:** Wrap-up
4
+ **Role:** Work Historian
5
+ **Goal:** Distill the full state.md into a useful summary for future developers, then clean up.
6
+
7
+ ## Workflow
8
+
9
+ The CLI archives `state.md`, `tracker.json`, and (if you wrote one) `summary.md` into
10
+ `.work-kit-tracker/archive/<slug>-<date>/` automatically when the wrap-up step completes.
11
+ It also appends a row to `.work-kit-tracker/index.md`.
12
+
13
+ **Your job:** write a real `summary.md` to `.work-kit/summary.md` *before* calling
14
+ `work-kit complete wrap-up/summary`. The CLI will pick it up and place it in the archive.
15
+
16
+ ## Instructions
17
+
18
+ 1. **Read the full `.work-kit/state.md`** — every phase output from Plan through Deploy.
19
+ 2. **Synthesize the summary** — not a copy-paste; a distillation a future developer can use.
20
+ 3. **Write `.work-kit/summary.md`** with the format below.
21
+ 4. **Run** `work-kit complete wrap-up/summary --outcome done`.
22
+ 5. **Ask the user** if they want the worktree and feature branch removed (use `work-kit cancel` only if no merge happened; otherwise prefer `git worktree remove`).
23
+
24
+ ## Summary File Format
25
+
26
+ Write to `.work-kit/summary.md`:
27
+
28
+ ```markdown
29
+ ---
30
+ slug: <slug>
31
+ branch: feature/<slug>
32
+ pr: <#number or n/a>
33
+ started: <YYYY-MM-DD>
34
+ completed: <YYYY-MM-DD>
35
+ status: <completed | partial | rolled-back>
36
+ ---
37
+
38
+ ## Summary
39
+ <2-3 sentences: what was built, why it was needed, and the end state>
40
+
41
+ ## Criteria
42
+ <copy the final criteria checklist from state.md — checked and unchecked>
43
+
44
+ ## Key Decisions
45
+ <only the non-obvious ones — decisions where the alternative was reasonable>
46
+ - <decision>: <what was chosen> — <why, in one line>
47
+
48
+ ## Deviations from Plan
49
+ <anything that changed between Blueprint and final implementation — skip if none>
50
+ - <what changed and why>
51
+ ```
52
+
53
+ ## Include vs. Exclude
54
+
55
+ **Include:**
56
+ - Decisions where you chose between real alternatives
57
+ - Deviations from the Blueprint (and why)
58
+ - Anything a future developer would need to understand the "why" behind the code
59
+ - Criteria status — what was met, what wasn't
60
+
61
+ **Exclude:**
62
+ - Artifact lists (files, PRs, migrations) — derivable from git
63
+ - Routine implementation details ("created file X, modified file Y")
64
+ - Full phase outputs — distill, don't dump
65
+ - Internal process notes ("ran tests 3 times before they passed")
66
+
67
+ ## Boundaries
68
+
69
+ ### Always
70
+ - Read the full state.md before writing the summary
71
+ - Include every non-obvious decision in Key Decisions
72
+ - Include every deviation from the Blueprint in Deviations
73
+
74
+ ### Never
75
+ - Copy-paste full phase outputs into the summary
76
+ - Skip the criteria checklist
77
+
78
+ ## After Completion
79
+
80
+ When you call `work-kit complete wrap-up/summary --outcome done`, the CLI:
81
+
82
+ 1. Creates `.work-kit-tracker/archive/<slug>-<date>/`
83
+ 2. Copies `state.md`, `tracker.json`, and `summary.md` into it
84
+ 3. Appends a row to `.work-kit-tracker/index.md`
85
+
86
+ You may then commit the archive to the main branch and remove the worktree.
@@ -1,32 +0,0 @@
1
- import { PhaseName, Location } from "../state/schema.js";
2
- import { LOOPBACK_ROUTES } from "../config/loopback-routes.js";
3
-
4
- interface LoopbackResult {
5
- to: Location;
6
- reason: string;
7
- }
8
-
9
- /**
10
- * Check if completing a sub-stage with a given outcome should trigger a loop-back.
11
- */
12
- export function checkLoopback(
13
- phase: PhaseName,
14
- subStage: string,
15
- outcome?: string
16
- ): LoopbackResult | null {
17
- if (!outcome) return null;
18
-
19
- const route = LOOPBACK_ROUTES.find(
20
- (r) =>
21
- r.from.phase === phase &&
22
- r.from.subStage === subStage &&
23
- r.triggerOutcome === outcome
24
- );
25
-
26
- if (!route) return null;
27
-
28
- return {
29
- to: route.to,
30
- reason: route.reason,
31
- };
32
- }
@@ -1,60 +0,0 @@
1
- import type { PhaseName, WorkKitState } from "../state/schema.js";
2
-
3
- /**
4
- * Defines which sub-stages run in parallel and which runs sequentially after.
5
- */
6
- export interface ParallelGroup {
7
- parallel: string[]; // sub-stages that run concurrently
8
- thenSequential?: string; // sub-stage that runs after all parallel complete
9
- }
10
-
11
- /**
12
- * Parallel group definitions per phase.
13
- */
14
- const PARALLEL_GROUPS: Record<string, ParallelGroup> = {
15
- test: {
16
- parallel: ["verify", "e2e"],
17
- thenSequential: "validate",
18
- },
19
- review: {
20
- parallel: ["self-review", "security", "performance", "compliance"],
21
- thenSequential: "handoff",
22
- },
23
- };
24
-
25
- /**
26
- * Check if a sub-stage triggers a parallel group.
27
- * Triggers on any parallel member that is the first non-skipped one in the group.
28
- * Returns null if the sub-stage is not a parallel trigger or the group doesn't apply.
29
- */
30
- export function getParallelGroup(phase: PhaseName, subStage: string, state?: WorkKitState): ParallelGroup | null {
31
- const group = PARALLEL_GROUPS[phase];
32
- if (!group) return null;
33
-
34
- if (!group.parallel.includes(subStage)) return null;
35
-
36
- // Find the first non-skipped parallel member
37
- if (state) {
38
- const phaseState = state.phases[phase];
39
- const firstActive = group.parallel.find((ss) => {
40
- const ssState = phaseState?.subStages[ss];
41
- return ssState && ssState.status !== "skipped" && ssState.status !== "completed";
42
- });
43
- // Only trigger if this sub-stage is the first active parallel member
44
- if (firstActive !== subStage) return null;
45
- } else {
46
- // No state provided — fall back to first-member trigger
47
- if (group.parallel[0] !== subStage) return null;
48
- }
49
-
50
- return group;
51
- }
52
-
53
- /**
54
- * Check if a sub-stage is a parallel member (part of a group, not necessarily trigger).
55
- */
56
- export function isParallelMember(phase: PhaseName, subStage: string): boolean {
57
- const group = PARALLEL_GROUPS[phase];
58
- if (!group) return false;
59
- return group.parallel.includes(subStage);
60
- }
@@ -1,129 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import * as assert from "node:assert/strict";
3
- import { nextSubStageInPhase, isPhaseComplete, determineNextStep } from "./transitions.js";
4
- import type { WorkKitState, PhaseName, PhaseState, SubStageState } from "../state/schema.js";
5
- import { PHASE_NAMES, SUBSTAGES_BY_PHASE } from "../state/schema.js";
6
-
7
- function makeState(): WorkKitState {
8
- const phases = {} as Record<PhaseName, PhaseState>;
9
- for (const phase of PHASE_NAMES) {
10
- const subStages: Record<string, SubStageState> = {};
11
- for (const ss of SUBSTAGES_BY_PHASE[phase]) {
12
- subStages[ss] = { status: "pending" };
13
- }
14
- phases[phase] = { status: "pending", subStages };
15
- }
16
- return {
17
- version: 1,
18
- slug: "test",
19
- branch: "feature/test",
20
- started: "2026-01-01",
21
- mode: "full-kit",
22
- status: "in-progress",
23
- currentPhase: "plan",
24
- currentSubStage: "clarify",
25
- phases,
26
- loopbacks: [],
27
- metadata: { worktreeRoot: "/tmp/test", mainRepoRoot: "/tmp/test" },
28
- };
29
- }
30
-
31
- describe("nextSubStageInPhase", () => {
32
- it("returns first pending sub-stage", () => {
33
- const state = makeState();
34
- const result = nextSubStageInPhase(state, "plan");
35
- assert.equal(result, "clarify");
36
- });
37
-
38
- it("returns null when all complete or skipped", () => {
39
- const state = makeState();
40
- for (const ss of Object.values(state.phases.plan.subStages)) {
41
- ss.status = "completed";
42
- }
43
- const result = nextSubStageInPhase(state, "plan");
44
- assert.equal(result, null);
45
- });
46
-
47
- it("skips completed sub-stages and returns next pending", () => {
48
- const state = makeState();
49
- state.phases.plan.subStages.clarify.status = "completed";
50
- state.phases.plan.subStages.investigate.status = "completed";
51
- const result = nextSubStageInPhase(state, "plan");
52
- assert.equal(result, "sketch");
53
- });
54
- });
55
-
56
- describe("isPhaseComplete", () => {
57
- it("returns true when all complete or skipped", () => {
58
- const state = makeState();
59
- for (const ss of Object.values(state.phases.plan.subStages)) {
60
- ss.status = "completed";
61
- }
62
- assert.equal(isPhaseComplete(state, "plan"), true);
63
- });
64
-
65
- it("returns true with mix of completed and skipped", () => {
66
- const state = makeState();
67
- let first = true;
68
- for (const ss of Object.values(state.phases.plan.subStages)) {
69
- ss.status = first ? "skipped" : "completed";
70
- first = false;
71
- }
72
- assert.equal(isPhaseComplete(state, "plan"), true);
73
- });
74
-
75
- it("returns false when some sub-stages are pending", () => {
76
- const state = makeState();
77
- assert.equal(isPhaseComplete(state, "plan"), false);
78
- });
79
- });
80
-
81
- describe("determineNextStep", () => {
82
- it("returns complete when state is completed", () => {
83
- const state = makeState();
84
- state.status = "completed";
85
- const step = determineNextStep(state);
86
- assert.equal(step.type, "complete");
87
- });
88
-
89
- it("returns phase-boundary when no current phase", () => {
90
- const state = makeState();
91
- state.currentPhase = null;
92
- const step = determineNextStep(state);
93
- assert.equal(step.type, "phase-boundary");
94
- assert.equal(step.phase, "plan");
95
- });
96
-
97
- it("returns sub-stage for current phase with pending work", () => {
98
- const state = makeState();
99
- state.currentPhase = "plan";
100
- state.phases.plan.status = "in-progress";
101
- const step = determineNextStep(state);
102
- assert.equal(step.type, "sub-stage");
103
- assert.equal(step.phase, "plan");
104
- assert.equal(step.subStage, "clarify");
105
- });
106
-
107
- it("auto-proceeds to next phase by default when current phase is complete", () => {
108
- const state = makeState();
109
- state.currentPhase = "plan";
110
- for (const ss of Object.values(state.phases.plan.subStages)) {
111
- ss.status = "completed";
112
- }
113
- const step = determineNextStep(state);
114
- assert.equal(step.type, "phase-boundary");
115
- assert.equal(step.phase, "build");
116
- });
117
-
118
- it("returns wait-for-user when gated and current phase is complete", () => {
119
- const state = makeState();
120
- state.gated = true;
121
- state.currentPhase = "plan";
122
- for (const ss of Object.values(state.phases.plan.subStages)) {
123
- ss.status = "completed";
124
- }
125
- const step = determineNextStep(state);
126
- assert.equal(step.type, "wait-for-user");
127
- assert.equal(step.phase, "build");
128
- });
129
- });
File without changes