@arii/boomtick-mcp 0.6.0 → 0.6.2
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/.mcp/schemas/github.create_issue.json +72 -3
- package/.mcp/schemas/github.get_merge_conflict_files.json +40 -3
- package/.mcp/schemas/github.get_pr_diff.json +30 -3
- package/.mcp/schemas/github.issue_update.json +72 -3
- package/.mcp/schemas/github.search_open_prs.json +130 -3
- package/.mcp/schemas/github_create_issue.json +72 -3
- package/.mcp/schemas/github_get_merge_conflict_files.json +40 -3
- package/.mcp/schemas/github_get_pr_diff.json +30 -3
- package/.mcp/schemas/github_issue_update.json +72 -3
- package/.mcp/schemas/github_search_open_prs.json +130 -3
- package/.mcp/schemas/repo.check_jules_status.json +38 -0
- package/.mcp/schemas/repo.run_tests.json +31 -3
- package/.mcp/schemas/repo_check_jules_status.json +38 -0
- package/.mcp/schemas/repo_run_tests.json +31 -3
- package/CHANGELOG.md +15 -0
- package/README.md +22 -1
- package/actions/run-project-gate/action.yml +1 -1
- package/dist/config/dynamic.js +33 -0
- package/dist/config/github.js +16 -0
- package/dist/config/repo.js +28 -0
- package/dist/config.js +16 -81
- package/dist/http.js +10 -0
- package/dist/mcp/definitions.js +29 -7
- package/dist/mcp/http.js +18 -10
- package/dist/mcp/http.test.js +54 -4
- package/dist/mcp/server.js +47 -10
- package/dist/mcp/server.test.js +52 -1
- package/dist/tools/contract.js +77 -0
- package/dist/tools/github.comment_triage_summary.js +3 -10
- package/dist/tools/github.get_merge_conflict_files.js +4 -2
- package/dist/tools/github.get_pr_commits.js +1 -1
- package/dist/tools/github.get_pr_diff.js +3 -11
- package/dist/tools/github.get_pr_reviews.js +1 -1
- package/dist/tools/jules/cancel-session.js +2 -9
- package/dist/tools/jules/cli.js +31 -0
- package/dist/tools/jules/get-messages.js +2 -9
- package/dist/tools/jules/list-sessions.js +4 -35
- package/dist/tools/jules/send-message.js +2 -9
- package/dist/tools/jules/session.js +26 -0
- package/dist/tools/jules/shared.js +2 -50
- package/dist/tools/jules/trigger-feedback.js +2 -10
- package/dist/tools/repo.check_jules_status.js +148 -0
- package/dist/tools/repo.check_jules_status.test.js +47 -0
- package/dist/tools/repo.get_workflow_runs.js +1 -1
- package/dist/tools/repo.run_tests.js +13 -1
- package/package.json +3 -2
- package/scripts/sync-contracts.ts +11 -2
- package/src/config/dynamic.ts +36 -0
- package/src/config/github.ts +16 -0
- package/src/config/repo.ts +36 -0
- package/src/config.ts +16 -89
- package/src/http.ts +13 -0
- package/src/mcp/definitions.ts +34 -6
- package/src/mcp/http.test.ts +65 -5
- package/src/mcp/http.ts +20 -14
- package/src/mcp/server.test.ts +62 -1
- package/src/mcp/server.ts +48 -11
- package/src/tools/contract.ts +85 -0
- package/src/tools/github.comment_triage_summary.ts +3 -12
- package/src/tools/github.get_merge_conflict_files.ts +5 -2
- package/src/tools/github.get_pr_commits.ts +1 -1
- package/src/tools/github.get_pr_diff.ts +3 -13
- package/src/tools/github.get_pr_reviews.ts +1 -1
- package/src/tools/jules/cancel-session.ts +2 -11
- package/src/tools/jules/cli.ts +42 -0
- package/src/tools/jules/get-messages.ts +2 -11
- package/src/tools/jules/list-sessions.ts +5 -39
- package/src/tools/jules/send-message.ts +2 -11
- package/src/tools/jules/session.ts +28 -0
- package/src/tools/jules/shared.ts +2 -63
- package/src/tools/jules/trigger-feedback.ts +2 -13
- package/src/tools/repo.check_jules_status.test.ts +55 -0
- package/src/tools/repo.check_jules_status.ts +176 -0
- package/src/tools/repo.get_workflow_runs.ts +1 -1
- package/src/tools/repo.run_tests.ts +14 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as crypto from "crypto";
|
|
5
|
+
import { config } from "../config.js";
|
|
6
|
+
|
|
7
|
+
export interface JulesStatusResult {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
status: "COMPLETED" | "FAILED" | "IN_PROGRESS" | "TIMED_OUT" | "NOT_FOUND";
|
|
10
|
+
rawState?: string;
|
|
11
|
+
completed: boolean;
|
|
12
|
+
remediationBranch?: string;
|
|
13
|
+
pullRequestUrl?: string;
|
|
14
|
+
idempotencyHash?: string;
|
|
15
|
+
isDuplicate?: boolean;
|
|
16
|
+
message?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const CheckJulesStatusInputSchema = z.object({
|
|
20
|
+
sessionId: z.string().optional().describe("The unique ID or task ID of the Jules session."),
|
|
21
|
+
commitSha: z.string().optional().describe("Commit SHA for idempotency key verification."),
|
|
22
|
+
failureLog: z.string().optional().describe("Failure log content for idempotency key calculation."),
|
|
23
|
+
poll: z.boolean().optional().describe("Whether to poll until session completion or timeout."),
|
|
24
|
+
timeoutMs: z.number().int().optional().describe("Timeout in milliseconds when polling."),
|
|
25
|
+
intervalMs: z.number().int().optional().describe("Polling interval in milliseconds."),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function calculateHash(commitSha: string, failureLog: string): string {
|
|
29
|
+
const hash = crypto.createHash("sha256");
|
|
30
|
+
return hash.update(`${commitSha}:${failureLog.trim()}`).digest("hex");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function checkJulesStatusHandler(input: z.infer<typeof CheckJulesStatusInputSchema>): Promise<JulesStatusResult> {
|
|
34
|
+
let resolvedTaskId = input.sessionId || process.env.TASK_ID;
|
|
35
|
+
|
|
36
|
+
if (!resolvedTaskId) {
|
|
37
|
+
return {
|
|
38
|
+
sessionId: "",
|
|
39
|
+
status: "NOT_FOUND",
|
|
40
|
+
completed: false,
|
|
41
|
+
message: "No session ID provided.",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sessionId = resolvedTaskId.startsWith("sessions/") ? resolvedTaskId : `sessions/${resolvedTaskId}`;
|
|
46
|
+
const apiKey = process.env.JULES_API_KEY;
|
|
47
|
+
|
|
48
|
+
if (!apiKey) {
|
|
49
|
+
return {
|
|
50
|
+
sessionId,
|
|
51
|
+
status: "NOT_FOUND",
|
|
52
|
+
completed: false,
|
|
53
|
+
message: "JULES_API_KEY environment variable not set.",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const commitSha = input.commitSha || process.env.GITHUB_SHA || process.env.COMMIT_SHA;
|
|
58
|
+
let idempotencyHash: string | undefined;
|
|
59
|
+
let isDuplicate = false;
|
|
60
|
+
|
|
61
|
+
if (commitSha && input.failureLog) {
|
|
62
|
+
idempotencyHash = calculateHash(commitSha, input.failureLog);
|
|
63
|
+
const artifactsDir = path.join(config.repoPath || process.cwd(), "artifacts");
|
|
64
|
+
const hashTrackingFile = path.join(artifactsDir, `.jules-dispatch-${sessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}.hash`);
|
|
65
|
+
if (fs.existsSync(hashTrackingFile)) {
|
|
66
|
+
try {
|
|
67
|
+
const existingHash = fs.readFileSync(hashTrackingFile, "utf8").trim();
|
|
68
|
+
if (existingHash === idempotencyHash) {
|
|
69
|
+
isDuplicate = true;
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.warn("⚠️ Could not read previous Jules dispatch hash file during status check:", e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const url = `https://jules.googleapis.com/v1alpha/${sessionId}`;
|
|
78
|
+
const poll = input.poll ?? false;
|
|
79
|
+
const timeoutMs = input.timeoutMs ?? 60000;
|
|
80
|
+
const intervalMs = input.intervalMs ?? 5000;
|
|
81
|
+
const startTime = Date.now();
|
|
82
|
+
|
|
83
|
+
const fetchSession = async () => {
|
|
84
|
+
const response = await fetch(url, {
|
|
85
|
+
method: "GET",
|
|
86
|
+
headers: {
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
"x-goog-api-key": apiKey,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return await response.json();
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
while (true) {
|
|
98
|
+
let sessionData: any = null;
|
|
99
|
+
try {
|
|
100
|
+
sessionData = await fetchSession();
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.warn(`⚠️ Error fetching Jules session status for ${sessionId}:`, e);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!sessionData) {
|
|
106
|
+
return {
|
|
107
|
+
sessionId,
|
|
108
|
+
status: "NOT_FOUND",
|
|
109
|
+
completed: false,
|
|
110
|
+
idempotencyHash,
|
|
111
|
+
isDuplicate,
|
|
112
|
+
message: `Failed to retrieve session data for ${sessionId}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const rawState = sessionData.state || "UNKNOWN";
|
|
117
|
+
let status: JulesStatusResult["status"] = "IN_PROGRESS";
|
|
118
|
+
if (rawState === "SUCCEEDED" || rawState === "COMPLETED") {
|
|
119
|
+
status = "COMPLETED";
|
|
120
|
+
} else if (rawState === "FAILED" || rawState === "CANCELLED" || rawState === "TERMINATED") {
|
|
121
|
+
status = "FAILED";
|
|
122
|
+
} else if (rawState === "IN_PROGRESS" || rawState === "PENDING") {
|
|
123
|
+
status = "IN_PROGRESS";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let remediationBranch: string | undefined;
|
|
127
|
+
let pullRequestUrl: string | undefined;
|
|
128
|
+
|
|
129
|
+
if (sessionData.sourceContext?.githubRepoContext?.startingBranch) {
|
|
130
|
+
remediationBranch = sessionData.sourceContext.githubRepoContext.startingBranch;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (Array.isArray(sessionData.outputs)) {
|
|
134
|
+
for (const output of sessionData.outputs) {
|
|
135
|
+
if (output?.pullRequest?.url) {
|
|
136
|
+
pullRequestUrl = output.pullRequest.url;
|
|
137
|
+
}
|
|
138
|
+
if (output?.pullRequest?.branch || output?.gitBranch) {
|
|
139
|
+
remediationBranch = output?.pullRequest?.branch || output?.gitBranch;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const completed = status === "COMPLETED";
|
|
145
|
+
|
|
146
|
+
if (!poll || status === "COMPLETED" || status === "FAILED") {
|
|
147
|
+
return {
|
|
148
|
+
sessionId,
|
|
149
|
+
status,
|
|
150
|
+
rawState,
|
|
151
|
+
completed,
|
|
152
|
+
remediationBranch,
|
|
153
|
+
pullRequestUrl,
|
|
154
|
+
idempotencyHash,
|
|
155
|
+
isDuplicate,
|
|
156
|
+
message: `Session state: ${rawState}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
161
|
+
return {
|
|
162
|
+
sessionId,
|
|
163
|
+
status: "TIMED_OUT",
|
|
164
|
+
rawState,
|
|
165
|
+
completed: false,
|
|
166
|
+
remediationBranch,
|
|
167
|
+
pullRequestUrl,
|
|
168
|
+
idempotencyHash,
|
|
169
|
+
isDuplicate,
|
|
170
|
+
message: `Polling timed out after ${timeoutMs}ms (current state: ${rawState})`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -3,7 +3,7 @@ import { runCommand } from "../lib/shell.js";
|
|
|
3
3
|
import { resolveRepo } from "../lib/git.js";
|
|
4
4
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
5
|
|
|
6
|
-
const GetWorkflowRunsInputSchema = z.object({
|
|
6
|
+
export const GetWorkflowRunsInputSchema = z.object({
|
|
7
7
|
branch: z.string().optional().describe("Filter workflow runs by branch name."),
|
|
8
8
|
event: z.string().optional().describe("Filter workflow runs by event (e.g. push, pull_request)."),
|
|
9
9
|
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
@@ -66,5 +66,18 @@ export async function runTestsHandler(args: RunTestsInput) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
const exitCode = results.length > 0 ? results[results.length - 1].exitCode : 0;
|
|
70
|
+
const failedTests: string[] = [];
|
|
71
|
+
for (const res of results) {
|
|
72
|
+
if (res.exitCode !== 0) {
|
|
73
|
+
failedTests.push(res.command);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
passed: success,
|
|
79
|
+
exitCode,
|
|
80
|
+
failedTests,
|
|
81
|
+
results,
|
|
82
|
+
};
|
|
70
83
|
}
|