@sunasteriskrnd/takumi 1.0.0-dev.43 → 1.0.0-dev.45

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.
Files changed (2) hide show
  1. package/dist/index.js +1286 -135
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19815,7 +19815,7 @@ var package_default;
19815
19815
  var init_package = __esm(() => {
19816
19816
  package_default = {
19817
19817
  name: "@sunasteriskrnd/takumi",
19818
- version: "1.0.0-dev.43",
19818
+ version: "1.0.0-dev.45",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -49807,6 +49807,73 @@ var init_artifact_command_help = __esm(() => {
49807
49807
  };
49808
49808
  });
49809
49809
 
49810
+ // src/domains/help/commands/mcp-command-help.ts
49811
+ var mcpCommandHelp;
49812
+ var init_mcp_command_help = __esm(() => {
49813
+ mcpCommandHelp = {
49814
+ name: "mcp",
49815
+ description: "Manage internal MCP connectors for coding agents (add|list|remove)",
49816
+ usage: "tkm mcp <add|list|remove> [service] [options] [-- <extra args...>]",
49817
+ examples: [
49818
+ {
49819
+ command: "tkm mcp list",
49820
+ description: "Show available internal MCP services and per-agent configured state"
49821
+ },
49822
+ {
49823
+ command: "tkm mcp add meet-plus",
49824
+ description: "Register the 'meet-plus' MCP with the current project's coding agent(s)"
49825
+ },
49826
+ {
49827
+ command: "tkm mcp add playwright -- --browser msedge --headless",
49828
+ description: "Add a stdio MCP with extra launch args appended after the registry defaults"
49829
+ }
49830
+ ],
49831
+ optionGroups: [
49832
+ {
49833
+ title: "Actions",
49834
+ options: [
49835
+ {
49836
+ flags: "add <service> [-- <extra args...>]",
49837
+ description: "Add a registry service to detected agent(s); argv after -- is appended to a stdio service's args (e.g. -- --browser msedge)"
49838
+ },
49839
+ {
49840
+ flags: "list",
49841
+ description: "List registry services with per-agent configured status"
49842
+ },
49843
+ {
49844
+ flags: "remove <service>",
49845
+ description: "Remove a service from detected agent(s); absent entries are skipped"
49846
+ }
49847
+ ]
49848
+ },
49849
+ {
49850
+ title: "Options",
49851
+ options: [
49852
+ {
49853
+ flags: "-a, --agent <agents...>",
49854
+ description: "Target specific agent(s): claude-code | codex (repeatable)"
49855
+ },
49856
+ {
49857
+ flags: "-s, --scope <scope>",
49858
+ description: "Config scope: local | user | project (default: user; agent-dependent)"
49859
+ },
49860
+ { flags: "-y, --yes", description: "Non-interactive mode: skip selection prompts" },
49861
+ { flags: "--json", description: "Machine-readable JSON output (list only)" }
49862
+ ]
49863
+ }
49864
+ ],
49865
+ sections: [
49866
+ {
49867
+ title: "Registry source",
49868
+ content: `Cache-first: an existing local cache (<TAKUMI_HOME>/mcp/registry-cache.json) is
49869
+ ` + `served immediately while a background request refreshes it for the next run.
49870
+ ` + `With no cache yet, the CLI fetches the Takumi server directly and reports an
49871
+ ` + "error when the server is unreachable (no stale in-package fallback)."
49872
+ }
49873
+ ]
49874
+ };
49875
+ });
49876
+
49810
49877
  // src/domains/help/commands/index.ts
49811
49878
  var init_commands2 = __esm(() => {
49812
49879
  init_init_command_help();
@@ -49817,6 +49884,7 @@ var init_commands2 = __esm(() => {
49817
49884
  init_config_command_help();
49818
49885
  init_auth_command_help();
49819
49886
  init_artifact_command_help();
49887
+ init_mcp_command_help();
49820
49888
  init_common_options();
49821
49889
  });
49822
49890
 
@@ -49836,7 +49904,8 @@ var init_help_commands = __esm(() => {
49836
49904
  doctor: doctorCommandHelp,
49837
49905
  uninstall: uninstallCommandHelp,
49838
49906
  auth: authCommandHelp,
49839
- artifact: artifactCommandHelp
49907
+ artifact: artifactCommandHelp,
49908
+ mcp: mcpCommandHelp
49840
49909
  };
49841
49910
  });
49842
49911
 
@@ -53485,6 +53554,7 @@ function tryOpenBrowser(target) {
53485
53554
  }
53486
53555
 
53487
53556
  // src/domains/sessions/server.ts
53557
+ init_logger();
53488
53558
  import * as http from "node:http";
53489
53559
 
53490
53560
  // src/domains/sessions/analytics.ts
@@ -53521,16 +53591,26 @@ function accumulateTokens(a3, b3) {
53521
53591
  a3.cacheRead += b3.cacheRead;
53522
53592
  a3.cacheWrite += b3.cacheWrite;
53523
53593
  }
53524
- function buildLast30(byDay, now) {
53594
+ function buildDenseWindow(byDay, startDayMs, endDayMs) {
53525
53595
  const out = [];
53526
- const todayMs = Date.parse(`${dayKey(now)}T00:00:00.000Z`);
53527
- for (let i = LAST_N_DAYS - 1;i >= 0; i -= 1) {
53528
- const date = dayKey(todayMs - i * DAY_MS);
53596
+ for (let ms = startDayMs;ms <= endDayMs; ms += DAY_MS) {
53597
+ const date = dayKey(ms);
53529
53598
  out.push({ date, count: byDay.get(date) ?? 0 });
53530
53599
  }
53531
53600
  return out;
53532
53601
  }
53533
- function foldAnalytics(aggregates, now) {
53602
+ function windowBounds(now, range) {
53603
+ if (!range) {
53604
+ const todayMs = Date.parse(`${dayKey(now)}T00:00:00.000Z`);
53605
+ return { start: todayMs - (LAST_N_DAYS - 1) * DAY_MS, end: todayMs };
53606
+ }
53607
+ const lo = Math.min(range.from, range.to);
53608
+ const hi = Math.max(range.from, range.to);
53609
+ const end = Date.parse(`${dayKey(hi)}T00:00:00.000Z`);
53610
+ const start = Math.max(Date.parse(`${dayKey(lo)}T00:00:00.000Z`), end - (PER_DAY_MAX - 1) * DAY_MS);
53611
+ return { start, end };
53612
+ }
53613
+ function foldAnalytics(aggregates, now, range) {
53534
53614
  const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
53535
53615
  const eventsByDay = new Map;
53536
53616
  const tokensByDow = new Map;
@@ -53585,7 +53665,8 @@ function foldAnalytics(aggregates, now) {
53585
53665
  const perDay = [...eventsByDay.entries()].filter(([date]) => Date.parse(`${date}T00:00:00.000Z`) >= oldestAllowedMs).map(([date, count]) => ({ date, count })).sort((a3, b3) => a3.date.localeCompare(b3.date));
53586
53666
  const byProjectAll = [...tokensByProject.entries()].map(([label, tokens]) => ({ label, tokens })).sort((a3, b3) => b3.tokens - a3.tokens || a3.label.localeCompare(b3.label));
53587
53667
  const byProject = byProjectAll.slice(0, TOP_PROJECTS);
53588
- const daily = buildLast30(tokensByDay, now);
53668
+ const { start: windowStart, end: windowEnd } = windowBounds(now, range);
53669
+ const daily = buildDenseWindow(tokensByDay, windowStart, windowEnd);
53589
53670
  const rankedModels = [...tokensByModel.entries()].map(([label, tokens]) => ({ label, tokens, total: sumTokens(tokens) })).filter((m2) => m2.total > 0).sort((a3, b3) => b3.total - a3.total || a3.label.localeCompare(b3.label)).slice(0, TOP_PROJECTS);
53590
53671
  const byModel = rankedModels.map(({ label, total }) => ({ label, tokens: total }));
53591
53672
  const byModelDetailed = rankedModels.map(({ label, tokens }) => ({ label, tokens }));
@@ -53602,7 +53683,7 @@ function foldAnalytics(aggregates, now) {
53602
53683
  events: {
53603
53684
  total: eventTotal,
53604
53685
  perDay,
53605
- last30: buildLast30(eventsByDay, now)
53686
+ last30: buildDenseWindow(eventsByDay, windowStart, windowEnd)
53606
53687
  },
53607
53688
  tokens: {
53608
53689
  byType: { ...totals },
@@ -54912,6 +54993,24 @@ function invalidate() {
54912
54993
  }
54913
54994
  }
54914
54995
 
54996
+ // src/domains/sessions/session-meta-filter.ts
54997
+ function filterSessionMetas(metas, filter) {
54998
+ return metas.filter((m2) => {
54999
+ if (filter.project !== undefined && m2.project !== filter.project)
55000
+ return false;
55001
+ if (filter.from === undefined && filter.to === undefined)
55002
+ return true;
55003
+ const ms = m2.startedAt === null ? Number.NaN : Date.parse(m2.startedAt);
55004
+ if (Number.isNaN(ms))
55005
+ return false;
55006
+ if (filter.from !== undefined && ms < filter.from)
55007
+ return false;
55008
+ if (filter.to !== undefined && ms > filter.to)
55009
+ return false;
55010
+ return true;
55011
+ });
55012
+ }
55013
+
54915
55014
  // src/domains/sessions/store/ingest.ts
54916
55015
  import { createHash as createHash9 } from "node:crypto";
54917
55016
  import { closeSync as closeSync4, openSync as openSync4, readSync as readSync4, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
@@ -64676,6 +64775,32 @@ async function readStoreAggregates(db) {
64676
64775
  get2(r2.session_id).agentCount += Number(r2.n ?? 0);
64677
64776
  return map;
64678
64777
  }
64778
+ async function attachSessionTokenTotals(db, list2) {
64779
+ const totals = await readSessionTokenTotals(db);
64780
+ return list2.map((s) => {
64781
+ const tokens = totals.get(s.id);
64782
+ return tokens ? { ...s, tokens } : s;
64783
+ });
64784
+ }
64785
+ async function readSessionTokenTotals(db) {
64786
+ const rows = await db.selectFrom("event").where("type", "=", "tokens").select((eb) => [
64787
+ "session_id",
64788
+ eb.fn.sum("input").as("input"),
64789
+ eb.fn.sum("output").as("output"),
64790
+ eb.fn.sum("cache_read").as("cache_read"),
64791
+ eb.fn.sum("cache_write").as("cache_write")
64792
+ ]).groupBy("session_id").execute();
64793
+ const map = new Map;
64794
+ for (const r2 of rows) {
64795
+ map.set(r2.session_id, {
64796
+ input: Number(r2.input ?? 0),
64797
+ output: Number(r2.output ?? 0),
64798
+ cacheCreate: Number(r2.cache_write ?? 0),
64799
+ cacheRead: Number(r2.cache_read ?? 0)
64800
+ });
64801
+ }
64802
+ return map;
64803
+ }
64679
64804
  function buildAggregatesFromStore(sessions, store) {
64680
64805
  return sessions.map((s) => {
64681
64806
  const a3 = store.get(s.id) ?? emptyStoreAggregate();
@@ -64692,30 +64817,52 @@ function buildAggregatesFromStore(sessions, store) {
64692
64817
  }
64693
64818
 
64694
64819
  // src/domains/sessions/analytics-source.ts
64695
- var cache2 = null;
64820
+ var DAY_MS2 = 24 * 60 * 60 * 1000;
64821
+ var DEFAULT_WINDOW_DAYS = 30;
64822
+ var cache2 = new Map;
64696
64823
  var storeHandle = null;
64697
64824
  function setStore(handle) {
64698
64825
  storeHandle = handle;
64699
64826
  }
64700
64827
  function invalidate2() {
64701
- cache2 = null;
64828
+ cache2 = new Map;
64829
+ }
64830
+ var cacheKey = (o2) => `${o2.from ?? ""}|${o2.to ?? ""}|${o2.project === undefined ? "" : `p:${o2.project}`}`;
64831
+ function rangeOf(o2, now) {
64832
+ if (o2.from === undefined && o2.to === undefined)
64833
+ return;
64834
+ const to = o2.to ?? now;
64835
+ const from = o2.from ?? to - (DEFAULT_WINDOW_DAYS - 1) * DAY_MS2;
64836
+ return { from, to };
64702
64837
  }
64703
- async function computeFromStore(handle, refresh2, now) {
64838
+ async function computeFromStore(handle, opts, now) {
64839
+ const refresh2 = opts.refresh ?? false;
64704
64840
  if (refresh2)
64705
64841
  await ingestAll(handle, { refresh: true });
64706
64842
  const sessions = await list({ refresh: refresh2 });
64843
+ const metas = filterSessionMetas(sessions.map((s) => ({ id: s.id, startedAt: s.startedAt, project: s.project })), { from: opts.from, to: opts.to, project: opts.project });
64707
64844
  const storeAggs = await readStoreAggregates(handle.db);
64708
- const metas = sessions.map((s) => ({ id: s.id, startedAt: s.startedAt, project: s.project }));
64709
- return foldAnalytics(buildAggregatesFromStore(metas, storeAggs), now);
64845
+ const payload = foldAnalytics(buildAggregatesFromStore(metas, storeAggs), now, rangeOf(opts, now));
64846
+ return {
64847
+ ...payload,
64848
+ filters: {
64849
+ from: opts.from ?? null,
64850
+ to: opts.to ?? null,
64851
+ project: opts.project ?? null
64852
+ }
64853
+ };
64710
64854
  }
64711
64855
  async function getAnalytics(opts = {}) {
64712
- if (cache2 && !opts.refresh)
64713
- return cache2;
64856
+ const key = cacheKey(opts);
64857
+ const hit = cache2.get(key);
64858
+ if (hit && !opts.refresh)
64859
+ return hit;
64714
64860
  if (!storeHandle)
64715
64861
  throw new StoreUnavailableError;
64716
64862
  const now = Date.now();
64717
- cache2 = await computeFromStore(storeHandle, opts.refresh ?? false, now);
64718
- return cache2;
64863
+ const payload = await computeFromStore(storeHandle, opts, now);
64864
+ cache2.set(key, payload);
64865
+ return payload;
64719
64866
  }
64720
64867
 
64721
64868
  // src/domains/sessions/origin-allowlist.ts
@@ -64743,13 +64890,33 @@ function isAllowedOrigin(origin) {
64743
64890
  return LOOPBACK_HOST_PATTERN.test(origin);
64744
64891
  }
64745
64892
 
64893
+ // src/domains/sessions/range-params.ts
64894
+ function intParam(search, key) {
64895
+ const raw = search.get(key);
64896
+ if (raw === null || raw === "")
64897
+ return;
64898
+ const n = Number(raw);
64899
+ if (!Number.isInteger(n) || n < 0)
64900
+ return { error: `invalid ${key}: ${raw}` };
64901
+ return n;
64902
+ }
64903
+ function parseRangeProjectParams(search) {
64904
+ const from = intParam(search, "from");
64905
+ if (typeof from === "object")
64906
+ return from;
64907
+ const to = intParam(search, "to");
64908
+ if (typeof to === "object")
64909
+ return to;
64910
+ return { from, to, project: search.get("project") || undefined };
64911
+ }
64912
+
64746
64913
  // src/domains/sessions/store/bridge-store.ts
64747
64914
  init_logger();
64748
64915
 
64749
64916
  // src/domains/sessions/store/prune.ts
64750
64917
  import { existsSync as existsSync34, statSync as statSync6 } from "node:fs";
64751
64918
  init_logger();
64752
- var DAY_MS2 = 24 * 60 * 60 * 1000;
64919
+ var DAY_MS3 = 24 * 60 * 60 * 1000;
64753
64920
  var DEFAULT_RETENTION_DAYS = 180;
64754
64921
  var SIZE_DELETE_BATCH = 500;
64755
64922
  var SIZE_PRUNE_MAX_ITERS = 200;
@@ -64775,7 +64942,7 @@ async function runPrune(handle, opts = {}) {
64775
64942
  const now = opts.now ?? Date.now();
64776
64943
  const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
64777
64944
  const dbPath = opts.dbPath ?? getConsoleStorePath();
64778
- const cutoff = now - retentionDays * DAY_MS2;
64945
+ const cutoff = now - retentionDays * DAY_MS3;
64779
64946
  await db.deleteFrom("event").where("ts", "<", cutoff).execute();
64780
64947
  if (opts.maxSizeMb && opts.maxSizeMb > 0) {
64781
64948
  const cap = opts.maxSizeMb * 1024 * 1024;
@@ -64953,6 +65120,17 @@ async function attachStore(deps = {}) {
64953
65120
  }
64954
65121
 
64955
65122
  // src/domains/sessions/server.ts
65123
+ var tokenStore = null;
65124
+ async function withTokenTotals(list2) {
65125
+ if (!tokenStore)
65126
+ return list2;
65127
+ try {
65128
+ return await attachSessionTokenTotals(tokenStore.db, list2);
65129
+ } catch (err) {
65130
+ logger.verbose(`console bridge: token totals enrichment failed (${String(err)})`);
65131
+ return list2;
65132
+ }
65133
+ }
64956
65134
  var PORT_BUMP_MAX = 9;
64957
65135
  function applyCorsHeaders(req, res) {
64958
65136
  const origin = req.headers.origin;
@@ -65010,15 +65188,20 @@ async function route(req, res, version3) {
65010
65188
  if (refresh2)
65011
65189
  invalidate();
65012
65190
  const list2 = await list({ refresh: refresh2 });
65013
- sendJSON(res, 200, list2);
65191
+ sendJSON(res, 200, await withTokenTotals(list2));
65014
65192
  return;
65015
65193
  }
65016
65194
  if (pathname === "/api/analytics") {
65017
65195
  const refresh2 = url.searchParams.get("refresh") === "1";
65196
+ const rp = parseRangeProjectParams(url.searchParams);
65197
+ if ("error" in rp) {
65198
+ sendJSON(res, 400, { error: rp.error });
65199
+ return;
65200
+ }
65018
65201
  if (refresh2)
65019
65202
  invalidate2();
65020
65203
  try {
65021
- const payload = await getAnalytics({ refresh: refresh2 });
65204
+ const payload = await getAnalytics({ refresh: refresh2, ...rp });
65022
65205
  sendJSON(res, 200, payload);
65023
65206
  } catch (err) {
65024
65207
  if (err instanceof StoreUnavailableError) {
@@ -65091,6 +65274,7 @@ function start(options2 = {}) {
65091
65274
  close: async () => {
65092
65275
  if (attached) {
65093
65276
  setStore(null);
65277
+ tokenStore = null;
65094
65278
  await attached.stop().catch(() => {});
65095
65279
  }
65096
65280
  await closeServer();
@@ -65101,8 +65285,10 @@ function start(options2 = {}) {
65101
65285
  };
65102
65286
  const doAttach = options2.attachStore ?? attachStore;
65103
65287
  doAttach({ onInvalidate: () => invalidate2() }).then((attached) => {
65104
- if (attached)
65288
+ if (attached) {
65105
65289
  setStore(attached.handle);
65290
+ tokenStore = attached.handle;
65291
+ }
65106
65292
  finish(attached);
65107
65293
  }).catch(() => finish(null));
65108
65294
  });
@@ -84144,22 +84330,984 @@ async function initCommand(options2) {
84144
84330
  throw error;
84145
84331
  }
84146
84332
  }
84333
+ // src/domains/mcp/mcp-service.ts
84334
+ init_logger();
84335
+
84336
+ // src/domains/mcp/mcp-service-core.ts
84337
+ init_environment();
84338
+ init_logger();
84339
+ init_dist2();
84340
+
84341
+ // src/domains/mcp/agent-detector.ts
84342
+ init_environment();
84343
+ import { existsSync as existsSync58 } from "node:fs";
84344
+ import { join as join126 } from "node:path";
84345
+
84346
+ // src/domains/mcp/shell-out.ts
84347
+ import { spawnSync as spawnSync5 } from "node:child_process";
84348
+ var DEFAULT_TIMEOUT_MS7 = 60000;
84349
+ var PROBE_TIMEOUT_MS = 5000;
84350
+ var SERVICE_NAME_PATTERN = /^[a-z0-9-]+$/;
84351
+ function isValidServiceName(name2) {
84352
+ return SERVICE_NAME_PATTERN.test(name2);
84353
+ }
84354
+ function runAgentCli(bin, args, timeoutMs = DEFAULT_TIMEOUT_MS7) {
84355
+ const result = spawnSync5(bin, args, {
84356
+ timeout: timeoutMs,
84357
+ encoding: "utf-8",
84358
+ shell: false,
84359
+ stdio: ["ignore", "pipe", "pipe"]
84360
+ });
84361
+ if (result.error) {
84362
+ return {
84363
+ ok: false,
84364
+ code: null,
84365
+ stdout: "",
84366
+ stderr: result.error.message
84367
+ };
84368
+ }
84369
+ return {
84370
+ ok: result.status === 0,
84371
+ code: result.status,
84372
+ stdout: result.stdout ?? "",
84373
+ stderr: result.stderr ?? ""
84374
+ };
84375
+ }
84376
+ function isBinaryOnPath(bin) {
84377
+ const probeCmd = process.platform === "win32" ? "where" : "which";
84378
+ const result = spawnSync5(probeCmd, [bin], {
84379
+ timeout: PROBE_TIMEOUT_MS,
84380
+ encoding: "utf-8",
84381
+ shell: false
84382
+ });
84383
+ if (result.error || result.status !== 0)
84384
+ return false;
84385
+ if (process.platform !== "win32")
84386
+ return true;
84387
+ return (result.stdout ?? "").split(/\r?\n/).some((line) => /\.(exe|com)$/i.test(line.trim()));
84388
+ }
84389
+ function isAlreadyExistsError(result) {
84390
+ return /already exists/i.test(`${result.stderr}
84391
+ ${result.stdout}`);
84392
+ }
84393
+
84394
+ // src/domains/mcp/types.ts
84395
+ init_zod();
84396
+ var McpAgentSchema = exports_external.enum(["claude-code", "codex"]);
84397
+ var ALL_MCP_AGENTS = ["claude-code", "codex"];
84398
+ var McpScopeSchema = exports_external.enum(["local", "user", "project"]);
84399
+ var ALL_MCP_SCOPES = ["local", "user", "project"];
84400
+ var DEFAULT_MCP_SCOPE = "user";
84401
+ var McpTransportSchema = exports_external.enum(["http", "sse", "stdio"]);
84402
+ var McpRemoteServiceEntrySchema = exports_external.object({
84403
+ name: exports_external.string(),
84404
+ description: exports_external.string(),
84405
+ transport: exports_external.enum(["http", "sse"]),
84406
+ url: exports_external.string().url()
84407
+ });
84408
+ var McpStdioServiceEntrySchema = exports_external.object({
84409
+ name: exports_external.string(),
84410
+ description: exports_external.string(),
84411
+ transport: exports_external.literal("stdio"),
84412
+ command: exports_external.string().min(1),
84413
+ args: exports_external.array(exports_external.string()).nullish()
84414
+ });
84415
+ var McpServiceEntrySchema = exports_external.union([
84416
+ McpRemoteServiceEntrySchema,
84417
+ McpStdioServiceEntrySchema
84418
+ ]);
84419
+ var McpRegistrySchema = exports_external.object({
84420
+ services: exports_external.array(McpServiceEntrySchema)
84421
+ });
84422
+
84423
+ // src/domains/mcp/agent-detector.ts
84424
+ var AGENT_BINARY = {
84425
+ "claude-code": "claude",
84426
+ codex: "codex"
84427
+ };
84428
+ function getAgentConfigPath(agent) {
84429
+ const home6 = getHomeDirectoryFromEnv();
84430
+ if (!home6)
84431
+ return null;
84432
+ switch (agent) {
84433
+ case "claude-code":
84434
+ return join126(home6, ".claude.json");
84435
+ case "codex":
84436
+ return join126(home6, ".codex", "config.toml");
84437
+ default: {
84438
+ const _exhaustive = agent;
84439
+ return _exhaustive;
84440
+ }
84441
+ }
84442
+ }
84443
+ var defaultCheckers = {
84444
+ binaryOnPath: isBinaryOnPath,
84445
+ configExists: existsSync58
84446
+ };
84447
+ function isAgentPresent(agent, checkers = defaultCheckers) {
84448
+ if (checkers.binaryOnPath(AGENT_BINARY[agent])) {
84449
+ return true;
84450
+ }
84451
+ const configPath = getAgentConfigPath(agent);
84452
+ return configPath !== null && checkers.configExists(configPath);
84453
+ }
84454
+ function detectAgents(checkers = defaultCheckers) {
84455
+ return ALL_MCP_AGENTS.filter((agent) => isAgentPresent(agent, checkers));
84456
+ }
84457
+ var PROJECT_MARKERS = {
84458
+ "claude-code": [".claude", "CLAUDE.md"],
84459
+ codex: [".codex", "AGENTS.md"]
84460
+ };
84461
+ function detectProjectAgents(cwd2 = process.cwd(), exists2 = existsSync58) {
84462
+ return ALL_MCP_AGENTS.filter((agent) => PROJECT_MARKERS[agent].some((marker) => exists2(join126(cwd2, marker))));
84463
+ }
84464
+
84465
+ // src/domains/mcp/registry-client.ts
84466
+ init_zod();
84467
+ init_logger();
84468
+ init_auth_client();
84469
+
84470
+ // src/domains/mcp/registry-cache.ts
84471
+ init_logger();
84472
+ init_paths2();
84473
+ import { promises as fs33 } from "node:fs";
84474
+ import { join as join127 } from "node:path";
84475
+ function getCacheDir() {
84476
+ return join127(getConfigDir(), "mcp");
84477
+ }
84478
+ function getCachePath2() {
84479
+ return join127(getCacheDir(), "registry-cache.json");
84480
+ }
84481
+ async function readCachedRegistry() {
84482
+ try {
84483
+ const raw = await fs33.readFile(getCachePath2(), "utf8");
84484
+ const parsed = JSON.parse(raw);
84485
+ const result = McpRegistrySchema.safeParse({ services: parsed.services });
84486
+ if (!result.success)
84487
+ return null;
84488
+ return {
84489
+ registry: result.data,
84490
+ cachedAt: typeof parsed.cachedAt === "number" ? parsed.cachedAt : 0
84491
+ };
84492
+ } catch (err) {
84493
+ if (err?.code === "ENOENT")
84494
+ return null;
84495
+ logger.verbose(`registry-cache: read failed (${err instanceof Error ? err.message : String(err)})`);
84496
+ return null;
84497
+ }
84498
+ }
84499
+ async function writeCachedRegistry(registry) {
84500
+ const dir = getCacheDir();
84501
+ await fs33.mkdir(dir, { recursive: true, mode: 448 });
84502
+ const body = { services: registry.services, cachedAt: Date.now() };
84503
+ await fs33.writeFile(getCachePath2(), JSON.stringify(body, null, 2), {
84504
+ mode: 384,
84505
+ encoding: "utf8"
84506
+ });
84507
+ }
84508
+
84509
+ // src/domains/mcp/writers/json-config-file.ts
84510
+ import { readFile as readFile44 } from "node:fs/promises";
84511
+ function toMessage(error) {
84512
+ return error instanceof Error ? error.message : String(error);
84513
+ }
84514
+ function isEnoent(error) {
84515
+ return error?.code === "ENOENT";
84516
+ }
84517
+ async function readJsonConfigFile(path11) {
84518
+ let content;
84519
+ try {
84520
+ content = await readFile44(path11, "utf-8");
84521
+ } catch (error) {
84522
+ if (isEnoent(error))
84523
+ return { ok: true, raw: {} };
84524
+ return { ok: false, detail: toMessage(error) };
84525
+ }
84526
+ try {
84527
+ const parsed = JSON.parse(content);
84528
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
84529
+ return { ok: true, raw: parsed };
84530
+ }
84531
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: not a JSON object` };
84532
+ } catch (error) {
84533
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: ${toMessage(error)}` };
84534
+ }
84535
+ }
84536
+ function extractMcpServers(raw) {
84537
+ const servers = raw.mcpServers;
84538
+ return servers && typeof servers === "object" && !Array.isArray(servers) ? servers : {};
84539
+ }
84540
+
84541
+ // src/domains/mcp/registry-client.ts
84542
+ class RegistryUnavailableError extends Error {
84543
+ constructor(cause) {
84544
+ super(`Cannot load the MCP registry: ${cause}. Check your network connection (the registry is fetched from the Takumi server) and retry.`);
84545
+ this.name = "RegistryUnavailableError";
84546
+ }
84547
+ }
84548
+ var REGISTRY_PATH2 = "/api/v1/mcp/registry";
84549
+ var REGISTRY_FETCH_TIMEOUT_MS = 3000;
84550
+ var LooseRegistrySchema = exports_external.object({ services: exports_external.array(exports_external.unknown()) });
84551
+ function parseRegistryLenient(data) {
84552
+ const loose = LooseRegistrySchema.parse(data);
84553
+ const services = [];
84554
+ for (const raw of loose.services) {
84555
+ const parsed = McpServiceEntrySchema.safeParse(raw);
84556
+ if (parsed.success) {
84557
+ services.push(parsed.data);
84558
+ } else {
84559
+ const name2 = raw?.name;
84560
+ logger.verbose(`registry: dropped invalid entry ${typeof name2 === "string" ? `"${name2}"` : "(unnamed)"}`);
84561
+ }
84562
+ }
84563
+ if (loose.services.length > 0 && services.length === 0) {
84564
+ throw new Error("registry: every entry failed validation (schema drift?)");
84565
+ }
84566
+ return { services };
84567
+ }
84568
+ async function fetchRemoteRegistry() {
84569
+ const res = await fetch(`${getServerUrl()}${REGISTRY_PATH2}`, {
84570
+ headers: { "X-TKM-Client": "takumi-cli/mcp" },
84571
+ signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
84572
+ });
84573
+ if (!res.ok)
84574
+ throw new Error(`registry: HTTP ${res.status}`);
84575
+ return parseRegistryLenient(await res.json());
84576
+ }
84577
+ async function refreshCache() {
84578
+ try {
84579
+ await writeCachedRegistry(await fetchRemoteRegistry());
84580
+ return true;
84581
+ } catch (err) {
84582
+ logger.verbose(`registry: revalidate skipped (${toMessage(err)})`);
84583
+ return false;
84584
+ }
84585
+ }
84586
+ async function getRegistryWithSource() {
84587
+ const cached = await readCachedRegistry();
84588
+ if (cached) {
84589
+ return { registry: cached.registry, source: "cache", revalidated: refreshCache() };
84590
+ }
84591
+ try {
84592
+ const remote = await fetchRemoteRegistry();
84593
+ try {
84594
+ await writeCachedRegistry(remote);
84595
+ } catch (err) {
84596
+ logger.verbose(`registry: cache write skipped (${toMessage(err)})`);
84597
+ }
84598
+ return { registry: remote, source: "server" };
84599
+ } catch (err) {
84600
+ throw new RegistryUnavailableError(toMessage(err));
84601
+ }
84602
+ }
84603
+ function resolveService(registry, name2) {
84604
+ return registry.services.find((service) => service.name === name2);
84605
+ }
84606
+
84607
+ // src/domains/mcp/writers/claude-config-file.ts
84608
+ import { existsSync as existsSync59 } from "node:fs";
84609
+ import { mkdir as mkdir29, writeFile as writeFile31 } from "node:fs/promises";
84610
+ import { dirname as dirname36, join as join128 } from "node:path";
84611
+ var AGENT = "claude-code";
84612
+ var LOCAL_SCOPE_FALLBACK_WARNING = "claude CLI not found; local scope isn't representable via direct file write, wrote to user-level ~/.claude.json instead";
84613
+ function userConfigPath() {
84614
+ return getAgentConfigPath(AGENT);
84615
+ }
84616
+ function projectConfigPath() {
84617
+ return join128(process.cwd(), ".mcp.json");
84618
+ }
84619
+ function configPathFor(scope) {
84620
+ return scope === "project" ? projectConfigPath() : userConfigPath();
84621
+ }
84622
+ function withLocalScopeFallbackWarning(detail, scope) {
84623
+ if (scope !== "local")
84624
+ return detail;
84625
+ return detail ? `${detail} (${LOCAL_SCOPE_FALLBACK_WARNING})` : LOCAL_SCOPE_FALLBACK_WARNING;
84626
+ }
84627
+ async function fileAdd(entry, scope) {
84628
+ const path11 = configPathFor(scope);
84629
+ if (!path11) {
84630
+ return { agent: AGENT, status: "failed", detail: "Could not resolve home directory" };
84631
+ }
84632
+ const existing = await readJsonConfigFile(path11);
84633
+ if (!existing.ok) {
84634
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84635
+ }
84636
+ try {
84637
+ const servers = extractMcpServers(existing.raw);
84638
+ servers[entry.name] = entry.transport === "stdio" ? {
84639
+ type: "stdio",
84640
+ command: entry.command,
84641
+ ...entry.args?.length ? { args: entry.args } : {}
84642
+ } : { type: entry.transport, url: entry.url };
84643
+ await mkdir29(dirname36(path11), { recursive: true });
84644
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84645
+ return {
84646
+ agent: AGENT,
84647
+ status: "added",
84648
+ detail: withLocalScopeFallbackWarning(`Wrote ${path11} (file fallback)`, scope)
84649
+ };
84650
+ } catch (error) {
84651
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84652
+ }
84653
+ }
84654
+ async function fileRemove(name2, scope) {
84655
+ const path11 = configPathFor(scope);
84656
+ if (!path11 || !existsSync59(path11)) {
84657
+ return { agent: AGENT, status: "skipped", detail: "No config file present" };
84658
+ }
84659
+ const existing = await readJsonConfigFile(path11);
84660
+ if (!existing.ok) {
84661
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84662
+ }
84663
+ try {
84664
+ const servers = extractMcpServers(existing.raw);
84665
+ if (!(name2 in servers)) {
84666
+ return { agent: AGENT, status: "skipped", detail: "Not configured" };
84667
+ }
84668
+ delete servers[name2];
84669
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84670
+ return {
84671
+ agent: AGENT,
84672
+ status: "removed",
84673
+ detail: withLocalScopeFallbackWarning(`Updated ${path11} (file fallback)`, scope)
84674
+ };
84675
+ } catch (error) {
84676
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84677
+ }
84678
+ }
84679
+ async function fileConfigured(name2, scope) {
84680
+ const path11 = configPathFor(scope);
84681
+ if (!path11)
84682
+ return false;
84683
+ const existing = await readJsonConfigFile(path11);
84684
+ return existing.ok && name2 in extractMcpServers(existing.raw);
84685
+ }
84686
+ async function listUserConfigured() {
84687
+ const path11 = userConfigPath();
84688
+ if (!path11)
84689
+ return { has: () => false };
84690
+ const existing = await readJsonConfigFile(path11);
84691
+ if (!existing.ok)
84692
+ return { has: () => false };
84693
+ const servers = extractMcpServers(existing.raw);
84694
+ return { has: (name2) => (name2 in servers) };
84695
+ }
84696
+
84697
+ // src/domains/mcp/writers/claude-writer.ts
84698
+ var AGENT2 = "claude-code";
84699
+ var BIN = "claude";
84700
+ async function add(entry, opts) {
84701
+ if (!isValidServiceName(entry.name)) {
84702
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${entry.name}` };
84703
+ }
84704
+ if (opts.scope !== "local" && await fileConfigured(entry.name, opts.scope)) {
84705
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84706
+ }
84707
+ if (isBinaryOnPath(BIN)) {
84708
+ const argv = entry.transport === "stdio" ? [
84709
+ "mcp",
84710
+ "add",
84711
+ "--scope",
84712
+ opts.scope,
84713
+ entry.name,
84714
+ "--",
84715
+ entry.command,
84716
+ ...entry.args ?? []
84717
+ ] : [
84718
+ "mcp",
84719
+ "add",
84720
+ "--transport",
84721
+ entry.transport,
84722
+ entry.name,
84723
+ entry.url,
84724
+ "--scope",
84725
+ opts.scope
84726
+ ];
84727
+ const result = runAgentCli(BIN, argv);
84728
+ if (result.ok) {
84729
+ return { agent: AGENT2, status: "added", detail: result.stdout.trim() || undefined };
84730
+ }
84731
+ if (isAlreadyExistsError(result)) {
84732
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84733
+ }
84734
+ return {
84735
+ agent: AGENT2,
84736
+ status: "failed",
84737
+ detail: result.stderr.trim() || `exit code ${result.code}`
84738
+ };
84739
+ }
84740
+ return fileAdd(entry, opts.scope);
84741
+ }
84742
+ async function remove10(name2, opts) {
84743
+ if (!isValidServiceName(name2)) {
84744
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${name2}` };
84745
+ }
84746
+ if (isBinaryOnPath(BIN)) {
84747
+ if (opts.scope !== "local" && !await fileConfigured(name2, opts.scope)) {
84748
+ return { agent: AGENT2, status: "skipped", detail: "Not configured" };
84749
+ }
84750
+ const result = runAgentCli(BIN, ["mcp", "remove", name2, "--scope", opts.scope]);
84751
+ if (result.ok) {
84752
+ return { agent: AGENT2, status: "removed", detail: result.stdout.trim() || undefined };
84753
+ }
84754
+ return {
84755
+ agent: AGENT2,
84756
+ status: "failed",
84757
+ detail: result.stderr.trim() || `exit code ${result.code}`
84758
+ };
84759
+ }
84760
+ return fileRemove(name2, opts.scope);
84761
+ }
84762
+ var claudeWriter = {
84763
+ agent: AGENT2,
84764
+ add,
84765
+ remove: remove10,
84766
+ listConfigured: listUserConfigured
84767
+ };
84768
+
84769
+ // src/domains/mcp/writers/codex-writer.ts
84770
+ init_path_safety();
84771
+ import { existsSync as existsSync60 } from "node:fs";
84772
+ import { readFile as readFile45, writeFile as writeFile32 } from "node:fs/promises";
84773
+
84774
+ // src/domains/mcp/writers/codex-config-file.ts
84775
+ function escapeRegex2(value) {
84776
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84777
+ }
84778
+ function sectionRegex(header) {
84779
+ const escaped = escapeRegex2(header);
84780
+ return new RegExp(`\\n?^\\[${escaped}\\]\\s*\\r?\\n(?:(?!\\[)[^\\r\\n]*\\r?\\n?)*`, "gm");
84781
+ }
84782
+ function tomlString(value) {
84783
+ return JSON.stringify(value).replace(/\u007f/g, "\\u007F");
84784
+ }
84785
+ function buildMcpServerSection(name2, entry) {
84786
+ if (!isValidServiceName(name2)) {
84787
+ throw new Error(`Refusing to build TOML section for invalid service name: ${name2}`);
84788
+ }
84789
+ if (entry.transport === "stdio") {
84790
+ const argsLine = entry.args?.length ? `args = [${entry.args.map(tomlString).join(", ")}]
84791
+ ` : "";
84792
+ return `[mcp_servers.${name2}]
84793
+ command = ${tomlString(entry.command)}
84794
+ ${argsLine}`;
84795
+ }
84796
+ return `[mcp_servers.${name2}]
84797
+ url = ${tomlString(entry.url)}
84798
+ `;
84799
+ }
84800
+ function upsertMcpServerSection(content, name2, section) {
84801
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84802
+ if (regex2.test(content)) {
84803
+ return content.replace(regex2, () => `
84804
+ ${section}`).trimStart();
84805
+ }
84806
+ const separator = content.trim().length > 0 ? `
84807
+
84808
+ ` : "";
84809
+ return `${content.trimEnd()}${separator}${section}`;
84810
+ }
84811
+ function removeMcpServerSection(content, name2) {
84812
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84813
+ if (!regex2.test(content)) {
84814
+ return { content, existed: false };
84815
+ }
84816
+ return { content: content.replace(regex2, ""), existed: true };
84817
+ }
84818
+ function hasMcpServerSection(content, name2) {
84819
+ return sectionRegex(`mcp_servers.${name2}`).test(content);
84820
+ }
84821
+
84822
+ // src/domains/mcp/writers/codex-writer.ts
84823
+ var AGENT3 = "codex";
84824
+ var BIN2 = "codex";
84825
+ var SCOPE_WARNING = "codex has no local/project scope; wrote to user-level ~/.codex/config.toml";
84826
+ function configPath() {
84827
+ return getAgentConfigPath(AGENT3);
84828
+ }
84829
+ function withScopeWarning(detail, scope) {
84830
+ if (scope === "user")
84831
+ return detail;
84832
+ return detail ? `${detail} (${SCOPE_WARNING})` : SCOPE_WARNING;
84833
+ }
84834
+ async function fileAdd2(entry, scope) {
84835
+ const path11 = configPath();
84836
+ if (!path11) {
84837
+ return { agent: AGENT3, status: "failed", detail: "Could not resolve home directory" };
84838
+ }
84839
+ try {
84840
+ return await withCodexTargetLock2(path11, async () => {
84841
+ let existing = "";
84842
+ try {
84843
+ existing = await readFile45(path11, "utf-8");
84844
+ } catch {
84845
+ existing = "";
84846
+ }
84847
+ const section = buildMcpServerSection(entry.name, entry);
84848
+ const updated = upsertMcpServerSection(existing, entry.name, section);
84849
+ await writeFile32(path11, updated, "utf-8");
84850
+ return {
84851
+ agent: AGENT3,
84852
+ status: "added",
84853
+ detail: withScopeWarning(`Wrote ${path11} (file fallback)`, scope)
84854
+ };
84855
+ });
84856
+ } catch (error) {
84857
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84858
+ }
84859
+ }
84860
+ async function fileRemove2(name2) {
84861
+ const path11 = configPath();
84862
+ if (!path11 || !existsSync60(path11)) {
84863
+ return { agent: AGENT3, status: "skipped", detail: "No config file present" };
84864
+ }
84865
+ try {
84866
+ return await withCodexTargetLock2(path11, async () => {
84867
+ const existing = await readFile45(path11, "utf-8");
84868
+ const { content, existed } = removeMcpServerSection(existing, name2);
84869
+ if (!existed) {
84870
+ return {
84871
+ agent: AGENT3,
84872
+ status: "skipped",
84873
+ detail: "Not configured"
84874
+ };
84875
+ }
84876
+ await writeFile32(path11, content, "utf-8");
84877
+ return {
84878
+ agent: AGENT3,
84879
+ status: "removed",
84880
+ detail: `Updated ${path11} (file fallback)`
84881
+ };
84882
+ });
84883
+ } catch (error) {
84884
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84885
+ }
84886
+ }
84887
+ async function add2(entry, opts) {
84888
+ if (!isValidServiceName(entry.name)) {
84889
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${entry.name}` };
84890
+ }
84891
+ if (await isConfigured(entry.name)) {
84892
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84893
+ }
84894
+ if (isBinaryOnPath(BIN2)) {
84895
+ const argv = entry.transport === "stdio" ? ["mcp", "add", entry.name, "--", entry.command, ...entry.args ?? []] : ["mcp", "add", entry.name, "--url", entry.url];
84896
+ const result = runAgentCli(BIN2, argv, 300000);
84897
+ if (result.ok) {
84898
+ return {
84899
+ agent: AGENT3,
84900
+ status: "added",
84901
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
84902
+ };
84903
+ }
84904
+ if (isAlreadyExistsError(result)) {
84905
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84906
+ }
84907
+ return {
84908
+ agent: AGENT3,
84909
+ status: "failed",
84910
+ detail: result.stderr.trim() || `exit code ${result.code}`
84911
+ };
84912
+ }
84913
+ return fileAdd2(entry, opts.scope);
84914
+ }
84915
+ async function remove11(name2, opts) {
84916
+ if (!isValidServiceName(name2)) {
84917
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${name2}` };
84918
+ }
84919
+ if (isBinaryOnPath(BIN2)) {
84920
+ if (!(await listConfigured()).has(name2)) {
84921
+ return { agent: AGENT3, status: "skipped", detail: "Not configured" };
84922
+ }
84923
+ const result = runAgentCli(BIN2, ["mcp", "remove", name2]);
84924
+ if (result.ok) {
84925
+ return {
84926
+ agent: AGENT3,
84927
+ status: "removed",
84928
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
84929
+ };
84930
+ }
84931
+ return {
84932
+ agent: AGENT3,
84933
+ status: "failed",
84934
+ detail: result.stderr.trim() || `exit code ${result.code}`
84935
+ };
84936
+ }
84937
+ return fileRemove2(name2);
84938
+ }
84939
+ async function listConfigured() {
84940
+ const path11 = configPath();
84941
+ if (!path11 || !existsSync60(path11))
84942
+ return { has: () => false };
84943
+ try {
84944
+ const content = await readFile45(path11, "utf-8");
84945
+ return { has: (name2) => hasMcpServerSection(content, name2) };
84946
+ } catch {
84947
+ return { has: () => false };
84948
+ }
84949
+ }
84950
+ async function isConfigured(name2) {
84951
+ return (await listConfigured()).has(name2);
84952
+ }
84953
+ var codexWriter = {
84954
+ agent: AGENT3,
84955
+ add: add2,
84956
+ remove: remove11,
84957
+ listConfigured
84958
+ };
84959
+
84960
+ // src/domains/mcp/writers/writer-registry.ts
84961
+ var writers = {
84962
+ "claude-code": claudeWriter,
84963
+ codex: codexWriter
84964
+ };
84965
+ function getWriter(agent) {
84966
+ return writers[agent];
84967
+ }
84968
+
84969
+ // src/domains/mcp/mcp-service-core.ts
84970
+ var defaultDeps2 = {
84971
+ getRegistry: getRegistryWithSource,
84972
+ detectAgents,
84973
+ detectProjectAgents,
84974
+ getWriter
84975
+ };
84976
+ function renderResults(results) {
84977
+ for (const result of results) {
84978
+ const line = `${result.agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ""}`;
84979
+ switch (result.status) {
84980
+ case "added":
84981
+ case "removed":
84982
+ logger.success(line);
84983
+ break;
84984
+ case "skipped":
84985
+ logger.warning(line);
84986
+ break;
84987
+ case "failed":
84988
+ logger.error(line);
84989
+ break;
84990
+ default: {
84991
+ const _exhaustive = result.status;
84992
+ }
84993
+ }
84994
+ }
84995
+ }
84996
+ async function selectAgents2(verb, entryName, candidates, opts) {
84997
+ if (opts.yes || isNonInteractive() || candidates.length <= 1) {
84998
+ return candidates;
84999
+ }
85000
+ const preposition = verb === "add" ? "to" : "from";
85001
+ const selected = await ae({
85002
+ message: `Select agents to ${verb} "${entryName}" ${preposition}:`,
85003
+ options: candidates.map((agent) => ({ value: agent, label: agent })),
85004
+ initialValues: candidates,
85005
+ required: false
85006
+ });
85007
+ if (lD(selected))
85008
+ return null;
85009
+ return selected;
85010
+ }
85011
+ async function resolveAgentTargets(verb, service, agentFilter, deps) {
85012
+ const { registry, revalidated } = await deps.getRegistry();
85013
+ const entry = resolveService(registry, service);
85014
+ if (!entry) {
85015
+ logger.error(`Unknown MCP service: ${service}`);
85016
+ logger.info(`Known services: ${registry.services.map((s3) => s3.name).join(", ") || "(none)"}`);
85017
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85018
+ }
85019
+ if (agentFilter && agentFilter.length > 0) {
85020
+ return { ok: true, entry, targets: agentFilter, prompt: false, revalidated };
85021
+ }
85022
+ const installed = deps.detectAgents();
85023
+ const projectTargets = deps.detectProjectAgents().filter((agent) => installed.includes(agent));
85024
+ if (projectTargets.length > 0) {
85025
+ const action = verb === "add" ? "adding to" : "removing from";
85026
+ logger.info(`Detected ${projectTargets.join(" + ")} project → ${action} ${projectTargets.join(", ")}.`);
85027
+ return { ok: true, entry, targets: projectTargets, prompt: false, revalidated };
85028
+ }
85029
+ if (installed.length === 0) {
85030
+ logger.warning("No coding agents detected (claude-code / codex).");
85031
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85032
+ }
85033
+ return { ok: true, entry, targets: installed, prompt: true, revalidated };
85034
+ }
85035
+
85036
+ // src/domains/mcp/mcp-service.ts
85037
+ async function runMutation(verb, opts, deps) {
85038
+ const resolved = await resolveAgentTargets(verb, opts.service, opts.agents, deps);
85039
+ try {
85040
+ if (!resolved.ok)
85041
+ return resolved.result;
85042
+ const { prompt } = resolved;
85043
+ let { entry } = resolved;
85044
+ let targets = resolved.targets;
85045
+ if (verb === "add" && opts.extraArgs && opts.extraArgs.length > 0) {
85046
+ if (entry.transport !== "stdio") {
85047
+ logger.error(`Extra args after "--" only apply to stdio MCP services; "${entry.name}" is ${entry.transport} (remote).`);
85048
+ return { results: [], exitCode: 1 };
85049
+ }
85050
+ entry = { ...entry, args: [...entry.args ?? [], ...opts.extraArgs] };
85051
+ }
85052
+ if (prompt) {
85053
+ const chosen = await selectAgents2(verb, entry.name, targets, { yes: opts.yes });
85054
+ if (chosen === null) {
85055
+ logger.info("Cancelled.");
85056
+ return { results: [], exitCode: 1 };
85057
+ }
85058
+ if (chosen.length === 0) {
85059
+ logger.warning(`No agents selected — nothing to ${verb}.`);
85060
+ return { results: [], exitCode: 1 };
85061
+ }
85062
+ targets = chosen;
85063
+ }
85064
+ const writerOpts = { scope: opts.scope };
85065
+ const results = [];
85066
+ for (const agent of targets) {
85067
+ const writer = deps.getWriter(agent);
85068
+ results.push(verb === "add" ? await writer.add(entry, writerOpts) : await writer.remove(entry.name, writerOpts));
85069
+ }
85070
+ renderResults(results);
85071
+ const allFailed = results.length > 0 && results.every((r2) => r2.status === "failed");
85072
+ return { results, exitCode: allFailed ? 1 : 0 };
85073
+ } finally {
85074
+ await resolved.revalidated;
85075
+ }
85076
+ }
85077
+ async function runAdd(opts, deps = defaultDeps2) {
85078
+ return runMutation("add", opts, deps);
85079
+ }
85080
+ async function runRemove(opts, deps = defaultDeps2) {
85081
+ return runMutation("remove", opts, deps);
85082
+ }
85083
+ async function runList(deps = defaultDeps2) {
85084
+ const { registry, source, revalidated } = await deps.getRegistry();
85085
+ const projectAgents = deps.detectProjectAgents();
85086
+ const detected = projectAgents.length > 0 ? projectAgents : deps.detectAgents();
85087
+ const lookups = await Promise.all(detected.map(async (agent) => ({
85088
+ agent,
85089
+ lookup: await deps.getWriter(agent).listConfigured()
85090
+ })));
85091
+ const entries = registry.services.map((entry) => ({
85092
+ entry,
85093
+ perAgent: lookups.map(({ agent, lookup }) => ({
85094
+ agent,
85095
+ configured: lookup.has(entry.name)
85096
+ }))
85097
+ }));
85098
+ return { entries, source, revalidated };
85099
+ }
85100
+
85101
+ // src/commands/mcp/mutation-command.ts
85102
+ init_logger();
85103
+
85104
+ // src/commands/mcp/agent-option.ts
85105
+ class InvalidAgentOptionError extends Error {
85106
+ invalidValues;
85107
+ constructor(invalidValues) {
85108
+ super(`Invalid --agent value(s): ${invalidValues.join(", ")}. Accepted agents: ${ALL_MCP_AGENTS.join(", ")}.`);
85109
+ this.invalidValues = invalidValues;
85110
+ this.name = "InvalidAgentOptionError";
85111
+ }
85112
+ }
85113
+ function normalizeAgentOption(agent) {
85114
+ if (agent === undefined)
85115
+ return;
85116
+ const values = Array.isArray(agent) ? agent : [agent];
85117
+ const invalid = values.filter((value) => !McpAgentSchema.safeParse(value).success);
85118
+ if (invalid.length > 0) {
85119
+ throw new InvalidAgentOptionError(invalid);
85120
+ }
85121
+ return values;
85122
+ }
85123
+
85124
+ // src/commands/mcp/scope-option.ts
85125
+ class InvalidScopeOptionError extends Error {
85126
+ invalidValue;
85127
+ constructor(invalidValue) {
85128
+ super(`Invalid --scope value: ${invalidValue}. Accepted scopes: ${ALL_MCP_SCOPES.join(", ")}.`);
85129
+ this.invalidValue = invalidValue;
85130
+ this.name = "InvalidScopeOptionError";
85131
+ }
85132
+ }
85133
+ function normalizeScopeOption(scope) {
85134
+ if (scope === undefined)
85135
+ return DEFAULT_MCP_SCOPE;
85136
+ const parsed = McpScopeSchema.safeParse(scope);
85137
+ if (!parsed.success) {
85138
+ throw new InvalidScopeOptionError(scope);
85139
+ }
85140
+ return parsed.data;
85141
+ }
85142
+
85143
+ // src/commands/mcp/mutation-command.ts
85144
+ async function mutationCommand(verb, service, options2 = {}) {
85145
+ if (!service) {
85146
+ logger.error(`Usage: tkm mcp ${verb} <service> [--agent <name>...] [-s, --scope local|user|project] [--yes]${verb === "add" ? " [-- <extra args...>]" : ""}`);
85147
+ process.exitCode = 1;
85148
+ return;
85149
+ }
85150
+ const extraArgs = options2["--"] ?? [];
85151
+ if (verb === "remove" && extraArgs.length > 0) {
85152
+ logger.warning(`Extra args after "--" only apply to add — ignored for remove.`);
85153
+ }
85154
+ let agents;
85155
+ try {
85156
+ agents = normalizeAgentOption(options2.agent);
85157
+ } catch (error) {
85158
+ if (error instanceof InvalidAgentOptionError) {
85159
+ logger.error(error.message);
85160
+ process.exitCode = 1;
85161
+ return;
85162
+ }
85163
+ throw error;
85164
+ }
85165
+ let scope;
85166
+ try {
85167
+ scope = normalizeScopeOption(options2.scope);
85168
+ } catch (error) {
85169
+ if (error instanceof InvalidScopeOptionError) {
85170
+ logger.error(error.message);
85171
+ process.exitCode = 1;
85172
+ return;
85173
+ }
85174
+ throw error;
85175
+ }
85176
+ const run2 = verb === "add" ? runAdd : runRemove;
85177
+ try {
85178
+ const { exitCode } = await run2({
85179
+ service,
85180
+ agents,
85181
+ scope,
85182
+ yes: options2.yes,
85183
+ extraArgs: verb === "add" ? extraArgs : undefined
85184
+ });
85185
+ process.exitCode = exitCode;
85186
+ } catch (error) {
85187
+ if (error instanceof RegistryUnavailableError) {
85188
+ logger.error(error.message);
85189
+ process.exitCode = 1;
85190
+ return;
85191
+ }
85192
+ throw error;
85193
+ }
85194
+ }
85195
+
85196
+ // src/commands/mcp/add-command.ts
85197
+ async function addCommand(service, options2 = {}) {
85198
+ await mutationCommand("add", service, options2);
85199
+ }
85200
+ // src/commands/mcp/list-command.ts
85201
+ init_logger();
85202
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
85203
+ function statusCell(configured) {
85204
+ return configured ? { value: "● configured", paint: import_picocolors25.default.green } : { value: "○ not set", paint: import_picocolors25.default.dim };
85205
+ }
85206
+ function renderTable(header, rows) {
85207
+ const lastCol = header.length - 1;
85208
+ const widths = header.map((h2, col) => Math.max(h2.length, ...rows.map((r2) => r2[col]?.value.length ?? 0)));
85209
+ const padPlain = (text, col) => col === lastCol ? text : text.padEnd(widths[col]);
85210
+ const headerLine = import_picocolors25.default.bold(header.map((h2, col) => padPlain(h2, col)).join(" ").trimEnd());
85211
+ const rowLine = (cells) => cells.map((cell, col) => {
85212
+ const padded = padPlain(cell.value, col);
85213
+ return cell.paint ? cell.paint(padded) : padded;
85214
+ }).join(" ").trimEnd();
85215
+ return [headerLine, ...rows.map(rowLine)].join(`
85216
+ `);
85217
+ }
85218
+ async function listCommand(options2 = {}) {
85219
+ let listed;
85220
+ try {
85221
+ listed = await runList();
85222
+ } catch (error) {
85223
+ if (error instanceof RegistryUnavailableError) {
85224
+ logger.error(error.message);
85225
+ process.exitCode = 1;
85226
+ return;
85227
+ }
85228
+ throw error;
85229
+ }
85230
+ const { entries, source, revalidated } = listed;
85231
+ if (options2.json) {
85232
+ const services = entries.map(({ entry, perAgent }) => ({
85233
+ name: entry.name,
85234
+ description: entry.description,
85235
+ transport: entry.transport,
85236
+ configured: Object.fromEntries(perAgent.map((p2) => [p2.agent, p2.configured]))
85237
+ }));
85238
+ process.stdout.write(`${JSON.stringify({ source, services })}
85239
+ `);
85240
+ await revalidated;
85241
+ return;
85242
+ }
85243
+ if (entries.length === 0) {
85244
+ logger.info("No MCP services in the registry.");
85245
+ return;
85246
+ }
85247
+ const agents = entries[0].perAgent.map((p2) => p2.agent);
85248
+ const header = ["NAME", "TRANSPORT", ...agents.map((a3) => a3.toUpperCase()), "DESCRIPTION"];
85249
+ const rows = entries.map(({ entry, perAgent }) => [
85250
+ { value: entry.name },
85251
+ { value: entry.transport },
85252
+ ...perAgent.map((p2) => statusCell(p2.configured)),
85253
+ { value: entry.description }
85254
+ ]);
85255
+ logger.info(`MCP registry — ${entries.length} services (source: ${source})`);
85256
+ if (agents.length === 0) {
85257
+ logger.info("No coding agents detected (claude-code / codex).");
85258
+ }
85259
+ process.stdout.write(`${renderTable(header, rows)}
85260
+ `);
85261
+ await revalidated;
85262
+ }
85263
+ // src/commands/mcp/mcp-command.ts
85264
+ init_logger();
85265
+
85266
+ // src/commands/mcp/remove-command.ts
85267
+ async function removeCommand(service, options2 = {}) {
85268
+ await mutationCommand("remove", service, options2);
85269
+ }
85270
+
85271
+ // src/commands/mcp/mcp-command.ts
85272
+ function printUsage() {
85273
+ logger.error("Usage: tkm mcp <add|list|remove> [service] [options] — see `tkm mcp --help`");
85274
+ }
85275
+ async function mcpCommand(action, service, options2 = {}) {
85276
+ switch (action) {
85277
+ case "add":
85278
+ await addCommand(service, options2);
85279
+ break;
85280
+ case "list":
85281
+ await listCommand(options2);
85282
+ break;
85283
+ case "remove":
85284
+ await removeCommand(service, options2);
85285
+ break;
85286
+ case undefined:
85287
+ printUsage();
85288
+ process.exitCode = 1;
85289
+ break;
85290
+ default:
85291
+ logger.error(`Unknown mcp action: ${action}. Available: add, list, remove`);
85292
+ process.exitCode = 1;
85293
+ }
85294
+ }
84147
85295
  // src/commands/plan/plan-command.ts
84148
85296
  init_output_manager();
84149
- import { existsSync as existsSync62, statSync as statSync11 } from "node:fs";
84150
- import { dirname as dirname41, join as join129, parse as parse4, resolve as resolve33 } from "node:path";
85297
+ import { existsSync as existsSync65, statSync as statSync11 } from "node:fs";
85298
+ import { dirname as dirname42, join as join132, parse as parse4, resolve as resolve33 } from "node:path";
84151
85299
 
84152
85300
  // src/commands/plan/plan-read-handlers.ts
84153
- import { existsSync as existsSync61, statSync as statSync10 } from "node:fs";
84154
- import { basename as basename21, dirname as dirname40, join as join128, relative as relative20, resolve as resolve31 } from "node:path";
85301
+ import { existsSync as existsSync64, statSync as statSync10 } from "node:fs";
85302
+ import { basename as basename21, dirname as dirname41, join as join131, relative as relative20, resolve as resolve31 } from "node:path";
84155
85303
 
84156
85304
  // src/domains/plan-parser/index.ts
84157
- import { dirname as dirname39 } from "node:path";
85305
+ import { dirname as dirname40 } from "node:path";
84158
85306
 
84159
85307
  // src/domains/plan-parser/plan-table-parser.ts
84160
85308
  var import_gray_matter5 = __toESM(require_gray_matter(), 1);
84161
85309
  import { readFileSync as readFileSync23 } from "node:fs";
84162
- import { dirname as dirname36, resolve as resolve30 } from "node:path";
85310
+ import { dirname as dirname37, resolve as resolve30 } from "node:path";
84163
85311
  function normalizeStatus(raw) {
84164
85312
  const s3 = raw.toLowerCase().trim();
84165
85313
  if (s3.includes("complete") || s3.includes("done") || s3.includes("✓") || s3.includes("✅")) {
@@ -84420,7 +85568,7 @@ function parseFormat4(content, planFilePath, options2) {
84420
85568
  const hasCheck = /[✅✓]/.test(line);
84421
85569
  current = { name: name2, status: hasCheck ? "completed" : "pending" };
84422
85570
  } else if (fileMatch && current) {
84423
- const planDir = dirname36(planFilePath);
85571
+ const planDir = dirname37(planFilePath);
84424
85572
  current.file = resolve30(planDir, fileMatch[1].trim());
84425
85573
  } else if (statusMatch && current) {
84426
85574
  current.status = normalizeStatus(statusMatch[2]);
@@ -84525,30 +85673,30 @@ function parsePhasesFromBody(body, dir, options2) {
84525
85673
  }
84526
85674
  function parsePlanFile(planFilePath, options2) {
84527
85675
  const content = readFileSync23(planFilePath, "utf8");
84528
- const dir = dirname36(planFilePath);
85676
+ const dir = dirname37(planFilePath);
84529
85677
  const { data: frontmatter, content: body } = import_gray_matter5.default(content);
84530
85678
  const phases = parsePhasesFromBody(body, dir, options2);
84531
85679
  return { frontmatter, phases };
84532
85680
  }
84533
85681
  // src/domains/plan-parser/plan-scanner.ts
84534
- import { existsSync as existsSync58, readdirSync as readdirSync10 } from "node:fs";
84535
- import { join as join126 } from "node:path";
85682
+ import { existsSync as existsSync61, readdirSync as readdirSync10 } from "node:fs";
85683
+ import { join as join129 } from "node:path";
84536
85684
  function scanPlanDir(dir) {
84537
- if (!existsSync58(dir))
85685
+ if (!existsSync61(dir))
84538
85686
  return [];
84539
85687
  try {
84540
- return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join126(dir, entry.name, "plan.md")).filter(existsSync58);
85688
+ return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join129(dir, entry.name, "plan.md")).filter(existsSync61);
84541
85689
  } catch {
84542
85690
  return [];
84543
85691
  }
84544
85692
  }
84545
85693
  // src/domains/plan-parser/plan-validator.ts
84546
85694
  var import_gray_matter6 = __toESM(require_gray_matter(), 1);
84547
- import { existsSync as existsSync59, readFileSync as readFileSync24 } from "node:fs";
84548
- import { basename as basename19, dirname as dirname37 } from "node:path";
85695
+ import { existsSync as existsSync62, readFileSync as readFileSync24 } from "node:fs";
85696
+ import { basename as basename19, dirname as dirname38 } from "node:path";
84549
85697
  function validatePlanFile(filePath, strict = false) {
84550
85698
  const content = readFileSync24(filePath, "utf8");
84551
- const dir = dirname37(filePath);
85699
+ const dir = dirname38(filePath);
84552
85700
  const issues = [];
84553
85701
  const lines = content.split(`
84554
85702
  `);
@@ -84584,7 +85732,7 @@ function validatePlanFile(filePath, strict = false) {
84584
85732
  });
84585
85733
  }
84586
85734
  for (const phase of phases) {
84587
- if (phase.file && !existsSync59(phase.file)) {
85735
+ if (phase.file && !existsSync62(phase.file)) {
84588
85736
  const fileBasename = basename19(phase.file);
84589
85737
  const refLine = lines.findIndex((l2) => l2.includes(fileBasename));
84590
85738
  issues.push({
@@ -84605,8 +85753,8 @@ function validatePlanFile(filePath, strict = false) {
84605
85753
  // src/domains/plan-parser/plan-writer.ts
84606
85754
  var import_gray_matter7 = __toESM(require_gray_matter(), 1);
84607
85755
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync25, writeFileSync as writeFileSync12 } from "node:fs";
84608
- import { existsSync as existsSync60 } from "node:fs";
84609
- import { basename as basename20, dirname as dirname38, join as join127 } from "node:path";
85756
+ import { existsSync as existsSync63 } from "node:fs";
85757
+ import { basename as basename20, dirname as dirname39, join as join130 } from "node:path";
84610
85758
  function phaseNameToFilename(id, name2) {
84611
85759
  const numMatch = /^(\d+)([a-z]*)$/i.exec(id);
84612
85760
  const num4 = numMatch ? numMatch[1] : id;
@@ -84714,12 +85862,12 @@ function scaffoldPlan(options2) {
84714
85862
  mkdirSync9(dir, { recursive: true });
84715
85863
  const resolvedPhases = resolvePhaseIds(options2.phases);
84716
85864
  const optionsWithResolved = { ...options2, phases: resolvedPhases };
84717
- const planFile = join127(dir, "plan.md");
85865
+ const planFile = join130(dir, "plan.md");
84718
85866
  writeFileSync12(planFile, generatePlanMd(optionsWithResolved), "utf8");
84719
85867
  const phaseFiles = [];
84720
85868
  for (const phase of resolvedPhases) {
84721
85869
  const filename = phaseNameToFilename(phase.id, phase.name);
84722
- const phaseFile = join127(dir, filename);
85870
+ const phaseFile = join130(dir, filename);
84723
85871
  writeFileSync12(phaseFile, generatePhaseTemplate(phase), "utf8");
84724
85872
  phaseFiles.push(phaseFile);
84725
85873
  }
@@ -84785,9 +85933,9 @@ function updatePhaseStatus(planFile, phaseId, newStatus) {
84785
85933
  const updatedFrontmatter = { ...frontmatter, status: planStatus };
84786
85934
  const updatedContent = import_gray_matter7.default.stringify(updatedBody, updatedFrontmatter);
84787
85935
  writeFileSync12(planFile, updatedContent, "utf8");
84788
- const planDir = dirname38(planFile);
85936
+ const planDir = dirname39(planFile);
84789
85937
  const phaseFilename = phaseNameFilenameFromTableRow(updatedBody, phaseId, planDir);
84790
- if (phaseFilename && existsSync60(phaseFilename)) {
85938
+ if (phaseFilename && existsSync63(phaseFilename)) {
84791
85939
  updatePhaseFileFrontmatter(phaseFilename, newStatus);
84792
85940
  }
84793
85941
  }
@@ -84799,7 +85947,7 @@ function phaseNameFilenameFromTableRow(body, phaseId, planDir) {
84799
85947
  continue;
84800
85948
  const linkMatch = /\[([^\]]+)\]\(\.\/([^)]+)\)/.exec(row);
84801
85949
  if (linkMatch)
84802
- return join127(planDir, linkMatch[2]);
85950
+ return join130(planDir, linkMatch[2]);
84803
85951
  }
84804
85952
  return null;
84805
85953
  }
@@ -84817,7 +85965,7 @@ function addPhase(planFile, name2, afterId) {
84817
85965
  throw new Error("Non-canonical plan.md — cannot add phase");
84818
85966
  }
84819
85967
  const { data: frontmatter, content: body } = import_gray_matter7.default(raw);
84820
- const planDir = dirname38(planFile);
85968
+ const planDir = dirname39(planFile);
84821
85969
  const existingIds = [];
84822
85970
  for (const match of body.matchAll(/^\|\s*(\d+[a-z]?)\s*\|/gim)) {
84823
85971
  existingIds.push(match[1].toLowerCase());
@@ -84880,7 +86028,7 @@ function addPhase(planFile, name2, afterId) {
84880
86028
  `);
84881
86029
  }
84882
86030
  writeFileSync12(planFile, import_gray_matter7.default.stringify(updatedBody, frontmatter), "utf8");
84883
- const phaseFilePath = join127(planDir, filename);
86031
+ const phaseFilePath = join130(planDir, filename);
84884
86032
  writeFileSync12(phaseFilePath, generatePhaseTemplate({ id: phaseId, name: name2 }), "utf8");
84885
86033
  return { phaseId, phaseFile: phaseFilePath };
84886
86034
  }
@@ -84892,7 +86040,7 @@ function buildPlanSummary(planFile) {
84892
86040
  const inProgress = phases.filter((p2) => p2.status === "in-progress").length;
84893
86041
  const pending = phases.filter((p2) => p2.status === "pending").length;
84894
86042
  return {
84895
- planDir: dirname39(planFile),
86043
+ planDir: dirname40(planFile),
84896
86044
  planFile,
84897
86045
  title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
84898
86046
  description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
@@ -84908,7 +86056,7 @@ function buildPlanSummary(planFile) {
84908
86056
  // src/commands/plan/plan-read-handlers.ts
84909
86057
  init_logger();
84910
86058
  init_output_manager();
84911
- var import_picocolors25 = __toESM(require_picocolors(), 1);
86059
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
84912
86060
  async function handleParse(target, options2) {
84913
86061
  const planFile = resolvePlanFile(target);
84914
86062
  if (!planFile) {
@@ -84929,9 +86077,9 @@ async function handleParse(target, options2) {
84929
86077
  console.log(JSON.stringify({ file: relative20(process.cwd(), planFile), frontmatter, phases }, null, 2));
84930
86078
  return;
84931
86079
  }
84932
- const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname40(planFile));
86080
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname41(planFile));
84933
86081
  console.log();
84934
- console.log(import_picocolors25.default.bold(` Plan: ${title}`));
86082
+ console.log(import_picocolors26.default.bold(` Plan: ${title}`));
84935
86083
  console.log(` File: ${planFile}`);
84936
86084
  console.log(` Phases found: ${phases.length}`);
84937
86085
  console.log();
@@ -84962,7 +86110,7 @@ async function handleValidate(target, options2) {
84962
86110
  return;
84963
86111
  }
84964
86112
  console.log();
84965
- console.log(import_picocolors25.default.bold(` Validating: ${planFile}`));
86113
+ console.log(import_picocolors26.default.bold(` Validating: ${planFile}`));
84966
86114
  console.log();
84967
86115
  if (result.issues.length === 0) {
84968
86116
  console.log(` [OK] No issues found — ${result.phases.length} phases detected`);
@@ -84976,7 +86124,7 @@ async function handleValidate(target, options2) {
84976
86124
  }
84977
86125
  }
84978
86126
  console.log();
84979
- const validStr = result.valid ? import_picocolors25.default.green("[OK] Valid") : import_picocolors25.default.red("[X] Invalid");
86127
+ const validStr = result.valid ? import_picocolors26.default.green("[OK] Valid") : import_picocolors26.default.red("[X] Invalid");
84980
86128
  console.log(` ${validStr} — ${result.issues.filter((i) => i.severity === "error").length} errors, ${result.issues.filter((i) => i.severity === "warning").length} warnings`);
84981
86129
  console.log();
84982
86130
  if (!result.valid)
@@ -84984,7 +86132,7 @@ async function handleValidate(target, options2) {
84984
86132
  }
84985
86133
  async function handleStatus(target, options2) {
84986
86134
  const t = target ? resolve31(target) : null;
84987
- const plansDir = t && existsSync61(t) && statSync10(t).isDirectory() && !existsSync61(join128(t, "plan.md")) ? t : null;
86135
+ const plansDir = t && existsSync64(t) && statSync10(t).isDirectory() && !existsSync64(join131(t, "plan.md")) ? t : null;
84988
86136
  if (plansDir) {
84989
86137
  const planFiles = scanPlanDir(plansDir);
84990
86138
  if (planFiles.length === 0) {
@@ -85003,20 +86151,20 @@ async function handleStatus(target, options2) {
85003
86151
  return;
85004
86152
  }
85005
86153
  console.log();
85006
- console.log(import_picocolors25.default.bold(` Plans in: ${plansDir}`));
86154
+ console.log(import_picocolors26.default.bold(` Plans in: ${plansDir}`));
85007
86155
  console.log();
85008
86156
  for (const pf of planFiles) {
85009
86157
  try {
85010
86158
  const s3 = buildPlanSummary(pf);
85011
86159
  const bar = progressBar(s3.completed, s3.totalPhases);
85012
- const title2 = s3.title ?? basename21(dirname40(pf));
85013
- console.log(` ${import_picocolors25.default.bold(title2)}`);
86160
+ const title2 = s3.title ?? basename21(dirname41(pf));
86161
+ console.log(` ${import_picocolors26.default.bold(title2)}`);
85014
86162
  console.log(` ${bar}`);
85015
86163
  if (s3.inProgress > 0)
85016
86164
  console.log(` [~] ${s3.inProgress} in progress`);
85017
86165
  console.log();
85018
86166
  } catch {
85019
- console.log(` [X] Failed to read: ${basename21(dirname40(pf))}`);
86167
+ console.log(` [X] Failed to read: ${basename21(dirname41(pf))}`);
85020
86168
  console.log();
85021
86169
  }
85022
86170
  }
@@ -85040,9 +86188,9 @@ async function handleStatus(target, options2) {
85040
86188
  console.log(JSON.stringify(summary, null, 2));
85041
86189
  return;
85042
86190
  }
85043
- const title = summary.title ?? basename21(dirname40(planFile));
86191
+ const title = summary.title ?? basename21(dirname41(planFile));
85044
86192
  console.log();
85045
- console.log(import_picocolors25.default.bold(` ${title}`));
86193
+ console.log(import_picocolors26.default.bold(` ${title}`));
85046
86194
  if (summary.status)
85047
86195
  console.log(` Status: ${summary.status}`);
85048
86196
  console.log();
@@ -85068,7 +86216,7 @@ async function handleKanban(target, _options) {
85068
86216
  // src/commands/plan/plan-write-handlers.ts
85069
86217
  import { basename as basename22, relative as relative21, resolve as resolve32 } from "node:path";
85070
86218
  init_output_manager();
85071
- var import_picocolors26 = __toESM(require_picocolors(), 1);
86219
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
85072
86220
  async function handleCreate(target, options2) {
85073
86221
  if (!options2.title) {
85074
86222
  output.error("[X] --title is required for create");
@@ -85115,7 +86263,7 @@ async function handleCreate(target, options2) {
85115
86263
  return;
85116
86264
  }
85117
86265
  console.log();
85118
- console.log(import_picocolors26.default.bold(` [OK] Plan created: ${options2.title}`));
86266
+ console.log(import_picocolors27.default.bold(` [OK] Plan created: ${options2.title}`));
85119
86267
  console.log(` Directory: ${resolve32(dir)}`);
85120
86268
  console.log(` Phases: ${result.phaseFiles.length}`);
85121
86269
  for (const f4 of result.phaseFiles) {
@@ -85215,22 +86363,22 @@ async function handleAddPhase(target, options2) {
85215
86363
  // src/commands/plan/plan-command.ts
85216
86364
  function resolvePlanFile(target) {
85217
86365
  const t = target ? resolve33(target) : process.cwd();
85218
- if (existsSync62(t)) {
86366
+ if (existsSync65(t)) {
85219
86367
  const stat14 = statSync11(t);
85220
86368
  if (stat14.isFile())
85221
86369
  return t;
85222
- const candidate = join129(t, "plan.md");
85223
- if (existsSync62(candidate))
86370
+ const candidate = join132(t, "plan.md");
86371
+ if (existsSync65(candidate))
85224
86372
  return candidate;
85225
86373
  }
85226
86374
  if (!target) {
85227
86375
  let dir = process.cwd();
85228
86376
  const root = parse4(dir).root;
85229
86377
  while (dir !== root) {
85230
- const candidate = join129(dir, "plan.md");
85231
- if (existsSync62(candidate))
86378
+ const candidate = join132(dir, "plan.md");
86379
+ if (existsSync65(candidate))
85232
86380
  return candidate;
85233
- dir = dirname41(dir);
86381
+ dir = dirname42(dir);
85234
86382
  }
85235
86383
  }
85236
86384
  return null;
@@ -85278,7 +86426,7 @@ async function planCommand(action, target, options2) {
85278
86426
  let resolvedTarget = target;
85279
86427
  if (resolvedAction && !knownActions.has(resolvedAction)) {
85280
86428
  const looksLikePath = resolvedAction.includes("/") || resolvedAction.includes("\\") || resolvedAction.endsWith(".md") || resolvedAction === "." || resolvedAction === "..";
85281
- const existsOnDisk = !looksLikePath && existsSync62(resolve33(resolvedAction));
86429
+ const existsOnDisk = !looksLikePath && existsSync65(resolve33(resolvedAction));
85282
86430
  if (looksLikePath || existsOnDisk) {
85283
86431
  resolvedTarget = resolvedAction;
85284
86432
  resolvedAction = undefined;
@@ -85322,22 +86470,22 @@ init_logger();
85322
86470
  init_logger();
85323
86471
 
85324
86472
  // src/commands/telemetry/shared.ts
85325
- import { existsSync as existsSync63, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
86473
+ import { existsSync as existsSync66, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
85326
86474
  import { homedir as homedir29 } from "node:os";
85327
- import { join as join130 } from "node:path";
86475
+ import { join as join133 } from "node:path";
85328
86476
  init_token_store();
85329
86477
  init_manifest_path_resolver();
85330
86478
  init_takumi_constants();
85331
- var USER_CACHE_PATH = join130(homedir29(), ".claude", "sk-user.json");
85332
- var EVENT_BUFFER_DIR = join130(homedir29(), ".claude", "sk-events");
85333
- var RATE_STATE_PATH = join130(homedir29(), ".claude", "sk-rate-state.json");
85334
- var TAKUMI_MANIFEST_PATH = join130(homedir29(), ".claude", MANIFEST_FILENAME);
85335
- var LEGACY_METADATA_PATH = join130(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
86479
+ var USER_CACHE_PATH = join133(homedir29(), ".claude", "sk-user.json");
86480
+ var EVENT_BUFFER_DIR = join133(homedir29(), ".claude", "sk-events");
86481
+ var RATE_STATE_PATH = join133(homedir29(), ".claude", "sk-rate-state.json");
86482
+ var TAKUMI_MANIFEST_PATH = join133(homedir29(), ".claude", MANIFEST_FILENAME);
86483
+ var LEGACY_METADATA_PATH = join133(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
85336
86484
  var TELEMETRY_HOOK_FIELD = "hooks.telemetry";
85337
86485
  var TOKEN_PLACEHOLDER = "__INJECT_AT_RELEASE__";
85338
86486
  function readUserCache() {
85339
86487
  try {
85340
- if (!existsSync63(USER_CACHE_PATH))
86488
+ if (!existsSync66(USER_CACHE_PATH))
85341
86489
  return null;
85342
86490
  const parsed = JSON.parse(readFileSync26(USER_CACHE_PATH, "utf8"));
85343
86491
  if (!parsed || typeof parsed !== "object")
@@ -85349,7 +86497,7 @@ function readUserCache() {
85349
86497
  }
85350
86498
  function countBufferFiles() {
85351
86499
  try {
85352
- if (!existsSync63(EVENT_BUFFER_DIR))
86500
+ if (!existsSync66(EVENT_BUFFER_DIR))
85353
86501
  return 0;
85354
86502
  return readdirSync11(EVENT_BUFFER_DIR).filter((f4) => f4.endsWith(".jsonl")).length;
85355
86503
  } catch {
@@ -85361,7 +86509,7 @@ function readTelemetryConfig() {
85361
86509
  const envToken = process.env.TAKUMI_TELEMETRY_TOKEN;
85362
86510
  let metadata = null;
85363
86511
  try {
85364
- const resolved = findManifestPathSync(join130(homedir29(), ".claude"));
86512
+ const resolved = findManifestPathSync(join133(homedir29(), ".claude"));
85365
86513
  if (resolved) {
85366
86514
  metadata = JSON.parse(readFileSync26(resolved.path, "utf8"));
85367
86515
  }
@@ -85387,8 +86535,8 @@ function collectRuntimeContext() {
85387
86535
  cacheSource: cache3?.source === "gh" || cache3?.source === "manual" ? cache3.source : null,
85388
86536
  bufferFileCount: countBufferFiles(),
85389
86537
  bufferDir: EVENT_BUFFER_DIR,
85390
- rateStateExists: existsSync63(RATE_STATE_PATH),
85391
- userCacheExists: existsSync63(USER_CACHE_PATH),
86538
+ rateStateExists: existsSync66(RATE_STATE_PATH),
86539
+ userCacheExists: existsSync66(USER_CACHE_PATH),
85392
86540
  endpoint,
85393
86541
  tokenConfigured: Boolean(token)
85394
86542
  };
@@ -85489,7 +86637,7 @@ init_manifest_writer();
85489
86637
  init_logger();
85490
86638
  init_safe_prompts();
85491
86639
  init_types2();
85492
- var import_picocolors28 = __toESM(require_picocolors(), 1);
86640
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
85493
86641
 
85494
86642
  // src/commands/uninstall/installation-detector.ts
85495
86643
  init_paths();
@@ -85539,7 +86687,7 @@ init_safe_prompts();
85539
86687
  init_safe_spinner();
85540
86688
  var import_fs_extra37 = __toESM(require_lib(), 1);
85541
86689
  import { readdirSync as readdirSync13, rmSync as rmSync9 } from "node:fs";
85542
- import { join as join132, resolve as resolve34, sep as sep9 } from "node:path";
86690
+ import { join as join135, resolve as resolve34, sep as sep9 } from "node:path";
85543
86691
 
85544
86692
  // src/commands/uninstall/analysis-handler.ts
85545
86693
  init_metadata_migration();
@@ -85548,14 +86696,14 @@ init_ownership_checker();
85548
86696
  init_logger();
85549
86697
  init_safe_prompts();
85550
86698
  init_takumi_constants();
85551
- var import_picocolors27 = __toESM(require_picocolors(), 1);
85552
- import { existsSync as existsSync64, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
85553
- import { dirname as dirname42, join as join131 } from "node:path";
86699
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
86700
+ import { existsSync as existsSync67, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
86701
+ import { dirname as dirname43, join as join134 } from "node:path";
85554
86702
  function listPresentManifestNames(installPath) {
85555
86703
  const present = [];
85556
- if (existsSync64(getManifestPath(installPath)))
86704
+ if (existsSync67(getManifestPath(installPath)))
85557
86705
  present.push(MANIFEST_FILENAME);
85558
- if (existsSync64(getLegacyManifestPath(installPath)))
86706
+ if (existsSync67(getLegacyManifestPath(installPath)))
85559
86707
  present.push(LEGACY_MANIFEST_FILENAME);
85560
86708
  return present;
85561
86709
  }
@@ -85573,7 +86721,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
85573
86721
  }
85574
86722
  async function cleanupEmptyDirectories3(filePath, installationRoot) {
85575
86723
  let cleaned = 0;
85576
- let currentDir = dirname42(filePath);
86724
+ let currentDir = dirname43(filePath);
85577
86725
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
85578
86726
  try {
85579
86727
  const entries = readdirSync12(currentDir);
@@ -85581,7 +86729,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
85581
86729
  rmSync8(currentDir, { recursive: true });
85582
86730
  cleaned++;
85583
86731
  logger.debug(`Removed empty directory: ${currentDir}`);
85584
- currentDir = dirname42(currentDir);
86732
+ currentDir = dirname43(currentDir);
85585
86733
  } else {
85586
86734
  break;
85587
86735
  }
@@ -85603,7 +86751,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85603
86751
  if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
85604
86752
  const kitFiles = metadata.kits[kit].files || [];
85605
86753
  for (const trackedFile of kitFiles) {
85606
- const filePath = join131(installation.path, trackedFile.path);
86754
+ const filePath = join134(installation.path, trackedFile.path);
85607
86755
  if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
85608
86756
  result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
85609
86757
  continue;
@@ -85635,7 +86783,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85635
86783
  return result;
85636
86784
  }
85637
86785
  for (const trackedFile of allTrackedFiles) {
85638
- const filePath = join131(installation.path, trackedFile.path);
86786
+ const filePath = join134(installation.path, trackedFile.path);
85639
86787
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
85640
86788
  if (!ownershipResult.exists)
85641
86789
  continue;
@@ -85653,27 +86801,27 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85653
86801
  }
85654
86802
  function displayDryRunPreview(analysis, installationType) {
85655
86803
  console.log("");
85656
- log.info(import_picocolors27.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
86804
+ log.info(import_picocolors28.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
85657
86805
  console.log("");
85658
86806
  if (analysis.toDelete.length > 0) {
85659
- console.log(import_picocolors27.default.red(import_picocolors27.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
86807
+ console.log(import_picocolors28.default.red(import_picocolors28.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
85660
86808
  const showDelete = analysis.toDelete.slice(0, 10);
85661
86809
  for (const item of showDelete) {
85662
- console.log(` ${import_picocolors27.default.red("✖")} ${item.path}`);
86810
+ console.log(` ${import_picocolors28.default.red("✖")} ${item.path}`);
85663
86811
  }
85664
86812
  if (analysis.toDelete.length > 10) {
85665
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
86813
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
85666
86814
  }
85667
86815
  console.log("");
85668
86816
  }
85669
86817
  if (analysis.toPreserve.length > 0) {
85670
- console.log(import_picocolors27.default.green(import_picocolors27.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
86818
+ console.log(import_picocolors28.default.green(import_picocolors28.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
85671
86819
  const showPreserve = analysis.toPreserve.slice(0, 10);
85672
86820
  for (const item of showPreserve) {
85673
- console.log(` ${import_picocolors27.default.green("✓")} ${item.path} ${import_picocolors27.default.gray(`(${item.reason})`)}`);
86821
+ console.log(` ${import_picocolors28.default.green("✓")} ${item.path} ${import_picocolors28.default.gray(`(${item.reason})`)}`);
85674
86822
  }
85675
86823
  if (analysis.toPreserve.length > 10) {
85676
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
86824
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
85677
86825
  }
85678
86826
  console.log("");
85679
86827
  }
@@ -85734,7 +86882,7 @@ async function removeInstallations(installations, options2) {
85734
86882
  let removedCount = 0;
85735
86883
  let cleanedDirs = 0;
85736
86884
  for (const item of analysis.toDelete) {
85737
- const filePath = join132(installation.path, item.path);
86885
+ const filePath = join135(installation.path, item.path);
85738
86886
  if (!await import_fs_extra37.pathExists(filePath))
85739
86887
  continue;
85740
86888
  if (!await isPathSafeToRemove(filePath, installation.path)) {
@@ -85795,15 +86943,15 @@ function displayInstallations(installations, scope) {
85795
86943
  const hasLegacy = installations.some((i) => !i.hasMetadata);
85796
86944
  const lines = installations.map((i) => {
85797
86945
  const typeLabel = i.type === "local" ? "Local " : "Global";
85798
- const legacyTag = !i.hasMetadata ? import_picocolors28.default.yellow(" [legacy]") : "";
86946
+ const legacyTag = !i.hasMetadata ? import_picocolors29.default.yellow(" [legacy]") : "";
85799
86947
  const components = formatComponentSummary(i);
85800
86948
  return ` ${typeLabel}: ${i.path}${legacyTag}${components}`;
85801
86949
  });
85802
86950
  prompts.note(lines.join(`
85803
86951
  `), `Detected Takumi installations (${scopeLabel})`);
85804
86952
  if (hasLegacy) {
85805
- log.warn(import_picocolors28.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
85806
- `) + import_picocolors28.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
86953
+ log.warn(import_picocolors29.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
86954
+ `) + import_picocolors29.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
85807
86955
  }
85808
86956
  log.warn("[!] This will permanently delete Takumi files from the above paths.");
85809
86957
  }
@@ -85863,7 +87011,7 @@ async function uninstallCommand(options2) {
85863
87011
  }
85864
87012
  const isAtHome = isLocalSameAsGlobal();
85865
87013
  if (validOptions.local && !validOptions.global && isAtHome) {
85866
- log.warn(import_picocolors28.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
87014
+ log.warn(import_picocolors29.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
85867
87015
  log.info("Use -g/--global or run from a project directory.");
85868
87016
  return;
85869
87017
  }
@@ -85875,7 +87023,7 @@ async function uninstallCommand(options2) {
85875
87023
  } else if (validOptions.global) {
85876
87024
  scope = "global";
85877
87025
  } else if (isAtHome) {
85878
- log.info(import_picocolors28.default.cyan("Running at HOME directory - targeting global installation"));
87026
+ log.info(import_picocolors29.default.cyan("Running at HOME directory - targeting global installation"));
85879
87027
  scope = "global";
85880
87028
  } else {
85881
87029
  const promptedScope = await promptScope(allInstallations);
@@ -85897,10 +87045,10 @@ async function uninstallCommand(options2) {
85897
87045
  }
85898
87046
  displayInstallations(installations, scope);
85899
87047
  if (validOptions.kit) {
85900
- log.info(import_picocolors28.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
87048
+ log.info(import_picocolors29.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
85901
87049
  }
85902
87050
  if (validOptions.dryRun) {
85903
- log.info(import_picocolors28.default.yellow("DRY RUN MODE - No files will be deleted"));
87051
+ log.info(import_picocolors29.default.yellow("DRY RUN MODE - No files will be deleted"));
85904
87052
  await removeInstallations(installations, {
85905
87053
  dryRun: true,
85906
87054
  forceOverwrite: validOptions.forceOverwrite,
@@ -85910,8 +87058,8 @@ async function uninstallCommand(options2) {
85910
87058
  return;
85911
87059
  }
85912
87060
  if (validOptions.forceOverwrite) {
85913
- log.warn(`${import_picocolors28.default.yellow(import_picocolors28.default.bold("FORCE MODE ENABLED"))}
85914
- ${import_picocolors28.default.yellow("User modifications will be permanently deleted!")}`);
87061
+ log.warn(`${import_picocolors29.default.yellow(import_picocolors29.default.bold("FORCE MODE ENABLED"))}
87062
+ ${import_picocolors29.default.yellow("User modifications will be permanently deleted!")}`);
85915
87063
  }
85916
87064
  if (!validOptions.yes) {
85917
87065
  const kitLabel = validOptions.kit ? ` (${validOptions.kit} kit only)` : "";
@@ -86364,7 +87512,7 @@ init_auth_client();
86364
87512
  init_github_client();
86365
87513
  init_logger();
86366
87514
  init_types2();
86367
- var import_picocolors29 = __toESM(require_picocolors(), 1);
87515
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
86368
87516
  function formatRelativeTime(dateString) {
86369
87517
  if (!dateString)
86370
87518
  return "Unknown";
@@ -86386,21 +87534,21 @@ function formatRelativeTime(dateString) {
86386
87534
  }
86387
87535
  function displayKitReleases(kitName, releases) {
86388
87536
  console.log(`
86389
- ${import_picocolors29.default.bold(import_picocolors29.default.cyan(kitName))} - Available Versions:
87537
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(kitName))} - Available Versions:
86390
87538
  `);
86391
87539
  if (releases.length === 0) {
86392
- console.log(import_picocolors29.default.dim(" No releases found"));
87540
+ console.log(import_picocolors30.default.dim(" No releases found"));
86393
87541
  return;
86394
87542
  }
86395
87543
  for (const release of releases) {
86396
- const version3 = import_picocolors29.default.green(release.tag);
87544
+ const version3 = import_picocolors30.default.green(release.tag);
86397
87545
  const publishedAt = formatRelativeTime(release.publishedAt);
86398
- const badge = release.prerelease ? ` ${import_picocolors29.default.yellow("[prerelease]")}` : "";
87546
+ const badge = release.prerelease ? ` ${import_picocolors30.default.yellow("[prerelease]")}` : "";
86399
87547
  const versionPart = version3.padEnd(20);
86400
- const timePart = import_picocolors29.default.dim(publishedAt.padEnd(20));
87548
+ const timePart = import_picocolors30.default.dim(publishedAt.padEnd(20));
86401
87549
  console.log(` ${versionPart} ${timePart}${badge}`);
86402
87550
  }
86403
- console.log(import_picocolors29.default.dim(`
87551
+ console.log(import_picocolors30.default.dim(`
86404
87552
  Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
86405
87553
  }
86406
87554
  async function fetchReleasesForKit(kitType, options2) {
@@ -86445,8 +87593,8 @@ async function versionCommand(options2) {
86445
87593
  for (const result of results) {
86446
87594
  if (result.error) {
86447
87595
  console.log(`
86448
- ${import_picocolors29.default.bold(import_picocolors29.default.cyan(result.kitConfig.name))} - ${import_picocolors29.default.red("Error")}`);
86449
- console.log(import_picocolors29.default.dim(` ${result.error}`));
87596
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(result.kitConfig.name))} - ${import_picocolors30.default.red("Error")}`);
87597
+ console.log(import_picocolors30.default.dim(` ${result.error}`));
86450
87598
  } else {
86451
87599
  displayKitReleases(result.kitConfig.name, result.releases);
86452
87600
  }
@@ -86592,6 +87740,9 @@ function registerCommands(cli) {
86592
87740
  process.exitCode = 1;
86593
87741
  }
86594
87742
  });
87743
+ cli.command("mcp [action] [service]", "Manage internal MCP connectors for coding agents (add|list|remove)").option("-a, --agent <agents...>", "Target agent(s): claude-code, codex").option("-s, --scope <scope>", "Config scope: local | user | project (default: user)").option("-y, --yes", "Non-interactive mode: skip confirmation prompts").option("--json", "Machine-readable JSON output (list only; ignored by add/remove)").action(async (action, service, options2 = {}) => {
87744
+ await mcpCommand(action, service, options2);
87745
+ });
86595
87746
  }
86596
87747
 
86597
87748
  // src/cli/version-display.ts
@@ -86603,7 +87754,7 @@ init_manifest_path_resolver();
86603
87754
  init_logger();
86604
87755
  init_types2();
86605
87756
  import { readFileSync as readFileSync27 } from "node:fs";
86606
- import { join as join133 } from "node:path";
87757
+ import { join as join136 } from "node:path";
86607
87758
  var PROVIDER_LOCAL_SUBDIRS = {
86608
87759
  "claude-code": ".claude",
86609
87760
  codex: ".codex"
@@ -86658,7 +87809,7 @@ async function displayVersion() {
86658
87809
  const localSubdir = PROVIDER_LOCAL_SUBDIRS[provider];
86659
87810
  if (!localSubdir)
86660
87811
  continue;
86661
- const localRoot = join133(process.cwd(), localSubdir);
87812
+ const localRoot = join136(process.cwd(), localSubdir);
86662
87813
  if (localRoot === inst.globalRoot())
86663
87814
  continue;
86664
87815
  const resolved = findManifestPathSync(localRoot);
@@ -86739,7 +87890,7 @@ function getPackageVersion3() {
86739
87890
 
86740
87891
  // src/shared/logger.ts
86741
87892
  init_output_manager();
86742
- var import_picocolors30 = __toESM(require_picocolors(), 1);
87893
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
86743
87894
  import { createWriteStream as createWriteStream4 } from "node:fs";
86744
87895
 
86745
87896
  class Logger2 {
@@ -86748,23 +87899,23 @@ class Logger2 {
86748
87899
  exitHandlerRegistered = false;
86749
87900
  info(message) {
86750
87901
  const symbols = output.getSymbols();
86751
- console.log(import_picocolors30.default.blue(symbols.info), message);
87902
+ console.log(import_picocolors31.default.blue(symbols.info), message);
86752
87903
  }
86753
87904
  success(message) {
86754
87905
  const symbols = output.getSymbols();
86755
- console.log(import_picocolors30.default.green(symbols.success), message);
87906
+ console.log(import_picocolors31.default.green(symbols.success), message);
86756
87907
  }
86757
87908
  warning(message) {
86758
87909
  const symbols = output.getSymbols();
86759
- console.log(import_picocolors30.default.yellow(symbols.warning), message);
87910
+ console.log(import_picocolors31.default.yellow(symbols.warning), message);
86760
87911
  }
86761
87912
  error(message) {
86762
87913
  const symbols = output.getSymbols();
86763
- console.error(import_picocolors30.default.red(symbols.error), message);
87914
+ console.error(import_picocolors31.default.red(symbols.error), message);
86764
87915
  }
86765
87916
  debug(message) {
86766
87917
  if (process.env.DEBUG) {
86767
- console.log(import_picocolors30.default.gray("[DEBUG]"), message);
87918
+ console.log(import_picocolors31.default.gray("[DEBUG]"), message);
86768
87919
  }
86769
87920
  }
86770
87921
  verbose(message, context) {
@@ -86773,7 +87924,7 @@ class Logger2 {
86773
87924
  const timestamp = this.getTimestamp();
86774
87925
  const sanitizedMessage = this.sanitize(message);
86775
87926
  const formattedContext = context ? this.formatContext(context) : "";
86776
- const logLine = `${timestamp} ${import_picocolors30.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
87927
+ const logLine = `${timestamp} ${import_picocolors31.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
86777
87928
  console.error(logLine);
86778
87929
  if (this.logFileStream) {
86779
87930
  const plainLogLine = `${timestamp} [VERBOSE] ${sanitizedMessage}${formattedContext}`;
@@ -86876,7 +88027,7 @@ var logger3 = new Logger2;
86876
88027
 
86877
88028
  // src/shared/output-manager.ts
86878
88029
  init_terminal_utils();
86879
- var import_picocolors31 = __toESM(require_picocolors(), 1);
88030
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
86880
88031
  var SYMBOLS2 = {
86881
88032
  unicode: {
86882
88033
  prompt: "◇",
@@ -86957,7 +88108,7 @@ class OutputManager2 {
86957
88108
  if (this.config.quiet)
86958
88109
  return;
86959
88110
  const symbol = this.getSymbols().success;
86960
- console.log(import_picocolors31.default.green(`${symbol} ${message}`));
88111
+ console.log(import_picocolors32.default.green(`${symbol} ${message}`));
86961
88112
  }
86962
88113
  error(message, data) {
86963
88114
  if (this.config.json) {
@@ -86965,7 +88116,7 @@ class OutputManager2 {
86965
88116
  return;
86966
88117
  }
86967
88118
  const symbol = this.getSymbols().error;
86968
- console.error(import_picocolors31.default.red(`${symbol} ${message}`));
88119
+ console.error(import_picocolors32.default.red(`${symbol} ${message}`));
86969
88120
  }
86970
88121
  warning(message, data) {
86971
88122
  if (this.config.json) {
@@ -86975,7 +88126,7 @@ class OutputManager2 {
86975
88126
  if (this.config.quiet)
86976
88127
  return;
86977
88128
  const symbol = this.getSymbols().warning;
86978
- console.log(import_picocolors31.default.yellow(`${symbol} ${message}`));
88129
+ console.log(import_picocolors32.default.yellow(`${symbol} ${message}`));
86979
88130
  }
86980
88131
  info(message, data) {
86981
88132
  if (this.config.json) {
@@ -86985,7 +88136,7 @@ class OutputManager2 {
86985
88136
  if (this.config.quiet)
86986
88137
  return;
86987
88138
  const symbol = this.getSymbols().info;
86988
- console.log(import_picocolors31.default.blue(`${symbol} ${message}`));
88139
+ console.log(import_picocolors32.default.blue(`${symbol} ${message}`));
86989
88140
  }
86990
88141
  verbose(message, data) {
86991
88142
  if (!this.config.verbose)
@@ -86994,7 +88145,7 @@ class OutputManager2 {
86994
88145
  this.addJsonEntry({ type: "info", message, data });
86995
88146
  return;
86996
88147
  }
86997
- console.log(import_picocolors31.default.dim(` ${message}`));
88148
+ console.log(import_picocolors32.default.dim(` ${message}`));
86998
88149
  }
86999
88150
  indent(message) {
87000
88151
  if (this.config.json)
@@ -87019,7 +88170,7 @@ class OutputManager2 {
87019
88170
  return;
87020
88171
  const symbols = this.getSymbols();
87021
88172
  console.log();
87022
- console.log(import_picocolors31.default.bold(import_picocolors31.default.cyan(`${symbols.line} ${title}`)));
88173
+ console.log(import_picocolors32.default.bold(import_picocolors32.default.cyan(`${symbols.line} ${title}`)));
87023
88174
  }
87024
88175
  addJsonEntry(entry) {
87025
88176
  this.jsonBuffer.push({