@phuthuycoding/kanban-flow 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 (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/cli/args.js +219 -0
  4. package/dist/cli/commands/approve.js +44 -0
  5. package/dist/cli/commands/archive.js +245 -0
  6. package/dist/cli/commands/artifacts.js +100 -0
  7. package/dist/cli/commands/autoconfig.js +180 -0
  8. package/dist/cli/commands/cancel.js +129 -0
  9. package/dist/cli/commands/contexts.js +101 -0
  10. package/dist/cli/commands/doctor.js +35 -0
  11. package/dist/cli/commands/harness.js +60 -0
  12. package/dist/cli/commands/helpers.js +22 -0
  13. package/dist/cli/commands/init.js +119 -0
  14. package/dist/cli/commands/inspect.js +141 -0
  15. package/dist/cli/commands/new.js +80 -0
  16. package/dist/cli/commands/rules.js +69 -0
  17. package/dist/cli/commands/run.js +156 -0
  18. package/dist/cli/commands/stage.js +186 -0
  19. package/dist/cli/result.js +1 -0
  20. package/dist/dashboard/dashboard-view.js +238 -0
  21. package/dist/dashboard/dashboard.js +206 -0
  22. package/dist/harness/chain.js +41 -0
  23. package/dist/harness/config.js +168 -0
  24. package/dist/harness/prompt.js +105 -0
  25. package/dist/harness/run.js +245 -0
  26. package/dist/harness/session.js +78 -0
  27. package/dist/harness/supervise.js +65 -0
  28. package/dist/index.js +123 -0
  29. package/dist/integrations/agents.js +67 -0
  30. package/dist/integrations/hooks.js +59 -0
  31. package/dist/integrations/install.js +193 -0
  32. package/dist/project/bootstrap.js +358 -0
  33. package/dist/project/config.js +111 -0
  34. package/dist/project/contexts.js +98 -0
  35. package/dist/project/doctor.js +163 -0
  36. package/dist/shared/frontmatter.js +54 -0
  37. package/dist/shared/paths.js +78 -0
  38. package/dist/shared/time.js +5 -0
  39. package/dist/workflow/direction.js +56 -0
  40. package/dist/workflow/features.js +198 -0
  41. package/dist/workflow/findings.js +3 -0
  42. package/dist/workflow/schema.js +148 -0
  43. package/dist/workflow/secrets.js +52 -0
  44. package/dist/workflow/status.js +188 -0
  45. package/dist/workflow/validate-approval.js +25 -0
  46. package/dist/workflow/validate-artifacts.js +89 -0
  47. package/dist/workflow/validate-cancel.js +14 -0
  48. package/dist/workflow/validate-reports.js +121 -0
  49. package/dist/workflow/validate-traceability.js +91 -0
  50. package/dist/workflow/validate.js +73 -0
  51. package/docs/workflow/README.md +67 -0
  52. package/docs/workflow/artifacts.md +60 -0
  53. package/docs/workflow/cli-reference.md +78 -0
  54. package/docs/workflow/dashboard.md +35 -0
  55. package/docs/workflow/gates.md +103 -0
  56. package/docs/workflow/harness.md +144 -0
  57. package/docs/workflow/lifecycle.md +107 -0
  58. package/docs/workflow/skills.md +52 -0
  59. package/docs/workflow/source-layout.md +47 -0
  60. package/docs/workflow/state-machine.md +83 -0
  61. package/kanban-flow/review/rules/README.md +30 -0
  62. package/kanban-flow/review/rules/general.md +41 -0
  63. package/kanban-flow/review/rules/performance.md +29 -0
  64. package/kanban-flow/review/rules/security.md +32 -0
  65. package/kanban-flow/review/stacks/go.md +33 -0
  66. package/kanban-flow/review/stacks/java.md +38 -0
  67. package/kanban-flow/review/stacks/node.md +28 -0
  68. package/kanban-flow/review/stacks/php.md +30 -0
  69. package/kanban-flow/review/stacks/python.md +34 -0
  70. package/kanban-flow/review/stacks/ruby.md +32 -0
  71. package/kanban-flow/review/stacks/rust.md +33 -0
  72. package/kanban-flow/templates/phase-1-bug-report.md +76 -0
  73. package/kanban-flow/templates/phase-1-spec-requirement.md +67 -0
  74. package/kanban-flow/templates/phase-2-implementation-plan.md +85 -0
  75. package/kanban-flow/templates/phase-2-test-case.md +68 -0
  76. package/kanban-flow/templates/phase-2-use-case-diagram.md +18 -0
  77. package/kanban-flow/templates/phase-2-use-case-specification.md +33 -0
  78. package/kanban-flow/templates/phase-2-use-case.md +60 -0
  79. package/kanban-flow/templates/phase-4-testing-result.md +63 -0
  80. package/kanban-flow/templates/phase-5-review-report.md +68 -0
  81. package/kanban-flow/templates/phase-6-feature-report.md +78 -0
  82. package/package.json +63 -0
  83. package/skills/kanban-archive/SKILL.md +78 -0
  84. package/skills/kanban-brainstorm/SKILL.md +310 -0
  85. package/skills/kanban-bug/SKILL.md +55 -0
  86. package/skills/kanban-flow/SKILL.md +136 -0
  87. package/skills/kanban-implement/SKILL.md +72 -0
  88. package/skills/kanban-plan/SKILL.md +102 -0
  89. package/skills/kanban-review/SKILL.md +90 -0
  90. package/skills/kanban-test/SKILL.md +76 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quyen (phuthuycoding)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # kanban-flow
2
+
3
+ An AI coding agent will tell you the tests passed, the plan was followed, the
4
+ edge cases are covered. Sometimes it is right, and you cannot tell which time
5
+ from the transcript, because the transcript is written by the same thing you are
6
+ checking. kanban-flow moves the proof into files on disk: the gates do not read
7
+ the agent's summary, they read `.works/`.
8
+
9
+ ## How it works
10
+
11
+ Every work item walks one pipeline, and only the CLI moves it between stages:
12
+
13
+ ```text
14
+ brainstorm → planning → implementation → testing → review → dones
15
+ ↘ backlog ↗ ↺ FAIL loops back
16
+ any stage → cancelled, with a reason on the record
17
+ ```
18
+
19
+ ```mermaid
20
+ flowchart LR
21
+ B([brainstorm]) -->|requirement confirmed| P([planning])
22
+ P -->|contract approved + fingerprint| I([implementation])
23
+ P -.->|hold| BL([backlog])
24
+ BL -.->|start| I
25
+ I -->|tasks done| T([testing])
26
+ T -->|report PASS + current execution id| R([review])
27
+ R -->|report PASS + feature report| D([dones])
28
+ T -.->|FAIL| I
29
+ R -.->|FAIL| I
30
+ I -.->|kf cancel --reason| X([cancelled])
31
+ X -.->|reopen where it stopped| I
32
+ ```
33
+
34
+ Solid arrows are the happy path, and every label on one is a gate the CLI checks
35
+ before the folder moves. Dotted arrows are the ways out: hold it in backlog,
36
+ loop back on a FAIL, or stop it for good with a reason on the record.
37
+
38
+ Most stages owe an artifact. A transition is refused when that artifact is
39
+ missing, empty, still full of template placeholders, or carrying a real secret.
40
+ A testing report that does not say `PASS` does not reach review; a review report
41
+ that does not say `PASS` does not archive. `--force` overrides a gate and is
42
+ recorded in `.kfw.json` where a reviewer will find it.
43
+
44
+ Three decisions belong to a human: marking the requirement `status: confirmed`,
45
+ approving the execution contract, and choosing whether to start now or hold it
46
+ in backlog. The approval is bound to a SHA-256 fingerprint of the contract, so
47
+ editing the plan invalidates it, and no stage further forward accepts the item
48
+ until it returns to planning and is approved again.
49
+
50
+ ## Install
51
+
52
+ Needs Node 20 or newer.
53
+
54
+ ```bash
55
+ npm install -g @phuthuycoding/kanban-flow
56
+ ```
57
+
58
+ That puts `kf` on your PATH. To work from source instead:
59
+
60
+ ```bash
61
+ git clone https://github.com/phuthuycoding/kanban-flow.git
62
+ cd kanban-flow && npm install && npm run build && npm link
63
+ ```
64
+
65
+ ## A run, end to end
66
+
67
+ The agent runs the commands and writes the artifacts between them, taking every
68
+ template and path from `kf instruct`. `kf status` shows the artifact checklist;
69
+ `kf validate` reports what is actually blocking a gate, traceability included.
70
+
71
+ ```bash
72
+ cd your-project
73
+ kf init # asks a few questions, seeds .kf/, installs the skills
74
+ kf new user-login --context auth # opens Phase 1 in brainstorm
75
+ kf instruct spec-requirement --change user-login
76
+ # write the requirement, set its frontmatter to status: confirmed — decision one
77
+ kf stage user-login planning
78
+ # write the plan, the use-case index, one file per UC, the diagram, the test plan
79
+ kf approve user-login # decision two; fingerprints all of it
80
+ kf stage user-login implementation # or backlog — decision three
81
+ kf stage user-login testing # mints a fresh execution id
82
+ # write the testing report, carrying that id and real exit codes
83
+ kf stage user-login review # refused unless that report says PASS
84
+ # write the review report, then the feature report
85
+ kf archive user-login # refused unless the review says PASS
86
+ ```
87
+
88
+ ## What is actually different
89
+
90
+ Most of the mechanics here exist elsewhere: requirement-to-test traceability as
91
+ a CI gate, role-to-runner configuration, hash-bound plan approval, a cancelled
92
+ state with a mandatory reason, blocking placeholders. No claim to inventing them.
93
+
94
+ One mechanic I have not found anywhere else: **every entry into testing mints a
95
+ fresh execution id**, and a report is accepted only when its `execution:` field
96
+ matches the current one. Fix a failure, go round again, and yesterday's green
97
+ report is inert. Stale test evidence stops being a way to pass.
98
+
99
+ ```mermaid
100
+ sequenceDiagram
101
+ participant A as Agent
102
+ participant K as kf
103
+ participant F as .works/
104
+
105
+ A->>K: kf stage x testing
106
+ K->>F: mint execution id run-1
107
+ A->>F: testing report, execution run-1
108
+ A->>K: kf stage x review
109
+ K-->>A: PASS and id matches, allowed
110
+
111
+ Note over A,F: review returns FAIL, the code changes
112
+
113
+ A->>K: kf stage x implementation
114
+ A->>K: kf stage x testing
115
+ K->>F: mint execution id run-2
116
+ A->>K: kf stage x review
117
+ K-->>A: refused, the report still says run-1
118
+ ```
119
+
120
+ The old report is not deleted, argued with, or trusted less. It simply stops
121
+ matching, so passing again costs exactly one honest test run. The rest of the
122
+ case is the combination: a gate reading files instead of claims, an approval
123
+ bound to the bytes it approved, evidence that expires.
124
+
125
+ ## Multi-agent harness
126
+
127
+ Stages route to **roles**, roles point at **runners**. A runner is one way to
128
+ invoke a CLI: its argv, its permission flags, how its session resumes. A role is
129
+ a job such as researcher or tester. Pointing a role at an existing runner is one
130
+ line; a runner nobody has declared yet is a few more.
131
+
132
+ ```json
133
+ "harness": {
134
+ "main": "architect",
135
+ "roles": { "architect": "claude", "researcher": "codex", "writer": "gemini", "coder": "claude" },
136
+ "stages": { "brainstorm": ["researcher", "writer"], "implementation": "coder" },
137
+ "runners": {
138
+ "claude": { "start": ["claude", "-p", "{prompt}"] },
139
+ "codex": { "start": ["codex", "exec", "{prompt}"] },
140
+ "gemini": { "start": ["gemini", "-p", "{prompt}"] }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ```mermaid
146
+ flowchart LR
147
+ S1[stage: brainstorm] --> R1[role: researcher] --> N2[runner: codex]
148
+ S1 --> R2[role: writer] --> N3[runner: gemini]
149
+ S2[stage: implementation] --> R3[role: coder] --> N1[runner: claude]
150
+ ```
151
+
152
+ A stage runs its roles in order, and a role that declares an `output` file hands
153
+ it to the next one. A role reporting neither `DONE` nor `DONE_WITH_CONCERNS`
154
+ stops the chain. Sessions are kept per work item and per role, so two roles on
155
+ one CLI never share context.
156
+
157
+ ```bash
158
+ kf harness # stage → role chain, role → runner, which CLIs are on PATH
159
+ kf run user-login --detach # run the current stage's chain; poll with kf runs
160
+ kf runs user-login --json # each run: role, runner, status, usage where reported
161
+ ```
162
+
163
+ Presets ship for claude, codex, devin, gemini and opencode. Assign no stages and the harness stays out of the way.
164
+
165
+ ## Documentation
166
+
167
+ The full reference lives in [docs/workflow](docs/workflow/README.md): the [state
168
+ machine](docs/workflow/state-machine.md), every [gate](docs/workflow/gates.md), the
169
+ [artifacts](docs/workflow/artifacts.md), the [CLI reference](docs/workflow/cli-reference.md),
170
+ the [harness](docs/workflow/harness.md), the [dashboard](docs/workflow/dashboard.md). Release
171
+ notes: [CHANGELOG.md](CHANGELOG.md).
172
+
173
+ ## License — MIT
@@ -0,0 +1,219 @@
1
+ import { parseArgs } from "node:util";
2
+ const COMMANDS = {
3
+ init: {
4
+ help: "Usage: kf init [path] [--defaults] [--minimal] [--context <ctx>] [--agent <id> ...] — onboarding: asks setup questions on a TTY (defaults when non-TTY or --defaults), seeds .kf config + installs skills; --minimal skips the questions, not the scaffolding (still writes .kf config, skills and AGENTS.md; only the template/hook/rule files are left out)",
5
+ allowPositionals: true,
6
+ options: {
7
+ context: { type: "string", short: "c" },
8
+ interactive: { type: "boolean", short: "i" },
9
+ defaults: { type: "boolean" },
10
+ minimal: { type: "boolean" },
11
+ agent: { type: "string", multiple: true },
12
+ },
13
+ },
14
+ new: {
15
+ help: "Usage: kf new <feature> [--context <ctx>] [--type feature|bug] — create a work item in .works/brainstorm",
16
+ allowPositionals: true,
17
+ options: {
18
+ context: { type: "string", short: "c" },
19
+ goal: { type: "string", short: "g" },
20
+ type: { type: "string", short: "t" },
21
+ },
22
+ },
23
+ list: {
24
+ help: "Usage: kf list [--json]",
25
+ options: {
26
+ json: { type: "boolean" },
27
+ },
28
+ },
29
+ show: {
30
+ help: "Usage: kf show <feature> [--json]",
31
+ allowPositionals: true,
32
+ options: {
33
+ json: { type: "boolean" },
34
+ },
35
+ },
36
+ view: {
37
+ help: "Usage: kf view [--json] — workflow metrics by stage, kind, context and approval",
38
+ options: {
39
+ json: { type: "boolean" },
40
+ },
41
+ },
42
+ dashboard: {
43
+ help: "Usage: kf dashboard [--port <n>] — start a local analytics dashboard with metrics and charts (default port 8787)",
44
+ options: {
45
+ port: { type: "string", short: "p" },
46
+ },
47
+ },
48
+ status: {
49
+ help: "Usage: kf status [--change <feature>] [--all] [--json] — artifact checklist, Next step, approval state and recorded bypasses",
50
+ options: {
51
+ change: { type: "string" },
52
+ all: { type: "boolean", short: "a" },
53
+ json: { type: "boolean" },
54
+ },
55
+ },
56
+ instruct: {
57
+ help: "Usage: kf instruct <artifact|use-case> [--change <feature>] [--id UC-###] — print the template, current execution id and exact output path",
58
+ allowPositionals: true,
59
+ options: {
60
+ change: { type: "string" },
61
+ id: { type: "string" },
62
+ json: { type: "boolean" },
63
+ },
64
+ },
65
+ templates: {
66
+ help: "Usage: kf templates [--json]",
67
+ options: {
68
+ json: { type: "boolean" },
69
+ },
70
+ },
71
+ validate: {
72
+ help: "Usage: kf validate [--change <feature>] [--all] [--strict] [--json] — report gate, traceability, secret and bypass issues (exit 1 on failure)",
73
+ options: {
74
+ change: { type: "string" },
75
+ all: { type: "boolean", short: "a" },
76
+ strict: { type: "boolean" },
77
+ json: { type: "boolean" },
78
+ },
79
+ },
80
+ stage: {
81
+ help: "Usage: kf stage <feature> <next-stage> [--skip-hooks] — move feature to next stage",
82
+ allowPositionals: true,
83
+ options: {
84
+ force: { type: "boolean", short: "f" },
85
+ "skip-hooks": { type: "boolean" },
86
+ },
87
+ },
88
+ approve: {
89
+ help: "Usage: kf approve <feature> [--by <name>] — approve Phase 2 execution contract (Human gate)",
90
+ allowPositionals: true,
91
+ options: {
92
+ by: { type: "string" },
93
+ },
94
+ },
95
+ archive: {
96
+ help: "Usage: kf archive <feature> [--skip-hooks] — close a work item in dones; features sync canonical docs, bugs retain related docs",
97
+ allowPositionals: true,
98
+ options: {
99
+ force: { type: "boolean", short: "f" },
100
+ "skip-specs": { type: "boolean" },
101
+ "skip-hooks": { type: "boolean" },
102
+ },
103
+ },
104
+ rules: {
105
+ help: "Usage: kf rules [--stack <id> ...] [--list] [--force] — copy stack best-practice review rules into .kf/review/rules (auto-detects stack; packs: node, go, rust, python, php, ruby, java)",
106
+ options: {
107
+ stack: { type: "string", multiple: true },
108
+ list: { type: "boolean" },
109
+ force: { type: "boolean", short: "f" },
110
+ },
111
+ },
112
+ autoconfig: {
113
+ help: "Usage: kf autoconfig — print a briefing for an agent to configure this project: context, setup checklist, effective review rules and the workflow guide",
114
+ options: {},
115
+ },
116
+ doctor: {
117
+ help: "Usage: kf doctor [--json] — diagnose the project: stage dirs, config, work item metadata, installed skills; exits 1 when something is broken",
118
+ options: {
119
+ json: { type: "boolean" },
120
+ },
121
+ },
122
+ install: {
123
+ help: "Usage: kf install [--agent <id> ...] — copy the 8 kanban skills into project-level agent skill dirs {root}/.<agent>/skills (default: claude). Requires a kanban project (.works/). Agents: claude, codex, gemini, kiro, cursor, opencode",
124
+ options: {
125
+ agent: { type: "string", multiple: true },
126
+ },
127
+ },
128
+ uninstall: {
129
+ help: "Usage: kf uninstall [--agent <id> ...] [--purge] [--force] — remove the 8 kanban skills from project-level agent skill dirs {root}/.<agent>/skills (default: claude). --purge also deletes .works/, .kf/ and kanban doc dirs (asks first; --force skips the prompt)",
130
+ options: {
131
+ agent: { type: "string", multiple: true },
132
+ purge: { type: "boolean" },
133
+ force: { type: "boolean" },
134
+ },
135
+ },
136
+ cancel: {
137
+ help: 'Usage: kf cancel <feature> --reason "<why>" [--by <name>] [--purge-docs] [--force] [--skip-hooks] — stop a work item for good, recording who dropped it and why; reopen later with kf stage <feature> <its old stage>',
138
+ allowPositionals: true,
139
+ options: {
140
+ reason: { type: "string" },
141
+ by: { type: "string" },
142
+ "purge-docs": { type: "boolean" },
143
+ force: { type: "boolean", short: "f" },
144
+ "skip-hooks": { type: "boolean" },
145
+ },
146
+ },
147
+ run: {
148
+ help: "Usage: kf run <feature> [--stage <s>] [--role <r>] [--fresh] [--detach] [--timeout <min>] [--dry-run] — run the roles assigned to the work item's stage (harness.stages) as worker agents, in order, and record each run",
149
+ allowPositionals: true,
150
+ options: {
151
+ stage: { type: "string" },
152
+ role: { type: "string" },
153
+ agent: { type: "string" },
154
+ fresh: { type: "boolean" },
155
+ detach: { type: "boolean" },
156
+ timeout: { type: "string" },
157
+ "dry-run": { type: "boolean" },
158
+ supervise: { type: "string" },
159
+ },
160
+ },
161
+ runs: {
162
+ help: "Usage: kf runs [<feature>] [--json] — list worker runs (role, runner, stage, status, STATUS line) for one or all open work items",
163
+ allowPositionals: true,
164
+ options: {
165
+ json: { type: "boolean" },
166
+ },
167
+ },
168
+ contexts: {
169
+ help: "Usage: kf contexts [--json] — list the declared contexts and the ones work items actually use; prints a survey brief when none are declared",
170
+ options: {
171
+ json: { type: "boolean" },
172
+ },
173
+ },
174
+ harness: {
175
+ help: "Usage: kf harness [--json] — show the multi-agent harness: stage to role chains, role to runner, and whether each runner CLI is on PATH",
176
+ options: {
177
+ json: { type: "boolean" },
178
+ },
179
+ },
180
+ help: {
181
+ help: "Usage: kf help [command]",
182
+ allowPositionals: true,
183
+ },
184
+ version: {},
185
+ };
186
+ export function parseArgsCli(argv) {
187
+ const command = argv[0] ?? "help";
188
+ const spec = COMMANDS[command];
189
+ if (!spec)
190
+ return { command, positionals: argv.slice(1), options: {} };
191
+ const opts = {};
192
+ for (const [k, v] of Object.entries(spec.options ?? {})) {
193
+ const d = { type: v.type };
194
+ if (v.short !== undefined)
195
+ d.short = v.short;
196
+ if (v.default !== undefined)
197
+ d.default = v.default;
198
+ if (v.multiple !== undefined)
199
+ d.multiple = v.multiple;
200
+ opts[k] = d;
201
+ }
202
+ opts.help = { type: "boolean", short: "h" };
203
+ const { values, positionals } = parseArgs({
204
+ args: argv.slice(1),
205
+ options: opts,
206
+ allowPositionals: spec.allowPositionals ?? false,
207
+ strict: true,
208
+ });
209
+ return { command, positionals, options: values };
210
+ }
211
+ export function commandHelp(command) {
212
+ const spec = COMMANDS[command];
213
+ if (!spec)
214
+ return `Unknown command: ${command}. Use "kf help".`;
215
+ return spec.help ?? `kf ${command}`;
216
+ }
217
+ export function allCommands() {
218
+ return Object.keys(COMMANDS);
219
+ }
@@ -0,0 +1,44 @@
1
+ import { findFeature, writeFeatureMeta, executionContractHash } from "../../workflow/features.js";
2
+ import { validateFeature, renderValidateText } from "../../workflow/validate.js";
3
+ import { readProjectConfig } from "../../project/config.js";
4
+ import { nowTimestamp } from "../../shared/time.js";
5
+ import { findRoot } from "./helpers.js";
6
+ /**
7
+ * Human-in-the-Loop gate for Phase 2: the execution contract (implementation
8
+ * plan + use cases + test cases) must be approved before autonomous execution.
9
+ */
10
+ export async function cmdApprove(args, cwd) {
11
+ const name = args.positionals[0];
12
+ if (!name)
13
+ return { code: 1, stdout: "Usage: kf approve <feature> [--by <name>]", stderr: "missing feature" };
14
+ const root = await findRoot(cwd);
15
+ if (!root.ok)
16
+ return { code: 1, stdout: root.err, stderr: "no works" };
17
+ const f = findFeature(root.root, name);
18
+ if (!f)
19
+ return { code: 1, stdout: `Unknown feature '${name}'. Run: kf list`, stderr: "unknown feature" };
20
+ if (f.stage !== "planning") {
21
+ return {
22
+ code: 1,
23
+ stdout: `Cannot approve '${name}': feature is in stage "${f.stage}". Approval only applies during Phase 2 (planning).`,
24
+ stderr: "wrong stage",
25
+ };
26
+ }
27
+ const check = validateFeature(f, false, false);
28
+ if (!check.valid) {
29
+ return { code: 1, stdout: `Cannot approve an incomplete execution contract:\n${renderValidateText(check)}`, stderr: "invalid contract" };
30
+ }
31
+ const contractHash = executionContractHash(f.dir, f.meta?.kind === "bug" ? "bug" : "feature");
32
+ if (!contractHash || !f.meta)
33
+ return { code: 1, stdout: "Execution contract or metadata is missing.", stderr: "invalid contract" };
34
+ const cfg = readProjectConfig(root.root);
35
+ const by = typeof args.options.by === "string" ? args.options.by : (cfg.reviewer ?? "human");
36
+ if (!by.trim())
37
+ return { code: 1, stdout: "Approver name must not be empty.", stderr: "missing approver" };
38
+ const approval = { status: "approved", by, at: nowTimestamp(), contractHash };
39
+ await writeFeatureMeta(f.dir, { ...f.meta, approval, executionId: undefined });
40
+ return {
41
+ code: 0,
42
+ stdout: `✓ Approved '${name}' (by ${by} at ${approval.at})\n Execution contract is now the binding scope for autonomous phases.`,
43
+ };
44
+ }