@arii/boomtick-mcp 0.3.3
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/.env.example +6 -0
- package/.mcp/schemas/agent.search_ddgs.json +1 -0
- package/.mcp/schemas/boomtick.health.json +1 -0
- package/.mcp/schemas/github.checkout_branch.json +1 -0
- package/.mcp/schemas/github.comment_triage_summary.json +1 -0
- package/.mcp/schemas/github.create_issue.json +1 -0
- package/.mcp/schemas/github.create_pull_request.json +1 -0
- package/.mcp/schemas/github.get_merge_conflict_files.json +1 -0
- package/.mcp/schemas/github.get_pr.json +1 -0
- package/.mcp/schemas/github.get_pr_diff.json +1 -0
- package/.mcp/schemas/github.issue_comment.json +1 -0
- package/.mcp/schemas/github.issue_update.json +1 -0
- package/.mcp/schemas/github.issue_view.json +1 -0
- package/.mcp/schemas/github.open_replacement_pr.json +1 -0
- package/.mcp/schemas/github.search_open_prs.json +1 -0
- package/.mcp/schemas/jules.cancel_session.json +1 -0
- package/.mcp/schemas/jules.create_session.json +1 -0
- package/.mcp/schemas/jules.get_messages.json +1 -0
- package/.mcp/schemas/jules.get_pr.json +1 -0
- package/.mcp/schemas/jules.get_session.json +1 -0
- package/.mcp/schemas/jules.list_sessions.json +1 -0
- package/.mcp/schemas/jules.send_message.json +1 -0
- package/.mcp/schemas/jules.trigger_feedback.json +1 -0
- package/.mcp/schemas/repo.commit_patch.json +1 -0
- package/.mcp/schemas/repo.create_branch.json +1 -0
- package/.mcp/schemas/repo.create_repair_branch.json +1 -0
- package/.mcp/schemas/repo.get_changed_files.json +1 -0
- package/.mcp/schemas/repo.get_command_schema.json +1 -0
- package/.mcp/schemas/repo.get_package_scripts.json +1 -0
- package/.mcp/schemas/repo.get_route_map.json +1 -0
- package/.mcp/schemas/repo.logs.json +1 -0
- package/.mcp/schemas/repo.read_agent_context.json +1 -0
- package/.mcp/schemas/repo.read_ci_logs.json +1 -0
- package/.mcp/schemas/repo.run_lighthouse.json +1 -0
- package/.mcp/schemas/repo.run_playwright.json +1 -0
- package/.mcp/schemas/repo.run_tests.json +1 -0
- package/CHANGELOG.md +50 -0
- package/README.md +108 -0
- package/actions/ai-review/action.yml +15 -0
- package/actions/audit/action.yml +37 -0
- package/actions/ci-validate/action.yml +9 -0
- package/actions/impact-analysis/action.yml +385 -0
- package/actions/lint-typecheck/action.yml +19 -0
- package/actions/run-project-gate/action.yml +163 -0
- package/actions/setup/action.yml +15 -0
- package/actions/setup-workspace/action.yml +179 -0
- package/actions/test-build/action.yml +71 -0
- package/actions/update-pr-comment/action.yml +101 -0
- package/dist/config.js +110 -0
- package/dist/evals/run-evals.js +38 -0
- package/dist/index.js +10 -0
- package/dist/lib/error_utils.js +8 -0
- package/dist/lib/git.js +33 -0
- package/dist/lib/result.js +21 -0
- package/dist/lib/shell.js +91 -0
- package/dist/lib/shell.test.js +10 -0
- package/dist/lib/td-cli.js +27 -0
- package/dist/lib/test-utils.js +20 -0
- package/dist/mcp/definitions.js +242 -0
- package/dist/mcp/server.js +317 -0
- package/dist/mcp/tools.js +18 -0
- package/dist/tools/contract.js +2069 -0
- package/dist/tools/ddgs.search.js +50 -0
- package/dist/tools/ddgs.search.test.js +67 -0
- package/dist/tools/ddgs_search.py +23 -0
- package/dist/tools/github.checkout_branch.js +20 -0
- package/dist/tools/github.comment_triage_summary.js +25 -0
- package/dist/tools/github.create_issue.js +28 -0
- package/dist/tools/github.create_issue.test.js +54 -0
- package/dist/tools/github.create_pull_request.js +44 -0
- package/dist/tools/github.get_merge_conflict_files.js +26 -0
- package/dist/tools/github.get_pr.js +18 -0
- package/dist/tools/github.get_pr_diff.js +24 -0
- package/dist/tools/github.issue_comment.js +24 -0
- package/dist/tools/github.issue_update.js +29 -0
- package/dist/tools/github.issue_view.js +28 -0
- package/dist/tools/github.open_replacement_pr.js +41 -0
- package/dist/tools/github.search_open_prs.js +31 -0
- package/dist/tools/github.search_open_prs.test.js +51 -0
- package/dist/tools/jules/cancel-session.js +20 -0
- package/dist/tools/jules/create-session.js +20 -0
- package/dist/tools/jules/create-session.test.js +99 -0
- package/dist/tools/jules/get-messages.js +19 -0
- package/dist/tools/jules/get-messages.test.js +45 -0
- package/dist/tools/jules/get-pr.js +14 -0
- package/dist/tools/jules/get-pr.test.js +37 -0
- package/dist/tools/jules/get-session.js +10 -0
- package/dist/tools/jules/list-sessions.js +43 -0
- package/dist/tools/jules/send-message.js +22 -0
- package/dist/tools/jules/send-message.test.js +56 -0
- package/dist/tools/jules/shared.js +26 -0
- package/dist/tools/jules/trigger-feedback.js +16 -0
- package/dist/tools/jules/trigger-feedback.test.js +42 -0
- package/dist/tools/repo.commit_patch.js +33 -0
- package/dist/tools/repo.create_branch.js +21 -0
- package/dist/tools/repo.create_branch.test.js +42 -0
- package/dist/tools/repo.create_repair_branch.js +38 -0
- package/dist/tools/repo.get_changed_files.js +21 -0
- package/dist/tools/repo.get_command_schema.js +27 -0
- package/dist/tools/repo.get_package_scripts.js +35 -0
- package/dist/tools/repo.get_package_scripts.test.js +39 -0
- package/dist/tools/repo.get_route_map.js +37 -0
- package/dist/tools/repo.logs.js +16 -0
- package/dist/tools/repo.read_agent_context.js +20 -0
- package/dist/tools/repo.read_ci_logs.js +18 -0
- package/dist/tools/repo.run_lighthouse.js +37 -0
- package/dist/tools/repo.run_playwright.js +29 -0
- package/dist/tools/repo.run_tests.js +43 -0
- package/dist/tools/types.js +1 -0
- package/docs/testing.md +73 -0
- package/package.json +45 -0
- package/scripts/create_instructions.sh +42 -0
- package/scripts/export-mcp-schema.ts +7 -0
- package/scripts/sync-contracts.ts +74 -0
- package/scripts/sync-mcp-schemas.ts +97 -0
- package/src/agents/conflict-scout.prompt.md +20 -0
- package/src/agents/pr-consolidation.prompt.md +32 -0
- package/src/agents/pr-writer.prompt.md +13 -0
- package/src/agents/repair-agent.prompt.md +18 -0
- package/src/agents/repo-context.prompt.md +20 -0
- package/src/agents/verifier-agent.prompt.md +22 -0
- package/src/config.ts +113 -0
- package/src/evals/fixtures/merge-conflict-simple/README.md +0 -0
- package/src/evals/run-evals.ts +40 -0
- package/src/index.ts +12 -0
- package/src/lib/error_utils.ts +8 -0
- package/src/lib/git.ts +41 -0
- package/src/lib/result.ts +28 -0
- package/src/lib/shell.test.ts +11 -0
- package/src/lib/shell.ts +118 -0
- package/src/lib/td-cli.ts +38 -0
- package/src/lib/test-utils.ts +23 -0
- package/src/mcp/definitions.ts +278 -0
- package/src/mcp/server.ts +359 -0
- package/src/mcp/tools.ts +20 -0
- package/src/tools/contract.ts +2245 -0
- package/src/tools/ddgs.search.test.ts +92 -0
- package/src/tools/ddgs.search.ts +58 -0
- package/src/tools/ddgs_search.py +23 -0
- package/src/tools/github.checkout_branch.ts +25 -0
- package/src/tools/github.comment_triage_summary.ts +30 -0
- package/src/tools/github.create_issue.test.ts +63 -0
- package/src/tools/github.create_issue.ts +34 -0
- package/src/tools/github.create_pull_request.ts +50 -0
- package/src/tools/github.get_merge_conflict_files.ts +31 -0
- package/src/tools/github.get_pr.ts +24 -0
- package/src/tools/github.get_pr_diff.ts +29 -0
- package/src/tools/github.issue_comment.ts +31 -0
- package/src/tools/github.issue_update.ts +31 -0
- package/src/tools/github.issue_view.ts +35 -0
- package/src/tools/github.open_replacement_pr.ts +48 -0
- package/src/tools/github.search_open_prs.test.ts +60 -0
- package/src/tools/github.search_open_prs.ts +40 -0
- package/src/tools/jules/cancel-session.ts +26 -0
- package/src/tools/jules/create-session.test.ts +114 -0
- package/src/tools/jules/create-session.ts +24 -0
- package/src/tools/jules/get-messages.test.ts +52 -0
- package/src/tools/jules/get-messages.ts +24 -0
- package/src/tools/jules/get-pr.test.ts +43 -0
- package/src/tools/jules/get-pr.ts +17 -0
- package/src/tools/jules/get-session.ts +12 -0
- package/src/tools/jules/list-sessions.ts +50 -0
- package/src/tools/jules/send-message.test.ts +66 -0
- package/src/tools/jules/send-message.ts +27 -0
- package/src/tools/jules/shared.ts +28 -0
- package/src/tools/jules/trigger-feedback.test.ts +50 -0
- package/src/tools/jules/trigger-feedback.ts +21 -0
- package/src/tools/repo.commit_patch.ts +41 -0
- package/src/tools/repo.create_branch.test.ts +55 -0
- package/src/tools/repo.create_branch.ts +29 -0
- package/src/tools/repo.create_repair_branch.ts +47 -0
- package/src/tools/repo.get_changed_files.ts +26 -0
- package/src/tools/repo.get_command_schema.ts +30 -0
- package/src/tools/repo.get_package_scripts.test.ts +51 -0
- package/src/tools/repo.get_package_scripts.ts +36 -0
- package/src/tools/repo.get_route_map.ts +41 -0
- package/src/tools/repo.logs.ts +21 -0
- package/src/tools/repo.read_agent_context.ts +22 -0
- package/src/tools/repo.read_ci_logs.ts +21 -0
- package/src/tools/repo.run_lighthouse.ts +40 -0
- package/src/tools/repo.run_playwright.ts +35 -0
- package/src/tools/repo.run_tests.ts +47 -0
- package/src/tools/types.ts +8 -0
- package/start_browsermcp.sh +14 -0
- package/start_github_mcp.sh +6 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
|
|
4
|
+
export const SearchOpenPrsInputSchema = z.object({
|
|
5
|
+
state: z.enum(["open", "closed", "all"]).optional().default("open"),
|
|
6
|
+
includeDrafts: z.boolean().optional().default(true),
|
|
7
|
+
limit: z.number().min(1).max(100).optional().default(100),
|
|
8
|
+
labels: z.array(z.string()).optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export async function searchOpenPrsHandler(args: z.infer<typeof SearchOpenPrsInputSchema>) {
|
|
12
|
+
const params = SearchOpenPrsInputSchema.parse(args);
|
|
13
|
+
|
|
14
|
+
const tdArgs = [
|
|
15
|
+
"gh", "search-prs",
|
|
16
|
+
"--state", params.state,
|
|
17
|
+
"--limit", params.limit.toString(),
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
if (!params.includeDrafts) {
|
|
21
|
+
tdArgs.push("--no-include-drafts");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (params.labels && params.labels.length > 0) {
|
|
25
|
+
tdArgs.push("--labels", params.labels.join(","));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = await runCommand("td-cli", tdArgs);
|
|
29
|
+
|
|
30
|
+
if (result.exitCode !== 0) {
|
|
31
|
+
throw new Error(`Failed to list PRs: ${result.stderr}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const output = JSON.parse(result.stdout);
|
|
35
|
+
if (output.status === "error") {
|
|
36
|
+
throw new Error(`Failed to list PRs: ${output.message}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { prs: output.prs };
|
|
40
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { JulesSession } from "../types.js";
|
|
3
|
+
import { runCommand } from "../../lib/shell.js";
|
|
4
|
+
|
|
5
|
+
export const CancelJulesSessionInputSchema = z.object({
|
|
6
|
+
sessionId: z.string(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export async function cancelJulesSessionHandler(input: z.infer<typeof CancelJulesSessionInputSchema>): Promise<JulesSession> {
|
|
10
|
+
const result = await runCommand("td-cli", ["agent", "cancel", input.sessionId]);
|
|
11
|
+
|
|
12
|
+
if (result.exitCode !== 0) {
|
|
13
|
+
throw new Error(`Failed to cancel session: ${result.stderr}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const output = JSON.parse(result.stdout);
|
|
17
|
+
if (output.status === "error") {
|
|
18
|
+
throw new Error(`Failed to cancel session: ${output.message}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
id: input.sessionId.replace("sessions/", ""),
|
|
23
|
+
status: "FAILED",
|
|
24
|
+
createdAt: new Date(),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { createJulesSessionHandler } from "./create-session.js";
|
|
3
|
+
import * as shell from "../../lib/shell.js";
|
|
4
|
+
import { setupTestEnv, teardownTestEnv } from "../../lib/test-utils.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("createJulesSessionHandler", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setupTestEnv();
|
|
13
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
14
|
+
stdout: JSON.stringify({
|
|
15
|
+
status: "success",
|
|
16
|
+
name: "sessions/1234567890",
|
|
17
|
+
state: "IN_PROGRESS",
|
|
18
|
+
createTime: new Date().toISOString(),
|
|
19
|
+
}),
|
|
20
|
+
stderr: "",
|
|
21
|
+
exitCode: 0,
|
|
22
|
+
durationMs: 10,
|
|
23
|
+
command: "td-cli agent dispatch"
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
teardownTestEnv();
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should create a session with just task", async () => {
|
|
33
|
+
const result = await createJulesSessionHandler({ task: "do work" });
|
|
34
|
+
expect(result.id).toBe("1234567890");
|
|
35
|
+
expect(result.status).toBe("IN_PROGRESS");
|
|
36
|
+
expect(result.createdAt).toBeInstanceOf(Date);
|
|
37
|
+
|
|
38
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
39
|
+
"agent", "dispatch", "main", "do work"
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should create a session with specific branch", async () => {
|
|
44
|
+
const result = await createJulesSessionHandler({ task: "do work", branch: "feature-x" });
|
|
45
|
+
expect(result.id).toBe("1234567890");
|
|
46
|
+
|
|
47
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
48
|
+
"agent", "dispatch", "feature-x", "do work"
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should create a session with PR number", async () => {
|
|
53
|
+
vi.mocked(shell.runCommand).mockImplementation(async (cmd, args) => {
|
|
54
|
+
if (args[1] === "view") {
|
|
55
|
+
return {
|
|
56
|
+
stdout: JSON.stringify({ status: "success", pr: { headRefName: "pr-branch" } }),
|
|
57
|
+
stderr: "",
|
|
58
|
+
exitCode: 0,
|
|
59
|
+
durationMs: 10,
|
|
60
|
+
command: "td-cli gh view"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
stdout: JSON.stringify({
|
|
65
|
+
status: "success",
|
|
66
|
+
name: "sessions/1234567890",
|
|
67
|
+
state: "IN_PROGRESS",
|
|
68
|
+
createTime: new Date().toISOString(),
|
|
69
|
+
}),
|
|
70
|
+
stderr: "",
|
|
71
|
+
exitCode: 0,
|
|
72
|
+
durationMs: 10,
|
|
73
|
+
command: "td-cli agent dispatch"
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const result = await createJulesSessionHandler({ task: "do work", pr: 42 });
|
|
78
|
+
expect(result.id).toBe("1234567890");
|
|
79
|
+
|
|
80
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
81
|
+
"gh", "view", "42"
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
85
|
+
"agent", "dispatch", "pr-branch", "do work"
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should throw error if PR view fails", async () => {
|
|
90
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
91
|
+
stdout: JSON.stringify({ status: "error", message: "PR not found" }),
|
|
92
|
+
stderr: "Original error",
|
|
93
|
+
exitCode: 1,
|
|
94
|
+
durationMs: 10,
|
|
95
|
+
command: "td-cli gh view"
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await expect(createJulesSessionHandler({ task: "do work", pr: 999 }))
|
|
99
|
+
.rejects.toThrow("td-cli returned error: PR not found");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should throw descriptive error if PR JSON is invalid", async () => {
|
|
103
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
104
|
+
stdout: "invalid json",
|
|
105
|
+
stderr: "",
|
|
106
|
+
exitCode: 0,
|
|
107
|
+
durationMs: 10,
|
|
108
|
+
command: "td-cli gh view"
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await expect(createJulesSessionHandler({ task: "do work", pr: 42 }))
|
|
112
|
+
.rejects.toThrow("td-cli returned non-JSON output with exit code 0: invalid json");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runTDCli } from "../../lib/td-cli.js";
|
|
3
|
+
import { parseJulesSession } from "./shared.js";
|
|
4
|
+
|
|
5
|
+
export const CreateJulesSessionInputSchema = z.object({
|
|
6
|
+
task: z.string(),
|
|
7
|
+
branch: z.string().optional(),
|
|
8
|
+
pr: z.number().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export async function createJulesSessionHandler(input: z.infer<typeof CreateJulesSessionInputSchema>) {
|
|
12
|
+
let branch = input.branch;
|
|
13
|
+
if (!branch && input.pr) {
|
|
14
|
+
const prData = await runTDCli(["gh", "view", input.pr.toString()]);
|
|
15
|
+
branch = prData.pr.headRefName;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!branch) {
|
|
19
|
+
branch = process.env.DEFAULT_BASE_BRANCH || "main";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const output = await runTDCli(["agent", "dispatch", branch, input.task]);
|
|
23
|
+
return parseJulesSession(output.session || output);
|
|
24
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { getJulesMessagesHandler } from "./get-messages.js";
|
|
3
|
+
import * as shell from "../../lib/shell.js";
|
|
4
|
+
import { setupTestEnv, teardownTestEnv } from "../../lib/test-utils.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("getJulesMessagesHandler", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setupTestEnv();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
teardownTestEnv();
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should return messages from td-cli", async () => {
|
|
21
|
+
const mockMessages = [
|
|
22
|
+
{
|
|
23
|
+
role: "user",
|
|
24
|
+
content: "Hello Jules",
|
|
25
|
+
time: new Date().toISOString(),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
role: "jules",
|
|
29
|
+
content: "Applying fixes...",
|
|
30
|
+
time: new Date().toISOString(),
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
35
|
+
stdout: JSON.stringify({ status: "success", messages: mockMessages }),
|
|
36
|
+
stderr: "",
|
|
37
|
+
exitCode: 0,
|
|
38
|
+
durationMs: 0,
|
|
39
|
+
command: "td-cli agent messages"
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const result = await getJulesMessagesHandler({ sessionId: "123" });
|
|
43
|
+
expect(result.id).toBe("123");
|
|
44
|
+
expect(result.messages.length).toBe(2);
|
|
45
|
+
expect(result.messages[0].role).toBe("user");
|
|
46
|
+
expect(result.messages[0].content).toBe("Hello Jules");
|
|
47
|
+
|
|
48
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
49
|
+
"agent", "messages", "123"
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../../lib/shell.js";
|
|
3
|
+
|
|
4
|
+
export const GetJulesMessagesInputSchema = z.object({
|
|
5
|
+
sessionId: z.string(),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export async function getJulesMessagesHandler(input: z.infer<typeof GetJulesMessagesInputSchema>) {
|
|
9
|
+
const result = await runCommand("td-cli", ["agent", "messages", input.sessionId]);
|
|
10
|
+
|
|
11
|
+
if (result.exitCode !== 0) {
|
|
12
|
+
throw new Error(`Failed to get messages: ${result.stderr}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const output = JSON.parse(result.stdout);
|
|
16
|
+
if (output.status === "error") {
|
|
17
|
+
throw new Error(`Failed to get messages: ${output.message}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
id: input.sessionId.replace("sessions/", ""),
|
|
22
|
+
messages: output.messages,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { getJulesPullRequestHandler } from "./get-pr.js";
|
|
3
|
+
import * as shell from "../../lib/shell.js";
|
|
4
|
+
import { setupTestEnv, teardownTestEnv } from "../../lib/test-utils.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("getJulesPullRequestHandler", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setupTestEnv();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
teardownTestEnv();
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should get PR from td-cli", async () => {
|
|
21
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
22
|
+
stdout: JSON.stringify({
|
|
23
|
+
status: "success",
|
|
24
|
+
session: {
|
|
25
|
+
name: "sessions/123",
|
|
26
|
+
outputs: [{ pullRequest: { url: "https://github.com/pr/1" } }]
|
|
27
|
+
}
|
|
28
|
+
}),
|
|
29
|
+
stderr: "",
|
|
30
|
+
exitCode: 0,
|
|
31
|
+
durationMs: 0,
|
|
32
|
+
command: "td-cli agent get-session"
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const result = await getJulesPullRequestHandler({ sessionId: "123" });
|
|
36
|
+
expect(result.id).toBe("123");
|
|
37
|
+
expect(result.pullRequestUrl).toBe("https://github.com/pr/1");
|
|
38
|
+
|
|
39
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
40
|
+
"agent", "get-session", "123"
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runTDCli } from "../../lib/td-cli.js";
|
|
3
|
+
import { parseJulesSession } from "./shared.js";
|
|
4
|
+
|
|
5
|
+
export const GetJulesPullRequestInputSchema = z.object({
|
|
6
|
+
sessionId: z.string(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export async function getJulesPullRequestHandler(input: z.infer<typeof GetJulesPullRequestInputSchema>) {
|
|
10
|
+
const output = await runTDCli(["agent", "get-session", input.sessionId]);
|
|
11
|
+
const session = parseJulesSession(output.session);
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
id: session.id,
|
|
15
|
+
pullRequestUrl: session.pullRequestUrl,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runTDCli } from "../../lib/td-cli.js";
|
|
3
|
+
import { parseJulesSession } from "./shared.js";
|
|
4
|
+
|
|
5
|
+
export const GetJulesSessionInputSchema = z.object({
|
|
6
|
+
sessionId: z.string(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export async function getJulesSessionHandler(input: z.infer<typeof GetJulesSessionInputSchema>) {
|
|
10
|
+
const output = await runTDCli(["agent", "get-session", input.sessionId]);
|
|
11
|
+
return parseJulesSession(output.session);
|
|
12
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { JulesSession, JulesStatus } from "../types.js";
|
|
3
|
+
import { runCommand } from "../../lib/shell.js";
|
|
4
|
+
|
|
5
|
+
export const ListJulesSessionsInputSchema = z.object({
|
|
6
|
+
pageSize: z.number().optional(),
|
|
7
|
+
pageToken: z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export async function listJulesSessionsHandler(input: z.infer<typeof ListJulesSessionsInputSchema>): Promise<JulesSession[]> {
|
|
11
|
+
const result = await runCommand("td-cli", ["agent", "sync"]);
|
|
12
|
+
|
|
13
|
+
if (result.exitCode !== 0) {
|
|
14
|
+
throw new Error(`Failed to list sessions: ${result.stderr}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const output = JSON.parse(result.stdout);
|
|
18
|
+
if (output.status === "error") {
|
|
19
|
+
throw new Error(`Failed to list sessions: ${output.message}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const sessions = output.sessions || [];
|
|
23
|
+
|
|
24
|
+
return sessions.map((session: any) => {
|
|
25
|
+
const name = session.name || "";
|
|
26
|
+
const id = name.startsWith("sessions/") ? name.substring(9) : name;
|
|
27
|
+
|
|
28
|
+
let status: JulesStatus = "PENDING";
|
|
29
|
+
if (session.state === "SUCCEEDED" || session.state === "COMPLETED") status = "COMPLETED";
|
|
30
|
+
else if (session.state === "FAILED" || session.state === "CANCELLED" || session.state === "TERMINATED") status = "FAILED";
|
|
31
|
+
else if (session.state === "IN_PROGRESS") status = "IN_PROGRESS";
|
|
32
|
+
|
|
33
|
+
let pullRequestUrl: string | undefined;
|
|
34
|
+
if (session.outputs && Array.isArray(session.outputs)) {
|
|
35
|
+
for (const out of session.outputs) {
|
|
36
|
+
if (out.pullRequest && out.pullRequest.url) {
|
|
37
|
+
pullRequestUrl = out.pullRequest.url;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
id,
|
|
45
|
+
status,
|
|
46
|
+
createdAt: session.createTime ? new Date(session.createTime) : new Date(),
|
|
47
|
+
pullRequestUrl,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { sendJulesMessageHandler } from "./send-message.js";
|
|
3
|
+
import * as shell from "../../lib/shell.js";
|
|
4
|
+
import { setupTestEnv, teardownTestEnv } from "../../lib/test-utils.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("sendJulesMessageHandler", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setupTestEnv();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
teardownTestEnv();
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should send a message via td-cli", async () => {
|
|
21
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
22
|
+
stdout: JSON.stringify({ status: "success", message: "Message sent successfully" }),
|
|
23
|
+
stderr: "",
|
|
24
|
+
exitCode: 0,
|
|
25
|
+
durationMs: 0,
|
|
26
|
+
command: "td-cli agent send"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const result = await sendJulesMessageHandler({ sessionId: "123", message: "hi" });
|
|
30
|
+
expect(result.id).toBe("123");
|
|
31
|
+
expect(result.status).toBe("success");
|
|
32
|
+
|
|
33
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
34
|
+
"agent", "send", "123", "hi"
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should support batch message sending", async () => {
|
|
39
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
40
|
+
stdout: JSON.stringify({
|
|
41
|
+
status: "success",
|
|
42
|
+
message: "Batch send completed. 2/2 successful.",
|
|
43
|
+
results: [
|
|
44
|
+
{ sessionId: "s1", status: "success" },
|
|
45
|
+
{ sessionId: "s2", status: "success" }
|
|
46
|
+
]
|
|
47
|
+
}),
|
|
48
|
+
stderr: "",
|
|
49
|
+
exitCode: 0,
|
|
50
|
+
durationMs: 0,
|
|
51
|
+
command: "td-cli agent send"
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await sendJulesMessageHandler({
|
|
55
|
+
sessionId: ["sessions/s1", "sessions/s2"],
|
|
56
|
+
message: "hi batch"
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(result.id).toEqual(["s1", "s2"]);
|
|
60
|
+
expect(result.status).toBe("success");
|
|
61
|
+
|
|
62
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
63
|
+
"agent", "send", "sessions/s1,sessions/s2", "hi batch"
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../../lib/shell.js";
|
|
3
|
+
|
|
4
|
+
export const SendJulesMessageInputSchema = z.object({
|
|
5
|
+
sessionId: z.union([z.string(), z.array(z.string())]),
|
|
6
|
+
message: z.string(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export async function sendJulesMessageHandler(input: z.infer<typeof SendJulesMessageInputSchema>) {
|
|
10
|
+
const sessionIds = Array.isArray(input.sessionId) ? input.sessionId.join(",") : input.sessionId;
|
|
11
|
+
const result = await runCommand("td-cli", ["agent", "send", sessionIds, input.message]);
|
|
12
|
+
|
|
13
|
+
if (result.exitCode !== 0) {
|
|
14
|
+
throw new Error(`Failed to send message: ${result.stderr}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const output = JSON.parse(result.stdout);
|
|
18
|
+
if (output.status === "error") {
|
|
19
|
+
throw new Error(`Failed to send message: ${output.message}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
id: Array.isArray(input.sessionId) ? input.sessionId.map(id => id.replace("sessions/", "")) : input.sessionId.replace("sessions/", ""),
|
|
24
|
+
status: "success",
|
|
25
|
+
message: "Message sent successfully",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JulesSession, JulesStatus } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export function parseJulesSession(data: any): JulesSession {
|
|
4
|
+
const name = data.name || "";
|
|
5
|
+
const id = name.startsWith("sessions/") ? name.substring(9) : name;
|
|
6
|
+
|
|
7
|
+
let status: JulesStatus = "PENDING";
|
|
8
|
+
if (data.state === "SUCCEEDED" || data.state === "COMPLETED") status = "COMPLETED";
|
|
9
|
+
else if (data.state === "FAILED" || data.state === "CANCELLED" || data.state === "TERMINATED") status = "FAILED";
|
|
10
|
+
else if (data.state === "IN_PROGRESS") status = "IN_PROGRESS";
|
|
11
|
+
|
|
12
|
+
let pullRequestUrl: string | undefined;
|
|
13
|
+
if (data.outputs && Array.isArray(data.outputs)) {
|
|
14
|
+
for (const out of data.outputs) {
|
|
15
|
+
if (out.pullRequest && out.pullRequest.url) {
|
|
16
|
+
pullRequestUrl = out.pullRequest.url;
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
status,
|
|
25
|
+
createdAt: data.createTime ? new Date(data.createTime) : new Date(),
|
|
26
|
+
pullRequestUrl,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { triggerJulesFeedbackHandler } from "./trigger-feedback.js";
|
|
3
|
+
import * as shell from "../../lib/shell.js";
|
|
4
|
+
import { setupTestEnv, teardownTestEnv } from "../../lib/test-utils.js";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../lib/shell.js", () => ({
|
|
7
|
+
runCommand: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("triggerJulesFeedbackHandler", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setupTestEnv();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
teardownTestEnv();
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should return success output from td-cli", async () => {
|
|
21
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
22
|
+
stdout: JSON.stringify({ status: "success", feedback: "All checks passed successfully. You may proceed." }),
|
|
23
|
+
stderr: "",
|
|
24
|
+
exitCode: 0,
|
|
25
|
+
durationMs: 0,
|
|
26
|
+
command: "td-cli agent trigger-feedback"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const result = await triggerJulesFeedbackHandler({ sessionId: "123" });
|
|
30
|
+
expect(result.status).toBe("success");
|
|
31
|
+
expect(result.feedback).toBe("All checks passed successfully. You may proceed.");
|
|
32
|
+
|
|
33
|
+
expect(shell.runCommand).toHaveBeenCalledWith("td-cli", [
|
|
34
|
+
"agent", "trigger-feedback", "123"
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should throw error if td-cli returns error", async () => {
|
|
39
|
+
vi.mocked(shell.runCommand).mockResolvedValue({
|
|
40
|
+
stdout: JSON.stringify({ status: "error", message: "Session not found" }),
|
|
41
|
+
stderr: "",
|
|
42
|
+
exitCode: 0,
|
|
43
|
+
durationMs: 0,
|
|
44
|
+
command: "td-cli agent trigger-feedback"
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await expect(triggerJulesFeedbackHandler({ sessionId: "123" }))
|
|
48
|
+
.rejects.toThrow("Failed to trigger feedback: Session not found");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../../lib/shell.js";
|
|
3
|
+
|
|
4
|
+
export const TriggerJulesFeedbackInputSchema = z.object({
|
|
5
|
+
sessionId: z.string(),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export async function triggerJulesFeedbackHandler(input: z.infer<typeof TriggerJulesFeedbackInputSchema>) {
|
|
9
|
+
const result = await runCommand("td-cli", ["agent", "trigger-feedback", input.sessionId]);
|
|
10
|
+
|
|
11
|
+
if (result.exitCode !== 0) {
|
|
12
|
+
throw new Error(`Failed to trigger feedback: ${result.stderr}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const output = JSON.parse(result.stdout);
|
|
16
|
+
if (output.status === "error") {
|
|
17
|
+
throw new Error(`Failed to trigger feedback: ${output.message}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return output;
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runCommand } from "../lib/shell.js";
|
|
3
|
+
|
|
4
|
+
export const CommitPatchInputSchema = z.object({
|
|
5
|
+
worktreePath: z.string(),
|
|
6
|
+
message: z.string(),
|
|
7
|
+
allowedFiles: z.array(z.string()),
|
|
8
|
+
writeMode: z.boolean().optional().default(false),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export async function commitPatchHandler(args: z.infer<typeof CommitPatchInputSchema>) {
|
|
12
|
+
if (!args.writeMode) {
|
|
13
|
+
throw new Error("writeMode must be true to commit changes.");
|
|
14
|
+
}
|
|
15
|
+
const statusResult = await runCommand("git", ["status", "--porcelain"], { cwd: args.worktreePath });
|
|
16
|
+
const changedFiles = statusResult.stdout.trim().split("\n").filter(l => l.length > 0).map(l => l.substring(3));
|
|
17
|
+
|
|
18
|
+
const forbiddenFiles = changedFiles.filter(f => !args.allowedFiles.includes(f));
|
|
19
|
+
if (forbiddenFiles.length > 0) {
|
|
20
|
+
throw new Error(`The following files are not in the allowed list: ${forbiddenFiles.join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (changedFiles.length === 0) {
|
|
24
|
+
throw new Error("No changes to commit.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await runCommand("git", ["add", "."], { cwd: args.worktreePath });
|
|
28
|
+
const commitResult = await runCommand("git", ["commit", "-m", args.message], { cwd: args.worktreePath });
|
|
29
|
+
|
|
30
|
+
if (commitResult.exitCode !== 0) {
|
|
31
|
+
throw new Error(`Failed to commit: ${commitResult.stderr}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const shaResult = await runCommand("git", ["rev-parse", "HEAD"], { cwd: args.worktreePath });
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
success: true,
|
|
38
|
+
commitSha: shaResult.stdout.trim(),
|
|
39
|
+
changedFiles
|
|
40
|
+
};
|
|
41
|
+
}
|