@kata-sh/cli 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +56 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +95 -0
- package/dist/resource-loader.d.ts +18 -0
- package/dist/resource-loader.js +50 -0
- package/dist/wizard.d.ts +15 -0
- package/dist/wizard.js +159 -0
- package/package.json +50 -21
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +45 -0
- package/src/resources/AGENTS.md +108 -0
- package/src/resources/KATA-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +200 -0
- package/src/resources/extensions/bg-shell/index.ts +2758 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4916 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/github/formatters.ts +207 -0
- package/src/resources/extensions/github/gh-api.ts +537 -0
- package/src/resources/extensions/github/index.ts +778 -0
- package/src/resources/extensions/kata/activity-log.ts +88 -0
- package/src/resources/extensions/kata/auto.ts +2786 -0
- package/src/resources/extensions/kata/commands.ts +355 -0
- package/src/resources/extensions/kata/crash-recovery.ts +85 -0
- package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
- package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
- package/src/resources/extensions/kata/doctor.ts +683 -0
- package/src/resources/extensions/kata/files.ts +730 -0
- package/src/resources/extensions/kata/gitignore.ts +165 -0
- package/src/resources/extensions/kata/guided-flow.ts +976 -0
- package/src/resources/extensions/kata/index.ts +556 -0
- package/src/resources/extensions/kata/metrics.ts +397 -0
- package/src/resources/extensions/kata/observability-validator.ts +408 -0
- package/src/resources/extensions/kata/package.json +11 -0
- package/src/resources/extensions/kata/paths.ts +346 -0
- package/src/resources/extensions/kata/preferences.ts +695 -0
- package/src/resources/extensions/kata/prompt-loader.ts +50 -0
- package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
- package/src/resources/extensions/kata/prompts/discuss.md +151 -0
- package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
- package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
- package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
- package/src/resources/extensions/kata/prompts/queue.md +85 -0
- package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
- package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
- package/src/resources/extensions/kata/prompts/system.md +341 -0
- package/src/resources/extensions/kata/session-forensics.ts +550 -0
- package/src/resources/extensions/kata/skill-discovery.ts +137 -0
- package/src/resources/extensions/kata/state.ts +509 -0
- package/src/resources/extensions/kata/templates/context.md +76 -0
- package/src/resources/extensions/kata/templates/decisions.md +8 -0
- package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/kata/templates/plan.md +133 -0
- package/src/resources/extensions/kata/templates/preferences.md +15 -0
- package/src/resources/extensions/kata/templates/project.md +31 -0
- package/src/resources/extensions/kata/templates/reassessment.md +28 -0
- package/src/resources/extensions/kata/templates/requirements.md +81 -0
- package/src/resources/extensions/kata/templates/research.md +46 -0
- package/src/resources/extensions/kata/templates/roadmap.md +118 -0
- package/src/resources/extensions/kata/templates/slice-context.md +58 -0
- package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
- package/src/resources/extensions/kata/templates/state.md +19 -0
- package/src/resources/extensions/kata/templates/task-plan.md +52 -0
- package/src/resources/extensions/kata/templates/task-summary.md +57 -0
- package/src/resources/extensions/kata/templates/uat.md +54 -0
- package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
- package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
- package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
- package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
- package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
- package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
- package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
- package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
- package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
- package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
- package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
- package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
- package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
- package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
- package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
- package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
- package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
- package/src/resources/extensions/kata/types.ts +159 -0
- package/src/resources/extensions/kata/unit-runtime.ts +163 -0
- package/src/resources/extensions/kata/workspace-index.ts +203 -0
- package/src/resources/extensions/kata/worktree.ts +182 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +68 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +822 -0
- package/src/resources/extensions/shared/next-action-ui.ts +235 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +92 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1293 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/dist/commands/task.d.ts +0 -9
- package/dist/commands/task.d.ts.map +0 -1
- package/dist/commands/task.js +0 -129
- package/dist/commands/task.js.map +0 -1
- package/dist/commands/task.test.d.ts +0 -2
- package/dist/commands/task.test.d.ts.map +0 -1
- package/dist/commands/task.test.js +0 -169
- package/dist/commands/task.test.js.map +0 -1
- package/dist/e2e/task-e2e.test.d.ts +0 -2
- package/dist/e2e/task-e2e.test.d.ts.map +0 -1
- package/dist/e2e/task-e2e.test.js +0 -173
- package/dist/e2e/task-e2e.test.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -93
- package/dist/index.js.map +0 -1
- package/dist/slug.d.ts +0 -2
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js +0 -12
- package/dist/slug.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -32
- package/dist/slug.test.js.map +0 -1
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdtempSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
existsSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
formatDoctorReport,
|
|
14
|
+
runKataDoctor,
|
|
15
|
+
summarizeDoctorIssues,
|
|
16
|
+
filterDoctorIssues,
|
|
17
|
+
selectDoctorScope,
|
|
18
|
+
} from "../doctor.js";
|
|
19
|
+
|
|
20
|
+
let passed = 0;
|
|
21
|
+
let failed = 0;
|
|
22
|
+
|
|
23
|
+
function assert(condition: boolean, message: string): void {
|
|
24
|
+
if (condition) {
|
|
25
|
+
passed++;
|
|
26
|
+
} else {
|
|
27
|
+
failed++;
|
|
28
|
+
console.error(` FAIL: ${message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function assertEq<T>(actual: T, expected: T, message: string): void {
|
|
33
|
+
if (JSON.stringify(actual) === JSON.stringify(expected)) {
|
|
34
|
+
passed++;
|
|
35
|
+
} else {
|
|
36
|
+
failed++;
|
|
37
|
+
console.error(
|
|
38
|
+
` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const tmpBase = mkdtempSync(join(tmpdir(), "kata-doctor-test-"));
|
|
44
|
+
const kata = join(tmpBase, ".kata");
|
|
45
|
+
const mDir = join(kata, "milestones", "M001");
|
|
46
|
+
const sDir = join(mDir, "slices", "S01");
|
|
47
|
+
const tDir = join(sDir, "tasks");
|
|
48
|
+
mkdirSync(tDir, { recursive: true });
|
|
49
|
+
|
|
50
|
+
writeFileSync(
|
|
51
|
+
join(mDir, "M001-ROADMAP.md"),
|
|
52
|
+
`# M001: Test Milestone
|
|
53
|
+
|
|
54
|
+
## Slices
|
|
55
|
+
- [ ] **S01: Demo Slice** \`risk:low\` \`depends:[]\`
|
|
56
|
+
> After this: demo works
|
|
57
|
+
`,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
writeFileSync(
|
|
61
|
+
join(sDir, "S01-PLAN.md"),
|
|
62
|
+
`# S01: Demo Slice
|
|
63
|
+
|
|
64
|
+
**Goal:** Demo
|
|
65
|
+
**Demo:** Demo
|
|
66
|
+
|
|
67
|
+
## Must-Haves
|
|
68
|
+
- done
|
|
69
|
+
|
|
70
|
+
## Tasks
|
|
71
|
+
- [x] **T01: Implement thing** \`est:10m\`
|
|
72
|
+
Task is complete.
|
|
73
|
+
`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
writeFileSync(
|
|
77
|
+
join(tDir, "T01-SUMMARY.md"),
|
|
78
|
+
`---
|
|
79
|
+
id: T01
|
|
80
|
+
parent: S01
|
|
81
|
+
milestone: M001
|
|
82
|
+
provides: []
|
|
83
|
+
requires: []
|
|
84
|
+
affects: []
|
|
85
|
+
key_files: []
|
|
86
|
+
key_decisions: []
|
|
87
|
+
patterns_established: []
|
|
88
|
+
observability_surfaces: []
|
|
89
|
+
drill_down_paths: []
|
|
90
|
+
duration: 10m
|
|
91
|
+
verification_result: passed
|
|
92
|
+
completed_at: 2026-03-09T00:00:00Z
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# T01: Implement thing
|
|
96
|
+
|
|
97
|
+
**Done**
|
|
98
|
+
|
|
99
|
+
## What Happened
|
|
100
|
+
Implemented.
|
|
101
|
+
|
|
102
|
+
## Diagnostics
|
|
103
|
+
- log
|
|
104
|
+
`,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
async function main(): Promise<void> {
|
|
108
|
+
console.log("\n=== doctor diagnose ===");
|
|
109
|
+
{
|
|
110
|
+
const report = await runKataDoctor(tmpBase, { fix: false });
|
|
111
|
+
assert(
|
|
112
|
+
!report.ok,
|
|
113
|
+
"report is not ok when completion artifacts are missing",
|
|
114
|
+
);
|
|
115
|
+
assert(
|
|
116
|
+
report.issues.some(
|
|
117
|
+
(issue) => issue.code === "all_tasks_done_missing_slice_summary",
|
|
118
|
+
),
|
|
119
|
+
"detects missing slice summary",
|
|
120
|
+
);
|
|
121
|
+
assert(
|
|
122
|
+
report.issues.some(
|
|
123
|
+
(issue) => issue.code === "all_tasks_done_missing_slice_uat",
|
|
124
|
+
),
|
|
125
|
+
"detects missing slice UAT",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log("\n=== doctor formatting ===");
|
|
130
|
+
{
|
|
131
|
+
const report = await runKataDoctor(tmpBase, { fix: false });
|
|
132
|
+
const summary = summarizeDoctorIssues(report.issues);
|
|
133
|
+
assertEq(summary.errors, 2, "two blocking errors in summary");
|
|
134
|
+
const scoped = filterDoctorIssues(report.issues, {
|
|
135
|
+
scope: "M001/S01",
|
|
136
|
+
includeWarnings: true,
|
|
137
|
+
});
|
|
138
|
+
assert(scoped.length >= 2, "scope filter keeps slice issues");
|
|
139
|
+
const text = formatDoctorReport(report, {
|
|
140
|
+
scope: "M001/S01",
|
|
141
|
+
includeWarnings: true,
|
|
142
|
+
maxIssues: 5,
|
|
143
|
+
});
|
|
144
|
+
assert(text.includes("Scope: M001/S01"), "formatted report shows scope");
|
|
145
|
+
assert(
|
|
146
|
+
text.includes("Top issue types:"),
|
|
147
|
+
"formatted report shows grouped issue types",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log("\n=== doctor default scope ===");
|
|
152
|
+
{
|
|
153
|
+
const scope = await selectDoctorScope(tmpBase);
|
|
154
|
+
assertEq(
|
|
155
|
+
scope,
|
|
156
|
+
"M001/S01",
|
|
157
|
+
"default doctor scope targets the active slice",
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log("\n=== doctor fix ===");
|
|
162
|
+
{
|
|
163
|
+
const report = await runKataDoctor(tmpBase, { fix: true });
|
|
164
|
+
if (report.fixesApplied.length < 3) console.error(report);
|
|
165
|
+
assert(report.fixesApplied.length >= 3, "applies multiple fixes");
|
|
166
|
+
assert(
|
|
167
|
+
existsSync(join(sDir, "S01-SUMMARY.md")),
|
|
168
|
+
"creates placeholder slice summary",
|
|
169
|
+
);
|
|
170
|
+
assert(existsSync(join(sDir, "S01-UAT.md")), "creates placeholder UAT");
|
|
171
|
+
|
|
172
|
+
const plan = readFileSync(join(sDir, "S01-PLAN.md"), "utf-8");
|
|
173
|
+
assert(plan.includes("- [x] **T01:"), "marks task checkbox done");
|
|
174
|
+
|
|
175
|
+
const roadmap = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
|
|
176
|
+
assert(roadmap.includes("- [x] **S01:"), "marks slice checkbox done");
|
|
177
|
+
|
|
178
|
+
const state = readFileSync(join(kata, "STATE.md"), "utf-8");
|
|
179
|
+
assert(state.includes("# Kata State"), "writes state file");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
rmSync(tmpBase, { recursive: true, force: true });
|
|
183
|
+
|
|
184
|
+
// ─── Milestone summary detection: missing summary ──────────────────────
|
|
185
|
+
console.log("\n=== doctor detects missing milestone summary ===");
|
|
186
|
+
{
|
|
187
|
+
const msBase = mkdtempSync(join(tmpdir(), "kata-doctor-ms-test-"));
|
|
188
|
+
const msKata = join(msBase, ".kata");
|
|
189
|
+
const msMDir = join(msKata, "milestones", "M001");
|
|
190
|
+
const msSDir = join(msMDir, "slices", "S01");
|
|
191
|
+
const msTDir = join(msSDir, "tasks");
|
|
192
|
+
mkdirSync(msTDir, { recursive: true });
|
|
193
|
+
|
|
194
|
+
// Roadmap with ALL slices [x] — milestone is complete by slice status
|
|
195
|
+
writeFileSync(
|
|
196
|
+
join(msMDir, "M001-ROADMAP.md"),
|
|
197
|
+
`# M001: Test Milestone
|
|
198
|
+
|
|
199
|
+
## Slices
|
|
200
|
+
- [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
|
|
201
|
+
> After this: done
|
|
202
|
+
`,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Slice has plan with all tasks done
|
|
206
|
+
writeFileSync(
|
|
207
|
+
join(msSDir, "S01-PLAN.md"),
|
|
208
|
+
`# S01: Done Slice
|
|
209
|
+
|
|
210
|
+
**Goal:** Done
|
|
211
|
+
**Demo:** Done
|
|
212
|
+
|
|
213
|
+
## Tasks
|
|
214
|
+
- [x] **T01: Done Task** \`est:10m\`
|
|
215
|
+
Done.
|
|
216
|
+
`,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// Task summary exists
|
|
220
|
+
writeFileSync(
|
|
221
|
+
join(msTDir, "T01-SUMMARY.md"),
|
|
222
|
+
`---
|
|
223
|
+
id: T01
|
|
224
|
+
parent: S01
|
|
225
|
+
milestone: M001
|
|
226
|
+
---
|
|
227
|
+
# T01: Done
|
|
228
|
+
**Done**
|
|
229
|
+
## What Happened
|
|
230
|
+
Done.
|
|
231
|
+
`,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Slice summary exists (so slice-level checks pass)
|
|
235
|
+
writeFileSync(
|
|
236
|
+
join(msSDir, "S01-SUMMARY.md"),
|
|
237
|
+
`---
|
|
238
|
+
id: S01
|
|
239
|
+
parent: M001
|
|
240
|
+
---
|
|
241
|
+
# S01: Done
|
|
242
|
+
`,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
// Slice UAT exists (so slice-level checks pass)
|
|
246
|
+
writeFileSync(join(msSDir, "S01-UAT.md"), `# S01 UAT\nDone.\n`);
|
|
247
|
+
|
|
248
|
+
// NO milestone summary — this is the condition we're detecting
|
|
249
|
+
|
|
250
|
+
const report = await runKataDoctor(msBase, { fix: false });
|
|
251
|
+
assert(
|
|
252
|
+
report.issues.some(
|
|
253
|
+
(issue) => issue.code === "all_slices_done_missing_milestone_summary",
|
|
254
|
+
),
|
|
255
|
+
"detects missing milestone summary when all slices are done",
|
|
256
|
+
);
|
|
257
|
+
const msIssue = report.issues.find(
|
|
258
|
+
(issue) => issue.code === "all_slices_done_missing_milestone_summary",
|
|
259
|
+
);
|
|
260
|
+
assertEq(
|
|
261
|
+
msIssue?.scope,
|
|
262
|
+
"milestone",
|
|
263
|
+
"milestone summary issue has scope 'milestone'",
|
|
264
|
+
);
|
|
265
|
+
assertEq(
|
|
266
|
+
msIssue?.severity,
|
|
267
|
+
"warning",
|
|
268
|
+
"milestone summary issue has severity 'warning'",
|
|
269
|
+
);
|
|
270
|
+
assertEq(
|
|
271
|
+
msIssue?.unitId,
|
|
272
|
+
"M001",
|
|
273
|
+
"milestone summary issue unitId is 'M001'",
|
|
274
|
+
);
|
|
275
|
+
assert(
|
|
276
|
+
msIssue?.message?.includes("SUMMARY") ?? false,
|
|
277
|
+
"milestone summary issue message mentions SUMMARY",
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
rmSync(msBase, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ─── Milestone summary detection: summary present (no false positive) ──
|
|
284
|
+
console.log("\n=== doctor does NOT flag milestone with summary ===");
|
|
285
|
+
{
|
|
286
|
+
const msBase = mkdtempSync(join(tmpdir(), "kata-doctor-ms-ok-test-"));
|
|
287
|
+
const msKata = join(msBase, ".kata");
|
|
288
|
+
const msMDir = join(msKata, "milestones", "M001");
|
|
289
|
+
const msSDir = join(msMDir, "slices", "S01");
|
|
290
|
+
const msTDir = join(msSDir, "tasks");
|
|
291
|
+
mkdirSync(msTDir, { recursive: true });
|
|
292
|
+
|
|
293
|
+
// Roadmap with ALL slices [x]
|
|
294
|
+
writeFileSync(
|
|
295
|
+
join(msMDir, "M001-ROADMAP.md"),
|
|
296
|
+
`# M001: Test Milestone
|
|
297
|
+
|
|
298
|
+
## Slices
|
|
299
|
+
- [x] **S01: Done Slice** \`risk:low\` \`depends:[]\`
|
|
300
|
+
> After this: done
|
|
301
|
+
`,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
writeFileSync(
|
|
305
|
+
join(msSDir, "S01-PLAN.md"),
|
|
306
|
+
`# S01: Done Slice
|
|
307
|
+
|
|
308
|
+
**Goal:** Done
|
|
309
|
+
**Demo:** Done
|
|
310
|
+
|
|
311
|
+
## Tasks
|
|
312
|
+
- [x] **T01: Done Task** \`est:10m\`
|
|
313
|
+
Done.
|
|
314
|
+
`,
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
writeFileSync(
|
|
318
|
+
join(msTDir, "T01-SUMMARY.md"),
|
|
319
|
+
`---
|
|
320
|
+
id: T01
|
|
321
|
+
parent: S01
|
|
322
|
+
milestone: M001
|
|
323
|
+
---
|
|
324
|
+
# T01: Done
|
|
325
|
+
**Done**
|
|
326
|
+
## What Happened
|
|
327
|
+
Done.
|
|
328
|
+
`,
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
writeFileSync(
|
|
332
|
+
join(msSDir, "S01-SUMMARY.md"),
|
|
333
|
+
`---
|
|
334
|
+
id: S01
|
|
335
|
+
parent: M001
|
|
336
|
+
---
|
|
337
|
+
# S01: Done
|
|
338
|
+
`,
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
writeFileSync(join(msSDir, "S01-UAT.md"), `# S01 UAT\nDone.\n`);
|
|
342
|
+
|
|
343
|
+
// Milestone summary EXISTS
|
|
344
|
+
writeFileSync(
|
|
345
|
+
join(msMDir, "M001-SUMMARY.md"),
|
|
346
|
+
`# M001 Summary\n\nMilestone complete.`,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const report = await runKataDoctor(msBase, { fix: false });
|
|
350
|
+
assert(
|
|
351
|
+
!report.issues.some(
|
|
352
|
+
(issue) => issue.code === "all_slices_done_missing_milestone_summary",
|
|
353
|
+
),
|
|
354
|
+
"does NOT report missing milestone summary when summary exists",
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
rmSync(msBase, { recursive: true, force: true });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ─── blocker_discovered_no_replan detection ────────────────────────────
|
|
361
|
+
console.log("\n=== doctor detects blocker_discovered_no_replan ===");
|
|
362
|
+
{
|
|
363
|
+
const bBase = mkdtempSync(join(tmpdir(), "kata-doctor-blocker-test-"));
|
|
364
|
+
const bKata = join(bBase, ".kata");
|
|
365
|
+
const bMDir = join(bKata, "milestones", "M001");
|
|
366
|
+
const bSDir = join(bMDir, "slices", "S01");
|
|
367
|
+
const bTDir = join(bSDir, "tasks");
|
|
368
|
+
mkdirSync(bTDir, { recursive: true });
|
|
369
|
+
|
|
370
|
+
writeFileSync(
|
|
371
|
+
join(bMDir, "M001-ROADMAP.md"),
|
|
372
|
+
`# M001: Test Milestone
|
|
373
|
+
|
|
374
|
+
## Slices
|
|
375
|
+
- [ ] **S01: Test Slice** \`risk:low\` \`depends:[]\`
|
|
376
|
+
> After this: stuff works
|
|
377
|
+
`,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
writeFileSync(
|
|
381
|
+
join(bSDir, "S01-PLAN.md"),
|
|
382
|
+
`# S01: Test Slice
|
|
383
|
+
|
|
384
|
+
**Goal:** Test
|
|
385
|
+
**Demo:** Test
|
|
386
|
+
|
|
387
|
+
## Tasks
|
|
388
|
+
- [x] **T01: First task** \`est:10m\`
|
|
389
|
+
First task.
|
|
390
|
+
|
|
391
|
+
- [ ] **T02: Second task** \`est:10m\`
|
|
392
|
+
Second task.
|
|
393
|
+
`,
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// Task summary with blocker_discovered: true
|
|
397
|
+
writeFileSync(
|
|
398
|
+
join(bTDir, "T01-SUMMARY.md"),
|
|
399
|
+
`---
|
|
400
|
+
id: T01
|
|
401
|
+
parent: S01
|
|
402
|
+
milestone: M001
|
|
403
|
+
provides: []
|
|
404
|
+
key_files: []
|
|
405
|
+
key_decisions: []
|
|
406
|
+
patterns_established: []
|
|
407
|
+
observability_surfaces: []
|
|
408
|
+
duration: 10m
|
|
409
|
+
verification_result: passed
|
|
410
|
+
completed_at: 2026-03-10T00:00:00Z
|
|
411
|
+
blocker_discovered: true
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
# T01: First task
|
|
415
|
+
|
|
416
|
+
**Found a blocker.**
|
|
417
|
+
|
|
418
|
+
## What Happened
|
|
419
|
+
|
|
420
|
+
Discovered an issue.
|
|
421
|
+
`,
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
// No REPLAN.md — should trigger the issue
|
|
425
|
+
const report = await runKataDoctor(bBase, { fix: false });
|
|
426
|
+
const blockerIssues = report.issues.filter(
|
|
427
|
+
(i) => i.code === "blocker_discovered_no_replan",
|
|
428
|
+
);
|
|
429
|
+
assert(blockerIssues.length > 0, "detects blocker_discovered_no_replan");
|
|
430
|
+
assertEq(
|
|
431
|
+
blockerIssues[0]?.severity,
|
|
432
|
+
"warning",
|
|
433
|
+
"blocker issue has warning severity",
|
|
434
|
+
);
|
|
435
|
+
assertEq(blockerIssues[0]?.scope, "slice", "blocker issue has slice scope");
|
|
436
|
+
assert(
|
|
437
|
+
blockerIssues[0]?.message?.includes("T01") ?? false,
|
|
438
|
+
"blocker issue message mentions T01",
|
|
439
|
+
);
|
|
440
|
+
assert(
|
|
441
|
+
blockerIssues[0]?.message?.includes("S01") ?? false,
|
|
442
|
+
"blocker issue message mentions S01",
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
rmSync(bBase, { recursive: true, force: true });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ─── blocker_discovered with REPLAN.md (no false positive) ─────────────
|
|
449
|
+
console.log("\n=== doctor does NOT flag blocker when REPLAN.md exists ===");
|
|
450
|
+
{
|
|
451
|
+
const bBase = mkdtempSync(join(tmpdir(), "kata-doctor-blocker-ok-test-"));
|
|
452
|
+
const bKata = join(bBase, ".kata");
|
|
453
|
+
const bMDir = join(bKata, "milestones", "M001");
|
|
454
|
+
const bSDir = join(bMDir, "slices", "S01");
|
|
455
|
+
const bTDir = join(bSDir, "tasks");
|
|
456
|
+
mkdirSync(bTDir, { recursive: true });
|
|
457
|
+
|
|
458
|
+
writeFileSync(
|
|
459
|
+
join(bMDir, "M001-ROADMAP.md"),
|
|
460
|
+
`# M001: Test Milestone
|
|
461
|
+
|
|
462
|
+
## Slices
|
|
463
|
+
- [ ] **S01: Test Slice** \`risk:low\` \`depends:[]\`
|
|
464
|
+
> After this: stuff works
|
|
465
|
+
`,
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
writeFileSync(
|
|
469
|
+
join(bSDir, "S01-PLAN.md"),
|
|
470
|
+
`# S01: Test Slice
|
|
471
|
+
|
|
472
|
+
**Goal:** Test
|
|
473
|
+
**Demo:** Test
|
|
474
|
+
|
|
475
|
+
## Tasks
|
|
476
|
+
- [x] **T01: First task** \`est:10m\`
|
|
477
|
+
First task.
|
|
478
|
+
|
|
479
|
+
- [ ] **T02: Second task** \`est:10m\`
|
|
480
|
+
Second task.
|
|
481
|
+
`,
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
writeFileSync(
|
|
485
|
+
join(bTDir, "T01-SUMMARY.md"),
|
|
486
|
+
`---
|
|
487
|
+
id: T01
|
|
488
|
+
parent: S01
|
|
489
|
+
milestone: M001
|
|
490
|
+
blocker_discovered: true
|
|
491
|
+
completed_at: 2026-03-10T00:00:00Z
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
# T01: First task
|
|
495
|
+
|
|
496
|
+
**Found a blocker.**
|
|
497
|
+
|
|
498
|
+
## What Happened
|
|
499
|
+
|
|
500
|
+
Discovered an issue.
|
|
501
|
+
`,
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
// REPLAN.md exists — should NOT trigger
|
|
505
|
+
writeFileSync(
|
|
506
|
+
join(bSDir, "S01-REPLAN.md"),
|
|
507
|
+
`# Replan\n\nAlready replanned.`,
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
const report = await runKataDoctor(bBase, { fix: false });
|
|
511
|
+
const blockerIssues = report.issues.filter(
|
|
512
|
+
(i) => i.code === "blocker_discovered_no_replan",
|
|
513
|
+
);
|
|
514
|
+
assertEq(
|
|
515
|
+
blockerIssues.length,
|
|
516
|
+
0,
|
|
517
|
+
"no blocker_discovered_no_replan when REPLAN.md exists",
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
rmSync(bBase, { recursive: true, force: true });
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ─── Must-have verification: all addressed → no issue ─────────────────
|
|
524
|
+
console.log(
|
|
525
|
+
"\n=== doctor: done task with must-haves all addressed → no issue ===",
|
|
526
|
+
);
|
|
527
|
+
{
|
|
528
|
+
const mhBase = mkdtempSync(join(tmpdir(), "kata-doctor-mh-ok-"));
|
|
529
|
+
const mhKata = join(mhBase, ".kata");
|
|
530
|
+
const mhMDir = join(mhKata, "milestones", "M001");
|
|
531
|
+
const mhSDir = join(mhMDir, "slices", "S01");
|
|
532
|
+
const mhTDir = join(mhSDir, "tasks");
|
|
533
|
+
mkdirSync(mhTDir, { recursive: true });
|
|
534
|
+
|
|
535
|
+
writeFileSync(
|
|
536
|
+
join(mhMDir, "M001-ROADMAP.md"),
|
|
537
|
+
`# M001: Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`,
|
|
538
|
+
);
|
|
539
|
+
writeFileSync(
|
|
540
|
+
join(mhSDir, "S01-PLAN.md"),
|
|
541
|
+
`# S01: Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [x] **T01: Implement** \`est:10m\`\n Done.\n`,
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
// Task plan with must-haves
|
|
545
|
+
writeFileSync(
|
|
546
|
+
join(mhTDir, "T01-PLAN.md"),
|
|
547
|
+
`# T01: Implement\n\n## Must-Haves\n\n- [ ] \`parseWidgets\` function exported\n- [ ] Unit tests pass with zero failures\n`,
|
|
548
|
+
);
|
|
549
|
+
|
|
550
|
+
// Summary mentioning both must-haves
|
|
551
|
+
writeFileSync(
|
|
552
|
+
join(mhTDir, "T01-SUMMARY.md"),
|
|
553
|
+
`---\nid: T01\nparent: S01\nmilestone: M001\n---\n# T01: Implement\n\n## What Happened\nAdded parseWidgets function. Unit tests pass with zero failures.\n`,
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
const report = await runKataDoctor(mhBase, { fix: false });
|
|
557
|
+
assert(
|
|
558
|
+
!report.issues.some(
|
|
559
|
+
(i) => i.code === "task_done_must_haves_not_verified",
|
|
560
|
+
),
|
|
561
|
+
"no must-have issue when all must-haves are addressed",
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
rmSync(mhBase, { recursive: true, force: true });
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ─── Must-have verification: not addressed → warning fired ───────────
|
|
568
|
+
console.log(
|
|
569
|
+
"\n=== doctor: done task with must-haves NOT addressed → warning ===",
|
|
570
|
+
);
|
|
571
|
+
{
|
|
572
|
+
const mhBase = mkdtempSync(join(tmpdir(), "kata-doctor-mh-fail-"));
|
|
573
|
+
const mhKata = join(mhBase, ".kata");
|
|
574
|
+
const mhMDir = join(mhKata, "milestones", "M001");
|
|
575
|
+
const mhSDir = join(mhMDir, "slices", "S01");
|
|
576
|
+
const mhTDir = join(mhSDir, "tasks");
|
|
577
|
+
mkdirSync(mhTDir, { recursive: true });
|
|
578
|
+
|
|
579
|
+
writeFileSync(
|
|
580
|
+
join(mhMDir, "M001-ROADMAP.md"),
|
|
581
|
+
`# M001: Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`,
|
|
582
|
+
);
|
|
583
|
+
writeFileSync(
|
|
584
|
+
join(mhSDir, "S01-PLAN.md"),
|
|
585
|
+
`# S01: Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [x] **T01: Implement** \`est:10m\`\n Done.\n`,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
// Task plan with 3 must-haves
|
|
589
|
+
writeFileSync(
|
|
590
|
+
join(mhTDir, "T01-PLAN.md"),
|
|
591
|
+
`# T01: Implement\n\n## Must-Haves\n\n- [ ] \`parseWidgets\` function exported\n- [ ] \`countWidgets\` utility added\n- [ ] Full regression suite passes\n`,
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
// Summary mentions only parseWidgets — the other two are missing
|
|
595
|
+
writeFileSync(
|
|
596
|
+
join(mhTDir, "T01-SUMMARY.md"),
|
|
597
|
+
`---\nid: T01\nparent: S01\nmilestone: M001\n---\n# T01: Implement\n\n## What Happened\nAdded parseWidgets function.\n`,
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
const report = await runKataDoctor(mhBase, { fix: false });
|
|
601
|
+
const mhIssue = report.issues.find(
|
|
602
|
+
(i) => i.code === "task_done_must_haves_not_verified",
|
|
603
|
+
);
|
|
604
|
+
assert(
|
|
605
|
+
!!mhIssue,
|
|
606
|
+
"must-have issue is fired when summary doesn't address all must-haves",
|
|
607
|
+
);
|
|
608
|
+
assertEq(
|
|
609
|
+
mhIssue?.severity,
|
|
610
|
+
"warning",
|
|
611
|
+
"must-have issue is warning severity",
|
|
612
|
+
);
|
|
613
|
+
assertEq(mhIssue?.scope, "task", "must-have issue scope is task");
|
|
614
|
+
assert(
|
|
615
|
+
mhIssue?.message?.includes("3 must-haves") ?? false,
|
|
616
|
+
"message mentions total must-have count",
|
|
617
|
+
);
|
|
618
|
+
assert(
|
|
619
|
+
mhIssue?.message?.includes("only 1") ?? false,
|
|
620
|
+
"message mentions addressed count",
|
|
621
|
+
);
|
|
622
|
+
assertEq(mhIssue?.fixable, false, "must-have issue is not fixable");
|
|
623
|
+
|
|
624
|
+
rmSync(mhBase, { recursive: true, force: true });
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// ─── Must-have verification: no task plan → no issue ─────────────────
|
|
628
|
+
console.log("\n=== doctor: done task with no task plan file → no issue ===");
|
|
629
|
+
{
|
|
630
|
+
const mhBase = mkdtempSync(join(tmpdir(), "kata-doctor-mh-noplan-"));
|
|
631
|
+
const mhKata = join(mhBase, ".kata");
|
|
632
|
+
const mhMDir = join(mhKata, "milestones", "M001");
|
|
633
|
+
const mhSDir = join(mhMDir, "slices", "S01");
|
|
634
|
+
const mhTDir = join(mhSDir, "tasks");
|
|
635
|
+
mkdirSync(mhTDir, { recursive: true });
|
|
636
|
+
|
|
637
|
+
writeFileSync(
|
|
638
|
+
join(mhMDir, "M001-ROADMAP.md"),
|
|
639
|
+
`# M001: Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`,
|
|
640
|
+
);
|
|
641
|
+
writeFileSync(
|
|
642
|
+
join(mhSDir, "S01-PLAN.md"),
|
|
643
|
+
`# S01: Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [x] **T01: Implement** \`est:10m\`\n Done.\n`,
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
// NO task plan file — just a summary
|
|
647
|
+
writeFileSync(
|
|
648
|
+
join(mhTDir, "T01-SUMMARY.md"),
|
|
649
|
+
`---\nid: T01\nparent: S01\nmilestone: M001\n---\n# T01: Implement\n\n## What Happened\nDone.\n`,
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
const report = await runKataDoctor(mhBase, { fix: false });
|
|
653
|
+
assert(
|
|
654
|
+
!report.issues.some(
|
|
655
|
+
(i) => i.code === "task_done_must_haves_not_verified",
|
|
656
|
+
),
|
|
657
|
+
"no must-have issue when task plan file doesn't exist",
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
rmSync(mhBase, { recursive: true, force: true });
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// ─── Must-have verification: plan exists but no Must-Haves section → no issue
|
|
664
|
+
console.log(
|
|
665
|
+
"\n=== doctor: done task with plan but no Must-Haves section → no issue ===",
|
|
666
|
+
);
|
|
667
|
+
{
|
|
668
|
+
const mhBase = mkdtempSync(join(tmpdir(), "kata-doctor-mh-nosect-"));
|
|
669
|
+
const mhKata = join(mhBase, ".kata");
|
|
670
|
+
const mhMDir = join(mhKata, "milestones", "M001");
|
|
671
|
+
const mhSDir = join(mhMDir, "slices", "S01");
|
|
672
|
+
const mhTDir = join(mhSDir, "tasks");
|
|
673
|
+
mkdirSync(mhTDir, { recursive: true });
|
|
674
|
+
|
|
675
|
+
writeFileSync(
|
|
676
|
+
join(mhMDir, "M001-ROADMAP.md"),
|
|
677
|
+
`# M001: Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`,
|
|
678
|
+
);
|
|
679
|
+
writeFileSync(
|
|
680
|
+
join(mhSDir, "S01-PLAN.md"),
|
|
681
|
+
`# S01: Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [x] **T01: Implement** \`est:10m\`\n Done.\n`,
|
|
682
|
+
);
|
|
683
|
+
|
|
684
|
+
// Task plan with NO Must-Haves section
|
|
685
|
+
writeFileSync(
|
|
686
|
+
join(mhTDir, "T01-PLAN.md"),
|
|
687
|
+
`# T01: Implement\n\n## Steps\n\n1. Do the thing.\n\n## Verification\n\n- Run tests.\n`,
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
writeFileSync(
|
|
691
|
+
join(mhTDir, "T01-SUMMARY.md"),
|
|
692
|
+
`---\nid: T01\nparent: S01\nmilestone: M001\n---\n# T01: Implement\n\n## What Happened\nDone.\n`,
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
const report = await runKataDoctor(mhBase, { fix: false });
|
|
696
|
+
assert(
|
|
697
|
+
!report.issues.some(
|
|
698
|
+
(i) => i.code === "task_done_must_haves_not_verified",
|
|
699
|
+
),
|
|
700
|
+
"no must-have issue when task plan has no Must-Haves section",
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
rmSync(mhBase, { recursive: true, force: true });
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
console.log(`\n${"=".repeat(40)}`);
|
|
707
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
708
|
+
if (failed > 0) {
|
|
709
|
+
process.exit(1);
|
|
710
|
+
} else {
|
|
711
|
+
console.log("All tests passed ✓");
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
main().catch((error) => {
|
|
716
|
+
console.error(error);
|
|
717
|
+
process.exit(1);
|
|
718
|
+
});
|