@iloom/cli 0.7.6 → 0.8.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 (169) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +32 -3
  3. package/dist/{ClaudeContextManager-Y2YJC6BU.js → ClaudeContextManager-RDP6CLK6.js} +5 -5
  4. package/dist/{ClaudeService-NDVFQRKC.js → ClaudeService-FKPOQRA4.js} +4 -4
  5. package/dist/GitHubService-ACZVNTJE.js +12 -0
  6. package/dist/{LoomLauncher-U2B3VHPC.js → LoomLauncher-NHZMEVTQ.js} +5 -5
  7. package/dist/{MetadataManager-XJ2YB762.js → MetadataManager-W3C54UYT.js} +2 -2
  8. package/dist/{PRManager-7F3AAY66.js → PRManager-XLTVG6YG.js} +5 -5
  9. package/dist/{PromptTemplateManager-7L3HJQQU.js → PromptTemplateManager-OUYDHOPI.js} +2 -2
  10. package/dist/README.md +32 -3
  11. package/dist/{SettingsManager-YU4VYPTW.js → SettingsManager-VCVLL32H.js} +4 -2
  12. package/dist/{SettingsMigrationManager-KZKDG66H.js → SettingsMigrationManager-LEBMJP3B.js} +3 -3
  13. package/dist/agents/iloom-code-reviewer.md +720 -0
  14. package/dist/agents/iloom-issue-analyze-and-plan.md +1 -1
  15. package/dist/agents/iloom-issue-analyzer.md +1 -1
  16. package/dist/agents/iloom-issue-complexity-evaluator.md +1 -1
  17. package/dist/agents/iloom-issue-enhancer.md +1 -1
  18. package/dist/agents/iloom-issue-implementer.md +1 -1
  19. package/dist/agents/iloom-issue-planner.md +1 -1
  20. package/dist/{build-HQ5HGA3T.js → build-H4DK3DMQ.js} +7 -7
  21. package/dist/{chunk-N7FVXZNI.js → chunk-4BSXZ5YZ.js} +31 -9
  22. package/dist/chunk-4BSXZ5YZ.js.map +1 -0
  23. package/dist/{chunk-VYKKWU36.js → chunk-4KGRPHM6.js} +3 -3
  24. package/dist/{chunk-CFQVOTHO.js → chunk-52MVUK5V.js} +2 -2
  25. package/dist/{chunk-TIYJEEVO.js → chunk-66QOCD5N.js} +1 -1
  26. package/dist/chunk-66QOCD5N.js.map +1 -0
  27. package/dist/chunk-7JDMYTFZ.js +251 -0
  28. package/dist/chunk-7JDMYTFZ.js.map +1 -0
  29. package/dist/{chunk-7LSSNB7Y.js → chunk-7ZEHSSUP.js} +2 -2
  30. package/dist/chunk-A4UQY3M2.js +75 -0
  31. package/dist/chunk-A4UQY3M2.js.map +1 -0
  32. package/dist/{chunk-WT4UGBE2.js → chunk-AZH27CPV.js} +5 -5
  33. package/dist/{chunk-ELJKYFSH.js → chunk-BCQDYAOJ.js} +4 -4
  34. package/dist/{chunk-F2PWIRV4.js → chunk-BYUMEDDD.js} +2 -2
  35. package/dist/{chunk-CAXFWFV6.js → chunk-ECP77QGE.js} +4 -4
  36. package/dist/{chunk-ZA575VLF.js → chunk-GDS2HXSW.js} +4 -4
  37. package/dist/{chunk-UDRZY65Y.js → chunk-HSGZW3ID.js} +2 -2
  38. package/dist/{chunk-WFQ5CLTR.js → chunk-IWIIOFEB.js} +56 -5
  39. package/dist/chunk-IWIIOFEB.js.map +1 -0
  40. package/dist/{chunk-VWGKGNJP.js → chunk-KBEIQP4G.js} +3 -1
  41. package/dist/chunk-KBEIQP4G.js.map +1 -0
  42. package/dist/{chunk-NPEMVE27.js → chunk-L4CN7YQT.js} +115 -3
  43. package/dist/chunk-L4CN7YQT.js.map +1 -0
  44. package/dist/{chunk-HBJITKSZ.js → chunk-LFVRG6UU.js} +159 -3
  45. package/dist/chunk-LFVRG6UU.js.map +1 -0
  46. package/dist/{chunk-64HCHVJM.js → chunk-PLI3JQWT.js} +2 -2
  47. package/dist/{chunk-USJSNHGG.js → chunk-PVW6JE7E.js} +3 -3
  48. package/dist/{chunk-3K3WY3BN.js → chunk-QJX6ICWY.js} +4 -4
  49. package/dist/{chunk-C7YW5IMS.js → chunk-RODL2HVY.js} +17 -6
  50. package/dist/{chunk-C7YW5IMS.js.map → chunk-RODL2HVY.js.map} +1 -1
  51. package/dist/{chunk-NEPH2O4C.js → chunk-SSASIBDJ.js} +3 -3
  52. package/dist/{chunk-GCPAZSGV.js → chunk-THS5L54H.js} +150 -3
  53. package/dist/chunk-THS5L54H.js.map +1 -0
  54. package/dist/{chunk-5V74K5ZA.js → chunk-TVH67KEO.js} +25 -2
  55. package/dist/chunk-TVH67KEO.js.map +1 -0
  56. package/dist/{chunk-ENMTWE74.js → chunk-VZYSM7N7.js} +2 -2
  57. package/dist/{chunk-ETY2SBW5.js → chunk-WNXYC7J4.js} +17 -15
  58. package/dist/chunk-WNXYC7J4.js.map +1 -0
  59. package/dist/{chunk-WZYBHD7P.js → chunk-XHNACIHO.js} +2 -2
  60. package/dist/{chunk-XAMBIVXE.js → chunk-XJHQVOT6.js} +2 -2
  61. package/dist/{chunk-O36JLYNW.js → chunk-XU5A6BWA.js} +4 -7
  62. package/dist/chunk-XU5A6BWA.js.map +1 -0
  63. package/dist/{chunk-6YAMWLCP.js → chunk-YAVVDZVF.js} +3 -3
  64. package/dist/{cleanup-IO4KV2DL.js → cleanup-25PCP2EM.js} +16 -16
  65. package/dist/cli.js +107 -157
  66. package/dist/cli.js.map +1 -1
  67. package/dist/{commit-3ULFKXNB.js → commit-SS77KUNX.js} +10 -10
  68. package/dist/{compile-CT7IR7O2.js → compile-ZOAODFN2.js} +7 -7
  69. package/dist/{contribute-GXKOIA42.js → contribute-7USRBWRM.js} +6 -6
  70. package/dist/{dev-server-OAP3RZC6.js → dev-server-TYYJM3XA.js} +9 -9
  71. package/dist/{feedback-ZLAX3BVL.js → feedback-HZVLOTQJ.js} +9 -9
  72. package/dist/{git-ENLT2VNI.js → git-GUNOPP4Q.js} +4 -4
  73. package/dist/hooks/iloom-hook.js +75 -3
  74. package/dist/{ignite-HA2OJF6Z.js → ignite-CPXPZ4ZD.js} +85 -25
  75. package/dist/ignite-CPXPZ4ZD.js.map +1 -0
  76. package/dist/index.d.ts +85 -2
  77. package/dist/index.js +133 -73
  78. package/dist/index.js.map +1 -1
  79. package/dist/init-MZBIXQ7V.js +21 -0
  80. package/dist/{lint-HAVU4U34.js → lint-MDVUV3W2.js} +7 -7
  81. package/dist/mcp/issue-management-server.js +569 -2
  82. package/dist/mcp/issue-management-server.js.map +1 -1
  83. package/dist/{neon-helpers-3KBC4A3Y.js → neon-helpers-VVFFTLXE.js} +3 -3
  84. package/dist/{open-IN3LUZXX.js → open-2LPZ7XXW.js} +9 -9
  85. package/dist/plan-N3YDCOIV.js +371 -0
  86. package/dist/plan-N3YDCOIV.js.map +1 -0
  87. package/dist/{projects-CTRTTMSK.js → projects-325GEEGJ.js} +2 -2
  88. package/dist/{prompt-3SAZYRUN.js → prompt-ONNPSNKM.js} +2 -2
  89. package/dist/prompts/init-prompt.txt +57 -1
  90. package/dist/prompts/issue-prompt.txt +51 -3
  91. package/dist/prompts/plan-prompt.txt +435 -0
  92. package/dist/prompts/pr-prompt.txt +38 -0
  93. package/dist/prompts/regular-prompt.txt +53 -3
  94. package/dist/{rebase-RLEVFHWN.js → rebase-7YS3N274.js} +6 -6
  95. package/dist/{recap-ZKGHZCX6.js → recap-GSXFEOD6.js} +6 -6
  96. package/dist/{run-QEIS2EH2.js → run-XPGCMFFO.js} +9 -9
  97. package/dist/schema/settings.schema.json +57 -1
  98. package/dist/{shell-2NNSIU34.js → shell-2SPM3Z5O.js} +6 -6
  99. package/dist/{summary-MPOOQIOX.js → summary-5UWNLAI5.js} +11 -11
  100. package/dist/{test-75WAA6DU.js → test-N2725YRI.js} +7 -7
  101. package/dist/{test-git-E2BLXR6M.js → test-git-ZPSPA2TP.js} +4 -4
  102. package/dist/{test-prefix-A7JGGYAA.js → test-prefix-6DLB2BHE.js} +4 -4
  103. package/dist/{test-webserver-J6SMNLU2.js → test-webserver-XLJ2TZFP.js} +6 -6
  104. package/package.json +1 -1
  105. package/dist/GitHubService-O7U4UQ7N.js +0 -12
  106. package/dist/agents/iloom-issue-reviewer.md +0 -139
  107. package/dist/chunk-5V74K5ZA.js.map +0 -1
  108. package/dist/chunk-ETY2SBW5.js.map +0 -1
  109. package/dist/chunk-GCPAZSGV.js.map +0 -1
  110. package/dist/chunk-HBJITKSZ.js.map +0 -1
  111. package/dist/chunk-N7FVXZNI.js.map +0 -1
  112. package/dist/chunk-NPEMVE27.js.map +0 -1
  113. package/dist/chunk-O36JLYNW.js.map +0 -1
  114. package/dist/chunk-TIYJEEVO.js.map +0 -1
  115. package/dist/chunk-VWGKGNJP.js.map +0 -1
  116. package/dist/chunk-WFQ5CLTR.js.map +0 -1
  117. package/dist/chunk-ZX3GTM7O.js +0 -119
  118. package/dist/chunk-ZX3GTM7O.js.map +0 -1
  119. package/dist/ignite-HA2OJF6Z.js.map +0 -1
  120. package/dist/init-S6IEGRSX.js +0 -21
  121. /package/dist/{ClaudeContextManager-Y2YJC6BU.js.map → ClaudeContextManager-RDP6CLK6.js.map} +0 -0
  122. /package/dist/{ClaudeService-NDVFQRKC.js.map → ClaudeService-FKPOQRA4.js.map} +0 -0
  123. /package/dist/{GitHubService-O7U4UQ7N.js.map → GitHubService-ACZVNTJE.js.map} +0 -0
  124. /package/dist/{LoomLauncher-U2B3VHPC.js.map → LoomLauncher-NHZMEVTQ.js.map} +0 -0
  125. /package/dist/{MetadataManager-XJ2YB762.js.map → MetadataManager-W3C54UYT.js.map} +0 -0
  126. /package/dist/{PRManager-7F3AAY66.js.map → PRManager-XLTVG6YG.js.map} +0 -0
  127. /package/dist/{PromptTemplateManager-7L3HJQQU.js.map → PromptTemplateManager-OUYDHOPI.js.map} +0 -0
  128. /package/dist/{SettingsManager-YU4VYPTW.js.map → SettingsManager-VCVLL32H.js.map} +0 -0
  129. /package/dist/{SettingsMigrationManager-KZKDG66H.js.map → SettingsMigrationManager-LEBMJP3B.js.map} +0 -0
  130. /package/dist/{build-HQ5HGA3T.js.map → build-H4DK3DMQ.js.map} +0 -0
  131. /package/dist/{chunk-VYKKWU36.js.map → chunk-4KGRPHM6.js.map} +0 -0
  132. /package/dist/{chunk-CFQVOTHO.js.map → chunk-52MVUK5V.js.map} +0 -0
  133. /package/dist/{chunk-7LSSNB7Y.js.map → chunk-7ZEHSSUP.js.map} +0 -0
  134. /package/dist/{chunk-WT4UGBE2.js.map → chunk-AZH27CPV.js.map} +0 -0
  135. /package/dist/{chunk-ELJKYFSH.js.map → chunk-BCQDYAOJ.js.map} +0 -0
  136. /package/dist/{chunk-F2PWIRV4.js.map → chunk-BYUMEDDD.js.map} +0 -0
  137. /package/dist/{chunk-CAXFWFV6.js.map → chunk-ECP77QGE.js.map} +0 -0
  138. /package/dist/{chunk-ZA575VLF.js.map → chunk-GDS2HXSW.js.map} +0 -0
  139. /package/dist/{chunk-UDRZY65Y.js.map → chunk-HSGZW3ID.js.map} +0 -0
  140. /package/dist/{chunk-64HCHVJM.js.map → chunk-PLI3JQWT.js.map} +0 -0
  141. /package/dist/{chunk-USJSNHGG.js.map → chunk-PVW6JE7E.js.map} +0 -0
  142. /package/dist/{chunk-3K3WY3BN.js.map → chunk-QJX6ICWY.js.map} +0 -0
  143. /package/dist/{chunk-NEPH2O4C.js.map → chunk-SSASIBDJ.js.map} +0 -0
  144. /package/dist/{chunk-ENMTWE74.js.map → chunk-VZYSM7N7.js.map} +0 -0
  145. /package/dist/{chunk-WZYBHD7P.js.map → chunk-XHNACIHO.js.map} +0 -0
  146. /package/dist/{chunk-XAMBIVXE.js.map → chunk-XJHQVOT6.js.map} +0 -0
  147. /package/dist/{chunk-6YAMWLCP.js.map → chunk-YAVVDZVF.js.map} +0 -0
  148. /package/dist/{cleanup-IO4KV2DL.js.map → cleanup-25PCP2EM.js.map} +0 -0
  149. /package/dist/{commit-3ULFKXNB.js.map → commit-SS77KUNX.js.map} +0 -0
  150. /package/dist/{compile-CT7IR7O2.js.map → compile-ZOAODFN2.js.map} +0 -0
  151. /package/dist/{contribute-GXKOIA42.js.map → contribute-7USRBWRM.js.map} +0 -0
  152. /package/dist/{dev-server-OAP3RZC6.js.map → dev-server-TYYJM3XA.js.map} +0 -0
  153. /package/dist/{feedback-ZLAX3BVL.js.map → feedback-HZVLOTQJ.js.map} +0 -0
  154. /package/dist/{git-ENLT2VNI.js.map → git-GUNOPP4Q.js.map} +0 -0
  155. /package/dist/{init-S6IEGRSX.js.map → init-MZBIXQ7V.js.map} +0 -0
  156. /package/dist/{lint-HAVU4U34.js.map → lint-MDVUV3W2.js.map} +0 -0
  157. /package/dist/{neon-helpers-3KBC4A3Y.js.map → neon-helpers-VVFFTLXE.js.map} +0 -0
  158. /package/dist/{open-IN3LUZXX.js.map → open-2LPZ7XXW.js.map} +0 -0
  159. /package/dist/{projects-CTRTTMSK.js.map → projects-325GEEGJ.js.map} +0 -0
  160. /package/dist/{prompt-3SAZYRUN.js.map → prompt-ONNPSNKM.js.map} +0 -0
  161. /package/dist/{rebase-RLEVFHWN.js.map → rebase-7YS3N274.js.map} +0 -0
  162. /package/dist/{recap-ZKGHZCX6.js.map → recap-GSXFEOD6.js.map} +0 -0
  163. /package/dist/{run-QEIS2EH2.js.map → run-XPGCMFFO.js.map} +0 -0
  164. /package/dist/{shell-2NNSIU34.js.map → shell-2SPM3Z5O.js.map} +0 -0
  165. /package/dist/{summary-MPOOQIOX.js.map → summary-5UWNLAI5.js.map} +0 -0
  166. /package/dist/{test-75WAA6DU.js.map → test-N2725YRI.js.map} +0 -0
  167. /package/dist/{test-git-E2BLXR6M.js.map → test-git-ZPSPA2TP.js.map} +0 -0
  168. /package/dist/{test-prefix-A7JGGYAA.js.map → test-prefix-6DLB2BHE.js.map} +0 -0
  169. /package/dist/{test-webserver-J6SMNLU2.js.map → test-webserver-XLJ2TZFP.js.map} +0 -0
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ InitCommand
4
+ } from "./chunk-RODL2HVY.js";
5
+ import "./chunk-Q7POFB5Q.js";
6
+ import "./chunk-BYUMEDDD.js";
7
+ import "./chunk-4BSXZ5YZ.js";
8
+ import "./chunk-HSGZW3ID.js";
9
+ import "./chunk-FXDYIV3K.js";
10
+ import "./chunk-66QOCD5N.js";
11
+ import "./chunk-GDS2HXSW.js";
12
+ import "./chunk-IWIIOFEB.js";
13
+ import "./chunk-KBEIQP4G.js";
14
+ import "./chunk-7JDMYTFZ.js";
15
+ import "./chunk-IGKPPACU.js";
16
+ import "./chunk-6MLEBAYZ.js";
17
+ import "./chunk-VT4PDUYT.js";
18
+ export {
19
+ InitCommand
20
+ };
21
+ //# sourceMappingURL=init-MZBIXQ7V.js.map
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ScriptCommandBase
4
- } from "./chunk-ELJKYFSH.js";
5
- import "./chunk-5V74K5ZA.js";
4
+ } from "./chunk-BCQDYAOJ.js";
5
+ import "./chunk-TVH67KEO.js";
6
6
  import "./chunk-RD7I2Q2F.js";
