@cosmicstack/mercury-agent 0.2.3 → 0.2.5

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
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { readFileSync as readFileSync12 } from "fs";
5
+ import { fileURLToPath } from "url";
6
+ import { dirname as dirname3, join as join11 } from "path";
4
7
  import { Command } from "commander";
5
8
  import readline2 from "readline";
6
9
  import chalk6 from "chalk";
@@ -1703,16 +1706,16 @@ var CLIChannel = class extends BaseChannel {
1703
1706
  }
1704
1707
  }
1705
1708
  async prompt(question) {
1706
- return new Promise((resolve9) => {
1707
- this.rl?.question(question, (answer) => resolve9(answer.trim()));
1709
+ return new Promise((resolve10) => {
1710
+ this.rl?.question(question, (answer) => resolve10(answer.trim()));
1708
1711
  });
1709
1712
  }
1710
1713
  async askPermission(prompt) {
1711
- return new Promise((resolve9) => {
1714
+ return new Promise((resolve10) => {
1712
1715
  console.log("");
1713
1716
  console.log(chalk2.yellow(` \u26A0 ${prompt}`));
1714
1717
  this.rl?.question(chalk2.yellow(" > "), (answer) => {
1715
- resolve9(answer.trim());
1718
+ resolve10(answer.trim());
1716
1719
  });
1717
1720
  });
1718
1721
  }
@@ -1975,15 +1978,15 @@ var TelegramChannel = class extends BaseChannel {
1975
1978
  reply_markup: keyboard
1976
1979
  });
1977
1980
  }
1978
- return new Promise((resolve9) => {
1979
- this.pendingApprovals.set(`${id}:yes`, () => resolve9("yes"));
1980
- this.pendingApprovals.set(`${id}:always`, () => resolve9("always"));
1981
- this.pendingApprovals.set(`${id}:no`, () => resolve9("no"));
1981
+ return new Promise((resolve10) => {
1982
+ this.pendingApprovals.set(`${id}:yes`, () => resolve10("yes"));
1983
+ this.pendingApprovals.set(`${id}:always`, () => resolve10("always"));
1984
+ this.pendingApprovals.set(`${id}:no`, () => resolve10("no"));
1982
1985
  setTimeout(() => {
1983
1986
  this.pendingApprovals.delete(`${id}:yes`);
1984
1987
  this.pendingApprovals.delete(`${id}:always`);
1985
1988
  this.pendingApprovals.delete(`${id}:no`);
1986
- resolve9("no");
1989
+ resolve10("no");
1987
1990
  }, 12e4);
1988
1991
  });
1989
1992
  }
@@ -2331,8 +2334,8 @@ var PermissionManager = class {
2331
2334
  clearElevation() {
2332
2335
  this.elevatedCommands.clear();
2333
2336
  }
2334
- isElevated(tool24) {
2335
- if (this.elevatedCommands.has(tool24)) return true;
2337
+ isElevated(tool25) {
2338
+ if (this.elevatedCommands.has(tool25)) return true;
2336
2339
  return false;
2337
2340
  }
2338
2341
  isShellElevated() {
@@ -2498,18 +2501,23 @@ var PermissionManager = class {
2498
2501
  }
2499
2502
  return void 0;
2500
2503
  }
2504
+ async requestScopeExternal(path3, mode) {
2505
+ return this.requestScope(path3, mode);
2506
+ }
2501
2507
  async requestScope(path3, mode) {
2502
2508
  if (!this.askHandler) {
2503
- return { allowed: false, reason: `No permission to ${mode} ${path3}. Add scope to ~/.mercury/permissions.yaml` };
2509
+ return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
2504
2510
  }
2505
- const response = await this.askHandler(
2506
- `Mercury needs ${mode} access to ${path3}. Allow? (y/n/always): `
2507
- );
2508
- if (response.toLowerCase() === "always") {
2511
+ const prompt = `Mercury needs ${mode} access to:
2512
+ ${path3}
2513
+
2514
+ Allow access?`;
2515
+ const response = await this.askHandler(prompt);
2516
+ if (response === "always") {
2509
2517
  this.addScope(path3, mode === "read", mode === "write");
2510
2518
  return { allowed: true };
2511
2519
  }
2512
- if (response.toLowerCase() === "y" || response.toLowerCase() === "yes") {
2520
+ if (response === "yes" || response === "y") {
2513
2521
  return { allowed: true };
2514
2522
  }
2515
2523
  return { allowed: false, reason: `Permission denied for ${mode} access to ${path3}` };
@@ -2580,7 +2588,8 @@ function createReadFileTool(permissions) {
2580
2588
  const resolved = resolve2(path3);
2581
2589
  const check = await permissions.checkFsAccess(resolved, "read");
2582
2590
  if (!check.allowed) {
2583
- return `Error: ${check.reason}`;
2591
+ const parentDir = resolve2(resolved, "..");
2592
+ 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.`;
2584
2593
  }
2585
2594
  if (!existsSync7(resolved)) {
2586
2595
  return `Error: File not found: ${resolved}`;
@@ -2617,7 +2626,8 @@ function createWriteFileTool(permissions) {
2617
2626
  const resolved = resolve3(path3);
2618
2627
  const check = await permissions.checkFsAccess(resolved, "write");
2619
2628
  if (!check.allowed) {
2620
- return `Error: ${check.reason}`;
2629
+ const parentDir = resolve3(resolved, "..");
2630
+ 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.`;
2621
2631
  }
2622
2632
  if (!existsSync8(resolved)) {
2623
2633
  return `Error: File not found: ${resolved}. Use create_file to create new files.`;
@@ -2648,7 +2658,8 @@ function createCreateFileTool(permissions) {
2648
2658
  const resolved = resolve4(path3);
2649
2659
  const check = await permissions.checkFsAccess(resolved, "write");
2650
2660
  if (!check.allowed) {
2651
- return `Error: ${check.reason}`;
2661
+ const parentDir = resolve4(resolved, "..");
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.`;
2652
2663
  }
2653
2664
  if (existsSync9(resolved)) {
2654
2665
  return `Error: File already exists: ${resolved}. Use write_file to modify existing files.`;
@@ -2682,7 +2693,7 @@ function createListDirTool(permissions) {
2682
2693
  const resolved = resolve5(path3);
2683
2694
  const check = await permissions.checkFsAccess(resolved, "read");
2684
2695
  if (!check.allowed) {
2685
- return `Error: ${check.reason}`;
2696
+ 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.`;
2686
2697
  }
2687
2698
  if (!existsSync10(resolved)) {
2688
2699
  return `Error: Directory not found: ${resolved}`;
@@ -2740,7 +2751,8 @@ function createDeleteFileTool(permissions) {
2740
2751
  const resolved = resolve6(path3);
2741
2752
  const check = await permissions.checkFsAccess(resolved, "write");
2742
2753
  if (!check.allowed) {
2743
- return `Error: ${check.reason}`;
2754
+ const parentDir = resolve6(resolved, "..");
2755
+ 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.`;
2744
2756
  }
2745
2757
  if (!existsSync11(resolved)) {
2746
2758
  return `Error: File not found: ${resolved}`;
@@ -2776,7 +2788,8 @@ function createEditFileTool(permissions) {
2776
2788
  const resolved = resolve7(path3);
2777
2789
  const fsCheck = await permissions.checkFsAccess(resolved, "write");
2778
2790
  if (!fsCheck.allowed) {
2779
- return `Error: ${fsCheck.reason}`;
2791
+ const parentDir = resolve7(resolved, "..");
2792
+ 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.`;
2780
2793
  }
2781
2794
  try {
2782
2795
  const content = readFileSync9(resolved, "utf-8");
@@ -2817,7 +2830,8 @@ function createSendFileTool(permissions, sendFile) {
2817
2830
  const resolved = resolve8(path3);
2818
2831
  const check = await permissions.checkFsAccess(resolved, "read");
2819
2832
  if (!check.allowed) {
2820
- return `Error: ${check.reason}`;
2833
+ const parentDir = resolve8(resolved, "..");
2834
+ 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.`;
2821
2835
  }
2822
2836
  if (!existsSync12(resolved)) {
2823
2837
  return `Error: File not found: ${resolved}`;
@@ -2841,18 +2855,40 @@ function createSendFileTool(permissions, sendFile) {
2841
2855
  });
2842
2856
  }
2843
2857
 
2844
- // src/capabilities/shell/run-command.ts
2858
+ // src/capabilities/filesystem/approve-scope.ts
2845
2859
  import { tool as tool8 } from "ai";
2846
2860
  import { z as z8 } from "zod";
2861
+ import { resolve as resolve9 } from "path";
2862
+ function createApproveScopeTool(permissions) {
2863
+ return tool8({
2864
+ description: "Request user approval to add a directory to the allowed filesystem scopes. Use this when a file operation fails due to scope permissions. The user will see an approval prompt (inline keyboard on Telegram, readline on CLI). If approved, the scope is added permanently.",
2865
+ parameters: z8.object({
2866
+ path: z8.string().describe("The directory path to add to allowed scopes"),
2867
+ mode: z8.enum(["read", "write"]).describe("The access mode needed")
2868
+ }),
2869
+ execute: async ({ path: path3, mode }) => {
2870
+ const resolved = resolve9(path3);
2871
+ const result = await permissions.requestScopeExternal(resolved, mode);
2872
+ if (result.allowed) {
2873
+ return `Access approved for ${mode} access to ${resolved}`;
2874
+ }
2875
+ return `Access denied for ${mode} access to ${resolved}`;
2876
+ }
2877
+ });
2878
+ }
2879
+
2880
+ // src/capabilities/shell/run-command.ts
2881
+ import { tool as tool9 } from "ai";
2882
+ import { z as z9 } from "zod";
2847
2883
  import { execSync } from "child_process";
2848
2884
  function createRunCommandTool(permissions) {
2849
- return tool8({
2885
+ return tool9({
2850
2886
  description: `Run a shell command. Commands run in the current working directory unless an absolute path is given.
2851
2887
  Blocked commands (sudo, rm -rf /, etc.) are never executed.
2852
2888
  Auto-approved commands (ls, cat, git status, curl, etc.) run without asking.
2853
2889
  Other commands require user approval \u2014 tell the user what command you want to run and ask for confirmation. If they say "yes", try again. If they say "always", use the approve_command tool.`,
2854
- parameters: z8.object({
2855
- command: z8.string().describe("The shell command to execute")
2890
+ parameters: z9.object({
2891
+ command: z9.string().describe("The shell command to execute")
2856
2892
  }),
2857
2893
  execute: async ({ command }) => {
2858
2894
  const check = await permissions.checkShellCommand(command);
@@ -2892,13 +2928,13 @@ Error: ${stderr}`;
2892
2928
  }
2893
2929
 
2894
2930
  // src/capabilities/shell/approve-command.ts
2895
- import { tool as tool9 } from "ai";
2896
- import { z as z9 } from "zod";
2931
+ import { tool as tool10 } from "ai";
2932
+ import { z as z10 } from "zod";
2897
2933
  function createApproveCommandTool(permissions) {
2898
- return tool9({
2934
+ return tool10({
2899
2935
  description: 'Permanently approve a command type so it runs without asking in the future. Use this when the user says "always" or "always approve" for a command. For example, if the user says "always approve curl", call this with command="curl".',
2900
- parameters: z9.object({
2901
- command: z9.string().describe('The base command to permanently approve (e.g. "curl", "docker", "npm")')
2936
+ parameters: z10.object({
2937
+ command: z10.string().describe('The base command to permanently approve (e.g. "curl", "docker", "npm")')
2902
2938
  }),
2903
2939
  execute: async ({ command }) => {
2904
2940
  const baseCmd = command.trim().split(/\s+/)[0];
@@ -2909,15 +2945,15 @@ function createApproveCommandTool(permissions) {
2909
2945
  }
2910
2946
 
2911
2947
  // src/capabilities/skills/install-skill.ts
2912
- import { tool as tool10 } from "ai";
2913
- import { z as z10 } from "zod";
2948
+ import { tool as tool11 } from "ai";
2949
+ import { z as z11 } from "zod";
2914
2950
  import { parse as parseYaml4 } from "yaml";
2915
2951
  function createInstallSkillTool(skillLoader) {
2916
- return tool10({
2952
+ return tool11({
2917
2953
  description: "Install a new skill by providing SKILL.md markdown content or a URL. The content must have YAML frontmatter (---) with at least name and description fields.",
2918
- parameters: z10.object({
2919
- content: z10.string().optional().describe("Raw SKILL.md markdown content with YAML frontmatter"),
2920
- url: z10.string().optional().describe("URL to fetch a SKILL.md from")
2954
+ parameters: z11.object({
2955
+ content: z11.string().optional().describe("Raw SKILL.md markdown content with YAML frontmatter"),
2956
+ url: z11.string().optional().describe("URL to fetch a SKILL.md from")
2921
2957
  }),
2922
2958
  execute: async ({ content, url }) => {
2923
2959
  let skillContent;
@@ -2957,12 +2993,12 @@ function createInstallSkillTool(skillLoader) {
2957
2993
  }
2958
2994
 
2959
2995
  // src/capabilities/skills/list-skills.ts
2960
- import { tool as tool11 } from "ai";
2961
- import { z as z11 } from "zod";
2996
+ import { tool as tool12 } from "ai";
2997
+ import { z as z12 } from "zod";
2962
2998
  function createListSkillsTool(skillLoader) {
2963
- return tool11({
2999
+ return tool12({
2964
3000
  description: "List all installed skills with their names and descriptions.",
2965
- parameters: z11.object({}),
3001
+ parameters: z12.object({}),
2966
3002
  execute: async () => {
2967
3003
  const skills = skillLoader.getDiscovered();
2968
3004
  if (skills.length === 0) {
@@ -2974,13 +3010,13 @@ function createListSkillsTool(skillLoader) {
2974
3010
  }
2975
3011
 
2976
3012
  // src/capabilities/skills/use-skill.ts
2977
- import { tool as tool12 } from "ai";
2978
- import { z as z12 } from "zod";
3013
+ import { tool as tool13 } from "ai";
3014
+ import { z as z13 } from "zod";
2979
3015
  function createUseSkillTool(skillLoader, permissions) {
2980
- return tool12({
3016
+ return tool13({
2981
3017
  description: "Load and invoke a skill by name. Returns the skill's full instructions which should be followed as guidance for the current task.",
2982
- parameters: z12.object({
2983
- name: z12.string().describe("Name of the skill to invoke")
3018
+ parameters: z13.object({
3019
+ name: z13.string().describe("Name of the skill to invoke")
2984
3020
  }),
2985
3021
  execute: async ({ name }) => {
2986
3022
  const skill = skillLoader.load(name);
@@ -3007,18 +3043,18 @@ Allowed tools: ${skill["allowed-tools"].join(", ")}`;
3007
3043
  }
3008
3044
 
3009
3045
  // src/capabilities/scheduler/schedule-task.ts
3010
- import { tool as tool13 } from "ai";
3011
- import { z as z13 } from "zod";
3046
+ import { tool as tool14 } from "ai";
3047
+ import { z as z14 } from "zod";
3012
3048
  import cron2 from "node-cron";
3013
3049
  function createScheduleTaskTool(scheduler, getContext) {
3014
- return tool13({
3050
+ return tool14({
3015
3051
  description: 'Schedule a task. Use "cron" for recurring tasks (e.g. "0 9 * * *" for daily at 9am) or "delay_seconds" for one-shot delayed tasks (e.g. 15 for "remind me in 15 seconds"). Provide exactly one of cron or delay_seconds.',
3016
- parameters: z13.object({
3017
- cron: z13.string().optional().describe('Cron expression for recurring tasks (e.g. "0 9 * * *" for daily at 9am)'),
3018
- delay_seconds: z13.number().optional().describe('Delay in seconds for one-shot tasks (e.g. 15 for "remind me in 15 seconds")'),
3019
- description: z13.string().describe("Human-readable description of what this task does"),
3020
- prompt: z13.string().optional().describe("Prompt to send to the agent when the task fires"),
3021
- skill_name: z13.string().optional().describe("Name of a skill to invoke when the task fires")
3052
+ parameters: z14.object({
3053
+ cron: z14.string().optional().describe('Cron expression for recurring tasks (e.g. "0 9 * * *" for daily at 9am)'),
3054
+ delay_seconds: z14.number().optional().describe('Delay in seconds for one-shot tasks (e.g. 15 for "remind me in 15 seconds")'),
3055
+ description: z14.string().describe("Human-readable description of what this task does"),
3056
+ prompt: z14.string().optional().describe("Prompt to send to the agent when the task fires"),
3057
+ skill_name: z14.string().optional().describe("Name of a skill to invoke when the task fires")
3022
3058
  }),
3023
3059
  execute: async ({ cron: cronExpr, delay_seconds, description, prompt, skill_name }) => {
3024
3060
  if (!cronExpr && !delay_seconds) {
@@ -3071,12 +3107,12 @@ function createScheduleTaskTool(scheduler, getContext) {
3071
3107
  }
3072
3108
 
3073
3109
  // src/capabilities/scheduler/list-tasks.ts
3074
- import { tool as tool14 } from "ai";
3075
- import { z as z14 } from "zod";
3110
+ import { tool as tool15 } from "ai";
3111
+ import { z as z15 } from "zod";
3076
3112
  function createListTasksTool(scheduler) {
3077
- return tool14({
3113
+ return tool15({
3078
3114
  description: "List all scheduled tasks with their cron expressions and descriptions.",
3079
- parameters: z14.object({}),
3115
+ parameters: z15.object({}),
3080
3116
  execute: async () => {
3081
3117
  const manifests = scheduler.getManifests();
3082
3118
  if (manifests.length === 0) {
@@ -3091,13 +3127,13 @@ function createListTasksTool(scheduler) {
3091
3127
  }
3092
3128
 
3093
3129
  // src/capabilities/scheduler/cancel-task.ts
3094
- import { tool as tool15 } from "ai";
3095
- import { z as z15 } from "zod";
3130
+ import { tool as tool16 } from "ai";
3131
+ import { z as z16 } from "zod";
3096
3132
  function createCancelTaskTool(scheduler) {
3097
- return tool15({
3133
+ return tool16({
3098
3134
  description: "Cancel and remove a scheduled task by its ID.",
3099
- parameters: z15.object({
3100
- id: z15.string().describe("ID of the scheduled task to cancel")
3135
+ parameters: z16.object({
3136
+ id: z16.string().describe("ID of the scheduled task to cancel")
3101
3137
  }),
3102
3138
  execute: async ({ id }) => {
3103
3139
  const manifests = scheduler.getManifests();
@@ -3113,12 +3149,12 @@ function createCancelTaskTool(scheduler) {
3113
3149
  }
3114
3150
 
3115
3151
  // src/capabilities/system/budget-status.ts
3116
- import { tool as tool16 } from "ai";
3117
- import { z as z16 } from "zod";
3152
+ import { tool as tool17 } from "ai";
3153
+ import { z as z17 } from "zod";
3118
3154
  function createBudgetStatusTool(tokenBudget) {
3119
- return tool16({
3155
+ return tool17({
3120
3156
  description: "Check the current token budget status \u2014 how many tokens have been used today, how many remain, and what percentage is consumed.",
3121
- parameters: z16.object({}),
3157
+ parameters: z17.object({}),
3122
3158
  execute: async () => {
3123
3159
  return tokenBudget.getStatusText();
3124
3160
  }
@@ -3126,14 +3162,14 @@ function createBudgetStatusTool(tokenBudget) {
3126
3162
  }
3127
3163
 
3128
3164
  // src/capabilities/git/git-status.ts
3129
- import { tool as tool17 } from "ai";
3130
- import { z as z17 } from "zod";
3165
+ import { tool as tool18 } from "ai";
3166
+ import { z as z18 } from "zod";
3131
3167
  import { execSync as execSync2 } from "child_process";
3132
3168
  function createGitStatusTool() {
3133
- return tool17({
3169
+ return tool18({
3134
3170
  description: "Show the working tree status. Returns staged, unstaged, and untracked files.",
3135
- parameters: z17.object({
3136
- path: z17.string().optional().describe("Path to check (defaults to current directory)")
3171
+ parameters: z18.object({
3172
+ path: z18.string().optional().describe("Path to check (defaults to current directory)")
3137
3173
  }),
3138
3174
  execute: async ({ path: path3 }) => {
3139
3175
  try {
@@ -3149,15 +3185,15 @@ function createGitStatusTool() {
3149
3185
  }
3150
3186
 
3151
3187
  // src/capabilities/git/git-diff.ts
3152
- import { tool as tool18 } from "ai";
3153
- import { z as z18 } from "zod";
3188
+ import { tool as tool19 } from "ai";
3189
+ import { z as z19 } from "zod";
3154
3190
  import { execSync as execSync3 } from "child_process";
3155
3191
  function createGitDiffTool() {
3156
- return tool18({
3192
+ return tool19({
3157
3193
  description: "Show changes between commits, commit and working tree, etc. Shows what has been modified.",
3158
- parameters: z18.object({
3159
- path: z18.string().optional().describe("File or directory to diff"),
3160
- staged: z18.boolean().optional().describe("Show staged changes (cached) instead of unstaged")
3194
+ parameters: z19.object({
3195
+ path: z19.string().optional().describe("File or directory to diff"),
3196
+ staged: z19.boolean().optional().describe("Show staged changes (cached) instead of unstaged")
3161
3197
  }),
3162
3198
  execute: async ({ path: path3, staged }) => {
3163
3199
  try {
@@ -3176,15 +3212,15 @@ function createGitDiffTool() {
3176
3212
  }
3177
3213
 
3178
3214
  // src/capabilities/git/git-log.ts
3179
- import { tool as tool19 } from "ai";
3180
- import { z as z19 } from "zod";
3215
+ import { tool as tool20 } from "ai";
3216
+ import { z as z20 } from "zod";
3181
3217
  import { execSync as execSync4 } from "child_process";
3182
3218
  function createGitLogTool() {
3183
- return tool19({
3219
+ return tool20({
3184
3220
  description: "Show commit logs. Returns recent commit history with hash, author, date, and message.",
3185
- parameters: z19.object({
3186
- count: z19.number().optional().describe("Number of commits to show (default 10)"),
3187
- path: z19.string().optional().describe("File or directory to show log for")
3221
+ parameters: z20.object({
3222
+ count: z20.number().optional().describe("Number of commits to show (default 10)"),
3223
+ path: z20.string().optional().describe("File or directory to show log for")
3188
3224
  }),
3189
3225
  execute: async ({ count, path: path3 }) => {
3190
3226
  try {
@@ -3202,14 +3238,14 @@ function createGitLogTool() {
3202
3238
  }
3203
3239
 
3204
3240
  // src/capabilities/git/git-add.ts
3205
- import { tool as tool20 } from "ai";
3206
- import { z as z20 } from "zod";
3241
+ import { tool as tool21 } from "ai";
3242
+ import { z as z21 } from "zod";
3207
3243
  import { execSync as execSync5 } from "child_process";
3208
3244
  function createGitAddTool() {
3209
- return tool20({
3245
+ return tool21({
3210
3246
  description: "Add file contents to the index (staging area). Prepares files for commit.",
3211
- parameters: z20.object({
3212
- paths: z20.array(z20.string()).describe("File paths to stage")
3247
+ parameters: z21.object({
3248
+ paths: z21.array(z21.string()).describe("File paths to stage")
3213
3249
  }),
3214
3250
  execute: async ({ paths }) => {
3215
3251
  try {
@@ -3224,14 +3260,14 @@ function createGitAddTool() {
3224
3260
  }
3225
3261
 
3226
3262
  // src/capabilities/git/git-commit.ts
3227
- import { tool as tool21 } from "ai";
3228
- import { z as z21 } from "zod";
3263
+ import { tool as tool22 } from "ai";
3264
+ import { z as z22 } from "zod";
3229
3265
  import { execSync as execSync6 } from "child_process";
3230
3266
  function createGitCommitTool() {
3231
- return tool21({
3267
+ return tool22({
3232
3268
  description: "Record changes to the repository. Creates a new commit with staged changes.",
3233
- parameters: z21.object({
3234
- message: z21.string().describe("Commit message")
3269
+ parameters: z22.object({
3270
+ message: z22.string().describe("Commit message")
3235
3271
  }),
3236
3272
  execute: async ({ message }) => {
3237
3273
  try {
@@ -3250,15 +3286,15 @@ function createGitCommitTool() {
3250
3286
  }
3251
3287
 
3252
3288
  // src/capabilities/git/git-push.ts
3253
- import { tool as tool22 } from "ai";
3254
- import { z as z22 } from "zod";
3289
+ import { tool as tool23 } from "ai";
3290
+ import { z as z23 } from "zod";
3255
3291
  import { execSync as execSync7 } from "child_process";
3256
3292
  function createGitPushTool(permissions) {
3257
- return tool22({
3293
+ return tool23({
3258
3294
  description: "Push commits to a remote repository. This modifies a remote and requires approval.",
3259
- parameters: z22.object({
3260
- remote: z22.string().optional().describe("Remote name (default: origin)"),
3261
- branch: z22.string().optional().describe("Branch name (default: current branch)")
3295
+ parameters: z23.object({
3296
+ remote: z23.string().optional().describe("Remote name (default: origin)"),
3297
+ branch: z23.string().optional().describe("Branch name (default: current branch)")
3262
3298
  }),
3263
3299
  execute: async ({ remote, branch }) => {
3264
3300
  const cmd = `git push ${remote || "origin"} ${branch || ""}`.trim();
@@ -3283,8 +3319,8 @@ Ask the user for permission. If they approve, try again. If they say "always", u
3283
3319
  }
3284
3320
 
3285
3321
  // src/capabilities/web/fetch-url.ts
3286
- import { tool as tool23 } from "ai";
3287
- import { z as z23 } from "zod";
3322
+ import { tool as tool24 } from "ai";
3323
+ import { z as z24 } from "zod";
3288
3324
  var MAX_CONTENT_LENGTH = 15e3;
3289
3325
  function stripHtml(html) {
3290
3326
  let text = html;
@@ -3317,11 +3353,11 @@ function stripHtml(html) {
3317
3353
  return text;
3318
3354
  }
3319
3355
  function createFetchUrlTool() {
3320
- return tool23({
3356
+ return tool24({
3321
3357
  description: "Fetch a URL and return its content as text. Strips HTML to readable markdown-like format. Useful for reading documentation, APIs, or web pages.",
3322
- parameters: z23.object({
3323
- url: z23.string().describe("The URL to fetch"),
3324
- format: z23.enum(["text", "markdown"]).optional().describe("Output format (default: markdown)")
3358
+ parameters: z24.object({
3359
+ url: z24.string().describe("The URL to fetch"),
3360
+ format: z24.enum(["text", "markdown"]).optional().describe("Output format (default: markdown)")
3325
3361
  }),
3326
3362
  execute: async ({ url, format }) => {
3327
3363
  const outputFormat = format ?? "markdown";
@@ -3410,6 +3446,7 @@ var CapabilityRegistry = class {
3410
3446
  if (this.sendFileHandler) {
3411
3447
  this.tools.send_file = createSendFileTool(this.permissions, this.sendFileHandler);
3412
3448
  }
3449
+ this.tools.approve_scope = createApproveScopeTool(this.permissions);
3413
3450
  logger.info("Filesystem tools registered");
3414
3451
  }
3415
3452
  if (manifest.capabilities.shell.enabled) {
@@ -4254,10 +4291,12 @@ async function runWithWatchdog(agentFn) {
4254
4291
  await attempt();
4255
4292
  }
4256
4293
  function sleep(ms) {
4257
- return new Promise((resolve9) => setTimeout(resolve9, ms));
4294
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
4258
4295
  }
4259
4296
 
4260
4297
  // src/index.ts
4298
+ var __dirname = dirname3(fileURLToPath(import.meta.url));
4299
+ var pkgVersion = JSON.parse(readFileSync12(join11(__dirname, "..", "package.json"), "utf8")).version;
4261
4300
  function hr() {
4262
4301
  console.log(chalk6.dim("\u2500".repeat(50)));
4263
4302
  }
@@ -4269,7 +4308,7 @@ function banner() {
4269
4308
  }
4270
4309
  console.log("");
4271
4310
  console.log(chalk6.white(" an AI agent for personal tasks"));
4272
- console.log(chalk6.dim(" v0.2.3 \xB7 by Cosmic Stack \xB7 mercury.cosmicstack.org"));
4311
+ console.log(chalk6.dim(` v${pkgVersion} \xB7 by Cosmic Stack \xB7 mercury.cosmicstack.org`));
4273
4312
  console.log("");
4274
4313
  }
4275
4314
  function splashScreen() {
@@ -4286,10 +4325,10 @@ function splashScreen() {
4286
4325
  }
4287
4326
  async function ask(prompt) {
4288
4327
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4289
- return new Promise((resolve9) => {
4328
+ return new Promise((resolve10) => {
4290
4329
  rl.question(prompt, (answer) => {
4291
4330
  rl.close();
4292
- resolve9(answer.trim());
4331
+ resolve10(answer.trim());
4293
4332
  });
4294
4333
  });
4295
4334
  }
@@ -4555,7 +4594,7 @@ async function runAgent(isDaemon = false) {
4555
4594
  process.on("SIGTERM", shutdown);
4556
4595
  }
4557
4596
  var program = new Command();
4558
- program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with permission-hardened tools, token budgets, and multi-channel access.").version("0.2.3").option("-v, --verbose", "Show debug logs").action(async () => {
4597
+ program.name("mercury").description("Mercury \u2014 Soul-driven AI agent with permission-hardened tools, token budgets, and multi-channel access.").version(pkgVersion).option("-v, --verbose", "Show debug logs").action(async () => {
4559
4598
  if (!isSetupComplete()) {
4560
4599
  await configure();
4561
4600
  autoDaemonize();