@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.
- package/LICENSE +1 -1
- package/README.md +32 -3
- package/dist/{ClaudeContextManager-Y2YJC6BU.js → ClaudeContextManager-RDP6CLK6.js} +5 -5
- package/dist/{ClaudeService-NDVFQRKC.js → ClaudeService-FKPOQRA4.js} +4 -4
- package/dist/GitHubService-ACZVNTJE.js +12 -0
- package/dist/{LoomLauncher-U2B3VHPC.js → LoomLauncher-NHZMEVTQ.js} +5 -5
- package/dist/{MetadataManager-XJ2YB762.js → MetadataManager-W3C54UYT.js} +2 -2
- package/dist/{PRManager-7F3AAY66.js → PRManager-XLTVG6YG.js} +5 -5
- package/dist/{PromptTemplateManager-7L3HJQQU.js → PromptTemplateManager-OUYDHOPI.js} +2 -2
- package/dist/README.md +32 -3
- package/dist/{SettingsManager-YU4VYPTW.js → SettingsManager-VCVLL32H.js} +4 -2
- package/dist/{SettingsMigrationManager-KZKDG66H.js → SettingsMigrationManager-LEBMJP3B.js} +3 -3
- package/dist/agents/iloom-code-reviewer.md +720 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +1 -1
- package/dist/agents/iloom-issue-analyzer.md +1 -1
- package/dist/agents/iloom-issue-complexity-evaluator.md +1 -1
- package/dist/agents/iloom-issue-enhancer.md +1 -1
- package/dist/agents/iloom-issue-implementer.md +1 -1
- package/dist/agents/iloom-issue-planner.md +1 -1
- package/dist/{build-HQ5HGA3T.js → build-H4DK3DMQ.js} +7 -7
- package/dist/{chunk-N7FVXZNI.js → chunk-4BSXZ5YZ.js} +31 -9
- package/dist/chunk-4BSXZ5YZ.js.map +1 -0
- package/dist/{chunk-VYKKWU36.js → chunk-4KGRPHM6.js} +3 -3
- package/dist/{chunk-CFQVOTHO.js → chunk-52MVUK5V.js} +2 -2
- package/dist/{chunk-TIYJEEVO.js → chunk-66QOCD5N.js} +1 -1
- package/dist/chunk-66QOCD5N.js.map +1 -0
- package/dist/chunk-7JDMYTFZ.js +251 -0
- package/dist/chunk-7JDMYTFZ.js.map +1 -0
- package/dist/{chunk-7LSSNB7Y.js → chunk-7ZEHSSUP.js} +2 -2
- package/dist/chunk-A4UQY3M2.js +75 -0
- package/dist/chunk-A4UQY3M2.js.map +1 -0
- package/dist/{chunk-WT4UGBE2.js → chunk-AZH27CPV.js} +5 -5
- package/dist/{chunk-ELJKYFSH.js → chunk-BCQDYAOJ.js} +4 -4
- package/dist/{chunk-F2PWIRV4.js → chunk-BYUMEDDD.js} +2 -2
- package/dist/{chunk-CAXFWFV6.js → chunk-ECP77QGE.js} +4 -4
- package/dist/{chunk-ZA575VLF.js → chunk-GDS2HXSW.js} +4 -4
- package/dist/{chunk-UDRZY65Y.js → chunk-HSGZW3ID.js} +2 -2
- package/dist/{chunk-WFQ5CLTR.js → chunk-IWIIOFEB.js} +56 -5
- package/dist/chunk-IWIIOFEB.js.map +1 -0
- package/dist/{chunk-VWGKGNJP.js → chunk-KBEIQP4G.js} +3 -1
- package/dist/chunk-KBEIQP4G.js.map +1 -0
- package/dist/{chunk-NPEMVE27.js → chunk-L4CN7YQT.js} +115 -3
- package/dist/chunk-L4CN7YQT.js.map +1 -0
- package/dist/{chunk-HBJITKSZ.js → chunk-LFVRG6UU.js} +159 -3
- package/dist/chunk-LFVRG6UU.js.map +1 -0
- package/dist/{chunk-64HCHVJM.js → chunk-PLI3JQWT.js} +2 -2
- package/dist/{chunk-USJSNHGG.js → chunk-PVW6JE7E.js} +3 -3
- package/dist/{chunk-3K3WY3BN.js → chunk-QJX6ICWY.js} +4 -4
- package/dist/{chunk-C7YW5IMS.js → chunk-RODL2HVY.js} +17 -6
- package/dist/{chunk-C7YW5IMS.js.map → chunk-RODL2HVY.js.map} +1 -1
- package/dist/{chunk-NEPH2O4C.js → chunk-SSASIBDJ.js} +3 -3
- package/dist/{chunk-GCPAZSGV.js → chunk-THS5L54H.js} +150 -3
- package/dist/chunk-THS5L54H.js.map +1 -0
- package/dist/{chunk-5V74K5ZA.js → chunk-TVH67KEO.js} +25 -2
- package/dist/chunk-TVH67KEO.js.map +1 -0
- package/dist/{chunk-ENMTWE74.js → chunk-VZYSM7N7.js} +2 -2
- package/dist/{chunk-ETY2SBW5.js → chunk-WNXYC7J4.js} +17 -15
- package/dist/chunk-WNXYC7J4.js.map +1 -0
- package/dist/{chunk-WZYBHD7P.js → chunk-XHNACIHO.js} +2 -2
- package/dist/{chunk-XAMBIVXE.js → chunk-XJHQVOT6.js} +2 -2
- package/dist/{chunk-O36JLYNW.js → chunk-XU5A6BWA.js} +4 -7
- package/dist/chunk-XU5A6BWA.js.map +1 -0
- package/dist/{chunk-6YAMWLCP.js → chunk-YAVVDZVF.js} +3 -3
- package/dist/{cleanup-IO4KV2DL.js → cleanup-25PCP2EM.js} +16 -16
- package/dist/cli.js +107 -157
- package/dist/cli.js.map +1 -1
- package/dist/{commit-3ULFKXNB.js → commit-SS77KUNX.js} +10 -10
- package/dist/{compile-CT7IR7O2.js → compile-ZOAODFN2.js} +7 -7
- package/dist/{contribute-GXKOIA42.js → contribute-7USRBWRM.js} +6 -6
- package/dist/{dev-server-OAP3RZC6.js → dev-server-TYYJM3XA.js} +9 -9
- package/dist/{feedback-ZLAX3BVL.js → feedback-HZVLOTQJ.js} +9 -9
- package/dist/{git-ENLT2VNI.js → git-GUNOPP4Q.js} +4 -4
- package/dist/hooks/iloom-hook.js +75 -3
- package/dist/{ignite-HA2OJF6Z.js → ignite-CPXPZ4ZD.js} +85 -25
- package/dist/ignite-CPXPZ4ZD.js.map +1 -0
- package/dist/index.d.ts +85 -2
- package/dist/index.js +133 -73
- package/dist/index.js.map +1 -1
- package/dist/init-MZBIXQ7V.js +21 -0
- package/dist/{lint-HAVU4U34.js → lint-MDVUV3W2.js} +7 -7
- package/dist/mcp/issue-management-server.js +569 -2
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/{neon-helpers-3KBC4A3Y.js → neon-helpers-VVFFTLXE.js} +3 -3
- package/dist/{open-IN3LUZXX.js → open-2LPZ7XXW.js} +9 -9
- package/dist/plan-N3YDCOIV.js +371 -0
- package/dist/plan-N3YDCOIV.js.map +1 -0
- package/dist/{projects-CTRTTMSK.js → projects-325GEEGJ.js} +2 -2
- package/dist/{prompt-3SAZYRUN.js → prompt-ONNPSNKM.js} +2 -2
- package/dist/prompts/init-prompt.txt +57 -1
- package/dist/prompts/issue-prompt.txt +51 -3
- package/dist/prompts/plan-prompt.txt +435 -0
- package/dist/prompts/pr-prompt.txt +38 -0
- package/dist/prompts/regular-prompt.txt +53 -3
- package/dist/{rebase-RLEVFHWN.js → rebase-7YS3N274.js} +6 -6
- package/dist/{recap-ZKGHZCX6.js → recap-GSXFEOD6.js} +6 -6
- package/dist/{run-QEIS2EH2.js → run-XPGCMFFO.js} +9 -9
- package/dist/schema/settings.schema.json +57 -1
- package/dist/{shell-2NNSIU34.js → shell-2SPM3Z5O.js} +6 -6
- package/dist/{summary-MPOOQIOX.js → summary-5UWNLAI5.js} +11 -11
- package/dist/{test-75WAA6DU.js → test-N2725YRI.js} +7 -7
- package/dist/{test-git-E2BLXR6M.js → test-git-ZPSPA2TP.js} +4 -4
- package/dist/{test-prefix-A7JGGYAA.js → test-prefix-6DLB2BHE.js} +4 -4
- package/dist/{test-webserver-J6SMNLU2.js → test-webserver-XLJ2TZFP.js} +6 -6
- package/package.json +1 -1
- package/dist/GitHubService-O7U4UQ7N.js +0 -12
- package/dist/agents/iloom-issue-reviewer.md +0 -139
- package/dist/chunk-5V74K5ZA.js.map +0 -1
- package/dist/chunk-ETY2SBW5.js.map +0 -1
- package/dist/chunk-GCPAZSGV.js.map +0 -1
- package/dist/chunk-HBJITKSZ.js.map +0 -1
- package/dist/chunk-N7FVXZNI.js.map +0 -1
- package/dist/chunk-NPEMVE27.js.map +0 -1
- package/dist/chunk-O36JLYNW.js.map +0 -1
- package/dist/chunk-TIYJEEVO.js.map +0 -1
- package/dist/chunk-VWGKGNJP.js.map +0 -1
- package/dist/chunk-WFQ5CLTR.js.map +0 -1
- package/dist/chunk-ZX3GTM7O.js +0 -119
- package/dist/chunk-ZX3GTM7O.js.map +0 -1
- package/dist/ignite-HA2OJF6Z.js.map +0 -1
- package/dist/init-S6IEGRSX.js +0 -21
- /package/dist/{ClaudeContextManager-Y2YJC6BU.js.map → ClaudeContextManager-RDP6CLK6.js.map} +0 -0
- /package/dist/{ClaudeService-NDVFQRKC.js.map → ClaudeService-FKPOQRA4.js.map} +0 -0
- /package/dist/{GitHubService-O7U4UQ7N.js.map → GitHubService-ACZVNTJE.js.map} +0 -0
- /package/dist/{LoomLauncher-U2B3VHPC.js.map → LoomLauncher-NHZMEVTQ.js.map} +0 -0
- /package/dist/{MetadataManager-XJ2YB762.js.map → MetadataManager-W3C54UYT.js.map} +0 -0
- /package/dist/{PRManager-7F3AAY66.js.map → PRManager-XLTVG6YG.js.map} +0 -0
- /package/dist/{PromptTemplateManager-7L3HJQQU.js.map → PromptTemplateManager-OUYDHOPI.js.map} +0 -0
- /package/dist/{SettingsManager-YU4VYPTW.js.map → SettingsManager-VCVLL32H.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-KZKDG66H.js.map → SettingsMigrationManager-LEBMJP3B.js.map} +0 -0
- /package/dist/{build-HQ5HGA3T.js.map → build-H4DK3DMQ.js.map} +0 -0
- /package/dist/{chunk-VYKKWU36.js.map → chunk-4KGRPHM6.js.map} +0 -0
- /package/dist/{chunk-CFQVOTHO.js.map → chunk-52MVUK5V.js.map} +0 -0
- /package/dist/{chunk-7LSSNB7Y.js.map → chunk-7ZEHSSUP.js.map} +0 -0
- /package/dist/{chunk-WT4UGBE2.js.map → chunk-AZH27CPV.js.map} +0 -0
- /package/dist/{chunk-ELJKYFSH.js.map → chunk-BCQDYAOJ.js.map} +0 -0
- /package/dist/{chunk-F2PWIRV4.js.map → chunk-BYUMEDDD.js.map} +0 -0
- /package/dist/{chunk-CAXFWFV6.js.map → chunk-ECP77QGE.js.map} +0 -0
- /package/dist/{chunk-ZA575VLF.js.map → chunk-GDS2HXSW.js.map} +0 -0
- /package/dist/{chunk-UDRZY65Y.js.map → chunk-HSGZW3ID.js.map} +0 -0
- /package/dist/{chunk-64HCHVJM.js.map → chunk-PLI3JQWT.js.map} +0 -0
- /package/dist/{chunk-USJSNHGG.js.map → chunk-PVW6JE7E.js.map} +0 -0
- /package/dist/{chunk-3K3WY3BN.js.map → chunk-QJX6ICWY.js.map} +0 -0
- /package/dist/{chunk-NEPH2O4C.js.map → chunk-SSASIBDJ.js.map} +0 -0
- /package/dist/{chunk-ENMTWE74.js.map → chunk-VZYSM7N7.js.map} +0 -0
- /package/dist/{chunk-WZYBHD7P.js.map → chunk-XHNACIHO.js.map} +0 -0
- /package/dist/{chunk-XAMBIVXE.js.map → chunk-XJHQVOT6.js.map} +0 -0
- /package/dist/{chunk-6YAMWLCP.js.map → chunk-YAVVDZVF.js.map} +0 -0
- /package/dist/{cleanup-IO4KV2DL.js.map → cleanup-25PCP2EM.js.map} +0 -0
- /package/dist/{commit-3ULFKXNB.js.map → commit-SS77KUNX.js.map} +0 -0
- /package/dist/{compile-CT7IR7O2.js.map → compile-ZOAODFN2.js.map} +0 -0
- /package/dist/{contribute-GXKOIA42.js.map → contribute-7USRBWRM.js.map} +0 -0
- /package/dist/{dev-server-OAP3RZC6.js.map → dev-server-TYYJM3XA.js.map} +0 -0
- /package/dist/{feedback-ZLAX3BVL.js.map → feedback-HZVLOTQJ.js.map} +0 -0
- /package/dist/{git-ENLT2VNI.js.map → git-GUNOPP4Q.js.map} +0 -0
- /package/dist/{init-S6IEGRSX.js.map → init-MZBIXQ7V.js.map} +0 -0
- /package/dist/{lint-HAVU4U34.js.map → lint-MDVUV3W2.js.map} +0 -0
- /package/dist/{neon-helpers-3KBC4A3Y.js.map → neon-helpers-VVFFTLXE.js.map} +0 -0
- /package/dist/{open-IN3LUZXX.js.map → open-2LPZ7XXW.js.map} +0 -0
- /package/dist/{projects-CTRTTMSK.js.map → projects-325GEEGJ.js.map} +0 -0
- /package/dist/{prompt-3SAZYRUN.js.map → prompt-ONNPSNKM.js.map} +0 -0
- /package/dist/{rebase-RLEVFHWN.js.map → rebase-7YS3N274.js.map} +0 -0
- /package/dist/{recap-ZKGHZCX6.js.map → recap-GSXFEOD6.js.map} +0 -0
- /package/dist/{run-QEIS2EH2.js.map → run-XPGCMFFO.js.map} +0 -0
- /package/dist/{shell-2NNSIU34.js.map → shell-2SPM3Z5O.js.map} +0 -0
- /package/dist/{summary-MPOOQIOX.js.map → summary-5UWNLAI5.js.map} +0 -0
- /package/dist/{test-75WAA6DU.js.map → test-N2725YRI.js.map} +0 -0
- /package/dist/{test-git-E2BLXR6M.js.map → test-git-ZPSPA2TP.js.map} +0 -0
- /package/dist/{test-prefix-A7JGGYAA.js.map → test-prefix-6DLB2BHE.js.map} +0 -0
- /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-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-BCQDYAOJ.js";
|
|
5
|
+
import "./chunk-TVH67KEO.js";
|
|
6
6
|
import "./chunk-RD7I2Q2F.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-HSGZW3ID.js";
|
|
8
8
|
import "./chunk-XPKN3QWY.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
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-
|
|
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();
|