7
- import "./chunk-UDRZY65Y.js";
7
+ import "./chunk-HSGZW3ID.js";
8
8
  import "./chunk-XPKN3QWY.js";
9
- import "./chunk-ZA575VLF.js";
10
- import "./chunk-WFQ5CLTR.js";
11
- import "./chunk-VWGKGNJP.js";
9
+ import "./chunk-GDS2HXSW.js";
10
+ import "./chunk-IWIIOFEB.js";
11
+ import "./chunk-KBEIQP4G.js";
12
12
  import "./chunk-6MLEBAYZ.js";
13
13
  import "./chunk-VT4PDUYT.js";
14
14
 
@@ -24,4 +24,4 @@ var LintCommand = class extends ScriptCommandBase {
24
24
  export {
25
25
  LintCommand
26
26
  };
27
- //# sourceMappingURL=lint-HAVU4U34.js.map
27
+ //# sourceMappingURL=lint-MDVUV3W2.js.map
@@ -125,7 +125,7 @@ async function executeGhCommand(args, options) {
125
125
  timeout: (options == null ? void 0 : options.timeout) ?? 3e4,
126
126
  encoding: "utf8"
127
127
  });
128
- const isJson = args.includes("--json") || args.includes("--jq") || args.includes("--format") && args[args.indexOf("--format") + 1] === "json";
128
+ const isJson = args.includes("--json") || args.includes("--jq") || args.includes("--format") && args[args.indexOf("--format") + 1] === "json" || args[0] === "api" && args[1] === "graphql";
129
129
  const data = isJson ? JSON.parse(result.stdout) : result.stdout;
