@agentsky/sky 2.30.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-TIFRDEFP.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-KNGZR3CX.js";
25
+ } from "./chunk-3SWL7YZD.js";
26
26
  import {
27
27
  AGENT_TYPES,
28
28
  MODEL_SUBSCRIPTION_PLAN_PHRASE,
@@ -44,9 +44,109 @@ import {
44
44
  skillRefSchema,
45
45
  splitMcpServers,
46
46
  supersededBy
47
- } from "./chunk-JOLDBZV3.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
151
  import { existsSync as existsSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "fs";
52
152
  import { basename as basename9, resolve as resolve9 } from "path";
@@ -54,22 +154,22 @@ 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,10 +3539,10 @@ 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 { existsSync as existsSync10, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
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";
3444
3544
  import { homedir as homedir8, hostname as hostname2, tmpdir as tmpdir2 } from "os";
3445
- import { basename as basename7, dirname as dirname6, join as join12, relative as relative3, resolve as resolve5 } from "path";
3545
+ import { basename as basename8, dirname as dirname6, join as join13, relative as relative4, resolve as resolve6 } from "path";
3446
3546
  import { inflateRawSync } from "zlib";
3447
3547
  var SINGLE_IMPORT_MAX = 25 * 1024 * 1024;
3448
3548
  var PART_SIZE = 8 * 1024 * 1024;
@@ -3477,15 +3577,15 @@ function folderFiles(dir) {
3477
3577
  const git2 = spawnSync4("git", ["-C", dir, "ls-files", "-z", "--cached", "--others", "--exclude-standard"], { encoding: "utf8" });
3478
3578
  const top = spawnSync4("git", ["-C", dir, "rev-parse", "--show-prefix"], { encoding: "utf8" });
3479
3579
  if (git2.status === 0 && top.status === 0) {
3480
- 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());
3481
3581
  }
3482
3582
  const files2 = [];
3483
3583
  const walk = (at) => {
3484
- for (const entry2 of readdirSync5(at, { withFileTypes: true })) {
3584
+ for (const entry2 of readdirSync6(at, { withFileTypes: true })) {
3485
3585
  if (SKIPPED2.has(entry2.name)) continue;
3486
- const full = join12(at, entry2.name);
3586
+ const full = join13(at, entry2.name);
3487
3587
  if (entry2.isDirectory()) walk(full);
3488
- else if (entry2.isFile()) files2.push(relative3(dir, full));
3588
+ else if (entry2.isFile()) files2.push(relative4(dir, full));
3489
3589
  }
3490
3590
  };
3491
3591
  walk(dir);
@@ -3501,8 +3601,8 @@ function folderArchive(dir, files2) {
3501
3601
  return tar.stdout;
3502
3602
  }
3503
3603
  async function importFolder(id, dir, opts = {}) {
3504
- const root = resolve5(dir);
3505
- const name = opts.name ?? basename7(root);
3604
+ const root = resolve6(dir);
3605
+ const name = opts.name ?? basename8(root);
3506
3606
  const archive = opts.archive ?? folderArchive(root, folderFiles(root));
3507
3607
  if (archive.length <= SINGLE_IMPORT_MAX) {
3508
3608
  const res = await apiBytes("POST", `${workspacePath(id)}/import?name=${encodeURIComponent(name)}`, { bytes: archive, mediaType: "application/gzip" });
@@ -3553,7 +3653,7 @@ async function handoff(id, targetAgent, opts = {}) {
3553
3653
  let { handoff: h } = await api("POST", `/sessions/${encodeURIComponent(id)}/handoff`, {
3554
3654
  target_agent: targetAgent,
3555
3655
  ...opts.fromMessage ? { last_message_id: opts.fromMessage } : {}
3556
- }, { idempotencyKey: opts.idempotencyKey ?? randomUUID() });
3656
+ }, { idempotencyKey: opts.idempotencyKey ?? randomUUID2() });
3557
3657
  opts.onStatus?.(h);
3558
3658
  while (opts.wait && !TERMINAL.has(h.status)) {
3559
3659
  await new Promise((r) => setTimeout(r, opts.pollMs ?? 2e3));
@@ -3620,7 +3720,7 @@ async function continueHere(id, dir, opts = {}) {
3620
3720
  const session2 = (await api("GET", `/sessions/${encodeURIComponent(id)}`)).session;
3621
3721
  const harness2 = session2.agentType;
3622
3722
  if (harness2 !== "claude_code" && harness2 !== "codex") throw new ApiError(409, "harness_unsupported", "only Claude Code and Codex sessions can continue on a computer");
3623
- let { handoff: h } = await api("POST", `/sessions/${encodeURIComponent(id)}/local-handoff`, { machine: hostname2().slice(0, 120) }, { idempotencyKey: opts.idempotencyKey ?? randomUUID() });
3723
+ let { handoff: h } = await api("POST", `/sessions/${encodeURIComponent(id)}/local-handoff`, { machine: hostname2().slice(0, 120) }, { idempotencyKey: opts.idempotencyKey ?? randomUUID2() });
3624
3724
  opts.onStatus?.(h);
3625
3725
  while (!TERMINAL.has(h.status)) {
3626
3726
  await new Promise((r) => setTimeout(r, opts.pollMs ?? 2e3));
@@ -3630,23 +3730,23 @@ async function continueHere(id, dir, opts = {}) {
3630
3730
  }
3631
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");
3632
3732
  const { nativeId, files: files2 } = conversationFiles(await verifiedDownload(`/session-handoffs/${encodeURIComponent(h.id)}/checkpoint`), harness2);
3633
- const root = harness2 === "codex" ? process.env.CODEX_HOME || join12(homedir8(), ".codex") : process.env.CLAUDE_CONFIG_DIR || join12(homedir8(), ".claude");
3733
+ const root = harness2 === "codex" ? process.env.CODEX_HOME || join13(homedir8(), ".codex") : process.env.CLAUDE_CONFIG_DIR || join13(homedir8(), ".claude");
3634
3734
  const stamp = Date.now();
3635
3735
  for (const file of files2) {
3636
- const target = join12(root, file.path);
3736
+ const target = join13(root, file.path);
3637
3737
  mkdirSync4(dirname6(target), { recursive: true });
3638
3738
  if (existsSync10(target)) {
3639
3739
  if (!lstatSync2(target).isFile()) throw new ApiError(409, "restore_blocked", `something other than a file is in the way at ${target}`);
3640
- if (readFileSync12(target).equals(file.data)) continue;
3740
+ if (readFileSync13(target).equals(file.data)) continue;
3641
3741
  renameSync(target, `${target}.before-agentsky-${stamp}`);
3642
3742
  }
3643
3743
  writeFileSync7(target, file.data, { mode: 384 });
3644
3744
  }
3645
- const folder = resolve5(dir);
3745
+ const folder = resolve6(dir);
3646
3746
  mkdirSync4(folder, { recursive: true });
3647
3747
  const carried = h.workspace_carried === true;
3648
3748
  if (carried) {
3649
- const archive = join12(tmpdir2(), `agentsky-workspace-${h.id}.tar.gz`);
3749
+ const archive = join13(tmpdir2(), `agentsky-workspace-${h.id}.tar.gz`);
3650
3750
  writeFileSync7(archive, await verifiedDownload(`/session-handoffs/${encodeURIComponent(h.id)}/workspace`), { mode: 384 });
3651
3751
  const tar = spawnSync4("tar", ["-xzf", archive, "-C", folder]);
3652
3752
  if (tar.status !== 0) throw new ApiError(422, "workspace_unpack_failed", `tar failed: ${tar.stderr.toString().trim()}`);
@@ -3775,15 +3875,15 @@ async function readTurn(sessionId, opts = {}) {
3775
3875
  }
3776
3876
 
3777
3877
  // src/setup.ts
3778
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
3779
- import { dirname as dirname8, 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";
3780
3880
  import { createInterface as createInterface3 } from "readline";
3781
3881
 
3782
3882
  // src/setup-local.ts
3783
3883
  import { isUtf8 as isUtf83 } from "buffer";
3784
- import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync13, statSync as statSync7 } from "fs";
3884
+ import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync8 } from "fs";
3785
3885
  import { homedir as homedir9 } from "os";
3786
- import { dirname as dirname7, isAbsolute as isAbsolute2, join as join13, resolve as resolve6 } from "path";
3886
+ import { dirname as dirname7, isAbsolute as isAbsolute2, join as join14, resolve as resolve7 } from "path";
3787
3887
  import { parse as parseToml4 } from "smol-toml";
3788
3888
  var MAX_SKILL_FILE_BYTES = 1024 * 1024;
3789
3889
  var MAX_IMPORT_DEPTH = 5;
@@ -3807,12 +3907,12 @@ function redacted(data) {
3807
3907
  }
3808
3908
  function skillFiles2(root, prefix) {
3809
3909
  const files2 = [];
3810
- for (const entry2 of readdirSync6(root, { withFileTypes: true })) {
3811
- const path = join13(root, entry2.name);
3910
+ for (const entry2 of readdirSync7(root, { withFileTypes: true })) {
3911
+ const path = join14(root, entry2.name);
3812
3912
  if (entry2.isDirectory()) {
3813
3913
  if (!SKIPPED_DIRS.has(entry2.name)) files2.push(...skillFiles2(path, `${prefix}/${entry2.name}`));
3814
- } else if (entry2.isFile() && !sensitive(entry2.name) && statSync7(path).size <= MAX_SKILL_FILE_BYTES) {
3815
- const bytes = redacted(readFileSync13(path));
3914
+ } else if (entry2.isFile() && !sensitive(entry2.name) && statSync8(path).size <= MAX_SKILL_FILE_BYTES) {
3915
+ const bytes = redacted(readFileSync14(path));
3816
3916
  if (bytes) files2.push({ path: `${prefix}/${entry2.name}`, bytes });
3817
3917
  }
3818
3918
  }
@@ -3823,18 +3923,18 @@ function readLocalSkills(agents) {
3823
3923
  const ignored = [];
3824
3924
  for (const agent2 of agents) {
3825
3925
  if (!existsSync11(agent2.skillsDir)) continue;
3826
- for (const entry2 of readdirSync6(agent2.skillsDir, { withFileTypes: true })) {
3827
- const dir = join13(agent2.skillsDir, entry2.name);
3926
+ for (const entry2 of readdirSync7(agent2.skillsDir, { withFileTypes: true })) {
3927
+ const dir = join14(agent2.skillsDir, entry2.name);
3828
3928
  let isDir2 = entry2.isDirectory();
3829
3929
  if (entry2.isSymbolicLink()) {
3830
3930
  try {
3831
- isDir2 = statSync7(dir).isDirectory();
3931
+ isDir2 = statSync8(dir).isDirectory();
3832
3932
  } catch {
3833
3933
  isDir2 = false;
3834
3934
  }
3835
3935
  }
3836
- if (!isDir2 || !existsSync11(join13(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
3837
- const name = frontmatterName(readFileSync13(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"));
3838
3938
  if (!name) {
3839
3939
  ignored.push({ agent: agent2.id, dir, reason: "SKILL.md has no name" });
3840
3940
  continue;
@@ -3862,11 +3962,11 @@ function inlineImports(content, from, home, depth, seen) {
3862
3962
  }
3863
3963
  const target = fence ? void 0 : line.match(/^@(\S+\.(?:md|markdown|txt))$/i)?.[1];
3864
3964
  if (!target || depth >= MAX_IMPORT_DEPTH) return line;
3865
- const path = target.startsWith("~/") ? join13(home, target.slice(2)) : isAbsolute2(target) ? target : resolve6(dirname7(from), target);
3965
+ const path = target.startsWith("~/") ? join14(home, target.slice(2)) : isAbsolute2(target) ? target : resolve7(dirname7(from), target);
3866
3966
  if (seen.includes(path)) return "";
3867
3967
  let imported;
3868
3968
  try {
3869
- imported = readFileSync13(path, "utf8");
3969
+ imported = readFileSync14(path, "utf8");
3870
3970
  } catch {
3871
3971
  return line;
3872
3972
  }
@@ -3879,10 +3979,10 @@ function readGlobalInstructions(agents, home = homedir9()) {
3879
3979
  for (const agent2 of agents) {
3880
3980
  const file = INSTRUCTION_FILES2[agent2.id];
3881
3981
  if (!file) continue;
3882
- const path = join13(agent2.root, file);
3982
+ const path = join14(agent2.root, file);
3883
3983
  let raw;
3884
3984
  try {
3885
- raw = readFileSync13(path, "utf8");
3985
+ raw = readFileSync14(path, "utf8");
3886
3986
  } catch {
3887
3987
  continue;
3888
3988
  }
@@ -3901,7 +4001,7 @@ ${item.content.trim()}`).join("\n\n");
3901
4001
  }
3902
4002
  function readJson3(path) {
3903
4003
  try {
3904
- return JSON.parse(readFileSync13(path, "utf8"));
4004
+ return JSON.parse(readFileSync14(path, "utf8"));
3905
4005
  } catch {
3906
4006
  return void 0;
3907
4007
  }
@@ -3915,10 +4015,10 @@ function bearerOf(def) {
3915
4015
  return match?.[1] ?? null;
3916
4016
  }
3917
4017
  function serversOf(agent2, home) {
3918
- if (agent2.id === "claude_code") return readJson3(join13(home, ".claude.json"))?.mcpServers ?? {};
3919
- 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 ?? {};
3920
4020
  try {
3921
- return parseToml4(readFileSync13(join13(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
4021
+ return parseToml4(readFileSync14(join14(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
3922
4022
  } catch {
3923
4023
  return {};
3924
4024
  }
@@ -3940,9 +4040,9 @@ function findLocalSecrets(agents, home = homedir9()) {
3940
4040
 
3941
4041
  // src/subscriptions.ts
3942
4042
  import { spawn as spawn3 } from "child_process";
3943
- import { existsSync as existsSync12, readFileSync as readFileSync14 } from "fs";
4043
+ import { existsSync as existsSync12, readFileSync as readFileSync15 } from "fs";
3944
4044
  import { homedir as homedir10 } from "os";
3945
- import { join as join14 } from "path";
4045
+ import { join as join15 } from "path";
3946
4046
  import { createInterface as createInterface2 } from "readline";
3947
4047
  var ALIASES = {
3948
4048
  claude: "anthropic",
@@ -4038,11 +4138,11 @@ async function connectClaude() {
4038
4138
  console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
4039
4139
  }
4040
4140
  function codexSignIn(env = process.env, home = homedir10()) {
4041
- const path = join14(env.CODEX_HOME || join14(home, ".codex"), "auth.json");
4141
+ const path = join15(env.CODEX_HOME || join15(home, ".codex"), "auth.json");
4042
4142
  if (!existsSync12(path)) {
4043
4143
  throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
4044
4144
  }
4045
- const raw = readFileSync14(path, "utf8");
4145
+ const raw = readFileSync15(path, "utf8");
4046
4146
  try {
4047
4147
  const parsed = JSON.parse(raw);
4048
4148
  return { path, raw, account: parsed.tokens?.account_id };
@@ -4051,11 +4151,11 @@ function codexSignIn(env = process.env, home = homedir10()) {
4051
4151
  }
4052
4152
  }
4053
4153
  function cursorSignIn(env = process.env, home = homedir10()) {
4054
- const path = join14(env.XDG_CONFIG_HOME || join14(home, ".config"), "cursor", "auth.json");
4154
+ const path = join15(env.XDG_CONFIG_HOME || join15(home, ".config"), "cursor", "auth.json");
4055
4155
  if (!existsSync12(path)) {
4056
4156
  throw new ApiError(404, "no_cursor_auth", `no ${path} \u2014 run \`cursor-agent login\` first`);
4057
4157
  }
4058
- const raw = readFileSync14(path, "utf8");
4158
+ const raw = readFileSync15(path, "utf8");
4059
4159
  try {
4060
4160
  const parsed = JSON.parse(raw);
4061
4161
  if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string") throw new Error("shape");
@@ -4216,11 +4316,11 @@ function planProviders(names2) {
4216
4316
  }));
4217
4317
  }
4218
4318
  function reportPath() {
4219
- return join15(dirname8(configPath()), "setup.json");
4319
+ return join16(dirname8(configPath()), "setup.json");
4220
4320
  }
4221
4321
  function loadReport() {
4222
4322
  try {
4223
- const saved = JSON.parse(readFileSync15(reportPath(), "utf8"));
4323
+ const saved = JSON.parse(readFileSync16(reportPath(), "utf8"));
4224
4324
  return { agents: saved.agents ?? [], plans: saved.plans ?? [], ...saved.secrets ? { secrets: saved.secrets } : {} };
4225
4325
  } catch {
4226
4326
  return { agents: [], plans: [] };
@@ -4604,67 +4704,6 @@ function renderArtifact(artifact2) {
4604
4704
  return `${artifact2.id} ${artifact2.name} v${artifact2.version} ${files2} entry ${artifact2.entry}${artifact2.share_url ? ` shared ${artifact2.share_url}` : ""}`;
4605
4705
  }
4606
4706
 
4607
- // src/skills-store.ts
4608
- import { readdirSync as readdirSync7, readFileSync as readFileSync16, statSync as statSync8 } from "fs";
4609
- import { basename as basename8, join as join16, relative as relative4, resolve as resolve7 } from "path";
4610
- function skillBundle(dir) {
4611
- const root = resolve7(dir);
4612
- const top = basename8(root);
4613
- const files2 = [];
4614
- const walk = (path) => {
4615
- for (const name of readdirSync7(path).sort()) {
4616
- const full = join16(path, name);
4617
- if (statSync8(full).isDirectory()) walk(full);
4618
- else files2.push({ path: `${top}/${relative4(root, full)}`, bytes: readFileSync16(full) });
4619
- }
4620
- };
4621
- walk(root);
4622
- if (!files2.some((file) => file.path === `${top}/SKILL.md`)) throw new Error(`${dir}: no SKILL.md at its root`);
4623
- return files2;
4624
- }
4625
- async function uploadSkill(dir, displayName) {
4626
- const form = new FormData();
4627
- for (const file of skillBundle(dir)) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
4628
- form.append("displayName", displayName ?? basename8(resolve7(dir)));
4629
- return apiForm("POST", "/skills", form);
4630
- }
4631
- async function listSkills() {
4632
- return (await api("GET", "/skills")).skills;
4633
- }
4634
- async function getTask(task2) {
4635
- return (await api("GET", `/tasks/${encodeURIComponent(task2)}`)).task;
4636
- }
4637
- function answerBody(answer) {
4638
- if (!answer.option === !answer.text) throw new Error("give exactly one of --option or --text");
4639
- return { ...answer.option ? { option_id: answer.option } : { text: answer.text }, ...answer.note ? { note: answer.note } : {} };
4640
- }
4641
- async function answerRequest(task2, request, answer) {
4642
- return api("POST", `/tasks/${encodeURIComponent(task2)}/requests/${encodeURIComponent(request)}/answer`, answerBody(answer));
4643
- }
4644
- function renderRequest(request) {
4645
- const options = request.options.length ? `
4646
- options: ${request.options.map((option) => `${option.id} (${option.label})`).join(", ")}` : "";
4647
- return `${request.id} ${request.kind} from ${request.session_id}
4648
- ${request.question}${options}`;
4649
- }
4650
- async function getSkillUsage(since) {
4651
- const qs = since ? `?since=${encodeURIComponent(since)}` : "";
4652
- return api("GET", `/skills/usage${qs}`);
4653
- }
4654
- function nextSkills(current, skillId, verb) {
4655
- const has = current.some((ref) => ref.type === "skill" && ref.skillId === skillId);
4656
- if (verb === "attach") return has ? null : [...current, { type: "skill", skillId, version: "latest" }];
4657
- return has ? current.filter((ref) => !(ref.type === "skill" && ref.skillId === skillId)) : null;
4658
- }
4659
- async function setAgentSkill(agentId, skillId, verb) {
4660
- const path = `/agents/${encodeURIComponent(agentId)}`;
4661
- const { agent: agent2 } = await api("GET", path);
4662
- const skills2 = nextSkills(agent2.skills ?? [], skillId, verb);
4663
- if (!skills2) return "unchanged";
4664
- await api("PATCH", path, { skills: skills2, expectedVersion: agent2.version });
4665
- return "changed";
4666
- }
4667
-
4668
4707
  // src/task-export.ts
4669
4708
  import { createHash as createHash6 } from "crypto";
4670
4709
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync9 } from "fs";
@@ -4974,7 +5013,7 @@ function parseRedactNames(values) {
4974
5013
  }
4975
5014
 
4976
5015
  // src/task-record.ts
4977
- import { randomUUID as randomUUID2 } from "crypto";
5016
+ import { randomUUID as randomUUID3 } from "crypto";
4978
5017
  var DECISION_KINDS = ["adopt", "rerun", "revise", "discard", "vote", "did_the_job", "approve"];
4979
5018
  async function listThreads(taskRef) {
4980
5019
  return (await api("GET", `/sessions/${encodeURIComponent(taskRef)}/threads`)).threads;
@@ -4987,7 +5026,7 @@ function renderThread(thread2) {
4987
5026
  async function recordDecision(sessionRef, kind, opts = {}) {
4988
5027
  if (!DECISION_KINDS.includes(kind)) throw new Error(`kind must be one of ${DECISION_KINDS.join(", ")}`);
4989
5028
  if ((kind === "did_the_job" || kind === "approve") && typeof opts.value !== "boolean") throw new Error(`${kind} needs --yes or --no`);
4990
- const key = opts.idempotencyKey ?? randomUUID2();
5029
+ const key = opts.idempotencyKey ?? randomUUID3();
4991
5030
  const res = await fetchRecord(sessionRef, {
4992
5031
  type: "human.decision",
4993
5032
  payload: {
@@ -5003,7 +5042,7 @@ async function recordDecision(sessionRef, kind, opts = {}) {
5003
5042
  var OUTCOME_KINDS = ["pr_merged", "deployed", "reopened", "later_failure", "shipped"];
5004
5043
  async function recordOutcome(sessionRef, kind, opts = {}) {
5005
5044
  if (!OUTCOME_KINDS.includes(kind)) throw new Error(`kind must be one of ${OUTCOME_KINDS.join(", ")}`);
5006
- 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());
5007
5046
  }
5008
5047
  async function fetchRecord(sessionRef, body, idempotencyKey) {
5009
5048
  const result = await api("POST", `/sessions/${encodeURIComponent(sessionRef)}/record`, body, { idempotencyKey });
@@ -7512,6 +7551,7 @@ library.command("download <id> <out>").description("save one version's bytes to
7512
7551
  })
7513
7552
  );
7514
7553
  registerLibraryLinkCommands(library);
7554
+ registerLibrarySkillCommands(library);
7515
7555
  var todos = program.command("todos").description("to-dos: the checklist items of your Library documents (- [ ] text @who YYYY-MM-DD)");
7516
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(
7517
7557
  run(async (opts) => {
@@ -8630,7 +8670,7 @@ harness.command("connect").description("run an ACP agent (the command after --)
8630
8670
  }));
8631
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)");
8632
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) => {
8633
- const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-5SW4YMCC.js");
8673
+ const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-2SAGMFYJ.js");
8634
8674
  if (readState()) throw new Error("a trace is already running here \u2014 `sky trace stop` it first");
8635
8675
  const scopes = parseContentScopes(opts.capture);
8636
8676
  const chosen = chosenPaths(opts.file ?? [], opts.dir ?? []);
@@ -8665,7 +8705,7 @@ traceCmd.command("start").description("start a trace: a grant for this machine,
8665
8705
  }));
8666
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 () => {
8667
8707
  try {
8668
- const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-5SW4YMCC.js");
8708
+ const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-2SAGMFYJ.js");
8669
8709
  const chunks = [];
8670
8710
  for await (const chunk of process.stdin) chunks.push(chunk);
8671
8711
  const input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
@@ -8678,14 +8718,14 @@ traceCmd.command("hook").description("record one Claude Code hook event read fro
8678
8718
  }
8679
8719
  });
8680
8720
  traceCmd.command("flush").description("send what the spool holds, retrying with backoff").action(run(async () => {
8681
- const { readState, flush, grantSender } = await import("./trace-5SW4YMCC.js");
8721
+ const { readState, flush, grantSender } = await import("./trace-2SAGMFYJ.js");
8682
8722
  const state = readState();
8683
8723
  if (!state) throw new Error("no trace is running here");
8684
8724
  const outcome = await flush(grantSender(state), { attempts: 4 });
8685
8725
  console.log(`sent ${outcome.sent}, refused ${outcome.refused}, given up ${outcome.expired}, still pending ${outcome.pending}${outcome.stopped ? ` (stopped: ${outcome.stopped})` : ""}`);
8686
8726
  }));
8687
8727
  traceCmd.command("status").description("the running trace and its spool").action(run(async () => {
8688
- const { readState, spoolStatus } = await import("./trace-5SW4YMCC.js");
8728
+ const { readState, spoolStatus } = await import("./trace-2SAGMFYJ.js");
8689
8729
  const state = readState();
8690
8730
  if (!state) {
8691
8731
  console.log("No trace is running here.");
@@ -8695,7 +8735,7 @@ traceCmd.command("status").description("the running trace and its spool").action
8695
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`);
8696
8736
  }));
8697
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) => {
8698
- const { readState } = await import("./trace-5SW4YMCC.js");
8738
+ const { readState } = await import("./trace-2SAGMFYJ.js");
8699
8739
  const ref = id ?? readState()?.trace_id;
8700
8740
  if (!ref) throw new Error("name a trace, or start one here");
8701
8741
  const bundle = await api("GET", `/traces/${ref}`);
@@ -8722,7 +8762,7 @@ traceCmd.command("show [id]").description("the trace bundle: spans, the sessions
8722
8762
  for (const gap of bundle.gaps) console.log(` gap: ${gap.span_id} ${gap.reason}`);
8723
8763
  }));
8724
8764
  traceCmd.command("stop").description("end the root span, send what is left, and revoke this machine's grant").action(run(async () => {
8725
- const { readState, record, flush, grantSender, clearState } = await import("./trace-5SW4YMCC.js");
8765
+ const { readState, record, flush, grantSender, clearState } = await import("./trace-2SAGMFYJ.js");
8726
8766
  const state = readState();
8727
8767
  if (!state) throw new Error("no trace is running here");
8728
8768
  if (state.root_span) record(() => [{ span_id: state.root_span, kind: "end", outcome: "completed" }]);
@@ -8732,7 +8772,7 @@ traceCmd.command("stop").description("end the root span, send what is left, and
8732
8772
  console.log(`Stopped ${state.trace_id}${outcome.pending ? ` (${outcome.pending} events could not be sent)` : ""}`);
8733
8773
  }));
8734
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) => {
8735
- const lib = await import("./trace-library-RKZNJ5O5.js");
8775
+ const lib = await import("./trace-library-RDJEEW7O.js");
8736
8776
  if (opts.hook) {
8737
8777
  try {
8738
8778
  let raw = payload ?? "";
@@ -8847,7 +8887,7 @@ traceCmd.command("search <query...>").description("search every session in a wor
8847
8887
  console.log(opts.json ? JSON.stringify(page, null, 2) : renderContextPage(page));
8848
8888
  }));
8849
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) => {
8850
- const { libraryRequest } = await import("./trace-library-RKZNJ5O5.js");
8890
+ const { libraryRequest } = await import("./trace-library-RDJEEW7O.js");
8851
8891
  const workspace2 = opts.workspace ?? currentUniverse();
8852
8892
  if (!workspace2) throw new Error("name a workspace with --workspace");
8853
8893
  const page = await libraryRequest("GET", `/trace-library?limit=${opts.limit}`, workspace2);
@@ -8951,6 +8991,7 @@ compare.command("pick <id>").description('record the winning column \u2014 or a
8951
8991
  })
8952
8992
  );
8953
8993
  var thread = program.command("thread").description("workspace threads: the conversation history across all agents");
8994
+ registerExperienceThreadCommand(thread);
8954
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(
8955
8996
  run(async (opts) => {
8956
8997
  const filter = opts.all ? "all" : opts.archived ? "archived" : "active";