sdd-cli 0.1.22 → 0.1.23

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 CHANGED
@@ -171,6 +171,7 @@ Use `--questions` when you want the manual question-by-question flow.
171
171
  - `sdd-cli import issue <github-issue-url>` -- import issue context and bootstrap autopilot
172
172
  - `sdd-cli import jira <ticket-or-browse-url>` -- import Jira context and bootstrap autopilot
173
173
  - `sdd-cli import linear <ticket-or-issue-url>` -- import Linear context and bootstrap autopilot
174
+ - `sdd-cli import azure <work-item-or-url>` -- import Azure Boards context and bootstrap autopilot
174
175
 
175
176
  ### Requirement lifecycle
176
177
  - `sdd-cli req create`
package/dist/cli.js CHANGED
@@ -49,6 +49,7 @@ 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
51
  const import_linear_1 = require("./commands/import-linear");
52
+ const import_azure_1 = require("./commands/import-azure");
52
53
  const paths_1 = require("./paths");
53
54
  const flags_1 = require("./context/flags");
54
55
  const prompt_1 = require("./ui/prompt");
@@ -406,4 +407,11 @@ importCmd
406
407
  .action(async (ticket) => {
407
408
  await (0, import_linear_1.runImportLinear)(ticket);
408
409
  });
410
+ importCmd
411
+ .command("azure")
412
+ .description("Import an Azure Boards work item and bootstrap autopilot")
413
+ .argument("<work-item>", "Azure work item id, AB#id, or work item URL")
414
+ .action(async (workItem) => {
415
+ await (0, import_azure_1.runImportAzure)(workItem);
416
+ });
409
417
  program.parse(process.argv);
@@ -0,0 +1 @@
1
+ export declare function runImportAzure(workItemInput: string): Promise<void>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runImportAzure = runImportAzure;
4
+ const hello_1 = require("./hello");
5
+ const errors_1 = require("../errors");
6
+ function parseAzureWorkItem(input) {
7
+ const trimmed = input.trim();
8
+ if (!trimmed) {
9
+ return null;
10
+ }
11
+ const shorthand = trimmed.match(/^AB#(\d+)$/i);
12
+ if (shorthand) {
13
+ return { id: shorthand[1] };
14
+ }
15
+ const numeric = trimmed.match(/^(\d+)$/);
16
+ if (numeric) {
17
+ return { id: numeric[1] };
18
+ }
19
+ const azureUrl = trimmed.match(/^https?:\/\/dev\.azure\.com\/([^/]+)\/([^/]+)\/_workitems\/edit\/(\d+)(?:[/?#].*)?$/i);
20
+ if (azureUrl) {
21
+ const parsed = new URL(trimmed);
22
+ return {
23
+ id: azureUrl[3],
24
+ siteBase: `${parsed.protocol}//${parsed.host}/${azureUrl[1]}`,
25
+ project: azureUrl[2]
26
+ };
27
+ }
28
+ return null;
29
+ }
30
+ function stripHtml(value) {
31
+ return value
32
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
33
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
34
+ .replace(/<[^>]+>/g, " ")
35
+ .replace(/&nbsp;/gi, " ")
36
+ .replace(/&amp;/gi, "&")
37
+ .replace(/\s+/g, " ")
38
+ .trim();
39
+ }
40
+ function getAzureApiBase(ref) {
41
+ if (process.env.SDD_AZURE_API_BASE && process.env.SDD_AZURE_API_BASE.trim().length > 0) {
42
+ return process.env.SDD_AZURE_API_BASE.trim().replace(/\/$/, "");
43
+ }
44
+ if (ref.siteBase && ref.project) {
45
+ return `${ref.siteBase.replace(/\/$/, "")}/${ref.project}/_apis/wit`;
46
+ }
47
+ return null;
48
+ }
49
+ function getAzureAuthHeader() {
50
+ const pat = (process.env.SDD_AZURE_PAT || "").trim();
51
+ if (pat.length === 0) {
52
+ return null;
53
+ }
54
+ const basic = Buffer.from(`:${pat}`, "utf-8").toString("base64");
55
+ return `Basic ${basic}`;
56
+ }
57
+ async function fetchAzureWorkItem(ref) {
58
+ const apiBase = getAzureApiBase(ref);
59
+ if (!apiBase) {
60
+ throw new Error("Azure API base is required. Set SDD_AZURE_API_BASE or provide a full URL: https://dev.azure.com/<org>/<project>/_workitems/edit/<id>");
61
+ }
62
+ const endpoint = `${apiBase}/workitems/${encodeURIComponent(ref.id)}?api-version=7.1`;
63
+ const headers = {
64
+ Accept: "application/json",
65
+ "User-Agent": "sdd-cli"
66
+ };
67
+ const auth = getAzureAuthHeader();
68
+ if (auth) {
69
+ headers.Authorization = auth;
70
+ }
71
+ const response = await fetch(endpoint, { headers });
72
+ if (!response.ok) {
73
+ throw new Error(`Failed to fetch Azure work item (${response.status}).`);
74
+ }
75
+ const payload = (await response.json());
76
+ const resolvedId = String(payload.id ?? ref.id);
77
+ const titleRaw = payload.fields?.["System.Title"];
78
+ const descriptionRaw = payload.fields?.["System.Description"];
79
+ const sourceUrlRaw = payload._links?.html?.href;
80
+ const title = typeof titleRaw === "string" && titleRaw.trim().length > 0 ? titleRaw.trim() : `Work item ${resolvedId}`;
81
+ const description = typeof descriptionRaw === "string" && descriptionRaw.trim().length > 0 ? stripHtml(descriptionRaw) : "";
82
+ const sourceUrl = typeof sourceUrlRaw === "string" && sourceUrlRaw.trim().length > 0
83
+ ? sourceUrlRaw.trim()
84
+ : ref.siteBase && ref.project
85
+ ? `${ref.siteBase.replace(/\/$/, "")}/${ref.project}/_workitems/edit/${resolvedId}`
86
+ : `AB#${resolvedId}`;
87
+ return {
88
+ id: resolvedId,
89
+ title,
90
+ description,
91
+ sourceUrl
92
+ };
93
+ }
94
+ function buildSeedText(item) {
95
+ const bodySnippet = item.description.trim().slice(0, 400).replace(/\s+/g, " ");
96
+ return `Resolve Azure work item: ${item.id} ${item.title}. Context: ${bodySnippet}. Source: ${item.sourceUrl}`;
97
+ }
98
+ async function runImportAzure(workItemInput) {
99
+ const ref = parseAzureWorkItem(workItemInput);
100
+ if (!ref) {
101
+ (0, errors_1.printError)("SDD-1131", "Invalid Azure work item. Expected AB#1234, 1234, or https://dev.azure.com/<org>/<project>/_workitems/edit/1234");
102
+ return;
103
+ }
104
+ console.log(`Importing Azure work item ${ref.id} ...`);
105
+ try {
106
+ const ticket = await fetchAzureWorkItem(ref);
107
+ console.log(`Imported: ${ticket.title}`);
108
+ await (0, hello_1.runHello)(buildSeedText(ticket), false);
109
+ }
110
+ catch (error) {
111
+ (0, errors_1.printError)("SDD-1132", error.message);
112
+ }
113
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sdd-cli",
3
- "version": "0.1.22",
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.",
3
+ "version": "0.1.23",
4
+ "description": "CLI for turning ideas and tracker work items (GitHub, Jira, Linear, Azure) into actionable requirements, specs, test plans, and done-ready delivery artifacts with guided autopilot workflows.",
5
5
  "keywords": [
6
6
  "cli",
7
7
  "specification-driven-development",