130
130
  return data;
131
131
  }
@@ -235,6 +235,148 @@ async function addSubIssue(parentNodeId, childNodeId) {
235
235
  `subIssueId=${childNodeId}`
236
236
  ]);
237
237
  }
238
+ async function getSubIssues(issueNumber, repo) {
239
+ var _a, _b;
240
+ logger.debug("Fetching GitHub sub-issues", { issueNumber, repo });
241
+ const parentNodeId = await getIssueNodeId(issueNumber, repo);
242
+ const query = `
243
+ query getSubIssues($parentId: ID!) {
244
+ node(id: $parentId) {
245
+ ... on Issue {
246
+ subIssues(first: 100) {
247
+ nodes {
248
+ number
249
+ title
250
+ url
251
+ state
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+ `;
258
+ try {
259
+ const result = await executeGhCommand([
260
+ "api",
261
+ "graphql",
262
+ "-H",
263
+ "GraphQL-Features: sub_issues",
264
+ "-f",
265
+ `query=${query}`,
266
+ "-F",
267
+ `parentId=${parentNodeId}`
268
+ ]);
269
+ const subIssues = ((_b = (_a = result.data.node) == null ? void 0 : _a.subIssues) == null ? void 0 : _b.nodes) ?? [];
270
+ return subIssues.map((issue) => ({
271
+ id: String(issue.number),
272
+ title: issue.title,
273
+ url: issue.url,
274
+ state: issue.state.toLowerCase()
275
+ }));
276
+ } catch (error) {
277
+ if (error instanceof Error) {
278
+ const errorMessage = error.message;
279
+ const stderr = "stderr" in error ? error.stderr ?? "" : "";
280
+ const combinedError = `${errorMessage} ${stderr}`;
281
+ if (combinedError.includes("sub_issues") || combinedError.includes("null")) {
282
+ return [];
283
+ }
284
+ }
285
+ throw error;
286
+ }
287
+ }
288
+ async function getIssueDatabaseId(issueNumber, repo) {
289
+ logger.debug("Fetching GitHub issue database ID", { issueNumber, repo });
290
+ const apiPath = repo ? `repos/${repo}/issues/${issueNumber}` : `repos/:owner/:repo/issues/${issueNumber}`;
291
+ const result = await executeGhCommand([
292
+ "api",
293
+ apiPath,
294
+ "--jq",
295
+ "{id: .id}"
296
+ ]);
297
+ return result.id;
298
+ }
299
+ async function getIssueDependencies(issueNumber, direction, repo) {
300
+ logger.debug("Fetching GitHub issue dependencies", { issueNumber, direction, repo });
301
+ const apiPath = repo ? `repos/${repo}/issues/${issueNumber}/dependencies/${direction}` : `repos/:owner/:repo/issues/${issueNumber}/dependencies/${direction}`;
302
+ try {
303
+ const result = await executeGhCommand([
304
+ "api",
305
+ "-H",
306
+ "Accept: application/vnd.github+json",
307
+ "-H",
308
+ "X-GitHub-Api-Version: 2022-11-28",
309
+ "--jq",
310
+ ".",
311
+ apiPath
312
+ ]);
313
+ return (result ?? []).map((dep) => ({
314
+ id: String(dep.number),
315
+ databaseId: dep.id,
316
+ title: dep.title,
317
+ url: dep.html_url,
318
+ state: dep.state
319
+ }));
320
+ } catch (error) {
321
+ if (error instanceof Error) {
322
+ const errorMessage = error.message;
323
+ const stderr = "stderr" in error ? error.stderr ?? "" : "";
324
+ const combinedError = `${errorMessage} ${stderr}`;
325
+ if (combinedError.includes("404") && combinedError.includes("dependencies")) {
326
+ return [];
327
+ }
328
+ }
329
+ throw error;
330
+ }
331
+ }
332
+ async function createIssueDependency(blockedIssueNumber, blockingIssueDatabaseId, repo) {
333
+ logger.debug("Creating GitHub issue dependency", { blockedIssueNumber, blockingIssueDatabaseId, repo });
334
+ const apiPath = repo ? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by` : `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by`;
335
+ try {
336
+ await executeGhCommand([
337
+ "api",
338
+ "-X",
339
+ "POST",
340
+ "-H",
341
+ "Accept: application/vnd.github+json",
342
+ "-H",
343
+ "X-GitHub-Api-Version: 2022-11-28",
344
+ apiPath,
345
+ "-F",
346
+ `issue_id=${blockingIssueDatabaseId}`
347
+ ]);
348
+ } catch (error) {
349
+ if (error instanceof Error) {
350
+ const errorMessage = error.message;
351
+ const stderr = "stderr" in error ? error.stderr ?? "" : "";
352
+ const combinedError = `${errorMessage} ${stderr}`;
353
+ if (combinedError.includes("422") || combinedError.includes("already exists") || combinedError.includes("Unprocessable Entity")) {
354
+ throw new Error(`Dependency already exists: issue #${blockedIssueNumber} is already blocked by the specified issue`);
355
+ }
356
+ if (combinedError.includes("404") || combinedError.includes("Not Found")) {
357
+ throw new Error(`Issue not found: unable to create dependency for issue #${blockedIssueNumber}. The issue may not exist or you may not have access to it.`);
358
+ }
359
+ if (combinedError.includes("403") || combinedError.includes("Forbidden") || combinedError.includes("not enabled")) {
360
+ throw new Error(`Dependencies feature not enabled: the repository may not have issue dependencies enabled. This feature requires GitHub Enterprise or specific repository settings.`);
361
+ }
362
+ }
363
+ throw error;
364
+ }
365
+ }
366
+ async function removeIssueDependency(blockedIssueNumber, blockingIssueDatabaseId, repo) {
367
+ logger.debug("Removing GitHub issue dependency", { blockedIssueNumber, blockingIssueDatabaseId, repo });
368
+ const apiPath = repo ? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}` : `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`;
369
+ await executeGhCommand([
370
+ "api",
371
+ "-X",
372
+ "DELETE",
373
+ "-H",
374
+ "Accept: application/vnd.github+json",
375
+ "-H",
376
+ "X-GitHub-Api-Version: 2022-11-28",
377
+ apiPath
378
+ ]);
379
+ }
238
380
 
239
381
  // src/utils/image-processor.ts
240
382
  import { tmpdir } from "os";
@@ -727,10 +869,76 @@ var GitHubIssueManagementProvider = class {
727
869
  number: childNumber
728
870
  };
729
871
  }
872
+ /**
873
+ * Create a blocking dependency between two issues (A blocks B)
874
+ * Uses GitHub's sub-issues API: blocking issue becomes parent, blocked issue becomes sub-issue
875
+ */
876
+ async createDependency(input) {
877
+ const { blockingIssue, blockedIssue, repo } = input;
878
+ const blockingNumber = parseInt(blockingIssue, 10);
879
+ if (isNaN(blockingNumber)) {
880
+ throw new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`);
881
+ }
882
+ const blockedNumber = parseInt(blockedIssue, 10);
883
+ if (isNaN(blockedNumber)) {
884
+ throw new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`);
885
+ }
886
+ const blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo);
887
+ await createIssueDependency(blockedNumber, blockingDatabaseId, repo);
888
+ }
889
+ /**
890
+ * Get dependencies for an issue
891
+ */
892
+ async getDependencies(input) {
893
+ const { number, direction, repo } = input;
894
+ const issueNumber = parseInt(number, 10);
895
+ if (isNaN(issueNumber)) {
896
+ throw new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`);
897
+ }
898
+ const result = {
899
+ blocking: [],
900
+ blockedBy: []
901
+ };
902
+ if (direction === "blocking" || direction === "both") {
903
+ result.blocking = await getIssueDependencies(issueNumber, "blocking", repo);
904
+ }
905
+ if (direction === "blocked_by" || direction === "both") {
906
+ result.blockedBy = await getIssueDependencies(issueNumber, "blocked_by", repo);
907
+ }
908
+ return result;
909
+ }
910
+ /**
911
+ * Remove a blocking dependency between two issues (A blocks B)
912
+ * Uses GitHub's sub-issues API: blocking issue is parent, blocked issue is sub-issue
913
+ */
914
+ async removeDependency(input) {
915
+ const { blockingIssue, blockedIssue, repo } = input;
916
+ const blockingNumber = parseInt(blockingIssue, 10);
917
+ if (isNaN(blockingNumber)) {
918
+ throw new Error(`Invalid GitHub issue number: ${blockingIssue}. GitHub issue IDs must be numeric.`);
919
+ }
920
+ const blockedNumber = parseInt(blockedIssue, 10);
921
+ if (isNaN(blockedNumber)) {
922
+ throw new Error(`Invalid GitHub issue number: ${blockedIssue}. GitHub issue IDs must be numeric.`);
923
+ }
924
+ const blockingDatabaseId = await getIssueDatabaseId(blockingNumber, repo);
925
+ await removeIssueDependency(blockedNumber, blockingDatabaseId, repo);
926
+ }
927
+ /**
928
+ * Get child issues (sub-issues) of a parent issue
929
+ */
930
+ async getChildIssues(input) {
931
+ const { number, repo } = input;
932
+ const issueNumber = parseInt(number, 10);
933
+ if (isNaN(issueNumber)) {
934
+ throw new Error(`Invalid GitHub issue number: ${number}. GitHub issue IDs must be numeric.`);
935
+ }
936
+ return await getSubIssues(issueNumber, repo);
937
+ }
730
938
  };
