@cosmicstack/mercury-agent 0.3.0 → 0.3.1

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 CHANGED
@@ -14,8 +14,12 @@ import { join } from "path";
14
14
  import { homedir } from "os";
15
15
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
16
16
  import { config as loadDotenv } from "dotenv";
17
- loadDotenv();
18
17
  var MERCURY_HOME = join(homedir(), ".mercury");
18
+ loadDotenv();
19
+ var mercuryEnvPath = join(MERCURY_HOME, ".env");
20
+ if (existsSync(mercuryEnvPath)) {
21
+ loadDotenv({ path: mercuryEnvPath });
22
+ }
19
23
  function getMercuryHome() {
20
24
  return process.env.MERCURY_HOME || MERCURY_HOME;
21
25
  }
@@ -1006,6 +1010,12 @@ You can override this:
1006
1010
  if (this.tokenBudget.getUsagePercentage() > 70) {
1007
1011
  prompt += "\nBe concise to conserve tokens.";
1008
1012
  }
1013
+ const toolNames = this.capabilities.getToolNames();
1014
+ const githubTools = ["create_pr", "review_pr", "list_issues", "create_issue", "github_api"];
1015
+ const hasGitHub = githubTools.some((t) => toolNames.includes(t));
1016
+ if (hasGitHub) {
1017
+ prompt += "\n\nGitHub companion is active. You can create pull requests, review PRs, manage issues, and use the GitHub API. When the user asks to 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.";
1018
+ }
1009
1019
  return prompt;
1010
1020
  }
1011
1021
  async processInternalPrompt(prompt, channelId, channelType) {
@@ -1709,16 +1719,16 @@ var CLIChannel = class extends BaseChannel {
1709
1719
  }
1710
1720
  }
