@agent-commons/cli 0.1.16 → 0.1.18

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 +382 -104
  2. package/package.json +7 -5
package/dist/bin.js CHANGED
@@ -106,7 +106,7 @@ var sym = {
106
106
  bullet: import_chalk.default.dim("\u2022"),
107
107
  dot: import_chalk.default.dim("\xB7")
108
108
  };
109
- function banner(version = "0.1.16") {
109
+ function banner(version = "0.1.18") {
110
110
  const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
111
111
  console.log("");
112
112
  console.log(line);
@@ -515,7 +515,7 @@ ${sym.ok} Agent created`);
515
515
  process.exit(1);
516
516
  }
517
517
  });
518
- const autonomy = cmd.command("autonomy").description("Manage agent heartbeat / autonomy");
518
+ const autonomy = cmd.command("autonomy").description("Manage agent heartbeat");
519
519
  autonomy.command("status").description("Show autonomy status for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--json", "Output as JSON").action(async (opts) => {
520
520
  const client = makeClient();
521
521
  const spinner = spin("Fetching autonomy status\u2026");
@@ -525,7 +525,7 @@ ${sym.ok} Agent created`);
525
525
  const s = res.data;
526
526
  if (opts.json) return jsonOut(s);
527
527
  console.log(`
528
- ${c.bold("Autonomy Status")}`);
528
+ ${c.bold("Heartbeat Status")}`);
529
529
  detail([
530
530
  ["Enabled", s.enabled ? c.bold("yes") : "no"],
531
531
  ["Interval", s.intervalSec ? `${s.intervalSec}s` : "n/a"],
@@ -539,7 +539,7 @@ ${c.bold("Autonomy Status")}`);
539
539
  process.exit(1);
540
540
  }
541
541
  });