731
939
 
732
940
  // src/utils/linear.ts
733
- import { LinearClient } from "@linear/sdk";
941
+ import { LinearClient, IssueRelationType } from "@linear/sdk";
734
942
 
735
943
  // src/types/linear.ts
736
944
  var LinearServiceError = class _LinearServiceError extends Error {
@@ -1000,6 +1208,157 @@ async function fetchLinearIssueComments(identifier) {
1000
1208
  handleLinearError(error, "fetchLinearIssueComments");
1001
1209
  }
1002
1210
  }
1211
+ async function getLinearChildIssues(identifier) {
1212
+ try {
1213
+ logger.debug(`Fetching child issues for Linear issue: ${identifier}`);
1214
+ const client = createLinearClient();
1215
+ const issue = await client.issue(identifier);
1216
+ if (!issue) {
1217
+ throw new LinearServiceError("NOT_FOUND", `Linear issue ${identifier} not found`);
1218
+ }
1219
+ const children = await issue.children({ first: 100 });
1220
+ const results = await Promise.all(
1221
+ children.nodes.map(async (child) => {
1222
+ const stateObj = await child.state;
1223
+ const state = (stateObj == null ? void 0 : stateObj.name) ?? "unknown";
1224
+ return {
1225
+ id: child.identifier,
1226
+ title: child.title,
1227
+ url: child.url,
1228
+ state
1229
+ };
1230
+ })
1231
+ );
1232
+ return results;
1233
+ } catch (error) {
1234
+ if (error instanceof LinearServiceError) {
1235
+ throw error;
1236
+ }
1237
+ handleLinearError(error, "getLinearChildIssues");
1238
+ }
1239
+ }
1240
+ async function createLinearIssueRelation(blockingIssueId, blockedIssueId) {
1241
+ try {
1242
+ logger.debug(`Creating Linear issue relation: ${blockingIssueId} blocks ${blockedIssueId}`);
1243
+ const client = createLinearClient();
1244
+ const payload = await client.createIssueRelation({
1245
+ issueId: blockingIssueId,
1246
+ relatedIssueId: blockedIssueId,
1247
+ type: IssueRelationType.Blocks
1248
+ });
1249
+ if (!payload.success) {
1250
+ throw new LinearServiceError("CLI_ERROR", "Failed to create Linear issue relation");
1251
+ }
1252
+ } catch (error) {
1253
+ if (error instanceof LinearServiceError) {
1254
+ throw error;
1255
+ }
1256
+ handleLinearError(error, "createLinearIssueRelation");
1257
+ }
1258
+ }
1259
+ async function getLinearIssueDependencies(identifier, direction) {
1260
+ try {
1261
+ logger.debug(`Fetching Linear issue dependencies: ${identifier} (direction: ${direction})`);
1262
+ const client = createLinearClient();
1263
+ const issue = await client.issue(identifier);
1264
+ if (!issue) {
1265
+ throw new LinearServiceError("NOT_FOUND", `Linear issue ${identifier} not found`);
1266
+ }
1267
+ const [relations, inverseRelations] = await Promise.all([
1268
+ issue.relations(),
1269
+ issue.inverseRelations()
1270
+ ]);
1271
+ const blocking = [];
1272
+ const blockedBy = [];
1273
+ const buildDependencyResult = async (relatedIssue) => {
1274
+ if (!relatedIssue) return null;
1275
+ const stateObj = await relatedIssue.state;
1276
+ const state = (stateObj == null ? void 0 : stateObj.name) ?? "unknown";
1277
+ return {
1278
+ id: relatedIssue.identifier,
1279
+ title: relatedIssue.title,
1280
+ url: relatedIssue.url,
1281
+ state
1282
+ };
1283
+ };
1284
+ if (direction === "blocking" || direction === "both") {
1285
+ const blockingRelations = relations.nodes.filter(
1286
+ (r) => r.type === IssueRelationType.Blocks
1287
+ );
1288
+ const relatedIssuePromises = blockingRelations.map((r) => r.relatedIssue).filter((p) => p !== void 0);
1289
+ const relatedIssues = await Promise.all(relatedIssuePromises);
1290
+ const blockingResults = await Promise.all(
1291
+ relatedIssues.map((issue2) => buildDependencyResult(issue2))
1292
+ );
1293
+ blocking.push(...blockingResults.filter((r) => r !== null));
1294
+ }
1295
+ if (direction === "blocked_by" || direction === "both") {
1296
+ const blockedByRelations = inverseRelations.nodes.filter(
1297
+ (r) => r.type === IssueRelationType.Blocks
1298
+ );
1299
+ const sourceIssuePromises = blockedByRelations.map((r) => r.issue).filter((p) => p !== void 0);
1300
+ const sourceIssues = await Promise.all(sourceIssuePromises);
1301
+ const blockedByResults = await Promise.all(
1302
+ sourceIssues.map((issue2) => buildDependencyResult(issue2))
1303
+ );
1304
+ blockedBy.push(...blockedByResults.filter((r) => r !== null));
1305
+ }
1306
+ return { blocking, blockedBy };
1307
+ } catch (error) {
1308
+ if (error instanceof LinearServiceError) {
1309
+ throw error;
1310
+ }
1311
+ handleLinearError(error, "getLinearIssueDependencies");
1312
+ }
1313
+ }
1314
+ async function deleteLinearIssueRelation(relationId) {
1315
+ try {
1316
+ logger.debug(`Deleting Linear issue relation: ${relationId}`);
1317
+ const client = createLinearClient();
1318
+ const payload = await client.deleteIssueRelation(relationId);
1319
+ if (!payload.success) {
1320
+ throw new LinearServiceError("CLI_ERROR", "Failed to delete Linear issue relation");
1321
+ }
1322
+ } catch (error) {
1323
+ if (error instanceof LinearServiceError) {
1324
+ throw error;
1325
+ }
1326
+ handleLinearError(error, "deleteLinearIssueRelation");
1327
+ }
1328
+ }
1329
+ async function findLinearIssueRelation(blockingIdentifier, blockedIdentifier) {
1330
+ try {
1331
+ logger.debug(`Finding Linear issue relation: ${blockingIdentifier} blocks ${blockedIdentifier}`);
1332
+ const client = createLinearClient();
1333
+ const blockingIssue = await client.issue(blockingIdentifier);
1334
+ if (!blockingIssue) {
1335
+ throw new LinearServiceError("NOT_FOUND", `Linear issue ${blockingIdentifier} not found`);
1336
+ }
1337
+ const blockedIssue = await client.issue(blockedIdentifier);
1338
+ if (!blockedIssue) {
1339
+ throw new LinearServiceError("NOT_FOUND", `Linear issue ${blockedIdentifier} not found`);
1340
+ }
1341
+ const relations = await blockingIssue.relations();
1342
+ const blockingRelations = relations.nodes.filter(
1343
+ (r) => r.type === IssueRelationType.Blocks
1344
+ );
1345
+ const relationsWithIssues = await Promise.all(
1346
+ blockingRelations.map(async (relation) => ({
1347
+ relation,
1348
+ relatedIssue: await relation.relatedIssue
1349
+ }))
1350
+ );
1351
+ const matchingRelation = relationsWithIssues.find(
1352
+ ({ relatedIssue }) => (relatedIssue == null ? void 0 : relatedIssue.id) === blockedIssue.id
1353
+ );
1354
+ return (matchingRelation == null ? void 0 : matchingRelation.relation.id) ?? null;
1355
+ } catch (error) {
1356
+ if (error instanceof LinearServiceError) {
1357
+ throw error;
1358
+ }
1359
+ handleLinearError(error, "findLinearIssueRelation");
1360
+ }
1361
+ }
1003
1362
 
