@agentsky/sky 2.5.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22176,17 +22176,20 @@ function modelSubscriptionProviderFor(agentType, llm) {
22176
22176
  if (agentType === "claude_code" && CLAUDE_PLAN_MODELS.has(llm)) return "anthropic";
22177
22177
  if (agentType === "codex" && CHATGPT_PLAN_MODELS.has(llm)) return "openai";
22178
22178
  if (agentType === "cursor" && llm === "harness-default") return "cursor";
22179
+ if (agentType === "grok_build" && llm === "harness-default") return "xai";
22179
22180
  return null;
22180
22181
  }
22181
22182
  var MODEL_SUBSCRIPTION_PROVIDER_NAME = {
22182
22183
  anthropic: "Claude",
22183
22184
  openai: "ChatGPT",
22184
- cursor: "Cursor"
22185
+ cursor: "Cursor",
22186
+ xai: "Grok"
22185
22187
  };
22186
22188
  var MODEL_SUBSCRIPTION_PLAN_PHRASE = {
22187
22189
  anthropic: "Claude subscription",
22188
22190
  openai: "ChatGPT plan",
22189
- cursor: "Cursor plan"
22191
+ cursor: "Cursor plan",
22192
+ xai: "Grok subscription"
22190
22193
  };
22191
22194
  var AGENT_TYPE_META = {
22192
22195
  ...Object.fromEntries(ACP_AGENT_TYPES.map((agentType) => [agentType, {
package/dist/index.js CHANGED
@@ -16,12 +16,12 @@ import {
16
16
  skillRefSchema,
17
17
  splitMcpServers,
18
18
  supersededBy
19
- } from "./chunk-MHL5Z6IQ.js";
19
+ } from "./chunk-IC4N2ZPL.js";
20
20
 
21
21
  // src/index.ts
22
- import { existsSync as existsSync6, writeFileSync as writeFileSync5 } from "fs";
23
- import { basename as basename3, resolve as resolve3 } from "path";
24
- import { createInterface as createInterface3 } from "readline";
22
+ import { existsSync as existsSync7, writeFileSync as writeFileSync6 } from "fs";
23
+ import { basename as basename3, resolve as resolve4 } from "path";
24
+ import { createInterface as createInterface4 } from "readline";
25
25
  import { Command } from "commander";
26
26
 
27
27
  // src/config.ts
@@ -64,13 +64,13 @@ function currentUniverse() {
64
64
  }
65
65
 
66
66
  // src/version.ts
67
- var CLI_VERSION = "2.5.0";
67
+ var CLI_VERSION = "2.7.1";
68
68
  var USER_AGENT = `sky-cli/${CLI_VERSION} (${process.platform}; node/${process.versions.node})`;
69
69
 
70
70
  // src/api.ts
71
71
  var ApiError = class extends Error {
72
- constructor(status, code, message) {
73
- super(message);
72
+ constructor(status, code, message2) {
73
+ super(message2);
74
74
  this.status = status;
75
75
  this.code = code;
76
76
  }
@@ -89,16 +89,16 @@ function authHeaders() {
89
89
  }
90
90
  async function parseError(res) {
91
91
  let code = "http_error";
92
- let message = `${res.status} ${res.statusText}`;
92
+ let message2 = `${res.status} ${res.statusText}`;
93
93
  try {
94
94
  const body = await res.json();
95
95
  if (body.error?.message) {
96
96
  code = body.error.code ?? code;
97
- message = body.error.message;
97
+ message2 = body.error.message;
98
98
  }
99
99
  } catch {
100
100
  }
101
- throw new ApiError(res.status, code, message);
101
+ throw new ApiError(res.status, code, message2);
102
102
  }
103
103
  async function api(method, path, body, opts = {}) {
104
104
  const res = await fetch(`${apiBase()}${path}`, {
@@ -167,7 +167,7 @@ async function resolveSessionRef(ref) {
167
167
  return agent2.defaultSessionId;
168
168
  }
169
169
  async function* streamEvents(sessionId, signal) {
170
- const { parseSseStream } = await import("./src-GLBJHVIJ.js");
170
+ const { parseSseStream } = await import("./src-S76Y7GHZ.js");
171
171
  const res = await fetch(
172
172
  `${apiBase()}/sessions/${encodeURIComponent(sessionId)}/stream`,
173
173
  {
@@ -402,6 +402,62 @@ You are ${name}, a helpful long-lived agent.
402
402
  );
403
403
  }
404
404
 
405
+ // src/catalog.ts
406
+ var bare = (handle) => handle.replace(/^@/, "");
407
+ var optionalAuth = () => ({ auth: Boolean(apiToken()) });
408
+ function catalogQuery(filter) {
409
+ const params = new URLSearchParams();
410
+ if (filter.text?.trim()) params.set("q", filter.text.trim());
411
+ if (filter.category) params.set("category", filter.category);
412
+ const query = params.toString();
413
+ return query ? `?${query}` : "";
414
+ }
415
+ function listingBody(agentId, options) {
416
+ return {
417
+ agent_id: agentId,
418
+ handle: bare(options.handle),
419
+ tagline: options.tagline,
420
+ ...options.description ? { description: options.description } : {},
421
+ ...options.category?.length ? { categories: options.category } : {},
422
+ ...options.does?.length ? { does: options.does } : {},
423
+ ...options.doesNot?.length ? { does_not: options.doesNot } : {},
424
+ ...options.example?.length ? { examples: options.example } : {}
425
+ };
426
+ }
427
+ async function listCatalog(filter = {}) {
428
+ return (await api("GET", `/catalog/agents${catalogQuery(filter)}`, void 0, optionalAuth())).agents;
429
+ }
430
+ async function showCatalogAgent(handle) {
431
+ return (await api("GET", `/catalog/agents/${encodeURIComponent(bare(handle))}`, void 0, optionalAuth())).agent;
432
+ }
433
+ async function listCategories() {
434
+ return (await api("GET", "/catalog/categories", void 0, { auth: false })).categories;
435
+ }
436
+ async function publishListing(agentId, options) {
437
+ return (await api("POST", "/catalog/listings", listingBody(agentId, options))).agent;
438
+ }
439
+ async function removeListing(handle) {
440
+ await api("DELETE", `/catalog/listings/${encodeURIComponent(bare(handle))}`);
441
+ }
442
+ var uses = (agent2) => `${agent2.stats.uses} ${agent2.stats.uses === 1 ? "use" : "uses"}`;
443
+ var mine = (agent2) => agent2.visibility === "workspace" ? " (yours)" : "";
444
+ function renderCatalogAgent(agent2) {
445
+ const section = (title, lines) => lines.length ? [`${title}:`, ...lines.map((line) => ` - ${line}`)] : [];
446
+ return [
447
+ `@${agent2.handle} ${agent2.name}${mine(agent2)}`,
448
+ agent2.tagline,
449
+ ...agent2.description && agent2.description !== agent2.tagline ? ["", agent2.description] : [],
450
+ "",
451
+ `Categories: ${agent2.categories.join(", ") || "none"}`,
452
+ `Used: ${uses(agent2)}`,
453
+ ...section("Does", agent2.does),
454
+ ...section("Does not", agent2.does_not),
455
+ ...section("Examples", agent2.examples),
456
+ "",
457
+ `Hire it: sky session create @${agent2.handle} --message "\u2026"`
458
+ ].join("\n");
459
+ }
460
+
405
461
  // src/clone.ts
406
462
  import { spawn } from "child_process";
407
463
  import { existsSync as existsSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
@@ -1009,7 +1065,7 @@ async function login(opts) {
1009
1065
  }
1010
1066
  async function browserLogin(verifier, challenge) {
1011
1067
  const state = b64url(randomBytes(16));
1012
- const code = await new Promise((resolve4, reject) => {
1068
+ const code = await new Promise((resolve5, reject) => {
1013
1069
  const server = createServer((req, res) => {
1014
1070
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
1015
1071
  if (url.pathname !== "/callback") {
@@ -1022,7 +1078,7 @@ async function browserLogin(verifier, challenge) {
1022
1078
  if (url.searchParams.get("state") !== state || !got) {
1023
1079
  reject(new Error("login callback state mismatch"));
1024
1080
  } else {
1025
- resolve4(got);
1081
+ resolve5(got);
1026
1082
  }
1027
1083
  server.close();
1028
1084
  });
@@ -1132,9 +1188,9 @@ function renderEvent(event, data) {
1132
1188
  break;
1133
1189
  case "session.error": {
1134
1190
  const error = data.error;
1135
- const message = String(error?.message ?? error?.type ?? "unknown");
1136
- if (error?.retry_status === "retrying") console.error(dim(`retrying: ${message}`));
1137
- else console.error(red(`error: ${message}`));
1191
+ const message2 = String(error?.message ?? error?.type ?? "unknown");
1192
+ if (error?.retry_status === "retrying") console.error(dim(`retrying: ${message2}`));
1193
+ else console.error(red(`error: ${message2}`));
1138
1194
  break;
1139
1195
  }
1140
1196
  case "session.status_terminated":
@@ -1186,10 +1242,21 @@ async function readTurn(sessionId, opts = {}) {
1186
1242
  for (; ; ) {
1187
1243
  const reading = latestTurn(await history(sessionId));
1188
1244
  if (reading.finished || !opts.wait || Date.now() >= deadline) return reading;
1189
- await new Promise((resolve4) => setTimeout(resolve4, opts.intervalMs ?? 3e3));
1245
+ await new Promise((resolve5) => setTimeout(resolve5, opts.intervalMs ?? 3e3));
1190
1246
  }
1191
1247
  }
1192
1248
 
1249
+ // src/setup.ts
1250
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
1251
+ import { dirname as dirname4, join as join8 } from "path";
1252
+ import { createInterface as createInterface3 } from "readline";
1253
+
1254
+ // src/setup-local.ts
1255
+ import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
1256
+ import { homedir as homedir5 } from "os";
1257
+ import { dirname as dirname3, isAbsolute, join as join6, resolve as resolve3 } from "path";
1258
+ import { parse as parseToml2 } from "smol-toml";
1259
+
1193
1260
  // src/skill.ts
1194
1261
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1195
1262
  import { join as join5 } from "path";
@@ -1280,6 +1347,9 @@ function installedVersion(dir) {
1280
1347
  return null;
1281
1348
  }
1282
1349
  }
1350
+ function isInstalledSkill(dir) {
1351
+ return installedVersion(dir) !== null;
1352
+ }
1283
1353
  function installSkill(agent2, opts = {}) {
1284
1354
  const dir = join5(agent2.skillsDir, SKILL_NAME);
1285
1355
  const previous = installedVersion(dir);
@@ -1293,11 +1363,154 @@ function installSkill(agent2, opts = {}) {
1293
1363
  return { agent: agent2.id, path: dir, outcome };
1294
1364
  }
1295
1365
 
1366
+ // src/setup-local.ts
1367
+ var MAX_SKILL_FILE_BYTES = 1024 * 1024;
1368
+ var MAX_IMPORT_DEPTH = 5;
1369
+ var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
1370
+ function frontmatterName(skillMd) {
1371
+ const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd)?.[1];
1372
+ const name = block?.match(/^name:\s*["']?([^"'\r\n]+?)["']?\s*$/m)?.[1];
1373
+ return name?.trim() || null;
1374
+ }
1375
+ function sensitive(name) {
1376
+ return name.startsWith(".env") || name.endsWith(".pem") || name.endsWith(".key");
1377
+ }
1378
+ function skillFiles(root, prefix) {
1379
+ const files2 = [];
1380
+ for (const entry of readdirSync2(root, { withFileTypes: true })) {
1381
+ const path = join6(root, entry.name);
1382
+ if (entry.isDirectory()) {
1383
+ if (!SKIPPED_DIRS.has(entry.name)) files2.push(...skillFiles(path, `${prefix}/${entry.name}`));
1384
+ } else if (entry.isFile() && !sensitive(entry.name) && statSync2(path).size <= MAX_SKILL_FILE_BYTES) {
1385
+ files2.push({ path: `${prefix}/${entry.name}`, bytes: new Uint8Array(readFileSync7(path)) });
1386
+ }
1387
+ }
1388
+ return files2;
1389
+ }
1390
+ function readLocalSkills(agents) {
1391
+ const skills = [];
1392
+ const ignored = [];
1393
+ for (const agent2 of agents) {
1394
+ if (!existsSync5(agent2.skillsDir)) continue;
1395
+ for (const entry of readdirSync2(agent2.skillsDir, { withFileTypes: true })) {
1396
+ const dir = join6(agent2.skillsDir, entry.name);
1397
+ let isDir = entry.isDirectory();
1398
+ if (entry.isSymbolicLink()) {
1399
+ try {
1400
+ isDir = statSync2(dir).isDirectory();
1401
+ } catch {
1402
+ isDir = false;
1403
+ }
1404
+ }
1405
+ if (!isDir || !existsSync5(join6(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
1406
+ const name = frontmatterName(readFileSync7(join6(dir, "SKILL.md"), "utf8"));
1407
+ if (!name) {
1408
+ ignored.push({ agent: agent2.id, dir, reason: "SKILL.md has no name" });
1409
+ continue;
1410
+ }
1411
+ const taken = skills.find((skill) => skill.name === name);
1412
+ if (taken) {
1413
+ ignored.push({ agent: agent2.id, dir, reason: `${taken.agent} already has a skill named ${name}` });
1414
+ continue;
1415
+ }
1416
+ const files2 = skillFiles(dir, entry.name);
1417
+ skills.push({ name, agent: agent2.id, dir, files: files2, bytes: files2.reduce((sum, file) => sum + file.bytes.length, 0) });
1418
+ }
1419
+ }
1420
+ return { skills, ignored };
1421
+ }
1422
+ var INSTRUCTION_FILES = { claude_code: "CLAUDE.md", codex: "AGENTS.md" };
1423
+ function inlineImports(content, from, home, depth, seen) {
1424
+ let fence;
1425
+ return content.split(/\r?\n/).map((line) => {
1426
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
1427
+ if (marker) {
1428
+ if (!fence) fence = { character: marker[1][0], length: marker[1].length };
1429
+ else if (marker[1][0] === fence.character && marker[1].length >= fence.length && !marker[2].trim()) fence = void 0;
1430
+ return line;
1431
+ }
1432
+ const target = fence ? void 0 : line.match(/^@(\S+\.(?:md|markdown|txt))$/i)?.[1];
1433
+ if (!target || depth >= MAX_IMPORT_DEPTH) return line;
1434
+ const path = target.startsWith("~/") ? join6(home, target.slice(2)) : isAbsolute(target) ? target : resolve3(dirname3(from), target);
1435
+ if (seen.includes(path)) return "";
1436
+ let imported;
1437
+ try {
1438
+ imported = readFileSync7(path, "utf8");
1439
+ } catch {
1440
+ return line;
1441
+ }
1442
+ seen.push(path);
1443
+ return inlineImports(imported, path, home, depth + 1, seen).trim();
1444
+ }).join("\n");
1445
+ }
1446
+ function readGlobalInstructions(agents, home = homedir5()) {
1447
+ const found = [];
1448
+ for (const agent2 of agents) {
1449
+ const file = INSTRUCTION_FILES[agent2.id];
1450
+ if (!file) continue;
1451
+ const path = join6(agent2.root, file);
1452
+ let raw;
1453
+ try {
1454
+ raw = readFileSync7(path, "utf8");
1455
+ } catch {
1456
+ continue;
1457
+ }
1458
+ if (!raw.trim()) continue;
1459
+ const imports = [];
1460
+ found.push({ agent: agent2.id, agentName: agent2.name, path, content: inlineImports(raw, path, home, 0, imports), imports });
1461
+ }
1462
+ return found;
1463
+ }
1464
+ function combineInstructions(found) {
1465
+ const distinct = found.filter((item, index) => found.findIndex((other) => other.content.trim() === item.content.trim()) === index);
1466
+ if (distinct.length <= 1) return distinct[0]?.content ?? "";
1467
+ return distinct.map((item) => `# From ${item.agentName}
1468
+
1469
+ ${item.content.trim()}`).join("\n\n");
1470
+ }
1471
+ function readJson2(path) {
1472
+ try {
1473
+ return JSON.parse(readFileSync7(path, "utf8"));
1474
+ } catch {
1475
+ return void 0;
1476
+ }
1477
+ }
1478
+ function bearerOf(def) {
1479
+ if (typeof def.bearer_token === "string" && def.bearer_token.trim()) return def.bearer_token.trim();
1480
+ const headers = def.headers ?? def.http_headers;
1481
+ if (!headers || typeof headers !== "object") return null;
1482
+ const entry = Object.entries(headers).find(([name]) => name.toLowerCase() === "authorization");
1483
+ const match = typeof entry?.[1] === "string" ? /^Bearer\s+(\S+)$/i.exec(entry[1].trim()) : null;
1484
+ return match?.[1] ?? null;
1485
+ }
1486
+ function serversOf(agent2, home) {
1487
+ if (agent2.id === "claude_code") return readJson2(join6(home, ".claude.json"))?.mcpServers ?? {};
1488
+ if (agent2.id === "cursor") return readJson2(join6(agent2.root, "mcp.json"))?.mcpServers ?? {};
1489
+ try {
1490
+ return parseToml2(readFileSync7(join6(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
1491
+ } catch {
1492
+ return {};
1493
+ }
1494
+ }
1495
+ function findLocalSecrets(agents, home = homedir5()) {
1496
+ const secrets2 = [];
1497
+ for (const agent2 of agents) {
1498
+ for (const [name, def] of Object.entries(serversOf(agent2, home))) {
1499
+ if (!def || typeof def !== "object" || typeof def.url !== "string" || !/^https:\/\//i.test(def.url)) continue;
1500
+ const token = bearerOf(def);
1501
+ if (!token || /^\$\{?[A-Za-z_]/.test(token)) continue;
1502
+ if (secrets2.some((secret) => secret.url === def.url)) continue;
1503
+ secrets2.push({ name: secrets2.some((secret) => secret.name === name) ? `${name}-${agent2.id}` : name, agent: agent2.id, url: def.url, token });
1504
+ }
1505
+ }
1506
+ return secrets2;
1507
+ }
1508
+
1296
1509
  // src/subscriptions.ts
1297
1510
  import { spawn as spawn3 } from "child_process";
1298
- import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
1299
- import { homedir as homedir5 } from "os";
1300
- import { join as join6 } from "path";
1511
+ import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
1512
+ import { homedir as homedir6 } from "os";
1513
+ import { join as join7 } from "path";
1301
1514
  import { createInterface as createInterface2 } from "readline";
1302
1515
  var ALIASES = {
1303
1516
  claude: "anthropic",
@@ -1382,20 +1595,35 @@ async function connectClaude() {
1382
1595
  const expiry = sub.expiresAt ? ` (expires ~${sub.expiresAt.slice(0, 10)})` : "";
1383
1596
  console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
1384
1597
  }
1385
- async function connectChatgpt(opts) {
1386
- const codexHome = process.env.CODEX_HOME || join6(homedir5(), ".codex");
1387
- const path = join6(codexHome, "auth.json");
1388
- if (!existsSync5(path)) {
1598
+ function codexSignIn(env = process.env, home = homedir6()) {
1599
+ const path = join7(env.CODEX_HOME || join7(home, ".codex"), "auth.json");
1600
+ if (!existsSync6(path)) {
1389
1601
  throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
1390
1602
  }
1391
- const raw = readFileSync7(path, "utf8");
1392
- let accountId;
1603
+ const raw = readFileSync8(path, "utf8");
1393
1604
  try {
1394
1605
  const parsed = JSON.parse(raw);
1395
- accountId = parsed.tokens?.account_id;
1606
+ return { path, raw, account: parsed.tokens?.account_id };
1396
1607
  } catch {
1397
1608
  throw new ApiError(400, "invalid_codex_auth", `${path} isn't valid JSON \u2014 re-run \`codex login\``);
1398
1609
  }
1610
+ }
1611
+ function cursorSignIn(env = process.env, home = homedir6()) {
1612
+ const path = join7(env.XDG_CONFIG_HOME || join7(home, ".config"), "cursor", "auth.json");
1613
+ if (!existsSync6(path)) {
1614
+ throw new ApiError(404, "no_cursor_auth", `no ${path} \u2014 run \`cursor-agent login\` first`);
1615
+ }
1616
+ const raw = readFileSync8(path, "utf8");
1617
+ try {
1618
+ const parsed = JSON.parse(raw);
1619
+ if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string") throw new Error("shape");
1620
+ return { path, raw };
1621
+ } catch {
1622
+ throw new ApiError(400, "invalid_cursor_auth", `${path} isn't a Cursor sign-in \u2014 re-run \`cursor-agent login\``);
1623
+ }
1624
+ }
1625
+ async function connectChatgpt(opts) {
1626
+ const { path, raw, account: accountId } = codexSignIn();
1399
1627
  console.log(`Found Codex sign-in at ${path}${accountId ? ` (account ${accountId})` : ""}.`);
1400
1628
  if (!opts.yes) {
1401
1629
  const ok = await confirm2("Upload it so your cloud Codex agents run on your ChatGPT plan?");
@@ -1452,6 +1680,292 @@ async function disconnect(name, opts) {
1452
1680
  }
1453
1681
  }
1454
1682
 
1683
+ // src/setup.ts
1684
+ var SETUP_STEPS = ["skill", "logins", "import", "secrets"];
1685
+ function reportPath() {
1686
+ return join8(dirname4(configPath()), "setup.json");
1687
+ }
1688
+ function loadReport() {
1689
+ try {
1690
+ const saved = JSON.parse(readFileSync9(reportPath(), "utf8"));
1691
+ return { agents: saved.agents ?? [], plans: saved.plans ?? [], ...saved.secrets ? { secrets: saved.secrets } : {} };
1692
+ } catch {
1693
+ return { agents: [], plans: [] };
1694
+ }
1695
+ }
1696
+ function mergeReport(base, next) {
1697
+ const agents = [...base.agents.filter((old) => !next.agents.some((item) => item.id === old.id)), ...next.agents];
1698
+ const plans = [...base.plans.filter((old) => !next.plans.some((item) => item.provider === old.provider)), ...next.plans];
1699
+ const secrets2 = base.secrets || next.secrets ? { saved: (base.secrets?.saved ?? 0) + (next.secrets?.saved ?? 0), skipped: next.secrets?.skipped ?? base.secrets?.skipped ?? 0 } : void 0;
1700
+ return { agents, plans, ...secrets2 ? { secrets: secrets2 } : {} };
1701
+ }
1702
+ function saveReport(report) {
1703
+ mkdirSync3(dirname4(reportPath()), { recursive: true });
1704
+ writeFileSync5(reportPath(), `${JSON.stringify(report, null, 2)}
1705
+ `, { mode: 384 });
1706
+ }
1707
+ function confirm3(question) {
1708
+ return new Promise((done) => {
1709
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
1710
+ rl.question(`${question} [Y/n] `, (answer) => {
1711
+ rl.close();
1712
+ done(/^(y(es)?)?$/i.test(answer.trim()));
1713
+ });
1714
+ });
1715
+ }
1716
+ function kb2(bytes) {
1717
+ return `${Math.max(1, Math.round(bytes / 1024))} KB`;
1718
+ }
1719
+ function describe(event) {
1720
+ switch (event.type) {
1721
+ case "agents":
1722
+ return event.agents.length ? `Found on this computer: ${event.agents.map((agent2) => agent2.name).join(", ")}` : "No agent found on this computer (looked for Claude Code, Codex and Cursor).";
1723
+ case "skill":
1724
+ return ` AgentSky skill ${event.outcome === "current" ? "already up to date" : event.outcome} for ${event.agent}`;
1725
+ case "plan":
1726
+ return ` ${event.provider} plan: ${event.outcome === "already" ? "already connected" : event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
1727
+ case "found": {
1728
+ const lines = [
1729
+ ` ${event.skills.length} skill${event.skills.length === 1 ? "" : "s"}${event.skills.length ? `: ${event.skills.map((skill) => skill.name).join(", ")}` : ""}`,
1730
+ ...event.instructions.map((item) => ` instructions from ${item.agent}: ${item.path} (${kb2(item.bytes)}${item.imports.length ? `, with ${item.imports.length} imported file${item.imports.length === 1 ? "" : "s"}` : ""})`),
1731
+ ...event.instructions.length ? [] : [" no global instructions file"],
1732
+ ...event.ignored.map((item) => ` left out ${item.dir}: ${item.reason}`),
1733
+ ...event.secrets.map((secret) => ` key for ${secret.name} (${secret.url}), from ${secret.agent}`)
1734
+ ];
1735
+ return lines.join("\n");
1736
+ }
1737
+ case "imported_skill":
1738
+ return ` skill ${event.name}: ${event.outcome}`;
1739
+ case "instructions":
1740
+ return event.outcome === "none" ? null : ` instructions: ${event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
1741
+ case "secret":
1742
+ return ` key for ${event.name}: ${event.outcome === "already" ? "already in your vault" : event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
1743
+ case "error":
1744
+ return `sky: ${event.message} (${event.code})`;
1745
+ case "done":
1746
+ return "Done. Your agentsky.dev tab has updated itself.";
1747
+ }
1748
+ }
1749
+ async function cloneCall(key, path, init = {}) {
1750
+ const res = await fetch(`${agentUseBase()}/api/clone/${path}`, {
1751
+ method: "POST",
1752
+ headers: {
1753
+ "user-agent": USER_AGENT,
1754
+ authorization: `Bearer ${key}`,
1755
+ ...init.json !== void 0 ? { "content-type": "application/json" } : {}
1756
+ },
1757
+ body: init.form ?? (init.json !== void 0 ? JSON.stringify(init.json) : void 0)
1758
+ });
1759
+ const body = await res.json().catch(() => ({}));
1760
+ if (res.ok) return { ok: true, body };
1761
+ return { ok: false, status: res.status, code: body.error?.code ?? "http_error", message: body.error?.message ?? `${res.status} ${res.statusText}` };
1762
+ }
1763
+ async function mintKey() {
1764
+ const token = apiToken();
1765
+ if (!token) throw new ApiError(401, "not_logged_in", "not logged in \u2014 run `sky auth login`");
1766
+ const res = await fetch(`${agentUseBase()}/api/clone/grants`, { method: "POST", headers: { "user-agent": USER_AGENT, authorization: `Bearer ${token}` } });
1767
+ const body = await res.json().catch(() => ({}));
1768
+ if (!res.ok || !body.key) throw new ApiError(res.status, body.error?.code ?? "http_error", body.error?.message ?? `${res.status} ${res.statusText}`);
1769
+ return body.key;
1770
+ }
1771
+ async function connectedProviders() {
1772
+ const { modelSubscriptions } = await api("GET", "/model-subscriptions");
1773
+ return new Set(modelSubscriptions.map((subscription) => subscription.provider));
1774
+ }
1775
+ async function defaultVaultId() {
1776
+ const { vaults } = await api("GET", "/vaults");
1777
+ const existing = vaults.find((vault) => vault.display_name === "default");
1778
+ if (existing) return existing.id;
1779
+ return (await api("POST", "/vaults", { display_name: "default" })).vault.id;
1780
+ }
1781
+ function message(error) {
1782
+ return error instanceof Error ? error.message : String(error);
1783
+ }
1784
+ async function runSetup(opts) {
1785
+ const steps = opts.steps?.length ? opts.steps : [...SETUP_STEPS];
1786
+ const interactive = !opts.json && !opts.yes && !opts.dryRun;
1787
+ const emit = (event) => {
1788
+ if (opts.json) console.log(JSON.stringify(event));
1789
+ else {
1790
+ const line = describe(event);
1791
+ if (line) console.log(line);
1792
+ }
1793
+ };
1794
+ const agreed = async (question) => !interactive || confirm3(question);
1795
+ const has = (step) => steps.includes(step);
1796
+ const agents = selectLocalAgents(opts.agents);
1797
+ emit({ type: "agents", agents: agents.map((agent2) => ({ id: agent2.id, name: agent2.name })) });
1798
+ const needsToken = !opts.dryRun && (has("logins") || has("secrets") || has("import") && !opts.key);
1799
+ if (needsToken && !apiToken()) {
1800
+ if (opts.json) throw new ApiError(401, "not_logged_in", "not logged in \u2014 run `sky auth login`");
1801
+ console.log("First, sign this computer in to AgentSky.");
1802
+ await login({});
1803
+ }
1804
+ const run2 = { agents: [], plans: [] };
1805
+ const earlier = loadReport();
1806
+ const wantsImport = has("import");
1807
+ const wantsSecrets = has("secrets");
1808
+ const tellsWorkspace = !opts.dryRun && (wantsImport || Boolean(opts.key));
1809
+ let key;
1810
+ let held = /* @__PURE__ */ new Map();
1811
+ const tell = async () => {
1812
+ if (!tellsWorkspace) return;
1813
+ key ??= opts.key ?? await mintKey();
1814
+ const claimed = await cloneCall(key, "claim", { json: { setup: mergeReport(earlier, run2) } });
1815
+ if (!claimed.ok) throw new ApiError(claimed.status, claimed.code, claimed.message);
1816
+ held = new Map((claimed.body.skills ?? []).map((skill) => [skill.name, skill.fingerprint]));
1817
+ };
1818
+ await tell();
1819
+ if (has("skill") && agents.length && await agreed("Add the AgentSky skill to these agents, so they can hand work to others?")) {
1820
+ for (const agent2 of agents) {
1821
+ try {
1822
+ const installed = installSkill(agent2, { dryRun: opts.dryRun });
1823
+ emit({ type: "skill", agent: agent2.id, outcome: installed.outcome, path: installed.path });
1824
+ if (!opts.dryRun) run2.agents.push({ id: agent2.id, skill: installed.outcome });
1825
+ } catch (error) {
1826
+ emit({ type: "skill", agent: agent2.id, outcome: "failed", path: agent2.skillsDir });
1827
+ emit({ type: "error", code: "skill_install_failed", message: message(error) });
1828
+ run2.agents.push({ id: agent2.id, skill: "failed" });
1829
+ }
1830
+ }
1831
+ await tell();
1832
+ }
1833
+ if (has("logins") && agents.length) {
1834
+ await connectPlans(agents, opts, interactive, emit, run2);
1835
+ await tell();
1836
+ }
1837
+ const { skills, ignored } = wantsImport ? readLocalSkills(agents) : { skills: [], ignored: [] };
1838
+ const instructions = wantsImport ? readGlobalInstructions(agents) : [];
1839
+ const secrets2 = wantsSecrets ? findLocalSecrets(agents) : [];
1840
+ if (wantsImport || wantsSecrets) {
1841
+ emit({
1842
+ type: "found",
1843
+ skills: skills.map((skill) => ({ name: skill.name, agent: skill.agent, files: skill.files.length, bytes: skill.bytes })),
1844
+ ignored: ignored.map((item) => ({ dir: item.dir, reason: item.reason })),
1845
+ instructions: instructions.map((item) => ({ agent: item.agent, path: item.path, bytes: Buffer.byteLength(item.content), imports: item.imports })),
1846
+ secrets: secrets2.map((secret) => ({ name: secret.name, agent: secret.agent, url: secret.url }))
1847
+ });
1848
+ }
1849
+ if (opts.dryRun) return;
1850
+ if (wantsSecrets && secrets2.length) {
1851
+ run2.secrets = await saveSecrets(secrets2, opts, interactive, emit);
1852
+ await tell();
1853
+ }
1854
+ const report = mergeReport(earlier, run2);
1855
+ saveReport(report);
1856
+ if (key) {
1857
+ const sendFiles = wantsImport && (skills.length > 0 || instructions.length > 0) && await agreed("Send these skills and instructions to your AgentSky workspace?");
1858
+ await importToWorkspace(key, held, sendFiles ? skills : [], sendFiles ? combineInstructions(instructions) : "", emit);
1859
+ }
1860
+ emit({ type: "done", report });
1861
+ }
1862
+ async function connectPlans(agents, opts, interactive, emit, run2) {
1863
+ const connected = opts.dryRun && !apiToken() ? /* @__PURE__ */ new Set() : await connectedProviders();
1864
+ const record = (provider, outcome, detail) => {
1865
+ emit({ type: "plan", provider, outcome, ...detail ? { detail } : {} });
1866
+ if (outcome === "available") return;
1867
+ run2.plans.push({ provider, outcome: outcome === "already" ? "connected" : outcome });
1868
+ };
1869
+ const copy = async (provider, name, read) => {
1870
+ if (connected.has(provider)) return record(provider, "already");
1871
+ let found;
1872
+ try {
1873
+ found = read();
1874
+ } catch (error) {
1875
+ if (error instanceof ApiError && error.status === 404) return;
1876
+ return record(provider, "failed", message(error));
1877
+ }
1878
+ if (opts.dryRun) return record(provider, "available", found.account ? `account ${found.account}` : void 0);
1879
+ if (interactive && !await confirm3(`Use the ${name} sign-in on this computer${found.account ? ` (account ${found.account})` : ""} for your cloud agents?`)) {
1880
+ return record(provider, "skipped");
1881
+ }
1882
+ try {
1883
+ await put(provider, found.raw);
1884
+ record(provider, "connected");
1885
+ } catch (error) {
1886
+ record(provider, "failed", message(error));
1887
+ }
1888
+ };
1889
+ if (agents.some((agent2) => agent2.id === "claude_code")) {
1890
+ if (connected.has("anthropic")) record("anthropic", "already");
1891
+ else if (opts.dryRun) record("anthropic", "available");
1892
+ else if (opts.json || opts.yes || opts.claude === false) record("anthropic", "skipped", "needs a browser approval");
1893
+ else if (!await confirm3("Connect your Claude plan? It opens the browser for one approval and does not sign Claude Code out.")) record("anthropic", "skipped");
1894
+ else {
1895
+ const token = await mintClaudeToken();
1896
+ if (!token) record("anthropic", "failed", "`claude setup-token` did not return a token");
1897
+ else {
1898
+ try {
1899
+ await put("anthropic", token);
1900
+ record("anthropic", "connected");
1901
+ } catch (error) {
1902
+ record("anthropic", "failed", message(error));
1903
+ }
1904
+ }
1905
+ }
1906
+ }
1907
+ if (agents.some((agent2) => agent2.id === "codex")) await copy("openai", "ChatGPT", () => codexSignIn());
1908
+ if (agents.some((agent2) => agent2.id === "cursor")) await copy("cursor", "Cursor", () => cursorSignIn());
1909
+ }
1910
+ async function saveSecrets(found, opts, interactive, emit) {
1911
+ const counts = { saved: 0, skipped: 0 };
1912
+ let vaultId;
1913
+ for (const secret of found) {
1914
+ const chosen = interactive ? await confirm3(`Save the key your ${secret.agent} uses for ${secret.name} (${secret.url}) to your vault?`) : (opts.secrets ?? []).includes(secret.name);
1915
+ if (!chosen) {
1916
+ counts.skipped += 1;
1917
+ emit({ type: "secret", name: secret.name, outcome: "skipped" });
1918
+ continue;
1919
+ }
1920
+ try {
1921
+ vaultId ??= await defaultVaultId();
1922
+ await api("POST", `/vaults/${encodeURIComponent(vaultId)}/secrets`, {
1923
+ display_name: secret.name,
1924
+ auth: { type: "static_bearer", mcp_server_url: secret.url, token: secret.token }
1925
+ });
1926
+ counts.saved += 1;
1927
+ emit({ type: "secret", name: secret.name, outcome: "saved" });
1928
+ } catch (error) {
1929
+ if (error instanceof ApiError && error.status === 409) emit({ type: "secret", name: secret.name, outcome: "already" });
1930
+ else emit({ type: "secret", name: secret.name, outcome: "failed", detail: message(error) });
1931
+ }
1932
+ }
1933
+ return counts;
1934
+ }
1935
+ async function importToWorkspace(key, held, skills, instructions, emit) {
1936
+ const outcomes = [];
1937
+ let signedOut = false;
1938
+ for (const skill of skills) {
1939
+ if (signedOut) {
1940
+ outcomes.push({ name: skill.name, outcome: "skipped: sign in to agentsky.dev again" });
1941
+ continue;
1942
+ }
1943
+ const form = new FormData();
1944
+ form.append("name", skill.name);
1945
+ form.append("expectedFingerprint", held.get(skill.name) ?? "");
1946
+ for (const file of skill.files) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
1947
+ const sent = await cloneCall(key, "skills", { form });
1948
+ const outcome = sent.ok ? String(sent.body.status ?? "created") : `skipped: ${sent.message}`;
1949
+ if (!sent.ok && sent.code === "sign_in_required") signedOut = true;
1950
+ outcomes.push({ name: skill.name, outcome });
1951
+ emit({ type: "imported_skill", name: skill.name, outcome });
1952
+ }
1953
+ let instructionsOutcome = null;
1954
+ let instructionError;
1955
+ if (instructions.trim()) {
1956
+ const sent = await cloneCall(key, "instructions", { json: { instructions } });
1957
+ instructionsOutcome = sent.ok ? "applied" : "failed";
1958
+ if (!sent.ok) instructionError = sent.message;
1959
+ emit({ type: "instructions", outcome: instructionsOutcome, ...instructionError ? { detail: instructionError } : {} });
1960
+ } else {
1961
+ emit({ type: "instructions", outcome: "none" });
1962
+ }
1963
+ const finished = await cloneCall(key, "finish", {
1964
+ json: { summary: { skills: outcomes, instructions: instructionsOutcome, ...instructionError ? { instructionError } : {} } }
1965
+ });
1966
+ if (!finished.ok) emit({ type: "error", code: finished.code, message: finished.message });
1967
+ }
1968
+
1455
1969
  // src/index.ts
1456
1970
  function printModelBilling(session2) {
1457
1971
  if (session2.modelBilling !== "account") return;
@@ -1647,10 +2161,10 @@ session.command("create <agent>").description("create a session (starts provisio
1647
2161
  console.log(JSON.stringify(created));
1648
2162
  return;
1649
2163
  }
1650
- console.log(`Created ${created.id} (${created.agent} \xB7 ${created.status})${created.default ? " [default]" : ""}`);
2164
+ console.log(`Created ${created.id} (${created.agentId ?? "-"} \xB7 ${created.status})${created.default ? " [default]" : ""}`);
1651
2165
  printModelBilling(created);
1652
2166
  for (const r of created.resources ?? []) console.log(` ${renderResource(r)}`);
1653
- console.log(` sky agent message ${created.agent} "hello"`);
2167
+ console.log(` sky session message ${created.id} "hello"`);
1654
2168
  }
1655
2169
  )
1656
2170
  );
@@ -1664,7 +2178,7 @@ session.command("list [agent]").action(
1664
2178
  }
1665
2179
  for (const s of sessions) {
1666
2180
  console.log(
1667
- `${s.id.padEnd(24)} ${(s.agent ?? "-").padEnd(20)} ${(s.title ?? "").padEnd(16)} ${s.status}${s.default ? " *" : ""}`
2181
+ `${s.id.padEnd(24)} ${(s.agentId ?? "-").padEnd(20)} ${(s.title ?? "").padEnd(16)} ${s.status}${s.default ? " *" : ""}`
1668
2182
  );
1669
2183
  }
1670
2184
  })
@@ -1676,7 +2190,7 @@ session.command("show <id>").action(
1676
2190
  `/sessions/${encodeURIComponent(id)}`
1677
2191
  );
1678
2192
  console.log(`${s.id}${s.default ? " [default]" : ""}`);
1679
- console.log(` agent ${s.agent ?? "-"}`);
2193
+ console.log(` agent ${s.agentId ?? "-"}`);
1680
2194
  console.log(` title ${s.title ?? "-"}`);
1681
2195
  console.log(` status ${s.status}`);
1682
2196
  console.log(` model ${s.agentType} \xB7 ${s.llm}`);
@@ -1733,7 +2247,7 @@ session.command("read <ref>").description("what the agent has said since the las
1733
2247
  console.log(JSON.stringify(reading));
1734
2248
  return;
1735
2249
  }
1736
- for (const message of reading.messages) console.log(message);
2250
+ for (const message2 of reading.messages) console.log(message2);
1737
2251
  for (const error of reading.errors) console.error(`error: ${error}`);
1738
2252
  if (!reading.finished) console.error("The agent is still working. Run this again to read more.");
1739
2253
  })
@@ -1858,7 +2372,7 @@ async function sendAndFollow(sessionId, text, opts) {
1858
2372
  async function chatLoop(sessionId, opts = {}) {
1859
2373
  console.log(`Chatting with ${sessionId} \u2014 Ctrl-C to leave.`);
1860
2374
  const controller = new AbortController();
1861
- const rl = createInterface3({ input: process.stdin, output: process.stdout, prompt: "> " });
2375
+ const rl = createInterface4({ input: process.stdin, output: process.stdout, prompt: "> " });
1862
2376
  let system = opts.system;
1863
2377
  void (async () => {
1864
2378
  try {
@@ -1892,12 +2406,10 @@ async function chatLoop(sessionId, opts = {}) {
1892
2406
  await new Promise(() => {
1893
2407
  });
1894
2408
  }
1895
- program.command("catalog").description("the agents that can be started by handle").command("search [words...]").description("find an agent for a piece of work; no words lists every agent").option("--category <category>", "only this category").option("--json", "print the full listings as JSON").action(
2409
+ var catalog = program.command("catalog").description("the agents that can be started by handle");
2410
+ catalog.command("search [words...]").description("find an agent for a piece of work; no words lists every agent; works without logging in").option("--category <category>", "only this category (see `sky catalog categories`)").option("--json", "print the full listings as JSON").action(
1896
2411
  run(async (words, opts) => {
1897
- const query = new URLSearchParams();
1898
- if (words.length) query.set("q", words.join(" "));
1899
- if (opts.category) query.set("category", opts.category);
1900
- const { agents } = await api("GET", `/catalog/agents${query.size ? `?${query}` : ""}`);
2412
+ const agents = await listCatalog({ text: words.join(" "), category: opts.category });
1901
2413
  if (opts.json) {
1902
2414
  console.log(JSON.stringify(agents));
1903
2415
  return;
@@ -1926,6 +2438,20 @@ program.command("skill").description("the AgentSky skill for the agents on this
1926
2438
  for (const i of installs) console.log(`${i.agent.padEnd(12)} ${verb[i.outcome].padEnd(14)} ${i.path}`);
1927
2439
  })
1928
2440
  );
2441
+ program.command("setup [steps...]").description("bring this machine's agents to AgentSky: the skill, your plans, your skills and instructions, your tool keys").addHelpText("after", `
2442
+ Steps (all when none is named): ${SETUP_STEPS.join(" | ")}`).option("--key <key>", "a setup key from agentsky.dev/setup").option("--agent <id...>", "only these agents: claude_code | codex | cursor").option("--secret <name...>", "save these tool keys to your vault (asked one by one when omitted)").option("--no-claude", "leave the Claude plan alone").option("--dry-run", "say what would be done; write and send nothing").option("-y, --yes", "do not ask before each step").option("--json", "print progress as one JSON object per line; never asks").action(
2443
+ run(async (steps, opts) => {
2444
+ const unknown = steps.find((step) => !SETUP_STEPS.includes(step));
2445
+ if (unknown) throw new Error(`unknown step ${unknown} \u2014 expected ${SETUP_STEPS.join(" | ")}`);
2446
+ try {
2447
+ await runSetup({ steps, key: opts.key, agents: opts.agent, secrets: opts.secret, claude: opts.claude, dryRun: opts.dryRun, yes: opts.yes, json: opts.json });
2448
+ } catch (error) {
2449
+ if (!opts.json) throw error;
2450
+ console.log(JSON.stringify({ type: "error", code: error instanceof ApiError ? error.code : "failed", message: error instanceof Error ? error.message : String(error) }));
2451
+ process.exit(1);
2452
+ }
2453
+ })
2454
+ );
1929
2455
  var secrets = program.command("secrets").description("agent secrets (values write-only)");
1930
2456
  secrets.command("set <id> <pairs...>").description("set KEY=VALUE pairs on the agent").action(
1931
2457
  run(async (id, pairs) => {
@@ -1960,8 +2486,8 @@ secrets.command("unset <id> <key>").action(
1960
2486
  );
1961
2487
  program.command("launch").description("scaffold ./agent.toml if absent, create the agent, print its address").option("-f, --file <path>", "agent file", "agent.toml").option("--type <type>", "override the file's harness: hermes | claude_code | codex | openclaw | pi | dsh | kimi_code | opencode").option("--llm <llm>", "override the file's model (must suit the harness)").action(
1962
2488
  run(async (opts) => {
1963
- const path = resolve3(opts.file);
1964
- if (!existsSync6(path)) {
2489
+ const path = resolve4(opts.file);
2490
+ if (!existsSync7(path)) {
1965
2491
  const name = basename3(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
1966
2492
  scaffoldAgentFile(path, name);
1967
2493
  console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
@@ -1993,7 +2519,7 @@ program.command("launch").description("scaffold ./agent.toml if absent, create t
1993
2519
  );
1994
2520
  program.command("deploy").description("re-apply ./agent.toml to the existing agent (idempotent)").option("-f, --file <path>", "agent file", "agent.toml").action(
1995
2521
  run(async (opts) => {
1996
- const path = resolve3(opts.file);
2522
+ const path = resolve4(opts.file);
1997
2523
  const { file, spec } = readAgentFile(path);
1998
2524
  if (!file.id) {
1999
2525
  console.log("No agent id in the file yet \u2014 running launch instead.");
@@ -2070,13 +2596,13 @@ program.command("import").description("import a local Claude Code or Codex sessi
2070
2596
  console.log(` ${ws}`);
2071
2597
  });
2072
2598
  console.log();
2073
- const rl = createInterface3({ input: process.stdin, output: process.stdout });
2599
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
2074
2600
  const answer = await new Promise(
2075
- (resolve4) => rl.question(
2601
+ (resolve5) => rl.question(
2076
2602
  `Import which session? [1-${candidates.length}, or comma-separated, or "all"]: `,
2077
2603
  (a) => {
2078
2604
  rl.close();
2079
- resolve4(a.trim());
2605
+ resolve5(a.trim());
2080
2606
  }
2081
2607
  )
2082
2608
  );
@@ -2096,10 +2622,10 @@ program.command("import").description("import a local Claude Code or Codex sessi
2096
2622
  console.log("The recorded conversation is redacted locally. Local project files and login credentials are not transferred.");
2097
2623
  if (!opts.yes) {
2098
2624
  if (!process.stdin.isTTY) throw new Error("Use --yes to confirm this transfer in a non-interactive terminal.");
2099
- const prompt = createInterface3({ input: process.stdin, output: process.stdout });
2100
- const answer = await new Promise((resolve4) => prompt.question("Upload these selected conversations? [y/N] ", (value) => {
2625
+ const prompt = createInterface4({ input: process.stdin, output: process.stdout });
2626
+ const answer = await new Promise((resolve5) => prompt.question("Upload these selected conversations? [y/N] ", (value) => {
2101
2627
  prompt.close();
2102
- resolve4(value);
2628
+ resolve5(value);
2103
2629
  }));
2104
2630
  if (!/^y(?:es)?$/i.test(answer.trim())) {
2105
2631
  console.log("Cancelled. Nothing uploaded.");
@@ -2159,7 +2685,7 @@ files.command("rm <id>").description("delete a file (a session that already rece
2159
2685
  files.command("download <id>").description("download a session file's bytes").requiredOption("-o, --out <path>", "where to write the bytes").action(
2160
2686
  run(async (id, opts) => {
2161
2687
  const { bytes } = await downloadFile(id);
2162
- writeFileSync5(opts.out, bytes);
2688
+ writeFileSync6(opts.out, bytes);
2163
2689
  console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
2164
2690
  })
2165
2691
  );
@@ -2302,6 +2828,30 @@ for (const [verb, method, suffix, blurb] of [
2302
2828
  })
2303
2829
  );
2304
2830
  }
2831
+ catalog.command("show <handle>").description("what one agent does, and how to hire it").option("--json", "print the raw listing").action(
2832
+ run(async (handle, opts) => {
2833
+ const agent2 = await showCatalogAgent(handle);
2834
+ console.log(opts.json ? JSON.stringify(agent2, null, 2) : renderCatalogAgent(agent2));
2835
+ })
2836
+ );
2837
+ catalog.command("categories").description("the categories a listing can carry").action(
2838
+ run(async () => {
2839
+ for (const category of await listCategories()) console.log(`${category.id.padEnd(10)} ${category.label}`);
2840
+ })
2841
+ );
2842
+ catalog.command("publish <agent>").description("list one of your agents under a handle, visible to your workspace; publishing again replaces the listing").requiredOption("--handle <handle>", "the @handle people hire it by: 2 to 40 lower-case letters, digits or underscores").requiredOption("--tagline <text>", "one line on what it is for").option("--description <text>", "a longer description").option("--category <id>", "a category; repeat for several", collect).option("--does <text>", "something it does; repeat for several", collect).option("--does-not <text>", "something it does not do; repeat for several", collect).option("--example <text>", "an example request; repeat for several", collect).action(
2843
+ run(async (agentId, opts) => {
2844
+ const agent2 = await publishListing(agentId, opts);
2845
+ console.log(`Listed @${agent2.handle} (${agent2.name})`);
2846
+ console.log(` sky session create @${agent2.handle} --message "\u2026"`);
2847
+ })
2848
+ );
2849
+ catalog.command("unpublish <handle>").description("take down one of your workspace's listings; the agent itself is untouched").action(
2850
+ run(async (handle) => {
2851
+ await removeListing(handle);
2852
+ console.log(`Removed @${handle.replace(/^@/, "")} from the catalog`);
2853
+ })
2854
+ );
2305
2855
  program.command("config").description("show the CLI config location and current values").action(() => {
2306
2856
  const config = loadConfig();
2307
2857
  console.log(`config: ${configPath()}`);
package/dist/products.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  skillsArraySchema,
9
9
  systemMessageEventSchema,
10
10
  userMessageEventSchema
11
- } from "./chunk-MHL5Z6IQ.js";
11
+ } from "./chunk-IC4N2ZPL.js";
12
12
 
13
13
  // ../../packages/spec/src/compatibility.ts
14
14
  var capabilityInputsSchema = capabilitiesSchema;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  external_exports,
3
3
  messagePartSchema
4
- } from "./chunk-MHL5Z6IQ.js";
4
+ } from "./chunk-IC4N2ZPL.js";
5
5
 
6
6
  // ../../packages/chat/src/stream.ts
7
7
  async function* parseSseStream(body) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsky/sky",
3
- "version": "2.5.0",
3
+ "version": "2.7.1",
4
4
  "description": "sky — the AgentSky developer CLI: launch and talk to long-lived agents from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",