542
- autonomy.command("enable").description("Enable autonomous heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--interval <seconds>", "Heartbeat interval in seconds (min 30)", "300").action(async (opts) => {
542
+ autonomy.command("enable").description("Enable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").option("--interval <seconds>", "Heartbeat interval in seconds (min 30)", "300").action(async (opts) => {
543
543
  const client = makeClient();
544
544
  const spinner = spin("Enabling autonomy\u2026");
545
545
  try {
@@ -557,7 +557,7 @@ ${sym.ok} Autonomy enabled for agent ${c.id(opts.agent)}`);
557
557
  process.exit(1);
558
558
  }
559
559
  });
560
- autonomy.command("disable").description("Disable autonomous heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
560
+ autonomy.command("disable").description("Disable heartbeat for an agent").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
561
561
  const client = makeClient();
562
562
  const spinner = spin("Disabling autonomy\u2026");
563
563
  try {
@@ -571,7 +571,7 @@ ${sym.ok} Autonomy disabled for agent ${c.id(opts.agent)}`);
571
571
  process.exit(1);
572
572
  }
573
573
  });
574
- autonomy.command("trigger").description("Trigger a single heartbeat beat immediately").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
574
+ autonomy.command("trigger").description("Trigger a single heartbeat immediately").requiredOption("--agent <agentId>", "Agent ID").action(async (opts) => {
575
575
  const client = makeClient();
576
576
  const spinner = spin("Triggering heartbeat\u2026");
577
577
  try {
@@ -1184,79 +1184,14 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1184
1184
 
1185
1185
  // src/commands/run.ts
1186
1186
  var import_commander7 = require("commander");
1187
- function runCommand() {
1188
- return new import_commander7.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Session ID").option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").action(async (prompt2, opts) => {
1189
- const cfg = loadConfig();
1190
- const agentId = opts.agent ?? cfg.defaultAgentId;
1191
- if (!agentId) {
1192
- console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1193
- process.exit(1);
1194
- }
1195
- const params = {
1196
- agentId,
1197
- sessionId: opts.session,
1198
- messages: [{ role: "user", content: prompt2 }],
1199
- ...cfg.initiator && { initiatorId: cfg.initiator }
1200
- };
1201
- if (opts.noStream) {
1202
- const spinner = spin("Running\u2026");
1203
- try {
1204
- const client = makeClient();
1205
- const result = await client.run.once(params);
1206
- spinner.stop();
1207
- if (opts.json) return jsonOut(result);
1208
- const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
1209
- console.log(text);
1210
- } catch (err) {
1211
- spinner.stop();
1212
- printError(err);
1213
- process.exit(1);
1214
- }
1215
- return;
1216
- }
1217
- try {
1218
- const client = makeClient();
1219
- let hasOutput = false;
1220
- for await (const event of client.agents.stream(params)) {
1221
- if (opts.json) {
1222
- console.log(JSON.stringify(event));
1223
- continue;
1224
- }
1225
- if (event.type === "token") {
1226
- process.stdout.write(event.content ?? "");
1227
- hasOutput = true;
1228
- } else if (event.type === "final") {
1229
- if (hasOutput) process.stdout.write("\n");
1230
- const e = event;
1231
- if (e.content && !hasOutput) console.log(e.content);
1232
- break;
1233
- } else if (event.type === "error") {
1234
- if (hasOutput) process.stdout.write("\n");
1235
- console.error(`
1236
- ${sym.fail} ${c.error(event.message ?? "Error")}`);
1237
- process.exit(1);
1238
- }
1239
- }
1240
- if (hasOutput && !opts.json) process.stdout.write("\n");
1241
- } catch (err) {
1242
- printError(err);
1243
- process.exit(1);
1244
- }
1245
- });
1246
- }
1247
-
1248
- // src/commands/chat.ts
1249
- var import_commander8 = require("commander");
1250
1187
  var readline3 = __toESM(require("readline"));
1251
- var import_fs4 = require("fs");
1252
- var import_path4 = require("path");
1253
- var import_os3 = require("os");
1254
1188
 
1255
1189
  // src/local-tools.ts
1256
1190
  var import_fs3 = require("fs");
1257
1191
  var import_path3 = require("path");
1258
1192
  var import_child_process2 = require("child_process");
1259
1193
  var readline2 = __toESM(require("readline"));
1194
+ var pdfParse = require("pdf-parse/lib/pdf-parse.js");
1260
1195
  var managedProcesses = /* @__PURE__ */ new Map();
1261
1196
  function capBuffer(existing, chunk, maxBytes) {
1262
1197
  const joined = existing + chunk;
@@ -1309,7 +1244,7 @@ function readFileForContext(rootDir, filePath) {
1309
1244
  return `[error reading file: ${err?.message}]`;
1310
1245
  }
1311
1246
  }
1312
- function buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks = []) {
1247
+ function buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks = [], autoApprove = false) {
1313
1248
  const fileSection = fileContextBlocks.length ? `
1314
1249
  ### File contents included in this turn
1315
1250
 
@@ -1335,7 +1270,8 @@ ${fileSection}
1335
1270
  2. **Always show the actual output** returned by the tool in your response. Never say "I listed the files" without showing them. Report exactly what the tool returns.
1336
1271
  3. **Never fabricate results.** Wait for the real tool output before responding.
1337
1272
  4. **Sensitive paths are blocked** (.ssh, .gnupg, .aws, .env, credentials). Attempting to access them will return an error.
1338
- 5. **cli_write_file and cli_run_command require the user to confirm** before executing \u2014 you will see the result after they approve.
1273
+ 5. ${autoApprove ? "**cli_write_file and cli_run_command execute immediately** \u2014 auto-approve is active, no user confirmation is required." : "**cli_write_file and cli_run_command require the user to confirm** before executing \u2014 you will see the result after they approve."}
1274
+ 6. **Git commits must carry the agc co-author trailer.** Always include \`--trailer "Co-Authored-By: <AgentName> (agc) <agc-agent@users.noreply.github.com>"\` when running \`git commit\`. The CLI injects this automatically \u2014 do not omit it or pass \`--no-trailer\`.
1339
1275
 
1340
1276
  ### Available CLI tools
1341
1277
 
@@ -1407,6 +1343,13 @@ function extractToolCall(text) {
1407
1343
  }
1408
1344
  return null;
1409
1345
  }
1346
+ function injectAgcTrailer(command, args, agentId, agentName) {
1347
+ if (command !== "git") return args;
1348
+ if (!args.some((a) => a === "commit")) return args;
1349
+ if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
1350
+ const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
1351
+ return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
1352
+ }
1410
1353
  function safePath(root, userPath) {
1411
1354
  const abs = (0, import_path3.resolve)(root, userPath);
1412
1355
  const rel = (0, import_path3.relative)(root, abs);
@@ -1433,6 +1376,7 @@ function assertNotSensitive(abs) {
1433
1376
  }
1434
1377
  }
1435
1378
  async function confirm(message, config, permissionKey) {
1379
+ if (config.autoApprove) return true;
1436
1380
  const cached = config.permissions.get(permissionKey);
1437
1381
  if (cached === "allow") return true;
1438
1382
  if (cached === "deny") return false;
@@ -1505,9 +1449,24 @@ function extractViaCommand(cmd, cmdArgs) {
1505
1449
  });
1506
1450
  }
1507
1451
  async function extractPdfText(abs) {
1452
+ try {
1453
+ const buffer = (0, import_fs3.readFileSync)(abs);
1454
+ const data = await pdfParse(buffer);
1455
+ const text2 = data.text?.trim();
1456
+ if (text2) {
1457
+ const MAX_CHARS = 15e4;
1458
+ if (text2.length > MAX_CHARS) {
1459
+ return text2.slice(0, MAX_CHARS) + `
1460
+
1461
+ [\u2026truncated \u2014 showing first ${MAX_CHARS.toLocaleString()} characters of ${text2.length.toLocaleString()} total]`;
1462
+ }
1463
+ return text2;
1464
+ }
1465
+ } catch {
1466
+ }
1508
1467
  const text = await extractViaCommand("pdftotext", [abs, "-"]);
1509
1468
  if (text) return text;
1510
- return `[Cannot extract PDF text: pdftotext not found. Install with: brew install poppler]`;
1469
+ return `[Cannot extract PDF text: the file may be scanned/image-only or password-protected]`;
1511
1470
  }
1512
1471
  async function extractOfficeText(abs, ext) {
1513
1472
  const text = await extractViaCommand("textutil", ["-stdout", "-cat", "txt", abs]);
@@ -1522,13 +1481,19 @@ async function toolReadFile(args, cfg) {
1522
1481
  if (!(0, import_fs3.existsSync)(abs)) throw new Error(`File not found: ${userPath}`);
1523
1482
  const stat = (0, import_fs3.statSync)(abs);
1524
1483
  if (stat.isDirectory()) throw new Error(`"${userPath}" is a directory, not a file`);
1525
- if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
1526
1484
  const ext = (0, import_path3.extname)(abs).toLowerCase();
1527
- if (PDF_EXTS.has(ext)) return extractPdfText(abs);
1528
- if (OFFICE_EXTS.has(ext)) return extractOfficeText(abs, ext);
1485
+ if (PDF_EXTS.has(ext)) {
1486
+ if (stat.size > 5e7) throw new Error(`PDF too large to read (${Math.round(stat.size / 1e6)} MB). Max 50 MB.`);
1487
+ return extractPdfText(abs);
1488
+ }
1489
+ if (OFFICE_EXTS.has(ext)) {
1490
+ if (stat.size > 2e7) throw new Error(`Document too large to read (${Math.round(stat.size / 1e6)} MB). Max 20 MB.`);
1491
+ return extractOfficeText(abs, ext);
1492
+ }
1529
1493
  if (UNREADABLE_BINARY_EXTS.has(ext)) {
1530
- throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Word documents are supported.`);
1494
+ throw new Error(`Cannot read binary file "${userPath}" (${ext} format). Only text, PDF, and Office documents are supported.`);
1531
1495
  }
1496
+ if (stat.size > 5e5) throw new Error(`File too large to read (${Math.round(stat.size / 1024)} KB). Max 500 KB.`);
1532
1497
  return (0, import_fs3.readFileSync)(abs, "utf8");
1533
1498
  }
