@agentskit/cli 0.13.37 → 0.13.40

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.cjs CHANGED
@@ -21,6 +21,7 @@ var url = require('url');
21
21
  var runtime = require('@agentskit/runtime');
22
22
  var prompts = require('@inquirer/prompts');
23
23
  var kleur = require('kleur');
24
+ var module$1 = require('module');
24
25
  var chokidar = require('chokidar');
25
26
  var rag = require('@agentskit/rag');
26
27
  var agentSchema = require('@agentskit/core/agent-schema');
@@ -28,6 +29,7 @@ var yaml = require('yaml');
28
29
  var security = require('@agentskit/core/security');
29
30
  var core = require('@agentskit/core');
30
31
 
32
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
31
33
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
32
34
 
33
35
  var React2__default = /*#__PURE__*/_interopDefault(React2);
@@ -35,20 +37,30 @@ var path__default = /*#__PURE__*/_interopDefault(path);
35
37
  var kleur__default = /*#__PURE__*/_interopDefault(kleur);
36
38
  var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
37
39
 
38
- async function loadJsonConfig(path5) {
40
+ // package.json
41
+ var package_default = {
42
+ version: "0.13.40"};
43
+ async function loadJsonConfig(path6) {
39
44
  try {
40
- const raw = await promises.readFile(path5, "utf8");
45
+ const raw = await promises.readFile(path6, "utf8");
41
46
  return JSON.parse(raw);
42
- } catch {
43
- return void 0;
47
+ } catch (err) {
48
+ if (err.code === "ENOENT") return void 0;
49
+ throw new Error(`Failed to load JSON config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
44
50
  }
45
51
  }
46
- async function loadTsConfig(path5) {
52
+ async function loadTsConfig(path6) {
53
+ try {
54
+ await promises.access(path6);
55
+ } catch (err) {
56
+ if (err.code === "ENOENT") return void 0;
57
+ throw err;
58
+ }
47
59
  try {
48
- const mod = await import(path5);
60
+ const mod = await import(`${path6}?agentskit_config=${Date.now()}`);
49
61
  return mod.default ?? mod;
50
- } catch {
51
- return void 0;
62
+ } catch (err) {
63
+ throw new Error(`Failed to load TypeScript config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
52
64
  }
53
65
  }
54
66
  async function loadPackageJsonConfig(dir) {
@@ -59,29 +71,31 @@ async function loadPackageJsonConfig(dir) {
59
71
  return pkg.agentskit;
60
72
  }
61
73
  return void 0;
62
- } catch {
63
- return void 0;
74
+ } catch (err) {
75
+ if (err.code === "ENOENT") return void 0;
76
+ throw new Error(`Failed to load package.json config ${dir}: ${err instanceof Error ? err.message : String(err)}`);
64
77
  }
65
78
  }
66
79
  function mergeConfigs(base, override) {
67
80
  if (!base && !override) return void 0;
68
81
  if (!base) return override;
69
82
  if (!override) return base;
70
- return {
71
- ...base,
72
- ...override,
73
- tools: { ...base.tools, ...override.tools },
74
- defaults: { ...base.defaults, ...override.defaults },
75
- runtime: { ...base.runtime, ...override.runtime },
76
- observability: { ...base.observability, ...override.observability }
83
+ const merge = (left, right) => {
84
+ if (!left || typeof left !== "object" || Array.isArray(left)) return right;
85
+ if (!right || typeof right !== "object" || Array.isArray(right)) return right;
86
+ const out = { ...left };
87
+ for (const [key, value] of Object.entries(right)) {
88
+ out[key] = key in out ? merge(out[key], value) : value;
89
+ }
90
+ return out;
77
91
  };
92
+ return merge(base, override);
78
93
  }
79
94
  async function loadLocalConfig(cwd) {
80
95
  const tsConfig = await loadTsConfig(path.join(cwd, ".agentskit.config.ts"));
81
- if (tsConfig) return tsConfig;
82
96
  const jsonConfig = await loadJsonConfig(path.join(cwd, ".agentskit.config.json"));
83
- if (jsonConfig) return jsonConfig;
84
- return await loadPackageJsonConfig(cwd);
97
+ const packageConfig = await loadPackageJsonConfig(cwd);
98
+ return mergeConfigs(mergeConfigs(packageConfig, jsonConfig), tsConfig);
85
99
  }
86
100
  async function loadGlobalConfig(home) {
87
101
  if (home === null) return void 0;
@@ -96,6 +110,21 @@ async function loadConfig(options) {
96
110
  const local = await loadLocalConfig(cwd);
97
111
  return mergeConfigs(global, local);
98
112
  }
113
+ var SECRET_KEY = /^(api[-_]?key|token|secret|password|authorization|private[-_]?key)$/i;
114
+ var SECRET_ENV_KEY = /(?:^|_)(?:api_?key|token|secret|password|authorization|private_?key)$/i;
115
+ function isSecretKey(key) {
116
+ return SECRET_KEY.test(key) || SECRET_ENV_KEY.test(key);
117
+ }
118
+ function redactConfig(value) {
119
+ if (Array.isArray(value)) return value.map(redactConfig);
120
+ if (!value || typeof value !== "object") return value;
121
+ return Object.fromEntries(
122
+ Object.entries(value).map(([key, child]) => [
123
+ key,
124
+ isSecretKey(key) ? "[REDACTED]" : redactConfig(child)
125
+ ])
126
+ );
127
+ }
99
128
  var providers = {
100
129
  openai: {
101
130
  label: "OpenAI",
@@ -167,7 +196,7 @@ function createDemoAdapter(provider, model) {
167
196
  ].join(" ");
168
197
  for (const chunk of reply.match(/.{1,18}/g) ?? []) {
169
198
  if (cancelled) return;
170
- await new Promise((resolve7) => setTimeout(resolve7, 35));
199
+ await new Promise((resolve8) => setTimeout(resolve8, 35));
171
200
  yield { type: "text", content: chunk };
172
201
  }
173
202
  yield { type: "done" };
@@ -227,7 +256,8 @@ function dirFor(cwd = process.cwd()) {
227
256
  return path.join(ROOT, cwdHash(cwd));
228
257
  }
229
258
  function ensureDir(dir) {
230
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
259
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 448 });
260
+ fs.chmodSync(dir, 448);
231
261
  }
232
262
  function generateSessionId() {
233
263
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
@@ -242,17 +272,19 @@ function metaPath(id, cwd = process.cwd()) {
242
272
  return path.join(dirFor(cwd), `${id}${META_SUFFIX}`);
243
273
  }
244
274
  function readMeta(id, cwd = process.cwd()) {
245
- const path5 = metaPath(id, cwd);
246
- if (!fs.existsSync(path5)) return null;
275
+ const path6 = metaPath(id, cwd);
276
+ if (!fs.existsSync(path6)) return null;
247
277
  try {
248
- return JSON.parse(fs.readFileSync(path5, "utf8"));
278
+ return JSON.parse(fs.readFileSync(path6, "utf8"));
249
279
  } catch {
250
280
  return null;
251
281
  }
252
282
  }
253
283
  function writeSessionMeta(meta, cwd = process.cwd()) {
254
284
  ensureDir(dirFor(cwd));
255
- fs.writeFileSync(metaPath(meta.id, cwd), JSON.stringify(meta, null, 2));
285
+ const path6 = metaPath(meta.id, cwd);
286
+ fs.writeFileSync(path6, JSON.stringify(meta, null, 2), { mode: 384 });
287
+ fs.chmodSync(path6, 384);
256
288
  }
257
289
  function derivePreview(messages) {
258
290
  const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
@@ -861,7 +893,7 @@ var HookDispatcher = class {
861
893
  result = await handler.run(current);
862
894
  } catch (err) {
863
895
  this.onError(handler, err);
864
- continue;
896
+ return { payload: current, blocked: true, reason: "hook failed" };
865
897
  }
866
898
  if (result.decision === "block") {
867
899
  return { payload: current, blocked: true, reason: result.reason };
@@ -878,6 +910,7 @@ var HookDispatcher = class {
878
910
  return handler.matcher.test(String(payload.tool ?? payload.prompt ?? ""));
879
911
  }
880
912
  };
913
+ var MAX_HOOK_STDOUT_BYTES = 64 * 1024;
881
914
  function configHooksToHandlers(config) {
882
915
  if (!config) return [];
883
916
  const handlers = [];
@@ -907,8 +940,26 @@ function runShellHook(entry, payload) {
907
940
  detached: true
908
941
  });
909
942
  let stdout = "";
943
+ let stdoutBytes = 0;
910
944
  child.stdout.on("data", (chunk) => {
911
- stdout += chunk.toString();
945
+ const text = chunk.toString();
946
+ const bytes = Buffer.byteLength(text);
947
+ if (stdoutBytes + bytes > MAX_HOOK_STDOUT_BYTES) {
948
+ if (child.pid !== void 0) {
949
+ try {
950
+ process.kill(-child.pid, "SIGKILL");
951
+ } catch {
952
+ }
953
+ }
954
+ try {
955
+ child.kill("SIGKILL");
956
+ } catch {
957
+ }
958
+ settle({ decision: "block", reason: `shell hook output exceeded ${MAX_HOOK_STDOUT_BYTES} bytes` });
959
+ return;
960
+ }
961
+ stdoutBytes += bytes;
962
+ stdout += text;
912
963
  });
913
964
  const timer = setTimeout(() => {
914
965
  if (child.pid !== void 0) {
@@ -940,9 +991,13 @@ function runShellHook(entry, payload) {
940
991
  }
941
992
  try {
942
993
  const parsed = JSON.parse(trimmed);
943
- settle(parsed);
994
+ if (!parsed || typeof parsed !== "object" || !("decision" in parsed) || !["continue", "block", "modify"].includes(String(parsed.decision))) {
995
+ settle({ decision: "block", reason: "shell hook returned an invalid decision" });
996
+ } else {
997
+ settle(parsed);
998
+ }
944
999
  } catch {
945
- settle({ decision: "continue" });
1000
+ settle({ decision: "block", reason: "shell hook returned invalid JSON" });
946
1001
  }
947
1002
  });
948
1003
  child.on("error", (err) => {
@@ -989,8 +1044,9 @@ function ChatApp(options) {
989
1044
  } = useRuntime(options);
990
1045
  const mergedTools = React2.useMemo(() => {
991
1046
  const extra = options.extraTools ?? [];
992
- return [...tools, ...extra];
993
- }, [tools, options.extraTools]);
1047
+ const combined = [...tools, ...extra];
1048
+ return options.permissionPolicy ? applyPolicyToTools(options.permissionPolicy, combined) : combined;
1049
+ }, [tools, options.extraTools, options.permissionPolicy]);
994
1050
  const mergedSkills = React2.useMemo(() => {
995
1051
  const extra = options.extraSkills ?? [];
996
1052
  if (!skills && extra.length === 0) return void 0;
@@ -1202,7 +1258,7 @@ async function loadPlugins(options = {}) {
1202
1258
  specs = [],
1203
1259
  pluginDirs = [],
1204
1260
  cwd = process.cwd(),
1205
- autoDiscoverUserDir = true,
1261
+ autoDiscoverUserDir = false,
1206
1262
  onError = (spec, err) => process.stderr.write(
1207
1263
  `[agentskit] plugin "${spec}" failed to load: ${err instanceof Error ? err.message : String(err)}
1208
1264
  `
@@ -1293,6 +1349,8 @@ function mergePluginsIntoBundle(plugins) {
1293
1349
  }
1294
1350
  return bundle;
1295
1351
  }
1352
+ var MAX_FRAME_BYTES = 1024 * 1024;
1353
+ var DISPOSE_GRACE_MS = 1e3;
1296
1354
  var McpClient = class {
1297
1355
  constructor(spec, onError = (err) => process.stderr.write(
1298
1356
  `[agentskit] mcp[${spec.name}] error: ${err instanceof Error ? err.message : String(err)}
@@ -1309,7 +1367,8 @@ var McpClient = class {
1309
1367
  if (this.child) return;
1310
1368
  const child = child_process.spawn(this.spec.command, this.spec.args ?? [], {
1311
1369
  env: { ...process.env, ...this.spec.env ?? {} },
1312
- stdio: ["pipe", "pipe", "pipe"]
1370
+ stdio: ["pipe", "pipe", "pipe"],
1371
+ detached: process.platform !== "win32"
1313
1372
  });
1314
1373
  this.child = child;
1315
1374
  child.stdout.on("data", (chunk) => this.onStdout(chunk.toString()));
@@ -1341,7 +1400,22 @@ var McpClient = class {
1341
1400
  dispose() {
1342
1401
  if (this.disposed) return;
1343
1402
  this.disposed = true;
1344
- this.child?.kill("SIGTERM");
1403
+ for (const pending of this.pending.values()) {
1404
+ pending({ jsonrpc: "2.0", id: 0, error: { code: -32800, message: "client disposed" } });
1405
+ }
1406
+ this.pending.clear();
1407
+ const child = this.child;
1408
+ if (!child || child.killed) return;
1409
+ const kill = (signal) => {
1410
+ try {
1411
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, signal);
1412
+ else child.kill(signal);
1413
+ } catch {
1414
+ }
1415
+ };
1416
+ kill("SIGTERM");
1417
+ const timer = setTimeout(() => kill("SIGKILL"), DISPOSE_GRACE_MS);
1418
+ timer.unref();
1345
1419
  }
1346
1420
  request(method, params) {
1347
1421
  return new Promise((resolvePromise, rejectPromise) => {
@@ -1369,10 +1443,20 @@ var McpClient = class {
1369
1443
  }
1370
1444
  onStdout(chunk) {
1371
1445
  this.buffer += chunk;
1446
+ if (Buffer.byteLength(this.buffer, "utf8") > MAX_FRAME_BYTES && !this.buffer.includes("\n")) {
1447
+ this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
1448
+ this.dispose();
1449
+ return;
1450
+ }
1372
1451
  let newlineIndex = this.buffer.indexOf("\n");
1373
1452
  while (newlineIndex !== -1) {
1374
1453
  const line = this.buffer.slice(0, newlineIndex).trim();
1375
1454
  this.buffer = this.buffer.slice(newlineIndex + 1);
1455
+ if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES) {
1456
+ this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
1457
+ this.dispose();
1458
+ return;
1459
+ }
1376
1460
  if (line) {
1377
1461
  try {
1378
1462
  const parsed = JSON.parse(line);
@@ -1434,7 +1518,7 @@ function registerChatCommand(program) {
1434
1518
  "Extra directory to auto-discover plugin modules from (repeatable)",
1435
1519
  (value, prev = []) => [...prev, value],
1436
1520
  []
1437
- ).option(
1521
+ ).option("--discover-plugins", "Also auto-discover plugins from ~/.agentskit/plugins").option(
1438
1522
  "--mode <mode>",
1439
1523
  "Permission mode: default | plan | acceptEdits | bypassPermissions"
1440
1524
  ).action(async (options) => {
@@ -1471,7 +1555,8 @@ function registerChatCommand(program) {
1471
1555
  }
1472
1556
  const pluginBundle = await loadPlugins({
1473
1557
  specs: config?.plugins ?? [],
1474
- pluginDirs: options.pluginDir ?? []
1558
+ pluginDirs: options.pluginDir ?? [],
1559
+ autoDiscoverUserDir: Boolean(options.discoverPlugins)
1475
1560
  });
1476
1561
  const configHooks = configHooksToHandlers(config?.hooks);
1477
1562
  const hookHandlers = [...configHooks, ...pluginBundle.hooks];
@@ -1559,8 +1644,7 @@ function formatEvent(event) {
1559
1644
  }
1560
1645
  async function runAgent(task, options) {
1561
1646
  if (options.skill && options.skills) {
1562
- process.stderr.write("Error: --skill and --skills are mutually exclusive. Use one or the other.\n");
1563
- process.exit(1);
1647
+ throw new Error("--skill and --skills are mutually exclusive. Use one or the other.");
1564
1648
  }
1565
1649
  const { adapter } = resolveChatProvider({
1566
1650
  provider: options.provider,
@@ -1752,6 +1836,69 @@ var PROVIDER_ENV_KEY = {
1752
1836
  openrouter: "OPENROUTER_API_KEY",
1753
1837
  demo: null
1754
1838
  };
1839
+ var PACKAGE_RANGES = {
1840
+ "@agentskit/adapters": "^0.15.2",
1841
+ "@agentskit/angular": "^0.5.3",
1842
+ "@agentskit/ink": "^0.10.9",
1843
+ "@agentskit/memory": "^0.11.8",
1844
+ "@agentskit/react": "^0.8.3",
1845
+ "@agentskit/runtime": "^0.10.15",
1846
+ "@agentskit/skills": "^0.9.3",
1847
+ "@agentskit/svelte": "^0.5.3",
1848
+ "@agentskit/tools": "^0.13.6",
1849
+ "@agentskit/vue": "^0.5.3"
1850
+ };
1851
+ function align(files) {
1852
+ const result = { ...files };
1853
+ for (const file of ["package.json", "deno.json"]) {
1854
+ if (!files[file]) continue;
1855
+ const parsed = JSON.parse(files[file]);
1856
+ const sections = file === "package.json" ? ["dependencies", "devDependencies", "peerDependencies"] : ["imports"];
1857
+ for (const sectionName of sections) {
1858
+ const section = parsed[sectionName];
1859
+ if (!section || typeof section !== "object" || Array.isArray(section)) continue;
1860
+ for (const [name, range] of Object.entries(PACKAGE_RANGES)) if (name in section) {
1861
+ section[name] = file === "deno.json" ? `npm:${name}@${range}` : range;
1862
+ }
1863
+ }
1864
+ result[file] = `${JSON.stringify(parsed, null, 2)}
1865
+ `;
1866
+ }
1867
+ return result;
1868
+ }
1869
+ async function existing(filePath) {
1870
+ try {
1871
+ return await promises.lstat(filePath);
1872
+ } catch (error) {
1873
+ if (error.code === "ENOENT") return void 0;
1874
+ throw error;
1875
+ }
1876
+ }
1877
+ async function writeStarterProject(options, renderers) {
1878
+ const ctx = { template: options.template, provider: options.provider ?? "demo", tools: options.tools ?? [], memory: options.memory ?? "none", pm: options.packageManager ?? "pnpm" };
1879
+ const files = align(renderers[ctx.template](ctx));
1880
+ await promises.mkdir(options.targetDir, { recursive: true });
1881
+ const target = await promises.lstat(options.targetDir);
1882
+ if (target.isSymbolicLink() || !target.isDirectory()) throw new Error(`Refusing to write starter into non-directory target: ${options.targetDir}`);
1883
+ if ((await promises.readdir(options.targetDir)).length > 0 && !options.force) throw new Error(`Target directory is not empty: ${options.targetDir} (re-run with --force to overwrite generated files)`);
1884
+ const overwritten = [];
1885
+ for (const [relativePath, content] of Object.entries(files)) {
1886
+ const absolutePath = path__default.default.join(options.targetDir, relativePath);
1887
+ await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
1888
+ const destination = await existing(absolutePath);
1889
+ if (destination?.isSymbolicLink()) throw new Error(`Refusing to overwrite symlink: ${absolutePath}`);
1890
+ if (destination) overwritten.push(relativePath);
1891
+ const tempDir = await promises.mkdtemp(path__default.default.join(path__default.default.dirname(absolutePath), `.agentskit-init-${crypto.randomUUID()}-`));
1892
+ try {
1893
+ await promises.writeFile(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), content, "utf8");
1894
+ await promises.rename(path__default.default.join(tempDir, path__default.default.basename(absolutePath)), absolutePath);
1895
+ } finally {
1896
+ await promises.rm(tempDir, { recursive: true, force: true }).catch(() => {
1897
+ });
1898
+ }
1899
+ }
1900
+ return overwritten;
1901
+ }
1755
1902
 
1756
1903
  // src/init.ts
1757
1904
  function adapterCall(provider, prefix = "process.env") {
@@ -3137,23 +3284,8 @@ var TEMPLATE_FN = {
3137
3284
  "deno-deploy": denoDeployStarter,
3138
3285
  angular: angularStarter
3139
3286
  };
3140
- async function writeStarterProject(options) {
3141
- const ctx = {
3142
- template: options.template,
3143
- provider: options.provider ?? "demo",
3144
- tools: options.tools ?? [],
3145
- memory: options.memory ?? "none",
3146
- pm: options.packageManager ?? "pnpm"
3147
- };
3148
- const files = TEMPLATE_FN[ctx.template](ctx);
3149
- await promises.mkdir(options.targetDir, { recursive: true });
3150
- await Promise.all(
3151
- Object.entries(files).map(async ([relativePath, content]) => {
3152
- const absolutePath = path__default.default.join(options.targetDir, relativePath);
3153
- await promises.mkdir(path__default.default.dirname(absolutePath), { recursive: true });
3154
- await promises.writeFile(absolutePath, content, "utf8");
3155
- })
3156
- );
3287
+ async function writeStarterProject2(options) {
3288
+ return writeStarterProject(options, TEMPLATE_FN);
3157
3289
  }
3158
3290
  async function runInteractiveInit(defaults = {}) {
3159
3291
  process.stdout.write(`
@@ -3302,7 +3434,7 @@ function printNextSteps(options) {
3302
3434
 
3303
3435
  // src/commands/init.ts
3304
3436
  function registerInitCommand(program) {
3305
- program.command("init").description("Generate a starter project. Run with no flags for interactive mode.").option("--template <template>", "Starter template (react|nextjs|sveltekit|nuxt|ink|vite-ink|cloudflare-workers|bun|deno-deploy|expo|angular|runtime|multi-agent)").option("--dir <directory>", "Target directory", "agentskit-app").option("--provider <provider>", "LLM provider (openai|anthropic|gemini|ollama|deepseek|grok|kimi|groq|openrouter|demo)").option("--tools <tools>", "Comma-separated tools (web_search,filesystem,shell)").option("--memory <backend>", "Memory backend (none|file|sqlite)").option("--pm <packageManager>", "Package manager (pnpm|npm|yarn|bun)").option("-y, --yes", "Skip interactive prompts; use flag values + defaults").action(async (rawOptions) => {
3437
+ program.command("init").description("Generate a starter project. Run with no flags for interactive mode.").option("--template <template>", "Starter template (react|nextjs|sveltekit|nuxt|ink|vite-ink|cloudflare-workers|bun|deno-deploy|expo|angular|runtime|multi-agent)").option("--dir <directory>", "Target directory", "agentskit-app").option("--provider <provider>", "LLM provider (openai|anthropic|gemini|ollama|deepseek|grok|kimi|groq|openrouter|demo)").option("--tools <tools>", "Comma-separated tools (web_search,filesystem,shell)").option("--memory <backend>", "Memory backend (none|file|sqlite)").option("--pm <packageManager>", "Package manager (pnpm|npm|yarn|bun)").option("--force", "Overwrite generated files in a non-empty target directory").option("-y, --yes", "Skip interactive prompts; use flag values + defaults").action(async (rawOptions) => {
3306
3438
  const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
3307
3439
  let resolved;
3308
3440
  if (isCi) {
@@ -3313,7 +3445,8 @@ function registerInitCommand(program) {
3313
3445
  provider: rawOptions.provider ?? "demo",
3314
3446
  tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
3315
3447
  memory: rawOptions.memory ?? "none",
3316
- packageManager: rawOptions.pm ?? "pnpm"
3448
+ packageManager: rawOptions.pm ?? "pnpm",
3449
+ force: Boolean(rawOptions.force)
3317
3450
  };
3318
3451
  } else {
3319
3452
  const result = await runInteractiveInit({
@@ -3323,17 +3456,22 @@ function registerInitCommand(program) {
3323
3456
  if (result.cancelled) {
3324
3457
  process.exit(0);
3325
3458
  }
3326
- resolved = result.options;
3459
+ resolved = { ...result.options, force: Boolean(rawOptions.force) };
3327
3460
  }
3328
- await writeStarterProject(resolved);
3461
+ const overwritten = await writeStarterProject2(resolved) ?? [];
3329
3462
  if (isCi) {
3330
3463
  process.stdout.write(
3331
3464
  `Created ${resolved.template} starter in ${path__default.default.relative(process.cwd(), resolved.targetDir) || "."}
3332
- \u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
3465
+ ` + (overwritten.length > 0 ? `Overwritten: ${overwritten.join(", ")}
3466
+ ` : "") + `\u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
3333
3467
  `
3334
3468
  );
3335
3469
  } else {
3336
3470
  printNextSteps(resolved);
3471
+ if (overwritten.length > 0) {
3472
+ process.stdout.write(`Overwritten: ${overwritten.join(", ")}
3473
+ `);
3474
+ }
3337
3475
  }
3338
3476
  });
3339
3477
  }
@@ -3582,8 +3720,8 @@ async function checkPnpm() {
3582
3720
  };
3583
3721
  }
3584
3722
  async function checkPackageJson() {
3585
- const path5 = path.join(process.cwd(), "package.json");
3586
- if (!fs.existsSync(path5)) {
3723
+ const path6 = path.join(process.cwd(), "package.json");
3724
+ if (!fs.existsSync(path6)) {
3587
3725
  return {
3588
3726
  status: "warn",
3589
3727
  name: "package.json",
@@ -3592,7 +3730,7 @@ async function checkPackageJson() {
3592
3730
  };
3593
3731
  }
3594
3732
  try {
3595
- const pkg = JSON.parse(await promises.readFile(path5, "utf8"));
3733
+ const pkg = JSON.parse(await promises.readFile(path6, "utf8"));
3596
3734
  const deps = {
3597
3735
  ...pkg.dependencies ?? {},
3598
3736
  ...pkg.devDependencies ?? {}
@@ -3825,6 +3963,14 @@ function registerDoctorCommand(program) {
3825
3963
  if (report.fail > 0) process.exit(1);
3826
3964
  });
3827
3965
  }
3966
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
3967
+ function resolveTsxCli() {
3968
+ try {
3969
+ return require2.resolve("tsx/cli");
3970
+ } catch {
3971
+ throw new Error("TypeScript execution requires the packaged tsx dependency");
3972
+ }
3973
+ }
3828
3974
  var DEFAULT_WATCH = [
3829
3975
  "**/*.ts",
3830
3976
  "**/*.tsx",
@@ -3852,8 +3998,8 @@ function startDev(options) {
3852
3998
  const stderr = options.stderr ?? process.stderr;
3853
3999
  const debounceMs = options.debounceMs ?? 200;
3854
4000
  const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
3855
- const cmd = isTs ? "tsx" : "node";
3856
- const baseArgs = [entry, ...options.scriptArgs ?? []];
4001
+ const cmd = isTs ? process.execPath : "node";
4002
+ const baseArgs = isTs ? [resolveTsxCli(), entry, ...options.scriptArgs ?? []] : [entry, ...options.scriptArgs ?? []];
3857
4003
  const spawnFn = options.spawn ?? ((c, a) => child_process.spawn(c, a, {
3858
4004
  stdio: ["inherit", "pipe", "pipe"],
3859
4005
  env: { ...process.env, FORCE_COLOR: "1" }
@@ -3865,7 +4011,9 @@ function startDev(options) {
3865
4011
  let child;
3866
4012
  let restartCount = 0;
3867
4013
  let restartTimer;
4014
+ let delayedStartTimer;
3868
4015
  let stopped = false;
4016
+ let stdinListener;
3869
4017
  let resolveDone;
3870
4018
  const done = new Promise((r) => {
3871
4019
  resolveDone = r;
@@ -3891,15 +4039,18 @@ function startDev(options) {
3891
4039
  }
3892
4040
  });
3893
4041
  };
3894
- const restart = (path5) => {
4042
+ const restart = (path6) => {
3895
4043
  if (restartTimer) clearTimeout(restartTimer);
3896
4044
  restartTimer = setTimeout(() => {
3897
4045
  restartTimer = void 0;
3898
- banner(`\u21BB change detected \u2014 ${path5}`, "yellow");
4046
+ banner(`\u21BB change detected \u2014 ${path6}`, "yellow");
3899
4047
  if (child && !child.killed && child.exitCode === null) {
3900
4048
  child.kill("SIGTERM");
3901
4049
  }
3902
- setTimeout(startChild, 80);
4050
+ delayedStartTimer = setTimeout(() => {
4051
+ delayedStartTimer = void 0;
4052
+ if (!stopped) startChild();
4053
+ }, 80);
3903
4054
  }, debounceMs);
3904
4055
  };
3905
4056
  watcher.on("change", restart);
@@ -3910,6 +4061,9 @@ function startDev(options) {
3910
4061
  if (stopped) return;
3911
4062
  stopped = true;
3912
4063
  if (restartTimer) clearTimeout(restartTimer);
4064
+ if (delayedStartTimer) clearTimeout(delayedStartTimer);
4065
+ if (stdinListener) process.stdin.off("data", stdinListener);
4066
+ if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(false);
3913
4067
  if (child && !child.killed && child.exitCode === null) {
3914
4068
  child.kill("SIGTERM");
3915
4069
  }
@@ -3920,11 +4074,12 @@ function startDev(options) {
3920
4074
  if (process.stdin.isTTY && process.stdin.setRawMode) {
3921
4075
  process.stdin.setRawMode(true);
3922
4076
  process.stdin.resume();
3923
- process.stdin.on("data", (data) => {
4077
+ stdinListener = (data) => {
3924
4078
  const key = data.toString();
3925
4079
  if (key === "r") restart("manual");
3926
4080
  if (key === "q" || key === "") void stop();
3927
- });
4081
+ };
4082
+ process.stdin.on("data", stdinListener);
3928
4083
  }
3929
4084
  return {
3930
4085
  done,
@@ -3964,7 +4119,7 @@ function registerConfigCommand(program) {
3964
4119
  const targetPath = isLocal ? path__default.default.join(process.cwd(), ".agentskit.config.json") : path__default.default.join(os.homedir(), ".agentskit", "config.json");
3965
4120
  if (action === "show") {
3966
4121
  const config = await loadConfig();
3967
- process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
4122
+ process.stdout.write(JSON.stringify(redactConfig(config ?? {}), null, 2) + "\n");
3968
4123
  return;
3969
4124
  }
3970
4125
  if (action !== "init") {
@@ -4023,6 +4178,7 @@ async function startTunnel(options) {
4023
4178
  });
4024
4179
  let requests = 0;
4025
4180
  let stopped = false;
4181
+ let signalHandler;
4026
4182
  let resolveDone;
4027
4183
  const done = new Promise((r) => {
4028
4184
  resolveDone = r;
@@ -4032,12 +4188,14 @@ async function startTunnel(options) {
4032
4188
  });
4033
4189
  tunnel.on("close", () => {
4034
4190
  if (stopped) return;
4191
+ if (signalHandler) process.off("SIGINT", signalHandler);
4035
4192
  banner(`tunnel closed by remote`, "yellow");
4036
4193
  resolveDone();
4037
4194
  });
4038
4195
  tunnel.on("error", (...args) => {
4039
4196
  const err = args[0];
4040
4197
  banner(`error: ${err?.message ?? "unknown"}`, "red");
4198
+ resolveDone();
4041
4199
  });
4042
4200
  banner(`\u2713 ready`, "green");
4043
4201
  stdout.write("\n");
@@ -4053,14 +4211,16 @@ async function startTunnel(options) {
4053
4211
  const stop = async () => {
4054
4212
  if (stopped) return;
4055
4213
  stopped = true;
4214
+ if (signalHandler) process.off("SIGINT", signalHandler);
4056
4215
  tunnel.close();
4057
4216
  banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
4058
4217
  resolveDone();
4059
4218
  };
4060
4219
  if (process.stdin.isTTY) {
4061
- process.on("SIGINT", () => {
4220
+ signalHandler = () => {
4062
4221
  void stop();
4063
- });
4222
+ };
4223
+ process.on("SIGINT", signalHandler);
4064
4224
  }
4065
4225
  return {
4066
4226
  url: tunnel.url,
@@ -4155,10 +4315,10 @@ async function indexSources(rag, config, cwd) {
4155
4315
  }
4156
4316
  }
4157
4317
  const documents = await Promise.all(
4158
- absolutePaths.map(async (path5) => ({
4159
- id: path5,
4160
- content: await promises.readFile(path5, "utf8"),
4161
- source: path5
4318
+ absolutePaths.map(async (path6) => ({
4319
+ id: path6,
4320
+ content: await promises.readFile(path6, "utf8"),
4321
+ source: path6
4162
4322
  }))
4163
4323
  );
4164
4324
  if (documents.length > 0) await rag.ingest(documents);
@@ -4293,7 +4453,7 @@ function scaffoldAgent(schema) {
4293
4453
  return files;
4294
4454
  }
4295
4455
  async function writeScaffold(files, outDir, opts = {}) {
4296
- const { writeFile: writeFile5, mkdir: mkdir5, access } = await import('fs/promises');
4456
+ const { writeFile: writeFile5, mkdir: mkdir5, access: access2 } = await import('fs/promises');
4297
4457
  const { dirname: dirname5, join: join11 } = await import('path');
4298
4458
  const written = [];
4299
4459
  for (const f of files) {
@@ -4301,7 +4461,7 @@ async function writeScaffold(files, outDir, opts = {}) {
4301
4461
  await mkdir5(dirname5(full), { recursive: true });
4302
4462
  if (!opts.overwrite) {
4303
4463
  try {
4304
- await access(full);
4464
+ await access2(full);
4305
4465
  continue;
4306
4466
  } catch {
4307
4467
  }
@@ -4777,66 +4937,66 @@ After writing, summarise which files landed and what each one does.
4777
4937
  // src/rules.ts
4778
4938
  var CODEX_BLOCK_START = "<!-- agentskit-codex-profile:start -->";
4779
4939
  var CODEX_BLOCK_END = "<!-- agentskit-codex-profile:end -->";
4780
- async function ensureDir2(path5) {
4781
- await promises.mkdir(path.dirname(path5), { recursive: true });
4940
+ async function ensureDir2(path6) {
4941
+ await promises.mkdir(path.dirname(path6), { recursive: true });
4782
4942
  }
4783
4943
  async function writeIfChanged(absPath, contents, force) {
4784
- let existing;
4944
+ let existing2;
4785
4945
  try {
4786
- existing = await promises.readFile(absPath, "utf8");
4946
+ existing2 = await promises.readFile(absPath, "utf8");
4787
4947
  } catch {
4788
4948
  }
4789
- if (existing === contents) return "skipped";
4790
- if (existing !== void 0 && !force) {
4949
+ if (existing2 === contents) return "skipped";
4950
+ if (existing2 !== void 0 && !force) {
4791
4951
  return "skipped";
4792
4952
  }
4793
4953
  await ensureDir2(absPath);
4794
4954
  await promises.writeFile(absPath, contents, "utf8");
4795
- return existing === void 0 ? "wrote" : "updated";
4955
+ return existing2 === void 0 ? "wrote" : "updated";
4796
4956
  }
4797
4957
  async function writeCursor(rootDir, force) {
4798
- const path5 = path.join(rootDir, ".cursor", "rules", "agentskit.mdc");
4799
- const action = await writeIfChanged(path5, CURSOR_RULE, force);
4800
- return [{ path: path5, action }];
4958
+ const path6 = path.join(rootDir, ".cursor", "rules", "agentskit.mdc");
4959
+ const action = await writeIfChanged(path6, CURSOR_RULE, force);
4960
+ return [{ path: path6, action }];
4801
4961
  }
4802
4962
  async function writeWindsurf(rootDir, force) {
4803
- const path5 = path.join(rootDir, ".windsurfrules");
4804
- const action = await writeIfChanged(path5, WINDSURF_RULE, force);
4805
- return [{ path: path5, action }];
4963
+ const path6 = path.join(rootDir, ".windsurfrules");
4964
+ const action = await writeIfChanged(path6, WINDSURF_RULE, force);
4965
+ return [{ path: path6, action }];
4806
4966
  }
4807
4967
  async function writeCodex(rootDir, force) {
4808
- const path5 = path.join(rootDir, "AGENTS.md");
4809
- let existing = "";
4968
+ const path6 = path.join(rootDir, "AGENTS.md");
4969
+ let existing2 = "";
4810
4970
  try {
4811
- existing = await promises.readFile(path5, "utf8");
4971
+ existing2 = await promises.readFile(path6, "utf8");
4812
4972
  } catch {
4813
4973
  }
4814
- const startIdx = existing.indexOf(CODEX_BLOCK_START);
4815
- const endIdx = startIdx >= 0 ? existing.lastIndexOf(CODEX_BLOCK_END, existing.length) : -1;
4974
+ const startIdx = existing2.indexOf(CODEX_BLOCK_START);
4975
+ const endIdx = startIdx >= 0 ? existing2.lastIndexOf(CODEX_BLOCK_END, existing2.length) : -1;
4816
4976
  if (startIdx >= 0 && endIdx <= startIdx) {
4817
4977
  throw new core.ConfigError({
4818
4978
  code: core.ErrorCodes.AK_CONFIG_INVALID,
4819
- message: `${path5}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
4979
+ message: `${path6}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
4820
4980
  hint: "Restore the closing sentinel or remove the partial block, then re-run."
4821
4981
  });
4822
4982
  }
4823
4983
  let next;
4824
4984
  if (startIdx >= 0 && endIdx > startIdx) {
4825
- next = existing.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing.slice(endIdx + CODEX_BLOCK_END.length);
4826
- } else if (existing) {
4827
- next = `${existing.replace(/\s+$/, "")}
4985
+ next = existing2.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing2.slice(endIdx + CODEX_BLOCK_END.length);
4986
+ } else if (existing2) {
4987
+ next = `${existing2.replace(/\s+$/, "")}
4828
4988
 
4829
4989
  ${CODEX_PROFILE}`;
4830
4990
  } else {
4831
4991
  next = CODEX_PROFILE;
4832
4992
  }
4833
- if (next === existing) return [{ path: path5, action: "skipped" }];
4834
- if (existing && !force) {
4835
- return [{ path: path5, action: "skipped" }];
4993
+ if (next === existing2) return [{ path: path6, action: "skipped" }];
4994
+ if (existing2 && !force) {
4995
+ return [{ path: path6, action: "skipped" }];
4836
4996
  }
4837
- await ensureDir2(path5);
4838
- await promises.writeFile(path5, next, "utf8");
4839
- return [{ path: path5, action: existing ? "updated" : "wrote" }];
4997
+ await ensureDir2(path6);
4998
+ await promises.writeFile(path6, next, "utf8");
4999
+ return [{ path: path6, action: existing2 ? "updated" : "wrote" }];
4840
5000
  }
4841
5001
  async function writeClaudeCode(rootDir, force) {
4842
5002
  const out = [];
@@ -4924,17 +5084,78 @@ rules failed: ${message}
4924
5084
  }
4925
5085
  var RAW_BASE = "https://raw.githubusercontent.com/AgentsKit-io/agentskit-registry/main";
4926
5086
  var HOSTED_BASE = `${RAW_BASE}/public/r`;
5087
+ var FETCH_TIMEOUT_MS = 15e3;
5088
+ var MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
5089
+ var MAX_AGENT_FILES = 100;
5090
+ var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
5091
+ function assertSafeId(id) {
5092
+ if (!SAFE_ID.test(id)) throw new Error(`Invalid registry agent id: ${JSON.stringify(id)}`);
5093
+ }
5094
+ function assertSafeRelativePath(filePath) {
5095
+ if (!filePath || path.isAbsolute(filePath) || filePath.includes("\0")) {
5096
+ throw new Error(`Invalid registry file path: ${JSON.stringify(filePath)}`);
5097
+ }
5098
+ const normalized = filePath.replace(/\\/g, "/");
5099
+ if (normalized.split("/").some((part) => part === ".." || part === "")) {
5100
+ throw new Error(`Registry file path must stay relative: ${JSON.stringify(filePath)}`);
5101
+ }
5102
+ }
5103
+ function assertInside(parent, child) {
5104
+ const rel = path.relative(path.resolve(parent), path.resolve(child));
5105
+ if (rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
5106
+ throw new Error(`Registry destination escapes target directory: ${child}`);
5107
+ }
5108
+ }
5109
+ async function assertNoSymlinkAncestors(targetDir, baseDir) {
5110
+ for (const path6 of [targetDir, baseDir]) {
5111
+ try {
5112
+ if ((await promises.lstat(path.resolve(path6))).isSymbolicLink()) {
5113
+ throw new Error(`Registry destination contains a symlink ancestor: ${path.resolve(path6)}`);
5114
+ }
5115
+ } catch (error) {
5116
+ if (error.code !== "ENOENT") throw error;
5117
+ }
5118
+ }
5119
+ let current = path.resolve(baseDir);
5120
+ while (true) {
5121
+ try {
5122
+ if ((await promises.lstat(current)).isSymbolicLink()) {
5123
+ throw new Error(`Registry destination contains a symlink ancestor: ${current}`);
5124
+ }
5125
+ return;
5126
+ } catch (error) {
5127
+ if (error.code !== "ENOENT") throw error;
5128
+ }
5129
+ const parent = path.dirname(current);
5130
+ if (parent === current) return;
5131
+ current = parent;
5132
+ }
5133
+ }
5134
+ async function fetchWithLimits(url, fetchImpl) {
5135
+ const controller = new AbortController();
5136
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
5137
+ try {
5138
+ const response = await fetchImpl(url, { signal: controller.signal });
5139
+ if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
5140
+ const body = await response.arrayBuffer();
5141
+ if (body.byteLength > MAX_RESPONSE_BYTES) {
5142
+ throw new Error(`Registry response exceeded ${MAX_RESPONSE_BYTES} bytes`);
5143
+ }
5144
+ return new Response(body, { status: response.status, headers: response.headers });
5145
+ } finally {
5146
+ clearTimeout(timer);
5147
+ }
5148
+ }
4927
5149
  async function getJson(url, fetchImpl) {
4928
- const res = await fetchImpl(url);
4929
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
4930
- return res.json();
5150
+ const res = await fetchWithLimits(url, fetchImpl);
5151
+ return JSON.parse(await res.text());
4931
5152
  }
4932
5153
  async function getText(url, fetchImpl) {
4933
- const res = await fetchImpl(url);
4934
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
5154
+ const res = await fetchWithLimits(url, fetchImpl);
4935
5155
  return res.text();
4936
5156
  }
4937
5157
  async function fetchAgent(id, options = {}) {
5158
+ assertSafeId(id);
4938
5159
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
4939
5160
  try {
4940
5161
  const hosted = await getJson(`${HOSTED_BASE}/${id}.json`, fetchImpl);
@@ -4942,6 +5163,10 @@ async function fetchAgent(id, options = {}) {
4942
5163
  throw new Error("hosted entry missing sources");
4943
5164
  } catch {
4944
5165
  const meta = await getJson(`${RAW_BASE}/registry/${id}/meta.json`, fetchImpl);
5166
+ if (!Array.isArray(meta.files) || meta.files.length > MAX_AGENT_FILES) {
5167
+ throw new Error(`Registry agent contains too many files (maximum ${MAX_AGENT_FILES})`);
5168
+ }
5169
+ for (const rel of meta.files) assertSafeRelativePath(rel);
4945
5170
  const sources = await Promise.all(
4946
5171
  meta.files.map(async (rel) => ({
4947
5172
  path: rel,
@@ -4958,10 +5183,10 @@ function resolveSystemPrompt(agent) {
4958
5183
  const m = src.match(/systemPrompt:\s*`((?:\\.|[^`\\])*)`/);
4959
5184
  return m ? m[1].replace(/\\`/g, "`").replace(/\\\$\{/g, "${") : null;
4960
5185
  }
4961
- async function defaultExists(path5) {
4962
- const { access } = await import('fs/promises');
5186
+ async function defaultExists(path6) {
5187
+ const { access: access2 } = await import('fs/promises');
4963
5188
  try {
4964
- await access(path5);
5189
+ await access2(path6);
4965
5190
  return true;
4966
5191
  } catch {
4967
5192
  return false;
@@ -4985,17 +5210,24 @@ async function addAgent(id, options = {}) {
4985
5210
  assertInstallable(agent);
4986
5211
  const baseDir = options.outDir ?? "agents";
4987
5212
  const targetDir = path.join(baseDir, id);
5213
+ assertInside(baseDir, targetDir);
5214
+ await assertNoSymlinkAncestors(targetDir, baseDir);
4988
5215
  const exists = options.existsImpl ?? defaultExists;
4989
- const write = options.writeFileImpl ?? (async (path5, content) => {
4990
- await promises.mkdir(path.dirname(path5), { recursive: true });
4991
- await promises.writeFile(path5, content, "utf8");
5216
+ const write = options.writeFileImpl ?? (async (path6, content) => {
5217
+ await promises.mkdir(path.dirname(path6), { recursive: true });
5218
+ await promises.writeFile(path6, content, "utf8");
4992
5219
  });
4993
5220
  const written = [];
4994
5221
  for (const file of agent.sources) {
5222
+ assertSafeRelativePath(file.path);
4995
5223
  const dest = path.join(targetDir, file.path);
5224
+ assertInside(targetDir, dest);
4996
5225
  if (!options.force && await exists(dest)) {
4997
5226
  throw new Error(`${dest} already exists (re-run with --force to overwrite)`);
4998
5227
  }
5228
+ }
5229
+ for (const file of agent.sources) {
5230
+ const dest = path.join(targetDir, file.path);
4999
5231
  await write(dest, file.content);
5000
5232
  written.push(dest);
5001
5233
  }
@@ -5058,8 +5290,8 @@ function allDeps(pkg) {
5058
5290
  if (!pkg) return {};
5059
5291
  return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
5060
5292
  }
5061
- function readJson(fs, path5) {
5062
- const raw = fs.readFile(path5);
5293
+ function readJson(fs, path6) {
5294
+ const raw = fs.readFile(path6);
5063
5295
  if (raw == null) return null;
5064
5296
  try {
5065
5297
  return JSON.parse(raw);
@@ -5243,13 +5475,13 @@ function readConfig(io, root = ".") {
5243
5475
  return raw == null ? null : parseConfig(raw);
5244
5476
  }
5245
5477
  function writeConfig(io, config, root = ".") {
5246
- const path5 = path.join(root, CONFIG_PATH);
5247
- io.write(path5, serializeConfig(config));
5248
- return path5;
5478
+ const path6 = path.join(root, CONFIG_PATH);
5479
+ io.write(path6, serializeConfig(config));
5480
+ return path6;
5249
5481
  }
5250
5482
  function resolveConfig(io, scan, root = ".") {
5251
- const existing = readConfig(io, root);
5252
- if (existing) return { config: existing, fromDisk: true };
5483
+ const existing2 = readConfig(io, root);
5484
+ if (existing2) return { config: existing2, fromDisk: true };
5253
5485
  return { config: buildConfig(scan), fromDisk: false };
5254
5486
  }
5255
5487
  var IntegrityError = class extends Error {
@@ -5334,7 +5566,18 @@ function resolveAuthHeader(base, config, env = {}) {
5334
5566
  const map = config?.registryAuth;
5335
5567
  if (!map) return {};
5336
5568
  for (const [registryBase, envVar] of Object.entries(map)) {
5337
- if (base.startsWith(registryBase)) {
5569
+ let configured;
5570
+ let target;
5571
+ try {
5572
+ configured = new URL(registryBase);
5573
+ target = new URL(base);
5574
+ } catch {
5575
+ continue;
5576
+ }
5577
+ const configuredPath = configured.pathname.replace(/\/+$/, "") || "/";
5578
+ const targetPath = target.pathname.replace(/\/+$/, "") || "/";
5579
+ const samePathBoundary = configuredPath === "/" || targetPath === configuredPath || targetPath.startsWith(`${configuredPath}/`);
5580
+ if (target.origin === configured.origin && samePathBoundary) {
5338
5581
  const token = env[envVar];
5339
5582
  if (token) return { authorization: `Bearer ${token}` };
5340
5583
  }
@@ -5353,8 +5596,8 @@ function verifyChecksums(files) {
5353
5596
  function manifestUrl(ref) {
5354
5597
  return `${ref.base}/r/${ref.itemId}.json`;
5355
5598
  }
5356
- function fileUrl(ref, path5) {
5357
- return `${ref.base}/r/${ref.itemId}/${path5}`;
5599
+ function fileUrl(ref, path6) {
5600
+ return `${ref.base}/r/${ref.itemId}/${path6}`;
5358
5601
  }
5359
5602
  async function getText2(fetchImpl, url, headers) {
5360
5603
  const res = await fetchImpl(url, { headers });
@@ -5691,44 +5934,44 @@ async function addComponent(options, deps) {
5691
5934
  }
5692
5935
  function nodeScanFs() {
5693
5936
  return {
5694
- readFile(path5) {
5937
+ readFile(path6) {
5695
5938
  try {
5696
- return fs.readFileSync(path5, "utf8");
5939
+ return fs.readFileSync(path6, "utf8");
5697
5940
  } catch {
5698
5941
  return null;
5699
5942
  }
5700
5943
  },
5701
- exists(path5) {
5702
- return fs.existsSync(path5);
5944
+ exists(path6) {
5945
+ return fs.existsSync(path6);
5703
5946
  }
5704
5947
  };
5705
5948
  }
5706
5949
  function nodeWriteFs() {
5707
5950
  return {
5708
- exists(path5) {
5709
- return fs.existsSync(path5);
5951
+ exists(path6) {
5952
+ return fs.existsSync(path6);
5710
5953
  },
5711
- write(path5, content) {
5712
- fs.mkdirSync(path.dirname(path5), { recursive: true });
5713
- fs.writeFileSync(path5, content, "utf8");
5954
+ write(path6, content) {
5955
+ fs.mkdirSync(path.dirname(path6), { recursive: true });
5956
+ fs.writeFileSync(path6, content, "utf8");
5714
5957
  },
5715
- remove(path5) {
5716
- fs.rmSync(path5, { force: true });
5958
+ remove(path6) {
5959
+ fs.rmSync(path6, { force: true });
5717
5960
  }
5718
5961
  };
5719
5962
  }
5720
5963
  function nodeConfigIo() {
5721
5964
  return {
5722
- read(path5) {
5965
+ read(path6) {
5723
5966
  try {
5724
- return fs.readFileSync(path5, "utf8");
5967
+ return fs.readFileSync(path6, "utf8");
5725
5968
  } catch {
5726
5969
  return null;
5727
5970
  }
5728
5971
  },
5729
- write(path5, content) {
5730
- fs.mkdirSync(path.dirname(path5), { recursive: true });
5731
- fs.writeFileSync(path5, content, "utf8");
5972
+ write(path6, content) {
5973
+ fs.mkdirSync(path.dirname(path6), { recursive: true });
5974
+ fs.writeFileSync(path6, content, "utf8");
5732
5975
  }
5733
5976
  };
5734
5977
  }
@@ -5948,7 +6191,7 @@ update failed: ${err instanceof Error ? err.message : String(err)}
5948
6191
  // src/commands/index.ts
5949
6192
  function createCli() {
5950
6193
  const program = new commander.Command();
5951
- program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
6194
+ program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.").version(package_default.version);
5952
6195
  registerChatCommand(program);
5953
6196
  registerRunCommand(program);
5954
6197
  registerInitCommand(program);
@@ -6004,6 +6247,6 @@ exports.sessionFilePath = sessionFilePath;
6004
6247
  exports.startDev = startDev;
6005
6248
  exports.startTunnel = startTunnel;
6006
6249
  exports.writeSessionMeta = writeSessionMeta;
6007
- exports.writeStarterProject = writeStarterProject;
6250
+ exports.writeStarterProject = writeStarterProject2;
6008
6251
  //# sourceMappingURL=index.cjs.map
6009
6252
  //# sourceMappingURL=index.cjs.map