sdd-cli 0.1.20 → 0.1.22
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 +8 -0
- package/dist/cli.js +8 -0
- package/dist/commands/hello.js +8 -1
- package/dist/commands/import-linear.d.ts +1 -0
- package/dist/commands/import-linear.js +88 -0
- package/dist/commands/list.js +19 -4
- package/dist/commands/quickstart.js +10 -1
- package/dist/commands/route.js +19 -4
- package/dist/commands/scope-list.js +2 -1
- package/dist/commands/status.js +1 -1
- package/dist/paths.js +4 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ Mission and vision live in `docs/MISSION.md` and `docs/VISION.md`.
|
|
|
14
14
|
|
|
15
15
|
Start with `docs/INDEX.md` for a full documentation map and `docs/STYLE.md` for formatting guidance.
|
|
16
16
|
Contributing guidelines live in `docs/CONTRIBUTING.md`.
|
|
17
|
+
Contributor quickstart lives in `docs/CONTRIBUTOR_QUICKSTART.md`.
|
|
18
|
+
Issue triage taxonomy lives in `docs/ISSUE_TRIAGE_PLAYBOOK.md`.
|
|
17
19
|
Use the PR template in `.github/PULL_REQUEST_TEMPLATE.md`.
|
|
18
20
|
Maintenance guidance lives in `docs/MAINTENANCE.md`.
|
|
19
21
|
Install troubleshooting lives in `docs/TROUBLESHOOTING.md`.
|
|
@@ -168,6 +170,7 @@ Use `--questions` when you want the manual question-by-question flow.
|
|
|
168
170
|
### Imports
|
|
169
171
|
- `sdd-cli import issue <github-issue-url>` -- import issue context and bootstrap autopilot
|
|
170
172
|
- `sdd-cli import jira <ticket-or-browse-url>` -- import Jira context and bootstrap autopilot
|
|
173
|
+
- `sdd-cli import linear <ticket-or-issue-url>` -- import Linear context and bootstrap autopilot
|
|
171
174
|
|
|
172
175
|
### Requirement lifecycle
|
|
173
176
|
- `sdd-cli req create`
|
|
@@ -253,6 +256,7 @@ For a full onboarding walkthrough, see:
|
|
|
253
256
|
- 90-day roadmap: `docs/ADOPTION_ROADMAP_90D.md`
|
|
254
257
|
- Value backlog: `docs/VALUE_BACKLOG.md`
|
|
255
258
|
- Error codes and remediation guide: `docs/ERROR_CODES.md`
|
|
259
|
+
- Integration adapters roadmap and contract: `docs/INTEGRATION_ADAPTERS.md`
|
|
256
260
|
|
|
257
261
|
## Where files are stored (clean repos)
|
|
258
262
|
|
|
@@ -275,6 +279,10 @@ Optional:
|
|
|
275
279
|
`npm run release:notes -- --write --version v0.1.20`
|
|
276
280
|
- Generate post-release quality summary:
|
|
277
281
|
`npm run release:metrics`
|
|
282
|
+
- Run fast contributor smoke checks:
|
|
283
|
+
`npm run dev:smoke`
|
|
284
|
+
- Run contributor pre-PR release checks:
|
|
285
|
+
`npm run dev:release-check`
|
|
278
286
|
- Promote `Unreleased` changelog entries into a version:
|
|
279
287
|
`npm run release:changelog -- --version v0.1.20`
|
|
280
288
|
- Verify tag/version consistency:
|
package/dist/cli.js
CHANGED
|
@@ -48,6 +48,7 @@ const quickstart_1 = require("./commands/quickstart");
|
|
|
48
48
|
const status_1 = require("./commands/status");
|
|
49
49
|
const import_issue_1 = require("./commands/import-issue");
|
|
50
50
|
const import_jira_1 = require("./commands/import-jira");
|
|
51
|
+
const import_linear_1 = require("./commands/import-linear");
|
|
51
52
|
const paths_1 = require("./paths");
|
|
52
53
|
const flags_1 = require("./context/flags");
|
|
53
54
|
const prompt_1 = require("./ui/prompt");
|
|
@@ -398,4 +399,11 @@ importCmd
|
|
|
398
399
|
.action(async (ticket) => {
|
|
399
400
|
await (0, import_jira_1.runImportJira)(ticket);
|
|
400
401
|
});
|
|
402
|
+
importCmd
|
|
403
|
+
.command("linear")
|
|
404
|
+
.description("Import a Linear ticket and bootstrap autopilot")
|
|
405
|
+
.argument("<ticket>", "Linear ticket key or issue URL")
|
|
406
|
+
.action(async (ticket) => {
|
|
407
|
+
await (0, import_linear_1.runImportLinear)(ticket);
|
|
408
|
+
});
|
|
401
409
|
program.parse(process.argv);
|
package/dist/commands/hello.js
CHANGED
|
@@ -226,7 +226,14 @@ async function runHello(input, runQuestions) {
|
|
|
226
226
|
printWhy("I will gather enough context to generate a valid first draft.");
|
|
227
227
|
printBeginnerTip(beginnerMode, "A requirement draft defines scope, acceptance criteria, and constraints.");
|
|
228
228
|
if (shouldRunQuestions) {
|
|
229
|
-
|
|
229
|
+
let packs;
|
|
230
|
+
try {
|
|
231
|
+
packs = (0, prompt_packs_1.loadPromptPacks)();
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
(0, errors_1.printError)("SDD-1012", `Unable to load prompt packs: ${error.message}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
230
237
|
const packIds = intent_1.FLOW_PROMPT_PACKS[intent.flow] ?? [];
|
|
231
238
|
const answers = {};
|
|
232
239
|
for (const packId of packIds) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runImportLinear(ticketInput: string): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runImportLinear = runImportLinear;
|
|
4
|
+
const hello_1 = require("./hello");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
function parseLinearTicket(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 { identifier: keyOnly[1].toUpperCase() };
|
|
14
|
+
}
|
|
15
|
+
const linearUrl = trimmed.match(/^https?:\/\/linear\.app\/[^/]+\/issue\/([a-z][a-z0-9_]*-\d+)(?:[/?#].*)?$/i);
|
|
16
|
+
if (linearUrl) {
|
|
17
|
+
return { identifier: linearUrl[1].toUpperCase() };
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
async function fetchLinearTicket(ref) {
|
|
22
|
+
const endpoint = (process.env.SDD_LINEAR_API_BASE || "https://api.linear.app/graphql").trim();
|
|
23
|
+
const token = (process.env.SDD_LINEAR_API_KEY || "").trim();
|
|
24
|
+
const query = `
|
|
25
|
+
query IssueByIdentifier($identifier: String!) {
|
|
26
|
+
issue(identifier: $identifier) {
|
|
27
|
+
identifier
|
|
28
|
+
title
|
|
29
|
+
description
|
|
30
|
+
url
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const headers = {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
Accept: "application/json",
|
|
37
|
+
"User-Agent": "sdd-cli"
|
|
38
|
+
};
|
|
39
|
+
if (token.length > 0) {
|
|
40
|
+
headers.Authorization = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
41
|
+
}
|
|
42
|
+
const response = await fetch(endpoint, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers,
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
query,
|
|
47
|
+
variables: { identifier: ref.identifier }
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Failed to fetch Linear ticket (${response.status}).`);
|
|
52
|
+
}
|
|
53
|
+
const payload = (await response.json());
|
|
54
|
+
if (payload.errors && payload.errors.length > 0) {
|
|
55
|
+
const message = payload.errors[0]?.message || "Unknown Linear API error.";
|
|
56
|
+
throw new Error(`Linear API error: ${message}`);
|
|
57
|
+
}
|
|
58
|
+
const issue = payload.data?.issue;
|
|
59
|
+
if (!issue) {
|
|
60
|
+
throw new Error(`Linear ticket not found: ${ref.identifier}`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
identifier: (issue.identifier || ref.identifier).toUpperCase(),
|
|
64
|
+
title: issue.title?.trim() || `Linear ticket ${ref.identifier.toUpperCase()}`,
|
|
65
|
+
description: issue.description?.trim() || "",
|
|
66
|
+
sourceUrl: issue.url?.trim() || `https://linear.app/issue/${ref.identifier.toUpperCase()}`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function buildSeedText(ticket) {
|
|
70
|
+
const bodySnippet = ticket.description.trim().slice(0, 400).replace(/\s+/g, " ");
|
|
71
|
+
return `Resolve Linear ticket: ${ticket.identifier} ${ticket.title}. Context: ${bodySnippet}. Source: ${ticket.sourceUrl}`;
|
|
72
|
+
}
|
|
73
|
+
async function runImportLinear(ticketInput) {
|
|
74
|
+
const ref = parseLinearTicket(ticketInput);
|
|
75
|
+
if (!ref) {
|
|
76
|
+
(0, errors_1.printError)("SDD-1121", "Invalid Linear ticket. Expected LIN-123 or https://linear.app/<team>/issue/LIN-123/<slug>");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
console.log(`Importing Linear ticket ${ref.identifier} ...`);
|
|
80
|
+
try {
|
|
81
|
+
const ticket = await fetchLinearTicket(ref);
|
|
82
|
+
console.log(`Imported: ${ticket.title}`);
|
|
83
|
+
await (0, hello_1.runHello)(buildSeedText(ticket), false);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
(0, errors_1.printError)("SDD-1122", error.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/commands/list.js
CHANGED
|
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const paths_1 = require("../paths");
|
|
10
10
|
const prompt_packs_1 = require("../router/prompt-packs");
|
|
11
11
|
const index_1 = require("../workspace/index");
|
|
12
|
+
const errors_1 = require("../errors");
|
|
12
13
|
function listDirectoryNames(dir, ext) {
|
|
13
14
|
if (!fs_1.default.existsSync(dir)) {
|
|
14
15
|
return [];
|
|
@@ -48,7 +49,14 @@ function runList() {
|
|
|
48
49
|
else {
|
|
49
50
|
templates.forEach((template) => console.log(`- ${template}`));
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
let packs;
|
|
53
|
+
try {
|
|
54
|
+
packs = (0, prompt_packs_1.loadPromptPacks)();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
(0, errors_1.printError)("SDD-1421", `Unable to load prompt packs: ${error.message}`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
52
60
|
console.log("Prompt packs:");
|
|
53
61
|
if (packs.length === 0) {
|
|
54
62
|
console.log("- none");
|
|
@@ -56,9 +64,16 @@ function runList() {
|
|
|
56
64
|
else {
|
|
57
65
|
packs.forEach((pack) => console.log(`- ${pack.id}`));
|
|
58
66
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
let projects;
|
|
68
|
+
try {
|
|
69
|
+
const workspace = (0, index_1.getWorkspaceInfo)();
|
|
70
|
+
(0, index_1.ensureWorkspace)(workspace);
|
|
71
|
+
projects = (0, index_1.listProjects)(workspace);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
(0, errors_1.printError)("SDD-1422", error.message);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
62
77
|
console.log("Projects:");
|
|
63
78
|
if (projects.length === 0) {
|
|
64
79
|
console.log("- none");
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.runQuickstart = runQuickstart;
|
|
4
4
|
const flags_1 = require("../context/flags");
|
|
5
5
|
const hello_1 = require("./hello");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
6
7
|
const QUICKSTART_EXAMPLES = {
|
|
7
8
|
saas: "Build a SaaS onboarding workflow for first-time users",
|
|
8
9
|
bugfix: "Fix a high-priority login failure with reproducible steps and tests",
|
|
@@ -12,7 +13,10 @@ const QUICKSTART_EXAMPLES = {
|
|
|
12
13
|
};
|
|
13
14
|
function normalizeExample(example) {
|
|
14
15
|
const value = (example || "saas").trim().toLowerCase();
|
|
15
|
-
|
|
16
|
+
if (!example) {
|
|
17
|
+
return "saas";
|
|
18
|
+
}
|
|
19
|
+
return QUICKSTART_EXAMPLES[value] ? value : null;
|
|
16
20
|
}
|
|
17
21
|
async function runQuickstart(example, listExamples) {
|
|
18
22
|
if (listExamples) {
|
|
@@ -23,6 +27,11 @@ async function runQuickstart(example, listExamples) {
|
|
|
23
27
|
return;
|
|
24
28
|
}
|
|
25
29
|
const selected = normalizeExample(example);
|
|
30
|
+
if (!selected) {
|
|
31
|
+
(0, errors_1.printError)("SDD-1011", `Invalid quickstart example: ${example}`);
|
|
32
|
+
(0, errors_1.printError)("SDD-1011", `Available examples: ${Object.keys(QUICKSTART_EXAMPLES).join(", ")}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
26
35
|
const seed = QUICKSTART_EXAMPLES[selected];
|
|
27
36
|
console.log(`Running quickstart example: ${selected}`);
|
|
28
37
|
(0, flags_1.setFlags)({ nonInteractive: true });
|
package/dist/commands/route.js
CHANGED
|
@@ -4,10 +4,25 @@ exports.runRoute = runRoute;
|
|
|
4
4
|
const intent_1 = require("../router/intent");
|
|
5
5
|
const flow_1 = require("../router/flow");
|
|
6
6
|
const prompt_packs_1 = require("../router/prompt-packs");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
7
8
|
function runRoute(input) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const text = input.trim();
|
|
10
|
+
if (!text) {
|
|
11
|
+
(0, errors_1.printError)("SDD-1423", "Route input is required.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let intent;
|
|
15
|
+
let flow;
|
|
16
|
+
let packs;
|
|
17
|
+
try {
|
|
18
|
+
intent = (0, intent_1.classifyIntent)(text);
|
|
19
|
+
flow = (0, flow_1.loadFlow)(intent.flow);
|
|
20
|
+
packs = (0, prompt_packs_1.loadPromptPacks)();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
(0, errors_1.printError)("SDD-1424", `Unable to load route context: ${error.message}`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
11
26
|
const packIds = intent_1.FLOW_PROMPT_PACKS[intent.flow] ?? [];
|
|
12
27
|
console.log(JSON.stringify(intent, null, 2));
|
|
13
28
|
if (flow) {
|
|
@@ -15,7 +30,7 @@ function runRoute(input) {
|
|
|
15
30
|
console.log(flow);
|
|
16
31
|
}
|
|
17
32
|
else {
|
|
18
|
-
|
|
33
|
+
(0, errors_1.printError)("SDD-1425", `No flow script found for ${intent.flow}.`);
|
|
19
34
|
}
|
|
20
35
|
if (packIds.length > 0) {
|
|
21
36
|
console.log("\n--- Prompt packs ---\n");
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runScopeList = runScopeList;
|
|
4
4
|
const index_1 = require("../workspace/index");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
5
6
|
function runScopeList() {
|
|
6
7
|
const baseRoot = (0, index_1.getWorkspaceBaseRoot)();
|
|
7
8
|
const scopes = (0, index_1.listScopes)(baseRoot);
|
|
8
9
|
if (scopes.length === 0) {
|
|
9
|
-
|
|
10
|
+
(0, errors_1.printError)("SDD-1412", "No scopes available in workspace.");
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
13
|
console.log(`Workspace base: ${baseRoot}`);
|
package/dist/commands/status.js
CHANGED
|
@@ -72,7 +72,7 @@ function runStatus(showNext) {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
if (!fs_1.default.existsSync(project.root)) {
|
|
75
|
-
|
|
75
|
+
(0, errors_1.printError)("SDD-1402", `Selected project not found in workspace: ${project.name}`);
|
|
76
76
|
if (showNext) {
|
|
77
77
|
console.log('Next command: sdd-cli quickstart --example saas');
|
|
78
78
|
}
|
package/dist/paths.js
CHANGED
|
@@ -6,5 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getRepoRoot = getRepoRoot;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
function getRepoRoot() {
|
|
9
|
+
const override = process.env.SDD_REPO_ROOT?.trim();
|
|
10
|
+
if (override) {
|
|
11
|
+
return path_1.default.resolve(override);
|
|
12
|
+
}
|
|
9
13
|
return path_1.default.resolve(__dirname, "..");
|
|
10
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdd-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "Use sdd-cli to turn ideas, GitHub/Jira work items, and PR feedback into actionable requirements, specs, plans, and done-ready delivery records with guided workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|
|
43
43
|
"start": "node dist/cli.js",
|
|
44
44
|
"dev": "ts-node src/cli.ts",
|
|
45
|
+
"dev:smoke": "npm run build && npm run smoke:autopilot",
|
|
46
|
+
"dev:release-check": "npm run check:error-codes && npm run check:docs && npm test && npm run verify:publish",
|
|
45
47
|
"check:docs": "node scripts/check-docs-flags.js",
|
|
46
48
|
"check:error-codes": "node scripts/check-error-codes.js",
|
|
47
49
|
"release:notes": "node scripts/generate-release-notes.js",
|