1534
1499
  async function toolWriteFile(args, cfg) {
@@ -1590,7 +1555,8 @@ async function toolRunCommand(args, cfg) {
1590
1555
  if (!command || typeof command !== "string") throw new Error('run_command requires a "command" string');
1591
1556
  if (!Array.isArray(cmdArgs)) throw new Error('"args" must be an array of strings');
1592
1557
  const workDir = cwd ? safePath(cfg.rootDir, cwd) : cfg.rootDir;
1593
- const preview = [command, ...cmdArgs].join(" ");
1558
+ const injectedArgs = injectAgcTrailer(command, cmdArgs, cfg.agentId, cfg.agentName);
1559
+ const preview = [command, ...injectedArgs].join(" ");
1594
1560
  const timeoutMs = Math.min((typeof timeout_seconds === "number" ? timeout_seconds : 120) * 1e3, 3e5);
1595
1561
  const ok = await confirm(
1596
1562
  `Agent wants to run: \x1B[1m${preview}\x1B[0m
@@ -1601,7 +1567,7 @@ async function toolRunCommand(args, cfg) {
1601
1567
  if (!ok) return "User denied command execution.";
1602
1568
  if (interactive) {
1603
1569
  return new Promise((resolve2) => {
1604
- const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), { cwd: workDir, stdio: "inherit" });
1570
+ const child = (0, import_child_process2.spawn)(command, injectedArgs.map(String), { cwd: workDir, stdio: "inherit" });
1605
1571
  const timer = setTimeout(() => {
1606
1572
  child.kill();
1607
1573
  resolve2(`(command timed out after ${timeoutMs / 1e3}s)`);
@@ -1617,7 +1583,7 @@ async function toolRunCommand(args, cfg) {
1617
1583
  });
1618
1584
  }
1619
1585
  return new Promise((resolve2) => {
1620
- (0, import_child_process2.execFile)(command, cmdArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
1586
+ (0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
1621
1587
  const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
1622
1588
  if (err && !out) return resolve2(`Error: ${err.message}`);
1623
1589
  resolve2(out || "(no output)");
@@ -1792,7 +1758,190 @@ async function runLocalTool(call, cfg) {
1792
1758
  return result;
1793
1759
  }
1794
1760
 
1761
+ // src/commands/run.ts
1762
+ function runCommand() {
1763
+ return new import_commander7.Command("run").description("Send a single prompt to an agent and stream the response").argument("<prompt>", "Prompt text to send").option("--agent <agentId>", "Agent ID").option("--session <sessionId>", "Resume an existing session by ID").option("--new-session", "Create a new session and print its ID for future use").option("--local", "Enable local file system access (with permission prompts)").option("-y, --yes", "Enable local file system access and auto-approve all operations").option("--no-stream", "Disable streaming (wait for full response)").option("--json", "Output raw event stream as JSON lines").action(async (prompt2, opts) => {
1764
+ const cfg = loadConfig();
1765
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1766
+ if (!agentId) {
1767
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1768
+ process.exit(1);
1769
+ }
1770
+ if (opts.session && opts.newSession) {
1771
+ console.error(c.error("Cannot use --session and --new-session together."));
1772
+ process.exit(1);
1773
+ }
1774
+ const client = makeClient();
1775
+ let sessionId = opts.session;
1776
+ if (opts.session) {
1777
+ const spinner = spin("Loading session\u2026");
1778
+ try {
1779
+ await client.sessions.get(opts.session);
1780
+ spinner.stop();
1781
+ } catch {
1782
+ spinner.stop();
1783
+ console.error(c.error(`Session "${opts.session}" not found.`));
1784
+ process.exit(1);
1785
+ }
1786
+ }
1787
+ if (opts.newSession) {
1788
+ const spinner = spin("Creating session\u2026");
1789
+ try {
1790
+ const res = await client.sessions.create({
1791
+ agentId,
1792
+ initiator: cfg.initiator ?? "",
1793
+ title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
1794
+ source: "cli"
1795
+ });
1796
+ const session = res?.data ?? res;
1797
+ sessionId = session.sessionId;
1798
+ spinner.stop();
1799
+ } catch (err) {
1800
+ spinner.stop();
1801
+ printError(err);
1802
+ process.exit(1);
1803
+ }
1804
+ }
1805
+ const localEnabled = opts.yes || opts.local;
1806
+ const autoApprove = !!opts.yes;
1807
+ let localToolsCfg = null;
1808
+ let cliContext;
1809
+ if (localEnabled) {
1810
+ const rootDir = process.cwd();
1811
+ localToolsCfg = {
1812
+ rootDir,
1813
+ sessionId: sessionId ?? "run",
1814
+ appendLog: () => {
1815
+ },
1816
+ permissions: /* @__PURE__ */ new Map(),
1817
+ agentId,
1818
+ autoApprove
1819
+ };
1820
+ const snapshot = buildDirSnapshot(rootDir, 2);
1821
+ cliContext = buildLocalToolsManifest(rootDir, snapshot, [], autoApprove);
1822
+ }
1823
+ if (!opts.json) {
1824
+ const rows = [];
1825
+ if (sessionId) {
1826
+ const label = opts.newSession ? `${c.id(sessionId)}${c.dim(" (new)")}` : `${c.id(sessionId)}${c.dim(" (resumed)")}`;
1827
+ rows.push(["Session", label]);
1828
+ }
1829
+ if (localEnabled) {
1830
+ rows.push(["Local tools", autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")]);
1831
+ }
1832
+ if (rows.length) {
1833
+ detail(rows);
1834
+ console.log();
1835
+ }
1836
+ }
1837
+ const params = {
1838
+ agentId,
1839
+ sessionId,
1840
+ messages: [{ role: "user", content: prompt2 }],
1841
+ ...cfg.initiator && { initiatorId: cfg.initiator },
1842
+ ...cliContext && { cliContext }
1843
+ };
1844
+ if (opts.noStream) {
1845
+ const spinner = spin("Running\u2026");
1846
+ try {
1847
+ const result = await client.run.once(params);
1848
+ spinner.stop();
1849
+ if (opts.json) return jsonOut(result);
1850
+ const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
1851
+ console.log(text);
1852
+ if (sessionId) console.log(c.dim(`
1853
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
1854
+ } catch (err) {
1855
+ spinner.stop();
1856
+ printError(err);
1857
+ process.exit(1);
1858
+ }
1859
+ return;
1860
+ }
1861
+ try {
1862
+ let hasOutput = false;
1863
+ let toolStartMs = 0;
1864
+ let lastToolName = "";
1865
+ for await (const event of client.agents.stream(params)) {
1866
+ if (opts.json) {
1867
+ console.log(JSON.stringify(event));
1868
+ continue;
1869
+ }
1870
+ if (event.type === "token") {
1871
+ process.stdout.write(event.content ?? "");
1872
+ hasOutput = true;
1873
+ } else if (event.type === "cli_tool_request" && localToolsCfg) {
1874
+ const { requestId, tool: toolName, args } = event;
1875
+ const displayName = String(toolName).replace("cli_", "");
1876
+ if (hasOutput) {
1877
+ process.stdout.write("\n");
1878
+ hasOutput = false;
1879
+ }
1880
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}`);
1881
+ const startMs = Date.now();
1882
+ let result;
1883
+ let toolOk = true;
1884
+ try {
1885
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
1886
+ } catch (err) {
1887
+ result = `Error: ${err?.message ?? String(err)}`;
1888
+ toolOk = false;
1889
+ }
1890
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
1891
+ readline3.cursorTo(process.stdout, 0);
1892
+ readline3.clearLine(process.stdout, 0);
1893
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
1894
+ `);
1895
+ try {
1896
+ await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
1897
+ method: "POST",
1898
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${cfg.apiKey}` },
1899
+ body: JSON.stringify({ requestId, result })
1900
+ });
1901
+ } catch {
1902
+ }
1903
+ } else if (event.type === "toolStart") {
1904
+ lastToolName = event.toolName ?? "";
1905
+ toolStartMs = Date.now();
1906
+ if (hasOutput) {
1907
+ process.stdout.write("\n");
1908
+ hasOutput = false;
1909
+ }
1910
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
1911
+ } else if (event.type === "toolEnd") {
1912
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
1913
+ readline3.cursorTo(process.stdout, 0);
1914
+ readline3.clearLine(process.stdout, 0);
1915
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
1916
+ `);
1917
+ } else if (event.type === "final") {
1918
+ if (hasOutput) process.stdout.write("\n");
1919
+ const e = event;
1920
+ if (e.content && !hasOutput) console.log(e.content);
1921
+ if (sessionId) console.log(c.dim(`
1922
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
1923
+ break;
1924
+ } else if (event.type === "error") {
1925
+ if (hasOutput) process.stdout.write("\n");
1926
+ console.error(`
1927
+ ${sym.fail} ${c.error(event.message ?? "Error")}`);
1928
+ process.exit(1);
1929
+ }
1930
+ }
1931
+ if (hasOutput && !opts.json) process.stdout.write("\n");
1932
+ } catch (err) {
1933
+ printError(err);
1934
+ process.exit(1);
1935
+ }
1936
+ });
1937
+ }
1938
+
1795
1939
  // src/commands/chat.ts
1940
+ var import_commander8 = require("commander");
1941
+ var readline4 = __toESM(require("readline"));
1942
+ var import_fs4 = require("fs");
1943
+ var import_path4 = require("path");
1944
+ var import_os3 = require("os");
1796
1945
  var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
1797
1946
  function ensureSessionsDir() {
1798
1947
  if (!(0, import_fs4.existsSync)(SESSIONS_DIR)) (0, import_fs4.mkdirSync)(SESSIONS_DIR, { recursive: true });
@@ -1929,7 +2078,7 @@ ${c.bold("Agent Commons Chat")}`);
1929
2078
  });
1930
2079
  }
