@agentskit/cli 0.13.37 → 0.13.39

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.
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { readdir, stat, mkdir, writeFile, glob, readFile } from 'fs/promises';
2
+ import { readdir, stat, glob, readFile, mkdir, lstat, mkdtemp, writeFile, rename, rm, access } from 'fs/promises';
3
3
  import { homedir } from 'os';
4
- import path, { join, resolve, isAbsolute, basename, dirname, sep } from 'path';
4
+ import path, { join, resolve, isAbsolute, basename, dirname, relative, sep } from 'path';
5
5
  import { openrouter, groq, kimi, grok, deepseek, ollama, gemini, anthropic, openai } from '@agentskit/adapters';
6
- import { randomBytes, createHash } from 'crypto';
7
- import { writeFileSync, existsSync, readdirSync, statSync, readFileSync, mkdirSync, rmSync } from 'fs';
6
+ import { randomBytes, randomUUID, createHash } from 'crypto';
7
+ import { writeFileSync, chmodSync, existsSync, readdirSync, statSync, readFileSync, mkdirSync, rmSync } from 'fs';
8
8
  import { spawn } from 'child_process';
9
9
  import React2, { useMemo, useState, useEffect, useRef } from 'react';
10
10
  import { Box, Text, render } from 'ink';
@@ -16,6 +16,7 @@ import { getIntegration, integrationToolsFromEnv } from '@agentskit/integrations
16
16
  import { jsxs, jsx } from 'react/jsx-runtime';
17
17
  import { pathToFileURL } from 'url';
18
18
  import { createRuntime, validateFlow, flowToMermaid, compileFlow, createFileStepLog } from '@agentskit/runtime';
19
+ import { createRequire } from 'module';
19
20
  import chokidar from 'chokidar';
20
21
  import kleur3 from 'kleur';
21
22
  import { createRAG } from '@agentskit/rag';
@@ -26,20 +27,27 @@ import { parse } from 'yaml';
26
27
  import { validatePIITaxonomy } from '@agentskit/core/security';
27
28
  import { ConfigError, ErrorCodes } from '@agentskit/core';
28
29
 
