sdd-cli 0.1.18 → 0.1.20
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 +44 -0
- package/dist/cli.js +67 -3
- package/dist/commands/ai-exec.js +3 -2
- package/dist/commands/ai-status.js +2 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +218 -10
- package/dist/commands/gen-architecture.js +6 -5
- package/dist/commands/gen-best-practices.js +6 -5
- package/dist/commands/gen-functional-spec.js +6 -5
- package/dist/commands/gen-project-readme.js +6 -5
- package/dist/commands/gen-technical-spec.js +6 -5
- package/dist/commands/gen-utils.js +2 -1
- package/dist/commands/hello.js +19 -8
- package/dist/commands/import-issue.d.ts +1 -0
- package/dist/commands/import-issue.js +53 -0
- package/dist/commands/import-jira.d.ts +1 -0
- package/dist/commands/import-jira.js +127 -0
- package/dist/commands/learn-deliver.js +6 -5
- package/dist/commands/learn-refine.js +8 -7
- package/dist/commands/learn-start.js +3 -2
- package/dist/commands/pr-audit.js +6 -5
- package/dist/commands/pr-bridge-check.d.ts +1 -0
- package/dist/commands/pr-bridge-check.js +88 -0
- package/dist/commands/pr-bridge.d.ts +1 -0
- package/dist/commands/pr-bridge.js +124 -0
- package/dist/commands/pr-finish.js +6 -5
- package/dist/commands/pr-report.js +6 -5
- package/dist/commands/pr-respond.js +7 -6
- package/dist/commands/pr-risk.d.ts +1 -0
- package/dist/commands/pr-risk.js +112 -0
- package/dist/commands/pr-start.js +4 -3
- package/dist/commands/req-archive.js +4 -3
- package/dist/commands/req-create.js +8 -7
- package/dist/commands/req-export.js +4 -3
- package/dist/commands/req-finish.js +9 -8
- package/dist/commands/req-lint.js +16 -6
- package/dist/commands/req-list.js +4 -3
- package/dist/commands/req-plan.js +12 -11
- package/dist/commands/req-refine.js +9 -8
- package/dist/commands/req-report.js +10 -9
- package/dist/commands/req-start.js +10 -9
- package/dist/commands/req-status.js +4 -3
- package/dist/commands/scope-list.d.ts +1 -0
- package/dist/commands/scope-list.js +15 -0
- package/dist/commands/scope-status.d.ts +1 -0
- package/dist/commands/scope-status.js +33 -0
- package/dist/commands/status.js +15 -6
- package/dist/commands/test-plan.js +6 -5
- package/dist/context/flags.d.ts +2 -0
- package/dist/context/flags.js +9 -1
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +10 -0
- package/dist/telemetry/local-metrics.d.ts +2 -0
- package/dist/telemetry/local-metrics.js +85 -0
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.js +129 -27
- package/package.json +24 -2
|
@@ -12,11 +12,12 @@ const validate_1 = require("../validation/validate");
|
|
|
12
12
|
const gen_utils_1 = require("./gen-utils");
|
|
13
13
|
const flags_1 = require("../context/flags");
|
|
14
14
|
const index_1 = require("../workspace/index");
|
|
15
|
+
const errors_1 = require("../errors");
|
|
15
16
|
async function runGenProjectReadme() {
|
|
16
17
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
17
18
|
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
18
19
|
if (!projectName || !reqId) {
|
|
19
|
-
|
|
20
|
+
(0, errors_1.printError)("SDD-1651", "Project name and requirement ID are required.");
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
@@ -25,12 +26,12 @@ async function runGenProjectReadme() {
|
|
|
25
26
|
project = (0, index_1.getProjectInfo)(workspace, projectName);
|
|
26
27
|
}
|
|
27
28
|
catch (error) {
|
|
28
|
-
|
|
29
|
+
(0, errors_1.printError)("SDD-1652", error.message);
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
const requirementDir = (0, gen_utils_1.findRequirementDir)(project.name, reqId);
|
|
32
33
|
if (!requirementDir) {
|
|
33
|
-
|
|
34
|
+
(0, errors_1.printError)("SDD-1653", "Requirement not found.");
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
const overview = await (0, prompt_1.ask)("Project overview: ");
|
|
@@ -58,8 +59,8 @@ async function runGenProjectReadme() {
|
|
|
58
59
|
};
|
|
59
60
|
const validation = (0, validate_1.validateJson)("project-readme.schema.json", projectReadmeJson);
|
|
60
61
|
if (!validation.valid) {
|
|
61
|
-
|
|
62
|
-
validation.errors.forEach((error) =>
|
|
62
|
+
(0, errors_1.printError)("SDD-1654", "Project README validation failed.");
|
|
63
|
+
validation.errors.forEach((error) => (0, errors_1.printError)("SDD-1654", error));
|
|
63
64
|
return;
|
|
64
65
|
}
|
|
65
66
|
const template = (0, render_1.loadTemplate)("project-readme");
|
|
@@ -13,11 +13,12 @@ const validate_1 = require("../validation/validate");
|
|
|
13
13
|
const gen_utils_1 = require("./gen-utils");
|
|
14
14
|
const flags_1 = require("../context/flags");
|
|
15
15
|
const index_1 = require("../workspace/index");
|
|
16
|
+
const errors_1 = require("../errors");
|
|
16
17
|
async function runGenTechnicalSpec() {
|
|
17
18
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
18
19
|
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
19
20
|
if (!projectName || !reqId) {
|
|
20
|
-
|
|
21
|
+
(0, errors_1.printError)("SDD-1621", "Project name and requirement ID are required.");
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
@@ -26,12 +27,12 @@ async function runGenTechnicalSpec() {
|
|
|
26
27
|
project = (0, index_1.getProjectInfo)(workspace, projectName);
|
|
27
28
|
}
|
|
28
29
|
catch (error) {
|
|
29
|
-
|
|
30
|
+
(0, errors_1.printError)("SDD-1622", error.message);
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
const requirementDir = (0, gen_utils_1.findRequirementDir)(project.name, reqId);
|
|
33
34
|
if (!requirementDir) {
|
|
34
|
-
|
|
35
|
+
(0, errors_1.printError)("SDD-1623", "Requirement not found.");
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
const stack = await (0, prompt_1.ask)("Tech stack - comma separated: ");
|
|
@@ -54,8 +55,8 @@ async function runGenTechnicalSpec() {
|
|
|
54
55
|
};
|
|
55
56
|
const validation = (0, validate_1.validateJson)("technical-spec.schema.json", technicalJson);
|
|
56
57
|
if (!validation.valid) {
|
|
57
|
-
|
|
58
|
-
validation.errors.forEach((error) =>
|
|
58
|
+
(0, errors_1.printError)("SDD-1624", "Technical spec validation failed.");
|
|
59
|
+
validation.errors.forEach((error) => (0, errors_1.printError)("SDD-1624", error));
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
const template = (0, render_1.loadTemplate)("technical-spec");
|
|
@@ -11,6 +11,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const index_1 = require("../workspace/index");
|
|
13
13
|
const flags_1 = require("../context/flags");
|
|
14
|
+
const errors_1 = require("../errors");
|
|
14
15
|
function findRequirementDir(projectName, reqId) {
|
|
15
16
|
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
16
17
|
let project;
|
|
@@ -18,7 +19,7 @@ function findRequirementDir(projectName, reqId) {
|
|
|
18
19
|
project = (0, index_1.getProjectInfo)(workspace, projectName);
|
|
19
20
|
}
|
|
20
21
|
catch (error) {
|
|
21
|
-
|
|
22
|
+
(0, errors_1.printError)("SDD-1605", error.message);
|
|
22
23
|
return null;
|
|
23
24
|
}
|
|
24
25
|
const base = path_1.default.join(project.root, "requirements");
|
package/dist/commands/hello.js
CHANGED
|
@@ -13,6 +13,8 @@ const req_start_1 = require("./req-start");
|
|
|
13
13
|
const req_finish_1 = require("./req-finish");
|
|
14
14
|
const route_1 = require("./route");
|
|
15
15
|
const test_plan_1 = require("./test-plan");
|
|
16
|
+
const local_metrics_1 = require("../telemetry/local-metrics");
|
|
17
|
+
const errors_1 = require("../errors");
|
|
16
18
|
const autopilot_checkpoint_1 = require("./autopilot-checkpoint");
|
|
17
19
|
function printStep(step, description) {
|
|
18
20
|
console.log(`${step}: ${description}`);
|
|
@@ -101,6 +103,10 @@ function buildAutopilotDraft(input, flow, domain) {
|
|
|
101
103
|
};
|
|
102
104
|
}
|
|
103
105
|
async function runHello(input, runQuestions) {
|
|
106
|
+
(0, local_metrics_1.recordActivationMetric)("started", {
|
|
107
|
+
directIntent: input.trim().length > 0,
|
|
108
|
+
questionMode: runQuestions === true
|
|
109
|
+
});
|
|
104
110
|
function loadWorkspace() {
|
|
105
111
|
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
106
112
|
(0, index_1.ensureWorkspace)(workspace);
|
|
@@ -181,7 +187,12 @@ async function runHello(input, runQuestions) {
|
|
|
181
187
|
}
|
|
182
188
|
let text = input || (await (0, prompt_1.ask)("Describe what you want to do: "));
|
|
183
189
|
let checkpoint = null;
|
|
184
|
-
|
|
190
|
+
const rawFromStep = runtimeFlags.fromStep?.trim();
|
|
191
|
+
let fromStep = (0, autopilot_checkpoint_1.normalizeStep)(rawFromStep);
|
|
192
|
+
if (rawFromStep && !fromStep) {
|
|
193
|
+
(0, errors_1.printError)("SDD-1003", `Invalid --from-step value. Use one of: ${autopilot_checkpoint_1.AUTOPILOT_STEPS.join(", ")}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
185
196
|
let activeProjectForCheckpoint = runtimeFlags.project;
|
|
186
197
|
if (!shouldRunQuestions && activeProjectForCheckpoint) {
|
|
187
198
|
checkpoint = (0, autopilot_checkpoint_1.loadCheckpoint)(activeProjectForCheckpoint);
|
|
@@ -196,7 +207,7 @@ async function runHello(input, runQuestions) {
|
|
|
196
207
|
}
|
|
197
208
|
}
|
|
198
209
|
if (!text) {
|
|
199
|
-
|
|
210
|
+
(0, errors_1.printError)("SDD-1001", "No input provided. Try again with a short description.");
|
|
200
211
|
return;
|
|
201
212
|
}
|
|
202
213
|
const intent = (0, intent_1.classifyIntent)(text);
|
|
@@ -258,7 +269,7 @@ async function runHello(input, runQuestions) {
|
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
271
|
if (!activeProject) {
|
|
261
|
-
|
|
272
|
+
(0, errors_1.printError)("SDD-1002", "Project name is required to run autopilot.");
|
|
262
273
|
return;
|
|
263
274
|
}
|
|
264
275
|
printWhy(`Using project: ${activeProject}`);
|
|
@@ -270,16 +281,12 @@ async function runHello(input, runQuestions) {
|
|
|
270
281
|
fromStep = candidate;
|
|
271
282
|
}
|
|
272
283
|
}
|
|
273
|
-
if (fromStep && !autopilot_checkpoint_1.AUTOPILOT_STEPS.includes(fromStep)) {
|
|
274
|
-
console.log(`Invalid --from-step value. Use one of: ${autopilot_checkpoint_1.AUTOPILOT_STEPS.join(", ")}`);
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
284
|
const draft = buildAutopilotDraft(text, intent.flow, intent.domain);
|
|
278
285
|
draft.project_name = activeProject;
|
|
279
286
|
let reqId = checkpoint?.reqId ?? "";
|
|
280
287
|
const startStep = fromStep ?? "create";
|
|
281
288
|
if (startStep !== "create" && !reqId) {
|
|
282
|
-
|
|
289
|
+
(0, errors_1.printError)("SDD-1004", "No checkpoint found for resume. Run full autopilot first or use --from-step create.");
|
|
283
290
|
printRecoveryNext(activeProject, "create", text);
|
|
284
291
|
return;
|
|
285
292
|
}
|
|
@@ -375,6 +382,10 @@ async function runHello(input, runQuestions) {
|
|
|
375
382
|
return;
|
|
376
383
|
}
|
|
377
384
|
(0, autopilot_checkpoint_1.clearCheckpoint)(activeProject);
|
|
385
|
+
(0, local_metrics_1.recordActivationMetric)("completed", {
|
|
386
|
+
project: activeProject,
|
|
387
|
+
reqId
|
|
388
|
+
});
|
|
378
389
|
console.log(`Autopilot completed successfully for ${reqId}.`);
|
|
379
390
|
console.log(`Artifacts finalized at: ${finished.doneDir}`);
|
|
380
391
|
return;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runImportIssue(issueUrl: string): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runImportIssue = runImportIssue;
|
|
4
|
+
const hello_1 = require("./hello");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
function parseGitHubIssueUrl(input) {
|
|
7
|
+
const trimmed = input.trim();
|
|
8
|
+
const match = trimmed.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)(?:[/?#].*)?$/i);
|
|
9
|
+
if (!match) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return { owner: match[1], repo: match[2], number: match[3] };
|
|
13
|
+
}
|
|
14
|
+
async function fetchIssue(ref) {
|
|
15
|
+
const baseApi = process.env.SDD_GITHUB_API_BASE || "https://api.github.com";
|
|
16
|
+
const endpoint = `${baseApi.replace(/\/$/, "")}/repos/${ref.owner}/${ref.repo}/issues/${ref.number}`;
|
|
17
|
+
const response = await fetch(endpoint, {
|
|
18
|
+
headers: {
|
|
19
|
+
Accept: "application/vnd.github+json",
|
|
20
|
+
"User-Agent": "sdd-cli"
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`Failed to fetch issue (${response.status}).`);
|
|
25
|
+
}
|
|
26
|
+
const payload = (await response.json());
|
|
27
|
+
return {
|
|
28
|
+
title: payload.title || `Issue #${ref.number}`,
|
|
29
|
+
body: payload.body || "",
|
|
30
|
+
url: payload.html_url || `https://github.com/${ref.owner}/${ref.repo}/issues/${ref.number}`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function buildSeedText(issue) {
|
|
34
|
+
const bodySnippet = issue.body.trim().slice(0, 400).replace(/\s+/g, " ");
|
|
35
|
+
return `Resolve issue: ${issue.title}. Context: ${bodySnippet}. Source: ${issue.url}`;
|
|
36
|
+
}
|
|
37
|
+
async function runImportIssue(issueUrl) {
|
|
38
|
+
const ref = parseGitHubIssueUrl(issueUrl);
|
|
39
|
+
if (!ref) {
|
|
40
|
+
(0, errors_1.printError)("SDD-1101", "Invalid GitHub issue URL. Expected format: https://github.com/<owner>/<repo>/issues/<number>");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(`Importing issue ${ref.owner}/${ref.repo}#${ref.number} ...`);
|
|
44
|
+
try {
|
|
45
|
+
const issue = await fetchIssue(ref);
|
|
46
|
+
const seedText = buildSeedText(issue);
|
|
47
|
+
console.log(`Imported: ${issue.title}`);
|
|
48
|
+
await (0, hello_1.runHello)(seedText, false);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
(0, errors_1.printError)("SDD-1102", error.message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runImportJira(ticketInput: string): Promise<void>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runImportJira = runImportJira;
|
|
4
|
+
const hello_1 = require("./hello");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
function parseJiraTicket(input) {
|
|
7
|
+
const trimmed = input.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const keyOnly = trimmed.match(/^([a-z][a-z0-9_]*-\d+)$/i);
|
|
12
|
+
if (keyOnly) {
|
|
13
|
+
return { key: keyOnly[1].toUpperCase() };
|
|
14
|
+
}
|
|
15
|
+
const browseUrl = trimmed.match(/^https?:\/\/([^/]+)\/browse\/([a-z][a-z0-9_]*-\d+)(?:[/?#].*)?$/i);
|
|
16
|
+
if (browseUrl) {
|
|
17
|
+
const parsed = new URL(trimmed);
|
|
18
|
+
return {
|
|
19
|
+
key: browseUrl[2].toUpperCase(),
|
|
20
|
+
siteBase: `${parsed.protocol}//${parsed.host}`
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function collectAdfText(node, chunks) {
|
|
26
|
+
if (!node || typeof node !== "object") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const obj = node;
|
|
30
|
+
if (typeof obj.text === "string") {
|
|
31
|
+
chunks.push(obj.text);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(obj.content)) {
|
|
34
|
+
for (const child of obj.content) {
|
|
35
|
+
collectAdfText(child, chunks);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function toDescriptionText(value) {
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
if (!value || typeof value !== "object") {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
const chunks = [];
|
|
47
|
+
collectAdfText(value, chunks);
|
|
48
|
+
return chunks.join(" ").trim();
|
|
49
|
+
}
|
|
50
|
+
function getJiraApiBase(ref) {
|
|
51
|
+
if (process.env.SDD_JIRA_API_BASE && process.env.SDD_JIRA_API_BASE.trim().length > 0) {
|
|
52
|
+
return [process.env.SDD_JIRA_API_BASE.trim().replace(/\/$/, "")];
|
|
53
|
+
}
|
|
54
|
+
if (ref.siteBase) {
|
|
55
|
+
const site = ref.siteBase.replace(/\/$/, "");
|
|
56
|
+
return [`${site}/rest/api/3`, `${site}/rest/api/2`];
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
function getJiraAuthHeader() {
|
|
61
|
+
if (process.env.SDD_JIRA_AUTH && process.env.SDD_JIRA_AUTH.trim().length > 0) {
|
|
62
|
+
return process.env.SDD_JIRA_AUTH.trim();
|
|
63
|
+
}
|
|
64
|
+
const email = process.env.SDD_JIRA_EMAIL || "";
|
|
65
|
+
const token = process.env.SDD_JIRA_TOKEN || "";
|
|
66
|
+
if (email.trim().length > 0 && token.trim().length > 0) {
|
|
67
|
+
const basic = Buffer.from(`${email.trim()}:${token.trim()}`, "utf-8").toString("base64");
|
|
68
|
+
return `Basic ${basic}`;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
async function fetchJiraTicket(ref) {
|
|
73
|
+
const apiBases = getJiraApiBase(ref);
|
|
74
|
+
if (apiBases.length === 0) {
|
|
75
|
+
throw new Error("Jira API base is required. Set SDD_JIRA_API_BASE or provide a full browse URL: https://<site>/browse/PROJ-123");
|
|
76
|
+
}
|
|
77
|
+
const auth = getJiraAuthHeader();
|
|
78
|
+
let lastStatus = 0;
|
|
79
|
+
for (const base of apiBases) {
|
|
80
|
+
const endpoint = `${base}/issue/${encodeURIComponent(ref.key)}`;
|
|
81
|
+
const headers = {
|
|
82
|
+
Accept: "application/json",
|
|
83
|
+
"User-Agent": "sdd-cli"
|
|
84
|
+
};
|
|
85
|
+
if (auth) {
|
|
86
|
+
headers.Authorization = auth;
|
|
87
|
+
}
|
|
88
|
+
const response = await fetch(endpoint, { headers });
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
lastStatus = response.status;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const payload = (await response.json());
|
|
94
|
+
const key = (payload.key || ref.key).toUpperCase();
|
|
95
|
+
const summary = payload.fields?.summary?.trim() || `Ticket ${key}`;
|
|
96
|
+
const description = toDescriptionText(payload.fields?.description);
|
|
97
|
+
let sourceUrl = `${key}`;
|
|
98
|
+
if (ref.siteBase) {
|
|
99
|
+
sourceUrl = `${ref.siteBase.replace(/\/$/, "")}/browse/${key}`;
|
|
100
|
+
}
|
|
101
|
+
else if (process.env.SDD_JIRA_SITE_BASE && process.env.SDD_JIRA_SITE_BASE.trim().length > 0) {
|
|
102
|
+
sourceUrl = `${process.env.SDD_JIRA_SITE_BASE.trim().replace(/\/$/, "")}/browse/${key}`;
|
|
103
|
+
}
|
|
104
|
+
return { key, summary, description, sourceUrl };
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Failed to fetch Jira ticket (${lastStatus || "unknown status"}).`);
|
|
107
|
+
}
|
|
108
|
+
function buildSeedText(ticket) {
|
|
109
|
+
const bodySnippet = ticket.description.trim().slice(0, 400).replace(/\s+/g, " ");
|
|
110
|
+
return `Resolve Jira ticket: ${ticket.key} ${ticket.summary}. Context: ${bodySnippet}. Source: ${ticket.sourceUrl}`;
|
|
111
|
+
}
|
|
112
|
+
async function runImportJira(ticketInput) {
|
|
113
|
+
const ref = parseJiraTicket(ticketInput);
|
|
114
|
+
if (!ref) {
|
|
115
|
+
(0, errors_1.printError)("SDD-1111", "Invalid Jira ticket. Expected format: PROJ-123 or https://<your-jira-site>/browse/PROJ-123");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(`Importing Jira ticket ${ref.key} ...`);
|
|
119
|
+
try {
|
|
120
|
+
const ticket = await fetchJiraTicket(ref);
|
|
121
|
+
console.log(`Imported: ${ticket.summary}`);
|
|
122
|
+
await (0, hello_1.runHello)(buildSeedText(ticket), false);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
(0, errors_1.printError)("SDD-1112", error.message);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -9,10 +9,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const prompt_1 = require("../ui/prompt");
|
|
10
10
|
const list_1 = require("../utils/list");
|
|
11
11
|
const learn_utils_1 = require("./learn-utils");
|
|
12
|
+
const errors_1 = require("../errors");
|
|
12
13
|
async function runLearnDeliver() {
|
|
13
14
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
14
15
|
if (!projectName) {
|
|
15
|
-
|
|
16
|
+
(0, errors_1.printError)("SDD-1731", "Project name is required.");
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
let sessions = [];
|
|
@@ -20,7 +21,7 @@ async function runLearnDeliver() {
|
|
|
20
21
|
sessions = (0, learn_utils_1.listLearnSessions)(projectName);
|
|
21
22
|
}
|
|
22
23
|
catch (error) {
|
|
23
|
-
|
|
24
|
+
(0, errors_1.printError)("SDD-1732", error.message);
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
if (sessions.length > 0) {
|
|
@@ -29,7 +30,7 @@ async function runLearnDeliver() {
|
|
|
29
30
|
}
|
|
30
31
|
const sessionId = await (0, prompt_1.ask)("Session ID: ");
|
|
31
32
|
if (!sessionId) {
|
|
32
|
-
|
|
33
|
+
(0, errors_1.printError)("SDD-1733", "Session ID is required.");
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
let loaded;
|
|
@@ -37,11 +38,11 @@ async function runLearnDeliver() {
|
|
|
37
38
|
loaded = (0, learn_utils_1.loadLearnSession)(projectName, sessionId);
|
|
38
39
|
}
|
|
39
40
|
catch (error) {
|
|
40
|
-
|
|
41
|
+
(0, errors_1.printError)("SDD-1734", error.message);
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
if (!loaded) {
|
|
44
|
-
|
|
45
|
+
(0, errors_1.printError)("SDD-1735", "Learning session not found.");
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
const brief = await (0, prompt_1.ask)("Brief summary: ");
|
|
@@ -9,10 +9,11 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const prompt_1 = require("../ui/prompt");
|
|
10
10
|
const list_1 = require("../utils/list");
|
|
11
11
|
const learn_utils_1 = require("./learn-utils");
|
|
12
|
+
const errors_1 = require("../errors");
|
|
12
13
|
async function runLearnRefine() {
|
|
13
14
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
14
15
|
if (!projectName) {
|
|
15
|
-
|
|
16
|
+
(0, errors_1.printError)("SDD-1721", "Project name is required.");
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
let sessions = [];
|
|
@@ -20,7 +21,7 @@ async function runLearnRefine() {
|
|
|
20
21
|
sessions = (0, learn_utils_1.listLearnSessions)(projectName);
|
|
21
22
|
}
|
|
22
23
|
catch (error) {
|
|
23
|
-
|
|
24
|
+
(0, errors_1.printError)("SDD-1722", error.message);
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
if (sessions.length > 0) {
|
|
@@ -29,7 +30,7 @@ async function runLearnRefine() {
|
|
|
29
30
|
}
|
|
30
31
|
const sessionId = await (0, prompt_1.ask)("Session ID: ");
|
|
31
32
|
if (!sessionId) {
|
|
32
|
-
|
|
33
|
+
(0, errors_1.printError)("SDD-1723", "Session ID is required.");
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
let loaded;
|
|
@@ -37,11 +38,11 @@ async function runLearnRefine() {
|
|
|
37
38
|
loaded = (0, learn_utils_1.loadLearnSession)(projectName, sessionId);
|
|
38
39
|
}
|
|
39
40
|
catch (error) {
|
|
40
|
-
|
|
41
|
+
(0, errors_1.printError)("SDD-1724", error.message);
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
if (!loaded) {
|
|
44
|
-
|
|
45
|
+
(0, errors_1.printError)("SDD-1725", "Learning session not found.");
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
const purpose = await (0, prompt_1.ask)(`Purpose (${loaded.session.purpose}): `);
|
|
@@ -62,11 +63,11 @@ async function runLearnRefine() {
|
|
|
62
63
|
});
|
|
63
64
|
}
|
|
64
65
|
catch (error) {
|
|
65
|
-
|
|
66
|
+
(0, errors_1.printError)("SDD-1726", error.message);
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
if (!updated) {
|
|
69
|
-
|
|
70
|
+
(0, errors_1.printError)("SDD-1727", "Failed to update session.");
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
73
|
const sessionMd = [
|
|
@@ -9,11 +9,12 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const prompt_1 = require("../ui/prompt");
|
|
10
10
|
const list_1 = require("../utils/list");
|
|
11
11
|
const learn_utils_1 = require("./learn-utils");
|
|
12
|
+
const errors_1 = require("../errors");
|
|
12
13
|
async function runLearnStart() {
|
|
13
14
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
14
15
|
const topic = await (0, prompt_1.ask)("Topic to learn: ");
|
|
15
16
|
if (!projectName || !topic) {
|
|
16
|
-
|
|
17
|
+
(0, errors_1.printError)("SDD-1711", "Project name and topic are required.");
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
const purpose = await (0, prompt_1.ask)("Why do you want to learn this? ");
|
|
@@ -27,7 +28,7 @@ async function runLearnStart() {
|
|
|
27
28
|
created = (0, learn_utils_1.createLearnSession)(projectName, topic, "learning");
|
|
28
29
|
}
|
|
29
30
|
catch (error) {
|
|
30
|
-
|
|
31
|
+
(0, errors_1.printError)("SDD-1712", error.message);
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
(0, learn_utils_1.updateLearnSession)(projectName, created.session.id, {
|
|
@@ -10,10 +10,11 @@ const prompt_1 = require("../ui/prompt");
|
|
|
10
10
|
const render_1 = require("../templates/render");
|
|
11
11
|
const list_1 = require("../utils/list");
|
|
12
12
|
const pr_utils_1 = require("./pr-utils");
|
|
13
|
+
const errors_1 = require("../errors");
|
|
13
14
|
async function runPrAudit() {
|
|
14
15
|
const projectName = await (0, prompt_1.askProjectName)();
|
|
15
16
|
if (!projectName) {
|
|
16
|
-
|
|
17
|
+
(0, errors_1.printError)("SDD-1346", "Project name is required.");
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
let available = [];
|
|
@@ -21,7 +22,7 @@ async function runPrAudit() {
|
|
|
21
22
|
available = (0, pr_utils_1.listPrReviews)(projectName);
|
|
22
23
|
}
|
|
23
24
|
catch (error) {
|
|
24
|
-
|
|
25
|
+
(0, errors_1.printError)("SDD-1347", error.message);
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
if (available.length > 0) {
|
|
@@ -30,7 +31,7 @@ async function runPrAudit() {
|
|
|
30
31
|
}
|
|
31
32
|
const prId = await (0, prompt_1.ask)("PR ID: ");
|
|
32
33
|
if (!prId) {
|
|
33
|
-
|
|
34
|
+
(0, errors_1.printError)("SDD-1348", "PR ID is required.");
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
let prDir;
|
|
@@ -38,11 +39,11 @@ async function runPrAudit() {
|
|
|
38
39
|
prDir = (0, pr_utils_1.resolvePrDir)(projectName, prId);
|
|
39
40
|
}
|
|
40
41
|
catch (error) {
|
|
41
|
-
|
|
42
|
+
(0, errors_1.printError)("SDD-1349", error.message);
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
if (!fs_1.default.existsSync(prDir)) {
|
|
45
|
-
|
|
46
|
+
(0, errors_1.printError)("SDD-1350", `PR review not found at ${prDir}`);
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
const prLink = await (0, prompt_1.ask)("PR link: ");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runPrBridgeCheck(): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
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.runPrBridgeCheck = runPrBridgeCheck;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const prompt_1 = require("../ui/prompt");
|
|
10
|
+
const gen_utils_1 = require("./gen-utils");
|
|
11
|
+
const errors_1 = require("../errors");
|
|
12
|
+
async function runPrBridgeCheck() {
|
|
13
|
+
const projectName = await (0, prompt_1.askProjectName)();
|
|
14
|
+
if (!projectName) {
|
|
15
|
+
(0, errors_1.printError)("SDD-1331", "Project name is required.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const reqId = await (0, prompt_1.ask)("Requirement ID (REQ-...): ");
|
|
19
|
+
if (!reqId) {
|
|
20
|
+
(0, errors_1.printError)("SDD-1332", "Requirement ID is required.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const requirementDir = (0, gen_utils_1.findRequirementDir)(projectName, reqId);
|
|
24
|
+
if (!requirementDir) {
|
|
25
|
+
(0, errors_1.printError)("SDD-1333", `Requirement not found: ${reqId}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const linksPath = path_1.default.join(requirementDir, "pr-links.json");
|
|
29
|
+
if (!fs_1.default.existsSync(linksPath)) {
|
|
30
|
+
(0, errors_1.printError)("SDD-1334", "No pr-links.json found for this requirement.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
let links = [];
|
|
34
|
+
try {
|
|
35
|
+
links = JSON.parse(fs_1.default.readFileSync(linksPath, "utf-8"));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
(0, errors_1.printError)("SDD-1335", "Unable to parse pr-links.json.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const checks = links.map((link) => {
|
|
42
|
+
const prId = link.prId || "unknown";
|
|
43
|
+
const prDir = link.prDir || "";
|
|
44
|
+
const bridgeDir = path_1.default.join(requirementDir, "pr-review", prId);
|
|
45
|
+
const prDirExists = prDir.length > 0 && fs_1.default.existsSync(prDir);
|
|
46
|
+
const bridgeExists = fs_1.default.existsSync(bridgeDir);
|
|
47
|
+
const copiedArtifacts = Array.isArray(link.copiedArtifacts) ? link.copiedArtifacts : [];
|
|
48
|
+
const missingArtifacts = copiedArtifacts.filter((name) => !fs_1.default.existsSync(path_1.default.join(bridgeDir, name)));
|
|
49
|
+
return {
|
|
50
|
+
prId,
|
|
51
|
+
prDir,
|
|
52
|
+
prDirExists,
|
|
53
|
+
bridgeDir,
|
|
54
|
+
bridgeExists,
|
|
55
|
+
missingArtifacts,
|
|
56
|
+
ok: prDirExists && bridgeExists && missingArtifacts.length === 0
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
const okCount = checks.filter((item) => item.ok).length;
|
|
60
|
+
const report = {
|
|
61
|
+
requirement: reqId,
|
|
62
|
+
checkedAt: new Date().toISOString(),
|
|
63
|
+
total: checks.length,
|
|
64
|
+
ok: okCount,
|
|
65
|
+
failed: checks.length - okCount,
|
|
66
|
+
checks
|
|
67
|
+
};
|
|
68
|
+
fs_1.default.writeFileSync(path_1.default.join(requirementDir, "pr-bridge-integrity.json"), JSON.stringify(report, null, 2), "utf-8");
|
|
69
|
+
const lines = [
|
|
70
|
+
`# PR Bridge Integrity: ${reqId}`,
|
|
71
|
+
"",
|
|
72
|
+
`- Total links: ${report.total}`,
|
|
73
|
+
`- OK: ${report.ok}`,
|
|
74
|
+
`- Failed: ${report.failed}`,
|
|
75
|
+
"",
|
|
76
|
+
"## Details"
|
|
77
|
+
];
|
|
78
|
+
checks.forEach((item) => {
|
|
79
|
+
lines.push(`- ${item.prId}: ok=${item.ok} prDirExists=${item.prDirExists} bridgeExists=${item.bridgeExists} missingArtifacts=${item.missingArtifacts.join(", ") || "none"}`);
|
|
80
|
+
});
|
|
81
|
+
fs_1.default.writeFileSync(path_1.default.join(requirementDir, "pr-bridge-integrity.md"), `${lines.join("\n")}\n`, "utf-8");
|
|
82
|
+
if (report.failed > 0) {
|
|
83
|
+
(0, errors_1.printError)("SDD-1336", `Bridge integrity failed for ${report.failed} linked PR(s).`);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(`PR bridge integrity OK for ${reqId}.`);
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runPrBridge(): Promise<void>;
|