@agent-commons/cli 0.1.17 → 0.2.0

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 +314 -116
  2. package/package.json +2 -2
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.17") {
109
+ function banner(version = "0.2.0") {
110
110
  const line = import_chalk.default.cyan(" \u2500".padEnd(2) + "\u2500".repeat(44));
111
111
  console.log("");
112
112
  console.log(line);
@@ -482,7 +482,7 @@ function agentsCommand() {
482
482
  process.exit(1);
483
483
  }
484
484
  });
485
- cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option("--provider <provider>", "Model provider (openai|anthropic|google|groq)", "openai").option("--model <id>", "Model ID", "gpt-4o").option("--json", "Output as JSON").action(async (opts) => {
485
+ cmd.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").option("--instructions <text>", "System instructions").option("--provider <provider>", "Model provider (openai|anthropic|google|groq|openrouter|xai|ollama|custom)", "openai").option("--model <id>", "Model ID", "gpt-5.4-mini").option("--model-api-key <key>", "Provider API key (BYOK)").option("--model-base-url <url>", "Base URL for custom or local OpenAI-compatible providers").option("--json", "Output as JSON").action(async (opts) => {
486
486
  const cfg = loadConfig();
487
487
  if (!cfg.initiator) {
488
488
  console.error(c.error("No initiator set. Run `agc login` first."));
@@ -496,7 +496,9 @@ function agentsCommand() {
496
496
  instructions: opts.instructions,
497
497
  owner: cfg.initiator,
498
498
  modelProvider: opts.provider,
499
- modelId: opts.model
499
+ modelId: opts.model,
500
+ modelApiKey: opts.modelApiKey,
501
+ modelBaseUrl: opts.modelBaseUrl
500
502
  });
501
503
  const agent = res?.data ?? res;
502
504
  spinner.stop();
@@ -645,7 +647,7 @@ function sessionsCommand() {
645
647
  process.exit(1);
646
648
  }
647
649
  });