29
- async function loadJsonConfig(path5) {
30
+ async function loadJsonConfig(path6) {
30
31
  try {
31
- const raw = await readFile(path5, "utf8");
32
+ const raw = await readFile(path6, "utf8");
32
33
  return JSON.parse(raw);
33
- } catch {
34
- return void 0;
34
+ } catch (err) {
35
+ if (err.code === "ENOENT") return void 0;
36
+ throw new Error(`Failed to load JSON config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
35
37
  }
36
38
  }
37
- async function loadTsConfig(path5) {
39
+ async function loadTsConfig(path6) {
40
+ try {
41
+ await access(path6);
42
+ } catch (err) {
43
+ if (err.code === "ENOENT") return void 0;
44
+ throw err;
45
+ }
38
46
  try {
39
- const mod = await import(path5);
47
+ const mod = await import(`${path6}?agentskit_config=${Date.now()}`);
40
48
  return mod.default ?? mod;
41
- } catch {
42
- return void 0;
49
+ } catch (err) {
50
+ throw new Error(`Failed to load TypeScript config ${path6}: ${err instanceof Error ? err.message : String(err)}`);
43
51
  }
44
52
  }
45
53
  async function loadPackageJsonConfig(dir) {
@@ -50,29 +58,31 @@ async function loadPackageJsonConfig(dir) {
50
58
  return pkg.agentskit;
51
59
  }
52
60
  return void 0;
53
- } catch {
54
- return void 0;
61
+ } catch (err) {
62
+ if (err.code === "ENOENT") return void 0;
63
+ throw new Error(`Failed to load package.json config ${dir}: ${err instanceof Error ? err.message : String(err)}`);
55
64
  }
56
65
  }
57
66
  function mergeConfigs(base, override) {
58
67
  if (!base && !override) return void 0;
59
68
  if (!base) return override;
60
69
  if (!override) return base;
61
- return {
62
- ...base,
63
- ...override,
64
- tools: { ...base.tools, ...override.tools },
65
- defaults: { ...base.defaults, ...override.defaults },
66
- runtime: { ...base.runtime, ...override.runtime },
67
- observability: { ...base.observability, ...override.observability }
70
+ const merge = (left, right) => {
71
+ if (!left || typeof left !== "object" || Array.isArray(left)) return right;
72
+ if (!right || typeof right !== "object" || Array.isArray(right)) return right;
73
+ const out = { ...left };
74
+ for (const [key, value] of Object.entries(right)) {
75
+ out[key] = key in out ? merge(out[key], value) : value;
76
+ }
77
+ return out;
68
78
  };
79
+ return merge(base, override);
69
80
  }
70
81
  async function loadLocalConfig(cwd) {
71
82
  const tsConfig = await loadTsConfig(join(cwd, ".agentskit.config.ts"));
72
- if (tsConfig) return tsConfig;
73
83
  const jsonConfig = await loadJsonConfig(join(cwd, ".agentskit.config.json"));
74
- if (jsonConfig) return jsonConfig;
75
- return await loadPackageJsonConfig(cwd);
84
+ const packageConfig = await loadPackageJsonConfig(cwd);
85
+ return mergeConfigs(mergeConfigs(packageConfig, jsonConfig), tsConfig);
76
86
  }
77
87
  async function loadGlobalConfig(home) {
78
88
  if (home === null) return void 0;
@@ -87,6 +97,21 @@ async function loadConfig(options) {
87
97
  const local = await loadLocalConfig(cwd);
88
98
  return mergeConfigs(global, local);
89
99
  }
100
+ var SECRET_KEY = /^(api[-_]?key|token|secret|password|authorization|private[-_]?key)$/i;
101
+ var SECRET_ENV_KEY = /(?:^|_)(?:api_?key|token|secret|password|authorization|private_?key)$/i;
102
+ function isSecretKey(key) {
103
+ return SECRET_KEY.test(key) || SECRET_ENV_KEY.test(key);
104
+ }
105
+ function redactConfig(value) {
106
+ if (Array.isArray(value)) return value.map(redactConfig);
107
+ if (!value || typeof value !== "object") return value;
108
+ return Object.fromEntries(
109
+ Object.entries(value).map(([key, child]) => [
110
+ key,
111
+ isSecretKey(key) ? "[REDACTED]" : redactConfig(child)
112
+ ])
113
+ );
114
+ }
90
115
  var providers = {
91
116
  openai: {
92
117
  label: "OpenAI",
@@ -158,7 +183,7 @@ function createDemoAdapter(provider, model) {
158
183
  ].join(" ");
159
184
  for (const chunk of reply.match(/.{1,18}/g) ?? []) {
160
185
  if (cancelled) return;
161
- await new Promise((resolve7) => setTimeout(resolve7, 35));
186
+ await new Promise((resolve8) => setTimeout(resolve8, 35));
162
187
  yield { type: "text", content: chunk };
163
188
  }
164
189
  yield { type: "done" };
@@ -218,7 +243,8 @@ function dirFor(cwd = process.cwd()) {
218
243
  return join(ROOT, cwdHash(cwd));
219
244
  }
220
245
  function ensureDir(dir) {
221
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
246
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
247
+ chmodSync(dir, 448);
222
248
  }
223
249
  function generateSessionId() {
224
250
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
@@ -233,17 +259,19 @@ function metaPath(id, cwd = process.cwd()) {
233
259
  return join(dirFor(cwd), `${id}${META_SUFFIX}`);
234
260
  }
235
261
  function readMeta(id, cwd = process.cwd()) {
236
- const path5 = metaPath(id, cwd);
237
- if (!existsSync(path5)) return null;
262
+ const path6 = metaPath(id, cwd);
263
+ if (!existsSync(path6)) return null;
238
264
  try {
239
- return JSON.parse(readFileSync(path5, "utf8"));
265
+ return JSON.parse(readFileSync(path6, "utf8"));
240
266
  } catch {
241
267
  return null;
242
268
  }
243
269
  }
244
270
  function writeSessionMeta(meta, cwd = process.cwd()) {
245
271
  ensureDir(dirFor(cwd));
246
- writeFileSync(metaPath(meta.id, cwd), JSON.stringify(meta, null, 2));
272
+ const path6 = metaPath(meta.id, cwd);
273
+ writeFileSync(path6, JSON.stringify(meta, null, 2), { mode: 384 });
274
+ chmodSync(path6, 384);
247
275
  }
248
276
  function derivePreview(messages) {
249
277
  const firstUser = messages.find((m) => m.role === "user" && m.content.trim());
@@ -445,7 +473,7 @@ var HookDispatcher = class {
445
473
  result = await handler.run(current);
446
474
  } catch (err) {
447
475
  this.onError(handler, err);
448
- continue;
476
+ return { payload: current, blocked: true, reason: "hook failed" };
449
477
  }
450
478
  if (result.decision === "block") {
451
479
  return { payload: current, blocked: true, reason: result.reason };
@@ -462,6 +490,7 @@ var HookDispatcher = class {
462
490
  return handler.matcher.test(String(payload.tool ?? payload.prompt ?? ""));
463
491
  }
464
492
  };
493
+ var MAX_HOOK_STDOUT_BYTES = 64 * 1024;
465
494
  function configHooksToHandlers(config) {
466
495
  if (!config) return [];
467
496
  const handlers = [];
@@ -491,8 +520,26 @@ function runShellHook(entry, payload) {
491
520
  detached: true
492
521
  });
493
522
  let stdout = "";
523
+ let stdoutBytes = 0;
494
524
  child.stdout.on("data", (chunk) => {
495
- stdout += chunk.toString();
525
+ const text = chunk.toString();
526
+ const bytes = Buffer.byteLength(text);
527
+ if (stdoutBytes + bytes > MAX_HOOK_STDOUT_BYTES) {
528
+ if (child.pid !== void 0) {
529
+ try {
530
+ process.kill(-child.pid, "SIGKILL");
531
+ } catch {
532
+ }
533
+ }
534
+ try {
535
+ child.kill("SIGKILL");
536
+ } catch {
537
+ }
538
+ settle({ decision: "block", reason: `shell hook output exceeded ${MAX_HOOK_STDOUT_BYTES} bytes` });
539
+ return;
540
+ }
541
+ stdoutBytes += bytes;
542
+ stdout += text;
496
543
  });
497
544
  const timer = setTimeout(() => {
498
545
  if (child.pid !== void 0) {
@@ -524,9 +571,13 @@ function runShellHook(entry, payload) {
524
571
  }
525
572
  try {
526
573
  const parsed = JSON.parse(trimmed);
527
- settle(parsed);
574
+ if (!parsed || typeof parsed !== "object" || !("decision" in parsed) || !["continue", "block", "modify"].includes(String(parsed.decision))) {
575
+ settle({ decision: "block", reason: "shell hook returned an invalid decision" });
576
+ } else {
577
+ settle(parsed);
578
+ }
528
579
  } catch {
529
- settle({ decision: "continue" });
580
+ settle({ decision: "block", reason: "shell hook returned invalid JSON" });
530
581
  }
531
582
  });
532
583
  child.on("error", (err) => {
@@ -980,8 +1031,9 @@ function ChatApp(options) {
980
1031
  } = useRuntime(options);
981
1032
  const mergedTools = useMemo(() => {
982
1033
  const extra = options.extraTools ?? [];
983
- return [...tools, ...extra];
984
- }, [tools, options.extraTools]);
1034
+ const combined = [...tools, ...extra];
1035
+ return options.permissionPolicy ? applyPolicyToTools(options.permissionPolicy, combined) : combined;
1036
+ }, [tools, options.extraTools, options.permissionPolicy]);
985
1037
  const mergedSkills = useMemo(() => {
986
1038
  const extra = options.extraSkills ?? [];
987
1039
  if (!skills && extra.length === 0) return void 0;
@@ -1175,7 +1227,7 @@ async function loadPlugins(options = {}) {
1175
1227
  specs = [],
1176
1228
  pluginDirs = [],
1177
1229
  cwd = process.cwd(),
1178
- autoDiscoverUserDir = true,
1230
+ autoDiscoverUserDir = false,
1179
1231
  onError = (spec, err) => process.stderr.write(
1180
1232
  `[agentskit] plugin "${spec}" failed to load: ${err instanceof Error ? err.message : String(err)}
1181
1233
  `
@@ -1266,6 +1318,8 @@ function mergePluginsIntoBundle(plugins) {
1266
1318
  }
1267
1319
  return bundle;
1268
1320
  }
1321
+ var MAX_FRAME_BYTES = 1024 * 1024;
1322
+ var DISPOSE_GRACE_MS = 1e3;
1269
1323
  var McpClient = class {
1270
1324
  constructor(spec, onError = (err) => process.stderr.write(
1271
1325
  `[agentskit] mcp[${spec.name}] error: ${err instanceof Error ? err.message : String(err)}
@@ -1282,7 +1336,8 @@ var McpClient = class {
1282
1336
  if (this.child) return;
1283
1337
  const child = spawn(this.spec.command, this.spec.args ?? [], {
1284
1338
  env: { ...process.env, ...this.spec.env ?? {} },
1285
- stdio: ["pipe", "pipe", "pipe"]
1339
+ stdio: ["pipe", "pipe", "pipe"],
1340
+ detached: process.platform !== "win32"
1286
1341
  });
1287
1342
  this.child = child;
1288
1343
  child.stdout.on("data", (chunk) => this.onStdout(chunk.toString()));
@@ -1314,7 +1369,22 @@ var McpClient = class {
1314
1369
  dispose() {
1315
1370
  if (this.disposed) return;
1316
1371
  this.disposed = true;
1317
- this.child?.kill("SIGTERM");
1372
+ for (const pending of this.pending.values()) {
1373
+ pending({ jsonrpc: "2.0", id: 0, error: { code: -32800, message: "client disposed" } });
1374
+ }
1375
+ this.pending.clear();
1376
+ const child = this.child;
1377
+ if (!child || child.killed) return;
1378
+ const kill = (signal) => {
1379
+ try {
1380
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, signal);
1381
+ else child.kill(signal);
1382
+ } catch {
1383
+ }
1384
+ };
1385
+ kill("SIGTERM");
1386
+ const timer = setTimeout(() => kill("SIGKILL"), DISPOSE_GRACE_MS);
1387
+ timer.unref();
1318
1388
  }
1319
1389
  request(method, params) {
1320
1390
  return new Promise((resolvePromise, rejectPromise) => {
@@ -1342,10 +1412,20 @@ var McpClient = class {
1342
1412
  }
1343
1413
  onStdout(chunk) {
1344
1414
  this.buffer += chunk;
1415
+ if (Buffer.byteLength(this.buffer, "utf8") > MAX_FRAME_BYTES && !this.buffer.includes("\n")) {
1416
+ this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
1417
+ this.dispose();
1418
+ return;
1419
+ }
1345
1420
  let newlineIndex = this.buffer.indexOf("\n");
1346
1421
  while (newlineIndex !== -1) {
1347
1422
  const line = this.buffer.slice(0, newlineIndex).trim();
1348
1423
  this.buffer = this.buffer.slice(newlineIndex + 1);
1424
+ if (Buffer.byteLength(line, "utf8") > MAX_FRAME_BYTES) {
1425
+ this.onError(new Error(`mcp ${this.spec.name} frame exceeded ${MAX_FRAME_BYTES} bytes`));
1426
+ this.dispose();
1427
+ return;
1428
+ }
1349
1429
  if (line) {
1350
1430
  try {
1351
1431
  const parsed = JSON.parse(line);
@@ -1421,8 +1501,7 @@ function formatEvent(event) {
1421
1501
  }
1422
1502
  async function runAgent(task, options) {
1423
1503
  if (options.skill && options.skills) {
1424
- process.stderr.write("Error: --skill and --skills are mutually exclusive. Use one or the other.\n");
1425
- process.exit(1);
1504
+ throw new Error("--skill and --skills are mutually exclusive. Use one or the other.");
1426
1505
  }
1427
1506
  const { adapter } = resolveChatProvider({
1428
1507
  provider: options.provider,
@@ -1492,6 +1571,69 @@ var PROVIDER_ENV_KEY = {
1492
1571
  openrouter: "OPENROUTER_API_KEY",
1493
1572
  demo: null
1494
1573
  };
1574
+ var PACKAGE_RANGES = {
1575
+ "@agentskit/adapters": "^0.15.2",
1576
+ "@agentskit/angular": "^0.5.3",
1577
+ "@agentskit/ink": "^0.10.9",
1578
+ "@agentskit/memory": "^0.11.8",
1579
+ "@agentskit/react": "^0.8.3",
1580
+ "@agentskit/runtime": "^0.10.15",
1581
+ "@agentskit/skills": "^0.9.3",
1582
+ "@agentskit/svelte": "^0.5.3",
1583
+ "@agentskit/tools": "^0.13.6",
1584
+ "@agentskit/vue": "^0.5.3"
1585
+ };
1586
+ function align(files) {
1587
+ const result = { ...files };
1588
+ for (const file of ["package.json", "deno.json"]) {
1589
+ if (!files[file]) continue;
1590
+ const parsed = JSON.parse(files[file]);
1591
+ const sections = file === "package.json" ? ["dependencies", "devDependencies", "peerDependencies"] : ["imports"];
1592
+ for (const sectionName of sections) {
1593
+ const section = parsed[sectionName];
1594
+ if (!section || typeof section !== "object" || Array.isArray(section)) continue;
1595
+ for (const [name, range] of Object.entries(PACKAGE_RANGES)) if (name in section) {
1596
+ section[name] = file === "deno.json" ? `npm:${name}@${range}` : range;
1597
+ }
1598
+ }
1599
+ result[file] = `${JSON.stringify(parsed, null, 2)}
1600
+ `;
1601
+ }
1602
+ return result;
1603
+ }
1604
+ async function existing(filePath) {
1605
+ try {
1606
+ return await lstat(filePath);
1607
+ } catch (error) {
1608
+ if (error.code === "ENOENT") return void 0;
1609
+ throw error;
1610
+ }
1611
+ }
1612
+ async function writeStarterProject(options, renderers) {
1613
+ const ctx = { template: options.template, provider: options.provider ?? "demo", tools: options.tools ?? [], memory: options.memory ?? "none", pm: options.packageManager ?? "pnpm" };
1614
+ const files = align(renderers[ctx.template](ctx));
1615
+ await mkdir(options.targetDir, { recursive: true });
1616
+ const target = await lstat(options.targetDir);
1617
+ if (target.isSymbolicLink() || !target.isDirectory()) throw new Error(`Refusing to write starter into non-directory target: ${options.targetDir}`);
1618
+ if ((await 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)`);
1619
+ const overwritten = [];
1620
+ for (const [relativePath, content] of Object.entries(files)) {
1621
+ const absolutePath = path.join(options.targetDir, relativePath);
1622
+ await mkdir(path.dirname(absolutePath), { recursive: true });
1623
+ const destination = await existing(absolutePath);
1624
+ if (destination?.isSymbolicLink()) throw new Error(`Refusing to overwrite symlink: ${absolutePath}`);
1625
+ if (destination) overwritten.push(relativePath);
1626
+ const tempDir = await mkdtemp(path.join(path.dirname(absolutePath), `.agentskit-init-${randomUUID()}-`));
1627
+ try {
1628
+ await writeFile(path.join(tempDir, path.basename(absolutePath)), content, "utf8");
1629
+ await rename(path.join(tempDir, path.basename(absolutePath)), absolutePath);
1630
+ } finally {
1631
+ await rm(tempDir, { recursive: true, force: true }).catch(() => {
1632
+ });
1633
+ }
1634
+ }
1635
+ return overwritten;
1636
+ }
1495
1637
 
1496
1638
  // src/init.ts
1497
1639
  function adapterCall(provider, prefix = "process.env") {
@@ -2877,23 +3019,8 @@ var TEMPLATE_FN = {
2877
3019
  "deno-deploy": denoDeployStarter,
2878
3020
  angular: angularStarter
2879
3021
  };
2880
- async function writeStarterProject(options) {
2881
- const ctx = {
2882
- template: options.template,
2883
- provider: options.provider ?? "demo",
2884
- tools: options.tools ?? [],
2885
- memory: options.memory ?? "none",
2886
- pm: options.packageManager ?? "pnpm"
2887
- };
2888
- const files = TEMPLATE_FN[ctx.template](ctx);
2889
- await mkdir(options.targetDir, { recursive: true });
2890
- await Promise.all(
2891
- Object.entries(files).map(async ([relativePath, content]) => {
2892
- const absolutePath = path.join(options.targetDir, relativePath);
2893
- await mkdir(path.dirname(absolutePath), { recursive: true });
2894
- await writeFile(absolutePath, content, "utf8");
2895
- })
2896
- );
3022
+ async function writeStarterProject2(options) {
3023
+ return writeStarterProject(options, TEMPLATE_FN);
2897
3024
  }
2898
3025
 
2899
3026
  // src/provider-registry.ts
@@ -3140,8 +3267,8 @@ async function checkPnpm() {
3140
3267
  };
3141
3268
  }
3142
3269
  async function checkPackageJson() {
3143
- const path5 = join(process.cwd(), "package.json");
3144
- if (!existsSync(path5)) {
3270
+ const path6 = join(process.cwd(), "package.json");
3271
+ if (!existsSync(path6)) {
3145
3272
  return {
3146
3273
  status: "warn",
3147
3274
  name: "package.json",
@@ -3150,7 +3277,7 @@ async function checkPackageJson() {
3150
3277
  };
3151
3278
  }
3152
3279
  try {
3153
- const pkg = JSON.parse(await readFile(path5, "utf8"));
3280
+ const pkg = JSON.parse(await readFile(path6, "utf8"));
3154
3281
  const deps = {
3155
3282
  ...pkg.dependencies ?? {},
3156
3283
  ...pkg.devDependencies ?? {}
@@ -3363,6 +3490,14 @@ function renderReport(report, opts = {}) {
3363
3490
  lines.push("");
3364
3491
  return lines.join("\n");
3365
3492
  }
3493
+ var require2 = createRequire(import.meta.url);
3494
+ function resolveTsxCli() {
3495
+ try {
3496
+ return require2.resolve("tsx/cli");
3497
+ } catch {
3498
+ throw new Error("TypeScript execution requires the packaged tsx dependency");
3499
+ }
3500
+ }
3366
3501
  var DEFAULT_WATCH = [
3367
3502
  "**/*.ts",
3368
3503
  "**/*.tsx",
@@ -3390,8 +3525,8 @@ function startDev(options) {
3390
3525
  const stderr = options.stderr ?? process.stderr;
3391
3526
  const debounceMs = options.debounceMs ?? 200;
3392
3527
  const isTs = entry.endsWith(".ts") || entry.endsWith(".tsx");
3393
- const cmd = isTs ? "tsx" : "node";
3394
- const baseArgs = [entry, ...options.scriptArgs ?? []];
3528
+ const cmd = isTs ? process.execPath : "node";
3529
+ const baseArgs = isTs ? [resolveTsxCli(), entry, ...options.scriptArgs ?? []] : [entry, ...options.scriptArgs ?? []];
3395
3530
  const spawnFn = options.spawn ?? ((c, a) => spawn(c, a, {
3396
3531
  stdio: ["inherit", "pipe", "pipe"],
3397
3532
  env: { ...process.env, FORCE_COLOR: "1" }
@@ -3403,7 +3538,9 @@ function startDev(options) {
3403
3538
  let child;
3404
3539
  let restartCount = 0;
3405
3540
  let restartTimer;
3541
+ let delayedStartTimer;
3406
3542
  let stopped = false;
3543
+ let stdinListener;
3407
3544
  let resolveDone;
3408
3545
  const done = new Promise((r) => {
3409
3546
  resolveDone = r;
@@ -3429,15 +3566,18 @@ function startDev(options) {
3429
3566
  }
3430
3567
  });
3431
3568
  };
3432
- const restart = (path5) => {
3569
+ const restart = (path6) => {
3433
3570
  if (restartTimer) clearTimeout(restartTimer);
3434
3571
  restartTimer = setTimeout(() => {
3435
3572
  restartTimer = void 0;
3436
- banner(`\u21BB change detected \u2014 ${path5}`, "yellow");
3573
+ banner(`\u21BB change detected \u2014 ${path6}`, "yellow");
3437
3574
  if (child && !child.killed && child.exitCode === null) {
3438
3575
  child.kill("SIGTERM");
3439
3576
  }
3440
- setTimeout(startChild, 80);
3577
+ delayedStartTimer = setTimeout(() => {
3578
+ delayedStartTimer = void 0;
3579
+ if (!stopped) startChild();
3580
+ }, 80);
3441
3581
  }, debounceMs);
3442
3582
  };
3443
3583
  watcher.on("change", restart);
@@ -3448,6 +3588,9 @@ function startDev(options) {
3448
3588
  if (stopped) return;
3449
3589
  stopped = true;
3450
3590
  if (restartTimer) clearTimeout(restartTimer);
3591
+ if (delayedStartTimer) clearTimeout(delayedStartTimer);
3592
+ if (stdinListener) process.stdin.off("data", stdinListener);
3593
+ if (process.stdin.isTTY && process.stdin.setRawMode) process.stdin.setRawMode(false);
3451
3594
  if (child && !child.killed && child.exitCode === null) {
3452
3595
  child.kill("SIGTERM");
3453
3596
  }
@@ -3458,11 +3601,12 @@ function startDev(options) {
3458
3601
  if (process.stdin.isTTY && process.stdin.setRawMode) {
3459
3602
  process.stdin.setRawMode(true);
3460
3603
  process.stdin.resume();
3461
- process.stdin.on("data", (data) => {
3604
+ stdinListener = (data) => {
3462
3605
  const key = data.toString();
3463
3606
  if (key === "r") restart("manual");
3464
3607
  if (key === "q" || key === "") void stop();
3465
- });
3608
+ };
3609
+ process.stdin.on("data", stdinListener);
3466
3610
  }
3467
3611
  return {
3468
3612
  done,
@@ -3488,6 +3632,7 @@ async function startTunnel(options) {
3488
3632
  });
3489
3633
  let requests = 0;
3490
3634
  let stopped = false;
3635
+ let signalHandler;
3491
3636
  let resolveDone;
3492
3637
  const done = new Promise((r) => {
3493
3638
  resolveDone = r;
@@ -3497,12 +3642,14 @@ async function startTunnel(options) {
3497
3642
  });
3498
3643
  tunnel.on("close", () => {
3499
3644
  if (stopped) return;
3645
+ if (signalHandler) process.off("SIGINT", signalHandler);
3500
3646
  banner(`tunnel closed by remote`, "yellow");
3501
3647
  resolveDone();
3502
3648
  });
3503
3649
  tunnel.on("error", (...args) => {
3504
3650
  const err = args[0];
3505
3651
  banner(`error: ${err?.message ?? "unknown"}`, "red");
3652
+ resolveDone();
3506
3653
  });
3507
3654
  banner(`\u2713 ready`, "green");
3508
3655
  stdout.write("\n");
@@ -3518,14 +3665,16 @@ async function startTunnel(options) {
3518
3665
  const stop = async () => {
3519
3666
  if (stopped) return;
3520
3667
  stopped = true;
3668
+ if (signalHandler) process.off("SIGINT", signalHandler);
3521
3669
  tunnel.close();
3522
3670
  banner(`stopped \u2014 proxied ${requests} request${requests === 1 ? "" : "s"}`, "cyan");
3523
3671
  resolveDone();
3524
3672
  };
3525
3673
  if (process.stdin.isTTY) {
3526
- process.on("SIGINT", () => {
3674
+ signalHandler = () => {
3527
3675
  void stop();
3528
- });
3676
+ };
3677
+ process.on("SIGINT", signalHandler);
3529
3678
  }
3530
3679
  return {
3531
3680
  url: tunnel.url,
@@ -3596,16 +3745,20 @@ async function indexSources(rag, config, cwd) {
3596
3745
  }
3597
3746
  }
3598
3747
  const documents = await Promise.all(
3599
- absolutePaths.map(async (path5) => ({
3600
- id: path5,
3601
- content: await readFile(path5, "utf8"),
3602
- source: path5
3748
+ absolutePaths.map(async (path6) => ({
3749
+ id: path6,
3750
+ content: await readFile(path6, "utf8"),
3751
+ source: path6
3603
3752
  }))
3604
3753
  );
3605
3754
  if (documents.length > 0) await rag.ingest(documents);
3606
3755
  return { documentCount: documents.length, sources: absolutePaths };
3607
3756
  }
3608
3757
 
3758
+ // package.json
3759
+ var package_default = {
3760
+ version: "0.13.39"};
3761
+
3609
3762
  // src/commands/shared.ts
3610
3763
  function mergeWithConfig(options, config) {
3611
3764
  if (!config) return options;
@@ -3631,7 +3784,7 @@ function registerChatCommand(program) {
3631
3784
  "Extra directory to auto-discover plugin modules from (repeatable)",
3632
3785
  (value, prev = []) => [...prev, value],
3633
3786
  []
3634
- ).option(
3787
+ ).option("--discover-plugins", "Also auto-discover plugins from ~/.agentskit/plugins").option(
3635
3788
  "--mode <mode>",
3636
3789
  "Permission mode: default | plan | acceptEdits | bypassPermissions"
3637
3790
  ).action(async (options) => {
@@ -3668,7 +3821,8 @@ function registerChatCommand(program) {
3668
3821
  }
3669
3822
  const pluginBundle = await loadPlugins({
3670
3823
  specs: config?.plugins ?? [],
3671
- pluginDirs: options.pluginDir ?? []
3824
+ pluginDirs: options.pluginDir ?? [],
3825
+ autoDiscoverUserDir: Boolean(options.discoverPlugins)
3672
3826
  });
3673
3827
  const configHooks = configHooksToHandlers(config?.hooks);
3674
3828
  const hookHandlers = [...configHooks, ...pluginBundle.hooks];
@@ -4003,7 +4157,7 @@ function printNextSteps(options) {
4003
4157
 
4004
4158
  // src/commands/init.ts
4005
4159
  function registerInitCommand(program) {
4006
- 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) => {
4160
+ 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) => {
4007
4161
  const isCi = !process.stdout.isTTY || rawOptions.yes || rawOptions.template;
4008
4162
  let resolved;
4009
4163
  if (isCi) {
@@ -4014,7 +4168,8 @@ function registerInitCommand(program) {
4014
4168
  provider: rawOptions.provider ?? "demo",
4015
4169
  tools: rawOptions.tools ? rawOptions.tools.split(",").map((t) => t.trim()) : [],
4016
4170
  memory: rawOptions.memory ?? "none",
4017
- packageManager: rawOptions.pm ?? "pnpm"
4171
+ packageManager: rawOptions.pm ?? "pnpm",
4172
+ force: Boolean(rawOptions.force)
4018
4173
  };
4019
4174
  } else {
4020
4175
  const result = await runInteractiveInit({
@@ -4024,17 +4179,22 @@ function registerInitCommand(program) {
4024
4179
  if (result.cancelled) {
4025
4180
  process.exit(0);
4026
4181
  }
4027
- resolved = result.options;
4182
+ resolved = { ...result.options, force: Boolean(rawOptions.force) };
4028
4183
  }
4029
- await writeStarterProject(resolved);
4184
+ const overwritten = await writeStarterProject2(resolved) ?? [];
4030
4185
  if (isCi) {
4031
4186
  process.stdout.write(
4032
4187
  `Created ${resolved.template} starter in ${path.relative(process.cwd(), resolved.targetDir) || "."}
4033
- \u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
4188
+ ` + (overwritten.length > 0 ? `Overwritten: ${overwritten.join(", ")}
4189
+ ` : "") + `\u2605 Star AgentsKit (solo-built \u2014 it helps a lot): https://github.com/AgentsKit-io/agentskit
4034
4190
  `
4035
4191
  );
4036
4192
  } else {
4037
4193
  printNextSteps(resolved);
4194
+ if (overwritten.length > 0) {
4195
+ process.stdout.write(`Overwritten: ${overwritten.join(", ")}
4196
+ `);
4197
+ }
4038
4198
  }
4039
4199
  });
4040
4200
  }
@@ -4090,7 +4250,7 @@ function registerConfigCommand(program) {
4090
4250
  const targetPath = isLocal ? path.join(process.cwd(), ".agentskit.config.json") : path.join(homedir(), ".agentskit", "config.json");
4091
4251
  if (action === "show") {
4092
4252
  const config = await loadConfig();
4093
- process.stdout.write(JSON.stringify(config ?? {}, null, 2) + "\n");
4253
+ process.stdout.write(JSON.stringify(redactConfig(config ?? {}), null, 2) + "\n");
4094
4254
  return;
4095
4255
  }
4096
4256
  if (action !== "init") {
@@ -4284,7 +4444,7 @@ function scaffoldAgent(schema) {
4284
4444
  return files;
4285
4445
  }
4286
4446
  async function writeScaffold(files, outDir, opts = {}) {
4287
- const { writeFile: writeFile5, mkdir: mkdir5, access } = await import('fs/promises');
4447
+ const { writeFile: writeFile5, mkdir: mkdir5, access: access2 } = await import('fs/promises');
4288
4448
  const { dirname: dirname5, join: join11 } = await import('path');
4289
4449
  const written = [];
4290
4450
  for (const f of files) {
@@ -4292,7 +4452,7 @@ async function writeScaffold(files, outDir, opts = {}) {
4292
4452
  await mkdir5(dirname5(full), { recursive: true });
4293
4453
  if (!opts.overwrite) {
4294
4454
  try {
4295
- await access(full);
4455
+ await access2(full);
4296
4456
  continue;
4297
4457
  } catch {
4298
4458
  }
@@ -4768,66 +4928,66 @@ After writing, summarise which files landed and what each one does.
4768
4928
  // src/rules.ts
4769
4929
  var CODEX_BLOCK_START = "<!-- agentskit-codex-profile:start -->";
4770
4930
  var CODEX_BLOCK_END = "<!-- agentskit-codex-profile:end -->";
4771
- async function ensureDir2(path5) {
4772
- await mkdir(dirname(path5), { recursive: true });
4931
+ async function ensureDir2(path6) {
4932
+ await mkdir(dirname(path6), { recursive: true });
4773
4933
  }
4774
4934
  async function writeIfChanged(absPath, contents, force) {
4775
- let existing;
4935
+ let existing2;
4776
4936
  try {
4777
- existing = await readFile(absPath, "utf8");
4937
+ existing2 = await readFile(absPath, "utf8");
4778
4938
  } catch {
4779
4939
  }
4780
- if (existing === contents) return "skipped";
4781
- if (existing !== void 0 && !force) {
4940
+ if (existing2 === contents) return "skipped";
4941
+ if (existing2 !== void 0 && !force) {
4782
4942
  return "skipped";
4783
4943
  }
4784
4944
  await ensureDir2(absPath);
4785
4945
  await writeFile(absPath, contents, "utf8");
4786
- return existing === void 0 ? "wrote" : "updated";
4946
+ return existing2 === void 0 ? "wrote" : "updated";
4787
4947
  }
4788
4948
  async function writeCursor(rootDir, force) {
4789
- const path5 = join(rootDir, ".cursor", "rules", "agentskit.mdc");
4790
- const action = await writeIfChanged(path5, CURSOR_RULE, force);
4791
- return [{ path: path5, action }];
4949
+ const path6 = join(rootDir, ".cursor", "rules", "agentskit.mdc");
4950
+ const action = await writeIfChanged(path6, CURSOR_RULE, force);
4951
+ return [{ path: path6, action }];
4792
4952
  }
4793
4953
  async function writeWindsurf(rootDir, force) {
4794
- const path5 = join(rootDir, ".windsurfrules");
4795
- const action = await writeIfChanged(path5, WINDSURF_RULE, force);
4796
- return [{ path: path5, action }];
4954
+ const path6 = join(rootDir, ".windsurfrules");
4955
+ const action = await writeIfChanged(path6, WINDSURF_RULE, force);
4956
+ return [{ path: path6, action }];
4797
4957
  }
4798
4958
  async function writeCodex(rootDir, force) {
4799
- const path5 = join(rootDir, "AGENTS.md");
4800
- let existing = "";
4959
+ const path6 = join(rootDir, "AGENTS.md");
4960
+ let existing2 = "";
4801
4961
  try {
4802
- existing = await readFile(path5, "utf8");
4962
+ existing2 = await readFile(path6, "utf8");
4803
4963
  } catch {
4804
4964
  }
4805
- const startIdx = existing.indexOf(CODEX_BLOCK_START);
4806
- const endIdx = startIdx >= 0 ? existing.lastIndexOf(CODEX_BLOCK_END, existing.length) : -1;
4965
+ const startIdx = existing2.indexOf(CODEX_BLOCK_START);
4966
+ const endIdx = startIdx >= 0 ? existing2.lastIndexOf(CODEX_BLOCK_END, existing2.length) : -1;
4807
4967
  if (startIdx >= 0 && endIdx <= startIdx) {
4808
4968
  throw new ConfigError({
4809
4969
  code: ErrorCodes.AK_CONFIG_INVALID,
4810
- message: `${path5}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
4970
+ message: `${path6}: found agentskit-codex-profile:start sentinel but no matching :end after it \u2014 refusing to modify a half-edited block.`,
4811
4971
  hint: "Restore the closing sentinel or remove the partial block, then re-run."
4812
4972
  });
4813
4973
  }
4814
4974
  let next;
4815
4975
  if (startIdx >= 0 && endIdx > startIdx) {
4816
- next = existing.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing.slice(endIdx + CODEX_BLOCK_END.length);
4817
- } else if (existing) {
4818
- next = `${existing.replace(/\s+$/, "")}
4976
+ next = existing2.slice(0, startIdx) + CODEX_PROFILE.trimEnd() + existing2.slice(endIdx + CODEX_BLOCK_END.length);
4977
+ } else if (existing2) {
4978
+ next = `${existing2.replace(/\s+$/, "")}
4819
4979
 
4820
4980
  ${CODEX_PROFILE}`;
4821
4981
  } else {
4822
4982
  next = CODEX_PROFILE;
4823
4983
  }
4824
- if (next === existing) return [{ path: path5, action: "skipped" }];
4825
- if (existing && !force) {
4826
- return [{ path: path5, action: "skipped" }];
4984
+ if (next === existing2) return [{ path: path6, action: "skipped" }];
4985
+ if (existing2 && !force) {
4986
+ return [{ path: path6, action: "skipped" }];
4827
4987
  }
4828
- await ensureDir2(path5);
4829
- await writeFile(path5, next, "utf8");
4830
- return [{ path: path5, action: existing ? "updated" : "wrote" }];
4988
+ await ensureDir2(path6);
4989
+ await writeFile(path6, next, "utf8");
4990
+ return [{ path: path6, action: existing2 ? "updated" : "wrote" }];
4831
4991
  }
4832
4992
  async function writeClaudeCode(rootDir, force) {
4833
4993
  const out = [];
@@ -4915,17 +5075,78 @@ rules failed: ${message}
4915
5075
  }
4916
5076
  var RAW_BASE = "https://raw.githubusercontent.com/AgentsKit-io/agentskit-registry/main";
4917
5077
  var HOSTED_BASE = `${RAW_BASE}/public/r`;
5078
+ var FETCH_TIMEOUT_MS = 15e3;
5079
+ var MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
5080
+ var MAX_AGENT_FILES = 100;
5081
+ var SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
5082
+ function assertSafeId(id) {
5083
+ if (!SAFE_ID.test(id)) throw new Error(`Invalid registry agent id: ${JSON.stringify(id)}`);
5084
+ }
5085
+ function assertSafeRelativePath(filePath) {
5086
+ if (!filePath || isAbsolute(filePath) || filePath.includes("\0")) {
5087
+ throw new Error(`Invalid registry file path: ${JSON.stringify(filePath)}`);
5088
+ }
5089
+ const normalized = filePath.replace(/\\/g, "/");
5090
+ if (normalized.split("/").some((part) => part === ".." || part === "")) {
5091
+ throw new Error(`Registry file path must stay relative: ${JSON.stringify(filePath)}`);
5092
+ }
5093
+ }
5094
+ function assertInside(parent, child) {
5095
+ const rel = relative(resolve(parent), resolve(child));
5096
+ if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
5097
+ throw new Error(`Registry destination escapes target directory: ${child}`);
5098
+ }
5099
+ }
5100
+ async function assertNoSymlinkAncestors(targetDir, baseDir) {
5101
+ for (const path6 of [targetDir, baseDir]) {
5102
+ try {
5103
+ if ((await lstat(resolve(path6))).isSymbolicLink()) {
5104
+ throw new Error(`Registry destination contains a symlink ancestor: ${resolve(path6)}`);
5105
+ }
5106
+ } catch (error) {
5107
+ if (error.code !== "ENOENT") throw error;
5108
+ }
5109
+ }
5110
+ let current = resolve(baseDir);
5111
+ while (true) {
5112
+ try {
5113
+ if ((await lstat(current)).isSymbolicLink()) {
5114
+ throw new Error(`Registry destination contains a symlink ancestor: ${current}`);
5115
+ }
5116
+ return;
5117
+ } catch (error) {
5118
+ if (error.code !== "ENOENT") throw error;
5119
+ }
5120
+ const parent = dirname(current);
5121
+ if (parent === current) return;
5122
+ current = parent;
5123
+ }
5124
+ }
5125
+ async function fetchWithLimits(url, fetchImpl) {
5126
+ const controller = new AbortController();
5127
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
5128
+ try {
5129
+ const response = await fetchImpl(url, { signal: controller.signal });
5130
+ if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
5131
+ const body = await response.arrayBuffer();
5132
+ if (body.byteLength > MAX_RESPONSE_BYTES) {
5133
+ throw new Error(`Registry response exceeded ${MAX_RESPONSE_BYTES} bytes`);
5134
+ }
5135
+ return new Response(body, { status: response.status, headers: response.headers });
5136
+ } finally {
5137
+ clearTimeout(timer);
5138
+ }
5139
+ }
4918
5140
  async function getJson(url, fetchImpl) {
4919
- const res = await fetchImpl(url);
4920
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
4921
- return res.json();
5141
+ const res = await fetchWithLimits(url, fetchImpl);
5142
+ return JSON.parse(await res.text());
4922
5143
  }
4923
5144
  async function getText(url, fetchImpl) {
4924
- const res = await fetchImpl(url);
4925
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
5145
+ const res = await fetchWithLimits(url, fetchImpl);
4926
5146
  return res.text();
4927
5147
  }
4928
5148
  async function fetchAgent(id, options = {}) {
5149
+ assertSafeId(id);
4929
5150
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
4930
5151
  try {
4931
5152
  const hosted = await getJson(`${HOSTED_BASE}/${id}.json`, fetchImpl);
@@ -4933,6 +5154,10 @@ async function fetchAgent(id, options = {}) {
4933
5154
  throw new Error("hosted entry missing sources");
4934
5155
  } catch {
4935
5156
  const meta = await getJson(`${RAW_BASE}/registry/${id}/meta.json`, fetchImpl);
5157
+ if (!Array.isArray(meta.files) || meta.files.length > MAX_AGENT_FILES) {
5158
+ throw new Error(`Registry agent contains too many files (maximum ${MAX_AGENT_FILES})`);
5159
+ }
5160
+ for (const rel of meta.files) assertSafeRelativePath(rel);
4936
5161
  const sources = await Promise.all(
4937
5162
  meta.files.map(async (rel) => ({
4938
5163
  path: rel,
@@ -4949,10 +5174,10 @@ function resolveSystemPrompt(agent) {
4949
5174
  const m = src.match(/systemPrompt:\s*`((?:\\.|[^`\\])*)`/);
4950
5175
  return m ? m[1].replace(/\\`/g, "`").replace(/\\\$\{/g, "${") : null;
4951
5176
  }
4952
- async function defaultExists(path5) {
4953
- const { access } = await import('fs/promises');
5177
+ async function defaultExists(path6) {
5178
+ const { access: access2 } = await import('fs/promises');
4954
5179
  try {
4955
- await access(path5);
5180
+ await access2(path6);
4956
5181
  return true;
4957
5182
  } catch {
4958
5183
  return false;
@@ -4976,17 +5201,24 @@ async function addAgent(id, options = {}) {
4976
5201
  assertInstallable(agent);
4977
5202
  const baseDir = options.outDir ?? "agents";
4978
5203
  const targetDir = join(baseDir, id);
5204
+ assertInside(baseDir, targetDir);
5205
+ await assertNoSymlinkAncestors(targetDir, baseDir);
4979
5206
  const exists = options.existsImpl ?? defaultExists;
4980
- const write = options.writeFileImpl ?? (async (path5, content) => {
4981
- await mkdir(dirname(path5), { recursive: true });
4982
- await writeFile(path5, content, "utf8");
5207
+ const write = options.writeFileImpl ?? (async (path6, content) => {
5208
+ await mkdir(dirname(path6), { recursive: true });
5209
+ await writeFile(path6, content, "utf8");
4983
5210
  });
4984
5211
  const written = [];
4985
5212
  for (const file of agent.sources) {
5213
+ assertSafeRelativePath(file.path);
4986
5214
  const dest = join(targetDir, file.path);
5215
+ assertInside(targetDir, dest);
4987
5216
  if (!options.force && await exists(dest)) {
4988
5217
  throw new Error(`${dest} already exists (re-run with --force to overwrite)`);
4989
5218
  }
5219
+ }
5220
+ for (const file of agent.sources) {
5221
+ const dest = join(targetDir, file.path);
4990
5222
  await write(dest, file.content);
4991
5223
  written.push(dest);
4992
5224
  }
@@ -5049,8 +5281,8 @@ function allDeps(pkg) {
5049
5281
  if (!pkg) return {};
5050
5282
  return { ...pkg.peerDependencies, ...pkg.devDependencies, ...pkg.dependencies };
5051
5283
  }
5052
- function readJson(fs, path5) {
5053
- const raw = fs.readFile(path5);
5284
+ function readJson(fs, path6) {
5285
+ const raw = fs.readFile(path6);
5054
5286
  if (raw == null) return null;
5055
5287
  try {
5056
5288
  return JSON.parse(raw);
@@ -5234,13 +5466,13 @@ function readConfig(io, root = ".") {
5234
5466
  return raw == null ? null : parseConfig(raw);
5235
5467
  }
5236
5468
  function writeConfig(io, config, root = ".") {
5237
- const path5 = join(root, CONFIG_PATH);
5238
- io.write(path5, serializeConfig(config));
5239
- return path5;
5469
+ const path6 = join(root, CONFIG_PATH);
5470
+ io.write(path6, serializeConfig(config));
5471
+ return path6;
5240
5472
  }
5241
5473
  function resolveConfig(io, scan, root = ".") {
5242
- const existing = readConfig(io, root);
5243
- if (existing) return { config: existing, fromDisk: true };
5474
+ const existing2 = readConfig(io, root);
5475
+ if (existing2) return { config: existing2, fromDisk: true };
5244
5476
  return { config: buildConfig(scan), fromDisk: false };
5245
5477
  }
5246
5478
  var IntegrityError = class extends Error {
@@ -5325,7 +5557,18 @@ function resolveAuthHeader(base, config, env = {}) {
5325
5557
  const map = config?.registryAuth;
5326
5558
  if (!map) return {};
5327
5559
  for (const [registryBase, envVar] of Object.entries(map)) {
5328
- if (base.startsWith(registryBase)) {
5560
+ let configured;
5561
+ let target;
5562
+ try {
5563
+ configured = new URL(registryBase);
5564
+ target = new URL(base);
5565
+ } catch {
5566
+ continue;
5567
+ }
5568
+ const configuredPath = configured.pathname.replace(/\/+$/, "") || "/";
5569
+ const targetPath = target.pathname.replace(/\/+$/, "") || "/";
5570
+ const samePathBoundary = configuredPath === "/" || targetPath === configuredPath || targetPath.startsWith(`${configuredPath}/`);
5571
+ if (target.origin === configured.origin && samePathBoundary) {
5329
5572
  const token = env[envVar];
5330
5573
  if (token) return { authorization: `Bearer ${token}` };
5331
5574
  }
@@ -5344,8 +5587,8 @@ function verifyChecksums(files) {
5344
5587
  function manifestUrl(ref) {
5345
5588
  return `${ref.base}/r/${ref.itemId}.json`;
5346
5589
  }
5347
- function fileUrl(ref, path5) {
5348
- return `${ref.base}/r/${ref.itemId}/${path5}`;
5590
+ function fileUrl(ref, path6) {
5591
+ return `${ref.base}/r/${ref.itemId}/${path6}`;
5349
5592
  }
5350
5593
  async function getText2(fetchImpl, url, headers) {
5351
5594
  const res = await fetchImpl(url, { headers });
@@ -5682,44 +5925,44 @@ async function addComponent(options, deps) {
5682
5925
  }
5683
5926
  function nodeScanFs() {
5684
5927
  return {
5685
- readFile(path5) {
5928
+ readFile(path6) {
5686
5929
  try {
5687
- return readFileSync(path5, "utf8");
5930
+ return readFileSync(path6, "utf8");
5688
5931
  } catch {
5689
5932
  return null;
5690
5933
  }
5691
5934
  },
5692
- exists(path5) {
5693
- return existsSync(path5);
5935
+ exists(path6) {
5936
+ return existsSync(path6);
5694
5937
  }
5695
5938
  };
5696
5939
  }
5697
5940
  function nodeWriteFs() {
5698
5941
  return {
5699
- exists(path5) {
5700
- return existsSync(path5);
5942
+ exists(path6) {
5943
+ return existsSync(path6);
5701
5944
  },
5702
- write(path5, content) {
5703
- mkdirSync(dirname(path5), { recursive: true });
5704
- writeFileSync(path5, content, "utf8");
5945
+ write(path6, content) {
5946
+ mkdirSync(dirname(path6), { recursive: true });
5947
+ writeFileSync(path6, content, "utf8");
5705
5948
  },
5706
- remove(path5) {
5707
- rmSync(path5, { force: true });
5949
+ remove(path6) {
5950
+ rmSync(path6, { force: true });
5708
5951
  }
5709
5952
  };
5710
5953
  }
5711
5954
  function nodeConfigIo() {
5712
5955
  return {
5713
- read(path5) {
5956
+ read(path6) {
5714
5957
  try {
5715
- return readFileSync(path5, "utf8");
5958
+ return readFileSync(path6, "utf8");
5716
5959
  } catch {
5717
5960
  return null;
5718
5961
  }
5719
5962
  },
5720
- write(path5, content) {
5721
- mkdirSync(dirname(path5), { recursive: true });
5722
- writeFileSync(path5, content, "utf8");
5963
+ write(path6, content) {
5964
+ mkdirSync(dirname(path6), { recursive: true });
5965
+ writeFileSync(path6, content, "utf8");
5723
5966
  }
5724
5967
  };
5725
5968
  }
@@ -5939,7 +6182,7 @@ update failed: ${err instanceof Error ? err.message : String(err)}
5939
6182
  // src/commands/index.ts
5940
6183
  function createCli() {
5941
6184
  const program = new Command();
5942
- program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.");
6185
+ program.name("agentskit").description("AgentsKit CLI for chat demos and project bootstrapping.").version(package_default.version);
5943
6186
  registerChatCommand(program);
5944
6187
  registerRunCommand(program);
5945
6188
  registerInitCommand(program);
@@ -5958,6 +6201,6 @@ function createCli() {
5958
6201
  return program;
5959
6202
  }
5960
6203
 
5961
- export { ChatApp, HookDispatcher, McpClient, applyPolicyToTool, applyPolicyToTools, bridgeMcpServers, buildRagFromConfig, computeCost, configHooksToHandlers, createCli, createOpenAiEmbedder, defaultPolicy, derivePreview, disposeMcpClients, evaluatePolicy, findLatestSession, findSession, forkSession, generateSessionId, getPricing, indexSources, listSessions, loadConfig, loadPlugins, mergePluginsIntoBundle, registerPricing, renameSession, renderChatHeader, renderReport, resolveChatProvider, resolveSession, runAgent, runDoctor, sessionFilePath, startDev, startTunnel, writeSessionMeta, writeStarterProject };
5962
- //# sourceMappingURL=chunk-DZKCRKFS.js.map
5963
- //# sourceMappingURL=chunk-DZKCRKFS.js.map
6204
+ export { ChatApp, HookDispatcher, McpClient, applyPolicyToTool, applyPolicyToTools, bridgeMcpServers, buildRagFromConfig, computeCost, configHooksToHandlers, createCli, createOpenAiEmbedder, defaultPolicy, derivePreview, disposeMcpClients, evaluatePolicy, findLatestSession, findSession, forkSession, generateSessionId, getPricing, indexSources, listSessions, loadConfig, loadPlugins, mergePluginsIntoBundle, registerPricing, renameSession, renderChatHeader, renderReport, resolveChatProvider, resolveSession, runAgent, runDoctor, sessionFilePath, startDev, startTunnel, writeSessionMeta, writeStarterProject2 as writeStarterProject };
6205
+ //# sourceMappingURL=chunk-AL4GKGCU.js.map
6206
+ //# sourceMappingURL=chunk-AL4GKGCU.js.map