@cosmicstack/mercury-agent 0.3.1 → 0.3.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/dist/index.js +63 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,7 +79,9 @@ function getDefaultConfig() {
|
|
|
79
79
|
},
|
|
80
80
|
github: {
|
|
81
81
|
username: getEnv("GITHUB_USERNAME", ""),
|
|
82
|
-
email: getEnv("GITHUB_EMAIL", "")
|
|
82
|
+
email: getEnv("GITHUB_EMAIL", ""),
|
|
83
|
+
defaultOwner: getEnv("GITHUB_DEFAULT_OWNER", ""),
|
|
84
|
+
defaultRepo: getEnv("GITHUB_DEFAULT_REPO", "")
|
|
83
85
|
},
|
|
84
86
|
memory: {
|
|
85
87
|
dir: getEnv("MEMORY_DIR", join(home, "memory")),
|
|
@@ -1014,7 +1016,13 @@ You can override this:
|
|
|
1014
1016
|
const githubTools = ["create_pr", "review_pr", "list_issues", "create_issue", "github_api"];
|
|
1015
1017
|
const hasGitHub = githubTools.some((t) => toolNames.includes(t));
|
|
1016
1018
|
if (hasGitHub) {
|
|
1017
|
-
|
|
1019
|
+
let githubHint = "\n\nGitHub companion is active. You can create pull requests, review PRs, manage issues, and use the GitHub API.";
|
|
1020
|
+
const { defaultOwner, defaultRepo } = this.config.github;
|
|
1021
|
+
if (defaultOwner && defaultRepo) {
|
|
1022
|
+
githubHint += ` Default repo: ${defaultOwner}/${defaultRepo}. Use this when the user doesn't specify a repo.`;
|
|
1023
|
+
}
|
|
1024
|
+
githubHint += ' When the user says "create a PR", use create_pr. When they ask about issues, use list_issues or create_issue. When they ask to review a PR, use review_pr. Always specify owner and repo parameters.';
|
|
1025
|
+
prompt += githubHint;
|
|
1018
1026
|
}
|
|
1019
1027
|
return prompt;
|
|
1020
1028
|
}
|
|
@@ -3402,23 +3410,11 @@ async function githubRequest(path3, options = {}) {
|
|
|
3402
3410
|
throw new Error(`GitHub API ${response.status}: ${body.slice(0, 500)}`);
|
|
3403
3411
|
}
|
|
3404
3412
|
if (response.status === 204) return null;
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/([^.\s]+)(?:\.git)?$/);
|
|
3409
|
-
if (sshMatch) return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
3410
|
-
const httpsMatch = remoteUrl.match(/https:\/\/github\.com\/([^/]+)\/([^.\s]+)(?:\.git)?$/);
|
|
3411
|
-
if (httpsMatch) return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
3412
|
-
return null;
|
|
3413
|
-
}
|
|
3414
|
-
async function getCurrentRepo() {
|
|
3415
|
-
try {
|
|
3416
|
-
const { execSync: execSync9 } = await import("child_process");
|
|
3417
|
-
const remoteUrl = execSync9("git remote get-url origin", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
3418
|
-
return parseRepo(remoteUrl);
|
|
3419
|
-
} catch {
|
|
3420
|
-
return null;
|
|
3413
|
+
const contentType = response.headers.get("content-type") || "";
|
|
3414
|
+
if (contentType.includes("application/json")) {
|
|
3415
|
+
return response.json();
|
|
3421
3416
|
}
|
|
3417
|
+
return response.text();
|
|
3422
3418
|
}
|
|
3423
3419
|
|
|
3424
3420
|
// src/capabilities/github/create-pr.ts
|
|
@@ -3426,25 +3422,19 @@ function createCreatePrTool() {
|
|
|
3426
3422
|
return tool24({
|
|
3427
3423
|
description: "Create a pull request on GitHub. Requires GITHUB_TOKEN to be configured.",
|
|
3428
3424
|
parameters: z24.object({
|
|
3425
|
+
owner: z24.string().describe("Repository owner (username or org)"),
|
|
3426
|
+
repo: z24.string().describe("Repository name"),
|
|
3429
3427
|
title: z24.string().describe("PR title"),
|
|
3430
3428
|
body: z24.string().describe("PR description (markdown supported)").default(""),
|
|
3431
3429
|
head: z24.string().describe("The branch containing the changes"),
|
|
3432
3430
|
base: z24.string().describe("The branch to merge into").default("main"),
|
|
3433
3431
|
draft: z24.boolean().describe("Create as draft PR").default(false)
|
|
3434
3432
|
}),
|
|
3435
|
-
execute: async ({ title, body, head, base, draft }) => {
|
|
3433
|
+
execute: async ({ owner, repo, title, body, head, base, draft }) => {
|
|
3436
3434
|
try {
|
|
3437
|
-
const
|
|
3438
|
-
if (!repo) return "Error: Could not detect GitHub repository. Make sure you are in a git repo with a GitHub remote.";
|
|
3439
|
-
const result = await githubRequest(`/repos/${repo.owner}/${repo.repo}/pulls`, {
|
|
3435
|
+
const result = await githubRequest(`/repos/${owner}/${repo}/pulls`, {
|
|
3440
3436
|
method: "POST",
|
|
3441
|
-
body: {
|
|
3442
|
-
title,
|
|
3443
|
-
body,
|
|
3444
|
-
head,
|
|
3445
|
-
base,
|
|
3446
|
-
draft
|
|
3447
|
-
}
|
|
3437
|
+
body: { title, body, head, base, draft }
|
|
3448
3438
|
});
|
|
3449
3439
|
return `PR created: ${result.html_url}
|
|
3450
3440
|
#${result.number}: ${result.title}
|
|
@@ -3463,18 +3453,15 @@ function createReviewPrTool() {
|
|
|
3463
3453
|
return tool25({
|
|
3464
3454
|
description: "Get details of a pull request including the diff. Reviews the PR and returns the title, body, changed files, and diff. Optionally post a review comment.",
|
|
3465
3455
|
parameters: z25.object({
|
|
3456
|
+
owner: z25.string().describe("Repository owner (username or org)"),
|
|
3457
|
+
repo: z25.string().describe("Repository name"),
|
|
3466
3458
|
number: z25.number().describe("PR number"),
|
|
3467
3459
|
comment: z25.string().describe("Review comment to post on the PR (optional)").optional()
|
|
3468
3460
|
}),
|
|
3469
|
-
execute: async ({ number, comment }) => {
|
|
3461
|
+
execute: async ({ owner, repo, number, comment }) => {
|
|
3470
3462
|
try {
|
|
3471
|
-
const
|
|
3472
|
-
if (!repo) return "Error: Could not detect GitHub repository.";
|
|
3473
|
-
const pr = await githubRequest(`/repos/${repo.owner}/${repo.repo}/pulls/${number}`);
|
|
3463
|
+
const pr = await githubRequest(`/repos/${owner}/${repo}/pulls/${number}`);
|
|
3474
3464
|
if (!pr) return `Error: PR #${number} not found.`;
|
|
3475
|
-
const diff = await githubRequest(`/repos/${repo.owner}/${repo.repo}/pulls/${number}`, {
|
|
3476
|
-
headers: { "Accept": "application/vnd.github.v3.diff" }
|
|
3477
|
-
});
|
|
3478
3465
|
let summary = `PR #${pr.number}: ${pr.title}
|
|
3479
3466
|
`;
|
|
3480
3467
|
summary += `Author: ${pr.user?.login}
|
|
@@ -3492,28 +3479,30 @@ ${pr.body.slice(0, 2e3)}
|
|
|
3492
3479
|
|
|
3493
3480
|
`;
|
|
3494
3481
|
}
|
|
3495
|
-
|
|
3496
|
-
const
|
|
3497
|
-
|
|
3498
|
-
|
|
3482
|
+
try {
|
|
3483
|
+
const diff = await githubRequest(`/repos/${owner}/${repo}/pulls/${number}`, {
|
|
3484
|
+
headers: { "Accept": "application/vnd.github.v3.diff" }
|
|
3485
|
+
});
|
|
3486
|
+
if (typeof diff === "string") {
|
|
3487
|
+
const diffLines = diff.split("\n");
|
|
3488
|
+
const maxDiffLines = 200;
|
|
3489
|
+
summary += `Diff (first ${Math.min(diffLines.length, maxDiffLines)} of ${diffLines.length} lines):
|
|
3499
3490
|
`;
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3491
|
+
summary += diffLines.slice(0, maxDiffLines).join("\n");
|
|
3492
|
+
if (diffLines.length > maxDiffLines) {
|
|
3493
|
+
summary += `
|
|
3503
3494
|
|
|
3504
3495
|
... (${diffLines.length - maxDiffLines} more lines)`;
|
|
3496
|
+
}
|
|
3505
3497
|
}
|
|
3506
|
-
}
|
|
3507
|
-
summary += `(Diff not available
|
|
3498
|
+
} catch {
|
|
3499
|
+
summary += `(Diff not available)`;
|
|
3508
3500
|
}
|
|
3509
3501
|
if (comment) {
|
|
3510
3502
|
try {
|
|
3511
|
-
await githubRequest(`/repos/${
|
|
3503
|
+
await githubRequest(`/repos/${owner}/${repo}/pulls/${number}/reviews`, {
|
|
3512
3504
|
method: "POST",
|
|
3513
|
-
body: {
|
|
3514
|
-
body: comment,
|
|
3515
|
-
event: "COMMENT"
|
|
3516
|
-
}
|
|
3505
|
+
body: { body: comment, event: "COMMENT" }
|
|
3517
3506
|
});
|
|
3518
3507
|
summary += `
|
|
3519
3508
|
|
|
@@ -3537,31 +3526,31 @@ import { tool as tool26 } from "ai";
|
|
|
3537
3526
|
import { z as z26 } from "zod";
|
|
3538
3527
|
function createListIssuesTool() {
|
|
3539
3528
|
return tool26({
|
|
3540
|
-
description: "List
|
|
3529
|
+
description: "List GitHub issues for a repository. Requires GITHUB_TOKEN.",
|
|
3541
3530
|
parameters: z26.object({
|
|
3531
|
+
owner: z26.string().describe("Repository owner (username or org)"),
|
|
3532
|
+
repo: z26.string().describe("Repository name"),
|
|
3542
3533
|
state: z26.enum(["open", "closed", "all"]).describe("Filter by issue state").default("open"),
|
|
3543
3534
|
labels: z26.string().describe("Comma-separated label names to filter by (optional)").optional(),
|
|
3544
3535
|
limit: z26.number().describe("Maximum number of issues to return").default(10)
|
|
3545
3536
|
}),
|
|
3546
|
-
execute: async ({ state, labels, limit }) => {
|
|
3537
|
+
execute: async ({ owner, repo, state, labels, limit }) => {
|
|
3547
3538
|
try {
|
|
3548
|
-
const repo = await getCurrentRepo();
|
|
3549
|
-
if (!repo) return "Error: Could not detect GitHub repository.";
|
|
3550
3539
|
const params = new URLSearchParams();
|
|
3551
3540
|
params.set("state", state);
|
|
3552
3541
|
params.set("per_page", String(Math.min(limit, 100)));
|
|
3553
3542
|
params.set("sort", "updated");
|
|
3554
3543
|
params.set("direction", "desc");
|
|
3555
3544
|
if (labels) params.set("labels", labels);
|
|
3556
|
-
const issues = await githubRequest(`/repos/${
|
|
3545
|
+
const issues = await githubRequest(`/repos/${owner}/${repo}/issues?${params}`);
|
|
3557
3546
|
if (!Array.isArray(issues) || issues.length === 0) {
|
|
3558
|
-
return `No ${state} issues found.`;
|
|
3547
|
+
return `No ${state} issues found in ${owner}/${repo}.`;
|
|
3559
3548
|
}
|
|
3560
3549
|
const lines = issues.map((issue) => {
|
|
3561
3550
|
const labelStr = issue.labels?.map((l) => `[${l.name}]`).join(" ") || "";
|
|
3562
3551
|
return `#${issue.number} ${issue.title} ${labelStr} (${issue.state}, by ${issue.user?.login})`;
|
|
3563
3552
|
});
|
|
3564
|
-
return `Issues in ${
|
|
3553
|
+
return `Issues in ${owner}/${repo} (${state}):
|
|
3565
3554
|
${lines.join("\n")}`;
|
|
3566
3555
|
} catch (err) {
|
|
3567
3556
|
return `Error listing issues: ${err.message}`;
|
|
@@ -3575,19 +3564,19 @@ import { tool as tool27 } from "ai";
|
|
|
3575
3564
|
import { z as z27 } from "zod";
|
|
3576
3565
|
function createCreateIssueTool() {
|
|
3577
3566
|
return tool27({
|
|
3578
|
-
description: "Create a new GitHub issue in
|
|
3567
|
+
description: "Create a new GitHub issue in a repository. Requires GITHUB_TOKEN.",
|
|
3579
3568
|
parameters: z27.object({
|
|
3569
|
+
owner: z27.string().describe("Repository owner (username or org)"),
|
|
3570
|
+
repo: z27.string().describe("Repository name"),
|
|
3580
3571
|
title: z27.string().describe("Issue title"),
|
|
3581
3572
|
body: z27.string().describe("Issue description (markdown supported)").default(""),
|
|
3582
3573
|
labels: z27.array(z27.string()).describe("Label names to apply").optional()
|
|
3583
3574
|
}),
|
|
3584
|
-
execute: async ({ title, body, labels }) => {
|
|
3575
|
+
execute: async ({ owner, repo, title, body, labels }) => {
|
|
3585
3576
|
try {
|
|
3586
|
-
const repo = await getCurrentRepo();
|
|
3587
|
-
if (!repo) return "Error: Could not detect GitHub repository.";
|
|
3588
3577
|
const payload = { title, body };
|
|
3589
3578
|
if (labels && labels.length > 0) payload.labels = labels;
|
|
3590
|
-
const result = await githubRequest(`/repos/${
|
|
3579
|
+
const result = await githubRequest(`/repos/${owner}/${repo}/issues`, {
|
|
3591
3580
|
method: "POST",
|
|
3592
3581
|
body: payload
|
|
3593
3582
|
});
|
|
@@ -3603,27 +3592,15 @@ function createCreateIssueTool() {
|
|
|
3603
3592
|
// src/capabilities/github/github-api.ts
|
|
3604
3593
|
import { tool as tool28 } from "ai";
|
|
3605
3594
|
import { z as z28 } from "zod";
|
|
3606
|
-
function createGithubApiTool(
|
|
3595
|
+
function createGithubApiTool() {
|
|
3607
3596
|
return tool28({
|
|
3608
|
-
description: "Make a raw request to the GitHub API. Use this for any GitHub operation not covered by other tools. GET requests are
|
|
3597
|
+
description: "Make a raw request to the GitHub API. Use this for any GitHub operation not covered by other tools. GET requests (read-only) are always allowed. Write operations (POST, PUT, PATCH, DELETE) will ask the user for approval via the permission system.",
|
|
3609
3598
|
parameters: z28.object({
|
|
3610
|
-
path: z28.string().describe("API path (e.g., /repos/owner/repo/
|
|
3599
|
+
path: z28.string().describe("Full API path (e.g., /repos/owner/repo/issues or /user)"),
|
|
3611
3600
|
method: z28.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).describe("HTTP method").default("GET"),
|
|
3612
3601
|
body: z28.string().describe("JSON body for write requests (as a JSON string)").optional()
|
|
3613
3602
|
}),
|
|
3614
3603
|
execute: async ({ path: path3, method, body }) => {
|
|
3615
|
-
const isWrite = ["POST", "PUT", "PATCH", "DELETE"].includes(method);
|
|
3616
|
-
if (isWrite) {
|
|
3617
|
-
const check = await permissions.checkShellCommand(`github-api ${method} ${path3}`);
|
|
3618
|
-
if (!check.allowed) {
|
|
3619
|
-
if (check.needsApproval) {
|
|
3620
|
-
return `This GitHub API write operation requires approval: ${method} ${path3}
|
|
3621
|
-
|
|
3622
|
-
Tell the user what this does and ask for confirmation. If approved, try again.`;
|
|
3623
|
-
}
|
|
3624
|
-
return `Error: ${check.reason}`;
|
|
3625
|
-
}
|
|
3626
|
-
}
|
|
3627
3604
|
try {
|
|
3628
3605
|
let parsedBody;
|
|
3629
3606
|
if (body) {
|
|
@@ -3813,7 +3790,7 @@ var CapabilityRegistry = class {
|
|
|
3813
3790
|
this.tools.review_pr = createReviewPrTool();
|
|
3814
3791
|
this.tools.list_issues = createListIssuesTool();
|
|
3815
3792
|
this.tools.create_issue = createCreateIssueTool();
|
|
3816
|
-
this.tools.github_api = createGithubApiTool(
|
|
3793
|
+
this.tools.github_api = createGithubApiTool();
|
|
3817
3794
|
logger.info("GitHub tools registered");
|
|
3818
3795
|
}
|
|
3819
3796
|
this.tools.fetch_url = createFetchUrlTool();
|
|
@@ -4803,6 +4780,14 @@ async function configure(existingConfig) {
|
|
|
4803
4780
|
if (ghToken) {
|
|
4804
4781
|
appendToEnv("GITHUB_TOKEN", ghToken);
|
|
4805
4782
|
}
|
|
4783
|
+
if (config.github.username || process.env.GITHUB_TOKEN) {
|
|
4784
|
+
const ghOwnerCurrent = isReconfig && config.github.defaultOwner ? ` [${config.github.defaultOwner}]` : "";
|
|
4785
|
+
const ghOwner = await ask(chalk6.white(` Default GitHub owner/org${ghOwnerCurrent}: `));
|
|
4786
|
+
if (ghOwner) config.github.defaultOwner = ghOwner;
|
|
4787
|
+
const ghRepoCurrent = isReconfig && config.github.defaultRepo ? ` [${config.github.defaultRepo}]` : "";
|
|
4788
|
+
const ghRepo = await ask(chalk6.white(` Default repo name${ghRepoCurrent}: `));
|
|
4789
|
+
if (ghRepo) config.github.defaultRepo = ghRepo;
|
|
4790
|
+
}
|
|
4806
4791
|
hr();
|
|
4807
4792
|
console.log("");
|
|
4808
4793
|
console.log(chalk6.bold.white(" Token Budget"));
|