1711
1721
  async prompt(question) {
1712
- return new Promise((resolve10) => {
1713
- this.rl?.question(question, (answer) => resolve10(answer.trim()));
1722
+ return new Promise((resolve11) => {
1723
+ this.rl?.question(question, (answer) => resolve11(answer.trim()));
1714
1724
  });
1715
1725
  }
1716
1726
  async askPermission(prompt) {
1717
- return new Promise((resolve10) => {
1727
+ return new Promise((resolve11) => {
1718
1728
  console.log("");
1719
1729
  console.log(chalk2.yellow(` \u26A0 ${prompt}`));
1720
1730
  this.rl?.question(chalk2.yellow(" > "), (answer) => {
1721
- resolve10(answer.trim());
1731
+ resolve11(answer.trim());
1722
1732
  });
1723
1733
  });
1724
1734
  }
@@ -1981,15 +1991,15 @@ var TelegramChannel = class extends BaseChannel {
1981
1991
  reply_markup: keyboard
1982
1992
  });
1983
1993
  }
1984
- return new Promise((resolve10) => {
1985
- this.pendingApprovals.set(`${id}:yes`, () => resolve10("yes"));
1986
- this.pendingApprovals.set(`${id}:always`, () => resolve10("always"));
1987
- this.pendingApprovals.set(`${id}:no`, () => resolve10("no"));
1994
+ return new Promise((resolve11) => {
1995
+ this.pendingApprovals.set(`${id}:yes`, () => resolve11("yes"));
1996
+ this.pendingApprovals.set(`${id}:always`, () => resolve11("always"));
1997
+ this.pendingApprovals.set(`${id}:no`, () => resolve11("no"));
1988
1998
  setTimeout(() => {
1989
1999
  this.pendingApprovals.delete(`${id}:yes`);
1990
2000
  this.pendingApprovals.delete(`${id}:always`);
1991
2001
  this.pendingApprovals.delete(`${id}:no`);
1992
- resolve10("no");
2002
+ resolve11("no");
1993
2003
  }, 12e4);
1994
2004
  });
1995
2005
  }
@@ -2213,7 +2223,7 @@ var TokenBudget = class {
2213
2223
 
2214
2224
  // src/capabilities/permissions.ts
2215
2225
  import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
2216
- import { join as join6, resolve, sep } from "path";
2226
+ import { join as join6, resolve as resolve2, sep } from "path";
2217
2227
  import { homedir as homedir2 } from "os";
2218
2228
  import { parse as parseYaml3, stringify as stringifyYaml3 } from "yaml";
2219
2229
  var DEFAULT_MANIFEST = {
@@ -2396,7 +2406,7 @@ var PermissionManager = class {
2396
2406
  if (!fs3.enabled) {
2397
2407
  return { allowed: false, reason: "Filesystem capability is disabled" };
2398
2408
  }
2399
- const resolved = resolve(path3);
2409
+ const resolved = resolve2(path3);
2400
2410
  const scope = this.findScope(resolved);
2401
2411
  const tempScope = this.findTempScope(resolved);
2402
2412
  if (scope) {
@@ -2486,7 +2496,7 @@ var PermissionManager = class {
2486
2496
  return this.manifest.capabilities.git.enabled && this.manifest.capabilities.git.approveWrite;
2487
2497
  }
2488
2498
  addScope(path3, read, write) {
2489
- const resolved = resolve(path3);
2499
+ const resolved = resolve2(path3);
2490
2500
  const existing = this.findScope(resolved);
2491
2501
  if (existing) {
2492
2502
  existing.read = existing.read || read;
@@ -2504,7 +2514,7 @@ var PermissionManager = class {
2504
2514
  findScope(resolvedPath) {
2505
2515
  const scopes = this.manifest.capabilities.filesystem.scopes;
2506
2516
  for (const scope of scopes) {
2507
- const scopeResolved = resolve(scope.path.replace(/^~/, homedir2()));
2517
+ const scopeResolved = resolve2(scope.path.replace(/^~/, homedir2()));
2508
2518
  if (resolvedPath === scopeResolved || resolvedPath.startsWith(scopeResolved + sep)) {
2509
2519
  return scope;
2510
2520
  }
@@ -2531,13 +2541,13 @@ Allow access?`;
2531
2541
  return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
2532
2542
  }
2533
2543
  addTempScope(path3, read, write) {
2534
- const resolved = resolve(path3);
2544
+ const resolved = resolve2(path3);
2535
2545
  this.tempScopes.push({ path: resolved, read, write });
2536
2546
  logger.info({ path: resolved, read, write }, "Temp permission scope added (session only)");
2537
2547
  }
2538
2548
  findTempScope(resolvedPath) {
2539
2549
  for (const scope of this.tempScopes) {
2540
- const scopeResolved = resolve(scope.path.replace(/^~/, homedir2()));
2550
+ const scopeResolved = resolve2(scope.path.replace(/^~/, homedir2()));
2541
2551
  if (resolvedPath === scopeResolved || resolvedPath.startsWith(scopeResolved + sep)) {
2542
2552
  return scope;
2543
2553
  }
@@ -2563,7 +2573,7 @@ Allow access?`;
2563
2573
  for (const p of pathPatterns) {
2564
2574
  const match = command.match(p);
2565
2575
  if (match) {
2566
- const candidate = resolve(match[1].replace(/^~/, homedir2()));
2576
+ const candidate = resolve2(match[1].replace(/^~/, homedir2()));
2567
2577
  if (!candidate.startsWith(this.cwd)) {
2568
2578
  return candidate;
2569
2579
  }
@@ -2599,7 +2609,7 @@ Allow access?`;
2599
2609
  import { tool } from "ai";
2600
2610
  import { z } from "zod";
2601
2611
  import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
2602
- import { resolve as resolve2 } from "path";
2612
+ import { resolve as resolve3 } from "path";
2603
2613
  function createReadFileTool(permissions) {
2604
2614
  return tool({
2605
2615
  description: "Read the contents of a file. The path must be within an allowed scope.",
@@ -2607,10 +2617,10 @@ function createReadFileTool(permissions) {
2607
2617
  path: z.string().describe("Absolute or relative path to the file")
2608
2618
  }),
2609
2619
  execute: async ({ path: path3 }) => {
2610
- const resolved = resolve2(path3);
2620
+ const resolved = resolve3(path3);
2611
2621
  const check = await permissions.checkFsAccess(resolved, "read");
2612
2622
  if (!check.allowed) {
2613
- const parentDir = resolve2(resolved, "..");
2623
+ const parentDir = resolve3(resolved, "..");
2614
2624
  return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="read" to request access from the user.`;
2615
2625
  }
2616
2626
  if (!existsSync7(resolved)) {
@@ -2636,7 +2646,7 @@ function createReadFileTool(permissions) {
2636
2646
  import { tool as tool2 } from "ai";
2637
2647
  import { z as z2 } from "zod";
2638
2648
  import { existsSync as existsSync8, writeFileSync as writeFileSync7 } from "fs";
2639
- import { resolve as resolve3 } from "path";
2649
+ import { resolve as resolve4 } from "path";
2640
2650
  function createWriteFileTool(permissions) {
2641
2651
  return tool2({
2642
2652
  description: "Write content to an existing file. The path must be within a writable scope.",
@@ -2645,10 +2655,10 @@ function createWriteFileTool(permissions) {
2645
2655
  content: z2.string().describe("The content to write to the file")
2646
2656
  }),
2647
2657
  execute: async ({ path: path3, content }) => {
2648
- const resolved = resolve3(path3);
2658
+ const resolved = resolve4(path3);
2649
2659
  const check = await permissions.checkFsAccess(resolved, "write");
2650
2660
  if (!check.allowed) {
2651
- const parentDir = resolve3(resolved, "..");
2661
+ const parentDir = resolve4(resolved, "..");
2652
2662
  return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2653
2663
  }
2654
2664
  if (!existsSync8(resolved)) {
@@ -2668,7 +2678,7 @@ function createWriteFileTool(permissions) {
2668
2678
  import { tool as tool3 } from "ai";
2669
2679
  import { z as z3 } from "zod";
2670
2680
  import { existsSync as existsSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
2671
- import { resolve as resolve4, dirname as dirname2 } from "path";
2681
+ import { resolve as resolve5, dirname as dirname2 } from "path";
2672
2682
  function createCreateFileTool(permissions) {
2673
2683
  return tool3({
2674
2684
  description: "Create a new file with the given content. Also creates parent directories if needed. The path must be within a writable scope.",
@@ -2677,10 +2687,10 @@ function createCreateFileTool(permissions) {
2677
2687
  content: z3.string().describe("The content of the new file")
2678
2688
  }),
2679
2689
  execute: async ({ path: path3, content }) => {
2680
- const resolved = resolve4(path3);
2690
+ const resolved = resolve5(path3);
2681
2691
  const check = await permissions.checkFsAccess(resolved, "write");
2682
2692
  if (!check.allowed) {
2683
- const parentDir = resolve4(resolved, "..");
2693
+ const parentDir = resolve5(resolved, "..");
2684
2694
  return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2685
2695
  }
2686
2696
  if (existsSync9(resolved)) {
@@ -2704,7 +2714,7 @@ function createCreateFileTool(permissions) {
2704
2714
  import { tool as tool4 } from "ai";
2705
2715
  import { z as z4 } from "zod";
2706
2716
  import { existsSync as existsSync10, readdirSync as readdirSync2, statSync } from "fs";
2707
- import { resolve as resolve5 } from "path";
2717
+ import { resolve as resolve6 } from "path";
2708
2718
  function createListDirTool(permissions) {
2709
2719
  return tool4({
2710
2720
  description: "List the contents of a directory. Shows file names, types, and sizes.",
@@ -2712,7 +2722,7 @@ function createListDirTool(permissions) {
2712
2722
  path: z4.string().describe("Absolute or relative path to the directory")
2713
2723
  }),
2714
2724
  execute: async ({ path: path3 }) => {
2715
- const resolved = resolve5(path3);
2725
+ const resolved = resolve6(path3);
2716
2726
  const check = await permissions.checkFsAccess(resolved, "read");
2717
2727
  if (!check.allowed) {
2718
2728
  return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${resolved}" and mode="read" to request access from the user.`;
@@ -2762,7 +2772,7 @@ function formatSize(bytes) {
2762
2772
  import { tool as tool5 } from "ai";
2763
2773
  import { z as z5 } from "zod";
2764
2774
  import { existsSync as existsSync11, unlinkSync as unlinkSync2 } from "fs";
2765
- import { resolve as resolve6 } from "path";
2775
+ import { resolve as resolve7 } from "path";
2766
2776
  function createDeleteFileTool(permissions) {
2767
2777
  return tool5({
2768
2778
  description: "Delete a file. This action cannot be undone. The path must be within a writable scope. Always asks for confirmation.",
@@ -2770,10 +2780,10 @@ function createDeleteFileTool(permissions) {
2770
2780
  path: z5.string().describe("Absolute or relative path to the file to delete")
2771
2781
  }),
2772
2782
  execute: async ({ path: path3 }) => {
2773
- const resolved = resolve6(path3);
2783
+ const resolved = resolve7(path3);
2774
2784
  const check = await permissions.checkFsAccess(resolved, "write");
2775
2785
  if (!check.allowed) {
2776
- const parentDir = resolve6(resolved, "..");
2786
+ const parentDir = resolve7(resolved, "..");
2777
2787
  return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2778
2788
  }
2779
2789
  if (!existsSync11(resolved)) {
@@ -2797,7 +2807,7 @@ function createDeleteFileTool(permissions) {
2797
2807
  import { tool as tool6 } from "ai";
2798
2808
  import { z as z6 } from "zod";
2799
2809
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
2800
- import { resolve as resolve7 } from "path";
2810
+ import { resolve as resolve8 } from "path";
2801
2811
  function createEditFileTool(permissions) {
2802
2812
  return tool6({
2803
2813
  description: "Edit a file by replacing an exact string match with new content. Use this instead of write_file when you only need to change part of a file. The old_string must match exactly (including whitespace and indentation). Fails if old_string is not found or found multiple times.",
@@ -2807,10 +2817,10 @@ function createEditFileTool(permissions) {
2807
2817
  new_string: z6.string().describe("The text to replace it with")
2808
2818
  }),
2809
2819
  execute: async ({ path: path3, old_string, new_string }) => {
2810
- const resolved = resolve7(path3);
2820
+ const resolved = resolve8(path3);
2811
2821
  const fsCheck = await permissions.checkFsAccess(resolved, "write");
2812
2822
  if (!fsCheck.allowed) {
2813
- const parentDir = resolve7(resolved, "..");
2823
+ const parentDir = resolve8(resolved, "..");
2814
2824
  return `Error: Permission denied for write access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="write" to request access from the user.`;
2815
2825
  }
2816
2826
  try {
@@ -2841,7 +2851,7 @@ function createEditFileTool(permissions) {
2841
2851
  import { tool as tool7 } from "ai";
2842
2852
  import { z as z7 } from "zod";
2843
2853
  import { existsSync as existsSync12, statSync as statSync2 } from "fs";
2844
- import { resolve as resolve8, basename } from "path";
2854
+ import { resolve as resolve9, basename } from "path";
2845
2855
  function createSendFileTool(permissions, sendFile) {
2846
2856
  return tool7({
2847
2857
  description: "Send a file to the user. On Telegram the file is uploaded as an attachment. On CLI the file path and size are displayed. The path must be within an allowed read scope.",
@@ -2849,10 +2859,10 @@ function createSendFileTool(permissions, sendFile) {
2849
2859
  path: z7.string().describe("Absolute or relative path to the file to send")
2850
2860
  }),
2851
2861
  execute: async ({ path: path3 }) => {
2852
- const resolved = resolve8(path3);
2862
+ const resolved = resolve9(path3);
2853
2863
  const check = await permissions.checkFsAccess(resolved, "read");
2854
2864
  if (!check.allowed) {
2855
- const parentDir = resolve8(resolved, "..");
2865
+ const parentDir = resolve9(resolved, "..");
2856
2866
  return `Error: Permission denied for read access to ${resolved}. Use the approve_scope tool with path="${parentDir}" and mode="read" to request access from the user.`;
2857
2867
  }
2858
2868
  if (!existsSync12(resolved)) {
@@ -2880,7 +2890,7 @@ function createSendFileTool(permissions, sendFile) {
2880
2890
  // src/capabilities/filesystem/approve-scope.ts
2881
2891
  import { tool as tool8 } from "ai";
2882
2892
  import { z as z8 } from "zod";
2883
- import { resolve as resolve9 } from "path";
2893
+ import { resolve as resolve10 } from "path";
2884
2894
  function createApproveScopeTool(permissions) {
2885
2895
  return tool8({
2886
2896
  description: 'Request user approval to access a directory outside current scopes. Use this when a file tool returns a permission denied error. The user gets an approval prompt (Allow/Always/Deny buttons on Telegram, yes/always/no on CLI). "Allow" grants session-only access. "Always" persists to disk. After approval, retry the original file operation.',
@@ -2889,7 +2899,7 @@ function createApproveScopeTool(permissions) {
2889
2899
  mode: z8.enum(["read", "write"]).describe("The access mode needed")
2890
2900
  }),
2891
2901
  execute: async ({ path: path3, mode }) => {
2892
- const resolved = resolve9(path3);
2902
+ const resolved = resolve10(path3);
2893
2903
  const result = await permissions.requestScopeExternal(resolved, mode);
2894
2904
  if (result.allowed) {
2895
2905
  return `Access approved for ${mode} access to ${resolved}. You can now retry the file operation.`;
@@ -4618,7 +4628,7 @@ async function runWithWatchdog(agentFn) {
4618
4628
  await attempt();
4619
4629
  }
4620
4630
  function sleep(ms) {
4621
- return new Promise((resolve10) => setTimeout(resolve10, ms));
4631
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
4622
4632
  }
4623
4633
 
4624
4634
  // src/index.ts
@@ -4657,10 +4667,10 @@ function splashScreen() {
4657
4667
  }
4658
4668
  async function ask(prompt) {
4659
4669
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4660
- return new Promise((resolve10) => {
4670
+ return new Promise((resolve11) => {
4661
4671
  rl.question(prompt, (answer) => {
4662
4672
  rl.close();
4663
- resolve10(answer.trim());
4673
+ resolve11(answer.trim());
4664
4674
  });
4665
4675
  });
4666
4676
  }