1931
2080
  console.log(c.dim("\nType your message and press Enter. Type /help for commands.\n"));
1932
- const rl = readline3.createInterface({
2081
+ const rl = readline4.createInterface({
1933
2082
  input: process.stdin,
1934
2083
  output: process.stdout,
1935
2084
  terminal: true,
@@ -2018,12 +2167,13 @@ ${content}
2018
2167
  messages: [{ role: "user", content: userMessage }],
2019
2168
  ...cliContext && { cliContext }
2020
2169
  };
2021
- process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2022
2170
  if (opts.noStream) {
2023
- const spinner = spin("");
2171
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2172
+ const spinner = spin("thinking\u2026");
2024
2173
  try {
2025
2174
  const result = await client.run.once(params);
2026
2175
  spinner.stop();
2176
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2027
2177
  const text = extractText(result);
2028
2178
  console.log(text);
2029
2179
  appendSessionLog(sessionId, {
@@ -2041,29 +2191,57 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2041
2191
  try {
2042
2192
  let hasOutput = false;
2043
2193
  let agentContent = "";
2194
+ let toolStartMs = 0;
2195
+ let lastToolName = "";
2196
+ const thinkingSpinner = spin("thinking\u2026");
2044
2197
  for await (const event of client.agents.stream(params)) {
2045
2198
  if (event.type === "token") {
2199
+ if (thinkingSpinner.isSpinning) {
2200
+ thinkingSpinner.stop();
2201
+ process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2202
+ }
2046
2203
  const tok = event.content ?? "";
2047
2204
  process.stdout.write(tok);
2048
2205
  agentContent += tok;
2049
2206
  hasOutput = true;
2050
2207
  } else if (event.type === "cli_tool_request" && localToolsCfg) {
2208
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2051
2209
  const { requestId, tool: toolName, args } = event;
2052
2210
  const displayName = String(toolName).replace("cli_", "");
2211
+ const argStr = toolArgSummary(displayName, args ?? {});
2212
+ const isWaiting = displayName === "wait_for_process";
2053
2213
  if (hasOutput) {
2054
2214
  process.stdout.write("\n");
2055
2215
  hasOutput = false;
2056
2216
  }
2057
- process.stdout.write(c.dim(` [local] ${displayName}\u2026`));
2217
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""}`);
2218
+ const startMs = Date.now();
2219
+ let elapsedSec = 0;
2220
+ let elapsedInterval = null;
2221
+ if (isWaiting) {
2222
+ elapsedInterval = setInterval(() => {
2223
+ elapsedSec++;
2224
+ readline4.cursorTo(process.stdout, 0);
2225
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${c.dim(elapsedSec + "s\u2026")}`);
2226
+ }, 1e3);
2227
+ }
2058
2228
  let result;
2229
+ let toolOk = true;
2059
2230
  try {
2060
- const localToolName = String(toolName).replace("cli_", "");
2061
- result = await runLocalTool({ tool: localToolName, args: args ?? {} }, localToolsCfg);
2062
- process.stdout.write(c.dim(" \u2713\n"));
2231
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
2063
2232
  } catch (err) {
2064
2233
  result = `Error: ${err?.message ?? String(err)}`;
2065
- process.stdout.write(c.dim(" \u2717\n"));
2234
+ toolOk = false;
2066
2235
  }
2236
+ if (elapsedInterval) clearInterval(elapsedInterval);
2237
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2238
+ const preview = toolOk ? toolResultPreview(displayName, result) : "";
2239
+ readline4.cursorTo(process.stdout, 0);
2240
+ readline4.clearLine(process.stdout, 0);
2241
+ const statusIcon = toolOk ? sym.ok : sym.fail;
2242
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
2243
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
2244
+ `);
2067
2245
  appendSessionLog(sessionId, {
2068
2246
  type: "local_tool_result",
2069
2247
  tool: toolName,
@@ -2083,14 +2261,20 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2083
2261
  console.error(c.warn(`
2084
2262
  [local] Failed to submit tool result: ${postErr?.message}`));
2085
2263
  }
2086
- } else if (event.type === "ping") {
2264
+ } else if (event.type === "keepalive") {
2087
2265
  } else if (event.type === "toolStart") {
2088
- const name = event.toolName ?? "";
2266
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2267
+ lastToolName = event.toolName ?? "";
2268
+ toolStartMs = Date.now();
2089
2269
  if (hasOutput) process.stdout.write("\n");
2090
- process.stdout.write(c.dim(` [tool] ${name}\u2026`));
2270
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
2091
2271
  hasOutput = false;
2092
2272
  } else if (event.type === "toolEnd") {
2093
- process.stdout.write(c.dim(" done\n"));
2273
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2274
+ readline4.cursorTo(process.stdout, 0);
2275
+ readline4.clearLine(process.stdout, 0);
2276
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2277
+ `);
2094
2278
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2095
2279
  hasOutput = false;
2096
2280
  } else if (event.type === "final") {
@@ -2127,12 +2311,14 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2127
2311
  }
2128
2312
  break;
2129
2313
  } else if (event.type === "error") {
2314
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2130
2315
  if (hasOutput) process.stdout.write("\n");
2131
2316
  console.error(`
2132
2317
  ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2133
2318
  break;
2134
2319
  }
2135
2320
  }
2321
+ if (thinkingSpinner.isSpinning) thinkingSpinner.stop();
2136
2322
  process.stdout.write("\n");
2137
2323
  if (localToolsCfg && agentContent) {
2138
2324
  await handleLocalToolLoop(agentContent, localToolsCfg, client, agentId, sessionId, appendSessionLog);
@@ -2143,6 +2329,8 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2143
2329
  }
2144
2330
  }
2145
2331
  console.log();
2332
+ readline4.cursorTo(process.stdout, 0);
2333
+ readline4.clearLine(process.stdout, 0);
2146
2334
  rl.resume();
2147
2335
  rl.prompt();
2148
2336
  });
@@ -2166,16 +2354,25 @@ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, a
2166
2354
  }
2167
2355
  const toolCall = extractToolCall(agentText);
2168
2356
  if (!toolCall) return;
2169
- process.stdout.write(c.dim(`
2170
- [local] ${toolCall.tool}`));
2357
+ const argStr = toolArgSummary(toolCall.tool, toolCall.args ?? {});
2358
+ process.stdout.write(`
2359
+ ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""}`);
2360
+ const startMs = Date.now();
2171
2361
  let result;
2362
+ let toolOk = true;
2172
2363
  try {
2173
2364
  result = await runLocalTool(toolCall, cfg);
2174
- process.stdout.write(c.dim(" \u2713\n"));
2175
2365
  } catch (err) {
2176
2366
  result = `Error: ${err?.message ?? String(err)}`;
2177
- process.stdout.write(c.dim(" \u2717\n"));
2367
+ toolOk = false;
2178
2368
  }
2369
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2370
+ const preview = toolOk ? toolResultPreview(toolCall.tool, result) : "";
2371
+ readline4.cursorTo(process.stdout, 0);
2372
+ readline4.clearLine(process.stdout, 0);
2373
+ const previewPart = preview ? ` ${c.dim(preview)}` : "";
2374
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
2375
+ `);
2179
2376
  const resultMsg = `[Tool result: ${toolCall.tool}]
2180
2377
  \`\`\`
2181
2378
  ${result}
@@ -2190,6 +2387,8 @@ ${result}
2190
2387
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2191
2388
  let followContent = "";
2192
2389
  try {
2390
+ let loopToolName = "";
2391
+ let loopToolStartMs = 0;
2193
2392
  for await (const evt of client.agents.stream({
2194
2393
  agentId,
2195
2394
  sessionId,
@@ -2200,11 +2399,16 @@ ${result}
2200
2399
  process.stdout.write(tok);
2201
2400
  followContent += tok;
2202
2401
  } else if (evt.type === "toolStart") {
2203
- const name = evt.toolName ?? "";
2402
+ loopToolName = evt.toolName ?? "";
2403
+ loopToolStartMs = Date.now();
2204
2404
  if (followContent) process.stdout.write("\n");
2205
- process.stdout.write(c.dim(` [tool] ${name}\u2026`));
2405
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)}`);
2206
2406
  } else if (evt.type === "toolEnd") {
2207
- process.stdout.write(c.dim(" done\n"));
2407
+ const elapsed2 = ((Date.now() - loopToolStartMs) / 1e3).toFixed(1);
2408
+ readline4.cursorTo(process.stdout, 0);
2409
+ readline4.clearLine(process.stdout, 0);
2410
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)} ${sym.ok} ${c.dim("(" + elapsed2 + "s)")}
2411
+ `);
2208
2412
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
2209
2413
  } else if (evt.type === "final") {
2210
2414
  const txt = extractText(evt?.payload);
@@ -2228,6 +2432,80 @@ ${sym.fail} ${c.error(evt.message ?? "Stream error")}`);
2228
2432
  }
