@agentsky/sky 2.29.0 → 2.30.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.
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  streamEvents,
12
12
  uploadFile,
13
13
  writeResponseFile
14
- } from "./chunk-GEL5GWF2.js";
14
+ } from "./chunk-T36VKZIJ.js";
15
15
  import {
16
16
  CLI_VERSION,
17
17
  USER_AGENT,
@@ -22,7 +22,7 @@ import {
22
22
  currentUniverse,
23
23
  loadConfig,
24
24
  saveConfig
25
- } from "./chunk-QZLKHGX6.js";
25
+ } from "./chunk-3SWL7YZD.js";
26
26
  import {
27
27
  AGENT_TYPES,
28
28
  MODEL_SUBSCRIPTION_PLAN_PHRASE,
@@ -44,32 +44,132 @@ import {
44
44
  skillRefSchema,
45
45
  splitMcpServers,
46
46
  supersededBy
47
- } from "./chunk-ISORBGQX.js";
47
+ } from "./chunk-7T5NSWSW.js";
48
48
  import "./chunk-MLKGABMK.js";
49
49
 
50
+ // src/thread-experience.ts
51
+ import { randomUUID } from "crypto";
52
+ async function startExperienceThread(experience, text, options = {}) {
53
+ const requestId = options.requestId ?? randomUUID();
54
+ if (!/^[a-zA-Z0-9_-]{16,100}$/.test(requestId)) throw new Error("request-id must contain 16\u2013100 letters, digits, underscores or hyphens.");
55
+ if (!text.trim() || text.trim().length > 2e4) throw new Error("text must contain 1\u201320000 characters.");
56
+ return api("POST", "/threads/experiences", { experience, text, requestId, ...options.timezone === void 0 ? {} : { timezone: options.timezone } });
57
+ }
58
+ function registerExperienceThreadCommand(thread2) {
59
+ thread2.command("experience <experience> <text>").description("start a catalog experience through the same Mac/web thread owner").option("--request-id <id>", "reuse the same 16\u2013100 character id when retrying; defaults to a new UUID").option("--timezone <iana>", "the person's IANA timezone").action(async (experience, text, options) => {
60
+ const requestId = options.requestId ?? randomUUID();
61
+ console.error(`Request id: ${requestId}`);
62
+ console.log(JSON.stringify(await startExperienceThread(experience, text, { ...options, requestId }), null, 2));
63
+ });
64
+ }
65
+
66
+ // src/skills-store.ts
67
+ import { readdirSync, readFileSync, statSync } from "fs";
68
+ import { basename, join, relative, resolve } from "path";
69
+ function skillBundle(dir) {
70
+ const root = resolve(dir);
71
+ const top = basename(root);
72
+ const files2 = [];
73
+ const walk = (path) => {
74
+ for (const name of readdirSync(path).sort()) {
75
+ const full = join(path, name);
76
+ if (statSync(full).isDirectory()) walk(full);
77
+ else files2.push({ path: `${top}/${relative(root, full)}`, bytes: readFileSync(full) });
78
+ }
79
+ };
80
+ walk(root);
81
+ if (!files2.some((file) => file.path === `${top}/SKILL.md`)) throw new Error(`${dir}: no SKILL.md at its root`);
82
+ return files2;
83
+ }
84
+ async function uploadSkill(dir, displayName) {
85
+ const form = new FormData();
86
+ for (const file of skillBundle(dir)) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
87
+ form.append("displayName", displayName ?? basename(resolve(dir)));
88
+ return apiForm("POST", "/skills", form);
89
+ }
90
+ async function listSkills() {
91
+ return (await api("GET", "/skills")).skills;
92
+ }
93
+ async function getTask(task2) {
94
+ return (await api("GET", `/tasks/${encodeURIComponent(task2)}`)).task;
95
+ }
96
+ function answerBody(answer) {
97
+ if (!answer.option === !answer.text) throw new Error("give exactly one of --option or --text");
98
+ return { ...answer.option ? { option_id: answer.option } : { text: answer.text }, ...answer.note ? { note: answer.note } : {} };
99
+ }
100
+ async function answerRequest(task2, request, answer) {
101
+ return api("POST", `/tasks/${encodeURIComponent(task2)}/requests/${encodeURIComponent(request)}/answer`, answerBody(answer));
102
+ }
103
+ function renderRequest(request) {
104
+ const options = request.options.length ? `
105
+ options: ${request.options.map((option) => `${option.id} (${option.label})`).join(", ")}` : "";
106
+ return `${request.id} ${request.kind} from ${request.session_id}
107
+ ${request.question}${options}`;
108
+ }
109
+ async function getSkillUsage(since) {
110
+ const qs = since ? `?since=${encodeURIComponent(since)}` : "";
111
+ return api("GET", `/skills/usage${qs}`);
112
+ }
113
+ function nextSkills(current, skillId, verb) {
114
+ const has = current.some((ref) => ref.type === "skill" && ref.skillId === skillId);
115
+ if (verb === "attach") return has ? null : [...current, { type: "skill", skillId, version: "latest" }];
116
+ return has ? current.filter((ref) => !(ref.type === "skill" && ref.skillId === skillId)) : null;
117
+ }
118
+ async function setAgentSkill(agentId, skillId, verb) {
119
+ const path = `/agents/${encodeURIComponent(agentId)}`;
120
+ const { agent: agent2 } = await api("GET", path);
121
+ const skills2 = nextSkills(agent2.skills ?? [], skillId, verb);
122
+ if (!skills2) return "unchanged";
123
+ await api("PATCH", path, { skills: skills2, expectedVersion: agent2.version });
124
+ return "changed";
125
+ }
126
+
127
+ // src/library-skills.ts
128
+ function listLibrarySkills() {
129
+ return api("GET", "/library/skills");
130
+ }
131
+ function writeLibrarySkill(directory, options = {}) {
132
+ const form = new FormData();
133
+ for (const file of skillBundle(directory)) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
134
+ if (options.name !== void 0) form.append("displayName", options.name);
135
+ if (options.expectedLatestRevision !== void 0) form.append("expectedLatestRevision", options.expectedLatestRevision);
136
+ return apiForm("POST", options.id ? `/library/skills/${encodeURIComponent(options.id)}/revisions` : "/library/skills", form);
137
+ }
138
+ function deleteLibrarySkill(id) {
139
+ return api("DELETE", `/library/skills/${encodeURIComponent(id)}`);
140
+ }
141
+ function registerLibrarySkillCommands(library2) {
142
+ const librarySkills = library2.command("skills").description("Library skills available to new conversations; requires a personal write key");
143
+ const print = (value) => console.log(JSON.stringify(value, null, 2));
144
+ librarySkills.command("list").description("list skills and make pending skills available to new conversations (write scope)").action(async () => print(await listLibrarySkills()));
145
+ librarySkills.command("upload <directory>").description("upload a skill folder and make it available to new workspace conversations").option("--name <name>", "display name").action(async (directory, options) => print(await writeLibrarySkill(directory, options)));
146
+ librarySkills.command("save <id> <directory>").description("save a complete skill revision").option("--expected-latest-revision <id>", "refuse if the current revision has changed").action(async (id, directory, options) => print(await writeLibrarySkill(directory, { ...options, id })));
147
+ librarySkills.command("rm <id>").description("remove a skill from the Library").action(async (id) => print(await deleteLibrarySkill(id)));
148
+ }
149
+
50
150
  // src/index.ts