648
- cmd.command("create").description("Create a new session").option("--agent <agentId>", "Agent ID").option("--title <title>", "Session title").option("--model <id>", "Model ID (e.g. gpt-4o, claude-sonnet-4-6)").option("--provider <provider>", "Model provider").option("--json", "Output as JSON").action(async (opts) => {
650
+ cmd.command("create").description("Create a new session").option("--agent <agentId>", "Agent ID").option("--title <title>", "Session title").option("--model <id>", "Model ID (e.g. gpt-5.4-mini, claude-sonnet-4-6)").option("--provider <provider>", "Model provider").option("--json", "Output as JSON").action(async (opts) => {
649
651
  const cfg = loadConfig();
650
652
  const agentId = opts.agent ?? cfg.defaultAgentId;
651
653
  if (!agentId) {
@@ -1184,73 +1186,7 @@ ${sym.fail} ${c.error(event.message ?? event.type)}`);
1184
1186
 
1185
1187
  // src/commands/run.ts
1186
1188
  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
1189
  var readline3 = __toESM(require("readline"));
1251
- var import_fs4 = require("fs");
1252
- var import_path4 = require("path");
1253
- var import_os3 = require("os");
1254
1190
 
1255
1191
  // src/local-tools.ts
1256
1192
  var import_fs3 = require("fs");
@@ -1310,7 +1246,7 @@ function readFileForContext(rootDir, filePath) {
1310
1246
  return `[error reading file: ${err?.message}]`;
1311
1247
  }
1312
1248
  }
1313
- function buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks = []) {
1249
+ function buildLocalToolsManifest(rootDir, snapshot, fileContextBlocks = [], autoApprove = false) {
1314
1250
  const fileSection = fileContextBlocks.length ? `
1315
1251
  ### File contents included in this turn
1316
1252
 
@@ -1333,10 +1269,12 @@ ${fileSection}
1333
1269
  ### MANDATORY RULES \u2014 READ CAREFULLY
1334
1270
 
1335
1271
  1. **Call cli_* tools immediately and directly.** Do NOT create tasks (createTask) for local file operations. Do NOT delegate to sub-agents. Do NOT ask the user to run commands themselves.
1336
- 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.
1337
- 3. **Never fabricate results.** Wait for the real tool output before responding.
1338
- 4. **Sensitive paths are blocked** (.ssh, .gnupg, .aws, .env, credentials). Attempting to access them will return an error.
1339
- 5. **cli_write_file and cli_run_command require the user to confirm** before executing \u2014 you will see the result after they approve.
1272
+ 2. **Own the request through completion.** Continue across tool calls, process polling, retries, debugging, and verification. Do not stop after describing a plan or asking whether to proceed when the request is already clear.
1273
+ 3. **Use actual tool output as evidence.** Summarize the important result; do not fabricate success or dump noisy logs unless they help diagnose a failure.
1274
+ 4. **Never fabricate results.** Wait for the real tool output before responding.
1275
+ 5. **Sensitive paths are blocked** (.ssh, .gnupg, .aws, .env, credentials). Attempting to access them will return an error.
1276
+ 6. ${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."}
1277
+ 7. **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\`.
1340
1278
 
1341
1279
  ### Available CLI tools
1342
1280
 
@@ -1369,12 +1307,12 @@ ${fileSection}
1369
1307
 
1370
1308
  For long commands like \`npx create-next-app@latest my-app --yes\`:
1371
1309
 
1372
- 1. Call \`cli_start_process\` \u2014 returns \`{processId, status: "running"}\` immediately. Tell the user it has started.
1373
- 2. Call \`cli_wait_for_process\` with \`{"processId": "...", "wait_seconds": 60}\` \u2014 blocks up to 60s then returns current stdout/status. Report progress to the user.
1374
- 3. Repeat step 2 until \`status\` is \`"done"\` or \`"error"\`.
1375
- 4. Report the final output to the user.
1310
+ 1. Call \`cli_start_process\` \u2014 it returns \`{processId, status: "running"}\` immediately.
1311
+ 2. Call \`cli_wait_for_process\` with \`{"processId": "...", "wait_seconds": 60}\`.
1312
+ 3. Repeat step 2 until \`status\` is \`"done"\` or \`"error"\`; diagnose and repair errors when possible.
1313
+ 4. Continue with the rest of the assignment and verify the final outcome before responding.
1376
1314
 
1377
- Never hold the user in silence. Between each \`cli_wait_for_process\` call, tell them what you saw so far.
1315
+ Do not end the turn merely because a process is still running. Keep polling within the same run. Progress events may be streamed by the client, but the final answer comes only after completion or a genuine blocker.
1378
1316
 
1379
1317
  ### Example \u2014 scaffolding a Next.js project
1380
1318
 
@@ -1382,18 +1320,14 @@ Never hold the user in silence. Between each \`cli_wait_for_process\` call, tell
1382
1320
  cli_start_process: {"command": "npx", "args": ["create-next-app@latest", "my-app", "--yes"], "cwd": "Desktop"}
1383
1321
  \u2192 {processId: "proc_1a2b", status: "running"}
1384
1322
 
1385
- Tell user: "Started! Installing dependencies, this takes a minute or two. Checking in 60s\u2026"
1386
-
1387
1323
  cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
1388
1324
  \u2192 {status: "running", elapsedSec: 60, stdout: "Creating project...
1389
1325
  Installing packages\u2026"}
1390
1326
 
1391
- Tell user: "Still installing \u2014 here's output so far: [stdout]. Checking again\u2026"
1392
-
1393
1327
  cli_wait_for_process: {"processId": "proc_1a2b", "wait_seconds": 60}
1394
1328
  \u2192 {status: "done", exitCode: 0, elapsedSec: 93, stdout: "Success! Created my-app"}
1395
1329
 
1396
- Tell user: "Done! Project created in Desktop/my-app"
1330
+ Continue by running the requested checks and opening/inspecting the app when the assignment requires it.
1397
1331
  \`\`\`
1398
1332
  `;
1399
1333
  }
@@ -1408,6 +1342,72 @@ function extractToolCall(text) {
1408
1342
  }
1409
1343
  return null;
1410
1344
  }
1345
+ function injectAgcTrailer(command, args, agentId, agentName) {
1346
+ if (command !== "git") return args;
1347
+ if (!args.some((a) => a === "commit")) return args;
1348
+ if (args.some((a) => a.includes("Co-Authored-By: agc"))) return args;
1349
+ const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
1350
+ return [...args, "--trailer", `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`];
1351
+ }
1352
+ var AGC_HOOK_MARKER = "# agc-session:";
1353
+ var HOOK_BACKUP_SUFFIX = ".agc-backup";
1354
+ function findGitDir(rootDir) {
1355
+ const gitPath = (0, import_path3.join)(rootDir, ".git");
1356
+ if (!(0, import_fs3.existsSync)(gitPath)) return null;
1357
+ const s = (0, import_fs3.statSync)(gitPath);
1358
+ if (s.isDirectory()) return gitPath;
1359
+ if (s.isFile()) {
1360
+ const content = (0, import_fs3.readFileSync)(gitPath, "utf8");
1361
+ const match = content.match(/^gitdir:\s*(.+)$/m);
1362
+ if (match) return match[1].trim();
1363
+ }
1364
+ return null;
1365
+ }
1366
+ function installGitHook(rootDir, sessionId, agentId, agentName) {
1367
+ const gitDir = findGitDir(rootDir);
1368
+ if (!gitDir) return;
1369
+ const hooksDir = (0, import_path3.join)(gitDir, "hooks");
1370
+ (0, import_fs3.mkdirSync)(hooksDir, { recursive: true });
1371
+ const hookPath = (0, import_path3.join)(hooksDir, "prepare-commit-msg");
1372
+ if ((0, import_fs3.existsSync)(hookPath)) {
1373
+ const existing = (0, import_fs3.readFileSync)(hookPath, "utf8");
1374
+ if (!existing.includes(AGC_HOOK_MARKER)) {
1375
+ (0, import_fs3.writeFileSync)(hookPath + HOOK_BACKUP_SUFFIX, existing, { mode: 493 });
1376
+ }
1377
+ }
1378
+ const identity = agentName ? `${agentName} (agc)` : agentId ? `agc/${agentId}` : "agc agent";
1379
+ const trailer = `Co-Authored-By: ${identity} <agc-agent@users.noreply.github.com>`;
1380
+ const chainLine = (0, import_fs3.existsSync)(hookPath + HOOK_BACKUP_SUFFIX) ? `
1381
+ # chain pre-existing hook
1382
+ "$(dirname "$0")/prepare-commit-msg${HOOK_BACKUP_SUFFIX}" "$@" 2>/dev/null || true
1383
+ ` : "";
1384
+ const hook = `#!/bin/sh
1385
+ ${AGC_HOOK_MARKER}${sessionId}
1386
+ COMMIT_MSG_FILE="$1"
1387
+ COMMIT_SOURCE="$2"
1388
+ ${chainLine}
1389
+ case "$COMMIT_SOURCE" in merge|squash) exit 0 ;; esac
1390
+ TRAILER="${trailer}"
1391
+ grep -qF "$TRAILER" "$COMMIT_MSG_FILE" 2>/dev/null && exit 0
1392
+ printf '\\n%s\\n' "$TRAILER" >> "$COMMIT_MSG_FILE"
1393
+ `;
1394
+ (0, import_fs3.writeFileSync)(hookPath, hook, { mode: 493 });
1395
+ }
1396
+ function removeGitHook(rootDir) {
1397
+ const gitDir = findGitDir(rootDir);
1398
+ if (!gitDir) return;
1399
+ const hookPath = (0, import_path3.join)(gitDir, "hooks", "prepare-commit-msg");
1400
+ if (!(0, import_fs3.existsSync)(hookPath)) return;
1401
+ const content = (0, import_fs3.readFileSync)(hookPath, "utf8");
1402
+ if (!content.includes(AGC_HOOK_MARKER)) return;
1403
+ const backupPath = hookPath + HOOK_BACKUP_SUFFIX;
1404
+ if ((0, import_fs3.existsSync)(backupPath)) {
1405
+ (0, import_fs3.writeFileSync)(hookPath, (0, import_fs3.readFileSync)(backupPath, "utf8"), { mode: 493 });
1406
+ (0, import_fs3.unlinkSync)(backupPath);
1407
+ } else {
1408
+ (0, import_fs3.unlinkSync)(hookPath);
1409
+ }
1410
+ }
1411
1411
  function safePath(root, userPath) {
1412
1412
  const abs = (0, import_path3.resolve)(root, userPath);
1413
1413
  const rel = (0, import_path3.relative)(root, abs);
@@ -1434,6 +1434,7 @@ function assertNotSensitive(abs) {
1434
1434
  }
1435
1435
  }
1436
1436
  async function confirm(message, config, permissionKey) {
1437
+ if (config.autoApprove) return true;
1437
1438
  const cached = config.permissions.get(permissionKey);
1438
1439
  if (cached === "allow") return true;
1439
1440
  if (cached === "deny") return false;
@@ -1612,7 +1613,8 @@ async function toolRunCommand(args, cfg) {
1612
1613
  if (!command || typeof command !== "string") throw new Error('run_command requires a "command" string');
1613
1614
  if (!Array.isArray(cmdArgs)) throw new Error('"args" must be an array of strings');
1614
1615
  const workDir = cwd ? safePath(cfg.rootDir, cwd) : cfg.rootDir;
1615
- const preview = [command, ...cmdArgs].join(" ");
1616
+ const injectedArgs = injectAgcTrailer(command, cmdArgs, cfg.agentId, cfg.agentName);
1617
+ const preview = [command, ...injectedArgs].join(" ");
1616
1618
  const timeoutMs = Math.min((typeof timeout_seconds === "number" ? timeout_seconds : 120) * 1e3, 3e5);
1617
1619
  const ok = await confirm(
1618
1620
  `Agent wants to run: \x1B[1m${preview}\x1B[0m
@@ -1623,7 +1625,7 @@ async function toolRunCommand(args, cfg) {
1623
1625
  if (!ok) return "User denied command execution.";
1624
1626
  if (interactive) {
1625
1627
  return new Promise((resolve2) => {
1626
- const child = (0, import_child_process2.spawn)(command, cmdArgs.map(String), { cwd: workDir, stdio: "inherit" });
1628
+ const child = (0, import_child_process2.spawn)(command, injectedArgs.map(String), { cwd: workDir, stdio: "inherit" });
1627
1629
  const timer = setTimeout(() => {
1628
1630
  child.kill();
1629
1631
  resolve2(`(command timed out after ${timeoutMs / 1e3}s)`);
@@ -1639,7 +1641,7 @@ async function toolRunCommand(args, cfg) {
1639
1641
  });
1640
1642
  }
1641
1643
  return new Promise((resolve2) => {
1642
- (0, import_child_process2.execFile)(command, cmdArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
1644
+ (0, import_child_process2.execFile)(command, injectedArgs.map(String), { cwd: workDir, timeout: timeoutMs, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
1643
1645
  const out = [stdout, stderr].filter(Boolean).join("\n--- stderr ---\n");
1644
1646
  if (err && !out) return resolve2(`Error: ${err.message}`);
1645
1647
  resolve2(out || "(no output)");
@@ -1814,7 +1816,191 @@ async function runLocalTool(call, cfg) {
1814
1816
  return result;
1815
1817
  }
1816
1818
 
1819
+ // src/commands/run.ts
1820
+ function runCommand() {
1821
+ 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) => {
1822
+ const cfg = loadConfig();
1823
+ const agentId = opts.agent ?? cfg.defaultAgentId;
1824
+ if (!agentId) {
1825
+ console.error(c.error("Specify --agent <agentId> or set defaultAgentId with `agc config set defaultAgentId <id>`"));
1826
+ process.exit(1);
1827
+ }
1828
+ if (opts.session && opts.newSession) {
1829
+ console.error(c.error("Cannot use --session and --new-session together."));
1830
+ process.exit(1);
1831
+ }
1832
+ const client = makeClient();
1833
+ let sessionId = opts.session;
1834
+ if (opts.session) {
1835
+ const spinner = spin("Loading session\u2026");
1836
+ try {
1837
+ await client.sessions.get(opts.session);
1838
+ spinner.stop();
1839
+ } catch {
1840
+ spinner.stop();
1841
+ console.error(c.error(`Session "${opts.session}" not found.`));
1842
+ process.exit(1);
1843
+ }
1844
+ }
1845
+ if (opts.newSession) {
1846
+ const spinner = spin("Creating session\u2026");
1847
+ try {
1848
+ const res = await client.sessions.create({
1849
+ agentId,
1850
+ initiator: cfg.initiator ?? "",
1851
+ title: `agc run ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}`,
1852
+ source: "cli"
1853
+ });
1854
+ const session = res?.data ?? res;
1855
+ sessionId = session.sessionId;
1856
+ spinner.stop();
1857
+ } catch (err) {
1858
+ spinner.stop();
1859
+ printError(err);
1860
+ process.exit(1);
1861
+ }
1862
+ }
1863
+ const localEnabled = opts.yes || opts.local;
1864
+ const autoApprove = !!opts.yes;
1865
+ let localToolsCfg = null;
1866
+ let cliContext;
1867
+ if (localEnabled) {
1868
+ const rootDir = process.cwd();
1869
+ localToolsCfg = {
1870
+ rootDir,
1871
+ sessionId: sessionId ?? "run",
1872
+ appendLog: () => {
1873
+ },
1874
+ permissions: /* @__PURE__ */ new Map(),
1875
+ agentId,
1876
+ autoApprove
1877
+ };
1878
+ const snapshot = buildDirSnapshot(rootDir, 2);
1879
+ cliContext = buildLocalToolsManifest(rootDir, snapshot, [], autoApprove);
1880
+ }
1881
+ if (!opts.json) {
1882
+ const rows = [];
1883
+ if (sessionId) {
1884
+ const label = opts.newSession ? `${c.id(sessionId)}${c.dim(" (new)")}` : `${c.id(sessionId)}${c.dim(" (resumed)")}`;
1885
+ rows.push(["Session", label]);
1886
+ }
1887
+ if (localEnabled) {
1888
+ rows.push(["Local tools", autoApprove ? c.warn("enabled (auto-approve on)") : c.success("enabled")]);
1889
+ }
1890
+ if (rows.length) {
1891
+ detail(rows);
1892
+ console.log();
1893
+ }
1894
+ }
1895
+ const params = {
1896
+ agentId,
1897
+ sessionId,
1898
+ messages: [{ role: "user", content: prompt2 }],
1899
+ ...cfg.initiator && { initiatorId: cfg.initiator },
1900
+ ...cliContext && { cliContext }
1901
+ };
1902
+ if (opts.noStream && !localEnabled) {
1903
+ const spinner = spin("Running\u2026");
1904
+ try {
1905
+ const result = await client.run.once(params);
1906
+ spinner.stop();
1907
+ if (opts.json) return jsonOut(result);
1908
+ const text = result?.content ?? result?.text ?? result?.message ?? JSON.stringify(result);
1909
+ console.log(text);
1910
+ if (sessionId) console.log(c.dim(`
1911
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
1912
+ } catch (err) {
1913
+ spinner.stop();
1914
+ printError(err);
1915
+ process.exit(1);
1916
+ }
1917
+ return;
1918
+ }
1919
+ try {
1920
+ let hasOutput = false;
1921
+ let toolStartMs = 0;
1922
+ let lastToolName = "";
1923
+ for await (const event of client.agents.stream(params)) {
1924
+ if (opts.json) {
1925
+ console.log(JSON.stringify(event));
1926
+ continue;
1927
+ }
1928
+ if (event.type === "token") {
1929
+ process.stdout.write(event.content ?? "");
1930
+ hasOutput = true;
1931
+ } else if (event.type === "cli_tool_request" && localToolsCfg) {
1932
+ const { requestId, tool: toolName, args } = event;
1933
+ const displayName = String(toolName).replace("cli_", "");
1934
+ if (hasOutput) {
1935
+ process.stdout.write("\n");
1936
+ hasOutput = false;
1937
+ }
1938
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}`);
1939
+ const startMs = Date.now();
1940
+ let result;
1941
+ let toolOk = true;
1942
+ try {
1943
+ result = await runLocalTool({ tool: displayName, args: args ?? {} }, localToolsCfg);
1944
+ } catch (err) {
1945
+ result = `Error: ${err?.message ?? String(err)}`;
1946
+ toolOk = false;
1947
+ }
1948
+ const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
1949
+ readline3.cursorTo(process.stdout, 0);
1950
+ readline3.clearLine(process.stdout, 0);
1951
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)} ${toolOk ? sym.ok : sym.fail} ${c.dim("(" + elapsed + "s)")}
1952
+ `);
1953
+ try {
1954
+ await fetch(`${cfg.apiUrl}/v1/agents/cli-tool-result`, {
1955
+ method: "POST",
1956
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${cfg.apiKey}` },
1957
+ body: JSON.stringify({ requestId, result })
1958
+ });
1959
+ } catch {
1960
+ }
1961
+ } else if (event.type === "toolStart") {
1962
+ lastToolName = event.toolName ?? "";
1963
+ toolStartMs = Date.now();
1964
+ if (hasOutput) {
1965
+ process.stdout.write("\n");
1966
+ hasOutput = false;
1967
+ }
1968
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)}`);
1969
+ } else if (event.type === "toolEnd") {
1970
+ const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
1971
+ readline3.cursorTo(process.stdout, 0);
1972
+ readline3.clearLine(process.stdout, 0);
1973
+ process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
1974
+ `);
1975
+ } else if (event.type === "final") {
1976
+ if (hasOutput) process.stdout.write("\n");
1977
+ const e = event;
1978
+ const finalText = e.content ?? e.payload?.content ?? e.payload?.text ?? e.payload?.message;
1979
+ if (finalText && !hasOutput) console.log(finalText);
1980
+ if (sessionId) console.log(c.dim(`
1981
+ Session: ${sessionId} (resume with: agc run --session ${sessionId} "<prompt>")`));
1982
+ break;
1983
+ } else if (event.type === "error") {
1984
+ if (hasOutput) process.stdout.write("\n");
1985
+ console.error(`
1986
+ ${sym.fail} ${c.error(event.message ?? "Error")}`);
1987
+ process.exit(1);
1988
+ }
1989
+ }
1990
+ if (hasOutput && !opts.json) process.stdout.write("\n");
1991
+ } catch (err) {
1992
+ printError(err);
1993
+ process.exit(1);
1994
+ }
1995
+ });
1996
+ }
1997
+
1817
1998
  // src/commands/chat.ts
1999
+ var import_commander8 = require("commander");
2000
+ var readline4 = __toESM(require("readline"));
2001
+ var import_fs4 = require("fs");
2002
+ var import_path4 = require("path");
2003
+ var import_os3 = require("os");
1818
2004
  var SESSIONS_DIR = (0, import_path4.join)((0, import_os3.homedir)(), ".agc", "sessions");
1819
2005
  function ensureSessionsDir() {
1820
2006
  if (!(0, import_fs4.existsSync)(SESSIONS_DIR)) (0, import_fs4.mkdirSync)(SESSIONS_DIR, { recursive: true });
@@ -1912,23 +2098,27 @@ function chatCommand() {
1912
2098
  process.exit(1);
1913
2099
  }
1914
2100
  }
2101
+ let agentName;
1915
2102
  let walletLine = "";
1916
- try {
1917
- const primary = await client.wallets.primary(agentId);
1918
- const w = primary?.data ?? primary;
1919
- if (w?.id) {
1920
- const bal = await client.wallets.balance(w.id).catch(() => null);
1921
- const b = bal?.data ?? bal;
1922
- const addr = `${w.address.slice(0, 6)}\u2026${w.address.slice(-4)}`;
1923
- const usdc = b?.usdc ?? "0";
1924
- walletLine = `${addr} ${c.bold(usdc + " USDC")}`;
1925
- }
1926
- } catch {
1927
- }
2103
+ await Promise.allSettled([
2104
+ client.agents.get(agentId).then((res) => {
2105
+ agentName = (res?.data ?? res)?.name;
2106
+ }),
2107
+ client.wallets.primary(agentId).then(async (primary) => {
2108
+ const w = primary?.data ?? primary;
2109
+ if (w?.id) {
2110
+ const bal = await client.wallets.balance(w.id).catch(() => null);
2111
+ const b = bal?.data ?? bal;
2112
+ const addr = `${w.address.slice(0, 6)}\u2026${w.address.slice(-4)}`;
2113
+ const usdc = b?.usdc ?? "0";
2114
+ walletLine = `${addr} ${c.bold(usdc + " USDC")}`;
2115
+ }
2116
+ })
2117
+ ]);
1928
2118
  console.log(`
1929
2119
  ${c.bold("Agent Commons Chat")}`);
1930
2120
  const headerRows = [
1931
- ["Agent", agentId],
2121
+ ["Agent", agentName ? `${agentName} ${c.dim(agentId)}` : agentId],
1932
2122
  ["Session", c.id(sessionId) + (isResume ? c.dim(" (resumed)") : c.dim(" (new)"))]
1933
2123
  ];
1934
2124
  if (walletLine) headerRows.push(["Wallet", walletLine]);
@@ -1941,9 +2131,12 @@ ${c.bold("Agent Commons Chat")}`);
1941
2131
  localToolsCfg = {
1942
2132
  rootDir,
1943
2133
  sessionId,
2134
+ agentId,
2135
+ agentName,
1944
2136
  appendLog: (record) => appendSessionLog(sessionId, record),
1945
2137
  permissions: /* @__PURE__ */ new Map()
1946
2138
  };
2139
+ installGitHook(rootDir, sessionId, agentId, agentName);
1947
2140
  appendSessionLog(sessionId, {
1948
2141
  type: "local_tools_enabled",
1949
2142
  rootDir,
@@ -1951,7 +2144,7 @@ ${c.bold("Agent Commons Chat")}`);
1951
2144
  });
1952
2145
  }
1953
2146
  console.log(c.dim("\nType your message and press Enter. Type /help for commands.\n"));
1954
- const rl = readline3.createInterface({
2147
+ const rl = readline4.createInterface({
1955
2148
  input: process.stdin,
1956
2149
  output: process.stdout,
1957
2150
  terminal: true,
@@ -2094,7 +2287,7 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2094
2287
  if (isWaiting) {
2095
2288
  elapsedInterval = setInterval(() => {
2096
2289
  elapsedSec++;
2097
- readline3.cursorTo(process.stdout, 0);
2290
+ readline4.cursorTo(process.stdout, 0);
2098
2291
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${c.dim(elapsedSec + "s\u2026")}`);
2099
2292
  }, 1e3);
2100
2293
  }
@@ -2109,8 +2302,8 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2109
2302
  if (elapsedInterval) clearInterval(elapsedInterval);
2110
2303
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2111
2304
  const preview = toolOk ? toolResultPreview(displayName, result) : "";
2112
- readline3.cursorTo(process.stdout, 0);
2113
- readline3.clearLine(process.stdout, 0);
2305
+ readline4.cursorTo(process.stdout, 0);
2306
+ readline4.clearLine(process.stdout, 0);
2114
2307
  const statusIcon = toolOk ? sym.ok : sym.fail;
2115
2308
  const previewPart = preview ? ` ${c.dim(preview)}` : "";
2116
2309
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(displayName)}${argStr ? " " + c.dim(argStr) : ""} ${statusIcon}${previewPart} ${c.dim("(" + elapsed + "s)")}
@@ -2144,8 +2337,8 @@ ${sym.fail} ${c.error(err.message ?? String(err))}`);
2144
2337
  hasOutput = false;
2145
2338
  } else if (event.type === "toolEnd") {
2146
2339
  const elapsed = ((Date.now() - toolStartMs) / 1e3).toFixed(1);
2147
- readline3.cursorTo(process.stdout, 0);
2148
- readline3.clearLine(process.stdout, 0);
2340
+ readline4.cursorTo(process.stdout, 0);
2341
+ readline4.clearLine(process.stdout, 0);
2149
2342
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(lastToolName)} ${sym.ok} ${c.dim("(" + elapsed + "s)")}
2150
2343
  `);
2151
2344
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
@@ -2202,15 +2395,20 @@ ${sym.fail} ${c.error(event.message ?? "Stream error")}`);
2202
2395
  }
2203
2396
  }
2204
2397
  console.log();
2205
- readline3.cursorTo(process.stdout, 0);
2206
- readline3.clearLine(process.stdout, 0);
2398
+ readline4.cursorTo(process.stdout, 0);
2399
+ readline4.clearLine(process.stdout, 0);
2207
2400
  rl.resume();
2208
2401
  rl.prompt();
2209
2402
  });
2403
+ const cleanup = () => {
2404
+ if (localToolsCfg) removeGitHook(localToolsCfg.rootDir);
2405
+ };
2210
2406
  rl.on("close", () => {
2407
+ cleanup();
2211
2408
  process.exit(0);
2212
2409
  });
2213
2410
  process.on("SIGINT", () => {
2411
+ cleanup();
2214
2412
  console.log(c.dim(`
2215
2413
  Session preserved. Resume with: agc chat --resume ${sessionId}`));
2216
2414
  process.exit(130);
@@ -2241,8 +2439,8 @@ async function handleLocalToolLoop(agentText, cfg, client, agentId, sessionId, a
2241
2439
  }
2242
2440
  const elapsed = ((Date.now() - startMs) / 1e3).toFixed(1);
2243
2441
  const preview = toolOk ? toolResultPreview(toolCall.tool, result) : "";
2244
- readline3.cursorTo(process.stdout, 0);
2245
- readline3.clearLine(process.stdout, 0);
2442
+ readline4.cursorTo(process.stdout, 0);
2443
+ readline4.clearLine(process.stdout, 0);
2246
2444
  const previewPart = preview ? ` ${c.dim(preview)}` : "";
2247
2445
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(toolCall.tool)}${argStr ? " " + c.dim(argStr) : ""} ${toolOk ? sym.ok : sym.fail}${previewPart} ${c.dim("(" + elapsed + "s)")}
2248
2446
  `);
@@ -2278,8 +2476,8 @@ ${result}
2278
2476
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)}`);
2279
2477
  } else if (evt.type === "toolEnd") {
2280
2478
  const elapsed2 = ((Date.now() - loopToolStartMs) / 1e3).toFixed(1);
2281
- readline3.cursorTo(process.stdout, 0);
2282
- readline3.clearLine(process.stdout, 0);
2479
+ readline4.cursorTo(process.stdout, 0);
2480
+ readline4.clearLine(process.stdout, 0);
2283
2481
  process.stdout.write(` ${c.dim("\u2500")} ${c.bold(loopToolName)} ${sym.ok} ${c.dim("(" + elapsed2 + "s)")}