2229
2433
  await handleLocalToolLoop(followContent, cfg, client, agentId, sessionId, appendLog, depth + 1);
2230
2434
  }
2435
+ function truncate(s, max) {
2436
+ const str = String(s ?? "");
2437
+ return str.length <= max ? str : str.slice(0, max - 1) + "\u2026";
2438
+ }
2439
+ function toolArgSummary(toolName, args) {
2440
+ switch (toolName) {
2441
+ case "read_file":
2442
+ return truncate(args.path ?? "", 60);
2443
+ case "write_file":
2444
+ return truncate(args.path ?? "", 60);
2445
+ case "delete_file":
2446
+ return truncate(args.path ?? "", 60);
2447
+ case "list_directory":
2448
+ return truncate(args.path ?? ".", 60);
2449
+ case "run_command":
2450
+ return truncate(args.command ?? "", 60);
2451
+ case "start_process":
2452
+ return truncate(args.command ?? "", 60);
2453
+ case "wait_for_process":
2454
+ return truncate(args.process_id ?? "", 20);
2455
+ case "process_status":
2456
+ return truncate(args.process_id ?? "", 20);
2457
+ case "kill_process":
2458
+ return truncate(args.process_id ?? "", 20);
2459
+ case "list_processes":
2460
+ return "";
2461
+ case "search_files": {
2462
+ const parts = [args.pattern, args.query].filter(Boolean);
2463
+ return truncate(parts.join(" "), 60);
2464
+ }
2465
+ default: {
2466
+ const first = args.path ?? args.query ?? args.command ?? args.pattern ?? "";
2467
+ return truncate(String(first), 60);
2468
+ }
2469
+ }
2470
+ }
2471
+ function toolResultPreview(toolName, result) {
2472
+ if (!result || result.startsWith("Error:")) return "";
2473
+ switch (toolName) {
2474
+ case "read_file": {
2475
+ const lines = result.split("\n").length;
2476
+ return `${lines} lines`;
2477
+ }
2478
+ case "write_file":
2479
+ return "written";
2480
+ case "delete_file":
2481
+ return "deleted";
2482
+ case "list_directory": {
2483
+ const count = result.split("\n").filter(Boolean).length;
2484
+ return `${count} entries`;
2485
+ }
2486
+ case "run_command": {
2487
+ const first = result.split("\n").find((l) => l.trim());
2488
+ return first ? truncate(first.trim(), 50) : "done";
2489
+ }
2490
+ case "start_process": {
2491
+ const match = result.match(/process[_\s-]?id[:\s]+([a-zA-Z0-9_-]+)/i) ?? result.match(/"id"[:\s]+"([^"]+)"/);
2492
+ return match ? `pid ${match[1]}` : "started";
2493
+ }
2494
+ case "wait_for_process": {
2495
+ if (/done|complete|exit/i.test(result)) return "done";
2496
+ if (/running/i.test(result)) return "still running";
2497
+ return truncate(result.split("\n")[0]?.trim() ?? "", 40);
2498
+ }
2499
+ case "search_files": {
2500
+ const count = result.split("\n").filter(Boolean).length;
2501
+ return `${count} match${count === 1 ? "" : "es"}`;
2502
+ }
2503
+ default: {
2504
+ const first = result.split("\n").find((l) => l.trim());
2505
+ return first ? truncate(first.trim(), 50) : "";
2506
+ }
2507
+ }
2508
+ }
2231
2509
  function extractText(payload) {
2232
2510
  if (!payload) return "";
2233
2511
  if (typeof payload === "string") return payload;
@@ -2766,8 +3044,8 @@ function skillsCommand() {
2766
3044
  });
