@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,185 @@
|
|
|
1
|
+
// Tests for inlinePriorMilestoneSummary — the cross-milestone context bridging helper.
|
|
2
|
+
//
|
|
3
|
+
// Scenarios covered:
|
|
4
|
+
// (A) M002 with M001-SUMMARY.md present → returns string containing "Prior Milestone Summary" and summary content
|
|
5
|
+
// (B) M001 (no prior milestone in dir) → returns null
|
|
6
|
+
// (C) M002 with no M001-SUMMARY.md written → returns null
|
|
7
|
+
// (D) M003 with M002 dir present but no M002-SUMMARY.md → returns null
|
|
8
|
+
|
|
9
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join, dirname } from "node:path";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
import { inlinePriorMilestoneSummary } from "../files.ts";
|
|
15
|
+
|
|
16
|
+
// ─── Worktree-aware prompt loader ──────────────────────────────────────────
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
// ─── Assertion helpers ─────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
let passed = 0;
|
|
22
|
+
let failed = 0;
|
|
23
|
+
|
|
24
|
+
function assert(condition: boolean, message: string): void {
|
|
25
|
+
if (condition) {
|
|
26
|
+
passed++;
|
|
27
|
+
} else {
|
|
28
|
+
failed++;
|
|
29
|
+
console.error(` FAIL: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function assertEq<T>(actual: T, expected: T, message: string): void {
|
|
34
|
+
if (JSON.stringify(actual) === JSON.stringify(expected)) {
|
|
35
|
+
passed++;
|
|
36
|
+
} else {
|
|
37
|
+
failed++;
|
|
38
|
+
console.error(
|
|
39
|
+
` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─── Fixture helpers ───────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
function createFixtureBase(): string {
|
|
47
|
+
const base = mkdtempSync(join(tmpdir(), "kata-plan-ms-test-"));
|
|
48
|
+
mkdirSync(join(base, ".kata", "milestones"), { recursive: true });
|
|
49
|
+
return base;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function writeMilestoneDir(base: string, mid: string): void {
|
|
53
|
+
mkdirSync(join(base, ".kata", "milestones", mid), { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function writeMilestoneSummary(
|
|
57
|
+
base: string,
|
|
58
|
+
mid: string,
|
|
59
|
+
content: string,
|
|
60
|
+
): void {
|
|
61
|
+
const dir = join(base, ".kata", "milestones", mid);
|
|
62
|
+
mkdirSync(dir, { recursive: true });
|
|
63
|
+
writeFileSync(join(dir, `${mid}-SUMMARY.md`), content);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function cleanup(base: string): void {
|
|
67
|
+
rmSync(base, { recursive: true, force: true });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
|
+
// Tests
|
|
72
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
73
|
+
|
|
74
|
+
async function main(): Promise<void> {
|
|
75
|
+
// ─── (A) M002 with M001-SUMMARY.md present ────────────────────────────────
|
|
76
|
+
console.log(
|
|
77
|
+
'\n── (A) M002 with M001-SUMMARY.md present → string containing "Prior Milestone Summary"',
|
|
78
|
+
);
|
|
79
|
+
{
|
|
80
|
+
const base = createFixtureBase();
|
|
81
|
+
try {
|
|
82
|
+
writeMilestoneDir(base, "M001");
|
|
83
|
+
writeMilestoneDir(base, "M002");
|
|
84
|
+
writeMilestoneSummary(
|
|
85
|
+
base,
|
|
86
|
+
"M001",
|
|
87
|
+
"# M001 Summary\n\nKey decisions: used TypeScript throughout.\n",
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const result = await inlinePriorMilestoneSummary("M002", base);
|
|
91
|
+
|
|
92
|
+
assert(
|
|
93
|
+
result !== null,
|
|
94
|
+
"(A) result is not null when prior milestone has SUMMARY",
|
|
95
|
+
);
|
|
96
|
+
assert(
|
|
97
|
+
typeof result === "string" &&
|
|
98
|
+
result.includes("Prior Milestone Summary"),
|
|
99
|
+
'(A) result contains "Prior Milestone Summary" label',
|
|
100
|
+
);
|
|
101
|
+
assert(
|
|
102
|
+
typeof result === "string" &&
|
|
103
|
+
result.includes("Key decisions: used TypeScript throughout."),
|
|
104
|
+
"(A) result contains the summary file content",
|
|
105
|
+
);
|
|
106
|
+
} finally {
|
|
107
|
+
cleanup(base);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── (B) M001 (no prior milestone in dir) ─────────────────────────────────
|
|
112
|
+
console.log("\n── (B) M001 — first milestone, no prior → null");
|
|
113
|
+
{
|
|
114
|
+
const base = createFixtureBase();
|
|
115
|
+
try {
|
|
116
|
+
writeMilestoneDir(base, "M001");
|
|
117
|
+
|
|
118
|
+
const result = await inlinePriorMilestoneSummary("M001", base);
|
|
119
|
+
|
|
120
|
+
assertEq(result, null, "(B) M001 with no prior milestone → null");
|
|
121
|
+
} finally {
|
|
122
|
+
cleanup(base);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── (C) M002 with no M001-SUMMARY.md ────────────────────────────────────
|
|
127
|
+
console.log("\n── (C) M002 with M001 dir but no M001-SUMMARY.md → null");
|
|
128
|
+
{
|
|
129
|
+
const base = createFixtureBase();
|
|
130
|
+
try {
|
|
131
|
+
writeMilestoneDir(base, "M001");
|
|
132
|
+
writeMilestoneDir(base, "M002");
|
|
133
|
+
// Intentionally do NOT write M001-SUMMARY.md
|
|
134
|
+
|
|
135
|
+
const result = await inlinePriorMilestoneSummary("M002", base);
|
|
136
|
+
|
|
137
|
+
assertEq(result, null, "(C) M002 when M001 has no SUMMARY file → null");
|
|
138
|
+
} finally {
|
|
139
|
+
cleanup(base);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── (D) M003 with M002 dir but no M002-SUMMARY.md ───────────────────────
|
|
144
|
+
console.log(
|
|
145
|
+
"\n── (D) M003, M002 is immediately prior but has no SUMMARY → null",
|
|
146
|
+
);
|
|
147
|
+
{
|
|
148
|
+
const base = createFixtureBase();
|
|
149
|
+
try {
|
|
150
|
+
writeMilestoneDir(base, "M001");
|
|
151
|
+
writeMilestoneDir(base, "M002");
|
|
152
|
+
writeMilestoneDir(base, "M003");
|
|
153
|
+
// M001 has a summary — but M002 (the immediately prior to M003) does NOT
|
|
154
|
+
writeMilestoneSummary(base, "M001", "# M001 Summary\n\nOld context.\n");
|
|
155
|
+
// Intentionally do NOT write M002-SUMMARY.md
|
|
156
|
+
|
|
157
|
+
const result = await inlinePriorMilestoneSummary("M003", base);
|
|
158
|
+
|
|
159
|
+
assertEq(
|
|
160
|
+
result,
|
|
161
|
+
null,
|
|
162
|
+
"(D) M003 when M002 (immediately prior) has no SUMMARY → null",
|
|
163
|
+
);
|
|
164
|
+
} finally {
|
|
165
|
+
cleanup(base);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
170
|
+
// Results
|
|
171
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
172
|
+
|
|
173
|
+
console.log(`\n${"=".repeat(40)}`);
|
|
174
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
175
|
+
if (failed > 0) {
|
|
176
|
+
process.exit(1);
|
|
177
|
+
} else {
|
|
178
|
+
console.log("All tests passed ✓");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main().catch((error) => {
|
|
183
|
+
console.error(error);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
});
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { validateTaskPlanContent, validateSlicePlanContent } from '../observability-validator.ts';
|
|
2
|
+
|
|
3
|
+
let passed = 0;
|
|
4
|
+
let failed = 0;
|
|
5
|
+
|
|
6
|
+
function assert(condition: boolean, message: string): void {
|
|
7
|
+
if (condition) passed++;
|
|
8
|
+
else {
|
|
9
|
+
failed++;
|
|
10
|
+
console.error(` FAIL: ${message}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function assertEq<T>(actual: T, expected: T, message: string): void {
|
|
15
|
+
if (JSON.stringify(actual) === JSON.stringify(expected)) passed++;
|
|
16
|
+
else {
|
|
17
|
+
failed++;
|
|
18
|
+
console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
23
|
+
// validateTaskPlanContent — empty/missing Steps section
|
|
24
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
25
|
+
|
|
26
|
+
console.log('\n=== validateTaskPlanContent: empty Steps section ===');
|
|
27
|
+
{
|
|
28
|
+
const content = `# T01: Some Task
|
|
29
|
+
|
|
30
|
+
## Description
|
|
31
|
+
|
|
32
|
+
Do something useful.
|
|
33
|
+
|
|
34
|
+
## Steps
|
|
35
|
+
|
|
36
|
+
## Verification
|
|
37
|
+
|
|
38
|
+
- Run the tests and confirm output.
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
42
|
+
const stepsIssues = issues.filter(i => i.ruleId === 'empty_steps_section');
|
|
43
|
+
assert(stepsIssues.length >= 1, 'empty Steps section produces empty_steps_section issue');
|
|
44
|
+
if (stepsIssues.length > 0) {
|
|
45
|
+
assertEq(stepsIssues[0].severity, 'warning', 'empty_steps_section severity is warning');
|
|
46
|
+
assertEq(stepsIssues[0].scope, 'task-plan', 'empty_steps_section scope is task-plan');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log('\n=== validateTaskPlanContent: missing Steps section entirely ===');
|
|
51
|
+
{
|
|
52
|
+
const content = `# T01: Some Task
|
|
53
|
+
|
|
54
|
+
## Description
|
|
55
|
+
|
|
56
|
+
Do something useful.
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
- Run the tests.
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
64
|
+
const stepsIssues = issues.filter(i => i.ruleId === 'empty_steps_section');
|
|
65
|
+
assert(stepsIssues.length >= 1, 'missing Steps section produces empty_steps_section issue');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// validateTaskPlanContent — placeholder-only Verification
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
console.log('\n=== validateTaskPlanContent: placeholder-only Verification ===');
|
|
73
|
+
{
|
|
74
|
+
const content = `# T01: Some Task
|
|
75
|
+
|
|
76
|
+
## Steps
|
|
77
|
+
|
|
78
|
+
1. Do the thing.
|
|
79
|
+
2. Do the other thing.
|
|
80
|
+
|
|
81
|
+
## Verification
|
|
82
|
+
|
|
83
|
+
- {{placeholder verification step}}
|
|
84
|
+
- {{another placeholder}}
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
88
|
+
const verifyIssues = issues.filter(i => i.ruleId === 'placeholder_verification');
|
|
89
|
+
assert(verifyIssues.length >= 1, 'placeholder-only Verification produces placeholder_verification issue');
|
|
90
|
+
if (verifyIssues.length > 0) {
|
|
91
|
+
assertEq(verifyIssues[0].severity, 'warning', 'placeholder_verification severity is warning');
|
|
92
|
+
assertEq(verifyIssues[0].scope, 'task-plan', 'placeholder_verification scope is task-plan');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('\n=== validateTaskPlanContent: Verification with only template text ===');
|
|
97
|
+
{
|
|
98
|
+
const content = `# T01: Some Task
|
|
99
|
+
|
|
100
|
+
## Steps
|
|
101
|
+
|
|
102
|
+
1. Do the thing.
|
|
103
|
+
|
|
104
|
+
## Verification
|
|
105
|
+
|
|
106
|
+
{{whatWasVerifiedAndHow — commands run, tests passed, behavior confirmed}}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
110
|
+
const verifyIssues = issues.filter(i => i.ruleId === 'placeholder_verification');
|
|
111
|
+
assert(verifyIssues.length >= 1, 'template-text-only Verification produces placeholder_verification issue');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
115
|
+
// validateSlicePlanContent — empty inline task entries
|
|
116
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
117
|
+
|
|
118
|
+
console.log('\n=== validateSlicePlanContent: empty inline task entries ===');
|
|
119
|
+
{
|
|
120
|
+
const content = `# S01: Some Slice
|
|
121
|
+
|
|
122
|
+
**Goal:** Build the thing.
|
|
123
|
+
**Demo:** It works.
|
|
124
|
+
|
|
125
|
+
## Tasks
|
|
126
|
+
|
|
127
|
+
- [ ] **T01: First Task** \`est:20m\`
|
|
128
|
+
|
|
129
|
+
- [ ] **T02: Second Task** \`est:15m\`
|
|
130
|
+
|
|
131
|
+
## Verification
|
|
132
|
+
|
|
133
|
+
- Run the tests.
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
const issues = validateSlicePlanContent('S01-PLAN.md', content);
|
|
137
|
+
const emptyTaskIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
|
|
138
|
+
assert(emptyTaskIssues.length >= 1, 'task entries with no description produce empty_task_entry issue');
|
|
139
|
+
if (emptyTaskIssues.length > 0) {
|
|
140
|
+
assertEq(emptyTaskIssues[0].severity, 'warning', 'empty_task_entry severity is warning');
|
|
141
|
+
assertEq(emptyTaskIssues[0].scope, 'slice-plan', 'empty_task_entry scope is slice-plan');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log('\n=== validateSlicePlanContent: task entries with content are fine ===');
|
|
146
|
+
{
|
|
147
|
+
const content = `# S01: Some Slice
|
|
148
|
+
|
|
149
|
+
**Goal:** Build the thing.
|
|
150
|
+
**Demo:** It works.
|
|
151
|
+
|
|
152
|
+
## Tasks
|
|
153
|
+
|
|
154
|
+
- [ ] **T01: First Task** \`est:20m\`
|
|
155
|
+
- Why: Because it matters.
|
|
156
|
+
- Files: \`src/index.ts\`
|
|
157
|
+
- Do: Implement the feature.
|
|
158
|
+
|
|
159
|
+
- [ ] **T02: Second Task** \`est:15m\`
|
|
160
|
+
- Why: Also important.
|
|
161
|
+
- Do: Add tests.
|
|
162
|
+
|
|
163
|
+
## Verification
|
|
164
|
+
|
|
165
|
+
- Run the tests.
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
const issues = validateSlicePlanContent('S01-PLAN.md', content);
|
|
169
|
+
const emptyTaskIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
|
|
170
|
+
assertEq(emptyTaskIssues.length, 0, 'task entries with description content produce no empty_task_entry issues');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
174
|
+
// validateTaskPlanContent — scope_estimate over threshold
|
|
175
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
176
|
+
|
|
177
|
+
console.log('\n=== validateTaskPlanContent: scope_estimate over threshold ===');
|
|
178
|
+
{
|
|
179
|
+
const content = `---
|
|
180
|
+
estimated_steps: 12
|
|
181
|
+
estimated_files: 15
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
# T01: Big Task
|
|
185
|
+
|
|
186
|
+
## Steps
|
|
187
|
+
|
|
188
|
+
1. Step one.
|
|
189
|
+
2. Step two.
|
|
190
|
+
3. Step three.
|
|
191
|
+
|
|
192
|
+
## Verification
|
|
193
|
+
|
|
194
|
+
- Check it works.
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
198
|
+
const stepsOverIssues = issues.filter(i => i.ruleId === 'scope_estimate_steps_high');
|
|
199
|
+
const filesOverIssues = issues.filter(i => i.ruleId === 'scope_estimate_files_high');
|
|
200
|
+
assert(stepsOverIssues.length >= 1, 'estimated_steps=12 (>=10) produces scope_estimate_steps_high issue');
|
|
201
|
+
assert(filesOverIssues.length >= 1, 'estimated_files=15 (>=12) produces scope_estimate_files_high issue');
|
|
202
|
+
if (stepsOverIssues.length > 0) {
|
|
203
|
+
assertEq(stepsOverIssues[0].severity, 'warning', 'scope_estimate_steps_high severity is warning');
|
|
204
|
+
assertEq(stepsOverIssues[0].scope, 'task-plan', 'scope_estimate_steps_high scope is task-plan');
|
|
205
|
+
}
|
|
206
|
+
if (filesOverIssues.length > 0) {
|
|
207
|
+
assertEq(filesOverIssues[0].severity, 'warning', 'scope_estimate_files_high severity is warning');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
212
|
+
// validateTaskPlanContent — scope_estimate within limits
|
|
213
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
214
|
+
|
|
215
|
+
console.log('\n=== validateTaskPlanContent: scope_estimate within limits ===');
|
|
216
|
+
{
|
|
217
|
+
const content = `---
|
|
218
|
+
estimated_steps: 4
|
|
219
|
+
estimated_files: 6
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
# T01: Small Task
|
|
223
|
+
|
|
224
|
+
## Steps
|
|
225
|
+
|
|
226
|
+
1. Do the thing.
|
|
227
|
+
|
|
228
|
+
## Verification
|
|
229
|
+
|
|
230
|
+
- Verify it works.
|
|
231
|
+
`;
|
|
232
|
+
|
|
233
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
234
|
+
const scopeIssues = issues.filter(i =>
|
|
235
|
+
i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
|
|
236
|
+
);
|
|
237
|
+
assertEq(scopeIssues.length, 0, 'scope_estimate within limits produces no scope issues');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
241
|
+
// validateTaskPlanContent — missing scope_estimate (no warning)
|
|
242
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
243
|
+
|
|
244
|
+
console.log('\n=== validateTaskPlanContent: missing scope_estimate ===');
|
|
245
|
+
{
|
|
246
|
+
const content = `# T01: No Frontmatter Task
|
|
247
|
+
|
|
248
|
+
## Steps
|
|
249
|
+
|
|
250
|
+
1. Do the thing.
|
|
251
|
+
|
|
252
|
+
## Verification
|
|
253
|
+
|
|
254
|
+
- Verify it works.
|
|
255
|
+
`;
|
|
256
|
+
|
|
257
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
258
|
+
const scopeIssues = issues.filter(i =>
|
|
259
|
+
i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
|
|
260
|
+
);
|
|
261
|
+
assertEq(scopeIssues.length, 0, 'missing scope_estimate produces no scope issues');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
console.log('\n=== validateTaskPlanContent: frontmatter without scope keys ===');
|
|
265
|
+
{
|
|
266
|
+
const content = `---
|
|
267
|
+
id: T01
|
|
268
|
+
parent: S01
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
# T01: Task With Other Frontmatter
|
|
272
|
+
|
|
273
|
+
## Steps
|
|
274
|
+
|
|
275
|
+
1. Do the thing.
|
|
276
|
+
|
|
277
|
+
## Verification
|
|
278
|
+
|
|
279
|
+
- Verify it works.
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
283
|
+
const scopeIssues = issues.filter(i =>
|
|
284
|
+
i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
|
|
285
|
+
);
|
|
286
|
+
assertEq(scopeIssues.length, 0, 'frontmatter without scope keys produces no scope issues');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
290
|
+
// Clean plans — no false positives
|
|
291
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
292
|
+
|
|
293
|
+
console.log('\n=== Clean task plan: no plan-quality issues ===');
|
|
294
|
+
{
|
|
295
|
+
const content = `---
|
|
296
|
+
estimated_steps: 5
|
|
297
|
+
estimated_files: 3
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
# T01: Well-Formed Task
|
|
301
|
+
|
|
302
|
+
## Description
|
|
303
|
+
|
|
304
|
+
A real task with real content.
|
|
305
|
+
|
|
306
|
+
## Steps
|
|
307
|
+
|
|
308
|
+
1. Read the input files.
|
|
309
|
+
2. Parse the configuration.
|
|
310
|
+
3. Transform the data.
|
|
311
|
+
4. Write the output.
|
|
312
|
+
5. Verify the results.
|
|
313
|
+
|
|
314
|
+
## Must-Haves
|
|
315
|
+
|
|
316
|
+
- [ ] Output file is valid JSON
|
|
317
|
+
- [ ] All input records are processed
|
|
318
|
+
|
|
319
|
+
## Verification
|
|
320
|
+
|
|
321
|
+
- Run \`node --test tests/transform.test.ts\` — all assertions pass
|
|
322
|
+
- Manually inspect output.json for correct structure
|
|
323
|
+
|
|
324
|
+
## Observability Impact
|
|
325
|
+
|
|
326
|
+
- Signals added/changed: structured error log on parse failure
|
|
327
|
+
- How a future agent inspects this: check stderr for JSON parse errors
|
|
328
|
+
- Failure state exposed: exit code 1 + error message on invalid input
|
|
329
|
+
`;
|
|
330
|
+
|
|
331
|
+
const issues = validateTaskPlanContent('T01-PLAN.md', content);
|
|
332
|
+
const planQualityIssues = issues.filter(i =>
|
|
333
|
+
i.ruleId === 'empty_steps_section' ||
|
|
334
|
+
i.ruleId === 'placeholder_verification' ||
|
|
335
|
+
i.ruleId === 'scope_estimate_steps_high' ||
|
|
336
|
+
i.ruleId === 'scope_estimate_files_high'
|
|
337
|
+
);
|
|
338
|
+
assertEq(planQualityIssues.length, 0, 'clean task plan produces no plan-quality issues');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log('\n=== Clean slice plan: no plan-quality issues ===');
|
|
342
|
+
{
|
|
343
|
+
const content = `# S01: Well-Formed Slice
|
|
344
|
+
|
|
345
|
+
**Goal:** Build a complete feature.
|
|
346
|
+
**Demo:** Run the test suite and see all green.
|
|
347
|
+
|
|
348
|
+
## Tasks
|
|
349
|
+
|
|
350
|
+
- [ ] **T01: Create tests** \`est:20m\`
|
|
351
|
+
- Why: Tests define the contract before implementation.
|
|
352
|
+
- Files: \`tests/feature.test.ts\`
|
|
353
|
+
- Do: Write comprehensive test assertions.
|
|
354
|
+
- Verify: Test file runs without syntax errors.
|
|
355
|
+
|
|
356
|
+
- [ ] **T02: Implement feature** \`est:30m\`
|
|
357
|
+
- Why: Core implementation.
|
|
358
|
+
- Files: \`src/feature.ts\`
|
|
359
|
+
- Do: Build the feature to make tests pass.
|
|
360
|
+
- Verify: All tests pass.
|
|
361
|
+
|
|
362
|
+
## Verification
|
|
363
|
+
|
|
364
|
+
- \`node --test tests/feature.test.ts\` — all assertions pass
|
|
365
|
+
- Check error output for diagnostic messages
|
|
366
|
+
|
|
367
|
+
## Observability / Diagnostics
|
|
368
|
+
|
|
369
|
+
- Runtime signals: structured error objects with error codes
|
|
370
|
+
- Inspection surfaces: test output shows pass/fail counts
|
|
371
|
+
- Failure visibility: exit code 1 on failure with descriptive message
|
|
372
|
+
- Redaction constraints: none
|
|
373
|
+
`;
|
|
374
|
+
|
|
375
|
+
const issues = validateSlicePlanContent('S01-PLAN.md', content);
|
|
376
|
+
const planQualityIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
|
|
377
|
+
assertEq(planQualityIssues.length, 0, 'clean slice plan produces no empty_task_entry issues');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
381
|
+
// Results
|
|
382
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
383
|
+
|
|
384
|
+
console.log(`\nResults: ${passed} passed, ${failed} failed`);
|
|
385
|
+
if (failed > 0) process.exit(1);
|
|
386
|
+
console.log('All tests passed ✓');
|