2284
2482
  `);
2285
2483
  process.stdout.write(c.primary("agent") + c.dim(" \u203A "));
@@ -2917,8 +3115,8 @@ function skillsCommand() {
2917
3115
  });
2918
3116
  cmd.command("delete <slug>").description("Permanently delete a skill").option("--yes", "Skip confirmation prompt").option("--json", "Output result as JSON").action(async (slug, opts) => {
2919
3117
  if (!opts.yes) {
2920
- const readline4 = await import("readline");
2921
- const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
3118
+ const readline5 = await import("readline");
3119
+ const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
2922
3120
  const answer = await new Promise(
2923
3121
  (resolve2) => rl.question(c.warn(`Delete skill "${slug}"? This cannot be undone. [y/N] `), resolve2)
2924
3122
  );
@@ -3674,7 +3872,7 @@ async function pickAgentInteractively(action) {
3674
3872
  return agentId;
3675
3873
  }
3676
3874
  var program = new import_commander16.Command();
3677
- program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.1.17", "-v, --version").action(async () => {
3875
+ program.name("agc").description("Agent Commons CLI \u2014 interact with the Agent Commons platform").version("0.2.0", "-v, --version").action(async () => {
3678
3876
  await interactiveMenu();
3679
3877
  });
3680
3878
  program.addCommand(loginCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-commons/cli",
3
- "version": "0.1.17",
3
+ "version": "0.2.0",
4
4
  "description": "Agent Commons CLI — chat, run, and manage agents from your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "commander": "^12.1.0",
17
17
  "ora": "^8.1.1",
18
18
  "pdf-parse": "^1.1.1",
19
- "@agent-commons/sdk": "0.1.13"
19
+ "@agent-commons/sdk": "0.2.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.10.2",