@agent-commons/cli 0.1.5 → 0.1.7

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.
Files changed (2) hide show
  1. package/dist/bin.js +748 -47
  2. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -25,10 +25,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/bin.ts
27
27
  var import_commander16 = require("commander");
28
+ var import_path5 = require("path");
29
+ var import_os4 = require("os");
30
+ var import_child_process3 = require("child_process");
28
31
 
29
32
  // src/commands/login.ts
30
33
  var import_commander = require("commander");
31
34
  var readline = __toESM(require("readline"));
35
+ var import_fs2 = require("fs");
36
+ var import_path2 = require("path");
37
+ var import_os2 = require("os");
32
38
 
33
39
  // src/config.ts
34
40
  var import_fs = require("fs");
@@ -37,7 +43,8 @@ var import_path = require("path");
37
43
  var import_sdk = require("@agent-commons/sdk");
38
44
  var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".agc");
39
45
  var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
40
- var DEFAULT_API_URL = process.env.AGC_API_URL ?? "http://localhost:3001";
46
+ var DEFAULT_API_URL = process.env.AGC_API_URL ?? "https://api.agentcommons.io";
47
+ var DEFAULT_APP_URL = "https://www.agentcommons.io";
41
48
  function loadConfig() {
42
49
  const fromEnv = {
43
50
  ...process.env.AGC_API_URL && { apiUrl: process.env.AGC_API_URL },
@@ -81,6 +88,7 @@ function makeClient(overrides) {
81
88
  // src/ui.ts
82
89
  var import_chalk = __toESM(require("chalk"));
83
90
  var import_ora = __toESM(require("ora"));
91
+ var import_child_process = require("child_process");
84
92
  var c = {
85
93
  primary: (s) => import_chalk.default.cyan(s),
86
94
  success: (s) => import_chalk.default.green(s),
@@ -98,6 +106,80 @@ var sym = {
98
106
  bullet: import_chalk.default.dim("\u2022"),
99
107
  dot: import_chalk.default.dim("\xB7")
100
108
  };
109
+ function banner(version = "0.1.4") {
110
+ const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
111
+ console.log("");
112
+ console.log(line);
113
+ console.log(
114
+ import_chalk.default.cyan(" \u2502 ") + import_chalk.default.bold.white(" \u25C8 Agent Commons") + import_chalk.default.dim(" \xB7 CLI") + " " + import_chalk.default.cyan(`v${version}`)
115
+ );
116
+ console.log(import_chalk.default.cyan(" \u2502 ") + import_chalk.default.dim(" The Open AI Agent Network \xB7 agentcommons.io"));
117
+ console.log(line);
118
+ console.log("");
119
+ }
120
+ function step(n, total, title) {
121
+ const fraction = import_chalk.default.dim(`${n}/${total}`);
122
+ console.log(`
123
+ ${import_chalk.default.cyan.bold(" Step " + n)} ${fraction} ${import_chalk.default.bold(title)}`);
124
+ console.log(import_chalk.default.dim(" " + "\u2500".repeat(38)));
125
+ }
126
+ async function select(prompt2, choices) {
127
+ if (!process.stdin.isTTY) {
128
+ return choices[0].value;
129
+ }
130
+ let idx = 0;
131
+ const total = choices.length;
132
+ const render = (first = false) => {
133
+ if (!first) {
134
+ process.stdout.write(`\x1B[${total + 2}A\x1B[0J`);
135
+ }
136
+ console.log("\n" + import_chalk.default.bold(" " + prompt2));
137
+ for (let i = 0; i < total; i++) {
138
+ const { label, hint } = choices[i];
139
+ if (i === idx) {
140
+ const hintStr = hint ? import_chalk.default.dim(" " + hint) : "";
141
+ process.stdout.write(import_chalk.default.cyan(" \u203A ") + import_chalk.default.bold.white(label) + hintStr + "\n");
142
+ } else {
143
+ process.stdout.write(import_chalk.default.dim(" " + label) + "\n");
144
+ }
145
+ }
146
+ };
147
+ render(true);
148
+ return new Promise((resolve2) => {
149
+ process.stdin.setRawMode(true);
150
+ process.stdin.resume();
151
+ process.stdin.setEncoding("utf8");
152
+ const handler = (data) => {
153
+ const key = String(data);
154
+ if (key === "\x1B[A" || key === "k") {
155
+ idx = (idx - 1 + total) % total;
156
+ render();
157
+ } else if (key === "\x1B[B" || key === "j") {
158
+ idx = (idx + 1) % total;
159
+ render();
160
+ } else if (key === "\r" || key === "\n" || key === " ") {
161
+ cleanup();
162
+ process.stdout.write("\n");
163
+ resolve2(choices[idx].value);
164
+ } else if (key === "") {
165
+ cleanup();
166
+ process.stdout.write("\n");
167
+ process.exit(130);
168
+ }
169
+ };
170
+ const cleanup = () => {
171
+ process.stdin.removeListener("data", handler);
172
+ process.stdin.setRawMode(false);
173
+ process.stdin.pause();
174
+ };
175
+ process.stdin.on("data", handler);
176
+ });
177
+ }
178
+ function openBrowser(url) {
179
+ const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
180
+ (0, import_child_process.exec)(cmd, () => {
181
+ });
182
+ }
101
183
  function spin(text) {
102
184
  return (0, import_ora.default)({ text, color: "cyan" }).start();
103
185
  }
@@ -174,8 +256,9 @@ function statusBadge(status) {
174
256
  }
175
257
 
176
258
  // src/commands/login.ts
259
+ var CONFIG_FILE2 = (0, import_path2.join)((0, import_os2.homedir)(), ".agc", "config.json");
177
260
  function prompt(question, hidden = false) {
178
- return new Promise((resolve) => {
261
+ return new Promise((resolve2) => {
179
262
  const rl = readline.createInterface({
180
263
  input: process.stdin,
181
264
  output: hidden ? void 0 : process.stdout,
@@ -186,42 +269,97 @@ function prompt(question, hidden = false) {
186
269
  process.stdin.once("data", (data) => {
187
270
  process.stdout.write("\n");
188
271
  rl.close();
189
- resolve(data.toString().trim());
272
+ resolve2(data.toString().trim());
190
273
  });
191
274
  process.stdin.setRawMode?.(false);
192
275
  } else {
193
276
  rl.question(question, (ans) => {
194
277
  rl.close();
195
- resolve(ans.trim());
278
+ resolve2(ans.trim());
196
279
  });
197
280
  }
198
281
  });
199
282
  }
200
283
  function loginCommand() {
201
284
  const cmd = new import_commander.Command("login").description("Configure API credentials");
202
- cmd.option("--api-url <url>", "API base URL", DEFAULT_API_URL).option("--api-key <key>", "API key (or set AGC_API_KEY env var)").option("--initiator <id>", "Default initiator ID (wallet address or user ID)").action(async (opts) => {
285
+ cmd.option("--api-url <url>", "API base URL", DEFAULT_API_URL).option("--api-key <key>", "API key (or set AGC_API_KEY env var)").option("--initiator <id>", "User/initiator ID (advanced \u2014 usually auto-detected)").action(async (opts) => {
203
286
  try {
204
287
  const current = loadConfig();
205
- const apiUrl = opts.apiUrl !== DEFAULT_API_URL ? opts.apiUrl : await prompt(`API URL [${current.apiUrl ?? DEFAULT_API_URL}]: `) || (current.apiUrl ?? DEFAULT_API_URL);
206
- const appUrl = apiUrl.includes("localhost") ? "http://localhost:3000" : apiUrl.replace(/\/api$/, "").replace("api.", "").replace(":3001", ":3000");
288
+ const isFirstRun = !(0, import_fs2.existsSync)(CONFIG_FILE2);
289
+ banner();
290
+ if (isFirstRun) {
291
+ console.log(c.bold(" Welcome to Agent Commons CLI!"));
292
+ console.log(c.dim(" You just need an API key to get started.\n"));
293
+ } else {
294
+ console.log(c.bold(" Update your credentials"));
295
+ console.log(c.dim(" Press Enter to keep existing values.\n"));
296
+ }
297
+ let apiUrl;
298
+ if (opts.apiUrl !== DEFAULT_API_URL) {
299
+ apiUrl = opts.apiUrl;
300
+ console.log(` ${c.dim("Using API endpoint:")} ${apiUrl}
301
+ `);
302
+ } else if (current.apiUrl && current.apiUrl !== DEFAULT_API_URL) {
303
+ apiUrl = current.apiUrl;
304
+ console.log(` ${c.dim("Using existing endpoint:")} ${apiUrl}
305
+ `);
306
+ } else {
307
+ apiUrl = DEFAULT_API_URL;
308
+ }
309
+ const appUrl = apiUrl.includes("localhost") ? "http://localhost:3000" : DEFAULT_APP_URL;
310
+ const apiKeysUrl = `${appUrl}/settings/api-keys`;
311
+ step(1, 1, "API Key");
207
312
  let apiKey = opts.apiKey;
208
313
  if (!apiKey) {
209
- console.log(`
210
- Generate an API key at:
211
- ${c.bold(`${appUrl}/settings/api-keys`)}
314
+ console.log(` ${c.dim("You'll need an API key from your Agent Commons account.")}`);
315
+ console.log(` ${c.dim("We'll open the API Keys page in your browser.")}
212
316
  `);
213
- apiKey = await prompt(`API Key (sk-ac-...): `);
317
+ console.log(` ${c.dim("On that page:")}`);
318
+ console.log(` ${sym.bullet} ${c.dim("Click")} ${c.bold('"Generate new key"')}`);
319
+ console.log(` ${sym.bullet} ${c.dim("Copy the key (it starts with")} ${c.bold("sk-ac-\u2026")}${c.dim(")")}`);
320
+ console.log(` ${sym.bullet} ${c.dim("Paste it here when prompted")}
321
+ `);
322
+ const openNow = await prompt(` ${c.dim("Open browser now? [Y/n]:")} `);
323
+ if (!openNow || openNow.toLowerCase() !== "n") {
324
+ openBrowser(apiKeysUrl);
325
+ console.log(` ${sym.ok} ${c.dim("Opened:")} ${c.primary(apiKeysUrl)}
326
+ `);
327
+ } else {
328
+ console.log(` ${c.dim("You can open it manually:")} ${c.primary(apiKeysUrl)}
329
+ `);
330
+ }
331
+ console.log(c.dim(" Paste your API key below (input is hidden):"));
332
+ apiKey = await prompt(` ${c.dim("API Key:")} `, true);
214
333
  if (!apiKey) apiKey = current.apiKey;
215
334
  }
216
- let initiator = opts.initiator;
217
- if (!initiator) {
218
- initiator = await prompt(`Wallet address (0x...): `);
219
- if (!initiator) initiator = current.initiator;
335
+ if (!apiKey) {
336
+ console.log(`
337
+ ${c.warn("\u26A0")} No API key provided \u2014 set one later with ${c.bold("agc config set apiKey <key>")}`);
338
+ } else {
339
+ console.log(` ${sym.ok} ${c.dim("Key saved:")} ****${apiKey.slice(-4)}`);
220
340
  }
221
- saveConfig({ apiUrl, apiKey, initiator });
341
+ let initiator = opts.initiator ?? current.initiator;
342
+ if (!initiator && apiKey) {
343
+ try {
344
+ const { CommonsClient: CommonsClient2 } = await import("@agent-commons/sdk");
345
+ const client = new CommonsClient2({ baseUrl: apiUrl, apiKey });
346
+ const me = await client.auth.me();
347
+ if (me?.principalId && me.principalType === "user") {
348
+ initiator = me.principalId;
349
+ console.log(` ${sym.ok} ${c.dim("Identity detected:")} ${c.id(initiator.slice(0, 10) + "\u2026" + initiator.slice(-6))}`);
350
+ }
351
+ } catch {
352
+ }
353
+ }
354
+ saveConfig({ apiUrl, apiKey, ...initiator ? { initiator } : {} });
222
355
  console.log(`
223
- ${sym.ok} Credentials saved to ~/.agc/config.json`);
224
- console.log(c.dim(" Run `agc whoami` to verify the connection."));
356
+ ${sym.ok} ${c.success("All set!")} Credentials saved to ${c.dim("~/.agc/config.json")}`);
357
+ console.log(`
358
+ ${c.dim("Next steps:")}`);
359
+ console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc")} ${c.dim("to open the interactive menu")}`);
360
+ console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc agents list")} ${c.dim("to see your agents")}`);
361
+ console.log(` ${sym.arrow} ${c.dim("Run")} ${c.bold("agc chat")} ${c.dim("to start chatting with an agent")}
362
+ `);
225
363
  } catch (err) {
226
364
  printError(err);
227
365
  process.exit(1);
@@ -299,10 +437,6 @@ function agentsCommand() {
299
437
  const cmd = new import_commander2.Command("agents").description("Manage agents");
300
438
  cmd.command("list").description("List agents owned by the current initiator").option("--json", "Output as JSON").action(async (opts) => {
301
439
  const cfg = loadConfig();
302
- if (!cfg.initiator) {
303
- console.error(c.error("No initiator set. Run `agc login` first."));
304
- process.exit(1);
305
- }
306
440
  const spinner = spin("Fetching agents\u2026");
307
441
  try {
308
442
  const client = makeClient();
@@ -739,12 +873,12 @@ ${sym.ok} Execution started: ${c.id(execution.executionId)}`);
739
873
  const steps = execution.stepResults ?? execution.nodeResults;
740
874
  if (steps && Object.keys(steps).length > 0) {
741
875
  console.log("\n" + c.label("Step Results"));
742
- for (const [nodeId, step] of Object.entries(steps)) {
743
- const icon = step.status === "success" ? sym.ok : step.status === "error" ? sym.fail : "\xB7";
744
- const dur = step.duration != null ? c.dim(` (${(step.duration / 1e3).toFixed(2)}s)`) : "";
876
+ for (const [nodeId, step2] of Object.entries(steps)) {
877
+ const icon = step2.status === "success" ? sym.ok : step2.status === "error" ? sym.fail : "\xB7";
878
+ const dur = step2.duration != null ? c.dim(` (${(step2.duration / 1e3).toFixed(2)}s)`) : "";
745
879
  console.log(` ${icon} ${c.id(nodeId)}${dur}`);
746
- if (step.error) console.log(` ${c.error(step.error)}`);
747
- else if (step.output !== void 0) console.log(` ${JSON.stringify(step.output, null, 2).replace(/\n/g, "\n ")}`);
880
+ if (step2.error) console.log(` ${c.error(step2.error)}`);
881
+ else if (step2.output !== void 0) console.log(` ${JSON.stringify(step2.output, null, 2).replace(/\n/g, "\n ")}`);
748
882
  }
749
883
  }
750
884
  } else {
@@ -769,12 +903,12 @@ ${sym.ok} ${c.success("Completed")}`);
769
903
  }
770
904
  if (e.nodeResults && Object.keys(e.nodeResults).length > 0) {
771
905
  console.log("\n" + c.label("Step Results"));
772
- for (const [nodeId, step] of Object.entries(e.nodeResults)) {
773
- const icon = step.status === "success" ? sym.ok : step.status === "error" ? sym.fail : "\xB7";
774
- const dur = step.duration != null ? c.dim(` (${(step.duration / 1e3).toFixed(2)}s)`) : "";
906
+ for (const [nodeId, step2] of Object.entries(e.nodeResults)) {
907
+ const icon = step2.status === "success" ? sym.ok : step2.status === "error" ? sym.fail : "\xB7";
908
+ const dur = step2.duration != null ? c.dim(` (${(step2.duration / 1e3).toFixed(2)}s)`) : "";
775
909
  console.log(` ${icon} ${c.id(nodeId)}${dur}`);
776
- if (step.error) console.log(` ${c.error(step.error)}`);
777
- else if (step.output !== void 0) console.log(` ${JSON.stringify(step.output, null, 2).replace(/\n/g, "\n ")}`);
910
+ if (step2.error) console.log(` ${c.error(step2.error)}`);
911
+ else if (step2.output !== void 0) console.log(` ${JSON.stringify(step2.output, null, 2).replace(/\n/g, "\n ")}`);
778
912
  }
779
913
  }
780
914
  break;
@@ -1113,16 +1247,301 @@ ${sym.fail} ${c.error(event.message ?? "Error")}`);
1113
1247
 
1114
1248
  // src/commands/chat.ts
1115
1249
  var import_commander8 = require("commander");
1250
+ var readline3 = __toESM(require("readline"));
1251
+ var import_fs4 = require("fs");
1252
+ var import_path4 = require("path");
1253
+ var import_os3 = require("os");
1254
+
1255
+ // src/local-tools.ts
1256
+ var import_fs3 = require("fs");
1257
+ var import_path3 = require("path");
1258
+ var import_child_process2 = require("child_process");
1116
1259
  var readline2 = __toESM(require("readline"));
1260
+ function buildLocalToolsManifest(rootDir) {
1261
+ return `## Local File System Access
1262
+
1263
+ You have direct access to the user's local machine file system. Use these tools freely to complete tasks \u2014 do not ask the user to run commands themselves.
1264
+
1265
+ **Session root:** ${rootDir}
1266
+ All paths are relative to the session root unless absolute.
1267
+
1268
+ ---
1269
+
1270
+ ### How to call a tool
1271
+
1272
+ When you need to use a local tool, output ONLY the following JSON block \u2014 nothing else in that message. After receiving the result, continue your response:
1273
+
1274
+ \`\`\`tool
1275
+ {"tool": "<tool_name>", "args": {"<arg>": "<value>"}}
1276
+ \`\`\`
1277
+
1278
+ You may call tools multiple times in sequence. Each call will be executed and the result returned to you before you continue.
1279
+
1280
+ ---
1281
+
1282
+ ### Available tools
1283
+
1284
+ **read_file** \u2014 Read the full contents of a file.
1285
+ \`\`\`tool
1286
+ {"tool": "read_file", "args": {"path": "src/index.ts"}}
1287
+ \`\`\`
1288
+
1289
+ **write_file** \u2014 Write content to a file (creates directories as needed). User must confirm.
1290
+ \`\`\`tool
1291
+ {"tool": "write_file", "args": {"path": "output.txt", "content": "Hello world"}}
1292
+ \`\`\`
1293
+
1294
+ **list_directory** \u2014 List files and directories at a path. Defaults to session root.
1295
+ \`\`\`tool
1296
+ {"tool": "list_directory", "args": {"path": "src"}}
1297
+ \`\`\`
1298
+
1299
+ **search_files** \u2014 Find files matching a name/path pattern (glob-style, up to 50 results).
1300
+ \`\`\`tool
1301
+ {"tool": "search_files", "args": {"pattern": "*.ts", "directory": "src"}}
1302
+ \`\`\`
1303
+
1304
+ **run_command** \u2014 Execute a shell command and return stdout/stderr. User must confirm. 30s timeout.
1305
+ \`\`\`tool
1306
+ {"tool": "run_command", "args": {"command": "node", "args": ["--version"]}}
1307
+ \`\`\`
1308
+
1309
+ ---
1310
+
1311
+ **Important:**
1312
+ - Never fabricate tool results. Always wait for the actual output.
1313
+ - Sensitive paths (.ssh, .env, .aws, credentials) are blocked by the system.
1314
+ - Write and run_command operations require explicit user approval before executing.
1315
+ `;
1316
+ }
1317
+ var TOOL_CALL_RE = /```tool\s*\n([\s\S]*?)\n```/;
1318
+ function extractToolCall(text) {
1319
+ const match = text.match(TOOL_CALL_RE);
1320
+ if (!match) return null;
1321
+ try {
1322
+ const parsed = JSON.parse(match[1].trim());
1323
+ if (typeof parsed.tool === "string") return parsed;
1324
+ } catch {
1325
+ }
1326
+ return null;
1327
+ }
1328
+ function safePath(root, userPath) {
1329
+ const abs = (0, import_path3.resolve)(root, userPath);
1330
+ const rel = (0, import_path3.relative)(root, abs);
1331
+ if (rel.startsWith("..") || rel.startsWith("/")) {
1332
+ throw new Error(`Path "${userPath}" escapes the session root. Access denied.`);
1333
+ }
1334
+ return abs;
1335
+ }
1336
+ var BLOCKED_PATTERNS = [
1337
+ /\/\.ssh\//,
1338
+ /\/\.gnupg\//,
1339
+ /\/\.agc\//,
1340
+ /\/\.aws\//,
1341
+ /\/\.env$/,
1342
+ /\/\.env\./,
1343
+ /id_rsa/,
1344
+ /id_ed25519/
1345
+ ];
1346
+ function assertNotSensitive(abs) {
1347
+ for (const pat of BLOCKED_PATTERNS) {
1348
+ if (pat.test(abs)) {
1349
+ throw new Error(`Access to "${abs}" is blocked for security reasons.`);
1350
+ }
1351
+ }
1352
+ }
1353
+ async function confirm(message, config, permissionKey) {
1354
+ const cached = config.permissions.get(permissionKey);
1355
+ if (cached === "allow") return true;
1356
+ if (cached === "deny") return false;
1357
+ return new Promise((resolve2) => {
1358
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
1359
+ process.stdout.write(
1360
+ `
1361
+ \x1B[33m\u26A0\x1B[0m ${message}
1362
+ \x1B[2m[y] Yes [n] No [A] Always allow this type [N] Never allow this type\x1B[0m
1363
+ \x1B[36m?\x1B[0m `
1364
+ );
1365
+ rl.once("line", (answer) => {
1366
+ rl.close();
1367
+ const a = answer.trim().toLowerCase();
1368
+ if (a === "a") {
1369
+ config.permissions.set(permissionKey, "allow");
1370
+ resolve2(true);
1371
+ } else if (a === "n" || a === "nn") {
1372
+ config.permissions.set(permissionKey, "deny");
1373
+ resolve2(false);
1374
+ } else {
1375
+ resolve2(a === "y" || a === "yes" || a === "");
1376
+ }
1377
+ });
1378
+ });
1379
+ }
1380
+ async function toolReadFile(args, cfg) {
1381
+ const { path: userPath } = args;
1382
+ if (!userPath) throw new Error('read_file requires a "path" argument');
1383
+ const abs = safePath(cfg.rootDir, userPath);
1384
+ assertNotSensitive(abs);
1385
+ if (!(0, import_fs3.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
1386
+ const stat = (0, import_fs3.statSync)(abs);
1387
+ if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
1388
+ if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
1389
+ return (0, import_fs3.readFileSync)(abs, "utf8");
1390
+ }
1391
+ async function toolWriteFile(args, cfg) {
1392
+ const { path: userPath, content } = args;
1393
+ if (!userPath) throw new Error('write_file requires a "path" argument');
1394
+ if (content === void 0) throw new Error('write_file requires a "content" argument');
1395
+ const abs = safePath(cfg.rootDir, userPath);
1396
+ assertNotSensitive(abs);
1397
+ const ok = await confirm(
1398
+ `Agent wants to write file: \x1B[1m${abs}\x1B[0m (${String(content).length} chars)`,
1399
+ cfg,
1400
+ "write_file"
1401
+ );
1402
+ if (!ok) return "User denied write operation.";
1403
+ (0, import_fs3.mkdirSync)((0, import_path3.dirname)(abs), { recursive: true });
1404
+ (0, import_fs3.writeFileSync)(abs, content, "utf8");
1405
+ return `Written ${String(content).length} bytes to ${userPath}`;
1406
+ }
1407
+ async function toolListDirectory(args, cfg) {
1408
+ const userPath = args.path ?? ".";
1409
+ const abs = safePath(cfg.rootDir, userPath);
1410
+ assertNotSensitive(abs);
1411
+ if (!(0, import_fs3.existsSync)(abs)) throw new Error(`Directory not found: ${userPath}`);
1412
+ const entries = (0, import_fs3.readdirSync)(abs, { withFileTypes: true });
1413
+ const lines = entries.map((e) => {
1414
+ const type = e.isDirectory() ? "d" : e.isSymbolicLink() ? "l" : "f";
1415
+ return `[${type}] ${e.name}`;
1416
+ });
1417
+ return lines.join("\n") || "(empty directory)";
1418
+ }
1419
+ async function toolSearchFiles(args, cfg) {
1420
+ const { pattern, directory } = args;
1421
+ if (!pattern) throw new Error('search_files requires a "pattern" argument');
1422
+ const baseDir = safePath(cfg.rootDir, directory ?? ".");
1423
+ assertNotSensitive(baseDir);
1424
+ const results = [];
1425
+ const pat = new RegExp(
1426
+ pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, "."),
1427
+ "i"
1428
+ );
1429
+ function walk(dir, depth = 0) {
1430
+ if (results.length >= 50 || depth > 10) return;
1431
+ try {
1432
+ for (const entry of (0, import_fs3.readdirSync)(dir, { withFileTypes: true })) {
1433
+ if (entry.name.startsWith(".") && depth > 0) continue;
1434
+ const full = (0, import_path3.join)(dir, entry.name);
1435
+ const rel = (0, import_path3.relative)(cfg.rootDir, full);
1436
+ if (pat.test(entry.name) || pat.test(rel)) results.push(rel);
1437
+ if (entry.isDirectory()) walk(full, depth + 1);
1438
+ }
1439
+ } catch {
1440
+ }
1441
+ }
1442
+ walk(baseDir);
1443
+ return results.length ? results.join("\n") : "No files found matching: " + pattern;
1444
+ }
1445
+ async function toolRunCommand(args, cfg) {
1446
+ const { command, args: cmdArgs = [], cwd } = args;
1447
+ if (!command || typeof command !== "string") throw new Error('run_command requires a "command" string');
1448
+ if (!Array.isArray(cmdArgs)) throw new Error('"args" must be an array of strings');
1449
+ const workDir = cwd ? safePath(cfg.rootDir, cwd) : cfg.rootDir;
1450
+ const preview = [command, ...cmdArgs].join(" ");
1451
+ const ok = await confirm(
1452
+ `Agent wants to run: \x1B[1m${preview}\x1B[0m
1453
+ \x1B[2min: ${workDir}\x1B[0m`,
1454
+ cfg,
1455
+ "run_command"
1456
+ );
1457
+ if (!ok) return "User denied command execution.";
1458
+ return new Promise((resolve2) => {
1459
+ (0, import_child_process2.execFile)(command, cmdArgs.map(String), { cwd: workDir, timeout: 3e4, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
1460
+ const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
1461
+ if (err && !out) return resolve2(`Error: ${err.message}`);
1462
+ resolve2(out || "(no output)");
1463
+ });
1464
+ });
1465
+ }
1466
+ async function runLocalTool(call, cfg) {
1467
+ const { tool, args } = call;
1468
+ cfg.appendLog({
1469
+ type: "local_tool_call",
1470
+ tool,
1471
+ args,
1472
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1473
+ });
1474
+ let result;
1475
+ try {
1476
+ switch (tool) {
1477
+ case "read_file":
1478
+ result = await toolReadFile(args, cfg);
1479
+ break;
1480
+ case "write_file":
1481
+ result = await toolWriteFile(args, cfg);
1482
+ break;
1483
+ case "list_directory":
1484
+ result = await toolListDirectory(args, cfg);
1485
+ break;
1486
+ case "search_files":
1487
+ result = await toolSearchFiles(args, cfg);
1488
+ break;
1489
+ case "run_command":
1490
+ result = await toolRunCommand(args, cfg);
1491
+ break;
1492
+ default:
1493
+ result = `Unknown tool: "${tool}". Available: read_file, write_file, list_directory, search_files, run_command`;
1494
+ }
1495
+ } catch (err) {
1496
+ result = `Error: ${err?.message ?? String(err)}`;
1497
+ }
1498
+ cfg.appendLog({
1499
+ type: "local_tool_result",
1500
+ tool,
1501
+ result: result.slice(0, 2e3),
1502
+ // cap log size
1503
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1504
+ });
1505
+ return result;
1506
+ }
1507
+
1508
+ // src/commands/chat.ts
1509
+ var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
1510
+ function ensureSessionsDir() {
1511
+ if (!(0, import_fs4.existsSync)(SESSIONS_DIR)) (0, import_fs4.mkdirSync)(SESSIONS_DIR, { recursive: true });
1512
+ }
1513
+ function appendSessionLog(sessionId, record) {
1514
+ try {
1515
+ ensureSessionsDir();
1516
+ const file = (0, import_path4.join)(SESSIONS_DIR, `${sessionId}.jsonl`);
1517
+ (0, import_fs4.appendFileSync)(file, JSON.stringify(record) + "\n", { mode: 384 });
1518
+ } catch {
1519
+ }
1520
+ }
1117
1521
  var HELP_TEXT = `
1118
1522
  ${c.label("Slash commands")}
1119
1523
  /help Show this help
1120
1524
  /session Print the current session ID (copy it to resume later)
1525
+ /tools Show local tool status and permissions (--local mode)
1121
1526
  /clear Clear the terminal screen
1122
1527
  /quit Exit (session is preserved \u2014 resume with --resume <id>)
1123
1528
  `;
1529
+ var LOCAL_TOOLS_DISCLAIMER = `
1530
+ ${c.warn("\u26A0")} ${c.bold("Local file system access enabled")}
1531
+
1532
+ ${c.dim("The agent can read and write files, list directories, search files,")}
1533
+ ${c.dim("and execute shell commands on your machine.")}
1534
+
1535
+ ${c.dim("Rules:")}
1536
+ ${sym.bullet} ${c.dim("All paths are restricted to:")} ${c.primary(process.cwd())}
1537
+ ${sym.bullet} ${c.dim("Sensitive paths (.ssh, .env, .aws, credentials) are always blocked")}
1538
+ ${sym.bullet} ${c.dim("Write and run_command operations require your confirmation")}
1539
+ ${sym.bullet} ${c.dim("You can deny any individual request")}
1540
+
1541
+ ${c.dim("Session activity is logged to")} ${c.primary("~/.agc/sessions/")}
1542
+ `;
1124
1543
  function chatCommand() {
1125
- return new import_commander8.Command("chat").description("Start an interactive chat REPL with an agent").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--no-stream", "Disable token streaming (wait for full response)").action(async (opts) => {
1544
+ return new import_commander8.Command("chat").description("Start an interactive chat REPL with an agent").option("--agent <agentId>", "Agent ID (or set defaultAgentId in config)").option("--resume <sessionId>", "Resume an existing session by ID").option("--no-stream", "Disable token streaming (wait for full response)").option("--local", "Enable local file system access for the agent (see disclaimer)").action(async (opts) => {
1126
1545
  const cfg = loadConfig();
1127
1546
  const agentId = opts.agent ?? cfg.defaultAgentId;
1128
1547
  if (!agentId) {
@@ -1136,17 +1555,27 @@ function chatCommand() {
1136
1555
  const client = makeClient();
1137
1556
  let sessionId = opts.resume ?? "";
1138
1557
  const isResume = !!opts.resume;
1558
+ const initiator = cfg.initiator ?? "";
1139
1559
  if (!isResume) {
1140
1560
  const spinner = spin("Creating session\u2026");
1141
1561
  try {
1142
1562
  const res = await client.sessions.create({
1143
1563
  agentId,
1144
- initiator: cfg.initiator,
1145
- title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`
1564
+ initiator,
1565
+ title: `agc chat ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
1566
+ source: "cli"
1146
1567
  });
1147
1568
  const session = res?.data ?? res;
1148
1569
  sessionId = session.sessionId;
1149
1570
  spinner.stop();
1571
+ appendSessionLog(sessionId, {
1572
+ type: "session_start",
1573
+ sessionId,
1574
+ agentId,
1575
+ initiator,
1576
+ source: "cli",
1577
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1578
+ });
1150
1579
  } catch (err) {
1151
1580
  spinner.stop();
1152
1581
  printError(err);
@@ -1189,9 +1618,26 @@ ${c.bold("Agent Commons Chat")}`);
1189
1618
  ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
1190
1619
  ];
1191
1620
  if (walletLine) headerRows.push(["Wallet", walletLine]);
1621
+ if (opts.local) headerRows.push(["Local tools", c.success("enabled") + c.dim(" (read, write, search, run)")]);
1192
1622
  detail(headerRows);
1623
+ let localToolsCfg = null;
1624
+ if (opts.local) {
1625
+ console.log(LOCAL_TOOLS_DISCLAIMER);
1626
+ const rootDir = process.cwd();
1627
+ localToolsCfg = {
1628
+ rootDir,
1629
+ sessionId,
1630
+ appendLog: (record) => appendSessionLog(sessionId, record),
1631
+ permissions: /* @__PURE__ */ new Map()
1632
+ };
1633
+ appendSessionLog(sessionId, {
1634
+ type: "local_tools_enabled",
1635
+ rootDir,
1636
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1637
+ });
1638
+ }
1193
1639
  console.log(c.dim("\nType your message and press Enter. Type /help for commands.\n"));
1194
- const rl = readline2.createInterface({
1640
+ const rl = readline3.createInterface({
1195
1641
  input: process.stdin,
1196
1642
  output: process.stdout,
1197
1643
  terminal: true,
@@ -1215,6 +1661,26 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
1215
1661
  rl.prompt();
1216
1662
  return;
1217
1663
  }
1664
+ if (input === "/tools") {
1665
+ if (!localToolsCfg) {
1666
+ console.log(c.dim(` Local tools are disabled. Restart with ${c.bold("agc chat --local")} to enable them.`));
1667
+ } else {
1668
+ console.log(`
1669
+ ${c.bold("Local tools")} ${c.success("enabled")}`);
1670
+ console.log(` ${c.dim("Root directory:")} ${c.primary(localToolsCfg.rootDir)}`);
1671
+ const perms = [...localToolsCfg.permissions.entries()];
1672
+ if (perms.length) {
1673
+ console.log(` ${c.dim("Cached permissions:")}`);
1674
+ for (const [k, v] of perms) {
1675
+ const badge = v === "allow" ? c.success("allow") : c.error("deny");
1676
+ console.log(` ${sym.bullet} ${k}: ${badge}`);
1677
+ }
1678
+ }
1679
+ }
1680
+ console.log();
1681
+ rl.prompt();
1682
+ return;
1683
+ }
1218
1684
  if (input === "/session") {
1219
1685
  console.log(c.dim(` ${sessionId}`));
1220
1686
  console.log(c.dim(` Resume with: agc chat --resume ${sessionId}`));
@@ -1232,10 +1698,20 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
1232
1698
  return;
1233
1699
  }
1234
1700
  rl.pause();
1701
+ appendSessionLog(sessionId, {
1702
+ type: "message",
1703
+ role: "user",
1704
+ content: input,
1705
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1706
+ });
1707
+ const outgoingMessages = localToolsCfg ? [
1708
+ { role: "system", content: buildLocalToolsManifest(localToolsCfg.rootDir) },
1709
+ { role: "user", content: input }
1710
+ ] : [{ role: "user", content: input }];
1235
1711
  const params = {
1236
1712
  agentId,
1237
1713
  sessionId,
1238
- messages: [{ role: "user", content: input }]
1714
+ messages: outgoingMessages
1239
1715
  };
1240
1716
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
1241
1717
  if (opts.noStream) {
@@ -1245,6 +1721,12 @@ Session saved. Resume with: agc chat --resume ${sessionId}`));
1245
1721
  spinner.stop();
1246
1722
  const text = extractText(result);
1247
1723
  console.log(text);
1724
+ appendSessionLog(sessionId, {
1725
+ type: "message",
1726
+ role: "assistant",
1727
+ content: text,
1728
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1729
+ });
1248
1730
  } catch (err) {
1249
1731
  spinner.stop();
1250
1732
  console.error(`
@@ -1253,9 +1735,12 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
1253
1735
  } else {
1254
1736
  try {
1255
1737
  let hasOutput = false;
1738
+ let agentContent = "";
1256
1739
  for await (const event of client.agents.stream(params)) {
1257
1740
  if (event.type === "token") {
1258
- process.stdout.write(event.content ?? "");
1741
+ const tok = event.content ?? "";
1742
+ process.stdout.write(tok);
1743
+ agentContent += tok;
1259
1744
  hasOutput = true;
1260
1745
  } else if (event.type === "toolStart") {
1261
1746
  const name = event.toolName ?? "";
@@ -1269,13 +1754,34 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
1269
1754
  } else if (event.type === "final") {
1270
1755
  const e = event;
1271
1756
  const text = extractText(e?.payload);
1272
- if (text && !hasOutput) process.stdout.write(text);
1757
+ if (text && !hasOutput) {
1758
+ process.stdout.write(text);
1759
+ agentContent += text;
1760
+ }
1273
1761
  const usage = e?.payload?.usage;
1274
1762
  if (usage) {
1275
- const tokens = usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
1763
+ const inputTok = usage.inputTokens ?? 0;
1764
+ const outputTok = usage.outputTokens ?? 0;
1765
+ const cachedTok = usage.cachedTokens ?? 0;
1766
+ const total = usage.totalTokens ?? inputTok + outputTok;
1276
1767
  const cost = typeof usage.costUsd === "number" ? `$${usage.costUsd.toFixed(4)}` : "";
1277
- const parts = [tokens ? `${tokens.toLocaleString()} tokens` : "", cost].filter(Boolean);
1768
+ const parts = [total ? `${total.toLocaleString()} tokens` : "", cost].filter(Boolean);
1278
1769
  if (parts.length) process.stdout.write("\n" + c.dim(` \u21B3 ${parts.join(" \xB7 ")}`));
1770
+ if (cachedTok > 0) process.stdout.write(c.dim(` (${cachedTok.toLocaleString()} cached)`));
1771
+ appendSessionLog(sessionId, {
1772
+ type: "message",
1773
+ role: "assistant",
1774
+ content: agentContent,
1775
+ usage: { inputTokens: inputTok, outputTokens: outputTok, cachedTokens: cachedTok, totalTokens: total, costUsd: usage.costUsd },
1776
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1777
+ });
1778
+ } else {
1779
+ appendSessionLog(sessionId, {
1780
+ type: "message",
1781
+ role: "assistant",
1782
+ content: agentContent,
1783
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1784
+ });
1279
1785
  }
1280
1786
  break;
1281
1787
  } else if (event.type === "error") {
@@ -1286,6 +1792,9 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
1286
1792
  }
1287
1793
  }
1288
1794
  process.stdout.write("\n");
1795
+ if (localToolsCfg && agentContent) {
1796
+ await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId, sessionId, appendSessionLog);
1797
+ }
1289
1798
  } catch (err) {
1290
1799
  process.stdout.write("\n");
1291
1800
  console.error(`${sym.fail} ${c.error(err.message ?? String(err))}`);
@@ -1305,6 +1814,78 @@ Session preserved. Resume with: agc chat --resume ${sessionId}`));
1305
1814
  });
1306
1815
  });
1307
1816
  }
1817
+ var MAX_TOOL_DEPTH = 10;
1818
+ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, appendLog, depth = 0) {
1819
+ if (depth >= MAX_TOOL_DEPTH) {
1820
+ console.log(c.dim(`
1821
+ [local] Max tool depth reached (${MAX_TOOL_DEPTH}). Stopping tool loop.
1822
+ `));
1823
+ return;
1824
+ }
1825
+ const toolCall = extractToolCall(agentText);
1826
+ if (!toolCall) return;
1827
+ process.stdout.write(c.dim(`
1828
+ [local] ${toolCall.tool}`));
1829
+ let result;
1830
+ try {
1831
+ result = await runLocalTool(toolCall, cfg);
1832
+ process.stdout.write(c.dim(" \u2713\n"));
1833
+ } catch (err) {
1834
+ result = `Error: ${err?.message ?? String(err)}`;
1835
+ process.stdout.write(c.dim(" \u2717\n"));
1836
+ }
1837
+ const resultMsg = `[Tool result: ${toolCall.tool}]
1838
+ \`\`\`
1839
+ ${result}
1840
+ \`\`\``;
1841
+ appendLog(sessionId, {
1842
+ type: "message",
1843
+ role: "tool",
1844
+ tool: toolCall.tool,
1845
+ result: result.slice(0, 4e3),
1846
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1847
+ });
1848
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
1849
+ let followContent = "";
1850
+ try {
1851
+ for await (const evt of client.agents.stream({
1852
+ agentId,
1853
+ sessionId,
1854
+ messages: [{ role: "user", content: resultMsg }]
1855
+ })) {
1856
+ if (evt.type === "token") {
1857
+ const tok = evt.content ?? "";
1858
+ process.stdout.write(tok);
1859
+ followContent += tok;
1860
+ } else if (evt.type === "toolStart") {
1861
+ const name = evt.toolName ?? "";
1862
+ if (followContent) process.stdout.write("\n");
1863
+ process.stdout.write(c.dim(` [tool] ${name}\u2026`));
1864
+ } else if (evt.type === "toolEnd") {
1865
+ process.stdout.write(c.dim(" done\n"));
1866
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
1867
+ } else if (evt.type === "final") {
1868
+ const txt = extractText(evt?.payload);
1869
+ if (txt && !followContent) {
1870
+ process.stdout.write(txt);
1871
+ followContent += txt;
1872
+ }
1873
+ appendLog(sessionId, { type: "message", role: "assistant", content: followContent, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1874
+ break;
1875
+ } else if (evt.type === "error") {
1876
+ console.error(`
1877
+ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
1878
+ break;
1879
+ }
1880
+ }
1881
+ process.stdout.write("\n");
1882
+ } catch (err) {
1883
+ process.stdout.write("\n");
1884
+ console.error(`${sym.fail} ${c.error(err?.message ?? String(err))}`);
1885
+ return;
1886
+ }
1887
+ await handleLocalToolLoop(followContent, cfg, client, agentId, sessionId, appendLog, depth + 1);
1888
+ }
1308
1889
  function extractText(payload) {
1309
1890
  if (!payload) return "";
1310
1891
  if (typeof payload === "string") return payload;
@@ -1843,10 +2424,10 @@ function skillsCommand() {
1843
2424
  });
1844
2425
  cmd.command("delete <slug>").description("Permanently delete a skill").option("--yes", "Skip confirmation prompt").option("--json", "Output result as JSON").action(async (slug, opts) => {
1845
2426
  if (!opts.yes) {
1846
- const readline3 = await import("readline");
1847
- const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
2427
+ const readline4 = await import("readline");
2428
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
1848
2429
  const answer = await new Promise(
1849
- (resolve) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve)
2430
+ (resolve2) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve2)
1850
2431
  );
1851
2432
  rl.close();
1852
2433
  if (!["y", "yes"].includes(answer.trim().toLowerCase())) {
@@ -2468,8 +3049,124 @@ function logsCommand() {
2468
3049
  }
2469
3050
 
2470
3051
  // src/bin.ts
3052
+ var CONFIG_FILE3 = (0, import_path5.join)((0, import_os4.homedir)(), ".agc", "config.json");
3053
+ async function interactiveMenu() {
3054
+ banner();
3055
+ const cfg = loadConfig();
3056
+ const isSetup = !!(cfg.apiKey && cfg.initiator);
3057
+ if (!isSetup) {
3058
+ console.log(c.bold(" Welcome to Agent Commons CLI!"));
3059
+ console.log(c.dim(" Looks like this is your first time here \u2014 let's get you set up.\n"));
3060
+ console.log(` ${sym.arrow} Running ${c.bold("agc login")} to configure your credentials\u2026
3061
+ `);
3062
+ runSubcommand(["login"]);
3063
+ return;
3064
+ }
3065
+ console.log(
3066
+ ` ${c.dim("Connected to")} ${c.primary(cfg.apiUrl)} ${c.dim("\xB7")} ${c.dim("Wallet")} ${c.id(cfg.initiator.slice(0, 8) + "\u2026" + cfg.initiator.slice(-4))}
3067
+ `
3068
+ );
3069
+ const action = await select("What would you like to do?", [
3070
+ { label: "Chat with an agent", value: "chat", hint: "agc chat" },
3071
+ { label: "Run an agent (one-shot)", value: "run", hint: "agc run" },
3072
+ { label: "View sessions", value: "sessions", hint: "agc sessions list" },
3073
+ { label: "Manage agents", value: "agents", hint: "agc agents list" },
3074
+ { label: "Tasks", value: "tasks", hint: "agc task list" },
3075
+ { label: "Workflows", value: "workflows", hint: "agc workflow list" },
3076
+ { label: "MCP servers", value: "mcp", hint: "agc mcp list" },
3077
+ { label: "Skills", value: "skills", hint: "agc skills list" },
3078
+ { label: "Wallet & balance", value: "wallet", hint: "agc wallet balance" },
3079
+ { label: "Usage & cost", value: "usage", hint: "agc usage" },
3080
+ { label: "Logs", value: "logs", hint: "agc logs" },
3081
+ { label: "Config & credentials", value: "config", hint: "agc config get" },
3082
+ { label: "Exit", value: "exit" }
3083
+ ]);
3084
+ if (action === "exit") {
3085
+ process.exit(0);
3086
+ }
3087
+ const commandMap = {
3088
+ chat: cfg.defaultAgentId ? ["chat", "--agent", cfg.defaultAgentId] : ["chat", "--agent"],
3089
+ run: cfg.defaultAgentId ? ["run", "--agent", cfg.defaultAgentId, "--message"] : ["run", "--agent"],
3090
+ sessions: ["sessions", "list"],
3091
+ agents: ["agents", "list"],
3092
+ tasks: ["task", "list"],
3093
+ workflows: ["workflow", "list"],
3094
+ mcp: ["mcp", "list"],
3095
+ skills: ["skills", "list"],
3096
+ wallet: ["wallet", "balance"],
3097
+ usage: ["usage"],
3098
+ logs: ["logs"],
3099
+ config: ["config", "get"],
3100
+ exit: []
3101
+ };
3102
+ if ((action === "chat" || action === "run") && !cfg.defaultAgentId) {
3103
+ const pickedId = await pickAgentInteractively(action);
3104
+ if (!pickedId) return;
3105
+ runSubcommand([action, "--agent", pickedId]);
3106
+ return;
3107
+ }
3108
+ runSubcommand(commandMap[action]);
3109
+ }
3110
+ function runSubcommand(args) {
3111
+ const child = (0, import_child_process3.spawn)(process.argv[0], [process.argv[1], ...args], {
3112
+ stdio: "inherit"
3113
+ });
3114
+ child.on("exit", (code) => process.exit(code ?? 0));
3115
+ }
3116
+ async function pickAgentInteractively(action) {
3117
+ const cfg = loadConfig();
3118
+ const spinner = spin("Fetching your agents\u2026");
3119
+ let agents = [];
3120
+ try {
3121
+ const client = makeClient();
3122
+ const res = await client.agents.list(cfg.initiator);
3123
+ agents = res?.data ?? (Array.isArray(res) ? res : []);
3124
+ spinner.stop();
3125
+ } catch {
3126
+ spinner.stop();
3127
+ console.log(`
3128
+ ${c.warn("\u26A0")} Could not fetch agents. Check your API key and connection.
3129
+ `);
3130
+ return null;
3131
+ }
3132
+ if (agents.length === 0) {
3133
+ console.log(`
3134
+ ${c.warn("\u26A0")} You don't have any agents yet.
3135
+ `);
3136
+ const choice = await select("What would you like to do?", [
3137
+ { label: "Create a new agent now", value: "create", hint: "agc agents create" },
3138
+ { label: "Go back", value: "cancel" }
3139
+ ]);
3140
+ if (choice === "create") {
3141
+ runSubcommand(["agents", "create"]);
3142
+ }
3143
+ return null;
3144
+ }
3145
+ console.log();
3146
+ const agentId = await select(
3147
+ `Choose an agent to ${action} with:`,
3148
+ agents.map((a) => ({
3149
+ label: a.name,
3150
+ value: a.agentId,
3151
+ hint: `${a.modelProvider}/${a.modelId}`
3152
+ }))
3153
+ );
3154
+ const saveDefault = await select("Set as your default agent?", [
3155
+ { label: "Yes \u2014 remember this agent for next time", value: true },
3156
+ { label: "No \u2014 just this once", value: false }
3157
+ ]);
3158
+ if (saveDefault) {
3159
+ saveConfig({ defaultAgentId: agentId });
3160
+ const chosen = agents.find((a) => a.agentId === agentId);
3161
+ console.log(` ${sym.ok} ${c.dim("Default agent set to")} ${c.bold(chosen?.name ?? agentId)}
3162
+ `);
3163
+ }
3164
+ return agentId;
3165
+ }
2471
3166
  var program = new import_commander16.Command();
2472
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.0", "-v, --version");
3167
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.4", "-v, --version").action(async () => {
3168
+ await interactiveMenu();
3169
+ });
2473
3170
  program.addCommand(loginCommand());
2474
3171
  program.addCommand(logoutCommand());
2475
3172
  program.addCommand(whoamiCommand());
@@ -2489,8 +3186,12 @@ program.addCommand(memoryCommand());
2489
3186
  program.addCommand(usageCommand());
2490
3187
  program.addCommand(logsCommand());
2491
3188
  program.on("command:*", () => {
2492
- console.error(`Unknown command: ${program.args.join(" ")}
2493
- Run \`agc --help\` to see available commands.`);
3189
+ console.error(
3190
+ `
3191
+ ${c.error("Unknown command:")} ${program.args.join(" ")}
3192
+ Run ${c.bold("agc --help")} to see available commands, or just ${c.bold("agc")} for the interactive menu.
3193
+ `
3194
+ );
2494
3195
  process.exit(1);
2495
3196
  });
2496
3197
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-commons/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Agent Commons CLI — chat, run, and manage agents from your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -14,7 +14,7 @@
14
14
  "commander": "^12.1.0",
15
15
  "chalk": "^5.3.0",
16
16
  "ora": "^8.1.1",
17
- "@agent-commons/sdk": "0.1.5"
17
+ "@agent-commons/sdk": "0.1.7"
18
18
  },
19
19
  "devDependencies": {
20
20
  "tsup": "^8.3.5",