2767
3045
  cmd.command("delete <slug>").description("Permanently delete a skill").option("--yes", "Skip confirmation prompt").option("--json", "Output result as JSON").action(async (slug, opts) => {
2768
3046
  if (!opts.yes) {
2769
- const readline4 = await import("readline");
2770
- const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
3047
+ const readline5 = await import("readline");
3048
+ const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
2771
3049
  const answer = await new Promise(
2772
3050
  (resolve2) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve2)
2773
3051
  );
@@ -3523,7 +3801,7 @@ async function pickAgentInteractively(action) {
3523
3801
  return agentId;
3524
3802
  }
3525
3803
  var program = new import_commander16.Command();
3526
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.16", "-v, --version").action(async () => {
3804
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.18", "-v, --version").action(async () => {
3527
3805
  await interactiveMenu();
3528
3806
  });
3529
3807
  program.addCommand(loginCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-commons/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Agent Commons CLI — chat, run, and manage agents from your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -11,15 +11,17 @@
11
11
  "dist"
12
12
  ],
13
13
  "dependencies": {
14
- "commander": "^12.1.0",
14
+ "@types/pdf-parse": "^1.1.5",
15
15
  "chalk": "^5.3.0",
16
+ "commander": "^12.1.0",
16
17
  "ora": "^8.1.1",
17
- "@agent-commons/sdk": "0.1.12"
18
+ "pdf-parse": "^1.1.1",
19
+ "@agent-commons/sdk": "0.1.13"
18
20
  },
19
21
  "devDependencies": {
22
+ "@types/node": "^22.10.2",
20
23
  "tsup": "^8.3.5",
21
- "typescript": "^5.7.2",
22
- "@types/node": "^22.10.2"
24
+ "typescript": "^5.7.2"
23
25
  },
24
26
  "engines": {
25
27
  "node": ">=18"