sdd-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +566 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +308 -0
- package/dist/commands/ai-exec.d.ts +1 -0
- package/dist/commands/ai-exec.js +18 -0
- package/dist/commands/ai-status.d.ts +1 -0
- package/dist/commands/ai-status.js +12 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +101 -0
- package/dist/commands/gen-architecture.d.ts +1 -0
- package/dist/commands/gen-architecture.js +61 -0
- package/dist/commands/gen-best-practices.d.ts +1 -0
- package/dist/commands/gen-best-practices.js +64 -0
- package/dist/commands/gen-functional-spec.d.ts +1 -0
- package/dist/commands/gen-functional-spec.js +67 -0
- package/dist/commands/gen-project-readme.d.ts +1 -0
- package/dist/commands/gen-project-readme.js +72 -0
- package/dist/commands/gen-requirements.d.ts +1 -0
- package/dist/commands/gen-requirements.js +7 -0
- package/dist/commands/gen-technical-spec.d.ts +1 -0
- package/dist/commands/gen-technical-spec.js +67 -0
- package/dist/commands/gen-utils.d.ts +4 -0
- package/dist/commands/gen-utils.js +44 -0
- package/dist/commands/hello.d.ts +1 -0
- package/dist/commands/hello.js +63 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +9 -0
- package/dist/commands/learn-deliver.d.ts +1 -0
- package/dist/commands/learn-deliver.js +55 -0
- package/dist/commands/learn-refine.d.ts +1 -0
- package/dist/commands/learn-refine.js +71 -0
- package/dist/commands/learn-start.d.ts +1 -0
- package/dist/commands/learn-start.js +63 -0
- package/dist/commands/learn-utils.d.ts +22 -0
- package/dist/commands/learn-utils.js +78 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +69 -0
- package/dist/commands/pr-audit.d.ts +1 -0
- package/dist/commands/pr-audit.js +59 -0
- package/dist/commands/pr-finish.d.ts +1 -0
- package/dist/commands/pr-finish.js +51 -0
- package/dist/commands/pr-report.d.ts +1 -0
- package/dist/commands/pr-report.js +59 -0
- package/dist/commands/pr-respond.d.ts +1 -0
- package/dist/commands/pr-respond.js +65 -0
- package/dist/commands/pr-start.d.ts +1 -0
- package/dist/commands/pr-start.js +79 -0
- package/dist/commands/pr-utils.d.ts +8 -0
- package/dist/commands/pr-utils.js +54 -0
- package/dist/commands/req-archive.d.ts +1 -0
- package/dist/commands/req-archive.js +33 -0
- package/dist/commands/req-create.d.ts +10 -0
- package/dist/commands/req-create.js +94 -0
- package/dist/commands/req-export.d.ts +1 -0
- package/dist/commands/req-export.js +37 -0
- package/dist/commands/req-finish.d.ts +1 -0
- package/dist/commands/req-finish.js +120 -0
- package/dist/commands/req-lint.d.ts +1 -0
- package/dist/commands/req-lint.js +58 -0
- package/dist/commands/req-list.d.ts +1 -0
- package/dist/commands/req-list.js +36 -0
- package/dist/commands/req-plan.d.ts +1 -0
- package/dist/commands/req-plan.js +200 -0
- package/dist/commands/req-refine.d.ts +1 -0
- package/dist/commands/req-refine.js +108 -0
- package/dist/commands/req-report.d.ts +1 -0
- package/dist/commands/req-report.js +44 -0
- package/dist/commands/req-start.d.ts +1 -0
- package/dist/commands/req-start.js +131 -0
- package/dist/commands/req-status.d.ts +1 -0
- package/dist/commands/req-status.js +29 -0
- package/dist/commands/route.d.ts +1 -0
- package/dist/commands/route.js +30 -0
- package/dist/commands/test-plan.d.ts +1 -0
- package/dist/commands/test-plan.js +81 -0
- package/dist/context/flags.d.ts +7 -0
- package/dist/context/flags.js +17 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +10 -0
- package/dist/providers/codex.d.ts +7 -0
- package/dist/providers/codex.js +19 -0
- package/dist/router/flow.d.ts +1 -0
- package/dist/router/flow.js +17 -0
- package/dist/router/intent.d.ts +3 -0
- package/dist/router/intent.js +69 -0
- package/dist/router/prompt-map.d.ts +1 -0
- package/dist/router/prompt-map.js +20 -0
- package/dist/router/prompt-packs.d.ts +8 -0
- package/dist/router/prompt-packs.js +20 -0
- package/dist/router/validate-prompt-packs.d.ts +4 -0
- package/dist/router/validate-prompt-packs.js +16 -0
- package/dist/templates/render.d.ts +2 -0
- package/dist/templates/render.js +25 -0
- package/dist/templates/validate.d.ts +4 -0
- package/dist/templates/validate.js +58 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/dist/ui/prompt.d.ts +2 -0
- package/dist/ui/prompt.js +49 -0
- package/dist/utils/list.d.ts +2 -0
- package/dist/utils/list.js +20 -0
- package/dist/validation/validate.d.ts +4 -0
- package/dist/validation/validate.js +20 -0
- package/dist/workspace/index.d.ts +21 -0
- package/dist/workspace/index.js +103 -0
- package/flows/ADMISSIONS_ADMIN.md +33 -0
- package/flows/ART.md +33 -0
- package/flows/BUG_FIX.md +32 -0
- package/flows/COURT_SYSTEM.md +33 -0
- package/flows/DATA_SCIENTIST.md +33 -0
- package/flows/ECOMMERCE.md +33 -0
- package/flows/ECONOMICS.md +33 -0
- package/flows/GRAPHIC_DESIGN.md +33 -0
- package/flows/HISTORY.md +33 -0
- package/flows/LAWYER.md +34 -0
- package/flows/PROGRAMMER.md +33 -0
- package/flows/PR_REVIEW.md +33 -0
- package/flows/README.md +29 -0
- package/flows/RETAIL_STORE.md +33 -0
- package/flows/SOCIOLOGY.md +33 -0
- package/flows/STATE_ADMIN.md +33 -0
- package/flows/STUDENT_UNIVERSITY.md +33 -0
- package/flows/TAXES_ADMIN.md +33 -0
- package/flows/TEACHER.md +33 -0
- package/package.json +32 -0
- package/router/BUG_FIX.flow.md +63 -0
- package/router/BUSINESS.flow.md +57 -0
- package/router/DATA_SCIENCE.flow.md +58 -0
- package/router/DESIGN.flow.md +58 -0
- package/router/FLOW_TEMPLATE.md +26 -0
- package/router/GENERIC.flow.md +37 -0
- package/router/HUMANITIES.flow.md +58 -0
- package/router/LEARN.flow.md +52 -0
- package/router/LEGAL.flow.md +58 -0
- package/router/PR_REVIEW.flow.md +55 -0
- package/router/README.md +23 -0
- package/router/SOFTWARE_FEATURE.flow.md +59 -0
- package/schemas/architecture.schema.json +13 -0
- package/schemas/decision-log.schema.json +16 -0
- package/schemas/diagram.schema.json +11 -0
- package/schemas/domain.schema.json +20 -0
- package/schemas/functional-spec.schema.json +15 -0
- package/schemas/gate.schema.json +10 -0
- package/schemas/learn-session.schema.json +15 -0
- package/schemas/pr-review.schema.json +20 -0
- package/schemas/progress-log.schema.json +21 -0
- package/schemas/project-readme.schema.json +23 -0
- package/schemas/project.schema.json +16 -0
- package/schemas/prompt-pack.schema.json +12 -0
- package/schemas/quality.schema.json +23 -0
- package/schemas/requirement.schema.json +34 -0
- package/schemas/role.schema.json +17 -0
- package/schemas/router-flow.schema.json +15 -0
- package/schemas/router-intent.schema.json +13 -0
- package/schemas/technical-spec.schema.json +15 -0
- package/schemas/template.schema.json +10 -0
- package/schemas/test-plan.schema.json +13 -0
- package/schemas/workspace.schema.json +12 -0
- package/templates/architecture.md +16 -0
- package/templates/changelog.md +3 -0
- package/templates/ci-checklist.md +14 -0
- package/templates/decision-log.md +16 -0
- package/templates/diagrams/component.mmd +3 -0
- package/templates/diagrams/container.mmd +3 -0
- package/templates/diagrams/context.mmd +3 -0
- package/templates/functional-spec.md +22 -0
- package/templates/gate-index.json +20 -0
- package/templates/implementation-plan.md +13 -0
- package/templates/pr-comment-audit.md +19 -0
- package/templates/pr-comment-lifecycle.md +11 -0
- package/templates/pr-comment-severity.md +13 -0
- package/templates/pr-dispute-resolution.md +16 -0
- package/templates/pr-metrics.md +10 -0
- package/templates/pr-response-generator.md +11 -0
- package/templates/pr-response-style.md +13 -0
- package/templates/pr-review-report.md +22 -0
- package/templates/pr-review-summary.md +16 -0
- package/templates/progress-log.md +6 -0
- package/templates/project-readme.md +19 -0
- package/templates/prompt-pack-index.json +127 -0
- package/templates/quality.yml +17 -0
- package/templates/requirement.md +33 -0
- package/templates/summary.md +10 -0
- package/templates/technical-spec.md +22 -0
- package/templates/template-index.json +212 -0
- package/templates/test-plan.md +16 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runReqStart = runReqStart;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const prompt_1 = require("../ui/prompt");
|
|
10
|
+
const flags_1 = require("../context/flags");
|
|
11
|
+
const index_1 = require("../workspace/index");
|
|
12
|
+
const render_1 = require("../templates/render");
|
|
13
|
+
const list_1 = require("../utils/list");
|
|
14
|
+
const validate_1 = require("../validation/validate");
|
|
15
|
+
function findRequirementDir(workspaceRoot, project, reqId) {
|
|
16
|
+
const backlog = path_1.default.join(workspaceRoot, project, "requirements", "backlog", reqId);
|
|
17
|
+
const wip = path_1.default.join(workspaceRoot, project, "requirements", "wip", reqId);
|
|
18
|
+
const inProgress = path_1.default.join(workspaceRoot, project, "requirements", "in-progress", reqId);
|
|
19
|
+
if (fs_1.default.existsSync(backlog))
|
|
20
|
+
return backlog;
|
|
21
|
+
if (fs_1.default.existsSync(wip))
|
|
22
|
+
return wip;
|
|
23
|
+
if (fs_1.default.existsSync(inProgress))
|
|
24
|
+
return inProgress;
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
async function runReqStart() {
|
|
28
|
+
const projectName = await (0, prompt_1.ask)("Project name: ");
|
|
29
|
+
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
30
|
+
if (!projectName || !reqId) {
|
|
31
|
+
console.log("Project name and requirement ID are required.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
35
|
+
const requirementDir = findRequirementDir(workspace.root, projectName, reqId);
|
|
36
|
+
if (!requirementDir) {
|
|
37
|
+
console.log("Requirement not found.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const requiredSpecs = [
|
|
41
|
+
{ file: "functional-spec.json", schema: "functional-spec.schema.json" },
|
|
42
|
+
{ file: "technical-spec.json", schema: "technical-spec.schema.json" },
|
|
43
|
+
{ file: "architecture.json", schema: "architecture.schema.json" },
|
|
44
|
+
{ file: "test-plan.json", schema: "test-plan.schema.json" }
|
|
45
|
+
];
|
|
46
|
+
const missing = requiredSpecs.filter((spec) => !fs_1.default.existsSync(path_1.default.join(requirementDir, spec.file)));
|
|
47
|
+
if (missing.length > 0) {
|
|
48
|
+
console.log("Cannot start. Missing specs:");
|
|
49
|
+
missing.forEach((spec) => console.log(`- ${spec.file}`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const spec of requiredSpecs) {
|
|
53
|
+
const data = JSON.parse(fs_1.default.readFileSync(path_1.default.join(requirementDir, spec.file), "utf-8"));
|
|
54
|
+
const result = (0, validate_1.validateJson)(spec.schema, data);
|
|
55
|
+
if (!result.valid) {
|
|
56
|
+
console.log(`Spec validation failed for ${spec.file}:`);
|
|
57
|
+
result.errors.forEach((error) => console.log(`- ${error}`));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const inProgressDir = path_1.default.join(workspace.root, projectName, "requirements", "in-progress", reqId);
|
|
62
|
+
if (!requirementDir.includes(path_1.default.join("requirements", "in-progress"))) {
|
|
63
|
+
fs_1.default.mkdirSync(path_1.default.dirname(inProgressDir), { recursive: true });
|
|
64
|
+
fs_1.default.renameSync(requirementDir, inProgressDir);
|
|
65
|
+
(0, index_1.updateProjectStatus)(workspace, projectName, "in-progress");
|
|
66
|
+
}
|
|
67
|
+
const targetDir = fs_1.default.existsSync(inProgressDir) ? inProgressDir : requirementDir;
|
|
68
|
+
const milestones = await (0, prompt_1.ask)("Milestones - comma separated: ");
|
|
69
|
+
const tasks = await (0, prompt_1.ask)("Tasks - comma separated: ");
|
|
70
|
+
const dependencies = await (0, prompt_1.ask)("Dependencies - comma separated: ");
|
|
71
|
+
const risks = await (0, prompt_1.ask)("Risks - comma separated: ");
|
|
72
|
+
const flags = (0, flags_1.getFlags)();
|
|
73
|
+
const improveNote = flags.improve ? await (0, prompt_1.ask)("Improve focus (optional): ") : "";
|
|
74
|
+
const implementationTemplate = (0, render_1.loadTemplate)("implementation-plan");
|
|
75
|
+
const rendered = (0, render_1.renderTemplate)(implementationTemplate, {
|
|
76
|
+
title: projectName,
|
|
77
|
+
milestones: (0, list_1.formatList)(milestones),
|
|
78
|
+
tasks: (0, list_1.formatList)(tasks),
|
|
79
|
+
dependencies: (0, list_1.formatList)(dependencies),
|
|
80
|
+
risks: (0, list_1.formatList)(risks)
|
|
81
|
+
});
|
|
82
|
+
const qualityPath = path_1.default.join(targetDir, "quality.yml");
|
|
83
|
+
if (!fs_1.default.existsSync(qualityPath)) {
|
|
84
|
+
const qualityTemplate = (0, render_1.loadTemplate)("quality");
|
|
85
|
+
fs_1.default.writeFileSync(qualityPath, qualityTemplate, "utf-8");
|
|
86
|
+
}
|
|
87
|
+
const qualityJson = {
|
|
88
|
+
rules: ["single-responsibility", "tests-for-critical-flows"],
|
|
89
|
+
thresholds: { coverage: "80%", complexity: "10" },
|
|
90
|
+
profiles: {}
|
|
91
|
+
};
|
|
92
|
+
const validation = (0, validate_1.validateJson)("quality.schema.json", qualityJson);
|
|
93
|
+
if (!validation.valid) {
|
|
94
|
+
console.log("Quality validation failed:");
|
|
95
|
+
validation.errors.forEach((error) => console.log(`- ${error}`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
fs_1.default.writeFileSync(path_1.default.join(targetDir, "implementation-plan.md"), rendered, "utf-8");
|
|
99
|
+
fs_1.default.writeFileSync(path_1.default.join(targetDir, "quality.json"), JSON.stringify(qualityJson, null, 2), "utf-8");
|
|
100
|
+
const decisionTemplate = (0, render_1.loadTemplate)("decision-log");
|
|
101
|
+
const decisionRendered = (0, render_1.renderTemplate)(decisionTemplate, {
|
|
102
|
+
id: "ADR-0001",
|
|
103
|
+
title: "Initial implementation plan",
|
|
104
|
+
status: "accepted",
|
|
105
|
+
context: "Implementation kickoff",
|
|
106
|
+
decision: "Proceed with planned milestones",
|
|
107
|
+
consequences: "Defines first iteration scope",
|
|
108
|
+
date: new Date().toISOString()
|
|
109
|
+
});
|
|
110
|
+
const decisionDir = path_1.default.join(targetDir, "decision-log");
|
|
111
|
+
fs_1.default.mkdirSync(decisionDir, { recursive: true });
|
|
112
|
+
fs_1.default.writeFileSync(path_1.default.join(decisionDir, "ADR-0001.md"), decisionRendered, "utf-8");
|
|
113
|
+
const progressLog = path_1.default.join(targetDir, "progress-log.md");
|
|
114
|
+
if (!fs_1.default.existsSync(progressLog)) {
|
|
115
|
+
fs_1.default.writeFileSync(progressLog, "# Progress Log\n\n", "utf-8");
|
|
116
|
+
}
|
|
117
|
+
const logEntry = `\n- ${new Date().toISOString()} started implementation for ${reqId}\n`;
|
|
118
|
+
fs_1.default.appendFileSync(progressLog, logEntry, "utf-8");
|
|
119
|
+
if (flags.improve) {
|
|
120
|
+
const improveEntry = `\n- ${new Date().toISOString()} improve: ${improveNote || "refinement requested"}\n`;
|
|
121
|
+
fs_1.default.appendFileSync(progressLog, improveEntry, "utf-8");
|
|
122
|
+
}
|
|
123
|
+
const changelog = path_1.default.join(targetDir, "changelog.md");
|
|
124
|
+
if (!fs_1.default.existsSync(changelog)) {
|
|
125
|
+
fs_1.default.writeFileSync(changelog, "# Changelog\n\n", "utf-8");
|
|
126
|
+
}
|
|
127
|
+
const changeEntry = `\n- ${new Date().toISOString()} started implementation for ${reqId}\n`;
|
|
128
|
+
fs_1.default.appendFileSync(changelog, changeEntry, "utf-8");
|
|
129
|
+
console.log(`Implementation plan generated in ${targetDir}`);
|
|
130
|
+
console.log(`Status updated to in-progress for ${projectName}`);
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runReqStatus(): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runReqStatus = runReqStatus;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const prompt_1 = require("../ui/prompt");
|
|
10
|
+
const index_1 = require("../workspace/index");
|
|
11
|
+
async function runReqStatus() {
|
|
12
|
+
const projectName = await (0, prompt_1.ask)("Project name: ");
|
|
13
|
+
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
14
|
+
if (!projectName || !reqId) {
|
|
15
|
+
console.log("Project name and requirement ID are required.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
19
|
+
const base = path_1.default.join(workspace.root, projectName, "requirements");
|
|
20
|
+
const statuses = ["backlog", "wip", "in-progress", "done", "archived"];
|
|
21
|
+
for (const status of statuses) {
|
|
22
|
+
const candidate = path_1.default.join(base, status, reqId);
|
|
23
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
24
|
+
console.log(`${reqId} is in ${status}`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
console.log("Requirement not found.");
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runRoute(input: string): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runRoute = runRoute;
|
|
4
|
+
const intent_1 = require("../router/intent");
|
|
5
|
+
const flow_1 = require("../router/flow");
|
|
6
|
+
const prompt_packs_1 = require("../router/prompt-packs");
|
|
7
|
+
function runRoute(input) {
|
|
8
|
+
const intent = (0, intent_1.classifyIntent)(input);
|
|
9
|
+
const flow = (0, flow_1.loadFlow)(intent.flow);
|
|
10
|
+
const packs = (0, prompt_packs_1.loadPromptPacks)();
|
|
11
|
+
const packIds = intent_1.FLOW_PROMPT_PACKS[intent.flow] ?? [];
|
|
12
|
+
console.log(JSON.stringify(intent, null, 2));
|
|
13
|
+
if (flow) {
|
|
14
|
+
console.log("\n--- Flow script ---\n");
|
|
15
|
+
console.log(flow);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log("\nNo flow script found.");
|
|
19
|
+
}
|
|
20
|
+
if (packIds.length > 0) {
|
|
21
|
+
console.log("\n--- Prompt packs ---\n");
|
|
22
|
+
for (const packId of packIds) {
|
|
23
|
+
const pack = (0, prompt_packs_1.getPromptPackById)(packs, packId);
|
|
24
|
+
if (!pack)
|
|
25
|
+
continue;
|
|
26
|
+
console.log(`Pack: ${pack.id}`);
|
|
27
|
+
pack.questions.forEach((question) => console.log(`- ${question}`));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runTestPlan(): Promise<void>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runTestPlan = runTestPlan;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const prompt_1 = require("../ui/prompt");
|
|
10
|
+
const index_1 = require("../workspace/index");
|
|
11
|
+
const render_1 = require("../templates/render");
|
|
12
|
+
const list_1 = require("../utils/list");
|
|
13
|
+
const validate_1 = require("../validation/validate");
|
|
14
|
+
const flags_1 = require("../context/flags");
|
|
15
|
+
function findRequirementDir(workspaceRoot, project, reqId) {
|
|
16
|
+
const base = path_1.default.join(workspaceRoot, project, "requirements");
|
|
17
|
+
const statuses = ["backlog", "wip", "in-progress", "done", "archived"];
|
|
18
|
+
for (const status of statuses) {
|
|
19
|
+
const candidate = path_1.default.join(base, status, reqId);
|
|
20
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
21
|
+
return candidate;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
async function runTestPlan() {
|
|
27
|
+
const projectName = await (0, prompt_1.ask)("Project name: ");
|
|
28
|
+
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
29
|
+
if (!projectName || !reqId) {
|
|
30
|
+
console.log("Project name and requirement ID are required.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
34
|
+
const requirementDir = findRequirementDir(workspace.root, projectName, reqId);
|
|
35
|
+
if (!requirementDir) {
|
|
36
|
+
console.log("Requirement not found.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const criticalPaths = await (0, prompt_1.ask)("Test critical paths - comma separated: ");
|
|
40
|
+
const edgeCases = await (0, prompt_1.ask)("Test edge cases - comma separated: ");
|
|
41
|
+
const acceptanceTests = await (0, prompt_1.ask)("Acceptance tests - comma separated: ");
|
|
42
|
+
const regressions = await (0, prompt_1.ask)("Regression tests - comma separated: ");
|
|
43
|
+
const coverageTarget = await (0, prompt_1.ask)("Coverage target: ");
|
|
44
|
+
const flags = (0, flags_1.getFlags)();
|
|
45
|
+
const improveNote = flags.improve ? await (0, prompt_1.ask)("Improve focus (optional): ") : "";
|
|
46
|
+
const testPlanJson = {
|
|
47
|
+
criticalPaths: (0, list_1.parseList)(criticalPaths),
|
|
48
|
+
edgeCases: (0, list_1.parseList)(edgeCases),
|
|
49
|
+
coverageTarget: coverageTarget || "N/A",
|
|
50
|
+
acceptanceTests: (0, list_1.parseList)(acceptanceTests),
|
|
51
|
+
regressions: (0, list_1.parseList)(regressions)
|
|
52
|
+
};
|
|
53
|
+
const validation = (0, validate_1.validateJson)("test-plan.schema.json", testPlanJson);
|
|
54
|
+
if (!validation.valid) {
|
|
55
|
+
console.log("Test plan validation failed:");
|
|
56
|
+
validation.errors.forEach((error) => console.log(`- ${error}`));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const template = (0, render_1.loadTemplate)("test-plan");
|
|
60
|
+
const rendered = (0, render_1.renderTemplate)(template, {
|
|
61
|
+
title: projectName,
|
|
62
|
+
critical_paths: (0, list_1.formatList)(criticalPaths),
|
|
63
|
+
edge_cases: (0, list_1.formatList)(edgeCases),
|
|
64
|
+
acceptance_tests: (0, list_1.formatList)(acceptanceTests),
|
|
65
|
+
regressions: (0, list_1.formatList)(regressions),
|
|
66
|
+
coverage_target: coverageTarget || "N/A"
|
|
67
|
+
});
|
|
68
|
+
fs_1.default.writeFileSync(path_1.default.join(requirementDir, "test-plan.md"), rendered, "utf-8");
|
|
69
|
+
fs_1.default.writeFileSync(path_1.default.join(requirementDir, "test-plan.json"), JSON.stringify(testPlanJson, null, 2), "utf-8");
|
|
70
|
+
const progressLog = path_1.default.join(requirementDir, "progress-log.md");
|
|
71
|
+
if (!fs_1.default.existsSync(progressLog)) {
|
|
72
|
+
fs_1.default.writeFileSync(progressLog, "# Progress Log\n\n", "utf-8");
|
|
73
|
+
}
|
|
74
|
+
const logEntry = `\n- ${new Date().toISOString()} updated test plan for ${reqId}\n`;
|
|
75
|
+
fs_1.default.appendFileSync(progressLog, logEntry, "utf-8");
|
|
76
|
+
if (flags.improve) {
|
|
77
|
+
const improveEntry = `\n- ${new Date().toISOString()} improve: ${improveNote || "refinement requested"}\n`;
|
|
78
|
+
fs_1.default.appendFileSync(progressLog, improveEntry, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
console.log(`Test plan updated in ${requirementDir}`);
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setFlags = setFlags;
|
|
4
|
+
exports.getFlags = getFlags;
|
|
5
|
+
const flags = {
|
|
6
|
+
approve: false,
|
|
7
|
+
improve: false,
|
|
8
|
+
parallel: false
|
|
9
|
+
};
|
|
10
|
+
function setFlags(next) {
|
|
11
|
+
flags.approve = Boolean(next.approve);
|
|
12
|
+
flags.improve = Boolean(next.improve);
|
|
13
|
+
flags.parallel = Boolean(next.parallel);
|
|
14
|
+
}
|
|
15
|
+
function getFlags() {
|
|
16
|
+
return { ...flags };
|
|
17
|
+
}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getRepoRoot(): string;
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getRepoRoot = getRepoRoot;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
function getRepoRoot() {
|
|
9
|
+
return path_1.default.resolve(__dirname, "..");
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.codexVersion = codexVersion;
|
|
4
|
+
exports.codexExec = codexExec;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
function codexVersion() {
|
|
7
|
+
const result = (0, child_process_1.spawnSync)("codex --version", { encoding: "utf-8", shell: true });
|
|
8
|
+
if (result.status !== 0) {
|
|
9
|
+
return { ok: false, output: "", error: result.stderr || "codex not available" };
|
|
10
|
+
}
|
|
11
|
+
return { ok: true, output: result.stdout.trim() };
|
|
12
|
+
}
|
|
13
|
+
function codexExec(prompt) {
|
|
14
|
+
const result = (0, child_process_1.spawnSync)(`codex exec "${prompt}"`, { encoding: "utf-8", shell: true });
|
|
15
|
+
if (result.status !== 0) {
|
|
16
|
+
return { ok: false, output: result.stdout || "", error: result.stderr || "codex exec failed" };
|
|
17
|
+
}
|
|
18
|
+
return { ok: true, output: result.stdout.trim() };
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadFlow(flowId: string): string | null;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadFlow = loadFlow;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const paths_1 = require("../paths");
|
|
10
|
+
function loadFlow(flowId) {
|
|
11
|
+
const root = (0, paths_1.getRepoRoot)();
|
|
12
|
+
const flowPath = path_1.default.join(root, "router", `${flowId}.flow.md`);
|
|
13
|
+
if (!fs_1.default.existsSync(flowPath)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return fs_1.default.readFileSync(flowPath, "utf-8");
|
|
17
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FLOW_PROMPT_PACKS = void 0;
|
|
4
|
+
exports.classifyIntent = classifyIntent;
|
|
5
|
+
const SIGNALS = [
|
|
6
|
+
{ intent: "bug_fix", flow: "BUG_FIX", domain: "bug_fix", keywords: ["bug", "issue", "error", "crash", "stack"] },
|
|
7
|
+
{ intent: "pr_review", flow: "PR_REVIEW", domain: "pr_review", keywords: ["pr", "pull request", "review"] },
|
|
8
|
+
{
|
|
9
|
+
intent: "learning",
|
|
10
|
+
flow: "HUMANITIES",
|
|
11
|
+
domain: "humanities",
|
|
12
|
+
keywords: ["history", "sociology", "anthropology", "philosophy", "literature", "humanities"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
intent: "learning",
|
|
16
|
+
flow: "LEARN",
|
|
17
|
+
domain: "learning",
|
|
18
|
+
keywords: ["learn", "explain", "teach me", "what is", "course", "syllabus", "lesson", "student", "teacher"]
|
|
19
|
+
},
|
|
20
|
+
{ intent: "design", flow: "DESIGN", domain: "design", keywords: ["logo", "brand", "layout", "visual", "design"] },
|
|
21
|
+
{ intent: "data_science", flow: "DATA_SCIENCE", domain: "data_science", keywords: ["model", "dataset", "prediction", "ml"] },
|
|
22
|
+
{ intent: "business", flow: "BUSINESS", domain: "business", keywords: ["pricing", "market", "forecast", "economics"] },
|
|
23
|
+
{ intent: "business", flow: "BUSINESS", domain: "business", keywords: ["ecommerce", "retail", "inventory", "checkout"] },
|
|
24
|
+
{
|
|
25
|
+
intent: "legal",
|
|
26
|
+
flow: "LEGAL",
|
|
27
|
+
domain: "legal",
|
|
28
|
+
keywords: ["court", "law", "policy", "compliance", "lawyer", "tax", "audit", "regulation"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
intent: "software",
|
|
32
|
+
flow: "SOFTWARE_FEATURE",
|
|
33
|
+
domain: "software",
|
|
34
|
+
keywords: ["feature", "api", "backend", "frontend", "implement", "developer", "refactor", "code"]
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
exports.FLOW_PROMPT_PACKS = {
|
|
38
|
+
BUG_FIX: ["discovery.core", "bug_fix.core"],
|
|
39
|
+
PR_REVIEW: ["pr_review.core", "review.severity"],
|
|
40
|
+
SOFTWARE_FEATURE: ["discovery.core", "release.rollout"],
|
|
41
|
+
DATA_SCIENCE: ["discovery.core", "data.monitoring"],
|
|
42
|
+
DESIGN: ["discovery.core", "design.accessibility"],
|
|
43
|
+
HUMANITIES: ["discovery.core", "humanities.sources"],
|
|
44
|
+
BUSINESS: ["discovery.core", "business.sensitivity"],
|
|
45
|
+
LEGAL: ["discovery.core", "legal.compliance"],
|
|
46
|
+
LEARN: ["discovery.core", "learn.format"],
|
|
47
|
+
GENERIC: ["discovery.core"]
|
|
48
|
+
};
|
|
49
|
+
function classifyIntent(input) {
|
|
50
|
+
const normalized = input.toLowerCase();
|
|
51
|
+
for (const rule of SIGNALS) {
|
|
52
|
+
if (rule.keywords.some((keyword) => normalized.includes(keyword))) {
|
|
53
|
+
return {
|
|
54
|
+
intent: rule.intent,
|
|
55
|
+
confidence: 0.7,
|
|
56
|
+
flow: rule.flow,
|
|
57
|
+
domain: rule.domain,
|
|
58
|
+
signals: rule.keywords.filter((keyword) => normalized.includes(keyword))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
intent: "generic",
|
|
64
|
+
confidence: 0.3,
|
|
65
|
+
flow: "GENERIC",
|
|
66
|
+
domain: "generic",
|
|
67
|
+
signals: []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mapAnswersToRequirement(answers: Record<string, string>): Record<string, string>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapAnswersToRequirement = mapAnswersToRequirement;
|
|
4
|
+
function mapAnswersToRequirement(answers) {
|
|
5
|
+
const objective = answers["What is the objective?"] ?? "N/A";
|
|
6
|
+
const actors = answers["Who are the users and actors?"] ?? "";
|
|
7
|
+
const scope = answers["What is in scope and out of scope?"] ?? "";
|
|
8
|
+
const acceptance = answers["What are the acceptance criteria?"] ?? "";
|
|
9
|
+
const nfrs = answers["What NFRs apply (security, performance, availability)?"] ?? "";
|
|
10
|
+
return {
|
|
11
|
+
objective,
|
|
12
|
+
actors,
|
|
13
|
+
scope_in: scope,
|
|
14
|
+
scope_out: "",
|
|
15
|
+
acceptance_criteria: acceptance,
|
|
16
|
+
nfr_security: nfrs,
|
|
17
|
+
nfr_performance: nfrs,
|
|
18
|
+
nfr_availability: nfrs
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type PromptPack = {
|
|
2
|
+
id: string;
|
|
3
|
+
questions: string[];
|
|
4
|
+
gates: string[];
|
|
5
|
+
followUps?: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare function loadPromptPacks(): PromptPack[];
|
|
8
|
+
export declare function getPromptPackById(packs: PromptPack[], id: string): PromptPack | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadPromptPacks = loadPromptPacks;
|
|
7
|
+
exports.getPromptPackById = getPromptPackById;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const paths_1 = require("../paths");
|
|
11
|
+
function loadPromptPacks() {
|
|
12
|
+
const root = (0, paths_1.getRepoRoot)();
|
|
13
|
+
const packPath = path_1.default.join(root, "templates", "prompt-pack-index.json");
|
|
14
|
+
const raw = fs_1.default.readFileSync(packPath, "utf-8");
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
return parsed.packs ?? [];
|
|
17
|
+
}
|
|
18
|
+
function getPromptPackById(packs, id) {
|
|
19
|
+
return packs.find((pack) => pack.id === id);
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validatePromptPacks = validatePromptPacks;
|
|
4
|
+
const validate_1 = require("../validation/validate");
|
|
5
|
+
const prompt_packs_1 = require("./prompt-packs");
|
|
6
|
+
function validatePromptPacks() {
|
|
7
|
+
const packs = (0, prompt_packs_1.loadPromptPacks)();
|
|
8
|
+
const errors = [];
|
|
9
|
+
for (const pack of packs) {
|
|
10
|
+
const result = (0, validate_1.validateJson)("prompt-pack.schema.json", pack);
|
|
11
|
+
if (!result.valid) {
|
|
12
|
+
errors.push(...result.errors.map((error) => `${pack.id}: ${error}`));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return { valid: errors.length === 0, errors };
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadTemplate = loadTemplate;
|
|
7
|
+
exports.renderTemplate = renderTemplate;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const paths_1 = require("../paths");
|
|
11
|
+
function loadTemplate(name) {
|
|
12
|
+
const root = (0, paths_1.getRepoRoot)();
|
|
13
|
+
const mdPath = path_1.default.join(root, "templates", `${name}.md`);
|
|
14
|
+
const ymlPath = path_1.default.join(root, "templates", `${name}.yml`);
|
|
15
|
+
const filePath = fs_1.default.existsSync(mdPath) ? mdPath : ymlPath;
|
|
16
|
+
return fs_1.default.readFileSync(filePath, "utf-8");
|
|
17
|
+
}
|
|
18
|
+
function renderTemplate(template, data) {
|
|
19
|
+
let output = template;
|
|
20
|
+
for (const [key, value] of Object.entries(data)) {
|
|
21
|
+
const token = `{{${key}}}`;
|
|
22
|
+
output = output.split(token).join(value);
|
|
23
|
+
}
|
|
24
|
+
return output;
|
|
25
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateTemplates = validateTemplates;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const paths_1 = require("../paths");
|
|
10
|
+
function extractPlaceholders(template) {
|
|
11
|
+
const matches = template.matchAll(/{{\s*([a-zA-Z0-9_]+)\s*}}/g);
|
|
12
|
+
const placeholders = new Set();
|
|
13
|
+
for (const match of matches) {
|
|
14
|
+
placeholders.add(match[1]);
|
|
15
|
+
}
|
|
16
|
+
return Array.from(placeholders);
|
|
17
|
+
}
|
|
18
|
+
function normalize(values) {
|
|
19
|
+
return Array.from(new Set(values)).sort();
|
|
20
|
+
}
|
|
21
|
+
function validateTemplates() {
|
|
22
|
+
const errors = [];
|
|
23
|
+
const root = (0, paths_1.getRepoRoot)();
|
|
24
|
+
const templatesDir = path_1.default.join(root, "templates");
|
|
25
|
+
const indexPath = path_1.default.join(templatesDir, "template-index.json");
|
|
26
|
+
if (!fs_1.default.existsSync(indexPath)) {
|
|
27
|
+
return { valid: false, errors: ["Missing templates/template-index.json"] };
|
|
28
|
+
}
|
|
29
|
+
const index = JSON.parse(fs_1.default.readFileSync(indexPath, "utf-8"));
|
|
30
|
+
const indexByName = new Map(index.templates.map((entry) => [entry.name, entry]));
|
|
31
|
+
const templateFiles = fs_1.default
|
|
32
|
+
.readdirSync(templatesDir, { withFileTypes: true })
|
|
33
|
+
.filter((entry) => entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".yml")))
|
|
34
|
+
.map((entry) => entry.name);
|
|
35
|
+
const fileByName = new Map(templateFiles.map((file) => [path_1.default.parse(file).name, path_1.default.join(templatesDir, file)]));
|
|
36
|
+
for (const entry of index.templates) {
|
|
37
|
+
if (!fileByName.has(entry.name)) {
|
|
38
|
+
errors.push(`Template index entry missing file: ${entry.name}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const [name, filePath] of fileByName.entries()) {
|
|
42
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
43
|
+
const placeholders = normalize(extractPlaceholders(content));
|
|
44
|
+
if (placeholders.length === 0) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const entry = indexByName.get(name);
|
|
48
|
+
if (!entry) {
|
|
49
|
+
errors.push(`Template file missing index entry: ${name}`);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const indexed = normalize(entry.placeholders);
|
|
53
|
+
if (indexed.join("|") !== placeholders.join("|")) {
|
|
54
|
+
errors.push(`Template placeholder mismatch: ${name} (index=${indexed.join(", ")} file=${placeholders.join(", ")})`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { valid: errors.length === 0, errors };
|
|
58
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type RouterIntent = {
|
|
2
|
+
intent: "bug_fix" | "pr_review" | "learning" | "design" | "data_science" | "business" | "legal" | "software" | "generic";
|
|
3
|
+
confidence: number;
|
|
4
|
+
flow: string;
|
|
5
|
+
domain: "bug_fix" | "pr_review" | "learning" | "humanities" | "design" | "data_science" | "business" | "legal" | "software" | "generic";
|
|
6
|
+
signals: string[];
|
|
7
|
+
};
|
package/dist/types.js
ADDED