work-kit-cli 0.2.7 → 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.
- package/README.md +13 -13
- package/cli/src/commands/bootstrap.ts +39 -13
- package/cli/src/commands/cancel.ts +1 -16
- package/cli/src/commands/complete.ts +92 -98
- package/cli/src/commands/completions.ts +2 -2
- package/cli/src/commands/doctor.ts +1 -1
- package/cli/src/commands/init.ts +40 -32
- package/cli/src/commands/loopback.ts +8 -11
- package/cli/src/commands/next.ts +64 -51
- package/cli/src/commands/pause-resume.test.ts +142 -0
- package/cli/src/commands/pause.ts +34 -0
- package/cli/src/commands/report.ts +217 -0
- package/cli/src/commands/resume.ts +38 -0
- package/cli/src/commands/setup.ts +136 -0
- package/cli/src/commands/status.ts +6 -6
- package/cli/src/commands/uninstall.ts +8 -3
- package/cli/src/commands/workflow.ts +27 -27
- package/cli/src/config/agent-map.ts +9 -9
- package/cli/src/config/constants.ts +44 -0
- package/cli/src/config/loopback-routes.ts +13 -13
- package/cli/src/config/project-config.test.ts +127 -0
- package/cli/src/config/project-config.ts +106 -0
- package/cli/src/config/{phases.ts → workflow.ts} +40 -23
- package/cli/src/context/prompt-builder.ts +10 -9
- package/cli/src/index.ts +63 -7
- package/cli/src/observer/data.ts +64 -56
- package/cli/src/observer/renderer.ts +162 -75
- package/cli/src/state/helpers.test.ts +28 -28
- package/cli/src/state/helpers.ts +37 -25
- package/cli/src/state/schema.ts +88 -45
- package/cli/src/state/store.ts +92 -7
- package/cli/src/state/validators.test.ts +13 -13
- package/cli/src/state/validators.ts +3 -4
- package/cli/src/utils/colors.ts +2 -0
- package/cli/src/utils/json.ts +20 -0
- package/cli/src/utils/time.ts +27 -0
- package/cli/src/{engine → workflow}/loopbacks.test.ts +2 -2
- package/cli/src/workflow/loopbacks.ts +42 -0
- package/cli/src/workflow/parallel.ts +64 -0
- package/cli/src/workflow/transitions.test.ts +129 -0
- package/cli/src/{engine → workflow}/transitions.ts +18 -22
- package/package.json +2 -2
- package/skills/auto-kit/SKILL.md +22 -22
- package/skills/cancel-kit/SKILL.md +4 -4
- package/skills/full-kit/SKILL.md +23 -23
- package/skills/pause-kit/SKILL.md +25 -0
- package/skills/resume-kit/SKILL.md +28 -0
- package/skills/wk-bootstrap/SKILL.md +5 -5
- package/skills/wk-build/SKILL.md +10 -10
- package/skills/wk-build/{stages → steps}/commit.md +1 -1
- package/skills/wk-build/{stages → steps}/core.md +3 -3
- package/skills/wk-build/{stages → steps}/integration.md +2 -2
- package/skills/wk-build/{stages → steps}/migration.md +1 -1
- package/skills/wk-build/{stages → steps}/red.md +1 -1
- package/skills/wk-build/{stages → steps}/refactor.md +1 -1
- package/skills/wk-build/{stages → steps}/setup.md +1 -1
- package/skills/wk-build/{stages → steps}/ui.md +1 -1
- package/skills/wk-deploy/SKILL.md +6 -6
- package/skills/wk-deploy/{stages → steps}/merge.md +1 -1
- package/skills/wk-deploy/{stages → steps}/monitor.md +1 -1
- package/skills/wk-deploy/{stages → steps}/remediate.md +1 -1
- package/skills/wk-plan/SKILL.md +13 -13
- package/skills/wk-plan/{stages → steps}/architecture.md +1 -1
- package/skills/wk-plan/{stages → steps}/audit.md +2 -2
- package/skills/wk-plan/{stages → steps}/blueprint.md +2 -2
- package/skills/wk-plan/{stages → steps}/clarify.md +1 -1
- package/skills/wk-plan/{stages → steps}/investigate.md +1 -1
- package/skills/wk-plan/{stages → steps}/scope.md +1 -1
- package/skills/wk-plan/{stages → steps}/sketch.md +1 -1
- package/skills/wk-plan/{stages → steps}/ux-flow.md +1 -1
- package/skills/wk-review/SKILL.md +10 -10
- package/skills/wk-review/{stages → steps}/compliance.md +1 -1
- package/skills/wk-review/{stages → steps}/handoff.md +2 -2
- package/skills/wk-review/{stages → steps}/performance.md +1 -1
- package/skills/wk-review/{stages → steps}/security.md +1 -1
- package/skills/wk-review/{stages → steps}/self-review.md +1 -1
- package/skills/wk-test/SKILL.md +8 -8
- package/skills/wk-test/{stages → steps}/e2e.md +1 -1
- package/skills/wk-test/{stages → steps}/validate.md +1 -1
- package/skills/wk-test/{stages → steps}/verify.md +1 -1
- package/skills/wk-wrap-up/SKILL.md +6 -5
- package/skills/wk-wrap-up/steps/summary.md +86 -0
- package/cli/src/engine/loopbacks.ts +0 -32
- package/cli/src/engine/parallel.ts +0 -60
- package/cli/src/engine/transitions.test.ts +0 -129
- /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
|
|
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
|
-
##
|
|
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
|
|
21
|
-
1. Read the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
61
|
+
- Its step skill file instructions
|
|
62
62
|
|
|
63
|
-
Each writes its own `### Review: <
|
|
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
|
|
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
|
|
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
|
|
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
|
|
package/skills/wk-test/SKILL.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test
|
|
3
|
-
description: "Run the Test phase — 3
|
|
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
|
-
##
|
|
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
|
|
19
|
-
1. Read the
|
|
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
|
|
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
|
|
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: <
|
|
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
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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. **
|
|
20
|
-
4. **
|
|
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
|
|
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
|