51
- import { existsSync as existsSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
151
+ import { existsSync as existsSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "fs";
52
152
  import { basename as basename9, resolve as resolve9 } from "path";
53
153
  import { createInterface as createInterface6 } from "readline";
54
154
  import { Command } from "commander";
55
155
 
56
156
  // src/files.ts
57
- import { existsSync, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
58
- import { basename as basename2, extname, join as join2 } from "path";
157
+ import { existsSync, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
158
+ import { basename as basename3, extname, join as join3 } from "path";
59
159
 
60
160
  // src/resumable-upload.ts
61
161
  import { createHash } from "crypto";
62
- import { mkdirSync, openSync, readFileSync, readSync, closeSync, rmSync, statSync, writeFileSync } from "fs";
63
- import { basename, dirname, join, resolve } from "path";
162
+ import { mkdirSync, openSync, readFileSync as readFileSync2, readSync, closeSync, rmSync, statSync as statSync2, writeFileSync } from "fs";
163
+ import { basename as basename2, dirname, join as join2, resolve as resolve2 } from "path";
64
164
  var SINGLE_REQUEST_MAX_BYTES = 32 * 1024 * 1024;
65
165
  var MAX_ATTEMPTS = 8;
66
166
  function statePath(path, size, mtimeMs) {
67
- const key = createHash("sha256").update(`${resolve(path)}\0${size}\0${mtimeMs}`).digest("hex").slice(0, 32);
68
- return join(dirname(configPath()), "uploads", `${key}.json`);
167
+ const key = createHash("sha256").update(`${resolve2(path)}\0${size}\0${mtimeMs}`).digest("hex").slice(0, 32);
168
+ return join2(dirname(configPath()), "uploads", `${key}.json`);
69
169
  }
70
170
  function loadSession(file, expiresInSeconds) {
71
171
  try {
72
- const saved = JSON.parse(readFileSync(file, "utf8"));
172
+ const saved = JSON.parse(readFileSync2(file, "utf8"));
73
173
  if (saved.expiresInSeconds !== expiresInSeconds) return null;
74
174
  if (Date.parse(saved.expires_at) - Date.now() < 3600 * 1e3) return null;
75
175
  return saved;
@@ -131,7 +231,7 @@ async function transfer(session2, path, onProgress) {
131
231
  }
132
232
  }
133
233
  async function uploadLargeFile(path, mediaType, expiresInSeconds, onProgress) {
134
- const { size, mtimeMs } = statSync(path);
234
+ const { size, mtimeMs } = statSync2(path);
135
235
  const stateFile = statePath(path, size, mtimeMs);
136
236
  let session2 = loadSession(stateFile, expiresInSeconds);
137
237
  if (session2) {
@@ -144,13 +244,13 @@ async function uploadLargeFile(path, mediaType, expiresInSeconds, onProgress) {
144
244
  }
145
245
  if (!session2) {
146
246
  session2 = await api("POST", "/files/uploads", {
147
- filename: basename(path),
247
+ filename: basename2(path),
148
248
  size_bytes: size,
149
249
  mime_type: mediaType,
150
250
  ...expiresInSeconds ? { expires_in_seconds: expiresInSeconds } : {}
151
251
  });
152
252
  mkdirSync(dirname(stateFile), { recursive: true });
153
- const saved = { ...session2, path: resolve(path), mtimeMs, expiresInSeconds };
253
+ const saved = { ...session2, path: resolve2(path), mtimeMs, expiresInSeconds };
154
254
  writeFileSync(stateFile, JSON.stringify(saved), { mode: 384 });
155
255
  }
156
256
  await transfer(session2, path, onProgress);
@@ -181,23 +281,23 @@ function mediaTypeFor(path) {
181
281
  }
182
282
  async function uploadLocalFile(path, expiresInSeconds, onProgress = () => {
183
283
  }) {
184
- if (statSync2(path).size > SINGLE_REQUEST_MAX_BYTES) {
284
+ if (statSync3(path).size > SINGLE_REQUEST_MAX_BYTES) {
185
285
  return uploadLargeFile(path, mediaTypeFor(path), expiresInSeconds, onProgress);
186
286
  }
187
- const bytes = readFileSync2(path);
188
- return uploadFile(new Uint8Array(bytes), basename2(path), mediaTypeFor(path), expiresInSeconds);
287
+ const bytes = readFileSync3(path);
288
+ return uploadFile(new Uint8Array(bytes), basename3(path), mediaTypeFor(path), expiresInSeconds);
189
289
  }
190
290
  var IGNORED_NAMES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", "desktop.ini"]);
191
291
  function filesUnder(paths) {
192
292
  const found = [];
193
293
  const walk = (path) => {
194
- const stat = statSync2(path);
294
+ const stat = statSync3(path);
195
295
  if (stat.isFile()) {
196
- if (!IGNORED_NAMES.has(basename2(path))) found.push(path);
296
+ if (!IGNORED_NAMES.has(basename3(path))) found.push(path);
197
297
  return;
198
298
  }
199
299
  if (!stat.isDirectory()) return;
200
- for (const name of readdirSync(path).sort()) walk(join2(path, name));
300
+ for (const name of readdirSync2(path).sort()) walk(join3(path, name));
201
301
  };
202
302
  for (const path of paths) walk(path);
203
303
  return found;
@@ -469,22 +569,22 @@ async function unsubscribeMarketing() {
469
569
  }
470
570
 
471
571
  // src/import-local.ts
472
- import { closeSync as closeSync3, existsSync as existsSync4, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync3, statSync as statSync5 } from "fs";
572
+ import { closeSync as closeSync3, existsSync as existsSync4, openSync as openSync3, readdirSync as readdirSync5, readFileSync as readFileSync7, readSync as readSync3, statSync as statSync6 } from "fs";
473
573
  import { homedir as homedir3 } from "os";
474
- import { join as join6 } from "path";
574
+ import { join as join7 } from "path";
475
575
  import { createHash as createHash3 } from "crypto";
476
576
 
477
577
  // src/import-files.ts
478
578
  import { isUtf8 } from "buffer";
479
- import { existsSync as existsSync3, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
579
+ import { existsSync as existsSync3, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
480
580
  import { homedir as homedir2 } from "os";
481
- import { dirname as dirname3, extname as extname2, isAbsolute, join as join4, relative as relative2, resolve as resolve2, sep as sep2, basename as basename4 } from "path";
581
+ import { dirname as dirname3, extname as extname2, isAbsolute, join as join5, relative as relative3, resolve as resolve3, sep as sep2, basename as basename5 } from "path";
482
582
 
483
583
  // src/import-workspace.ts
484
584
  import { spawnSync } from "child_process";
485
- import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, realpathSync, statSync as statSync3 } from "fs";
585
+ import { existsSync as existsSync2, readdirSync as readdirSync3, readFileSync as readFileSync4, realpathSync, statSync as statSync4 } from "fs";
486
586
  import { homedir } from "os";
487
- import { basename as basename3, dirname as dirname2, join as join3, relative, sep } from "path";
587
+ import { basename as basename4, dirname as dirname2, join as join4, relative as relative2, sep } from "path";
488
588
  import { parse as parseToml } from "smol-toml";
489
589
  var MAX_TREE = 5e3;
490
590
  var SKILL_LINE = /Base directory for this skill: (\/[^\n\r"'\\<>]+)/g;
@@ -494,26 +594,26 @@ function git(cwd, ...args) {
494
594
  }
495
595
  var isFile = (path) => {
496
596
  try {
497
- return statSync3(path).isFile();
597
+ return statSync4(path).isFile();
498
598
  } catch {
499
599
  return false;
500
600
  }
501
601
  };
502
602
  var isDir = (path) => {
503
603
  try {
504
- return statSync3(path).isDirectory();
604
+ return statSync4(path).isDirectory();
505
605
  } catch {
506
606
  return false;
507
607
  }
508
608
  };
509
609
  function sharedInstructionPaths(home = homedir()) {
510
- const rules = join3(home, ".agents", "rules");
610
+ const rules = join4(home, ".agents", "rules");
511
611
  let ruleFiles = [];
512
612
  try {
513
- ruleFiles = readdirSync2(rules).filter((name) => name.endsWith(".md")).sort().map((name) => join3(rules, name));
613
+ ruleFiles = readdirSync3(rules).filter((name) => name.endsWith(".md")).sort().map((name) => join4(rules, name));
514
614
  } catch {
515
615
  }
516
- return [join3(home, "AGENTS.md"), join3(home, "MEMORY.md"), ...ruleFiles].filter(isFile);
616
+ return [join4(home, "AGENTS.md"), join4(home, "MEMORY.md"), ...ruleFiles].filter(isFile);
517
617
  }
518
618
  function strings(value, out, depth = 0) {
519
619
  if (depth > 12) return;
@@ -522,14 +622,14 @@ function strings(value, out, depth = 0) {
522
622
  else if (value && typeof value === "object") for (const item of Object.values(value)) strings(item, out, depth + 1);
523
623
  }
524
624
  function skillRoots(cwd, home) {
525
- const claude = process.env.CLAUDE_CONFIG_DIR ?? join3(home, ".claude");
526
- const codex = process.env.CODEX_HOME ?? join3(home, ".codex");
625
+ const claude = process.env.CLAUDE_CONFIG_DIR ?? join4(home, ".claude");
626
+ const codex = process.env.CODEX_HOME ?? join4(home, ".codex");
527
627
  return [
528
- ...cwd ? [join3(cwd, ".claude", "skills"), join3(cwd, ".agents", "skills")] : [],
529
- join3(home, ".agents", "skills"),
530
- join3(claude, "skills"),
531
- join3(codex, "skills"),
532
- join3(home, ".hermes", "skills")
628
+ ...cwd ? [join4(cwd, ".claude", "skills"), join4(cwd, ".agents", "skills")] : [],
629
+ join4(home, ".agents", "skills"),
630
+ join4(claude, "skills"),
631
+ join4(codex, "skills"),
632
+ join4(home, ".hermes", "skills")
533
633
  ];
534
634
  }
535
635
  function pluginSkill(plugin, skill, home) {
@@ -537,35 +637,35 @@ function pluginSkill(plugin, skill, home) {
537
637
  if (depth > 7) return null;
538
638
  let entries;
539
639
  try {
540
- entries = readdirSync2(dir, { withFileTypes: true });
640
+ entries = readdirSync3(dir, { withFileTypes: true });
541
641
  } catch {
542
642
  return null;
543
643
  }
544
644
  for (const entry2 of entries) {
545
645
  if (!entry2.isDirectory() || entry2.name === "node_modules" || entry2.name === ".git") continue;
546
- const path = join3(dir, entry2.name);
547
- if (entry2.name === skill && basename3(dir) === "skills" && (underPlugin || path.includes(`${sep}${plugin}${sep}`)) && isFile(join3(path, "SKILL.md"))) return realpathSync(path);
646
+ const path = join4(dir, entry2.name);
647
+ if (entry2.name === skill && basename4(dir) === "skills" && (underPlugin || path.includes(`${sep}${plugin}${sep}`)) && isFile(join4(path, "SKILL.md"))) return realpathSync(path);
548
648
  const found = walk(path, depth + 1, underPlugin);
549
649
  if (found) return found;
550
650
  }
551
651
  return null;
552
652
  };
553
- return walk(join3(process.env.CLAUDE_CONFIG_DIR ?? join3(home, ".claude"), "plugins"), 0, false) ?? walk(join3(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions", "skills-plugin"), 0, true);
653
+ return walk(join4(process.env.CLAUDE_CONFIG_DIR ?? join4(home, ".claude"), "plugins"), 0, false) ?? walk(join4(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions", "skills-plugin"), 0, true);
554
654
  }
555
655
  function findSkill(name, cwd, home) {
556
656
  const [scope, skill] = name.includes(":") ? name.split(":", 2) : [null, name];
557
657
  if (scope && skill) return pluginSkill(scope, skill, home);
558
658
  for (const root of skillRoots(cwd, home)) {
559
- const direct = join3(root, name);
560
- if (isFile(join3(direct, "SKILL.md"))) return realpathSync(direct);
659
+ const direct = join4(root, name);
660
+ if (isFile(join4(direct, "SKILL.md"))) return realpathSync(direct);
561
661
  }
562
662
  return null;
563
663
  }
564
664
  function loadedSkills(records, touched, cwd, home = homedir()) {
565
665
  const dirs = /* @__PURE__ */ new Map();
566
666
  const missing = /* @__PURE__ */ new Set();
567
- const add = (dir, name = basename3(dir)) => {
568
- if (!isFile(join3(dir, "SKILL.md"))) return false;
667
+ const add = (dir, name = basename4(dir)) => {
668
+ if (!isFile(join4(dir, "SKILL.md"))) return false;
569
669
  const real = realpathSync(dir);
570
670
  if (![...dirs.values()].includes(real)) dirs.set(name, real);
571
671
  return true;
@@ -582,7 +682,7 @@ function loadedSkills(records, touched, cwd, home = homedir()) {
582
682
  if (b?.type === "tool_use" && b.name === "Skill" && typeof b.input?.skill === "string") called.push(b.input.skill.replace(/^\//, ""));
583
683
  }
584
684
  }
585
- for (const path of touched) if (basename3(path) === "SKILL.md") add(dirname2(path));
685
+ for (const path of touched) if (basename4(path) === "SKILL.md") add(dirname2(path));
586
686
  for (const name of called) {
587
687
  if (!name || dirs.has(name) || [...dirs.keys()].some((key) => key.endsWith(`:${name}`) || name.endsWith(`:${key}`))) continue;
588
688
  const dir = findSkill(name, cwd, home);
@@ -592,7 +692,7 @@ function loadedSkills(records, touched, cwd, home = homedir()) {
592
692
  }
593
693
  function readJson(path) {
594
694
  try {
595
- const value = JSON.parse(readFileSync3(path, "utf8"));
695
+ const value = JSON.parse(readFileSync4(path, "utf8"));
596
696
  return value && typeof value === "object" ? value : null;
597
697
  } catch {
598
698
  return null;
@@ -637,16 +737,16 @@ function mcpServers(harness2, cwd, home = homedir()) {
637
737
  }
638
738
  };
639
739
  if (harness2 === "claude_code") {
640
- const config = readJson(join3(process.env.CLAUDE_CONFIG_DIR ? dirname2(process.env.CLAUDE_CONFIG_DIR) : home, ".claude.json"));
740
+ const config = readJson(join4(process.env.CLAUDE_CONFIG_DIR ? dirname2(process.env.CLAUDE_CONFIG_DIR) : home, ".claude.json"));
641
741
  const projects = config?.projects && typeof config.projects === "object" ? config.projects : {};
642
742
  const root = cwd ? git(cwd, "rev-parse", "--show-toplevel")?.trim() || cwd : "";
643
743
  if (cwd) push(projects[cwd]?.mcpServers ?? projects[root]?.mcpServers, "local");
644
- if (root) push(readJson(join3(root, ".mcp.json"))?.mcpServers, "project");
744
+ if (root) push(readJson(join4(root, ".mcp.json"))?.mcpServers, "project");
645
745
  push(config?.mcpServers, "user");
646
746
  }
647
747
  if (harness2 === "codex") {
648
748
  try {
649
- const config = parseToml(readFileSync3(join3(process.env.CODEX_HOME ?? join3(home, ".codex"), "config.toml"), "utf8"));
749
+ const config = parseToml(readFileSync4(join4(process.env.CODEX_HOME ?? join4(home, ".codex"), "config.toml"), "utf8"));
650
750
  push(config.mcp_servers, "user");
651
751
  } catch {
652
752
  }
@@ -684,7 +784,7 @@ function workspaceState(cwd, harness2, skills2, home = homedir(), title = null)
684
784
  harness: harness2,
685
785
  title: title?.trim().slice(0, 120) || null,
686
786
  cwd: cwd ? cwd.replace(home, "~") : null,
687
- project: cwd ? basename3(root ?? cwd) : null,
787
+ project: cwd ? basename4(root ?? cwd) : null,
688
788
  git: gitState,
689
789
  tree: tree.slice(0, MAX_TREE),
690
790
  tree_truncated: tree.length > MAX_TREE,
@@ -700,15 +800,15 @@ function skillFiles(skill) {
700
800
  if (depth > 8 || out.length > 400) return;
701
801
  let entries;
702
802
  try {
703
- entries = readdirSync2(dir, { withFileTypes: true });
803
+ entries = readdirSync3(dir, { withFileTypes: true });
704
804
  } catch {
705
805
  return;
706
806
  }
707
807
  for (const entry2 of entries) {
708
808
  if (entry2.name === "node_modules" || entry2.name === ".git" || entry2.name === "__pycache__" || entry2.name === ".venv") continue;
709
- const path = join3(dir, entry2.name);
809
+ const path = join4(dir, entry2.name);
710
810
  if (entry2.isDirectory()) walk(path, depth + 1);
711
- else if (entry2.isFile()) out.push({ path, name: `skills/${skill.name}/${relative(skill.dir, path).split(sep).join("/")}` });
811
+ else if (entry2.isFile()) out.push({ path, name: `skills/${skill.name}/${relative2(skill.dir, path).split(sep).join("/")}` });
712
812
  }
713
813
  };
714
814
  if (existsSync2(skill.dir)) walk(skill.dir, 0);
@@ -717,7 +817,7 @@ function skillFiles(skill) {
717
817
  function codexThreadName(sessionId, home = homedir()) {
718
818
  let name = null;
719
819
  try {
720
- for (const line of readFileSync3(join3(process.env.CODEX_HOME ?? join3(home, ".codex"), "session_index.jsonl"), "utf8").split("\n")) {
820
+ for (const line of readFileSync4(join4(process.env.CODEX_HOME ?? join4(home, ".codex"), "session_index.jsonl"), "utf8").split("\n")) {
721
821
  if (!line.includes(sessionId)) continue;
722
822
  try {
723
823
  const entry2 = JSON.parse(line);
@@ -779,7 +879,7 @@ function classify(path) {
779
879
  if (DOCUMENTS[ext]) return { kind: "document", mediaType: DOCUMENTS[ext] };
780
880
  if (IMAGES[ext]) return { kind: "image", mediaType: IMAGES[ext] };
781
881
  if (TEXT[ext]) return { kind: "text", mediaType: TEXT[ext] };
782
- if (PLAIN_TEXT.has(ext) || PLAIN_NAMES.has(basename4(path))) return { kind: "text", mediaType: "text/plain" };
882
+ if (PLAIN_TEXT.has(ext) || PLAIN_NAMES.has(basename5(path))) return { kind: "text", mediaType: "text/plain" };
783
883
  return null;
784
884
  }
785
885
  var SHELL_READ = /(?:^|[\s;&|(`"'])(?:cat|head|tail|nl|less|more|wc|bat|sed\s+-n\s+(?:'[^']*'|"[^"]*"|\S+))(?:\s+-[\w-]+(?:\s+\d+)?)*\s+((?:~|\.{0,2}\/)?[\w@%+./~-]*[\w-]\.[\w]{1,10}|\/[\w@%+./-]+)/g;
@@ -798,8 +898,8 @@ function touchedPaths(records, cwd, home = homedir2()) {
798
898
  const add = (raw, base) => {
799
899
  if (!raw || raw.length > 1e3) return;
800
900
  const path = raw.replace(/^~(?=\/)/, home);
801
- if (isAbsolute(path)) out.add(resolve2(path));
802
- else if (base) out.add(resolve2(base, path));
901
+ if (isAbsolute(path)) out.add(resolve3(path));
902
+ else if (base) out.add(resolve3(base, path));
803
903
  };
804
904
  const call = (name, input) => {
805
905
  const i = obj(input);
@@ -837,7 +937,7 @@ function touchedPaths(records, cwd, home = homedir2()) {
837
937
  }
838
938
  function gitRoot(dir) {
839
939
  for (let d = dir; ; d = dirname3(d)) {
840
- if (existsSync3(join4(d, ".git"))) return d;
940
+ if (existsSync3(join5(d, ".git"))) return d;
841
941
  if (d === dirname3(d)) return null;
842
942
  }
843
943
  }
@@ -845,26 +945,26 @@ function instructionPaths(cwd, harness2, home = homedir2()) {
845
945
  const out = [];
846
946
  if (cwd && existsSync3(cwd)) {
847
947
  const root = gitRoot(cwd) ?? cwd;
848
- for (let d = resolve2(cwd); ; d = dirname3(d)) {
849
- for (const name of INSTRUCTION_FILES) out.push(join4(d, name));
948
+ for (let d = resolve3(cwd); ; d = dirname3(d)) {
949
+ for (const name of INSTRUCTION_FILES) out.push(join5(d, name));
850
950
  if (d === root || d === dirname3(d)) break;
851
951
  }
852
952
  }
853
- if (harness2 === "claude_code") out.push(join4(process.env.CLAUDE_CONFIG_DIR ?? join4(home, ".claude"), "CLAUDE.md"));
854
- if (harness2 === "codex") out.push(join4(process.env.CODEX_HOME ?? join4(home, ".codex"), "AGENTS.md"));
953
+ if (harness2 === "claude_code") out.push(join5(process.env.CLAUDE_CONFIG_DIR ?? join5(home, ".claude"), "CLAUDE.md"));
954
+ if (harness2 === "codex") out.push(join5(process.env.CODEX_HOME ?? join5(home, ".codex"), "AGENTS.md"));
855
955
  out.push(...sharedInstructionPaths(home));
856
956
  return out.filter((path) => {
857
957
  try {
858
- return statSync4(path).isFile();
958
+ return statSync5(path).isFile();
859
959
  } catch {
860
960
  return false;
861
961
  }
862
962
  });
863
963
  }
864
964
  function resourceName(path, cwd, home = homedir2()) {
865
- const inside = cwd ? relative2(resolve2(cwd), path) : "";
965
+ const inside = cwd ? relative3(resolve3(cwd), path) : "";
866
966
  if (cwd && inside && !inside.startsWith("..") && !isAbsolute(inside)) return `workspace/${inside.split(sep2).join("/")}`;
867
- const underHome = relative2(home, path);
967
+ const underHome = relative3(home, path);
868
968
  const shown = !underHome.startsWith("..") && !isAbsolute(underHome) ? `~/${underHome}` : path.replace(/^\/+/, "");
869
969
  return `files/${shown.split(sep2).join("/")}`;
870
970
  }
@@ -874,7 +974,7 @@ function resourceLine(name, mediaType, data) {
874
974
  }
875
975
  function collectSessionFiles(records, opts) {
876
976
  const home = opts.home ?? homedir2();
877
- const exclude = new Set((opts.exclude ?? []).map((path) => resolve2(path)));
977
+ const exclude = new Set((opts.exclude ?? []).map((path) => resolve3(path)));
878
978
  const leftOut = [];
879
979
  const candidates = [];
880
980
  const seen = /* @__PURE__ */ new Set();
@@ -889,7 +989,7 @@ function collectSessionFiles(records, opts) {
889
989
  }
890
990
  let size;
891
991
  try {
892
- const stat = statSync4(path);
992
+ const stat = statSync5(path);
893
993
  if (!stat.isFile()) return;
894
994
  size = stat.size;
895
995
  } catch {
@@ -939,7 +1039,7 @@ function collectSessionFiles(records, opts) {
939
1039
  for (const file of candidates) {
940
1040
  let data;
941
1041
  try {
942
- data = readFileSync4(file.path);
1042
+ data = readFileSync5(file.path);
943
1043
  } catch {
944
1044
  leftOut.push({ path: file.path, reason: "missing" });
945
1045
  continue;
@@ -976,9 +1076,9 @@ function collectSessionFiles(records, opts) {
976
1076
  // src/import-environment.ts
977
1077
  import { spawnSync as spawnSync2 } from "child_process";
978
1078
  import { createHash as createHash2 } from "crypto";
979
- import { closeSync as closeSync2, createWriteStream, lstatSync, mkdtempSync, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync5, readSync as readSync2, rmSync as rmSync2 } from "fs";
1079
+ import { closeSync as closeSync2, createWriteStream, lstatSync, mkdtempSync, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync2, rmSync as rmSync2 } from "fs";
980
1080
  import { tmpdir } from "os";
981
- import { join as join5 } from "path";
1081
+ import { join as join6 } from "path";
982
1082
  import { isUtf8 as isUtf82 } from "buffer";
983
1083
  import { createGzip } from "zlib";
984
1084
 
@@ -1157,14 +1257,14 @@ function listFiles(cwd) {
1157
1257
  const walk = (dir, prefix) => {
1158
1258
  let entries;
1159
1259
  try {
1160
- entries = readdirSync3(dir, { withFileTypes: true });
1260
+ entries = readdirSync4(dir, { withFileTypes: true });
1161
1261
  } catch {
1162
1262
  return;
1163
1263
  }
1164
1264
  for (const entry2 of entries) {
1165
1265
  if (SKIPPED.has(entry2.name)) continue;
1166
1266
  const rel = prefix ? `${prefix}/${entry2.name}` : entry2.name;
1167
- if (entry2.isDirectory()) walk(join5(dir, entry2.name), rel);
1267
+ if (entry2.isDirectory()) walk(join6(dir, entry2.name), rel);
1168
1268
  else out.push(rel);
1169
1269
  if (out.length > MAX_ENVIRONMENT_FILES) return;
1170
1270
  }
@@ -1174,15 +1274,15 @@ function listFiles(cwd) {
1174
1274
  }
1175
1275
  function textOf(path, size) {
1176
1276
  if (size > MAX_REDACTED_TEXT_BYTES) return null;
1177
- const data = readFileSync5(path);
1277
+ const data = readFileSync6(path);
1178
1278
  if (data.subarray(0, 8e3).includes(0) || !isUtf82(data)) return null;
1179
1279
  return data;
1180
1280
  }
1181
1281
  async function packEnvironment(cwd, resources, platform = IMPORT_PLATFORM) {
1182
1282
  const listed = listFiles(cwd);
1183
1283
  if (listed.length > MAX_ENVIRONMENT_FILES) throw new Error(`The working folder has more than ${MAX_ENVIRONMENT_FILES} files; import it with --no-environment.`);
1184
- const scratch = mkdtempSync(join5(tmpdir(), "sky-environment-"));
1185
- const path = join5(scratch, "environment.tar.gz");
1284
+ const scratch = mkdtempSync(join6(tmpdir(), "sky-environment-"));
1285
+ const path = join6(scratch, "environment.tar.gz");
1186
1286
  const leftOut = [];
1187
1287
  const written2 = /* @__PURE__ */ new Set();
1188
1288
  const hash = createHash2("sha256");
@@ -1210,7 +1310,7 @@ async function packEnvironment(cwd, resources, platform = IMPORT_PLATFORM) {
1210
1310
  });
1211
1311
  try {
1212
1312
  for (const rel of listed) {
1213
- const full = join5(cwd, ...rel.split("/"));
1313
+ const full = join6(cwd, ...rel.split("/"));
1214
1314
  if (SECRET_FILE2.test(rel)) {
1215
1315
  leftOut.push({ path: rel, reason: "credential file" });
1216
1316
  continue;
@@ -1364,16 +1464,16 @@ function sniffTitle(filePath) {
1364
1464
  }
1365
1465
  }
1366
1466
  function listClaudeCodeSessions(limit) {
1367
- const root = process.env.CLAUDE_CONFIG_DIR ?? join6(homedir3(), ".claude");
1368
- const projectsDir = join6(root, "projects");
1467
+ const root = process.env.CLAUDE_CONFIG_DIR ?? join7(homedir3(), ".claude");
1468
+ const projectsDir = join7(root, "projects");
1369
1469
  if (!existsSync4(projectsDir)) return [];
1370
1470
  const sessions = [];
1371
1471
  try {
1372
- for (const encoded of readdirSync4(projectsDir)) {
1373
- const projectDir = join6(projectsDir, encoded);
1472
+ for (const encoded of readdirSync5(projectsDir)) {
1473
+ const projectDir = join7(projectsDir, encoded);
1374
1474
  let entries;
1375
1475
  try {
1376
- entries = readdirSync4(projectDir);
1476
+ entries = readdirSync5(projectDir);
1377
1477
  } catch {
1378
1478
  continue;
1379
1479
  }
@@ -1381,22 +1481,22 @@ function listClaudeCodeSessions(limit) {
1381
1481
  for (const entry2 of entries) {
1382
1482
  if (!entry2.endsWith(".jsonl")) continue;
1383
1483
  const sessionId = entry2.slice(0, -6);
1384
- const mainFile = join6(projectDir, entry2);
1484
+ const mainFile = join7(projectDir, entry2);
1385
1485
  let stat;
1386
1486
  try {
1387
- stat = statSync5(mainFile);
1487
+ stat = statSync6(mainFile);
1388
1488
  } catch {
1389
1489
  continue;
1390
1490
  }
1391
1491
  if (!stat.isFile()) continue;
1392
1492
  let discoveryError;
1393
1493
  const subagentFiles = [];
1394
- const subagentDir = join6(projectDir, sessionId, "subagents");
1494
+ const subagentDir = join7(projectDir, sessionId, "subagents");
1395
1495
  if (existsSync4(subagentDir)) {
1396
1496
  try {
1397
- const subFiles = readdirSync4(subagentDir).filter((f) => f.endsWith(".jsonl")).sort();
1497
+ const subFiles = readdirSync5(subagentDir).filter((f) => f.endsWith(".jsonl")).sort();
1398
1498
  for (const f of subFiles) {
1399
- subagentFiles.push(join6(subagentDir, f));
1499
+ subagentFiles.push(join7(subagentDir, f));
1400
1500
  }
1401
1501
  } catch {
1402
1502
  discoveryError = "Cannot read all recorded subagent conversations. Fix local file access before importing this session.";
@@ -1418,17 +1518,17 @@ function listClaudeCodeSessions(limit) {
1418
1518
  return sessions.sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, limit);
1419
1519
  }
1420
1520
  function listCodexSessions(limit) {
1421
- const sessionsDir = join6(process.env.CODEX_HOME ?? process.env.CODEX_CONFIG_DIR ?? join6(homedir3(), ".codex"), "sessions");
1521
+ const sessionsDir = join7(process.env.CODEX_HOME ?? process.env.CODEX_CONFIG_DIR ?? join7(homedir3(), ".codex"), "sessions");
1422
1522
  const records = [];
1423
1523
  function walk(dir) {
1424
1524
  let entries;
1425
1525
  try {
1426
- entries = readdirSync4(dir, { withFileTypes: true });
1526
+ entries = readdirSync5(dir, { withFileTypes: true });
1427
1527
  } catch {
1428
1528
  return;
1429
1529
  }
1430
1530
  for (const entry2 of entries) {
1431
- const full = join6(dir, entry2.name);
1531
+ const full = join7(dir, entry2.name);
1432
1532
  if (entry2.isDirectory()) {
1433
1533
  walk(full);
1434
1534
  continue;
@@ -1446,7 +1546,7 @@ function listCodexSessions(limit) {
1446
1546
  sessionId: data.id,
1447
1547
  workspace: typeof data.cwd === "string" ? data.cwd : dir,
1448
1548
  title: sniffTitle(full),
1449
- mtimeMs: statSync5(full).mtimeMs,
1549
+ mtimeMs: statSync6(full).mtimeMs,
1450
1550
  files: [full]
1451
1551
  },
1452
1552
  ...typeof parent === "string" ? { parent } : {}
@@ -1515,7 +1615,7 @@ async function mintTrace(base, harness2) {
1515
1615
  function loadSummaryFile(summaryPath) {
1516
1616
  let raw;
1517
1617
  try {
1518
- raw = readFileSync6(summaryPath, "utf8");
1618
+ raw = readFileSync7(summaryPath, "utf8");
1519
1619
  } catch {
1520
1620
  throw new Error(`Cannot read summary file: ${summaryPath}`);
1521
1621
  }
@@ -1556,9 +1656,9 @@ function prepareImport(session2, summaryPath, options = {}) {
1556
1656
  const source = new SessionSourceCollector();
1557
1657
  const records = [];
1558
1658
  for (const path of session2.files) {
1559
- const stat = statSync5(path);
1659
+ const stat = statSync6(path);
1560
1660
  if (!stat.isFile() || (inputBytes += stat.size) > maximum) throw new Error("Selected transcript exceeds the 16 MiB limit or is not a regular file.");
1561
- const text = readFileSync6(path, "utf8");
1661
+ const text = readFileSync7(path, "utf8");
1562
1662
  for (const raw of text.split("\n")) {
1563
1663
  if (!raw.trim()) continue;
1564
1664
  let record;
@@ -1769,8 +1869,8 @@ function renderContextPage(page) {
1769
1869
  }
1770
1870
 
1771
1871
  // src/agent-file.ts
1772
- import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
1773
- import { dirname as dirname4, resolve as resolve3 } from "path";
1872
+ import { existsSync as existsSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "fs";
1873
+ import { dirname as dirname4, resolve as resolve4 } from "path";
1774
1874
  import { parse as parseToml2 } from "smol-toml";
1775
1875
  var fileInstructionSchema = external_exports.object({
1776
1876
  name: external_exports.string(),
@@ -1803,7 +1903,7 @@ var agentFileSchema = external_exports.object({
1803
1903
  memory_mb: external_exports.number().int().optional()
1804
1904
  });
1805
1905
  function readAgentFile(path) {
1806
- const raw = readFileSync7(path, "utf8");
1906
+ const raw = readFileSync8(path, "utf8");
1807
1907
  const parsed = agentFileSchema.safeParse(parseToml2(raw));
1808
1908
  if (!parsed.success) {
1809
1909
  const issue = parsed.error.issues[0];
@@ -1815,11 +1915,11 @@ function readAgentFile(path) {
1815
1915
  `${path}: reasoning_effort '${file.reasoning_effort}' is not available for ${file.type} \u2014 ${reasoningEffortDocLine()}`
1816
1916
  );
1817
1917
  }
1818
- const base = dirname4(resolve3(path));
1918
+ const base = dirname4(resolve4(path));
1819
1919
  const inline = (ref, inlineContent) => {
1820
1920
  if (inlineContent !== void 0) return inlineContent;
1821
1921
  if (!ref) return void 0;
1822
- return readFileSync7(resolve3(base, ref), "utf8");
1922
+ return readFileSync8(resolve4(base, ref), "utf8");
1823
1923
  };
1824
1924
  const spec = {
1825
1925
  name: file.name,
@@ -1845,7 +1945,7 @@ function readAgentFile(path) {
1845
1945
  return { file, spec };
1846
1946
  }
1847
1947
  function stampId(path, id) {
1848
- writeFileSync2(path, stampIdText(readFileSync7(path, "utf8"), id));
1948
+ writeFileSync2(path, stampIdText(readFileSync8(path, "utf8"), id));
1849
1949
  }
1850
1950
  function stampIdText(text, id) {
1851
1951
  const out = [];
@@ -1997,18 +2097,18 @@ function renderCatalogAgent(agent2) {
1997
2097
 
1998
2098
  // src/clone.ts
1999
2099
  import { spawn } from "child_process";
2000
- import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
2100
+ import { existsSync as existsSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
2001
2101
  import { homedir as homedir4 } from "os";
2002
- import { basename as basename5, join as join7 } from "path";
2102
+ import { basename as basename6, join as join8 } from "path";
2003
2103
  import { createInterface } from "readline";
2004
2104
  var PROMPT_CAP = 1e5;
2005
2105
  var HARNESSES = ["claude_code", "codex", "openclaw"];
2006
2106
  function claudeConfigRoot() {
2007
- return process.env.CLAUDE_CONFIG_DIR ?? join7(homedir4(), ".claude");
2107
+ return process.env.CLAUDE_CONFIG_DIR ?? join8(homedir4(), ".claude");
2008
2108
  }
2009
2109
  function readIfExists(path) {
2010
2110
  try {
2011
- return readFileSync8(path, "utf8");
2111
+ return readFileSync9(path, "utf8");
2012
2112
  } catch {
2013
2113
  return void 0;
2014
2114
  }
@@ -2024,9 +2124,9 @@ function readJson2(path) {
2024
2124
  }
2025
2125
  function claudeModel(configRoot) {
2026
2126
  const candidates = [
2027
- join7(process.cwd(), ".claude", "settings.local.json"),
2028
- join7(process.cwd(), ".claude", "settings.json"),
2029
- join7(configRoot, "settings.json")
2127
+ join8(process.cwd(), ".claude", "settings.local.json"),
2128
+ join8(process.cwd(), ".claude", "settings.json"),
2129
+ join8(configRoot, "settings.json")
2030
2130
  ];
2031
2131
  for (const path of candidates) {
2032
2132
  const model = readJson2(path)?.model;
@@ -2036,11 +2136,11 @@ function claudeModel(configRoot) {
2036
2136
  }
2037
2137
  function claudeMcpServers() {
2038
2138
  const merged = {};
2039
- const userConfig = readJson2(join7(homedir4(), ".claude.json"));
2139
+ const userConfig = readJson2(join8(homedir4(), ".claude.json"));
2040
2140
  Object.assign(merged, userConfig?.mcpServers ?? {});
2041
2141
  Object.assign(merged, userConfig?.projects?.[process.cwd()]?.mcpServers ?? {});
2042
2142
  const projectConfig = readJson2(
2043
- join7(process.cwd(), ".mcp.json")
2143
+ join8(process.cwd(), ".mcp.json")
2044
2144
  );
2045
2145
  Object.assign(merged, projectConfig?.mcpServers ?? {});
2046
2146
  return merged;
@@ -2057,13 +2157,13 @@ function readClaudeCode() {
2057
2157
  const configRoot = claudeConfigRoot();
2058
2158
  const parts = [];
2059
2159
  const sources = [];
2060
- const global = readIfExists(join7(configRoot, "CLAUDE.md"));
2160
+ const global = readIfExists(join8(configRoot, "CLAUDE.md"));
2061
2161
  if (global?.trim()) {
2062
2162
  parts.push({ label: "Global CLAUDE.md", content: global });
2063
2163
  sources.push("global CLAUDE.md");
2064
2164
  }
2065
2165
  for (const name of ["CLAUDE.md", "AGENTS.md"]) {
2066
- const project2 = readIfExists(join7(process.cwd(), name));
2166
+ const project2 = readIfExists(join8(process.cwd(), name));
2067
2167
  if (project2?.trim()) {
2068
2168
  parts.push({ label: `Project ${name}`, content: project2 });
2069
2169
  sources.push(`project ${name}`);
@@ -2104,8 +2204,8 @@ function detectHarness(explicit) {
2104
2204
  return explicit;
2105
2205
  }
2106
2206
  if (existsSync6(claudeConfigRoot())) return "claude_code";
2107
- if (existsSync6(join7(homedir4(), ".codex"))) return "codex";
2108
- if (existsSync6(join7(homedir4(), ".openclaw"))) return "openclaw";
2207
+ if (existsSync6(join8(homedir4(), ".codex"))) return "codex";
2208
+ if (existsSync6(join8(homedir4(), ".openclaw"))) return "openclaw";
2109
2209
  throw new Error(
2110
2210
  "no local harness found (~/.claude, ~/.codex, ~/.openclaw) \u2014 pass --harness to override"
2111
2211
  );
@@ -2117,7 +2217,7 @@ function readLocal(harness2) {
2117
2217
  );
2118
2218
  }
2119
2219
  function linkPath(configRoot) {
2120
- return join7(configRoot, "agentsky-link.json");
2220
+ return join8(configRoot, "agentsky-link.json");
2121
2221
  }
2122
2222
  function readLink(configRoot) {
2123
2223
  const link2 = readJson2(linkPath(configRoot));
@@ -2226,7 +2326,7 @@ async function createClone(local, opts) {
2226
2326
  `model ${local.model ?? "(harness default)"}`,
2227
2327
  `${keptNames.length} MCP server${keptNames.length === 1 ? "" : "s"}${keptNames.length ? ` (${keptNames.join(", ")})` : ""}`
2228
2328
  ].join(" \xB7 ");
2229
- console.log(`Detected ${local.harness} at ${local.configRoot} (project ${basename5(process.cwd())})`);
2329
+ console.log(`Detected ${local.harness} at ${local.configRoot} (project ${basename6(process.cwd())})`);
2230
2330
  console.log(`Will clone: ${summary}`);
2231
2331
  if (local.truncated) console.log(" note: instructions exceeded 100k chars and were truncated");
2232
2332
  if (local.droppedServers.length) {
@@ -2426,25 +2526,25 @@ import { hostname } from "os";
2426
2526
  import { spawn as spawn2 } from "child_process";
2427
2527
 
2428
2528
  // src/mcp-register.ts
2429
- import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
2529
+ import { existsSync as existsSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
2430
2530
  import { spawnSync as spawnSync3 } from "child_process";
2431
2531
  import { homedir as homedir6 } from "os";
2432
- import { join as join10 } from "path";
2532
+ import { join as join11 } from "path";
2433
2533
  import { parse as parseToml3 } from "smol-toml";
2434
2534
  import { isMap, parseDocument } from "yaml";
2435
2535
 
2436
2536
  // src/local-agents.ts
2437
2537
  import { existsSync as existsSync7 } from "fs";
2438
2538
  import { homedir as homedir5 } from "os";
2439
- import { join as join8 } from "path";
2539
+ import { join as join9 } from "path";
2440
2540
  function localAgents(env = process.env, home = homedir5()) {
2441
- const claude = env.CLAUDE_CONFIG_DIR || join8(home, ".claude");
2442
- const codex = env.CODEX_HOME || join8(home, ".codex");
2443
- const cursor = join8(home, ".cursor");
2541
+ const claude = env.CLAUDE_CONFIG_DIR || join9(home, ".claude");
2542
+ const codex = env.CODEX_HOME || join9(home, ".codex");
2543
+ const cursor = join9(home, ".cursor");
2444
2544
  return [
2445
- { id: "claude_code", name: "Claude Code", root: claude, skillsDir: join8(claude, "skills") },
2446
- { id: "codex", name: "Codex", root: codex, skillsDir: join8(codex, "skills") },
2447
- { id: "cursor", name: "Cursor", root: cursor, skillsDir: join8(cursor, "skills") }
2545
+ { id: "claude_code", name: "Claude Code", root: claude, skillsDir: join9(claude, "skills") },
2546
+ { id: "codex", name: "Codex", root: codex, skillsDir: join9(codex, "skills") },
2547
+ { id: "cursor", name: "Cursor", root: cursor, skillsDir: join9(cursor, "skills") }
2448
2548
  ];
2449
2549
  }
2450
2550
  function detectLocalAgents(env = process.env, home = homedir5()) {
@@ -2461,8 +2561,8 @@ function selectLocalAgents(ids, env = process.env, home = homedir5()) {
2461
2561
  }
2462
2562
 
2463
2563
  // src/skill.ts
2464
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
2465
- import { join as join9 } from "path";
2564
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
2565
+ import { join as join10 } from "path";
2466
2566
 
2467
2567
  // src/catalog-file.ts
2468
2568
  var CATALOG_FILE = "catalog.md";
@@ -2964,7 +3064,7 @@ To set a per-repo default, create \`.agentsky.json\` at the git root:
2964
3064
  }
2965
3065
  function installedVersion(dir) {
2966
3066
  try {
2967
- const marker = JSON.parse(readFileSync9(join9(dir, SKILL_MARKER), "utf8"));
3067
+ const marker = JSON.parse(readFileSync10(join10(dir, SKILL_MARKER), "utf8"));
2968
3068
  return typeof marker.version === "string" ? marker.version : null;
2969
3069
  } catch {
2970
3070
  return null;
@@ -2974,32 +3074,32 @@ function isInstalledSkill(dir) {
2974
3074
  return installedVersion(dir) !== null;
2975
3075
  }
2976
3076
  function installSkill(agent2, opts = {}) {
2977
- const dir = join9(agent2.skillsDir, SKILL_NAME);
3077
+ const dir = join10(agent2.skillsDir, SKILL_NAME);
2978
3078
  const previous = installedVersion(dir);
2979
3079
  const outcome = previous === CLI_VERSION ? "current" : previous ? "updated" : "installed";
2980
3080
  if (opts.dryRun) return { agent: agent2.id, path: dir, outcome };
2981
3081
  if (outcome !== "current") {
2982
3082
  mkdirSync2(dir, { recursive: true });
2983
- writeFileSync4(join9(dir, "SKILL.md"), skillDocument());
2984
- writeFileSync4(join9(dir, SKILL_MARKER), `${JSON.stringify({ version: CLI_VERSION }, null, 2)}
3083
+ writeFileSync4(join10(dir, "SKILL.md"), skillDocument());
3084
+ writeFileSync4(join10(dir, SKILL_MARKER), `${JSON.stringify({ version: CLI_VERSION }, null, 2)}
2985
3085
  `);
2986
3086
  }
2987
3087
  if (opts.catalog) {
2988
3088
  mkdirSync2(dir, { recursive: true });
2989
- writeFileSync4(join9(dir, CATALOG_FILE), renderCatalog(opts.catalog, opts.now ?? /* @__PURE__ */ new Date()));
3089
+ writeFileSync4(join10(dir, CATALOG_FILE), renderCatalog(opts.catalog, opts.now ?? /* @__PURE__ */ new Date()));
2990
3090
  }
2991
- const refsDir = join9(dir, REFERENCES_DIR);
3091
+ const refsDir = join10(dir, REFERENCES_DIR);
2992
3092
  mkdirSync2(refsDir, { recursive: true });
2993
- writeFileSync4(join9(refsDir, "import.md"), importReferenceDoc());
2994
- writeFileSync4(join9(refsDir, "workspaces.md"), workspacesReferenceDoc());
2995
- writeFileSync4(join9(refsDir, "focused-agents.md"), focusedAgentsReferenceDoc());
3093
+ writeFileSync4(join10(refsDir, "import.md"), importReferenceDoc());
3094
+ writeFileSync4(join10(refsDir, "workspaces.md"), workspacesReferenceDoc());
3095
+ writeFileSync4(join10(refsDir, "focused-agents.md"), focusedAgentsReferenceDoc());
2996
3096
  return { agent: agent2.id, path: dir, outcome };
2997
3097
  }
2998
3098
 
2999
3099
  // src/mcp-register.ts
3000
3100
  var MCP_SERVER_NAME = SKILL_NAME;
3001
3101
  function detectHermes(env = process.env, home = homedir6()) {
3002
- const root = env.HERMES_HOME || join10(home, ".hermes");
3102
+ const root = env.HERMES_HOME || join11(home, ".hermes");
3003
3103
  return existsSync8(root) ? { id: "hermes", name: "Hermes", root } : null;
3004
3104
  }
3005
3105
  function detectMcpClients(env = process.env, home = homedir6()) {
@@ -3047,18 +3147,18 @@ function registerMcpServer(client, opts, runner = systemCommandRunner()) {
3047
3147
  function mcpConfigPath(client, env = process.env, home = homedir6()) {
3048
3148
  switch (client.id) {
3049
3149
  case "claude_code":
3050
- return join10(env.CLAUDE_CONFIG_DIR || home, ".claude.json");
3150
+ return join11(env.CLAUDE_CONFIG_DIR || home, ".claude.json");
3051
3151
  case "codex":
3052
- return join10(client.root, "config.toml");
3152
+ return join11(client.root, "config.toml");
3053
3153
  case "cursor":
3054
- return join10(client.root, "mcp.json");
3154
+ return join11(client.root, "mcp.json");
3055
3155
  case "hermes":
3056
- return join10(client.root, "config.yaml");
3156
+ return join11(client.root, "config.yaml");
3057
3157
  }
3058
3158
  }
3059
3159
  function readText(path) {
3060
3160
  try {
3061
- return readFileSync10(path, "utf8");
3161
+ return readFileSync11(path, "utf8");
3062
3162
  } catch {
3063
3163
  return null;
3064
3164
  }
@@ -3160,29 +3260,29 @@ function refreshMcpRegistrations(opts, clients = detectMcpClients(), runner = sy
3160
3260
  }
3161
3261
 
3162
3262
  // src/shell-env.ts
3163
- import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
3263
+ import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
3164
3264
  import { homedir as homedir7 } from "os";
3165
- import { basename as basename6, dirname as dirname5, join as join11 } from "path";
3265
+ import { basename as basename7, dirname as dirname5, join as join12 } from "path";
3166
3266
  var TOKEN_ENV = "AGENTSKY_API_KEY";
3167
3267
  var HOOK_MARK = "# AgentSky API token, kept current by sky";
3168
3268
  function shellEnvFiles() {
3169
3269
  const dir = dirname5(configPath());
3170
- return { sh: join11(dir, "env.sh"), fish: join11(dir, "env.fish") };
3270
+ return { sh: join12(dir, "env.sh"), fish: join12(dir, "env.fish") };
3171
3271
  }
3172
3272
  function quoted(value) {
3173
3273
  return `'${value.replace(/'/g, `'\\''`)}'`;
3174
3274
  }
3175
3275
  function shellStartupFiles(env = process.env, home = homedir7(), platform = process.platform) {
3176
- const shell = basename6(env.SHELL ?? "");
3276
+ const shell = basename7(env.SHELL ?? "");
3177
3277
  const files2 = [];
3178
- const zshenv = join11(env.ZDOTDIR || home, ".zshenv");
3278
+ const zshenv = join12(env.ZDOTDIR || home, ".zshenv");
3179
3279
  if (shell === "zsh" || platform === "darwin" || existsSync9(zshenv)) files2.push({ path: zshenv, kind: "sh" });
3180
- const bashrc = join11(home, ".bashrc");
3280
+ const bashrc = join12(home, ".bashrc");
3181
3281
  if (shell === "bash" || existsSync9(bashrc)) files2.push({ path: bashrc, kind: "sh" });
3182
- const bashProfile = join11(home, ".bash_profile");
3282
+ const bashProfile = join12(home, ".bash_profile");
3183
3283
  if (existsSync9(bashProfile)) files2.push({ path: bashProfile, kind: "sh" });
3184
- const fish = join11(env.XDG_CONFIG_HOME || join11(home, ".config"), "fish");
3185
- if (shell === "fish" || existsSync9(fish)) files2.push({ path: join11(fish, "conf.d", "agentsky.fish"), kind: "fish" });
3284
+ const fish = join12(env.XDG_CONFIG_HOME || join12(home, ".config"), "fish");
3285
+ if (shell === "fish" || existsSync9(fish)) files2.push({ path: join12(fish, "conf.d", "agentsky.fish"), kind: "fish" });
3186
3286
  return files2;
3187
3287
  }
3188
3288
  function writeShellEnv(token, opts = {}) {
@@ -3198,7 +3298,7 @@ function writeShellEnv(token, opts = {}) {
3198
3298
  writeFileSync6(fish, `${header2}${token ? `set -gx ${TOKEN_ENV} ${quoted(token)}` : `set -e ${TOKEN_ENV}`}
3199
3299
  `, { mode: 384 });
3200
3300
  for (const file of startup) {
3201
- const existing = existsSync9(file.path) ? readFileSync11(file.path, "utf8") : "";
3301
+ const existing = existsSync9(file.path) ? readFileSync12(file.path, "utf8") : "";
3202
3302
  if (existing.includes(HOOK_MARK)) continue;
3203
3303
  const line = file.kind === "fish" ? `${HOOK_MARK}
3204
3304
  test -f ${quoted(fish)}; and source ${quoted(fish)}
@@ -3439,9 +3539,11 @@ async function triggerRequest(id, resource, change) {
3439
3539
 
3440
3540
  // src/session-workspace.ts
3441
3541
  import { spawnSync as spawnSync4 } from "child_process";
3442
- import { createHash as createHash5, randomUUID } from "crypto";
3443
- import { readdirSync as readdirSync5, statSync as statSync6 } from "fs";
3444
- import { basename as basename7, join as join12, relative as relative3, resolve as resolve5 } from "path";
3542
+ import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
3543
+ import { existsSync as existsSync10, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync6, readFileSync as readFileSync13, renameSync, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
3544
+ import { homedir as homedir8, hostname as hostname2, tmpdir as tmpdir2 } from "os";
3545
+ import { basename as basename8, dirname as dirname6, join as join13, relative as relative4, resolve as resolve6 } from "path";
3546
+ import { inflateRawSync } from "zlib";
3445
3547
  var SINGLE_IMPORT_MAX = 25 * 1024 * 1024;
3446
3548
  var PART_SIZE = 8 * 1024 * 1024;
3447
3549
  var SKIPPED2 = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -3475,15 +3577,15 @@ function folderFiles(dir) {
3475
3577
  const git2 = spawnSync4("git", ["-C", dir, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], { encoding: "utf8" });
3476
3578
  const top = spawnSync4("git", ["-C", dir, "rev-parse", "--show-prefix"], { encoding: "utf8" });
3477
3579
  if (git2.status === 0 && top.status === 0) {
3478
- return git2.stdout.split("\0").filter((path) => path && statSync6(join12(dir, path), { throwIfNoEntry: false })?.isFile());
3580
+ return git2.stdout.split("\0").filter((path) => path && statSync7(join13(dir, path), { throwIfNoEntry: false })?.isFile());
3479
3581
  }
3480
3582
  const files2 = [];
3481
3583
  const walk = (at) => {
3482
- for (const entry2 of readdirSync5(at, { withFileTypes: true })) {
3584
+ for (const entry2 of readdirSync6(at, { withFileTypes: true })) {
3483
3585
  if (SKIPPED2.has(entry2.name)) continue;
3484
- const full = join12(at, entry2.name);
3586
+ const full = join13(at, entry2.name);
3485
3587
  if (entry2.isDirectory()) walk(full);
3486
- else if (entry2.isFile()) files2.push(relative3(dir, full));
3588
+ else if (entry2.isFile()) files2.push(relative4(dir, full));
3487
3589
  }
3488
3590
  };
3489
3591
  walk(dir);
@@ -3499,8 +3601,8 @@ function folderArchive(dir, files2) {
3499
3601
  return tar.stdout;
3500
3602
  }
3501
3603
  async function importFolder(id, dir, opts = {}) {
3502
- const root = resolve5(dir);
3503
- const name = opts.name ?? basename7(root);
3604
+ const root = resolve6(dir);
3605
+ const name = opts.name ?? basename8(root);
3504
3606
  const archive = opts.archive ?? folderArchive(root, folderFiles(root));
3505
3607
  if (archive.length <= SINGLE_IMPORT_MAX) {
3506
3608
  const res = await apiBytes("POST", `${workspacePath(id)}/import?name=${encodeURIComponent(name)}`, { bytes: archive, mediaType: "application/gzip" });
@@ -3551,7 +3653,7 @@ async function handoff(id, targetAgent, opts = {}) {
3551
3653
  let { handoff: h } = await api("POST", `/sessions/${encodeURIComponent(id)}/handoff`, {
3552
3654
  target_agent: targetAgent,
3553
3655
  ...opts.fromMessage ? { last_message_id: opts.fromMessage } : {}
3554
- }, { idempotencyKey: opts.idempotencyKey ?? randomUUID() });
3656
+ }, { idempotencyKey: opts.idempotencyKey ?? randomUUID2() });
3555
3657
  opts.onStatus?.(h);
3556
3658
  while (opts.wait && !TERMINAL.has(h.status)) {
3557
3659
  await new Promise((r) => setTimeout(r, opts.pollMs ?? 2e3));
@@ -3561,6 +3663,97 @@ async function handoff(id, targetAgent, opts = {}) {
3561
3663
  }
3562
3664
  return h;
3563
3665
  }
3666
+ function zipEntries(archive) {
3667
+ let end = archive.length - 22;
3668
+ while (end >= 0 && archive.readUInt32LE(end) !== 101010256) end -= 1;
3669
+ if (end < 0) throw new ApiError(422, "checkpoint_invalid", "the downloaded conversation is not a readable archive");
3670
+ const entries = /* @__PURE__ */ new Map();
3671
+ let at = archive.readUInt32LE(end + 16);
3672
+ for (let index = archive.readUInt16LE(end + 10); index > 0; index -= 1) {
3673
+ const method = archive.readUInt16LE(at + 10);
3674
+ const size = archive.readUInt32LE(at + 20);
3675
+ const nameLength = archive.readUInt16LE(at + 28);
3676
+ const local = archive.readUInt32LE(at + 42);
3677
+ const start = local + 30 + archive.readUInt16LE(local + 26) + archive.readUInt16LE(local + 28);
3678
+ const packed = archive.subarray(start, start + size);
3679
+ if (method !== 0 && method !== 8) throw new ApiError(422, "checkpoint_invalid", "the downloaded conversation uses an unsupported compression");
3680
+ entries.set(archive.subarray(at + 46, at + 46 + nameLength).toString("utf8"), method === 8 ? inflateRawSync(packed) : Buffer.from(packed));
3681
+ at += 46 + nameLength + archive.readUInt16LE(at + 30) + archive.readUInt16LE(at + 32);
3682
+ }
3683
+ return entries;
3684
+ }
3685
+ function conversationFiles(archive, harness2) {
3686
+ const entries = zipEntries(archive);
3687
+ const manifest = JSON.parse(entries.get("manifest.json")?.toString("utf8") ?? "null");
3688
+ const nativeId = manifest?.metadata?.native_session_id ?? "";
3689
+ if (manifest?.format !== "asteroids.native-checkpoint" || manifest.source_harness !== harness2 || !Array.isArray(manifest.files) || !/^[A-Za-z0-9_-]{1,200}$/.test(nativeId)) {
3690
+ throw new ApiError(422, "checkpoint_invalid", "the downloaded conversation is not one this computer can continue");
3691
+ }
3692
+ const ours = (path) => {
3693
+ const parts = path.split("/");
3694
+ if (parts.some((part) => !part || part === "." || part === "..")) return false;
3695
+ if (harness2 === "claude_code") return parts[0] === "projects" && parts.length >= 3 && (parts[2] === `${nativeId}.jsonl` || parts[2] === nativeId);
3696
+ return parts[0] === "sessions" && parts.at(-1).startsWith("rollout-") && parts.at(-1).endsWith(`${nativeId}.jsonl`);
3697
+ };
3698
+ const files2 = manifest.files.filter((file) => ours(file.path)).map((file) => {
3699
+ const data = entries.get(`files/${file.path}`);
3700
+ if (!data || data.length !== file.size || createHash5("sha256").update(data).digest("hex") !== file.sha256) {
3701
+ throw new ApiError(422, "checkpoint_invalid", "the downloaded conversation is incomplete; try again");
3702
+ }
3703
+ return { path: file.path, data };
3704
+ });
3705
+ if (!files2.length) throw new ApiError(422, "checkpoint_invalid", "the downloaded conversation has no transcript");
3706
+ return { nativeId, files: files2 };
3707
+ }
3708
+ async function verifiedDownload(path) {
3709
+ const res = await apiRaw("GET", path);
3710
+ if (!res.ok) {
3711
+ const body = await res.json().catch(() => null);
3712
+ throw new ApiError(res.status, body?.error?.code ?? "request_failed", body?.error?.message ?? `AgentSky answered ${res.status}`);
3713
+ }
3714
+ const bytes = Buffer.from(await res.arrayBuffer());
3715
+ const expected = res.headers.get("x-content-sha256");
3716
+ if (expected && createHash5("sha256").update(bytes).digest("hex") !== expected) throw new ApiError(502, "download_corrupt", "the download was damaged; try again");
3717
+ return bytes;
3718
+ }
3719
+ async function continueHere(id, dir, opts = {}) {
3720
+ const session2 = (await api("GET", `/sessions/${encodeURIComponent(id)}`)).session;
3721
+ const harness2 = session2.agentType;
3722
+ if (harness2 !== "claude_code" && harness2 !== "codex") throw new ApiError(409, "harness_unsupported", "only Claude Code and Codex sessions can continue on a computer");
3723
+ let { handoff: h } = await api("POST", `/sessions/${encodeURIComponent(id)}/local-handoff`, { machine: hostname2().slice(0, 120) }, { idempotencyKey: opts.idempotencyKey ?? randomUUID2() });
3724
+ opts.onStatus?.(h);
3725
+ while (!TERMINAL.has(h.status)) {
3726
+ await new Promise((r) => setTimeout(r, opts.pollMs ?? 2e3));
3727
+ const next = (await api("GET", `/session-handoffs/${encodeURIComponent(h.id)}`)).handoff;
3728
+ if (next.status !== h.status) opts.onStatus?.(next);
3729
+ h = next;
3730
+ }
3731
+ if (h.status !== "completed") throw new ApiError(422, h.error?.code ?? "handoff_failed", h.error?.message ?? "the session could not be moved; it stays in the cloud");
3732
+ const { nativeId, files: files2 } = conversationFiles(await verifiedDownload(`/session-handoffs/${encodeURIComponent(h.id)}/checkpoint`), harness2);
3733
+ const root = harness2 === "codex" ? process.env.CODEX_HOME || join13(homedir8(), ".codex") : process.env.CLAUDE_CONFIG_DIR || join13(homedir8(), ".claude");
3734
+ const stamp = Date.now();
3735
+ for (const file of files2) {
3736
+ const target = join13(root, file.path);
3737
+ mkdirSync4(dirname6(target), { recursive: true });
3738
+ if (existsSync10(target)) {
3739
+ if (!lstatSync2(target).isFile()) throw new ApiError(409, "restore_blocked", `something other than a file is in the way at ${target}`);
3740
+ if (readFileSync13(target).equals(file.data)) continue;
3741
+ renameSync(target, `${target}.before-agentsky-${stamp}`);
3742
+ }
3743
+ writeFileSync7(target, file.data, { mode: 384 });
3744
+ }
3745
+ const folder = resolve6(dir);
3746
+ mkdirSync4(folder, { recursive: true });
3747
+ const carried = h.workspace_carried === true;
3748
+ if (carried) {
3749
+ const archive = join13(tmpdir2(), `agentsky-workspace-${h.id}.tar.gz`);
3750
+ writeFileSync7(archive, await verifiedDownload(`/session-handoffs/${encodeURIComponent(h.id)}/workspace`), { mode: 384 });
3751
+ const tar = spawnSync4("tar", ["-xzf", archive, "-C", folder]);
3752
+ if (tar.status !== 0) throw new ApiError(422, "workspace_unpack_failed", `tar failed: ${tar.stderr.toString().trim()}`);
3753
+ }
3754
+ const resume = harness2 === "codex" ? `cd ${JSON.stringify(folder)} && codex resume ${nativeId}` : `cd ${JSON.stringify(folder)} && claude --resume ${nativeId}`;
3755
+ return { harness: harness2, nativeId, folder, workspaceCarried: carried, resume };
3756
+ }
3564
3757
 
3565
3758
  // src/render.ts
3566
3759
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
@@ -3682,15 +3875,15 @@ async function readTurn(sessionId, opts = {}) {
3682
3875
  }
3683
3876
 
3684
3877
  // src/setup.ts
3685
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
3686
- import { dirname as dirname7, join as join15 } from "path";
3878
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
3879
+ import { dirname as dirname8, join as join16 } from "path";
3687
3880
  import { createInterface as createInterface3 } from "readline";
3688
3881
 
3689
3882
  // src/setup-local.ts
3690
3883
  import { isUtf8 as isUtf83 } from "buffer";
3691
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync7 } from "fs";
3692
- import { homedir as homedir8 } from "os";
3693
- import { dirname as dirname6, isAbsolute as isAbsolute2, join as join13, resolve as resolve6 } from "path";
3884
+ import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync8 } from "fs";
3885
+ import { homedir as homedir9 } from "os";
3886
+ import { dirname as dirname7, isAbsolute as isAbsolute2, join as join14, resolve as resolve7 } from "path";
3694
3887
  import { parse as parseToml4 } from "smol-toml";
3695
3888
  var MAX_SKILL_FILE_BYTES = 1024 * 1024;
3696
3889
  var MAX_IMPORT_DEPTH = 5;
@@ -3714,12 +3907,12 @@ function redacted(data) {
3714
3907
  }
3715
3908
  function skillFiles2(root, prefix) {
3716
3909
  const files2 = [];
3717
- for (const entry2 of readdirSync6(root, { withFileTypes: true })) {
3718
- const path = join13(root, entry2.name);
3910
+ for (const entry2 of readdirSync7(root, { withFileTypes: true })) {
3911
+ const path = join14(root, entry2.name);
3719
3912
  if (entry2.isDirectory()) {
3720
3913
  if (!SKIPPED_DIRS.has(entry2.name)) files2.push(...skillFiles2(path, `${prefix}/${entry2.name}`));
3721
- } else if (entry2.isFile() && !sensitive(entry2.name) && statSync7(path).size <= MAX_SKILL_FILE_BYTES) {
3722
- const bytes = redacted(readFileSync12(path));
3914
+ } else if (entry2.isFile() && !sensitive(entry2.name) && statSync8(path).size <= MAX_SKILL_FILE_BYTES) {
3915
+ const bytes = redacted(readFileSync14(path));
3723
3916
  if (bytes) files2.push({ path: `${prefix}/${entry2.name}`, bytes });
3724
3917
  }
3725
3918
  }
@@ -3729,19 +3922,19 @@ function readLocalSkills(agents) {
3729
3922
  const skills2 = [];
3730
3923
  const ignored = [];
3731
3924
  for (const agent2 of agents) {
3732
- if (!existsSync10(agent2.skillsDir)) continue;
3733
- for (const entry2 of readdirSync6(agent2.skillsDir, { withFileTypes: true })) {
3734
- const dir = join13(agent2.skillsDir, entry2.name);
3925
+ if (!existsSync11(agent2.skillsDir)) continue;
3926
+ for (const entry2 of readdirSync7(agent2.skillsDir, { withFileTypes: true })) {
3927
+ const dir = join14(agent2.skillsDir, entry2.name);
3735
3928
  let isDir2 = entry2.isDirectory();
3736
3929
  if (entry2.isSymbolicLink()) {
3737
3930
  try {
3738
- isDir2 = statSync7(dir).isDirectory();
3931
+ isDir2 = statSync8(dir).isDirectory();
3739
3932
  } catch {
3740
3933
  isDir2 = false;
3741
3934
  }
3742
3935
  }
3743
- if (!isDir2 || !existsSync10(join13(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
3744
- const name = frontmatterName(readFileSync12(join13(dir, "SKILL.md"), "utf8"));
3936
+ if (!isDir2 || !existsSync11(join14(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
3937
+ const name = frontmatterName(readFileSync14(join14(dir, "SKILL.md"), "utf8"));
3745
3938
  if (!name) {
3746
3939
  ignored.push({ agent: agent2.id, dir, reason: "SKILL.md has no name" });
3747
3940
  continue;
@@ -3769,11 +3962,11 @@ function inlineImports(content, from, home, depth, seen) {
3769
3962
  }
3770
3963
  const target = fence ? void 0 : line.match(/^@(\S+\.(?:md|markdown|txt))$/i)?.[1];
3771
3964
  if (!target || depth >= MAX_IMPORT_DEPTH) return line;
3772
- const path = target.startsWith("~/") ? join13(home, target.slice(2)) : isAbsolute2(target) ? target : resolve6(dirname6(from), target);
3965
+ const path = target.startsWith("~/") ? join14(home, target.slice(2)) : isAbsolute2(target) ? target : resolve7(dirname7(from), target);
3773
3966
  if (seen.includes(path)) return "";
3774
3967
  let imported;
3775
3968
  try {
3776
- imported = readFileSync12(path, "utf8");
3969
+ imported = readFileSync14(path, "utf8");
3777
3970
  } catch {
3778
3971
  return line;
3779
3972
  }
@@ -3781,15 +3974,15 @@ function inlineImports(content, from, home, depth, seen) {
3781
3974
  return inlineImports(imported, path, home, depth + 1, seen).trim();
3782
3975
  }).join("\n");
3783
3976
  }
3784
- function readGlobalInstructions(agents, home = homedir8()) {
3977
+ function readGlobalInstructions(agents, home = homedir9()) {
3785
3978
  const found = [];
3786
3979
  for (const agent2 of agents) {
3787
3980
  const file = INSTRUCTION_FILES2[agent2.id];
3788
3981
  if (!file) continue;
3789
- const path = join13(agent2.root, file);
3982
+ const path = join14(agent2.root, file);
3790
3983
  let raw;
3791
3984
  try {
3792
- raw = readFileSync12(path, "utf8");
3985
+ raw = readFileSync14(path, "utf8");
3793
3986
  } catch {
3794
3987
  continue;
3795
3988
  }
@@ -3808,7 +4001,7 @@ ${item.content.trim()}`).join("\n\n");
3808
4001
  }
3809
4002
  function readJson3(path) {
3810
4003
  try {
3811
- return JSON.parse(readFileSync12(path, "utf8"));
4004
+ return JSON.parse(readFileSync14(path, "utf8"));
3812
4005
  } catch {
3813
4006
  return void 0;
3814
4007
  }
@@ -3822,15 +4015,15 @@ function bearerOf(def) {
3822
4015
  return match?.[1] ?? null;
3823
4016
  }
3824
4017
  function serversOf(agent2, home) {
3825
- if (agent2.id === "claude_code") return readJson3(join13(home, ".claude.json"))?.mcpServers ?? {};
3826
- if (agent2.id === "cursor") return readJson3(join13(agent2.root, "mcp.json"))?.mcpServers ?? {};
4018
+ if (agent2.id === "claude_code") return readJson3(join14(home, ".claude.json"))?.mcpServers ?? {};
4019
+ if (agent2.id === "cursor") return readJson3(join14(agent2.root, "mcp.json"))?.mcpServers ?? {};
3827
4020
  try {
3828
- return parseToml4(readFileSync12(join13(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
4021
+ return parseToml4(readFileSync14(join14(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
3829
4022
  } catch {
3830
4023
  return {};
3831
4024
  }
3832
4025
  }
3833
- function findLocalSecrets(agents, home = homedir8()) {
4026
+ function findLocalSecrets(agents, home = homedir9()) {
3834
4027
  const secrets2 = [];
3835
4028
  for (const agent2 of agents) {
3836
4029
  for (const [name, def] of Object.entries(serversOf(agent2, home))) {
@@ -3847,9 +4040,9 @@ function findLocalSecrets(agents, home = homedir8()) {
3847
4040
 
3848
4041
  // src/subscriptions.ts
3849
4042
  import { spawn as spawn3 } from "child_process";
3850
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
3851
- import { homedir as homedir9 } from "os";
3852
- import { join as join14 } from "path";
4043
+ import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
4044
+ import { homedir as homedir10 } from "os";
4045
+ import { join as join15 } from "path";
3853
4046
  import { createInterface as createInterface2 } from "readline";
3854
4047
  var ALIASES = {
3855
4048
  claude: "anthropic",
@@ -3944,12 +4137,12 @@ async function connectClaude() {
3944
4137
  const expiry = sub.expiresAt ? ` (expires ~${sub.expiresAt.slice(0, 10)})` : "";
3945
4138
  console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
3946
4139
  }
3947
- function codexSignIn(env = process.env, home = homedir9()) {
3948
- const path = join14(env.CODEX_HOME || join14(home, ".codex"), "auth.json");
3949
- if (!existsSync11(path)) {
4140
+ function codexSignIn(env = process.env, home = homedir10()) {
4141
+ const path = join15(env.CODEX_HOME || join15(home, ".codex"), "auth.json");
4142
+ if (!existsSync12(path)) {
3950
4143
  throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
3951
4144
  }
3952
- const raw = readFileSync13(path, "utf8");
4145
+ const raw = readFileSync15(path, "utf8");
3953
4146
  try {
3954
4147
  const parsed = JSON.parse(raw);
3955
4148
  return { path, raw, account: parsed.tokens?.account_id };
@@ -3957,12 +4150,12 @@ function codexSignIn(env = process.env, home = homedir9()) {
3957
4150
  throw new ApiError(400, "invalid_codex_auth", `${path} isn't valid JSON \u2014 re-run \`codex login\``);
3958
4151
  }
3959
4152
  }
3960
- function cursorSignIn(env = process.env, home = homedir9()) {
3961
- const path = join14(env.XDG_CONFIG_HOME || join14(home, ".config"), "cursor", "auth.json");
3962
- if (!existsSync11(path)) {
4153
+ function cursorSignIn(env = process.env, home = homedir10()) {
4154
+ const path = join15(env.XDG_CONFIG_HOME || join15(home, ".config"), "cursor", "auth.json");
4155
+ if (!existsSync12(path)) {
3963
4156
  throw new ApiError(404, "no_cursor_auth", `no ${path} \u2014 run \`cursor-agent login\` first`);
3964
4157
  }
3965
- const raw = readFileSync13(path, "utf8");
4158
+ const raw = readFileSync15(path, "utf8");
3966
4159
  try {
3967
4160
  const parsed = JSON.parse(raw);
3968
4161
  if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string") throw new Error("shape");
@@ -4123,11 +4316,11 @@ function planProviders(names2) {
4123
4316
  }));
4124
4317
  }
4125
4318
  function reportPath() {
4126
- return join15(dirname7(configPath()), "setup.json");
4319
+ return join16(dirname8(configPath()), "setup.json");
4127
4320
  }
4128
4321
  function loadReport() {
4129
4322
  try {
4130
- const saved = JSON.parse(readFileSync14(reportPath(), "utf8"));
4323
+ const saved = JSON.parse(readFileSync16(reportPath(), "utf8"));
4131
4324
  return { agents: saved.agents ?? [], plans: saved.plans ?? [], ...saved.secrets ? { secrets: saved.secrets } : {} };
4132
4325
  } catch {
4133
4326
  return { agents: [], plans: [] };
@@ -4140,8 +4333,8 @@ function mergeReport(base, next) {
4140
4333
  return { agents, plans, ...secrets2 ? { secrets: secrets2 } : {} };
4141
4334
  }
4142
4335
  function saveReport(report) {
4143
- mkdirSync4(dirname7(reportPath()), { recursive: true });
4144
- writeFileSync7(reportPath(), `${JSON.stringify(report, null, 2)}
4336
+ mkdirSync5(dirname8(reportPath()), { recursive: true });
4337
+ writeFileSync8(reportPath(), `${JSON.stringify(report, null, 2)}
4145
4338
  `, { mode: 384 });
4146
4339
  }
4147
4340
  function confirm3(question) {
@@ -4511,71 +4704,10 @@ function renderArtifact(artifact2) {
4511
4704
  return `${artifact2.id} ${artifact2.name} v${artifact2.version} ${files2} entry ${artifact2.entry}${artifact2.share_url ? ` shared ${artifact2.share_url}` : ""}`;
4512
4705
  }
4513
4706
 
4514
- // src/skills-store.ts
4515
- import { readdirSync as readdirSync7, readFileSync as readFileSync15, statSync as statSync8 } from "fs";
4516
- import { basename as basename8, join as join16, relative as relative4, resolve as resolve7 } from "path";
4517
- function skillBundle(dir) {
4518
- const root = resolve7(dir);
4519
- const top = basename8(root);
4520
- const files2 = [];
4521
- const walk = (path) => {
4522
- for (const name of readdirSync7(path).sort()) {
4523
- const full = join16(path, name);
4524
- if (statSync8(full).isDirectory()) walk(full);
4525
- else files2.push({ path: `${top}/${relative4(root, full)}`, bytes: readFileSync15(full) });
4526
- }
4527
- };
4528
- walk(root);
4529
- if (!files2.some((file) => file.path === `${top}/SKILL.md`)) throw new Error(`${dir}: no SKILL.md at its root`);
4530
- return files2;
4531
- }
4532
- async function uploadSkill(dir, displayName) {
4533
- const form = new FormData();
4534
- for (const file of skillBundle(dir)) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
4535
- form.append("displayName", displayName ?? basename8(resolve7(dir)));
4536
- return apiForm("POST", "/skills", form);
4537
- }
4538
- async function listSkills() {
4539
- return (await api("GET", "/skills")).skills;
4540
- }
4541
- async function getTask(task2) {
4542
- return (await api("GET", `/tasks/${encodeURIComponent(task2)}`)).task;
4543
- }
4544
- function answerBody(answer) {
4545
- if (!answer.option === !answer.text) throw new Error("give exactly one of --option or --text");
4546
- return { ...answer.option ? { option_id: answer.option } : { text: answer.text }, ...answer.note ? { note: answer.note } : {} };
4547
- }
4548
- async function answerRequest(task2, request, answer) {
4549
- return api("POST", `/tasks/${encodeURIComponent(task2)}/requests/${encodeURIComponent(request)}/answer`, answerBody(answer));
4550
- }
4551
- function renderRequest(request) {
4552
- const options = request.options.length ? `
4553
- options: ${request.options.map((option) => `${option.id} (${option.label})`).join(", ")}` : "";
4554
- return `${request.id} ${request.kind} from ${request.session_id}
4555
- ${request.question}${options}`;
4556
- }
4557
- async function getSkillUsage(since) {
4558
- const qs = since ? `?since=${encodeURIComponent(since)}` : "";
4559
- return api("GET", `/skills/usage${qs}`);
4560
- }
4561
- function nextSkills(current, skillId, verb) {
4562
- const has = current.some((ref) => ref.type === "skill" && ref.skillId === skillId);
4563
- if (verb === "attach") return has ? null : [...current, { type: "skill", skillId, version: "latest" }];
4564
- return has ? current.filter((ref) => !(ref.type === "skill" && ref.skillId === skillId)) : null;
4565
- }
4566
- async function setAgentSkill(agentId, skillId, verb) {
4567
- const path = `/agents/${encodeURIComponent(agentId)}`;
4568
- const { agent: agent2 } = await api("GET", path);
4569
- const skills2 = nextSkills(agent2.skills ?? [], skillId, verb);
4570
- if (!skills2) return "unchanged";
4571
- await api("PATCH", path, { skills: skills2, expectedVersion: agent2.version });
4572
- return "changed";
4573
- }
4574
-
4575
4707
  // src/task-export.ts
4576
4708
  import { createHash as createHash6 } from "crypto";
4577
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "fs";
4578
- import { dirname as dirname8, join as join17 } from "path";
4709
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "fs";
4710
+ import { dirname as dirname9, join as join17 } from "path";
4579
4711
  import { stringify as stringifyToml } from "smol-toml";
4580
4712
  var TEXT_MAX = 2e4;
4581
4713
  function scrub(text, names2) {
@@ -4866,8 +4998,8 @@ async function exportTaskHarbor(taskRef, outDir, opts) {
4866
4998
  const written2 = [];
4867
4999
  for (const [path, content] of files2) {
4868
5000
  const target = join17(outDir, path);
4869
- mkdirSync5(dirname8(target), { recursive: true });
4870
- writeFileSync8(target, content, typeof content === "string" ? { mode: path.endsWith(".sh") || path.endsWith(".py") ? 493 : 420 } : void 0);
5001
+ mkdirSync6(dirname9(target), { recursive: true });
5002
+ writeFileSync9(target, content, typeof content === "string" ? { mode: path.endsWith(".sh") || path.endsWith(".py") ? 493 : 420 } : void 0);
4871
5003
  written2.push(path);
4872
5004
  }
4873
5005
  return { written: written2, record };
@@ -4881,7 +5013,7 @@ function parseRedactNames(values) {
4881
5013
  }
4882
5014
 
4883
5015
  // src/task-record.ts
4884
- import { randomUUID as randomUUID2 } from "crypto";
5016
+ import { randomUUID as randomUUID3 } from "crypto";
4885
5017
  var DECISION_KINDS = ["adopt", "rerun", "revise", "discard", "vote", "did_the_job", "approve"];
4886
5018
  async function listThreads(taskRef) {
4887
5019
  return (await api("GET", `/sessions/${encodeURIComponent(taskRef)}/threads`)).threads;
@@ -4894,7 +5026,7 @@ function renderThread(thread2) {
4894
5026
  async function recordDecision(sessionRef, kind, opts = {}) {
4895
5027
  if (!DECISION_KINDS.includes(kind)) throw new Error(`kind must be one of ${DECISION_KINDS.join(", ")}`);
4896
5028
  if ((kind === "did_the_job" || kind === "approve") && typeof opts.value !== "boolean") throw new Error(`${kind} needs --yes or --no`);
4897
- const key = opts.idempotencyKey ?? randomUUID2();
5029
+ const key = opts.idempotencyKey ?? randomUUID3();
4898
5030
  const res = await fetchRecord(sessionRef, {
4899
5031
  type: "human.decision",
4900
5032
  payload: {
@@ -4910,7 +5042,7 @@ async function recordDecision(sessionRef, kind, opts = {}) {
4910
5042
  var OUTCOME_KINDS = ["pr_merged", "deployed", "reopened", "later_failure", "shipped"];
4911
5043
  async function recordOutcome(sessionRef, kind, opts = {}) {
4912
5044
  if (!OUTCOME_KINDS.includes(kind)) throw new Error(`kind must be one of ${OUTCOME_KINDS.join(", ")}`);
4913
- return fetchRecord(sessionRef, { type: "outcome.observed", payload: { kind, ...opts.ref ? { ref: opts.ref } : {}, observer: opts.observer ?? "cli" } }, opts.idempotencyKey ?? randomUUID2());
5045
+ return fetchRecord(sessionRef, { type: "outcome.observed", payload: { kind, ...opts.ref ? { ref: opts.ref } : {}, observer: opts.observer ?? "cli" } }, opts.idempotencyKey ?? randomUUID3());
4914
5046
  }
4915
5047
  async function fetchRecord(sessionRef, body, idempotencyKey) {
4916
5048
  const result = await api("POST", `/sessions/${encodeURIComponent(sessionRef)}/record`, body, { idempotencyKey });
@@ -5359,14 +5491,14 @@ function renderThreadSummary(t) {
5359
5491
  }
5360
5492
 
5361
5493
  // src/library.ts
5362
- import { readFileSync as readFileSync17 } from "fs";
5494
+ import { readFileSync as readFileSync18 } from "fs";
5363
5495
 
5364
5496
  // src/api-command.ts
5365
- import { readFileSync as readFileSync16 } from "fs";
5497
+ import { readFileSync as readFileSync17 } from "fs";
5366
5498
  var API_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
5367
- function readData(data, stdin = () => readFileSync16(0, "utf8")) {
5499
+ function readData(data, stdin = () => readFileSync17(0, "utf8")) {
5368
5500
  if (data === void 0) return void 0;
5369
- const raw = data === "-" ? stdin() : data.startsWith("@") ? readFileSync16(data.slice(1), "utf8") : data;
5501
+ const raw = data === "-" ? stdin() : data.startsWith("@") ? readFileSync17(data.slice(1), "utf8") : data;
5370
5502
  try {
5371
5503
  JSON.parse(raw);
5372
5504
  } catch {
@@ -5572,7 +5704,7 @@ function registerLibraryLinkCommands(library2) {
5572
5704
  });
5573
5705
  libraryLinks.command("save").description("save through a live recipient edit grant; --revision is the opaque source revision from links read").requiredOption("--token <token>", "43-character token from the authenticated shared URL (public URLs have no writer)").requiredOption("--revision <revision>", "native source revision from links read; stale revisions fail without retry").option("--file <path>", "UTF-8 text file to save").option("--data <json>", "instead of --file, JSON/ @file / - containing exactly one text/html/office/docx/restoreRevision and optional requestId").action(async (opts) => {
5574
5706
  if (opts.file !== void 0 === (opts.data !== void 0)) throw new Error("Choose exactly one of --file or --data.");
5575
- const body = opts.file !== void 0 ? { text: new TextDecoder("utf-8", { fatal: true }).decode(readFileSync17(opts.file)) } : JSON.parse(readData(opts.data));
5707
+ const body = opts.file !== void 0 ? { text: new TextDecoder("utf-8", { fatal: true }).decode(readFileSync18(opts.file)) } : JSON.parse(readData(opts.data));
5576
5708
  if (!body || typeof body !== "object" || Array.isArray(body)) throw new Error("--data must be a JSON object with one native save field.");
5577
5709
  if ("revision" in body && body.revision !== opts.revision) throw new Error("--data revision must match --revision.");
5578
5710
  console.log(JSON.stringify(await saveSharedLibraryFile(opts.token, sharedLibrarySaveSchema.parse({ ...body, revision: opts.revision })), null, 2));
@@ -5615,7 +5747,7 @@ function renderTodo(todo) {
5615
5747
  }
5616
5748
 
5617
5749
  // src/design-systems.ts
5618
- import { readdirSync as readdirSync8, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
5750
+ import { readdirSync as readdirSync8, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
5619
5751
  import { join as join18, relative as relative5, sep as sep3 } from "path";
5620
5752
  var TEXT2 = /\.(mdx?|markdown|txt|json|m?js|cjs|jsx|tsx?|css|html?|svg|xml|csv|ya?ml|toml)$/i;
5621
5753
  var MEDIA = {
@@ -5666,7 +5798,7 @@ function fileTableFrom(dir) {
5666
5798
  continue;
5667
5799
  }
5668
5800
  const key = relative5(dir, path).split(sep3).join("/");
5669
- const bytes = readFileSync18(path);
5801
+ const bytes = readFileSync19(path);
5670
5802
  files2[key] = TEXT2.test(name) ? bytes.toString("utf8") : `data:${MEDIA[name.split(".").pop()?.toLowerCase() ?? ""] ?? "application/octet-stream"};base64,${bytes.toString("base64")}`;
5671
5803
  }
5672
5804
  };
@@ -5775,19 +5907,19 @@ function workspaceFlagIsCommands(argv, commandPath) {
5775
5907
  }
5776
5908
 
5777
5909
  // src/harness.ts
5778
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
5779
- import { dirname as dirname9, join as join19, resolve as resolve8 } from "path";
5910
+ import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
5911
+ import { dirname as dirname10, join as join19, resolve as resolve8 } from "path";
5780
5912
  var HARNESS_FILE = "harness.json";
5781
5913
  function harnessFilePath(dir) {
5782
5914
  return join19(resolve8(dir), HARNESS_FILE);
5783
5915
  }
5784
5916
  function readHarnessFile(dir) {
5785
5917
  const path = harnessFilePath(dir);
5786
- if (!existsSync12(path)) {
5918
+ if (!existsSync13(path)) {
5787
5919
  throw new Error(`no ${HARNESS_FILE} found in ${resolve8(dir)} \u2014 run \`sky harness init\` first`);
5788
5920
  }
5789
5921
  try {
5790
- return JSON.parse(readFileSync19(path, "utf8"));
5922
+ return JSON.parse(readFileSync20(path, "utf8"));
5791
5923
  } catch (err) {
5792
5924
  throw new Error(`could not parse ${path}: ${err instanceof Error ? err.message : String(err)}`);
5793
5925
  }
@@ -5814,11 +5946,11 @@ var INIT_COMMENT = `# harness.json \u2014 workspace harness profile skeleton
5814
5946
  `;
5815
5947
  function harnessInit(dir) {
5816
5948
  const path = harnessFilePath(dir);
5817
- if (existsSync12(path)) {
5949
+ if (existsSync13(path)) {
5818
5950
  throw new Error(`${path} already exists \u2014 delete it to re-init`);
5819
5951
  }
5820
- mkdirSync6(dirname9(path), { recursive: true });
5821
- writeFileSync9(path, `${JSON.stringify(INIT_SKELETON, null, 2)}
5952
+ mkdirSync7(dirname10(path), { recursive: true });
5953
+ writeFileSync10(path, `${JSON.stringify(INIT_SKELETON, null, 2)}
5822
5954
  `);
5823
5955
  console.log(`Created ${path}`);
5824
5956
  console.log(INIT_COMMENT);
@@ -6575,6 +6707,14 @@ session.command("handoff <ref> <targetAgent>").description("hand the session's c
6575
6707
  if (done.target_session) console.log(`${done.target_session} (${done.resolved_mode === "native_fork" ? "forked" : "from a written summary"})`);
6576
6708
  })
6577
6709
  );
6710
+ session.command("continue-here <ref>").description("move an idle Claude Code or Codex session you own onto this computer and print how to resume it; the cloud session then takes no more turns").option("--dir <folder>", "where the session's workspace goes", ".").action(
6711
+ run(async (ref, opts) => {
6712
+ const id = await resolveSessionRef(ref);
6713
+ const moved = await continueHere(id, opts.dir, { onStatus: (h) => console.error(`${h.id}: ${h.status}`) });
6714
+ if (!moved.workspaceCarried) console.error("The workspace was not copied with this move; copy what you need with sky session workspace ls and cat.");
6715
+ console.log(moved.resume);
6716
+ })
6717
+ );
6578
6718
  session.command("cloud-copy <ref>").description("what is kept in the cloud for a chat synced from your own computer: its working folder and native session").option("--json", "print the raw answer").action(
6579
6719
  run(async (ref, opts) => {
6580
6720
  const id = await resolveSessionRef(ref);
@@ -6608,7 +6748,7 @@ sessionWorkspace.command("cat <ref> <path>").description("print a file (up to 1
6608
6748
  run(async (ref, path, opts) => {
6609
6749
  const bytes = await readFile(await resolveSessionRef(ref), path);
6610
6750
  if (opts.out) {
6611
- writeFileSync10(opts.out, bytes);
6751
+ writeFileSync11(opts.out, bytes);
6612
6752
  console.error(`saved ${bytes.length} bytes to ${opts.out}`);
6613
6753
  } else process.stdout.write(bytes);
6614
6754
  })
@@ -6625,7 +6765,7 @@ sessionWorkspace.command("patch <ref> [repo]").description("the work in a reposi
6625
6765
  console.error(`${meta.repo}: ${meta.commits.length} commit${meta.commits.length === 1 ? "" : "s"} since ${meta.base_branch || "the imported copy"} (${meta.base_commit.slice(0, 12)})`);
6626
6766
  if (!patch.length) console.error("nothing changed");
6627
6767
  else if (opts.out) {
6628
- writeFileSync10(opts.out, patch);
6768
+ writeFileSync11(opts.out, patch);
6629
6769
  console.error(`wrote ${opts.out} \u2014 apply with: git apply ${opts.out}`);
6630
6770
  } else process.stdout.write(patch);
6631
6771
  })
@@ -6859,7 +6999,7 @@ async function launchFromFile(path, spec, opts) {
6859
6999
  program.command("launch").description("scaffold ./agent.toml if absent, create the agent and its first session (idle until the first message), 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)").option("--start", "boot the first session now instead of on the first message").action(
6860
7000
  run(async (opts) => {
6861
7001
  const path = resolve9(opts.file);
6862
- if (!existsSync13(path)) {
7002
+ if (!existsSync14(path)) {
6863
7003
  const name = basename9(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
6864
7004
  scaffoldAgentFile(path, name);
6865
7005
  console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
@@ -7130,14 +7270,14 @@ task.command("get <task> <path>").description("read one task file; prints it, or
7130
7270
  run(async (taskId, path, opts) => {
7131
7271
  const bytes = await readTaskFile(taskId, path);
7132
7272
  if (opts.out) {
7133
- writeFileSync10(opts.out, bytes);
7273
+ writeFileSync11(opts.out, bytes);
7134
7274
  console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
7135
7275
  } else process.stdout.write(bytes);
7136
7276
  })
7137
7277
  );
7138
7278
  task.command("put <task> <path> <local>").description("write a local file to <path> in the task's files (every session of the task sees it)").action(
7139
7279
  run(async (taskId, path, local) => {
7140
- const { file, conflict } = await writeTaskFile(taskId, path, readFileSync20(local), mediaTypeFor(local));
7280
+ const { file, conflict } = await writeTaskFile(taskId, path, readFileSync21(local), mediaTypeFor(local));
7141
7281
  console.log(`${renderTaskFile(file)}${conflict ? " (replaced a version you had not seen)" : ""}`);
7142
7282
  })
7143
7283
  );
@@ -7375,7 +7515,7 @@ library.command("list").description("your Library, newest first").option("--sear
7375
7515
  );
7376
7516
  library.command("upload <path>").description("save a file from this computer to your Library").option("--workspace <key>", "place it in this workspace").action(
7377
7517
  run(async (path, opts) => {
7378
- const bytes = readFileSync20(path);
7518
+ const bytes = readFileSync21(path);
7379
7519
  const { artifact: artifact2 } = await uploadToLibrary({ bytes, name: basename9(path), mediaType: "application/octet-stream" }, `sky-upload-${crypto.randomUUID()}`, opts.workspace);
7380
7520
  console.log(renderLibraryArtifact(artifact2));
7381
7521
  })
@@ -7406,11 +7546,12 @@ library.command("place <id> <workspace>").description('place your artifact in a
7406
7546
  library.command("download <id> <out>").description("save one version's bytes to a file").option("--version <n>", "a saved version (the current one when omitted)").action(
7407
7547
  run(async (id, out, opts) => {
7408
7548
  const { bytes } = await downloadLibraryArtifact(id, opts.version ? Number(opts.version) : void 0);
7409
- writeFileSync10(out, bytes);
7549
+ writeFileSync11(out, bytes);
7410
7550
  console.log(`wrote ${bytes.length} bytes to ${out}`);
7411
7551
  })
7412
7552
  );
7413
7553
  registerLibraryLinkCommands(library);
7554
+ registerLibrarySkillCommands(library);
7414
7555
  var todos = program.command("todos").description("to-dos: the checklist items of your Library documents (- [ ] text @who YYYY-MM-DD)");
7415
7556
  todos.command("list").description("open to-dos assigned to you, or unassigned in your documents").option("--workspace", "every to-do in documents you can read, not only yours").option("--status <status>", "open, done or all", "open").option("--due <when>", "any, overdue, today, week or none", "any").option("--source <artifactId>", "only this document's to-dos").option("--json", "print as JSON").action(
7416
7557
  run(async (opts) => {
@@ -7449,7 +7590,7 @@ artifact.command("get <id> <path>").description("read one file of an artifact; p
7449
7590
  run(async (id, path, opts) => {
7450
7591
  const bytes = await readArtifactFile(id, path);
7451
7592
  if (opts.out) {
7452
- writeFileSync10(opts.out, bytes);
7593
+ writeFileSync11(opts.out, bytes);
7453
7594
  console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
7454
7595
  } else process.stdout.write(bytes);
7455
7596
  })
@@ -7487,14 +7628,14 @@ designSystem.command("import <task> <folder>").description("import a local desig
7487
7628
  );
7488
7629
  designSystem.command("tokens <id> <file>").description("save a tokens.json file as the system's next version; --base is the version you edited").requiredOption("--base <version>", "the version the file was edited from", (value) => Number(value)).option("--note <note>", "one line saying what changed").action(
7489
7630
  run(async (id, file, opts) => {
7490
- const tokens = JSON.parse(readFileSync20(resolve9(file), "utf8"));
7631
+ const tokens = JSON.parse(readFileSync21(resolve9(file), "utf8"));
7491
7632
  console.log(renderArtifact(await saveDesignTokens(id, opts.base, tokens, opts.note)));
7492
7633
  })
7493
7634
  );
7494
7635
  designSystem.command("write <id> <path> [file]").description("save one of the system's editable files (path such as project/README.md) from a local file as its next version, or put back an earlier version's copy with --restore").requiredOption("--base <version>", "the version the file was edited from", (value) => Number(value)).option("--restore <version>", "restore the file as it was in this version instead of reading [file]", (value) => Number(value)).option("--note <note>", "one line saying what changed").action(
7495
7636
  run(async (id, path, file, opts) => {
7496
7637
  if (file === void 0 === (opts.restore === void 0)) throw Error("Give a local [file] to save, or --restore <version>.");
7497
- const change = opts.restore === void 0 ? { content: readFileSync20(resolve9(file), "utf8") } : { restore_version: opts.restore };
7638
+ const change = opts.restore === void 0 ? { content: readFileSync21(resolve9(file), "utf8") } : { restore_version: opts.restore };
7498
7639
  console.log(renderArtifact(await saveDesignSystemFile(id, path, opts.base, change, opts.note)));
7499
7640
  })
7500
7641
  );
@@ -7807,7 +7948,7 @@ usageOptions(
7807
7948
  run(async (opts) => {
7808
7949
  const { bytes } = await apiBytes("GET", `/billing/usage/export${usageQuery(opts)}`);
7809
7950
  if (opts.out) {
7810
- writeFileSync10(opts.out, bytes);
7951
+ writeFileSync11(opts.out, bytes);
7811
7952
  console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
7812
7953
  } else {
7813
7954
  process.stdout.write(bytes);
@@ -8429,7 +8570,7 @@ notes.command("read <storeId> <noteId>").description("print one note").action(
8429
8570
  notes.command("save <storeId> <file>").description("write a note from a local file: --path creates one, --note saves a new version of one").option("--path <path>", "path of a new note, e.g. /projects/launch.md").option("--note <noteId>", "the note to update").action(
8430
8571
  run(async (storeId, file, opts) => {
8431
8572
  if (!opts.path === !opts.note) throw new Error("pass exactly one of --path (new note) or --note (update)");
8432
- const content = readFileSync20(file, "utf8");
8573
+ const content = readFileSync21(file, "utf8");
8433
8574
  const target = `/memory_stores/${encodeURIComponent(storeId)}/memories${opts.note ? `/${encodeURIComponent(opts.note)}` : ""}`;
8434
8575
  const { memory: memory2 } = await api("POST", target, { content, ...opts.path ? { path: opts.path } : {} });
8435
8576
  console.log(`Saved ${memory2.path} (${memory2.id})`);
@@ -8529,7 +8670,7 @@ harness.command("connect").description("run an ACP agent (the command after --)
8529
8670
  }));
8530
8671
  var traceCmd = program.command("trace").description("correlate this machine's agent work with the cloud sessions it starts (metadata only unless you choose content)");
8531
8672
  traceCmd.command("start").description("start a trace: a grant for this machine, a root span, and the spool the hooks write to").option("--name <name>", "a name for the task").option("--source <source>", "how this machine reports itself", "claude-code").option("--hooks [settings]", "also install the Claude Code hooks (default: ./.claude/settings.local.json)").option("--capture <scopes>", "also send content, redacted on this machine first: any of prompts,tool_output,files").option("--file <path>", "a file the files scope may send (repeatable)", collect).option("--dir <path>", "a folder whose files the files scope may send when a file tool touches them (repeatable)", collect).action(run(async (opts) => {
8532
- const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-RUZF6QL6.js");
8673
+ const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-2SAGMFYJ.js");
8533
8674
  if (readState()) throw new Error("a trace is already running here \u2014 `sky trace stop` it first");
8534
8675
  const scopes = parseContentScopes(opts.capture);
8535
8676
  const chosen = chosenPaths(opts.file ?? [], opts.dir ?? []);
@@ -8564,7 +8705,7 @@ traceCmd.command("start").description("start a trace: a grant for this machine,
8564
8705
  }));
8565
8706
  traceCmd.command("hook").description("record one Claude Code hook event read from stdin (run by the installed hooks; never fails, prints nothing)").action(async () => {
8566
8707
  try {
8567
- const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-RUZF6QL6.js");
8708
+ const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-2SAGMFYJ.js");
8568
8709
  const chunks = [];
8569
8710
  for await (const chunk of process.stdin) chunks.push(chunk);
8570
8711
  const input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
@@ -8577,14 +8718,14 @@ traceCmd.command("hook").description("record one Claude Code hook event read fro
8577
8718
  }
8578
8719
  });
8579
8720
  traceCmd.command("flush").description("send what the spool holds, retrying with backoff").action(run(async () => {
8580
- const { readState, flush, grantSender } = await import("./trace-RUZF6QL6.js");
8721
+ const { readState, flush, grantSender } = await import("./trace-2SAGMFYJ.js");
8581
8722
  const state = readState();
8582
8723
  if (!state) throw new Error("no trace is running here");
8583
8724
  const outcome = await flush(grantSender(state), { attempts: 4 });
8584
8725
  console.log(`sent ${outcome.sent}, refused ${outcome.refused}, given up ${outcome.expired}, still pending ${outcome.pending}${outcome.stopped ? ` (stopped: ${outcome.stopped})` : ""}`);
8585
8726
  }));
8586
8727
  traceCmd.command("status").description("the running trace and its spool").action(run(async () => {
8587
- const { readState, spoolStatus } = await import("./trace-RUZF6QL6.js");
8728
+ const { readState, spoolStatus } = await import("./trace-2SAGMFYJ.js");
8588
8729
  const state = readState();
8589
8730
  if (!state) {
8590
8731
  console.log("No trace is running here.");
@@ -8594,7 +8735,7 @@ traceCmd.command("status").description("the running trace and its spool").action
8594
8735
  console.log(`${state.trace_id} as ${state.source}: ${spool.pending} pending, ${spool.dropped_full} dropped (spool full), ${spool.dropped_expired} given up, ${spool.refused} refused`);
8595
8736
  }));
8596
8737
  traceCmd.command("show [id]").description("the trace bundle: spans, the sessions they reach, cost counted once per session").option("--json", "print the bundle as JSON").action(run(async (id, opts) => {
8597
- const { readState } = await import("./trace-RUZF6QL6.js");
8738
+ const { readState } = await import("./trace-2SAGMFYJ.js");
8598
8739
  const ref = id ?? readState()?.trace_id;
8599
8740
  if (!ref) throw new Error("name a trace, or start one here");
8600
8741
  const bundle = await api("GET", `/traces/${ref}`);
@@ -8621,7 +8762,7 @@ traceCmd.command("show [id]").description("the trace bundle: spans, the sessions
8621
8762
  for (const gap of bundle.gaps) console.log(` gap: ${gap.span_id} ${gap.reason}`);
8622
8763
  }));
8623
8764
  traceCmd.command("stop").description("end the root span, send what is left, and revoke this machine's grant").action(run(async () => {
8624
- const { readState, record, flush, grantSender, clearState } = await import("./trace-RUZF6QL6.js");
8765
+ const { readState, record, flush, grantSender, clearState } = await import("./trace-2SAGMFYJ.js");
8625
8766
  const state = readState();
8626
8767
  if (!state) throw new Error("no trace is running here");
8627
8768
  if (state.root_span) record(() => [{ span_id: state.root_span, kind: "end", outcome: "completed" }]);
@@ -8631,7 +8772,7 @@ traceCmd.command("stop").description("end the root span, send what is left, and
8631
8772
  console.log(`Stopped ${state.trace_id}${outcome.pending ? ` (${outcome.pending} events could not be sent)` : ""}`);
8632
8773
  }));
8633
8774
  traceCmd.command("sync [payload]").description("upload this machine's agent sessions (Claude Code, Codex, Cursor, Gemini CLI and every other agent `--agents` lists) to the matching workspace's trace library, redacted here first").option("--enable", "turn sync on for this machine").option("--disable", "turn sync off; nothing more is uploaded").option("--route <match=workspace>", "send sessions whose folder path contains <match> to <workspace> (repeatable; replaces the saved rules)", collect).option("--default <workspace>", "where a session no rule matches goes").option("--keep-local", "keep sessions no rule matches on this machine (the default)").option("--session <ref>", "only this session: its id or transcript path (repeatable)", collect).option("--import <export>", "add a downloaded cloud export (claude.ai, ChatGPT, Gemini, Grok, DeepSeek \u2026: its .zip, folder or file) so sync uploads it (repeatable)", collect).option("--agents", "list the agents sync reads and which are on this machine").option("--full", "re-read every session, not only files that changed").option("--limit <n>", "at most this many changed sessions this run", (v) => Number.parseInt(v, 10)).option("--dry-run", "show what would be uploaded without uploading").option("--hook", "run from a Claude Code SessionEnd hook or Codex notify: upload that session in the background and return at once").option("--install-schedule", "also sweep every 15 minutes (macOS launchd)").option("--status", "what sync has done on this machine").option("--quiet", "print only failures").action(async (payload, opts) => {
8634
- const lib = await import("./trace-library-5Y63PZAD.js");
8775
+ const lib = await import("./trace-library-RDJEEW7O.js");
8635
8776
  if (opts.hook) {
8636
8777
  try {
8637
8778
  let raw = payload ?? "";
@@ -8691,9 +8832,9 @@ traceCmd.command("sync [payload]").description("upload this machine's agent sess
8691
8832
  }
8692
8833
  if (opts.installSchedule) {
8693
8834
  const { join: join20 } = await import("path");
8694
- const { homedir: homedir10 } = await import("os");
8835
+ const { homedir: homedir11 } = await import("os");
8695
8836
  const { execFileSync } = await import("child_process");
8696
- const log = join20(homedir10(), ".config", "sky", "trace-sync.log");
8837
+ const log = join20(homedir11(), ".config", "sky", "trace-sync.log");
8697
8838
  const plist = lib.installLaunchd([process.execPath, process.argv[1], "trace", "sync", "--quiet"], log);
8698
8839
  try {
8699
8840
  execFileSync("launchctl", ["unload", plist], { stdio: "ignore" });
@@ -8703,11 +8844,11 @@ traceCmd.command("sync [payload]").description("upload this machine's agent sess
8703
8844
  console.log(`Sweeping every 15 minutes (${plist}); a full re-read runs weekly. Log: ${log}`);
8704
8845
  }
8705
8846
  if (opts.status) {
8706
- const { readFileSync: readFileSync21 } = await import("fs");
8847
+ const { readFileSync: readFileSync22 } = await import("fs");
8707
8848
  const { join: join20 } = await import("path");
8708
8849
  let state = { sessions: {} };
8709
8850
  try {
8710
- state = JSON.parse(readFileSync21(join20(lib.syncConfigPath(), "..", "trace-sync-state.json"), "utf8"));
8851
+ state = JSON.parse(readFileSync22(join20(lib.syncConfigPath(), "..", "trace-sync-state.json"), "utf8"));
8711
8852
  } catch {
8712
8853
  }
8713
8854
  const counts = /* @__PURE__ */ new Map();
@@ -8746,7 +8887,7 @@ traceCmd.command("search <query...>").description("search every session in a wor
8746
8887
  console.log(opts.json ? JSON.stringify(page, null, 2) : renderContextPage(page));
8747
8888
  }));
8748
8889
  traceCmd.command("library").description("list the sessions in a workspace's trace library, most recently updated first").option("--workspace <slug>", "the workspace (default: the current one)").option("--limit <n>", "how many", (v) => Number.parseInt(v, 10), 20).option("--json", "print the page as JSON").action(run(async (opts) => {
8749
- const { libraryRequest } = await import("./trace-library-5Y63PZAD.js");
8890
+ const { libraryRequest } = await import("./trace-library-RDJEEW7O.js");
8750
8891
  const workspace2 = opts.workspace ?? currentUniverse();
8751
8892
  if (!workspace2) throw new Error("name a workspace with --workspace");
8752
8893
  const page = await libraryRequest("GET", `/trace-library?limit=${opts.limit}`, workspace2);
@@ -8850,6 +8991,7 @@ compare.command("pick <id>").description('record the winning column \u2014 or a
8850
8991
  })
8851
8992
  );
8852
8993
  var thread = program.command("thread").description("workspace threads: the conversation history across all agents");
8994
+ registerExperienceThreadCommand(thread);
8853
8995
  thread.command("list").description("your workspace threads, most recently updated first (includes comparisons)").option("--archived", "list archived threads only").option("--all", "include both active and archived").option("--json", "print as JSON").action(
8854
8996
  run(async (opts) => {
8855
8997
  const filter = opts.all ? "all" : opts.archived ? "archived" : "active";