1004
1363
  // src/utils/linear-markup-converter.ts
1005
1364
  import { appendFileSync } from "fs";
@@ -1315,6 +1674,42 @@ var LinearIssueManagementProvider = class {
1315
1674
  url: result.url
1316
1675
  };
1317
1676
  }
1677
+ /**
1678
+ * Create a blocking dependency between two issues
1679
+ */
1680
+ async createDependency(input) {
1681
+ const { blockingIssue, blockedIssue } = input;
1682
+ const [blockingIssueData, blockedIssueData] = await Promise.all([
1683
+ fetchLinearIssue(blockingIssue),
1684
+ fetchLinearIssue(blockedIssue)
1685
+ ]);
1686
+ await createLinearIssueRelation(blockingIssueData.id, blockedIssueData.id);
1687
+ }
1688
+ /**
1689
+ * Get dependencies for an issue
1690
+ */
1691
+ async getDependencies(input) {
1692
+ const { number, direction } = input;
1693
+ return await getLinearIssueDependencies(number, direction);
1694
+ }
1695
+ /**
1696
+ * Remove a blocking dependency between two issues
1697
+ */
1698
+ async removeDependency(input) {
1699
+ const { blockingIssue, blockedIssue } = input;
1700
+ const relationId = await findLinearIssueRelation(blockingIssue, blockedIssue);
1701
+ if (!relationId) {
1702
+ throw new Error(`No blocking dependency found from ${blockingIssue} to ${blockedIssue}`);
1703
+ }
1704
+ await deleteLinearIssueRelation(relationId);
1705
+ }
1706
+ /**
1707
+ * Get child issues of a parent issue
1708
+ */
1709
+ async getChildIssues(input) {
1710
+ const { number } = input;
1711
+ return await getLinearChildIssues(number);
1712
+ }
1318
1713
  };
