@specship/specship-win32-arm64 0.13.0 → 0.14.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.
Files changed (240) hide show
  1. package/lib/dist/bin/specship.js +180 -1
  2. package/lib/dist/bin/specship.js.map +1 -1
  3. package/lib/dist/jira/auth.d.ts +17 -0
  4. package/lib/dist/jira/auth.d.ts.map +1 -0
  5. package/lib/dist/jira/auth.js +33 -0
  6. package/lib/dist/jira/auth.js.map +1 -0
  7. package/lib/dist/jira/client.d.ts +120 -0
  8. package/lib/dist/jira/client.d.ts.map +1 -0
  9. package/lib/dist/jira/client.js +316 -0
  10. package/lib/dist/jira/client.js.map +1 -0
  11. package/lib/dist/jira/config.d.ts +45 -0
  12. package/lib/dist/jira/config.d.ts.map +1 -0
  13. package/lib/dist/jira/config.js +178 -0
  14. package/lib/dist/jira/config.js.map +1 -0
  15. package/lib/dist/jira/index.d.ts +8 -0
  16. package/lib/dist/jira/index.d.ts.map +1 -0
  17. package/lib/dist/jira/index.js +24 -0
  18. package/lib/dist/jira/index.js.map +1 -0
  19. package/lib/dist/jira/pull-request.d.ts +72 -0
  20. package/lib/dist/jira/pull-request.d.ts.map +1 -0
  21. package/lib/dist/jira/pull-request.js +127 -0
  22. package/lib/dist/jira/pull-request.js.map +1 -0
  23. package/lib/dist/jira/spec-generator.d.ts +34 -0
  24. package/lib/dist/jira/spec-generator.d.ts.map +1 -0
  25. package/lib/dist/jira/spec-generator.js +138 -0
  26. package/lib/dist/jira/spec-generator.js.map +1 -0
  27. package/lib/dist/jira/spec-writer.d.ts +36 -0
  28. package/lib/dist/jira/spec-writer.d.ts.map +1 -0
  29. package/lib/dist/jira/spec-writer.js +143 -0
  30. package/lib/dist/jira/spec-writer.js.map +1 -0
  31. package/lib/dist/jira/types.d.ts +174 -0
  32. package/lib/dist/jira/types.d.ts.map +1 -0
  33. package/lib/dist/jira/types.js +60 -0
  34. package/lib/dist/jira/types.js.map +1 -0
  35. package/lib/dist/mcp/jira-tools.d.ts +234 -0
  36. package/lib/dist/mcp/jira-tools.d.ts.map +1 -0
  37. package/lib/dist/mcp/jira-tools.js +787 -0
  38. package/lib/dist/mcp/jira-tools.js.map +1 -0
  39. package/lib/dist/mcp/server-instructions.d.ts +1 -1
  40. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  41. package/lib/dist/mcp/server-instructions.js +10 -0
  42. package/lib/dist/mcp/server-instructions.js.map +1 -1
  43. package/lib/dist/mcp/tools.d.ts.map +1 -1
  44. package/lib/dist/mcp/tools.js +32 -0
  45. package/lib/dist/mcp/tools.js.map +1 -1
  46. package/lib/dist/server/cli.js +1 -1
  47. package/lib/dist/server/routes/sse.js +1 -1
  48. package/lib/dist/server/server.js +1 -1
  49. package/lib/dist/statusline/index.d.ts +19 -0
  50. package/lib/dist/statusline/index.d.ts.map +1 -1
  51. package/lib/dist/statusline/index.js +96 -3
  52. package/lib/dist/statusline/index.js.map +1 -1
  53. package/lib/dist/statusline/render.d.ts +24 -0
  54. package/lib/dist/statusline/render.d.ts.map +1 -1
  55. package/lib/dist/statusline/render.js +26 -5
  56. package/lib/dist/statusline/render.js.map +1 -1
  57. package/lib/dist/workflows/executor.d.ts +25 -1
  58. package/lib/dist/workflows/executor.d.ts.map +1 -1
  59. package/lib/dist/workflows/executor.js +28 -3
  60. package/lib/dist/workflows/executor.js.map +1 -1
  61. package/lib/node_modules/.package-lock.json +11 -20
  62. package/lib/node_modules/ajv/node_modules/fast-uri/index.js +1 -1
  63. package/lib/node_modules/ajv/node_modules/fast-uri/package.json +1 -1
  64. package/lib/node_modules/ajv/node_modules/fast-uri/test/security.test.js +28 -0
  65. package/lib/node_modules/toad-cache/README.md +10 -9
  66. package/lib/node_modules/toad-cache/dist/toad-cache.cjs +139 -139
  67. package/lib/node_modules/toad-cache/dist/toad-cache.mjs +136 -140
  68. package/lib/node_modules/toad-cache/package.json +8 -8
  69. package/lib/node_modules/toad-cache/toad-cache.d.cts +20 -14
  70. package/lib/node_modules/toad-cache/toad-cache.d.ts +18 -14
  71. package/lib/node_modules/yaml/README.md +14 -31
  72. package/lib/node_modules/yaml/browser/dist/compose/compose-collection.js +3 -3
  73. package/lib/node_modules/yaml/browser/dist/compose/compose-doc.js +0 -1
  74. package/lib/node_modules/yaml/browser/dist/compose/compose-node.js +4 -21
  75. package/lib/node_modules/yaml/browser/dist/compose/compose-scalar.js +8 -14
  76. package/lib/node_modules/yaml/browser/dist/compose/composer.js +2 -4
  77. package/lib/node_modules/yaml/browser/dist/compose/resolve-block-map.js +0 -2
  78. package/lib/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +1 -3
  79. package/lib/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +2 -8
  80. package/lib/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +4 -6
  81. package/lib/node_modules/yaml/browser/dist/compose/resolve-props.js +5 -3
  82. package/lib/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +2 -1
  83. package/lib/node_modules/yaml/browser/dist/compose/util-map-includes.js +5 -1
  84. package/lib/node_modules/yaml/browser/dist/doc/Document.js +2 -3
  85. package/lib/node_modules/yaml/browser/dist/doc/anchors.js +2 -1
  86. package/lib/node_modules/yaml/browser/dist/doc/applyReviver.js +0 -1
  87. package/lib/node_modules/yaml/browser/dist/doc/createNode.js +2 -1
  88. package/lib/node_modules/yaml/browser/dist/errors.js +1 -1
  89. package/lib/node_modules/yaml/browser/dist/log.js +6 -1
  90. package/lib/node_modules/yaml/browser/dist/node_modules/tslib/tslib.es6.js +21 -0
  91. package/lib/node_modules/yaml/browser/dist/nodes/Alias.js +12 -27
  92. package/lib/node_modules/yaml/browser/dist/nodes/YAMLMap.js +1 -1
  93. package/lib/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +49 -8
  94. package/lib/node_modules/yaml/browser/dist/parse/lexer.js +24 -28
  95. package/lib/node_modules/yaml/browser/dist/parse/parser.js +8 -31
  96. package/lib/node_modules/yaml/browser/dist/public-api.js +0 -3
  97. package/lib/node_modules/yaml/browser/dist/schema/Schema.js +2 -1
  98. package/lib/node_modules/yaml/browser/dist/schema/json/schema.js +1 -1
  99. package/lib/node_modules/yaml/browser/dist/schema/tags.js +16 -29
  100. package/lib/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +13 -5
  101. package/lib/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +1 -1
  102. package/lib/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +0 -2
  103. package/lib/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +1 -1
  104. package/lib/node_modules/yaml/browser/dist/stringify/stringify.js +5 -10
  105. package/lib/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +3 -13
  106. package/lib/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +2 -3
  107. package/lib/node_modules/yaml/browser/dist/stringify/stringifyPair.js +1 -1
  108. package/lib/node_modules/yaml/browser/dist/stringify/stringifyString.js +16 -24
  109. package/lib/node_modules/yaml/browser/dist/util.js +1 -1
  110. package/lib/node_modules/yaml/dist/cli.d.ts +1 -1
  111. package/lib/node_modules/yaml/dist/cli.mjs +3 -5
  112. package/lib/node_modules/yaml/dist/compose/compose-collection.d.ts +4 -4
  113. package/lib/node_modules/yaml/dist/compose/compose-collection.js +3 -3
  114. package/lib/node_modules/yaml/dist/compose/compose-doc.d.ts +6 -6
  115. package/lib/node_modules/yaml/dist/compose/compose-doc.js +0 -1
  116. package/lib/node_modules/yaml/dist/compose/compose-node.d.ts +7 -8
  117. package/lib/node_modules/yaml/dist/compose/compose-node.js +4 -21
  118. package/lib/node_modules/yaml/dist/compose/compose-scalar.d.ts +4 -4
  119. package/lib/node_modules/yaml/dist/compose/compose-scalar.js +7 -13
  120. package/lib/node_modules/yaml/dist/compose/composer.d.ts +6 -7
  121. package/lib/node_modules/yaml/dist/compose/composer.js +3 -6
  122. package/lib/node_modules/yaml/dist/compose/resolve-block-map.d.ts +5 -5
  123. package/lib/node_modules/yaml/dist/compose/resolve-block-map.js +0 -2
  124. package/lib/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +5 -5
  125. package/lib/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +5 -5
  126. package/lib/node_modules/yaml/dist/compose/resolve-block-seq.js +1 -3
  127. package/lib/node_modules/yaml/dist/compose/resolve-end.d.ts +2 -2
  128. package/lib/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +7 -7
  129. package/lib/node_modules/yaml/dist/compose/resolve-flow-collection.js +2 -8
  130. package/lib/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +4 -4
  131. package/lib/node_modules/yaml/dist/compose/resolve-flow-scalar.js +4 -6
  132. package/lib/node_modules/yaml/dist/compose/resolve-props.d.ts +2 -2
  133. package/lib/node_modules/yaml/dist/compose/resolve-props.js +5 -3
  134. package/lib/node_modules/yaml/dist/compose/util-contains-newline.d.ts +1 -1
  135. package/lib/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +1 -1
  136. package/lib/node_modules/yaml/dist/compose/util-empty-scalar-position.js +2 -1
  137. package/lib/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +2 -2
  138. package/lib/node_modules/yaml/dist/compose/util-map-includes.d.ts +3 -3
  139. package/lib/node_modules/yaml/dist/compose/util-map-includes.js +5 -1
  140. package/lib/node_modules/yaml/dist/doc/Document.d.ts +11 -11
  141. package/lib/node_modules/yaml/dist/doc/Document.js +2 -3
  142. package/lib/node_modules/yaml/dist/doc/anchors.d.ts +2 -2
  143. package/lib/node_modules/yaml/dist/doc/anchors.js +2 -1
  144. package/lib/node_modules/yaml/dist/doc/applyReviver.js +0 -1
  145. package/lib/node_modules/yaml/dist/doc/createNode.d.ts +4 -4
  146. package/lib/node_modules/yaml/dist/doc/createNode.js +2 -1
  147. package/lib/node_modules/yaml/dist/doc/directives.d.ts +1 -1
  148. package/lib/node_modules/yaml/dist/errors.d.ts +1 -1
  149. package/lib/node_modules/yaml/dist/errors.js +1 -1
  150. package/lib/node_modules/yaml/dist/index.d.ts +18 -21
  151. package/lib/node_modules/yaml/dist/log.js +4 -4
  152. package/lib/node_modules/yaml/dist/nodes/Alias.d.ts +9 -10
  153. package/lib/node_modules/yaml/dist/nodes/Alias.js +11 -26
  154. package/lib/node_modules/yaml/dist/nodes/Collection.d.ts +4 -4
  155. package/lib/node_modules/yaml/dist/nodes/Node.d.ts +9 -16
  156. package/lib/node_modules/yaml/dist/nodes/Pair.d.ts +8 -9
  157. package/lib/node_modules/yaml/dist/nodes/Scalar.d.ts +4 -9
  158. package/lib/node_modules/yaml/dist/nodes/YAMLMap.d.ts +10 -10
  159. package/lib/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +10 -10
  160. package/lib/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +3 -3
  161. package/lib/node_modules/yaml/dist/nodes/addPairToJSMap.js +48 -7
  162. package/lib/node_modules/yaml/dist/nodes/identity.d.ts +7 -7
  163. package/lib/node_modules/yaml/dist/nodes/toJS.d.ts +2 -4
  164. package/lib/node_modules/yaml/dist/options.d.ts +10 -22
  165. package/lib/node_modules/yaml/dist/parse/cst-scalar.d.ts +4 -4
  166. package/lib/node_modules/yaml/dist/parse/cst-stringify.d.ts +1 -1
  167. package/lib/node_modules/yaml/dist/parse/cst-visit.d.ts +2 -2
  168. package/lib/node_modules/yaml/dist/parse/cst.d.ts +3 -4
  169. package/lib/node_modules/yaml/dist/parse/lexer.d.ts +1 -1
  170. package/lib/node_modules/yaml/dist/parse/lexer.js +24 -28
  171. package/lib/node_modules/yaml/dist/parse/parser.d.ts +4 -4
  172. package/lib/node_modules/yaml/dist/parse/parser.js +9 -33
  173. package/lib/node_modules/yaml/dist/public-api.d.ts +5 -6
  174. package/lib/node_modules/yaml/dist/public-api.js +0 -3
  175. package/lib/node_modules/yaml/dist/schema/Schema.d.ts +5 -4
  176. package/lib/node_modules/yaml/dist/schema/Schema.js +2 -1
  177. package/lib/node_modules/yaml/dist/schema/common/map.d.ts +1 -1
  178. package/lib/node_modules/yaml/dist/schema/common/null.d.ts +1 -1
  179. package/lib/node_modules/yaml/dist/schema/common/seq.d.ts +1 -1
  180. package/lib/node_modules/yaml/dist/schema/common/string.d.ts +1 -1
  181. package/lib/node_modules/yaml/dist/schema/core/bool.d.ts +1 -1
  182. package/lib/node_modules/yaml/dist/schema/core/float.d.ts +1 -1
  183. package/lib/node_modules/yaml/dist/schema/core/int.d.ts +1 -1
  184. package/lib/node_modules/yaml/dist/schema/core/schema.d.ts +1 -1
  185. package/lib/node_modules/yaml/dist/schema/json/schema.d.ts +1 -1
  186. package/lib/node_modules/yaml/dist/schema/json/schema.js +1 -1
  187. package/lib/node_modules/yaml/dist/schema/tags.d.ts +3 -11
  188. package/lib/node_modules/yaml/dist/schema/tags.js +13 -26
  189. package/lib/node_modules/yaml/dist/schema/types.d.ts +12 -14
  190. package/lib/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +1 -1
  191. package/lib/node_modules/yaml/dist/schema/yaml-1.1/binary.js +7 -9
  192. package/lib/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +1 -1
  193. package/lib/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +1 -1
  194. package/lib/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +1 -1
  195. package/lib/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +17 -11
  196. package/lib/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +7 -7
  197. package/lib/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +1 -1
  198. package/lib/node_modules/yaml/dist/schema/yaml-1.1/schema.js +0 -2
  199. package/lib/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +9 -9
  200. package/lib/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +1 -1
  201. package/lib/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +1 -1
  202. package/lib/node_modules/yaml/dist/stringify/stringify.d.ts +3 -3
  203. package/lib/node_modules/yaml/dist/stringify/stringify.js +5 -10
  204. package/lib/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +2 -2
  205. package/lib/node_modules/yaml/dist/stringify/stringifyCollection.js +3 -13
  206. package/lib/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +3 -3
  207. package/lib/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +1 -1
  208. package/lib/node_modules/yaml/dist/stringify/stringifyNumber.js +2 -3
  209. package/lib/node_modules/yaml/dist/stringify/stringifyPair.d.ts +2 -2
  210. package/lib/node_modules/yaml/dist/stringify/stringifyPair.js +1 -1
  211. package/lib/node_modules/yaml/dist/stringify/stringifyString.d.ts +2 -2
  212. package/lib/node_modules/yaml/dist/stringify/stringifyString.js +15 -23
  213. package/lib/node_modules/yaml/dist/util.d.ts +12 -16
  214. package/lib/node_modules/yaml/dist/util.js +2 -2
  215. package/lib/node_modules/yaml/dist/visit.d.ts +7 -7
  216. package/lib/node_modules/yaml/package.json +14 -13
  217. package/lib/package.json +5 -6
  218. package/package.json +1 -1
  219. package/lib/node_modules/jsonc-parser/CHANGELOG.md +0 -76
  220. package/lib/node_modules/jsonc-parser/LICENSE.md +0 -21
  221. package/lib/node_modules/jsonc-parser/README.md +0 -364
  222. package/lib/node_modules/jsonc-parser/SECURITY.md +0 -41
  223. package/lib/node_modules/jsonc-parser/lib/esm/impl/edit.js +0 -185
  224. package/lib/node_modules/jsonc-parser/lib/esm/impl/format.js +0 -261
  225. package/lib/node_modules/jsonc-parser/lib/esm/impl/parser.js +0 -659
  226. package/lib/node_modules/jsonc-parser/lib/esm/impl/scanner.js +0 -443
  227. package/lib/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +0 -29
  228. package/lib/node_modules/jsonc-parser/lib/esm/main.d.ts +0 -351
  229. package/lib/node_modules/jsonc-parser/lib/esm/main.js +0 -178
  230. package/lib/node_modules/jsonc-parser/lib/umd/impl/edit.js +0 -201
  231. package/lib/node_modules/jsonc-parser/lib/umd/impl/format.js +0 -275
  232. package/lib/node_modules/jsonc-parser/lib/umd/impl/parser.js +0 -682
  233. package/lib/node_modules/jsonc-parser/lib/umd/impl/scanner.js +0 -456
  234. package/lib/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +0 -42
  235. package/lib/node_modules/jsonc-parser/lib/umd/main.d.ts +0 -351
  236. package/lib/node_modules/jsonc-parser/lib/umd/main.js +0 -194
  237. package/lib/node_modules/jsonc-parser/package.json +0 -37
  238. package/lib/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +0 -67
  239. package/lib/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +0 -9
  240. package/lib/node_modules/yaml/dist/schema/yaml-1.1/merge.js +0 -71
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ /**
3
+ * JIRA config load / resolve / save (REQ-JIRA-001, A2).
4
+ *
5
+ * Config lives at `~/.specship/jira.json` — **user-level only, never in
6
+ * the project tree** (not even a gitignored project file), because it
7
+ * holds a secret. The path is overridable via `SPECSHIP_JIRA_CONFIG`
8
+ * (mirrors `SPECSHIP_SERVER_CONFIG`) so tests never touch the real home.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.jiraConfigPath = jiraConfigPath;
45
+ exports.loadJiraConfig = loadJiraConfig;
46
+ exports.inferDeployment = inferDeployment;
47
+ exports.resolveJiraCredentials = resolveJiraCredentials;
48
+ exports.saveJiraConfig = saveJiraConfig;
49
+ const fs = __importStar(require("fs"));
50
+ const os = __importStar(require("os"));
51
+ const path = __importStar(require("path"));
52
+ const shared_1 = require("../installer/targets/shared");
53
+ const types_1 = require("./types");
54
+ /**
55
+ * Absolute path to the user-level JIRA config. Overridable via
56
+ * `SPECSHIP_JIRA_CONFIG` for tests. Never project-scoped.
57
+ */
58
+ function jiraConfigPath() {
59
+ return (process.env.SPECSHIP_JIRA_CONFIG ??
60
+ path.join(os.homedir(), '.specship', 'jira.json'));
61
+ }
62
+ /**
63
+ * Read `~/.specship/jira.json`. Returns `null` when the file is absent.
64
+ * Throws `JiraConfigError` when it exists but is malformed — a corrupt
65
+ * secret file is a hard error, not a silent fall-through to `{}`.
66
+ */
67
+ function loadJiraConfig() {
68
+ const file = jiraConfigPath();
69
+ let raw;
70
+ try {
71
+ raw = fs.readFileSync(file, 'utf-8');
72
+ }
73
+ catch {
74
+ return null; // absent → not configured
75
+ }
76
+ try {
77
+ const parsed = JSON.parse(raw);
78
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
79
+ throw new types_1.JiraConfigError(`JIRA config at ${file} is not a JSON object. Re-run "specship jira configure".`);
80
+ }
81
+ return parsed;
82
+ }
83
+ catch (err) {
84
+ if (err instanceof types_1.JiraConfigError)
85
+ throw err;
86
+ throw new types_1.JiraConfigError(`JIRA config at ${file} is not valid JSON. Re-run "specship jira configure".`);
87
+ }
88
+ }
89
+ /**
90
+ * Infer the deployment kind from which credentials are present. An
91
+ * explicit `deployment` field always wins. Otherwise: a PAT (with no
92
+ * email+token pair) ⇒ Data Center; an email+token pair ⇒ Cloud.
93
+ */
94
+ function inferDeployment(creds) {
95
+ if (creds.deployment)
96
+ return creds.deployment;
97
+ const hasBasic = Boolean(creds.email && creds.apiToken);
98
+ const hasPat = Boolean(creds.pat);
99
+ if (hasPat && !hasBasic)
100
+ return 'datacenter';
101
+ if (hasBasic)
102
+ return 'cloud';
103
+ if (hasPat)
104
+ return 'datacenter';
105
+ throw new types_1.JiraConfigError('Cannot infer JIRA deployment: provide either an email + API token (Cloud) ' +
106
+ 'or a personal access token (Data Center).');
107
+ }
108
+ /**
109
+ * Resolve effective credentials from environment (highest precedence,
110
+ * per-field) then the on-disk config. Throws `JiraConfigError` with an
111
+ * actionable message when required fields are missing or malformed.
112
+ *
113
+ * SECURITY: the thrown messages never include a token or PAT value.
114
+ */
115
+ function resolveJiraCredentials() {
116
+ const file = loadJiraConfig(); // may throw on malformed
117
+ const baseUrl = process.env.SPECSHIP_JIRA_BASE_URL ?? file?.baseUrl;
118
+ const email = process.env.SPECSHIP_JIRA_EMAIL ?? file?.email;
119
+ const apiToken = process.env.SPECSHIP_JIRA_API_TOKEN ?? file?.apiToken;
120
+ const pat = process.env.SPECSHIP_JIRA_PAT ?? file?.pat;
121
+ const explicitDeployment = normalizeDeployment(process.env.SPECSHIP_JIRA_DEPLOYMENT ?? file?.deployment);
122
+ if (!baseUrl) {
123
+ throw new types_1.JiraConfigError('No JIRA base URL configured. Run "specship jira configure" or set ' +
124
+ 'SPECSHIP_JIRA_BASE_URL.');
125
+ }
126
+ // Lifecycle transition names (REQ-JIRA-007): env overrides file, per field;
127
+ // an unconfigured project keeps the defaults so status-push still works.
128
+ const inProgress = process.env.SPECSHIP_JIRA_TRANSITION_IN_PROGRESS ??
129
+ file?.transitions?.inProgress ??
130
+ 'In Progress';
131
+ const inReview = process.env.SPECSHIP_JIRA_TRANSITION_IN_REVIEW ??
132
+ file?.transitions?.inReview ??
133
+ 'In Review';
134
+ const deployment = inferDeployment({
135
+ deployment: explicitDeployment,
136
+ email,
137
+ apiToken,
138
+ pat,
139
+ });
140
+ if (deployment === 'cloud' && !(email && apiToken)) {
141
+ throw new types_1.JiraConfigError('JIRA Cloud requires both an email and an API token. Run ' +
142
+ '"specship jira configure".');
143
+ }
144
+ if (deployment === 'datacenter' && !pat) {
145
+ throw new types_1.JiraConfigError('JIRA Data Center requires a personal access token. Run ' +
146
+ '"specship jira configure".');
147
+ }
148
+ return {
149
+ baseUrl,
150
+ deployment,
151
+ email,
152
+ apiToken,
153
+ pat,
154
+ transitions: { inProgress, inReview },
155
+ };
156
+ }
157
+ /**
158
+ * Persist config to `~/.specship/jira.json` with `0600` permissions —
159
+ * it holds a secret, so it must not be group/world readable.
160
+ */
161
+ function saveJiraConfig(config) {
162
+ const file = jiraConfigPath();
163
+ (0, shared_1.writeJsonFile)(file, config);
164
+ try {
165
+ fs.chmodSync(file, 0o600);
166
+ }
167
+ catch {
168
+ /* best-effort on platforms without POSIX perms (Windows) */
169
+ }
170
+ }
171
+ function normalizeDeployment(value) {
172
+ if (value === undefined)
173
+ return undefined;
174
+ if (value === 'cloud' || value === 'datacenter')
175
+ return value;
176
+ throw new types_1.JiraConfigError(`Unknown JIRA deployment "${value}". Use "cloud" or "datacenter".`);
177
+ }
178
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/jira/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * JIRA connection module (REQ-JIRA-001). Barrel re-export.
3
+ */
4
+ export * from './types';
5
+ export * from './config';
6
+ export * from './auth';
7
+ export * from './client';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jira/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * JIRA connection module (REQ-JIRA-001). Barrel re-export.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./config"), exports);
22
+ __exportStar(require("./auth"), exports);
23
+ __exportStar(require("./client"), exports);
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jira/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;AAEH,0CAAwB;AACxB,2CAAyB;AACzB,yCAAuB;AACvB,2CAAyB"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Raise a pull request for a completed, verified JIRA implementation run
3
+ * (REQ-JIRA-006).
4
+ *
5
+ * The flow is driven entirely through the repo's EXISTING PR tooling — the
6
+ * GitHub CLI (`gh`) plus `git` — so SpecShip never re-implements auth or the
7
+ * host API. Every step goes through an injectable shell seam so the test suite
8
+ * can assert the command shapes WITHOUT ever running a real `git push` or
9
+ * `gh pr create` (which would push branches / open real PRs).
10
+ *
11
+ * Guarantees:
12
+ * - A3: if `gh` is missing or unauthenticated, or the push fails, we return
13
+ * the reason and DO NOTHING destructive — the caller leaves the branch +
14
+ * worktree fully intact for a manual PR. This helper never removes a
15
+ * worktree or deletes a branch.
16
+ * - A1/A2: the PR title and body embed the issue key (also carried by the
17
+ * branch name) so JIRA's development panel links the PR back to the ticket.
18
+ * - REQ-JIRA-009: no credential/secret is handled or echoed here — only the
19
+ * public issue key ever appears in the title/body.
20
+ */
21
+ /** Result of a single shell invocation through the seam. */
22
+ export interface ShellResult {
23
+ status: number | null;
24
+ stdout: string;
25
+ stderr: string;
26
+ }
27
+ /** Injectable shell runner — stubbed in tests, real `spawnSync` in prod. */
28
+ export type ShellRunner = (command: string, args: string[], cwd?: string) => ShellResult;
29
+ /** Discriminated outcome of a PR-raise attempt. */
30
+ export type PullRequestOutcome = {
31
+ ok: true;
32
+ url: string;
33
+ } | {
34
+ ok: false;
35
+ reason: 'gh-missing' | 'gh-unauthenticated' | 'push-failed' | 'create-failed';
36
+ message: string;
37
+ };
38
+ export interface RaisePullRequestOptions {
39
+ /** Project root (the git repo the worktree branches from). */
40
+ repoRoot: string;
41
+ /** The worktree branch carrying the implementation (and the issue key). */
42
+ branchName: string;
43
+ /** Filesystem path of the worktree the push/PR is created from. */
44
+ worktreePath: string;
45
+ /** The source JIRA issue key (for the caller's title/body; embedded already). */
46
+ issueKey: string;
47
+ /** PR title (build with `buildPrTitle`). */
48
+ title: string;
49
+ /** PR body (build with `buildPrBody`). */
50
+ body: string;
51
+ /** Shell seam override (default: `spawnSync`). Tests always pass a stub. */
52
+ shell?: ShellRunner;
53
+ }
54
+ /**
55
+ * The PR title (A1): `<ISSUE-KEY>: <spec title>`. The leading key makes the PR
56
+ * traceable to the ticket at a glance and is one of the three places JIRA's dev
57
+ * panel matches (branch name + PR title + PR body).
58
+ */
59
+ export declare function buildPrTitle(issueKey: string, specTitle: string): string;
60
+ /**
61
+ * The PR body (A2): the key sits on its OWN line (`Closes <KEY>` + `JIRA: <KEY>`)
62
+ * so JIRA's development panel links the PR back to the issue. No token/secret is
63
+ * ever embedded — only the public key (REQ-JIRA-009).
64
+ */
65
+ export declare function buildPrBody(issueKey: string, specTitle: string): string;
66
+ /**
67
+ * Raise a PR for a verified run. Probes `gh`, pushes the branch, then creates
68
+ * the PR — returning early with a reason on the first failure and NEVER
69
+ * touching the branch or worktree so the work is recoverable (A3).
70
+ */
71
+ export declare function raisePullRequest(opts: RaisePullRequestOptions): Promise<PullRequestOutcome>;
72
+ //# sourceMappingURL=pull-request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-request.d.ts","sourceRoot":"","sources":["../../src/jira/pull-request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,4DAA4D;AAC5D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,CAAC,EAAE,MAAM,KACT,WAAW,CAAC;AAEjB,mDAAmD;AACnD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACzB;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,YAAY,GAAG,oBAAoB,GAAG,aAAa,GAAG,eAAe,CAAC;IAC9E,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAiBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAavE;AASD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CA0D7B"}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * Raise a pull request for a completed, verified JIRA implementation run
4
+ * (REQ-JIRA-006).
5
+ *
6
+ * The flow is driven entirely through the repo's EXISTING PR tooling — the
7
+ * GitHub CLI (`gh`) plus `git` — so SpecShip never re-implements auth or the
8
+ * host API. Every step goes through an injectable shell seam so the test suite
9
+ * can assert the command shapes WITHOUT ever running a real `git push` or
10
+ * `gh pr create` (which would push branches / open real PRs).
11
+ *
12
+ * Guarantees:
13
+ * - A3: if `gh` is missing or unauthenticated, or the push fails, we return
14
+ * the reason and DO NOTHING destructive — the caller leaves the branch +
15
+ * worktree fully intact for a manual PR. This helper never removes a
16
+ * worktree or deletes a branch.
17
+ * - A1/A2: the PR title and body embed the issue key (also carried by the
18
+ * branch name) so JIRA's development panel links the PR back to the ticket.
19
+ * - REQ-JIRA-009: no credential/secret is handled or echoed here — only the
20
+ * public issue key ever appears in the title/body.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.buildPrTitle = buildPrTitle;
24
+ exports.buildPrBody = buildPrBody;
25
+ exports.raisePullRequest = raisePullRequest;
26
+ const child_process_1 = require("child_process");
27
+ /** The default shell runner — wraps `spawnSync`. Never used under test. */
28
+ function defaultShell(command, args, cwd) {
29
+ const r = (0, child_process_1.spawnSync)(command, args, {
30
+ cwd,
31
+ encoding: 'utf-8',
32
+ windowsHide: true,
33
+ });
34
+ return {
35
+ // ENOENT (command not found) surfaces as a null status — treat as failure.
36
+ status: r.status,
37
+ stdout: (r.stdout ?? '').toString(),
38
+ stderr: (r.stderr ?? '').toString(),
39
+ };
40
+ }
41
+ /**
42
+ * The PR title (A1): `<ISSUE-KEY>: <spec title>`. The leading key makes the PR
43
+ * traceable to the ticket at a glance and is one of the three places JIRA's dev
44
+ * panel matches (branch name + PR title + PR body).
45
+ */
46
+ function buildPrTitle(issueKey, specTitle) {
47
+ const title = specTitle.trim() || issueKey;
48
+ return `${issueKey}: ${title}`;
49
+ }
50
+ /**
51
+ * The PR body (A2): the key sits on its OWN line (`Closes <KEY>` + `JIRA: <KEY>`)
52
+ * so JIRA's development panel links the PR back to the issue. No token/secret is
53
+ * ever embedded — only the public key (REQ-JIRA-009).
54
+ */
55
+ function buildPrBody(issueKey, specTitle) {
56
+ const title = specTitle.trim();
57
+ const lines = [
58
+ `Implements ${issueKey}${title ? `: ${title}` : ''}.`,
59
+ '',
60
+ 'Raised automatically by SpecShip after the implementation completed and',
61
+ 'its verification step passed.',
62
+ '',
63
+ // The key on its own line is what JIRA's dev panel matches on.
64
+ `Closes ${issueKey}`,
65
+ `JIRA: ${issueKey}`,
66
+ ];
67
+ return lines.join('\n');
68
+ }
69
+ /** Pull the created PR URL out of `gh pr create` stdout (it prints the URL). */
70
+ function extractUrl(stdout) {
71
+ const match = stdout.match(/https?:\/\/\S+/);
72
+ if (match)
73
+ return match[0];
74
+ return stdout.trim();
75
+ }
76
+ /**
77
+ * Raise a PR for a verified run. Probes `gh`, pushes the branch, then creates
78
+ * the PR — returning early with a reason on the first failure and NEVER
79
+ * touching the branch or worktree so the work is recoverable (A3).
80
+ */
81
+ async function raisePullRequest(opts) {
82
+ const shell = opts.shell ?? defaultShell;
83
+ const { branchName, worktreePath, title, body } = opts;
84
+ // A3: probe the PR tooling before we change anything.
85
+ const version = shell('gh', ['--version'], worktreePath);
86
+ if (version.status !== 0) {
87
+ return {
88
+ ok: false,
89
+ reason: 'gh-missing',
90
+ message: 'GitHub CLI (gh) is not installed, so no pull request was raised. ' +
91
+ 'Install gh (https://cli.github.com) and re-run, or open a PR manually — ' +
92
+ `the branch "${branchName}" and its worktree are left intact.`,
93
+ };
94
+ }
95
+ const auth = shell('gh', ['auth', 'status'], worktreePath);
96
+ if (auth.status !== 0) {
97
+ return {
98
+ ok: false,
99
+ reason: 'gh-unauthenticated',
100
+ message: 'GitHub CLI (gh) is not authenticated, so no pull request was raised. ' +
101
+ 'Run "gh auth login" and re-run, or open a PR manually — the branch ' +
102
+ `"${branchName}" and its worktree are left intact.`,
103
+ };
104
+ }
105
+ const push = shell('git', ['push', '-u', 'origin', branchName], worktreePath);
106
+ if (push.status !== 0) {
107
+ return {
108
+ ok: false,
109
+ reason: 'push-failed',
110
+ message: `Pushing branch "${branchName}" failed, so no pull request was raised: ` +
111
+ `${push.stderr.trim() || 'unknown error'}. The branch and its worktree ` +
112
+ 'are left intact for a manual push/PR.',
113
+ };
114
+ }
115
+ const create = shell('gh', ['pr', 'create', '--title', title, '--body', body, '--head', branchName], worktreePath);
116
+ if (create.status !== 0) {
117
+ return {
118
+ ok: false,
119
+ reason: 'create-failed',
120
+ message: `"gh pr create" failed, so no pull request was raised: ` +
121
+ `${create.stderr.trim() || 'unknown error'}. The branch "${branchName}" ` +
122
+ 'was pushed and its worktree is intact — open the PR manually.',
123
+ };
124
+ }
125
+ return { ok: true, url: extractUrl(create.stdout) };
126
+ }
127
+ //# sourceMappingURL=pull-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-request.js","sourceRoot":"","sources":["../../src/jira/pull-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * JIRA issue → SpecShip spec markdown (REQ-JIRA-004).
3
+ *
4
+ * A "pick" turns a fetched issue into a well-formed, low-ceremony spec:
5
+ * the summary becomes the title, the description becomes an RFC-2119
6
+ * requirement body, and each subtask becomes an acceptance bullet. The
7
+ * generated markdown mirrors the `MarkdownSpecExtractor` contract exactly
8
+ * (frontmatter, `<!-- id: -->` markers, `## Acceptance` container, id-marked
9
+ * bullets) so it indexes with zero `spec_missing_id`/error results (A2).
10
+ *
11
+ * SECURITY: the issue summary / description / subtask summaries are UNTRUSTED
12
+ * content being embedded into a spec file. A description that contains a
13
+ * literal `<!-- id: REQ-EVIL-001 -->` marker or a `## Acceptance` / `# ` heading
14
+ * must NOT be able to inject spec structure that corrupts the generated spec or,
15
+ * on index, collides with another spec. `neutralizeLine` defuses those
16
+ * sequences so the injected markers index as inert prose. This module is pure —
17
+ * no I/O — and never handles or echoes any credential (REQ-JIRA-009).
18
+ */
19
+ import type { JiraIssue } from './types';
20
+ /**
21
+ * Derive a stable, collision-safe requirement id from the FULL issue key
22
+ * (unique within the connected JIRA). `PROJ-123` → `REQ-PROJ-123`. The key is
23
+ * upper-cased and sanitized to `[A-Z0-9-]` so it is safe both as an id marker
24
+ * and as a filename slug. Drives the `<!-- id: -->` marker AND idempotency.
25
+ */
26
+ export declare function reqIdForIssue(key: string): string;
27
+ /**
28
+ * Generate a full, well-formed spec document from a JIRA issue. The frontmatter
29
+ * records the source key (`jira_issue:` — the A1 link + the idempotency key);
30
+ * the H1 carries the requirement id marker; the `## Acceptance` container holds
31
+ * one id-marked bullet per subtask.
32
+ */
33
+ export declare function generateSpecMarkdown(issue: JiraIssue): string;
34
+ //# sourceMappingURL=spec-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-generator.d.ts","sourceRoot":"","sources":["../../src/jira/spec-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAUzC;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOjD;AAyED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAuB7D"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * JIRA issue → SpecShip spec markdown (REQ-JIRA-004).
4
+ *
5
+ * A "pick" turns a fetched issue into a well-formed, low-ceremony spec:
6
+ * the summary becomes the title, the description becomes an RFC-2119
7
+ * requirement body, and each subtask becomes an acceptance bullet. The
8
+ * generated markdown mirrors the `MarkdownSpecExtractor` contract exactly
9
+ * (frontmatter, `<!-- id: -->` markers, `## Acceptance` container, id-marked
10
+ * bullets) so it indexes with zero `spec_missing_id`/error results (A2).
11
+ *
12
+ * SECURITY: the issue summary / description / subtask summaries are UNTRUSTED
13
+ * content being embedded into a spec file. A description that contains a
14
+ * literal `<!-- id: REQ-EVIL-001 -->` marker or a `## Acceptance` / `# ` heading
15
+ * must NOT be able to inject spec structure that corrupts the generated spec or,
16
+ * on index, collides with another spec. `neutralizeLine` defuses those
17
+ * sequences so the injected markers index as inert prose. This module is pure —
18
+ * no I/O — and never handles or echoes any credential (REQ-JIRA-009).
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.reqIdForIssue = reqIdForIssue;
22
+ exports.generateSpecMarkdown = generateSpecMarkdown;
23
+ /**
24
+ * RFC-2119 keyword set. If the derived body already carries one of these, we
25
+ * keep it as-is; otherwise we prefix a MUST sentence so the requirement is
26
+ * always normative (A2 — the pipeline expects RFC-2119 keywords).
27
+ */
28
+ const RFC2119 = /\b(MUST|MUST NOT|SHALL|SHALL NOT|SHOULD|SHOULD NOT|REQUIRED|MAY)\b/;
29
+ /**
30
+ * Derive a stable, collision-safe requirement id from the FULL issue key
31
+ * (unique within the connected JIRA). `PROJ-123` → `REQ-PROJ-123`. The key is
32
+ * upper-cased and sanitized to `[A-Z0-9-]` so it is safe both as an id marker
33
+ * and as a filename slug. Drives the `<!-- id: -->` marker AND idempotency.
34
+ */
35
+ function reqIdForIssue(key) {
36
+ const sanitized = key
37
+ .toUpperCase()
38
+ .replace(/[^A-Z0-9-]/g, '-')
39
+ .replace(/-+/g, '-')
40
+ .replace(/^-|-$/g, '');
41
+ return `REQ-${sanitized || 'JIRA'}`;
42
+ }
43
+ /**
44
+ * Neutralize a single line of UNTRUSTED issue text so it can't inject spec
45
+ * structure when embedded in the generated markdown:
46
+ * - break HTML-comment markers (`<!-- id: … -->`) so they never match the
47
+ * extractor's `ID_COMMENT`, which would otherwise strand or re-parent nodes;
48
+ * - escape a leading heading run (`#`..`######`) so it never opens a new
49
+ * id-less section (which raises `spec_missing_id`) or a second `## Acceptance`
50
+ * container.
51
+ * The line stays human-readable — the injected markers render as literal text.
52
+ */
53
+ function neutralizeLine(line) {
54
+ let out = line.replace(/<!--/g, '&lt;!--').replace(/-->/g, '--&gt;');
55
+ // Escape a leading heading marker: `## Acceptance` → `\## Acceptance`.
56
+ out = out.replace(/^(\s*)(#{1,6})(\s+)/, '$1\\$2$3');
57
+ return out;
58
+ }
59
+ /** Neutralize a multi-line block line-by-line, preserving paragraph breaks. */
60
+ function neutralizeBlock(text) {
61
+ return text.split(/\r?\n/).map(neutralizeLine).join('\n').trim();
62
+ }
63
+ /**
64
+ * Collapse UNTRUSTED text to a single neutralized line — for the H1 title and
65
+ * acceptance bullets, which must stay on one line. Newlines are folded to spaces
66
+ * (so a multi-line summary can't spill into the next markdown construct) and the
67
+ * result is neutralized. Returns `fallback` when the text is empty.
68
+ */
69
+ function neutralizeInline(text, fallback) {
70
+ const collapsed = text.replace(/\s+/g, ' ').trim();
71
+ const safe = neutralizeLine(collapsed).trim();
72
+ return safe || fallback;
73
+ }
74
+ /**
75
+ * Derive the requirement body from the issue. If the (neutralized) description
76
+ * already carries an RFC-2119 keyword we use it verbatim; otherwise we prefix a
77
+ * MUST sentence built from the summary so the body is always normative and never
78
+ * empty (A2).
79
+ */
80
+ function deriveBody(issue) {
81
+ const desc = neutralizeBlock(issue.description?.trim() ?? '');
82
+ if (desc && RFC2119.test(desc))
83
+ return desc;
84
+ const summary = neutralizeInline(issue.summary ?? '', issue.key);
85
+ const must = `The implementation MUST satisfy JIRA issue ${issue.key}: ${summary}.`;
86
+ return desc ? `${must}\n\n${desc}` : must;
87
+ }
88
+ /**
89
+ * Derive acceptance bullets — one per subtask, or a single derived bullet when
90
+ * the issue has none (never zero — A2). Each bullet id is `${reqId}.A<n>` so the
91
+ * extractor parents it to the requirement by id suffix.
92
+ */
93
+ function deriveAcceptance(issue) {
94
+ const reqId = reqIdForIssue(issue.key);
95
+ const subs = issue.subtasks ?? [];
96
+ if (subs.length > 0) {
97
+ return subs.map((st, i) => ({
98
+ id: `${reqId}.A${i + 1}`,
99
+ text: neutralizeInline(st.summary ?? '', st.key || `Subtask ${i + 1}`),
100
+ }));
101
+ }
102
+ return [
103
+ {
104
+ id: `${reqId}.A1`,
105
+ text: `The behaviour described by ${issue.key} is implemented and verified against the issue.`,
106
+ },
107
+ ];
108
+ }
109
+ /**
110
+ * Generate a full, well-formed spec document from a JIRA issue. The frontmatter
111
+ * records the source key (`jira_issue:` — the A1 link + the idempotency key);
112
+ * the H1 carries the requirement id marker; the `## Acceptance` container holds
113
+ * one id-marked bullet per subtask.
114
+ */
115
+ function generateSpecMarkdown(issue) {
116
+ const reqId = reqIdForIssue(issue.key);
117
+ const title = neutralizeInline(issue.summary ?? '', issue.key);
118
+ const body = deriveBody(issue);
119
+ const acceptance = deriveAcceptance(issue);
120
+ const lines = [
121
+ '---',
122
+ 'format: markdown',
123
+ `jira_issue: ${issue.key}`,
124
+ '---',
125
+ `<!-- id: ${reqId} -->`,
126
+ `# ${title}`,
127
+ '',
128
+ body,
129
+ '',
130
+ '## Acceptance',
131
+ ];
132
+ for (const bullet of acceptance) {
133
+ lines.push(`<!-- id: ${bullet.id} -->`);
134
+ lines.push(`- ${bullet.text}`);
135
+ }
136
+ return lines.join('\n') + '\n';
137
+ }
138
+ //# sourceMappingURL=spec-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-generator.js","sourceRoot":"","sources":["../../src/jira/spec-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * JIRA issue → spec file on disk (REQ-JIRA-004).
3
+ *
4
+ * `writeSpecFromIssue` resolves the project's `specs/` directory, generates the
5
+ * spec markdown from the issue, and writes it idempotently (A3). Idempotency is
6
+ * keyed on the frontmatter `jira_issue:` value — parsed from a real frontmatter
7
+ * block, NOT grepped from the body (which could match the key in prose) — so a
8
+ * re-pick of the same issue overwrites the existing file even if it was renamed,
9
+ * and never leaves a duplicate. Pure filesystem side of the feature; it handles
10
+ * no credential (REQ-JIRA-009).
11
+ */
12
+ import type { JiraIssue } from './types';
13
+ export interface WriteSpecResult {
14
+ /** Absolute path of the written spec file. */
15
+ path: string;
16
+ /** True when a new file was created; false when an existing one was updated. */
17
+ created: boolean;
18
+ }
19
+ /**
20
+ * Find the spec file (if any) whose frontmatter records `issueKey`, scanning
21
+ * `<projectRoot>/specs/`. Returns the absolute path, or `null` when no spec for
22
+ * the key exists. Keyed on the parsed `jira_issue:` frontmatter value — never a
23
+ * body match — so it locates the exact file a pick wrote even if it was renamed.
24
+ * Shared by `writeSpecFromIssue` (idempotent overwrite) and `specship_jira_start`
25
+ * (does a spec for this key exist yet?).
26
+ */
27
+ export declare function findSpecForIssueKey(issueKey: string, projectRoot: string): string | null;
28
+ /**
29
+ * Generate and write a spec for `issue` under `<projectRoot>/specs/`, creating
30
+ * the directory if needed. If an existing spec already records this issue key in
31
+ * its frontmatter, that file is overwritten (keyed on the key, not the filename,
32
+ * so a renamed file still updates); otherwise a new `specs/jira-<slug>.md` is
33
+ * created.
34
+ */
35
+ export declare function writeSpecFromIssue(issue: JiraIssue, projectRoot: string): WriteSpecResult;
36
+ //# sourceMappingURL=spec-writer.d.ts.map