1319
1714
 
1320
1715
  // src/mcp/IssueManagementProviderFactory.ts
@@ -1729,6 +2124,178 @@ server.registerTool(
1729
2124
  }
1730
2125
  }
1731
2126
  );
2127
+ var dependencyResultSchema = z.object({
2128
+ id: z.string().describe("Issue identifier"),
2129
+ title: z.string().describe("Issue title"),
2130
+ url: z.string().describe("Issue URL"),
2131
+ state: z.string().describe("Issue state")
2132
+ });
2133
+ server.registerTool(
2134
+ "create_dependency",
2135
+ {
2136
+ title: "Create Dependency",
2137
+ description: 'Create a blocking dependency between two issues. The blockingIssue will block the blockedIssue. For GitHub: uses the sub-issue API. For Linear: creates a "blocks" relation.',
2138
+ inputSchema: {
2139
+ blockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like "ENG-123")'),
2140
+ blockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like "ENG-123")'),
2141
+ repo: z.string().optional().describe(
2142
+ 'Optional repository in "owner/repo" format or full GitHub URL. When not provided, uses the current repository. GitHub only.'
2143
+ )
2144
+ },
2145
+ outputSchema: {
2146
+ success: z.boolean().describe("Whether the dependency was created successfully")
2147
+ }
2148
+ },
2149
+ async ({ blockingIssue, blockedIssue, repo }) => {
2150
+ console.error(`Creating dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ""}`);
2151
+ try {
2152
+ const provider = IssueManagementProviderFactory.create(
2153
+ process.env.ISSUE_PROVIDER
2154
+ );
2155
+ await provider.createDependency({ blockingIssue, blockedIssue, repo });
2156
+ console.error(`Dependency created successfully: ${blockingIssue} -> ${blockedIssue}`);
2157
+ return {
2158
+ content: [
2159
+ {
2160
+ type: "text",
2161
+ text: JSON.stringify({ success: true })
2162
+ }
2163
+ ],
2164
+ structuredContent: { success: true }
2165
+ };
2166
+ } catch (error) {
2167
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2168
+ console.error(`Failed to create dependency: ${errorMessage}`);
2169
+ throw new Error(`Failed to create dependency: ${errorMessage}`);
2170
+ }
2171
+ }
2172
+ );
2173
+ server.registerTool(
2174
+ "get_dependencies",
2175
+ {
2176
+ title: "Get Dependencies",
2177
+ description: "Get blocking/blocked_by dependencies for an issue. Returns lists of issues that this issue blocks and/or is blocked by.",
2178
+ inputSchema: {
2179
+ number: z.string().describe('Issue identifier (GitHub issue number or Linear identifier like "ENG-123")'),
2180
+ direction: z.enum(["blocking", "blocked_by", "both"]).describe('Which dependencies to fetch: "blocking" for issues this blocks, "blocked_by" for issues blocking this, "both" for all'),
2181
+ repo: z.string().optional().describe(
2182
+ 'Optional repository in "owner/repo" format or full GitHub URL. When not provided, uses the current repository. GitHub only.'
2183
+ )
2184
+ },
2185
+ outputSchema: {
2186
+ blocking: z.array(dependencyResultSchema).describe("Issues that this issue blocks"),
2187
+ blockedBy: z.array(dependencyResultSchema).describe("Issues that block this issue")
2188
+ }
2189
+ },
2190
+ async ({ number, direction, repo }) => {
2191
+ console.error(`Getting dependencies for ${number} (direction: ${direction})${repo ? ` in ${repo}` : ""}`);
2192
+ try {
2193
+ const provider = IssueManagementProviderFactory.create(
2194
+ process.env.ISSUE_PROVIDER
2195
+ );
2196
+ const result = await provider.getDependencies({ number, direction, repo });
2197
+ console.error(`Dependencies fetched: ${result.blocking.length} blocking, ${result.blockedBy.length} blocked_by`);
2198
+ return {
2199
+ content: [
2200
+ {
2201
+ type: "text",
2202
+ text: JSON.stringify(result)
2203
+ }
2204
+ ],
2205
+ structuredContent: result
2206
+ };
2207
+ } catch (error) {
2208
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2209
+ console.error(`Failed to get dependencies: ${errorMessage}`);
2210
+ throw new Error(`Failed to get dependencies: ${errorMessage}`);
2211
+ }
2212
+ }
2213
+ );
2214
+ server.registerTool(
2215
+ "remove_dependency",
2216
+ {
2217
+ title: "Remove Dependency",
2218
+ description: "Remove a blocking dependency between two issues. The blockingIssue will no longer block the blockedIssue.",
2219
+ inputSchema: {
2220
+ blockingIssue: z.string().describe('The issue that blocks (GitHub issue number or Linear identifier like "ENG-123")'),
2221
+ blockedIssue: z.string().describe('The issue being blocked (GitHub issue number or Linear identifier like "ENG-123")'),
2222
+ repo: z.string().optional().describe(
2223
+ 'Optional repository in "owner/repo" format or full GitHub URL. When not provided, uses the current repository. GitHub only.'
2224
+ )
2225
+ },
2226
+ outputSchema: {
2227
+ success: z.boolean().describe("Whether the dependency was removed successfully")
2228
+ }
2229
+ },
2230
+ async ({ blockingIssue, blockedIssue, repo }) => {
2231
+ console.error(`Removing dependency: ${blockingIssue} blocks ${blockedIssue}${repo ? ` in ${repo}` : ""}`);
2232
+ try {
2233
+ const provider = IssueManagementProviderFactory.create(
2234
+ process.env.ISSUE_PROVIDER
2235
+ );
2236
+ await provider.removeDependency({ blockingIssue, blockedIssue, repo });
2237
+ console.error(`Dependency removed successfully: ${blockingIssue} -> ${blockedIssue}`);
2238
+ return {
2239
+ content: [
2240
+ {
2241
+ type: "text",
2242
+ text: JSON.stringify({ success: true })
2243
+ }
2244
+ ],
2245
+ structuredContent: { success: true }
2246
+ };
2247
+ } catch (error) {
2248
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2249
+ console.error(`Failed to remove dependency: ${errorMessage}`);
2250
+ throw new Error(`Failed to remove dependency: ${errorMessage}`);
2251
+ }
2252
+ }
2253
+ );
2254
+ var childIssueResultSchema = z.object({
2255
+ id: z.string().describe("Issue identifier"),
2256
+ title: z.string().describe("Issue title"),
2257
+ url: z.string().describe("Issue URL"),
2258
+ state: z.string().describe("Issue state")
2259
+ });
2260
+ server.registerTool(
2261
+ "get_child_issues",
2262
+ {
2263
+ title: "Get Child Issues",
2264
+ description: "Get child issues (sub-issues) of a parent issue. Returns a list of issues that are children of the specified parent.",
2265
+ inputSchema: {
2266
+ number: z.string().describe('Parent issue identifier (GitHub issue number or Linear identifier like "ENG-123")'),
2267
+ repo: z.string().optional().describe(
2268
+ 'Optional repository in "owner/repo" format or full GitHub URL. When not provided, uses the current repository. GitHub only.'
2269
+ )
2270
+ },
2271
+ outputSchema: {
2272
+ children: z.array(childIssueResultSchema).describe("Child issues of the parent")
2273
+ }
2274
+ },
2275
+ async ({ number, repo }) => {
2276
+ console.error(`Getting child issues for ${number}${repo ? ` in ${repo}` : ""}`);
2277
+ try {
2278
+ const provider = IssueManagementProviderFactory.create(
2279
+ process.env.ISSUE_PROVIDER
2280
+ );
2281
+ const result = await provider.getChildIssues({ number, repo });
2282
+ console.error(`Child issues fetched: ${result.length} children`);
2283
+ return {
2284
+ content: [
2285
+ {
2286
+ type: "text",
2287
+ text: JSON.stringify({ children: result })
2288
+ }
2289
+ ],
2290
+ structuredContent: { children: result }
2291
+ };
2292
+ } catch (error) {
2293
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2294
+ console.error(`Failed to get child issues: ${errorMessage}`);
2295
+ throw new Error(`Failed to get child issues: ${errorMessage}`);
2296
+ }
2297
+ }
2298
+ );
1732
2299
  async function main() {
1733
2300
  console.error("Starting Issue Management MCP Server...");
1734
2301
  const provider = validateEnvironment();