@sunasteriskrnd/takumi 0.13.0 → 0.14.0

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 +1369 -144
  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: "0.13.0",
19818
+ version: "0.14.0",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -48344,7 +48344,12 @@ async function runPkceFlow(options2) {
48344
48344
  codeChallenge: run.pair.challenge,
48345
48345
  port: run.port
48346
48346
  });
48347
- await openBrowser(url);
48347
+ options2.onAuthUrl?.(url);
48348
+ try {
48349
+ await openBrowser(url);
48350
+ } catch (err) {
48351
+ options2.onOpenBrowserError?.(err instanceof Error ? err : new Error(String(err)));
48352
+ }
48348
48353
  const callback = await Promise.race([received, timeoutPromise]);
48349
48354
  const code = callback.searchParams.get("code");
48350
48355
  const callbackState = callback.searchParams.get("state");
@@ -48364,7 +48369,9 @@ var init_pkce_flow = __esm(() => {
48364
48369
  PkceTimeoutError = class PkceTimeoutError extends Error {
48365
48370
  constructor() {
48366
48371
  super(`Login timed out after 90s waiting for browser callback.
48367
- ` + " - Re-run `tkm auth login` if the browser opened but you didn't complete sign-in.\n" + " - On a headless / WSL2 box without a browser, run `tkm auth login` from your\n" + " laptop and copy the token file to `<home>/.takumi/auth/token.json`.");
48372
+ ` + " - Re-run `tkm auth login` if the browser opened but you didn't complete sign-in.\n" + ` - If the browser didn't open, copy the sign-in link printed above and open it
48373
+ ` + ` manually in a browser on this machine.
48374
+ ` + " - On a headless / WSL2 box without a browser, run `tkm auth login` from your\n" + " laptop and copy the token file to `<home>/.takumi/auth/token.json`.");
48368
48375
  this.name = "PkceTimeoutError";
48369
48376
  }
48370
48377
  };
@@ -48397,7 +48404,7 @@ async function exchangeCodeForSession(webUrl, code, codeVerifier) {
48397
48404
  }
48398
48405
  return await res.json();
48399
48406
  }
48400
- async function runLoginFlow() {
48407
+ async function runLoginFlow(hooks = {}) {
48401
48408
  const serverUrl = getServerUrl();
48402
48409
  const pair = generatePkcePair();
48403
48410
  const state = generateState();
@@ -48405,6 +48412,8 @@ async function runLoginFlow() {
48405
48412
  const callback = await runPkceFlow({
48406
48413
  state,
48407
48414
  pair,
48415
+ onAuthUrl: hooks.onAuthUrl,
48416
+ onOpenBrowserError: hooks.onOpenBrowserError,
48408
48417
  authUrl: ({ state: s, codeChallenge, port }) => `${serverUrl}/cli/auth?state=${encodeURIComponent(s)}` + `&code_challenge=${encodeURIComponent(codeChallenge)}` + `&port=${port}&prompt=1` + `&cli_version=${encodeURIComponent(cliVersion)}`
48409
48418
  });
48410
48419
  const tokens = await exchangeCodeForSession(serverUrl, callback.code, pair.verifier);
@@ -48439,15 +48448,29 @@ function reportLoginError(err) {
48439
48448
  async function login() {
48440
48449
  oe("Takumi login");
48441
48450
  const spinner = de();
48442
- spinner.start("Opening browser for sign-in (90s timeout)…");
48451
+ let spinning = false;
48443
48452
  try {
48444
- const result = await runLoginFlow();
48445
- spinner.stop("Signed in.");
48453
+ const result = await runLoginFlow({
48454
+ onAuthUrl: (url) => {
48455
+ f2.step("Open this link to sign in (copy it if the browser doesn't open):");
48456
+ console.log(`
48457
+ ${url}
48458
+ `);
48459
+ spinner.start("Waiting for sign-in in browser (90s timeout)…");
48460
+ spinning = true;
48461
+ },
48462
+ onOpenBrowserError: () => {
48463
+ spinner.message("Browser didn't open — copy the link above to sign in (90s timeout)…");
48464
+ }
48465
+ });
48466
+ if (spinning)
48467
+ spinner.stop("Signed in.");
48446
48468
  if (result.email)
48447
48469
  f2.success(`Signed in as ${result.email}`);
48448
48470
  $e("You're ready to go.");
48449
48471
  } catch (err) {
48450
- spinner.stop("Login failed.", 1);
48472
+ if (spinning)
48473
+ spinner.stop("Login failed.", 1);
48451
48474
  reportLoginError(err);
48452
48475
  process.exitCode = 1;
48453
48476
  }
@@ -48631,7 +48654,18 @@ async function ensureAuthenticated(options2) {
48631
48654
  if (!mod.runLoginFlow) {
48632
48655
  throw new Error("Login command is not available in this build. Run `tkm auth login` manually.");
48633
48656
  }
48634
- await mod.runLoginFlow();
48657
+ const { log: log2 } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
48658
+ await mod.runLoginFlow({
48659
+ onAuthUrl: (url) => {
48660
+ log2.step("Open this link to sign in (copy it if the browser doesn't open):");
48661
+ console.log(`
48662
+ ${url}
48663
+ `);
48664
+ },
48665
+ onOpenBrowserError: () => {
48666
+ log2.warn("Browser didn't open — copy the link above to sign in.");
48667
+ }
48668
+ });
48635
48669
  });
48636
48670
  await runLogin();
48637
48671
  const token = await getToken({ ...options2, forceConfigRefresh: false });
@@ -49807,6 +49841,73 @@ var init_artifact_command_help = __esm(() => {
49807
49841
  };
49808
49842
  });
49809
49843
 
49844
+ // src/domains/help/commands/mcp-command-help.ts
49845
+ var mcpCommandHelp;
49846
+ var init_mcp_command_help = __esm(() => {
49847
+ mcpCommandHelp = {
49848
+ name: "mcp",
49849
+ description: "Manage internal MCP connectors for coding agents (add|list|remove)",
49850
+ usage: "tkm mcp <add|list|remove> [service] [options] [-- <extra args...>]",
49851
+ examples: [
49852
+ {
49853
+ command: "tkm mcp list",
49854
+ description: "Show available internal MCP services and per-agent configured state"
49855
+ },
49856
+ {
49857
+ command: "tkm mcp add meet-plus",
49858
+ description: "Register the 'meet-plus' MCP with the current project's coding agent(s)"
49859
+ },
49860
+ {
49861
+ command: "tkm mcp add playwright -- --browser msedge --headless",
49862
+ description: "Add a stdio MCP with extra launch args appended after the registry defaults"
49863
+ }
49864
+ ],
49865
+ optionGroups: [
49866
+ {
49867
+ title: "Actions",
49868
+ options: [
49869
+ {
49870
+ flags: "add <service> [-- <extra args...>]",
49871
+ description: "Add a registry service to detected agent(s); argv after -- is appended to a stdio service's args (e.g. -- --browser msedge)"
49872
+ },
49873
+ {
49874
+ flags: "list",
49875
+ description: "List registry services with per-agent configured status"
49876
+ },
49877
+ {
49878
+ flags: "remove <service>",
49879
+ description: "Remove a service from detected agent(s); absent entries are skipped"
49880
+ }
49881
+ ]
49882
+ },
49883
+ {
49884
+ title: "Options",
49885
+ options: [
49886
+ {
49887
+ flags: "-a, --agent <agents...>",
49888
+ description: "Target specific agent(s): claude-code | codex (repeatable)"
49889
+ },
49890
+ {
49891
+ flags: "-s, --scope <scope>",
49892
+ description: "Config scope: local | user | project (default: user; agent-dependent)"
49893
+ },
49894
+ { flags: "-y, --yes", description: "Non-interactive mode: skip selection prompts" },
49895
+ { flags: "--json", description: "Machine-readable JSON output (list only)" }
49896
+ ]
49897
+ }
49898
+ ],
49899
+ sections: [
49900
+ {
49901
+ title: "Registry source",
49902
+ content: `Cache-first: an existing local cache (<TAKUMI_HOME>/mcp/registry-cache.json) is
49903
+ ` + `served immediately while a background request refreshes it for the next run.
49904
+ ` + `With no cache yet, the CLI fetches the Takumi server directly and reports an
49905
+ ` + "error when the server is unreachable (no stale in-package fallback)."
49906
+ }
49907
+ ]
49908
+ };
49909
+ });
49910
+
49810
49911
  // src/domains/help/commands/index.ts
49811
49912
  var init_commands2 = __esm(() => {
49812
49913
  init_init_command_help();
@@ -49817,6 +49918,7 @@ var init_commands2 = __esm(() => {
49817
49918
  init_config_command_help();
49818
49919
  init_auth_command_help();
49819
49920
  init_artifact_command_help();
49921
+ init_mcp_command_help();
49820
49922
  init_common_options();
49821
49923
  });
49822
49924
 
@@ -49836,7 +49938,8 @@ var init_help_commands = __esm(() => {
49836
49938
  doctor: doctorCommandHelp,
49837
49939
  uninstall: uninstallCommandHelp,
49838
49940
  auth: authCommandHelp,
49839
- artifact: artifactCommandHelp
49941
+ artifact: artifactCommandHelp,
49942
+ mcp: mcpCommandHelp
49840
49943
  };
49841
49944
  });
49842
49945
 
@@ -53485,8 +53588,47 @@ function tryOpenBrowser(target) {
53485
53588
  }
53486
53589
 
53487
53590
  // src/domains/sessions/server.ts
53591
+ init_logger();
53488
53592
  import * as http from "node:http";
53489
53593
 
53594
+ // src/domains/sessions/analytics-daily-by-model.ts
53595
+ function buildDailyByModel(aggregates, oldestAllowedMs) {
53596
+ const byDayModel = new Map;
53597
+ for (const agg of aggregates) {
53598
+ const ms = startedMs(agg?.startedAt);
53599
+ if (ms === null)
53600
+ continue;
53601
+ const perModel = agg?.tokensByModel;
53602
+ if (!perModel || typeof perModel !== "object")
53603
+ continue;
53604
+ const date = dayKey(ms);
53605
+ if (Date.parse(`${date}T00:00:00.000Z`) < oldestAllowedMs)
53606
+ continue;
53607
+ let byModel = byDayModel.get(date);
53608
+ if (!byModel) {
53609
+ byModel = new Map;
53610
+ byDayModel.set(date, byModel);
53611
+ }
53612
+ for (const [model, mtok] of Object.entries(perModel)) {
53613
+ let acc = byModel.get(model);
53614
+ if (!acc) {
53615
+ acc = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
53616
+ byModel.set(model, acc);
53617
+ }
53618
+ accumulateTokens(acc, normalizeTokens(mtok));
53619
+ }
53620
+ }
53621
+ const rows = [];
53622
+ for (const [date, byModel] of byDayModel) {
53623
+ for (const [model, tokens] of byModel) {
53624
+ if (sumTokens(tokens) > 0)
53625
+ rows.push({ date, model, tokens });
53626
+ }
53627
+ }
53628
+ rows.sort((a3, b3) => a3.date.localeCompare(b3.date) || a3.model.localeCompare(b3.model));
53629
+ return rows;
53630
+ }
53631
+
53490
53632
  // src/domains/sessions/analytics.ts
53491
53633
  var DAY_MS = 24 * 60 * 60 * 1000;
53492
53634
  var PER_DAY_MAX = 371;
@@ -53521,16 +53663,26 @@ function accumulateTokens(a3, b3) {
53521
53663
  a3.cacheRead += b3.cacheRead;
53522
53664
  a3.cacheWrite += b3.cacheWrite;
53523
53665
  }
53524
- function buildLast30(byDay, now) {
53666
+ function buildDenseWindow(byDay, startDayMs, endDayMs) {
53525
53667
  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);
53668
+ for (let ms = startDayMs;ms <= endDayMs; ms += DAY_MS) {
53669
+ const date = dayKey(ms);
53529
53670
  out.push({ date, count: byDay.get(date) ?? 0 });
53530
53671
  }
53531
53672
  return out;
53532
53673
  }
53533
- function foldAnalytics(aggregates, now) {
53674
+ function windowBounds(now, range) {
53675
+ if (!range) {
53676
+ const todayMs = Date.parse(`${dayKey(now)}T00:00:00.000Z`);
53677
+ return { start: todayMs - (LAST_N_DAYS - 1) * DAY_MS, end: todayMs };
53678
+ }
53679
+ const lo = Math.min(range.from, range.to);
53680
+ const hi = Math.max(range.from, range.to);
53681
+ const end = Date.parse(`${dayKey(hi)}T00:00:00.000Z`);
53682
+ const start = Math.max(Date.parse(`${dayKey(lo)}T00:00:00.000Z`), end - (PER_DAY_MAX - 1) * DAY_MS);
53683
+ return { start, end };
53684
+ }
53685
+ function foldAnalytics(aggregates, now, range) {
53534
53686
  const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
53535
53687
  const eventsByDay = new Map;
53536
53688
  const tokensByDow = new Map;
@@ -53585,10 +53737,12 @@ function foldAnalytics(aggregates, now) {
53585
53737
  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
53738
  const byProjectAll = [...tokensByProject.entries()].map(([label, tokens]) => ({ label, tokens })).sort((a3, b3) => b3.tokens - a3.tokens || a3.label.localeCompare(b3.label));
53587
53739
  const byProject = byProjectAll.slice(0, TOP_PROJECTS);
53588
- const daily = buildLast30(tokensByDay, now);
53740
+ const { start: windowStart, end: windowEnd } = windowBounds(now, range);
53741
+ const daily = buildDenseWindow(tokensByDay, windowStart, windowEnd);
53589
53742
  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
53743
  const byModel = rankedModels.map(({ label, total }) => ({ label, tokens: total }));
53591
53744
  const byModelDetailed = rankedModels.map(({ label, tokens }) => ({ label, tokens }));
53745
+ const dailyByModel = buildDailyByModel(aggregates, oldestAllowedMs);
53592
53746
  const cacheDenom = totals.cacheRead + totals.input;
53593
53747
  const cacheHitRate = cacheDenom > 0 ? totals.cacheRead / cacheDenom : 0;
53594
53748
  return {
@@ -53602,7 +53756,7 @@ function foldAnalytics(aggregates, now) {
53602
53756
  events: {
53603
53757
  total: eventTotal,
53604
53758
  perDay,
53605
- last30: buildLast30(eventsByDay, now)
53759
+ last30: buildDenseWindow(eventsByDay, windowStart, windowEnd)
53606
53760
  },
53607
53761
  tokens: {
53608
53762
  byType: { ...totals },
@@ -53610,7 +53764,8 @@ function foldAnalytics(aggregates, now) {
53610
53764
  byProject,
53611
53765
  daily,
53612
53766
  byModel,
53613
- byModelDetailed
53767
+ byModelDetailed,
53768
+ dailyByModel
53614
53769
  },
53615
53770
  window: {
53616
53771
  earliest: earliestMs === null ? null : new Date(earliestMs).toISOString(),
@@ -54912,6 +55067,24 @@ function invalidate() {
54912
55067
  }
54913
55068
  }
54914
55069
 
55070
+ // src/domains/sessions/session-meta-filter.ts
55071
+ function filterSessionMetas(metas, filter) {
55072
+ return metas.filter((m2) => {
55073
+ if (filter.project !== undefined && m2.project !== filter.project)
55074
+ return false;
55075
+ if (filter.from === undefined && filter.to === undefined)
55076
+ return true;
55077
+ const ms = m2.startedAt === null ? Number.NaN : Date.parse(m2.startedAt);
55078
+ if (Number.isNaN(ms))
55079
+ return false;
55080
+ if (filter.from !== undefined && ms < filter.from)
55081
+ return false;
55082
+ if (filter.to !== undefined && ms > filter.to)
55083
+ return false;
55084
+ return true;
55085
+ });
55086
+ }
55087
+
54915
55088
  // src/domains/sessions/store/ingest.ts
54916
55089
  import { createHash as createHash9 } from "node:crypto";
54917
55090
  import { closeSync as closeSync4, openSync as openSync4, readSync as readSync4, readdirSync as readdirSync6, statSync as statSync5 } from "node:fs";
@@ -64676,6 +64849,32 @@ async function readStoreAggregates(db) {
64676
64849
  get2(r2.session_id).agentCount += Number(r2.n ?? 0);
64677
64850
  return map;
64678
64851
  }
64852
+ async function attachSessionTokenTotals(db, list2) {
64853
+ const totals = await readSessionTokenTotals(db);
64854
+ return list2.map((s) => {
64855
+ const tokens = totals.get(s.id);
64856
+ return tokens ? { ...s, tokens } : s;
64857
+ });
64858
+ }
64859
+ async function readSessionTokenTotals(db) {
64860
+ const rows = await db.selectFrom("event").where("type", "=", "tokens").select((eb) => [
64861
+ "session_id",
64862
+ eb.fn.sum("input").as("input"),
64863
+ eb.fn.sum("output").as("output"),
64864
+ eb.fn.sum("cache_read").as("cache_read"),
64865
+ eb.fn.sum("cache_write").as("cache_write")
64866
+ ]).groupBy("session_id").execute();
64867
+ const map = new Map;
64868
+ for (const r2 of rows) {
64869
+ map.set(r2.session_id, {
64870
+ input: Number(r2.input ?? 0),
64871
+ output: Number(r2.output ?? 0),
64872
+ cacheCreate: Number(r2.cache_write ?? 0),
64873
+ cacheRead: Number(r2.cache_read ?? 0)
64874
+ });
64875
+ }
64876
+ return map;
64877
+ }
64679
64878
  function buildAggregatesFromStore(sessions, store) {
64680
64879
  return sessions.map((s) => {
64681
64880
  const a3 = store.get(s.id) ?? emptyStoreAggregate();
@@ -64692,30 +64891,52 @@ function buildAggregatesFromStore(sessions, store) {
64692
64891
  }
64693
64892
 
64694
64893
  // src/domains/sessions/analytics-source.ts
64695
- var cache2 = null;
64894
+ var DAY_MS2 = 24 * 60 * 60 * 1000;
64895
+ var DEFAULT_WINDOW_DAYS = 30;
64896
+ var cache2 = new Map;
64696
64897
  var storeHandle = null;
64697
64898
  function setStore(handle) {
64698
64899
  storeHandle = handle;
64699
64900
  }
64700
64901
  function invalidate2() {
64701
- cache2 = null;
64902
+ cache2 = new Map;
64702
64903
  }
64703
- async function computeFromStore(handle, refresh2, now) {
64904
+ var cacheKey = (o2) => `${o2.from ?? ""}|${o2.to ?? ""}|${o2.project === undefined ? "" : `p:${o2.project}`}`;
64905
+ function rangeOf(o2, now) {
64906
+ if (o2.from === undefined && o2.to === undefined)
64907
+ return;
64908
+ const to = o2.to ?? now;
64909
+ const from = o2.from ?? to - (DEFAULT_WINDOW_DAYS - 1) * DAY_MS2;
64910
+ return { from, to };
64911
+ }
64912
+ async function computeFromStore(handle, opts, now) {
64913
+ const refresh2 = opts.refresh ?? false;
64704
64914
  if (refresh2)
64705
64915
  await ingestAll(handle, { refresh: true });
64706
64916
  const sessions = await list({ refresh: refresh2 });
64917
+ 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
64918
  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);
64919
+ const payload = foldAnalytics(buildAggregatesFromStore(metas, storeAggs), now, rangeOf(opts, now));
64920
+ return {
64921
+ ...payload,
64922
+ filters: {
64923
+ from: opts.from ?? null,
64924
+ to: opts.to ?? null,
64925
+ project: opts.project ?? null
64926
+ }
64927
+ };
64710
64928
  }
64711
64929
  async function getAnalytics(opts = {}) {
64712
- if (cache2 && !opts.refresh)
64713
- return cache2;
64930
+ const key = cacheKey(opts);
64931
+ const hit = cache2.get(key);
64932
+ if (hit && !opts.refresh)
64933
+ return hit;
64714
64934
  if (!storeHandle)
64715
64935
  throw new StoreUnavailableError;
64716
64936
  const now = Date.now();
64717
- cache2 = await computeFromStore(storeHandle, opts.refresh ?? false, now);
64718
- return cache2;
64937
+ const payload = await computeFromStore(storeHandle, opts, now);
64938
+ cache2.set(key, payload);
64939
+ return payload;
64719
64940
  }
64720
64941
 
64721
64942
  // src/domains/sessions/origin-allowlist.ts
@@ -64743,13 +64964,33 @@ function isAllowedOrigin(origin) {
64743
64964
  return LOOPBACK_HOST_PATTERN.test(origin);
64744
64965
  }
64745
64966
 
64967
+ // src/domains/sessions/range-params.ts
64968
+ function intParam(search, key) {
64969
+ const raw = search.get(key);
64970
+ if (raw === null || raw === "")
64971
+ return;
64972
+ const n = Number(raw);
64973
+ if (!Number.isInteger(n) || n < 0)
64974
+ return { error: `invalid ${key}: ${raw}` };
64975
+ return n;
64976
+ }
64977
+ function parseRangeProjectParams(search) {
64978
+ const from = intParam(search, "from");
64979
+ if (typeof from === "object")
64980
+ return from;
64981
+ const to = intParam(search, "to");
64982
+ if (typeof to === "object")
64983
+ return to;
64984
+ return { from, to, project: search.get("project") || undefined };
64985
+ }
64986
+
64746
64987
  // src/domains/sessions/store/bridge-store.ts
64747
64988
  init_logger();
64748
64989
 
64749
64990
  // src/domains/sessions/store/prune.ts
64750
64991
  import { existsSync as existsSync34, statSync as statSync6 } from "node:fs";
64751
64992
  init_logger();
64752
- var DAY_MS2 = 24 * 60 * 60 * 1000;
64993
+ var DAY_MS3 = 24 * 60 * 60 * 1000;
64753
64994
  var DEFAULT_RETENTION_DAYS = 180;
64754
64995
  var SIZE_DELETE_BATCH = 500;
64755
64996
  var SIZE_PRUNE_MAX_ITERS = 200;
@@ -64775,7 +65016,7 @@ async function runPrune(handle, opts = {}) {
64775
65016
  const now = opts.now ?? Date.now();
64776
65017
  const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
64777
65018
  const dbPath = opts.dbPath ?? getConsoleStorePath();
64778
- const cutoff = now - retentionDays * DAY_MS2;
65019
+ const cutoff = now - retentionDays * DAY_MS3;
64779
65020
  await db.deleteFrom("event").where("ts", "<", cutoff).execute();
64780
65021
  if (opts.maxSizeMb && opts.maxSizeMb > 0) {
64781
65022
  const cap = opts.maxSizeMb * 1024 * 1024;
@@ -64953,6 +65194,17 @@ async function attachStore(deps = {}) {
64953
65194
  }
64954
65195
 
64955
65196
  // src/domains/sessions/server.ts
65197
+ var tokenStore = null;
65198
+ async function withTokenTotals(list2) {
65199
+ if (!tokenStore)
65200
+ return list2;
65201
+ try {
65202
+ return await attachSessionTokenTotals(tokenStore.db, list2);
65203
+ } catch (err) {
65204
+ logger.verbose(`console bridge: token totals enrichment failed (${String(err)})`);
65205
+ return list2;
65206
+ }
65207
+ }
64956
65208
  var PORT_BUMP_MAX = 9;
64957
65209
  function applyCorsHeaders(req, res) {
64958
65210
  const origin = req.headers.origin;
@@ -65010,15 +65262,20 @@ async function route(req, res, version3) {
65010
65262
  if (refresh2)
65011
65263
  invalidate();
65012
65264
  const list2 = await list({ refresh: refresh2 });
65013
- sendJSON(res, 200, list2);
65265
+ sendJSON(res, 200, await withTokenTotals(list2));
65014
65266
  return;
65015
65267
  }
65016
65268
  if (pathname === "/api/analytics") {
65017
65269
  const refresh2 = url.searchParams.get("refresh") === "1";
65270
+ const rp = parseRangeProjectParams(url.searchParams);
65271
+ if ("error" in rp) {
65272
+ sendJSON(res, 400, { error: rp.error });
65273
+ return;
65274
+ }
65018
65275
  if (refresh2)
65019
65276
  invalidate2();
65020
65277
  try {
65021
- const payload = await getAnalytics({ refresh: refresh2 });
65278
+ const payload = await getAnalytics({ refresh: refresh2, ...rp });
65022
65279
  sendJSON(res, 200, payload);
65023
65280
  } catch (err) {
65024
65281
  if (err instanceof StoreUnavailableError) {
@@ -65091,6 +65348,7 @@ function start(options2 = {}) {
65091
65348
  close: async () => {
65092
65349
  if (attached) {
65093
65350
  setStore(null);
65351
+ tokenStore = null;
65094
65352
  await attached.stop().catch(() => {});
65095
65353
  }
65096
65354
  await closeServer();
@@ -65101,8 +65359,10 @@ function start(options2 = {}) {
65101
65359
  };
65102
65360
  const doAttach = options2.attachStore ?? attachStore;
65103
65361
  doAttach({ onInvalidate: () => invalidate2() }).then((attached) => {
65104
- if (attached)
65362
+ if (attached) {
65105
65363
  setStore(attached.handle);
65364
+ tokenStore = attached.handle;
65365
+ }
65106
65366
  finish(attached);
65107
65367
  }).catch(() => finish(null));
65108
65368
  });
@@ -84144,22 +84404,984 @@ async function initCommand(options2) {
84144
84404
  throw error;
84145
84405
  }
84146
84406
  }
84407
+ // src/domains/mcp/mcp-service.ts
84408
+ init_logger();
84409
+
84410
+ // src/domains/mcp/mcp-service-core.ts
84411
+ init_environment();
84412
+ init_logger();
84413
+ init_dist2();
84414
+
84415
+ // src/domains/mcp/agent-detector.ts
84416
+ init_environment();
84417
+ import { existsSync as existsSync58 } from "node:fs";
84418
+ import { join as join126 } from "node:path";
84419
+
84420
+ // src/domains/mcp/shell-out.ts
84421
+ import { spawnSync as spawnSync5 } from "node:child_process";
84422
+ var DEFAULT_TIMEOUT_MS7 = 60000;
84423
+ var PROBE_TIMEOUT_MS = 5000;
84424
+ var SERVICE_NAME_PATTERN = /^[a-z0-9-]+$/;
84425
+ function isValidServiceName(name2) {
84426
+ return SERVICE_NAME_PATTERN.test(name2);
84427
+ }
84428
+ function runAgentCli(bin, args, timeoutMs = DEFAULT_TIMEOUT_MS7) {
84429
+ const result = spawnSync5(bin, args, {
84430
+ timeout: timeoutMs,
84431
+ encoding: "utf-8",
84432
+ shell: false,
84433
+ stdio: ["ignore", "pipe", "pipe"]
84434
+ });
84435
+ if (result.error) {
84436
+ return {
84437
+ ok: false,
84438
+ code: null,
84439
+ stdout: "",
84440
+ stderr: result.error.message
84441
+ };
84442
+ }
84443
+ return {
84444
+ ok: result.status === 0,
84445
+ code: result.status,
84446
+ stdout: result.stdout ?? "",
84447
+ stderr: result.stderr ?? ""
84448
+ };
84449
+ }
84450
+ function isBinaryOnPath(bin) {
84451
+ const probeCmd = process.platform === "win32" ? "where" : "which";
84452
+ const result = spawnSync5(probeCmd, [bin], {
84453
+ timeout: PROBE_TIMEOUT_MS,
84454
+ encoding: "utf-8",
84455
+ shell: false
84456
+ });
84457
+ if (result.error || result.status !== 0)
84458
+ return false;
84459
+ if (process.platform !== "win32")
84460
+ return true;
84461
+ return (result.stdout ?? "").split(/\r?\n/).some((line) => /\.(exe|com)$/i.test(line.trim()));
84462
+ }
84463
+ function isAlreadyExistsError(result) {
84464
+ return /already exists/i.test(`${result.stderr}
84465
+ ${result.stdout}`);
84466
+ }
84467
+
84468
+ // src/domains/mcp/types.ts
84469
+ init_zod();
84470
+ var McpAgentSchema = exports_external.enum(["claude-code", "codex"]);
84471
+ var ALL_MCP_AGENTS = ["claude-code", "codex"];
84472
+ var McpScopeSchema = exports_external.enum(["local", "user", "project"]);
84473
+ var ALL_MCP_SCOPES = ["local", "user", "project"];
84474
+ var DEFAULT_MCP_SCOPE = "user";
84475
+ var McpTransportSchema = exports_external.enum(["http", "sse", "stdio"]);
84476
+ var McpRemoteServiceEntrySchema = exports_external.object({
84477
+ name: exports_external.string(),
84478
+ description: exports_external.string(),
84479
+ transport: exports_external.enum(["http", "sse"]),
84480
+ url: exports_external.string().url()
84481
+ });
84482
+ var McpStdioServiceEntrySchema = exports_external.object({
84483
+ name: exports_external.string(),
84484
+ description: exports_external.string(),
84485
+ transport: exports_external.literal("stdio"),
84486
+ command: exports_external.string().min(1),
84487
+ args: exports_external.array(exports_external.string()).nullish()
84488
+ });
84489
+ var McpServiceEntrySchema = exports_external.union([
84490
+ McpRemoteServiceEntrySchema,
84491
+ McpStdioServiceEntrySchema
84492
+ ]);
84493
+ var McpRegistrySchema = exports_external.object({
84494
+ services: exports_external.array(McpServiceEntrySchema)
84495
+ });
84496
+
84497
+ // src/domains/mcp/agent-detector.ts
84498
+ var AGENT_BINARY = {
84499
+ "claude-code": "claude",
84500
+ codex: "codex"
84501
+ };
84502
+ function getAgentConfigPath(agent) {
84503
+ const home6 = getHomeDirectoryFromEnv();
84504
+ if (!home6)
84505
+ return null;
84506
+ switch (agent) {
84507
+ case "claude-code":
84508
+ return join126(home6, ".claude.json");
84509
+ case "codex":
84510
+ return join126(home6, ".codex", "config.toml");
84511
+ default: {
84512
+ const _exhaustive = agent;
84513
+ return _exhaustive;
84514
+ }
84515
+ }
84516
+ }
84517
+ var defaultCheckers = {
84518
+ binaryOnPath: isBinaryOnPath,
84519
+ configExists: existsSync58
84520
+ };
84521
+ function isAgentPresent(agent, checkers = defaultCheckers) {
84522
+ if (checkers.binaryOnPath(AGENT_BINARY[agent])) {
84523
+ return true;
84524
+ }
84525
+ const configPath = getAgentConfigPath(agent);
84526
+ return configPath !== null && checkers.configExists(configPath);
84527
+ }
84528
+ function detectAgents(checkers = defaultCheckers) {
84529
+ return ALL_MCP_AGENTS.filter((agent) => isAgentPresent(agent, checkers));
84530
+ }
84531
+ var PROJECT_MARKERS = {
84532
+ "claude-code": [".claude", "CLAUDE.md"],
84533
+ codex: [".codex", "AGENTS.md"]
84534
+ };
84535
+ function detectProjectAgents(cwd2 = process.cwd(), exists2 = existsSync58) {
84536
+ return ALL_MCP_AGENTS.filter((agent) => PROJECT_MARKERS[agent].some((marker) => exists2(join126(cwd2, marker))));
84537
+ }
84538
+
84539
+ // src/domains/mcp/registry-client.ts
84540
+ init_zod();
84541
+ init_logger();
84542
+ init_auth_client();
84543
+
84544
+ // src/domains/mcp/registry-cache.ts
84545
+ init_logger();
84546
+ init_paths2();
84547
+ import { promises as fs33 } from "node:fs";
84548
+ import { join as join127 } from "node:path";
84549
+ function getCacheDir() {
84550
+ return join127(getConfigDir(), "mcp");
84551
+ }
84552
+ function getCachePath2() {
84553
+ return join127(getCacheDir(), "registry-cache.json");
84554
+ }
84555
+ async function readCachedRegistry() {
84556
+ try {
84557
+ const raw = await fs33.readFile(getCachePath2(), "utf8");
84558
+ const parsed = JSON.parse(raw);
84559
+ const result = McpRegistrySchema.safeParse({ services: parsed.services });
84560
+ if (!result.success)
84561
+ return null;
84562
+ return {
84563
+ registry: result.data,
84564
+ cachedAt: typeof parsed.cachedAt === "number" ? parsed.cachedAt : 0
84565
+ };
84566
+ } catch (err) {
84567
+ if (err?.code === "ENOENT")
84568
+ return null;
84569
+ logger.verbose(`registry-cache: read failed (${err instanceof Error ? err.message : String(err)})`);
84570
+ return null;
84571
+ }
84572
+ }
84573
+ async function writeCachedRegistry(registry) {
84574
+ const dir = getCacheDir();
84575
+ await fs33.mkdir(dir, { recursive: true, mode: 448 });
84576
+ const body = { services: registry.services, cachedAt: Date.now() };
84577
+ await fs33.writeFile(getCachePath2(), JSON.stringify(body, null, 2), {
84578
+ mode: 384,
84579
+ encoding: "utf8"
84580
+ });
84581
+ }
84582
+
84583
+ // src/domains/mcp/writers/json-config-file.ts
84584
+ import { readFile as readFile44 } from "node:fs/promises";
84585
+ function toMessage(error) {
84586
+ return error instanceof Error ? error.message : String(error);
84587
+ }
84588
+ function isEnoent(error) {
84589
+ return error?.code === "ENOENT";
84590
+ }
84591
+ async function readJsonConfigFile(path11) {
84592
+ let content;
84593
+ try {
84594
+ content = await readFile44(path11, "utf-8");
84595
+ } catch (error) {
84596
+ if (isEnoent(error))
84597
+ return { ok: true, raw: {} };
84598
+ return { ok: false, detail: toMessage(error) };
84599
+ }
84600
+ try {
84601
+ const parsed = JSON.parse(content);
84602
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
84603
+ return { ok: true, raw: parsed };
84604
+ }
84605
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: not a JSON object` };
84606
+ } catch (error) {
84607
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: ${toMessage(error)}` };
84608
+ }
84609
+ }
84610
+ function extractMcpServers(raw) {
84611
+ const servers = raw.mcpServers;
84612
+ return servers && typeof servers === "object" && !Array.isArray(servers) ? servers : {};
84613
+ }
84614
+
84615
+ // src/domains/mcp/registry-client.ts
84616
+ class RegistryUnavailableError extends Error {
84617
+ constructor(cause) {
84618
+ super(`Cannot load the MCP registry: ${cause}. Check your network connection (the registry is fetched from the Takumi server) and retry.`);
84619
+ this.name = "RegistryUnavailableError";
84620
+ }
84621
+ }
84622
+ var REGISTRY_PATH2 = "/api/v1/mcp/registry";
84623
+ var REGISTRY_FETCH_TIMEOUT_MS = 3000;
84624
+ var LooseRegistrySchema = exports_external.object({ services: exports_external.array(exports_external.unknown()) });
84625
+ function parseRegistryLenient(data) {
84626
+ const loose = LooseRegistrySchema.parse(data);
84627
+ const services = [];
84628
+ for (const raw of loose.services) {
84629
+ const parsed = McpServiceEntrySchema.safeParse(raw);
84630
+ if (parsed.success) {
84631
+ services.push(parsed.data);
84632
+ } else {
84633
+ const name2 = raw?.name;
84634
+ logger.verbose(`registry: dropped invalid entry ${typeof name2 === "string" ? `"${name2}"` : "(unnamed)"}`);
84635
+ }
84636
+ }
84637
+ if (loose.services.length > 0 && services.length === 0) {
84638
+ throw new Error("registry: every entry failed validation (schema drift?)");
84639
+ }
84640
+ return { services };
84641
+ }
84642
+ async function fetchRemoteRegistry() {
84643
+ const res = await fetch(`${getServerUrl()}${REGISTRY_PATH2}`, {
84644
+ headers: { "X-TKM-Client": "takumi-cli/mcp" },
84645
+ signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
84646
+ });
84647
+ if (!res.ok)
84648
+ throw new Error(`registry: HTTP ${res.status}`);
84649
+ return parseRegistryLenient(await res.json());
84650
+ }
84651
+ async function refreshCache() {
84652
+ try {
84653
+ await writeCachedRegistry(await fetchRemoteRegistry());
84654
+ return true;
84655
+ } catch (err) {
84656
+ logger.verbose(`registry: revalidate skipped (${toMessage(err)})`);
84657
+ return false;
84658
+ }
84659
+ }
84660
+ async function getRegistryWithSource() {
84661
+ const cached = await readCachedRegistry();
84662
+ if (cached) {
84663
+ return { registry: cached.registry, source: "cache", revalidated: refreshCache() };
84664
+ }
84665
+ try {
84666
+ const remote = await fetchRemoteRegistry();
84667
+ try {
84668
+ await writeCachedRegistry(remote);
84669
+ } catch (err) {
84670
+ logger.verbose(`registry: cache write skipped (${toMessage(err)})`);
84671
+ }
84672
+ return { registry: remote, source: "server" };
84673
+ } catch (err) {
84674
+ throw new RegistryUnavailableError(toMessage(err));
84675
+ }
84676
+ }
84677
+ function resolveService(registry, name2) {
84678
+ return registry.services.find((service) => service.name === name2);
84679
+ }
84680
+
84681
+ // src/domains/mcp/writers/claude-config-file.ts
84682
+ import { existsSync as existsSync59 } from "node:fs";
84683
+ import { mkdir as mkdir29, writeFile as writeFile31 } from "node:fs/promises";
84684
+ import { dirname as dirname36, join as join128 } from "node:path";
84685
+ var AGENT = "claude-code";
84686
+ 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";
84687
+ function userConfigPath() {
84688
+ return getAgentConfigPath(AGENT);
84689
+ }
84690
+ function projectConfigPath() {
84691
+ return join128(process.cwd(), ".mcp.json");
84692
+ }
84693
+ function configPathFor(scope) {
84694
+ return scope === "project" ? projectConfigPath() : userConfigPath();
84695
+ }
84696
+ function withLocalScopeFallbackWarning(detail, scope) {
84697
+ if (scope !== "local")
84698
+ return detail;
84699
+ return detail ? `${detail} (${LOCAL_SCOPE_FALLBACK_WARNING})` : LOCAL_SCOPE_FALLBACK_WARNING;
84700
+ }
84701
+ async function fileAdd(entry, scope) {
84702
+ const path11 = configPathFor(scope);
84703
+ if (!path11) {
84704
+ return { agent: AGENT, status: "failed", detail: "Could not resolve home directory" };
84705
+ }
84706
+ const existing = await readJsonConfigFile(path11);
84707
+ if (!existing.ok) {
84708
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84709
+ }
84710
+ try {
84711
+ const servers = extractMcpServers(existing.raw);
84712
+ servers[entry.name] = entry.transport === "stdio" ? {
84713
+ type: "stdio",
84714
+ command: entry.command,
84715
+ ...entry.args?.length ? { args: entry.args } : {}
84716
+ } : { type: entry.transport, url: entry.url };
84717
+ await mkdir29(dirname36(path11), { recursive: true });
84718
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84719
+ return {
84720
+ agent: AGENT,
84721
+ status: "added",
84722
+ detail: withLocalScopeFallbackWarning(`Wrote ${path11} (file fallback)`, scope)
84723
+ };
84724
+ } catch (error) {
84725
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84726
+ }
84727
+ }
84728
+ async function fileRemove(name2, scope) {
84729
+ const path11 = configPathFor(scope);
84730
+ if (!path11 || !existsSync59(path11)) {
84731
+ return { agent: AGENT, status: "skipped", detail: "No config file present" };
84732
+ }
84733
+ const existing = await readJsonConfigFile(path11);
84734
+ if (!existing.ok) {
84735
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84736
+ }
84737
+ try {
84738
+ const servers = extractMcpServers(existing.raw);
84739
+ if (!(name2 in servers)) {
84740
+ return { agent: AGENT, status: "skipped", detail: "Not configured" };
84741
+ }
84742
+ delete servers[name2];
84743
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84744
+ return {
84745
+ agent: AGENT,
84746
+ status: "removed",
84747
+ detail: withLocalScopeFallbackWarning(`Updated ${path11} (file fallback)`, scope)
84748
+ };
84749
+ } catch (error) {
84750
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84751
+ }
84752
+ }
84753
+ async function fileConfigured(name2, scope) {
84754
+ const path11 = configPathFor(scope);
84755
+ if (!path11)
84756
+ return false;
84757
+ const existing = await readJsonConfigFile(path11);
84758
+ return existing.ok && name2 in extractMcpServers(existing.raw);
84759
+ }
84760
+ async function listUserConfigured() {
84761
+ const path11 = userConfigPath();
84762
+ if (!path11)
84763
+ return { has: () => false };
84764
+ const existing = await readJsonConfigFile(path11);
84765
+ if (!existing.ok)
84766
+ return { has: () => false };
84767
+ const servers = extractMcpServers(existing.raw);
84768
+ return { has: (name2) => (name2 in servers) };
84769
+ }
84770
+
84771
+ // src/domains/mcp/writers/claude-writer.ts
84772
+ var AGENT2 = "claude-code";
84773
+ var BIN = "claude";
84774
+ async function add(entry, opts) {
84775
+ if (!isValidServiceName(entry.name)) {
84776
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${entry.name}` };
84777
+ }
84778
+ if (opts.scope !== "local" && await fileConfigured(entry.name, opts.scope)) {
84779
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84780
+ }
84781
+ if (isBinaryOnPath(BIN)) {
84782
+ const argv = entry.transport === "stdio" ? [
84783
+ "mcp",
84784
+ "add",
84785
+ "--scope",
84786
+ opts.scope,
84787
+ entry.name,
84788
+ "--",
84789
+ entry.command,
84790
+ ...entry.args ?? []
84791
+ ] : [
84792
+ "mcp",
84793
+ "add",
84794
+ "--transport",
84795
+ entry.transport,
84796
+ entry.name,
84797
+ entry.url,
84798
+ "--scope",
84799
+ opts.scope
84800
+ ];
84801
+ const result = runAgentCli(BIN, argv);
84802
+ if (result.ok) {
84803
+ return { agent: AGENT2, status: "added", detail: result.stdout.trim() || undefined };
84804
+ }
84805
+ if (isAlreadyExistsError(result)) {
84806
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84807
+ }
84808
+ return {
84809
+ agent: AGENT2,
84810
+ status: "failed",
84811
+ detail: result.stderr.trim() || `exit code ${result.code}`
84812
+ };
84813
+ }
84814
+ return fileAdd(entry, opts.scope);
84815
+ }
84816
+ async function remove10(name2, opts) {
84817
+ if (!isValidServiceName(name2)) {
84818
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${name2}` };
84819
+ }
84820
+ if (isBinaryOnPath(BIN)) {
84821
+ if (opts.scope !== "local" && !await fileConfigured(name2, opts.scope)) {
84822
+ return { agent: AGENT2, status: "skipped", detail: "Not configured" };
84823
+ }
84824
+ const result = runAgentCli(BIN, ["mcp", "remove", name2, "--scope", opts.scope]);
84825
+ if (result.ok) {
84826
+ return { agent: AGENT2, status: "removed", detail: result.stdout.trim() || undefined };
84827
+ }
84828
+ return {
84829
+ agent: AGENT2,
84830
+ status: "failed",
84831
+ detail: result.stderr.trim() || `exit code ${result.code}`
84832
+ };
84833
+ }
84834
+ return fileRemove(name2, opts.scope);
84835
+ }
84836
+ var claudeWriter = {
84837
+ agent: AGENT2,
84838
+ add,
84839
+ remove: remove10,
84840
+ listConfigured: listUserConfigured
84841
+ };
84842
+
84843
+ // src/domains/mcp/writers/codex-writer.ts
84844
+ init_path_safety();
84845
+ import { existsSync as existsSync60 } from "node:fs";
84846
+ import { readFile as readFile45, writeFile as writeFile32 } from "node:fs/promises";
84847
+
84848
+ // src/domains/mcp/writers/codex-config-file.ts
84849
+ function escapeRegex2(value) {
84850
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84851
+ }
84852
+ function sectionRegex(header) {
84853
+ const escaped = escapeRegex2(header);
84854
+ return new RegExp(`\\n?^\\[${escaped}\\]\\s*\\r?\\n(?:(?!\\[)[^\\r\\n]*\\r?\\n?)*`, "gm");
84855
+ }
84856
+ function tomlString(value) {
84857
+ return JSON.stringify(value).replace(/\u007f/g, "\\u007F");
84858
+ }
84859
+ function buildMcpServerSection(name2, entry) {
84860
+ if (!isValidServiceName(name2)) {
84861
+ throw new Error(`Refusing to build TOML section for invalid service name: ${name2}`);
84862
+ }
84863
+ if (entry.transport === "stdio") {
84864
+ const argsLine = entry.args?.length ? `args = [${entry.args.map(tomlString).join(", ")}]
84865
+ ` : "";
84866
+ return `[mcp_servers.${name2}]
84867
+ command = ${tomlString(entry.command)}
84868
+ ${argsLine}`;
84869
+ }
84870
+ return `[mcp_servers.${name2}]
84871
+ url = ${tomlString(entry.url)}
84872
+ `;
84873
+ }
84874
+ function upsertMcpServerSection(content, name2, section) {
84875
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84876
+ if (regex2.test(content)) {
84877
+ return content.replace(regex2, () => `
84878
+ ${section}`).trimStart();
84879
+ }
84880
+ const separator = content.trim().length > 0 ? `
84881
+
84882
+ ` : "";
84883
+ return `${content.trimEnd()}${separator}${section}`;
84884
+ }
84885
+ function removeMcpServerSection(content, name2) {
84886
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84887
+ if (!regex2.test(content)) {
84888
+ return { content, existed: false };
84889
+ }
84890
+ return { content: content.replace(regex2, ""), existed: true };
84891
+ }
84892
+ function hasMcpServerSection(content, name2) {
84893
+ return sectionRegex(`mcp_servers.${name2}`).test(content);
84894
+ }
84895
+
84896
+ // src/domains/mcp/writers/codex-writer.ts
84897
+ var AGENT3 = "codex";
84898
+ var BIN2 = "codex";
84899
+ var SCOPE_WARNING = "codex has no local/project scope; wrote to user-level ~/.codex/config.toml";
84900
+ function configPath() {
84901
+ return getAgentConfigPath(AGENT3);
84902
+ }
84903
+ function withScopeWarning(detail, scope) {
84904
+ if (scope === "user")
84905
+ return detail;
84906
+ return detail ? `${detail} (${SCOPE_WARNING})` : SCOPE_WARNING;
84907
+ }
84908
+ async function fileAdd2(entry, scope) {
84909
+ const path11 = configPath();
84910
+ if (!path11) {
84911
+ return { agent: AGENT3, status: "failed", detail: "Could not resolve home directory" };
84912
+ }
84913
+ try {
84914
+ return await withCodexTargetLock2(path11, async () => {
84915
+ let existing = "";
84916
+ try {
84917
+ existing = await readFile45(path11, "utf-8");
84918
+ } catch {
84919
+ existing = "";
84920
+ }
84921
+ const section = buildMcpServerSection(entry.name, entry);
84922
+ const updated = upsertMcpServerSection(existing, entry.name, section);
84923
+ await writeFile32(path11, updated, "utf-8");
84924
+ return {
84925
+ agent: AGENT3,
84926
+ status: "added",
84927
+ detail: withScopeWarning(`Wrote ${path11} (file fallback)`, scope)
84928
+ };
84929
+ });
84930
+ } catch (error) {
84931
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84932
+ }
84933
+ }
84934
+ async function fileRemove2(name2) {
84935
+ const path11 = configPath();
84936
+ if (!path11 || !existsSync60(path11)) {
84937
+ return { agent: AGENT3, status: "skipped", detail: "No config file present" };
84938
+ }
84939
+ try {
84940
+ return await withCodexTargetLock2(path11, async () => {
84941
+ const existing = await readFile45(path11, "utf-8");
84942
+ const { content, existed } = removeMcpServerSection(existing, name2);
84943
+ if (!existed) {
84944
+ return {
84945
+ agent: AGENT3,
84946
+ status: "skipped",
84947
+ detail: "Not configured"
84948
+ };
84949
+ }
84950
+ await writeFile32(path11, content, "utf-8");
84951
+ return {
84952
+ agent: AGENT3,
84953
+ status: "removed",
84954
+ detail: `Updated ${path11} (file fallback)`
84955
+ };
84956
+ });
84957
+ } catch (error) {
84958
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84959
+ }
84960
+ }
84961
+ async function add2(entry, opts) {
84962
+ if (!isValidServiceName(entry.name)) {
84963
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${entry.name}` };
84964
+ }
84965
+ if (await isConfigured(entry.name)) {
84966
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84967
+ }
84968
+ if (isBinaryOnPath(BIN2)) {
84969
+ const argv = entry.transport === "stdio" ? ["mcp", "add", entry.name, "--", entry.command, ...entry.args ?? []] : ["mcp", "add", entry.name, "--url", entry.url];
84970
+ const result = runAgentCli(BIN2, argv, 300000);
84971
+ if (result.ok) {
84972
+ return {
84973
+ agent: AGENT3,
84974
+ status: "added",
84975
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
84976
+ };
84977
+ }
84978
+ if (isAlreadyExistsError(result)) {
84979
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84980
+ }
84981
+ return {
84982
+ agent: AGENT3,
84983
+ status: "failed",
84984
+ detail: result.stderr.trim() || `exit code ${result.code}`
84985
+ };
84986
+ }
84987
+ return fileAdd2(entry, opts.scope);
84988
+ }
84989
+ async function remove11(name2, opts) {
84990
+ if (!isValidServiceName(name2)) {
84991
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${name2}` };
84992
+ }
84993
+ if (isBinaryOnPath(BIN2)) {
84994
+ if (!(await listConfigured()).has(name2)) {
84995
+ return { agent: AGENT3, status: "skipped", detail: "Not configured" };
84996
+ }
84997
+ const result = runAgentCli(BIN2, ["mcp", "remove", name2]);
84998
+ if (result.ok) {
84999
+ return {
85000
+ agent: AGENT3,
85001
+ status: "removed",
85002
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
85003
+ };
85004
+ }
85005
+ return {
85006
+ agent: AGENT3,
85007
+ status: "failed",
85008
+ detail: result.stderr.trim() || `exit code ${result.code}`
85009
+ };
85010
+ }
85011
+ return fileRemove2(name2);
85012
+ }
85013
+ async function listConfigured() {
85014
+ const path11 = configPath();
85015
+ if (!path11 || !existsSync60(path11))
85016
+ return { has: () => false };
85017
+ try {
85018
+ const content = await readFile45(path11, "utf-8");
85019
+ return { has: (name2) => hasMcpServerSection(content, name2) };
85020
+ } catch {
85021
+ return { has: () => false };
85022
+ }
85023
+ }
85024
+ async function isConfigured(name2) {
85025
+ return (await listConfigured()).has(name2);
85026
+ }
85027
+ var codexWriter = {
85028
+ agent: AGENT3,
85029
+ add: add2,
85030
+ remove: remove11,
85031
+ listConfigured
85032
+ };
85033
+
85034
+ // src/domains/mcp/writers/writer-registry.ts
85035
+ var writers = {
85036
+ "claude-code": claudeWriter,
85037
+ codex: codexWriter
85038
+ };
85039
+ function getWriter(agent) {
85040
+ return writers[agent];
85041
+ }
85042
+
85043
+ // src/domains/mcp/mcp-service-core.ts
85044
+ var defaultDeps2 = {
85045
+ getRegistry: getRegistryWithSource,
85046
+ detectAgents,
85047
+ detectProjectAgents,
85048
+ getWriter
85049
+ };
85050
+ function renderResults(results) {
85051
+ for (const result of results) {
85052
+ const line = `${result.agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ""}`;
85053
+ switch (result.status) {
85054
+ case "added":
85055
+ case "removed":
85056
+ logger.success(line);
85057
+ break;
85058
+ case "skipped":
85059
+ logger.warning(line);
85060
+ break;
85061
+ case "failed":
85062
+ logger.error(line);
85063
+ break;
85064
+ default: {
85065
+ const _exhaustive = result.status;
85066
+ }
85067
+ }
85068
+ }
85069
+ }
85070
+ async function selectAgents2(verb, entryName, candidates, opts) {
85071
+ if (opts.yes || isNonInteractive() || candidates.length <= 1) {
85072
+ return candidates;
85073
+ }
85074
+ const preposition = verb === "add" ? "to" : "from";
85075
+ const selected = await ae({
85076
+ message: `Select agents to ${verb} "${entryName}" ${preposition}:`,
85077
+ options: candidates.map((agent) => ({ value: agent, label: agent })),
85078
+ initialValues: candidates,
85079
+ required: false
85080
+ });
85081
+ if (lD(selected))
85082
+ return null;
85083
+ return selected;
85084
+ }
85085
+ async function resolveAgentTargets(verb, service, agentFilter, deps) {
85086
+ const { registry, revalidated } = await deps.getRegistry();
85087
+ const entry = resolveService(registry, service);
85088
+ if (!entry) {
85089
+ logger.error(`Unknown MCP service: ${service}`);
85090
+ logger.info(`Known services: ${registry.services.map((s3) => s3.name).join(", ") || "(none)"}`);
85091
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85092
+ }
85093
+ if (agentFilter && agentFilter.length > 0) {
85094
+ return { ok: true, entry, targets: agentFilter, prompt: false, revalidated };
85095
+ }
85096
+ const installed = deps.detectAgents();
85097
+ const projectTargets = deps.detectProjectAgents().filter((agent) => installed.includes(agent));
85098
+ if (projectTargets.length > 0) {
85099
+ const action = verb === "add" ? "adding to" : "removing from";
85100
+ logger.info(`Detected ${projectTargets.join(" + ")} project → ${action} ${projectTargets.join(", ")}.`);
85101
+ return { ok: true, entry, targets: projectTargets, prompt: false, revalidated };
85102
+ }
85103
+ if (installed.length === 0) {
85104
+ logger.warning("No coding agents detected (claude-code / codex).");
85105
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85106
+ }
85107
+ return { ok: true, entry, targets: installed, prompt: true, revalidated };
85108
+ }
85109
+
85110
+ // src/domains/mcp/mcp-service.ts
85111
+ async function runMutation(verb, opts, deps) {
85112
+ const resolved = await resolveAgentTargets(verb, opts.service, opts.agents, deps);
85113
+ try {
85114
+ if (!resolved.ok)
85115
+ return resolved.result;
85116
+ const { prompt } = resolved;
85117
+ let { entry } = resolved;
85118
+ let targets = resolved.targets;
85119
+ if (verb === "add" && opts.extraArgs && opts.extraArgs.length > 0) {
85120
+ if (entry.transport !== "stdio") {
85121
+ logger.error(`Extra args after "--" only apply to stdio MCP services; "${entry.name}" is ${entry.transport} (remote).`);
85122
+ return { results: [], exitCode: 1 };
85123
+ }
85124
+ entry = { ...entry, args: [...entry.args ?? [], ...opts.extraArgs] };
85125
+ }
85126
+ if (prompt) {
85127
+ const chosen = await selectAgents2(verb, entry.name, targets, { yes: opts.yes });
85128
+ if (chosen === null) {
85129
+ logger.info("Cancelled.");
85130
+ return { results: [], exitCode: 1 };
85131
+ }
85132
+ if (chosen.length === 0) {
85133
+ logger.warning(`No agents selected — nothing to ${verb}.`);
85134
+ return { results: [], exitCode: 1 };
85135
+ }
85136
+ targets = chosen;
85137
+ }
85138
+ const writerOpts = { scope: opts.scope };
85139
+ const results = [];
85140
+ for (const agent of targets) {
85141
+ const writer = deps.getWriter(agent);
85142
+ results.push(verb === "add" ? await writer.add(entry, writerOpts) : await writer.remove(entry.name, writerOpts));
85143
+ }
85144
+ renderResults(results);
85145
+ const allFailed = results.length > 0 && results.every((r2) => r2.status === "failed");
85146
+ return { results, exitCode: allFailed ? 1 : 0 };
85147
+ } finally {
85148
+ await resolved.revalidated;
85149
+ }
85150
+ }
85151
+ async function runAdd(opts, deps = defaultDeps2) {
85152
+ return runMutation("add", opts, deps);
85153
+ }
85154
+ async function runRemove(opts, deps = defaultDeps2) {
85155
+ return runMutation("remove", opts, deps);
85156
+ }
85157
+ async function runList(deps = defaultDeps2) {
85158
+ const { registry, source, revalidated } = await deps.getRegistry();
85159
+ const projectAgents = deps.detectProjectAgents();
85160
+ const detected = projectAgents.length > 0 ? projectAgents : deps.detectAgents();
85161
+ const lookups = await Promise.all(detected.map(async (agent) => ({
85162
+ agent,
85163
+ lookup: await deps.getWriter(agent).listConfigured()
85164
+ })));
85165
+ const entries = registry.services.map((entry) => ({
85166
+ entry,
85167
+ perAgent: lookups.map(({ agent, lookup }) => ({
85168
+ agent,
85169
+ configured: lookup.has(entry.name)
85170
+ }))
85171
+ }));
85172
+ return { entries, source, revalidated };
85173
+ }
85174
+
85175
+ // src/commands/mcp/mutation-command.ts
85176
+ init_logger();
85177
+
85178
+ // src/commands/mcp/agent-option.ts
85179
+ class InvalidAgentOptionError extends Error {
85180
+ invalidValues;
85181
+ constructor(invalidValues) {
85182
+ super(`Invalid --agent value(s): ${invalidValues.join(", ")}. Accepted agents: ${ALL_MCP_AGENTS.join(", ")}.`);
85183
+ this.invalidValues = invalidValues;
85184
+ this.name = "InvalidAgentOptionError";
85185
+ }
85186
+ }
85187
+ function normalizeAgentOption(agent) {
85188
+ if (agent === undefined)
85189
+ return;
85190
+ const values = Array.isArray(agent) ? agent : [agent];
85191
+ const invalid = values.filter((value) => !McpAgentSchema.safeParse(value).success);
85192
+ if (invalid.length > 0) {
85193
+ throw new InvalidAgentOptionError(invalid);
85194
+ }
85195
+ return values;
85196
+ }
85197
+
85198
+ // src/commands/mcp/scope-option.ts
85199
+ class InvalidScopeOptionError extends Error {
85200
+ invalidValue;
85201
+ constructor(invalidValue) {
85202
+ super(`Invalid --scope value: ${invalidValue}. Accepted scopes: ${ALL_MCP_SCOPES.join(", ")}.`);
85203
+ this.invalidValue = invalidValue;
85204
+ this.name = "InvalidScopeOptionError";
85205
+ }
85206
+ }
85207
+ function normalizeScopeOption(scope) {
85208
+ if (scope === undefined)
85209
+ return DEFAULT_MCP_SCOPE;
85210
+ const parsed = McpScopeSchema.safeParse(scope);
85211
+ if (!parsed.success) {
85212
+ throw new InvalidScopeOptionError(scope);
85213
+ }
85214
+ return parsed.data;
85215
+ }
85216
+
85217
+ // src/commands/mcp/mutation-command.ts
85218
+ async function mutationCommand(verb, service, options2 = {}) {
85219
+ if (!service) {
85220
+ logger.error(`Usage: tkm mcp ${verb} <service> [--agent <name>...] [-s, --scope local|user|project] [--yes]${verb === "add" ? " [-- <extra args...>]" : ""}`);
85221
+ process.exitCode = 1;
85222
+ return;
85223
+ }
85224
+ const extraArgs = options2["--"] ?? [];
85225
+ if (verb === "remove" && extraArgs.length > 0) {
85226
+ logger.warning(`Extra args after "--" only apply to add — ignored for remove.`);
85227
+ }
85228
+ let agents;
85229
+ try {
85230
+ agents = normalizeAgentOption(options2.agent);
85231
+ } catch (error) {
85232
+ if (error instanceof InvalidAgentOptionError) {
85233
+ logger.error(error.message);
85234
+ process.exitCode = 1;
85235
+ return;
85236
+ }
85237
+ throw error;
85238
+ }
85239
+ let scope;
85240
+ try {
85241
+ scope = normalizeScopeOption(options2.scope);
85242
+ } catch (error) {
85243
+ if (error instanceof InvalidScopeOptionError) {
85244
+ logger.error(error.message);
85245
+ process.exitCode = 1;
85246
+ return;
85247
+ }
85248
+ throw error;
85249
+ }
85250
+ const run2 = verb === "add" ? runAdd : runRemove;
85251
+ try {
85252
+ const { exitCode } = await run2({
85253
+ service,
85254
+ agents,
85255
+ scope,
85256
+ yes: options2.yes,
85257
+ extraArgs: verb === "add" ? extraArgs : undefined
85258
+ });
85259
+ process.exitCode = exitCode;
85260
+ } catch (error) {
85261
+ if (error instanceof RegistryUnavailableError) {
85262
+ logger.error(error.message);
85263
+ process.exitCode = 1;
85264
+ return;
85265
+ }
85266
+ throw error;
85267
+ }
85268
+ }
85269
+
85270
+ // src/commands/mcp/add-command.ts
85271
+ async function addCommand(service, options2 = {}) {
85272
+ await mutationCommand("add", service, options2);
85273
+ }
85274
+ // src/commands/mcp/list-command.ts
85275
+ init_logger();
85276
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
85277
+ function statusCell(configured) {
85278
+ return configured ? { value: "● configured", paint: import_picocolors25.default.green } : { value: "○ not set", paint: import_picocolors25.default.dim };
85279
+ }
85280
+ function renderTable(header, rows) {
85281
+ const lastCol = header.length - 1;
85282
+ const widths = header.map((h2, col) => Math.max(h2.length, ...rows.map((r2) => r2[col]?.value.length ?? 0)));
85283
+ const padPlain = (text, col) => col === lastCol ? text : text.padEnd(widths[col]);
85284
+ const headerLine = import_picocolors25.default.bold(header.map((h2, col) => padPlain(h2, col)).join(" ").trimEnd());
85285
+ const rowLine = (cells) => cells.map((cell, col) => {
85286
+ const padded = padPlain(cell.value, col);
85287
+ return cell.paint ? cell.paint(padded) : padded;
85288
+ }).join(" ").trimEnd();
85289
+ return [headerLine, ...rows.map(rowLine)].join(`
85290
+ `);
85291
+ }
85292
+ async function listCommand(options2 = {}) {
85293
+ let listed;
85294
+ try {
85295
+ listed = await runList();
85296
+ } catch (error) {
85297
+ if (error instanceof RegistryUnavailableError) {
85298
+ logger.error(error.message);
85299
+ process.exitCode = 1;
85300
+ return;
85301
+ }
85302
+ throw error;
85303
+ }
85304
+ const { entries, source, revalidated } = listed;
85305
+ if (options2.json) {
85306
+ const services = entries.map(({ entry, perAgent }) => ({
85307
+ name: entry.name,
85308
+ description: entry.description,
85309
+ transport: entry.transport,
85310
+ configured: Object.fromEntries(perAgent.map((p2) => [p2.agent, p2.configured]))
85311
+ }));
85312
+ process.stdout.write(`${JSON.stringify({ source, services })}
85313
+ `);
85314
+ await revalidated;
85315
+ return;
85316
+ }
85317
+ if (entries.length === 0) {
85318
+ logger.info("No MCP services in the registry.");
85319
+ return;
85320
+ }
85321
+ const agents = entries[0].perAgent.map((p2) => p2.agent);
85322
+ const header = ["NAME", "TRANSPORT", ...agents.map((a3) => a3.toUpperCase()), "DESCRIPTION"];
85323
+ const rows = entries.map(({ entry, perAgent }) => [
85324
+ { value: entry.name },
85325
+ { value: entry.transport },
85326
+ ...perAgent.map((p2) => statusCell(p2.configured)),
85327
+ { value: entry.description }
85328
+ ]);
85329
+ logger.info(`MCP registry — ${entries.length} services (source: ${source})`);
85330
+ if (agents.length === 0) {
85331
+ logger.info("No coding agents detected (claude-code / codex).");
85332
+ }
85333
+ process.stdout.write(`${renderTable(header, rows)}
85334
+ `);
85335
+ await revalidated;
85336
+ }
85337
+ // src/commands/mcp/mcp-command.ts
85338
+ init_logger();
85339
+
85340
+ // src/commands/mcp/remove-command.ts
85341
+ async function removeCommand(service, options2 = {}) {
85342
+ await mutationCommand("remove", service, options2);
85343
+ }
85344
+
85345
+ // src/commands/mcp/mcp-command.ts
85346
+ function printUsage() {
85347
+ logger.error("Usage: tkm mcp <add|list|remove> [service] [options] — see `tkm mcp --help`");
85348
+ }
85349
+ async function mcpCommand(action, service, options2 = {}) {
85350
+ switch (action) {
85351
+ case "add":
85352
+ await addCommand(service, options2);
85353
+ break;
85354
+ case "list":
85355
+ await listCommand(options2);
85356
+ break;
85357
+ case "remove":
85358
+ await removeCommand(service, options2);
85359
+ break;
85360
+ case undefined:
85361
+ printUsage();
85362
+ process.exitCode = 1;
85363
+ break;
85364
+ default:
85365
+ logger.error(`Unknown mcp action: ${action}. Available: add, list, remove`);
85366
+ process.exitCode = 1;
85367
+ }
85368
+ }
84147
85369
  // src/commands/plan/plan-command.ts
84148
85370
  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";
85371
+ import { existsSync as existsSync65, statSync as statSync11 } from "node:fs";
85372
+ import { dirname as dirname42, join as join132, parse as parse4, resolve as resolve33 } from "node:path";
84151
85373
 
84152
85374
  // 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";
85375
+ import { existsSync as existsSync64, statSync as statSync10 } from "node:fs";
85376
+ import { basename as basename21, dirname as dirname41, join as join131, relative as relative20, resolve as resolve31 } from "node:path";
84155
85377
 
84156
85378
  // src/domains/plan-parser/index.ts
84157
- import { dirname as dirname39 } from "node:path";
85379
+ import { dirname as dirname40 } from "node:path";
84158
85380
 
84159
85381
  // src/domains/plan-parser/plan-table-parser.ts
84160
85382
  var import_gray_matter5 = __toESM(require_gray_matter(), 1);
84161
85383
  import { readFileSync as readFileSync23 } from "node:fs";
84162
- import { dirname as dirname36, resolve as resolve30 } from "node:path";
85384
+ import { dirname as dirname37, resolve as resolve30 } from "node:path";
84163
85385
  function normalizeStatus(raw) {
84164
85386
  const s3 = raw.toLowerCase().trim();
84165
85387
  if (s3.includes("complete") || s3.includes("done") || s3.includes("✓") || s3.includes("✅")) {
@@ -84420,7 +85642,7 @@ function parseFormat4(content, planFilePath, options2) {
84420
85642
  const hasCheck = /[✅✓]/.test(line);
84421
85643
  current = { name: name2, status: hasCheck ? "completed" : "pending" };
84422
85644
  } else if (fileMatch && current) {
84423
- const planDir = dirname36(planFilePath);
85645
+ const planDir = dirname37(planFilePath);
84424
85646
  current.file = resolve30(planDir, fileMatch[1].trim());
84425
85647
  } else if (statusMatch && current) {
84426
85648
  current.status = normalizeStatus(statusMatch[2]);
@@ -84525,30 +85747,30 @@ function parsePhasesFromBody(body, dir, options2) {
84525
85747
  }
84526
85748
  function parsePlanFile(planFilePath, options2) {
84527
85749
  const content = readFileSync23(planFilePath, "utf8");
84528
- const dir = dirname36(planFilePath);
85750
+ const dir = dirname37(planFilePath);
84529
85751
  const { data: frontmatter, content: body } = import_gray_matter5.default(content);
84530
85752
  const phases = parsePhasesFromBody(body, dir, options2);
84531
85753
  return { frontmatter, phases };
84532
85754
  }
84533
85755
  // 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";
85756
+ import { existsSync as existsSync61, readdirSync as readdirSync10 } from "node:fs";
85757
+ import { join as join129 } from "node:path";
84536
85758
  function scanPlanDir(dir) {
84537
- if (!existsSync58(dir))
85759
+ if (!existsSync61(dir))
84538
85760
  return [];
84539
85761
  try {
84540
- return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join126(dir, entry.name, "plan.md")).filter(existsSync58);
85762
+ return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join129(dir, entry.name, "plan.md")).filter(existsSync61);
84541
85763
  } catch {
84542
85764
  return [];
84543
85765
  }
84544
85766
  }
84545
85767
  // src/domains/plan-parser/plan-validator.ts
84546
85768
  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";
85769
+ import { existsSync as existsSync62, readFileSync as readFileSync24 } from "node:fs";
85770
+ import { basename as basename19, dirname as dirname38 } from "node:path";
84549
85771
  function validatePlanFile(filePath, strict = false) {
84550
85772
  const content = readFileSync24(filePath, "utf8");
84551
- const dir = dirname37(filePath);
85773
+ const dir = dirname38(filePath);
84552
85774
  const issues = [];
84553
85775
  const lines = content.split(`
84554
85776
  `);
@@ -84584,7 +85806,7 @@ function validatePlanFile(filePath, strict = false) {
84584
85806
  });
84585
85807
  }
84586
85808
  for (const phase of phases) {
84587
- if (phase.file && !existsSync59(phase.file)) {
85809
+ if (phase.file && !existsSync62(phase.file)) {
84588
85810
  const fileBasename = basename19(phase.file);
84589
85811
  const refLine = lines.findIndex((l2) => l2.includes(fileBasename));
84590
85812
  issues.push({
@@ -84605,8 +85827,8 @@ function validatePlanFile(filePath, strict = false) {
84605
85827
  // src/domains/plan-parser/plan-writer.ts
84606
85828
  var import_gray_matter7 = __toESM(require_gray_matter(), 1);
84607
85829
  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";
85830
+ import { existsSync as existsSync63 } from "node:fs";
85831
+ import { basename as basename20, dirname as dirname39, join as join130 } from "node:path";
84610
85832
  function phaseNameToFilename(id, name2) {
84611
85833
  const numMatch = /^(\d+)([a-z]*)$/i.exec(id);
84612
85834
  const num4 = numMatch ? numMatch[1] : id;
@@ -84714,12 +85936,12 @@ function scaffoldPlan(options2) {
84714
85936
  mkdirSync9(dir, { recursive: true });
84715
85937
  const resolvedPhases = resolvePhaseIds(options2.phases);
84716
85938
  const optionsWithResolved = { ...options2, phases: resolvedPhases };
84717
- const planFile = join127(dir, "plan.md");
85939
+ const planFile = join130(dir, "plan.md");
84718
85940
  writeFileSync12(planFile, generatePlanMd(optionsWithResolved), "utf8");
84719
85941
  const phaseFiles = [];
84720
85942
  for (const phase of resolvedPhases) {
84721
85943
  const filename = phaseNameToFilename(phase.id, phase.name);
84722
- const phaseFile = join127(dir, filename);
85944
+ const phaseFile = join130(dir, filename);
84723
85945
  writeFileSync12(phaseFile, generatePhaseTemplate(phase), "utf8");
84724
85946
  phaseFiles.push(phaseFile);
84725
85947
  }
@@ -84785,9 +86007,9 @@ function updatePhaseStatus(planFile, phaseId, newStatus) {
84785
86007
  const updatedFrontmatter = { ...frontmatter, status: planStatus };
84786
86008
  const updatedContent = import_gray_matter7.default.stringify(updatedBody, updatedFrontmatter);
84787
86009
  writeFileSync12(planFile, updatedContent, "utf8");
84788
- const planDir = dirname38(planFile);
86010
+ const planDir = dirname39(planFile);
84789
86011
  const phaseFilename = phaseNameFilenameFromTableRow(updatedBody, phaseId, planDir);
84790
- if (phaseFilename && existsSync60(phaseFilename)) {
86012
+ if (phaseFilename && existsSync63(phaseFilename)) {
84791
86013
  updatePhaseFileFrontmatter(phaseFilename, newStatus);
84792
86014
  }
84793
86015
  }
@@ -84799,7 +86021,7 @@ function phaseNameFilenameFromTableRow(body, phaseId, planDir) {
84799
86021
  continue;
84800
86022
  const linkMatch = /\[([^\]]+)\]\(\.\/([^)]+)\)/.exec(row);
84801
86023
  if (linkMatch)
84802
- return join127(planDir, linkMatch[2]);
86024
+ return join130(planDir, linkMatch[2]);
84803
86025
  }
84804
86026
  return null;
84805
86027
  }
@@ -84817,7 +86039,7 @@ function addPhase(planFile, name2, afterId) {
84817
86039
  throw new Error("Non-canonical plan.md — cannot add phase");
84818
86040
  }
84819
86041
  const { data: frontmatter, content: body } = import_gray_matter7.default(raw);
84820
- const planDir = dirname38(planFile);
86042
+ const planDir = dirname39(planFile);
84821
86043
  const existingIds = [];
84822
86044
  for (const match of body.matchAll(/^\|\s*(\d+[a-z]?)\s*\|/gim)) {
84823
86045
  existingIds.push(match[1].toLowerCase());
@@ -84880,7 +86102,7 @@ function addPhase(planFile, name2, afterId) {
84880
86102
  `);
84881
86103
  }
84882
86104
  writeFileSync12(planFile, import_gray_matter7.default.stringify(updatedBody, frontmatter), "utf8");
84883
- const phaseFilePath = join127(planDir, filename);
86105
+ const phaseFilePath = join130(planDir, filename);
84884
86106
  writeFileSync12(phaseFilePath, generatePhaseTemplate({ id: phaseId, name: name2 }), "utf8");
84885
86107
  return { phaseId, phaseFile: phaseFilePath };
84886
86108
  }
@@ -84892,7 +86114,7 @@ function buildPlanSummary(planFile) {
84892
86114
  const inProgress = phases.filter((p2) => p2.status === "in-progress").length;
84893
86115
  const pending = phases.filter((p2) => p2.status === "pending").length;
84894
86116
  return {
84895
- planDir: dirname39(planFile),
86117
+ planDir: dirname40(planFile),
84896
86118
  planFile,
84897
86119
  title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
84898
86120
  description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
@@ -84908,7 +86130,7 @@ function buildPlanSummary(planFile) {
84908
86130
  // src/commands/plan/plan-read-handlers.ts
84909
86131
  init_logger();
84910
86132
  init_output_manager();
84911
- var import_picocolors25 = __toESM(require_picocolors(), 1);
86133
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
84912
86134
  async function handleParse(target, options2) {
84913
86135
  const planFile = resolvePlanFile(target);
84914
86136
  if (!planFile) {
@@ -84929,9 +86151,9 @@ async function handleParse(target, options2) {
84929
86151
  console.log(JSON.stringify({ file: relative20(process.cwd(), planFile), frontmatter, phases }, null, 2));
84930
86152
  return;
84931
86153
  }
84932
- const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname40(planFile));
86154
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname41(planFile));
84933
86155
  console.log();
84934
- console.log(import_picocolors25.default.bold(` Plan: ${title}`));
86156
+ console.log(import_picocolors26.default.bold(` Plan: ${title}`));
84935
86157
  console.log(` File: ${planFile}`);
84936
86158
  console.log(` Phases found: ${phases.length}`);
84937
86159
  console.log();
@@ -84962,7 +86184,7 @@ async function handleValidate(target, options2) {
84962
86184
  return;
84963
86185
  }
84964
86186
  console.log();
84965
- console.log(import_picocolors25.default.bold(` Validating: ${planFile}`));
86187
+ console.log(import_picocolors26.default.bold(` Validating: ${planFile}`));
84966
86188
  console.log();
84967
86189
  if (result.issues.length === 0) {
84968
86190
  console.log(` [OK] No issues found — ${result.phases.length} phases detected`);
@@ -84976,7 +86198,7 @@ async function handleValidate(target, options2) {
84976
86198
  }
84977
86199
  }
84978
86200
  console.log();
84979
- const validStr = result.valid ? import_picocolors25.default.green("[OK] Valid") : import_picocolors25.default.red("[X] Invalid");
86201
+ const validStr = result.valid ? import_picocolors26.default.green("[OK] Valid") : import_picocolors26.default.red("[X] Invalid");
84980
86202
  console.log(` ${validStr} — ${result.issues.filter((i) => i.severity === "error").length} errors, ${result.issues.filter((i) => i.severity === "warning").length} warnings`);
84981
86203
  console.log();
84982
86204
  if (!result.valid)
@@ -84984,7 +86206,7 @@ async function handleValidate(target, options2) {
84984
86206
  }
84985
86207
  async function handleStatus(target, options2) {
84986
86208
  const t = target ? resolve31(target) : null;
84987
- const plansDir = t && existsSync61(t) && statSync10(t).isDirectory() && !existsSync61(join128(t, "plan.md")) ? t : null;
86209
+ const plansDir = t && existsSync64(t) && statSync10(t).isDirectory() && !existsSync64(join131(t, "plan.md")) ? t : null;
84988
86210
  if (plansDir) {
84989
86211
  const planFiles = scanPlanDir(plansDir);
84990
86212
  if (planFiles.length === 0) {
@@ -85003,20 +86225,20 @@ async function handleStatus(target, options2) {
85003
86225
  return;
85004
86226
  }
85005
86227
  console.log();
85006
- console.log(import_picocolors25.default.bold(` Plans in: ${plansDir}`));
86228
+ console.log(import_picocolors26.default.bold(` Plans in: ${plansDir}`));
85007
86229
  console.log();
85008
86230
  for (const pf of planFiles) {
85009
86231
  try {
85010
86232
  const s3 = buildPlanSummary(pf);
85011
86233
  const bar = progressBar(s3.completed, s3.totalPhases);
85012
- const title2 = s3.title ?? basename21(dirname40(pf));
85013
- console.log(` ${import_picocolors25.default.bold(title2)}`);
86234
+ const title2 = s3.title ?? basename21(dirname41(pf));
86235
+ console.log(` ${import_picocolors26.default.bold(title2)}`);
85014
86236
  console.log(` ${bar}`);
85015
86237
  if (s3.inProgress > 0)
85016
86238
  console.log(` [~] ${s3.inProgress} in progress`);
85017
86239
  console.log();
85018
86240
  } catch {
85019
- console.log(` [X] Failed to read: ${basename21(dirname40(pf))}`);
86241
+ console.log(` [X] Failed to read: ${basename21(dirname41(pf))}`);
85020
86242
  console.log();
85021
86243
  }
85022
86244
  }
@@ -85040,9 +86262,9 @@ async function handleStatus(target, options2) {
85040
86262
  console.log(JSON.stringify(summary, null, 2));
85041
86263
  return;
85042
86264
  }
85043
- const title = summary.title ?? basename21(dirname40(planFile));
86265
+ const title = summary.title ?? basename21(dirname41(planFile));
85044
86266
  console.log();
85045
- console.log(import_picocolors25.default.bold(` ${title}`));
86267
+ console.log(import_picocolors26.default.bold(` ${title}`));
85046
86268
  if (summary.status)
85047
86269
  console.log(` Status: ${summary.status}`);
85048
86270
  console.log();
@@ -85068,7 +86290,7 @@ async function handleKanban(target, _options) {
85068
86290
  // src/commands/plan/plan-write-handlers.ts
85069
86291
  import { basename as basename22, relative as relative21, resolve as resolve32 } from "node:path";
85070
86292
  init_output_manager();
85071
- var import_picocolors26 = __toESM(require_picocolors(), 1);
86293
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
85072
86294
  async function handleCreate(target, options2) {
85073
86295
  if (!options2.title) {
85074
86296
  output.error("[X] --title is required for create");
@@ -85115,7 +86337,7 @@ async function handleCreate(target, options2) {
85115
86337
  return;
85116
86338
  }
85117
86339
  console.log();
85118
- console.log(import_picocolors26.default.bold(` [OK] Plan created: ${options2.title}`));
86340
+ console.log(import_picocolors27.default.bold(` [OK] Plan created: ${options2.title}`));
85119
86341
  console.log(` Directory: ${resolve32(dir)}`);
85120
86342
  console.log(` Phases: ${result.phaseFiles.length}`);
85121
86343
  for (const f4 of result.phaseFiles) {
@@ -85215,22 +86437,22 @@ async function handleAddPhase(target, options2) {
85215
86437
  // src/commands/plan/plan-command.ts
85216
86438
  function resolvePlanFile(target) {
85217
86439
  const t = target ? resolve33(target) : process.cwd();
85218
- if (existsSync62(t)) {
86440
+ if (existsSync65(t)) {
85219
86441
  const stat14 = statSync11(t);
85220
86442
  if (stat14.isFile())
85221
86443
  return t;
85222
- const candidate = join129(t, "plan.md");
85223
- if (existsSync62(candidate))
86444
+ const candidate = join132(t, "plan.md");
86445
+ if (existsSync65(candidate))
85224
86446
  return candidate;
85225
86447
  }
85226
86448
  if (!target) {
85227
86449
  let dir = process.cwd();
85228
86450
  const root = parse4(dir).root;
85229
86451
  while (dir !== root) {
85230
- const candidate = join129(dir, "plan.md");
85231
- if (existsSync62(candidate))
86452
+ const candidate = join132(dir, "plan.md");
86453
+ if (existsSync65(candidate))
85232
86454
  return candidate;
85233
- dir = dirname41(dir);
86455
+ dir = dirname42(dir);
85234
86456
  }
85235
86457
  }
85236
86458
  return null;
@@ -85278,7 +86500,7 @@ async function planCommand(action, target, options2) {
85278
86500
  let resolvedTarget = target;
85279
86501
  if (resolvedAction && !knownActions.has(resolvedAction)) {
85280
86502
  const looksLikePath = resolvedAction.includes("/") || resolvedAction.includes("\\") || resolvedAction.endsWith(".md") || resolvedAction === "." || resolvedAction === "..";
85281
- const existsOnDisk = !looksLikePath && existsSync62(resolve33(resolvedAction));
86503
+ const existsOnDisk = !looksLikePath && existsSync65(resolve33(resolvedAction));
85282
86504
  if (looksLikePath || existsOnDisk) {
85283
86505
  resolvedTarget = resolvedAction;
85284
86506
  resolvedAction = undefined;
@@ -85322,22 +86544,22 @@ init_logger();
85322
86544
  init_logger();
85323
86545
 
85324
86546
  // src/commands/telemetry/shared.ts
85325
- import { existsSync as existsSync63, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
86547
+ import { existsSync as existsSync66, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
85326
86548
  import { homedir as homedir29 } from "node:os";
85327
- import { join as join130 } from "node:path";
86549
+ import { join as join133 } from "node:path";
85328
86550
  init_token_store();
85329
86551
  init_manifest_path_resolver();
85330
86552
  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);
86553
+ var USER_CACHE_PATH = join133(homedir29(), ".claude", "sk-user.json");
86554
+ var EVENT_BUFFER_DIR = join133(homedir29(), ".claude", "sk-events");
86555
+ var RATE_STATE_PATH = join133(homedir29(), ".claude", "sk-rate-state.json");
86556
+ var TAKUMI_MANIFEST_PATH = join133(homedir29(), ".claude", MANIFEST_FILENAME);
86557
+ var LEGACY_METADATA_PATH = join133(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
85336
86558
  var TELEMETRY_HOOK_FIELD = "hooks.telemetry";
85337
86559
  var TOKEN_PLACEHOLDER = "__INJECT_AT_RELEASE__";
85338
86560
  function readUserCache() {
85339
86561
  try {
85340
- if (!existsSync63(USER_CACHE_PATH))
86562
+ if (!existsSync66(USER_CACHE_PATH))
85341
86563
  return null;
85342
86564
  const parsed = JSON.parse(readFileSync26(USER_CACHE_PATH, "utf8"));
85343
86565
  if (!parsed || typeof parsed !== "object")
@@ -85349,7 +86571,7 @@ function readUserCache() {
85349
86571
  }
85350
86572
  function countBufferFiles() {
85351
86573
  try {
85352
- if (!existsSync63(EVENT_BUFFER_DIR))
86574
+ if (!existsSync66(EVENT_BUFFER_DIR))
85353
86575
  return 0;
85354
86576
  return readdirSync11(EVENT_BUFFER_DIR).filter((f4) => f4.endsWith(".jsonl")).length;
85355
86577
  } catch {
@@ -85361,7 +86583,7 @@ function readTelemetryConfig() {
85361
86583
  const envToken = process.env.TAKUMI_TELEMETRY_TOKEN;
85362
86584
  let metadata = null;
85363
86585
  try {
85364
- const resolved = findManifestPathSync(join130(homedir29(), ".claude"));
86586
+ const resolved = findManifestPathSync(join133(homedir29(), ".claude"));
85365
86587
  if (resolved) {
85366
86588
  metadata = JSON.parse(readFileSync26(resolved.path, "utf8"));
85367
86589
  }
@@ -85387,8 +86609,8 @@ function collectRuntimeContext() {
85387
86609
  cacheSource: cache3?.source === "gh" || cache3?.source === "manual" ? cache3.source : null,
85388
86610
  bufferFileCount: countBufferFiles(),
85389
86611
  bufferDir: EVENT_BUFFER_DIR,
85390
- rateStateExists: existsSync63(RATE_STATE_PATH),
85391
- userCacheExists: existsSync63(USER_CACHE_PATH),
86612
+ rateStateExists: existsSync66(RATE_STATE_PATH),
86613
+ userCacheExists: existsSync66(USER_CACHE_PATH),
85392
86614
  endpoint,
85393
86615
  tokenConfigured: Boolean(token)
85394
86616
  };
@@ -85489,7 +86711,7 @@ init_manifest_writer();
85489
86711
  init_logger();
85490
86712
  init_safe_prompts();
85491
86713
  init_types2();
85492
- var import_picocolors28 = __toESM(require_picocolors(), 1);
86714
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
85493
86715
 
85494
86716
  // src/commands/uninstall/installation-detector.ts
85495
86717
  init_paths();
@@ -85539,7 +86761,7 @@ init_safe_prompts();
85539
86761
  init_safe_spinner();
85540
86762
  var import_fs_extra37 = __toESM(require_lib(), 1);
85541
86763
  import { readdirSync as readdirSync13, rmSync as rmSync9 } from "node:fs";
85542
- import { join as join132, resolve as resolve34, sep as sep9 } from "node:path";
86764
+ import { join as join135, resolve as resolve34, sep as sep9 } from "node:path";
85543
86765
 
85544
86766
  // src/commands/uninstall/analysis-handler.ts
85545
86767
  init_metadata_migration();
@@ -85548,14 +86770,14 @@ init_ownership_checker();
85548
86770
  init_logger();
85549
86771
  init_safe_prompts();
85550
86772
  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";
86773
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
86774
+ import { existsSync as existsSync67, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
86775
+ import { dirname as dirname43, join as join134 } from "node:path";
85554
86776
  function listPresentManifestNames(installPath) {
85555
86777
  const present = [];
85556
- if (existsSync64(getManifestPath(installPath)))
86778
+ if (existsSync67(getManifestPath(installPath)))
85557
86779
  present.push(MANIFEST_FILENAME);
85558
- if (existsSync64(getLegacyManifestPath(installPath)))
86780
+ if (existsSync67(getLegacyManifestPath(installPath)))
85559
86781
  present.push(LEGACY_MANIFEST_FILENAME);
85560
86782
  return present;
85561
86783
  }
@@ -85573,7 +86795,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
85573
86795
  }
85574
86796
  async function cleanupEmptyDirectories3(filePath, installationRoot) {
85575
86797
  let cleaned = 0;
85576
- let currentDir = dirname42(filePath);
86798
+ let currentDir = dirname43(filePath);
85577
86799
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
85578
86800
  try {
85579
86801
  const entries = readdirSync12(currentDir);
@@ -85581,7 +86803,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
85581
86803
  rmSync8(currentDir, { recursive: true });
85582
86804
  cleaned++;
85583
86805
  logger.debug(`Removed empty directory: ${currentDir}`);
85584
- currentDir = dirname42(currentDir);
86806
+ currentDir = dirname43(currentDir);
85585
86807
  } else {
85586
86808
  break;
85587
86809
  }
@@ -85603,7 +86825,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85603
86825
  if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
85604
86826
  const kitFiles = metadata.kits[kit].files || [];
85605
86827
  for (const trackedFile of kitFiles) {
85606
- const filePath = join131(installation.path, trackedFile.path);
86828
+ const filePath = join134(installation.path, trackedFile.path);
85607
86829
  if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
85608
86830
  result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
85609
86831
  continue;
@@ -85635,7 +86857,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85635
86857
  return result;
85636
86858
  }
85637
86859
  for (const trackedFile of allTrackedFiles) {
85638
- const filePath = join131(installation.path, trackedFile.path);
86860
+ const filePath = join134(installation.path, trackedFile.path);
85639
86861
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
85640
86862
  if (!ownershipResult.exists)
85641
86863
  continue;
@@ -85653,27 +86875,27 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85653
86875
  }
85654
86876
  function displayDryRunPreview(analysis, installationType) {
85655
86877
  console.log("");
85656
- log.info(import_picocolors27.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
86878
+ log.info(import_picocolors28.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
85657
86879
  console.log("");
85658
86880
  if (analysis.toDelete.length > 0) {
85659
- console.log(import_picocolors27.default.red(import_picocolors27.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
86881
+ console.log(import_picocolors28.default.red(import_picocolors28.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
85660
86882
  const showDelete = analysis.toDelete.slice(0, 10);
85661
86883
  for (const item of showDelete) {
85662
- console.log(` ${import_picocolors27.default.red("✖")} ${item.path}`);
86884
+ console.log(` ${import_picocolors28.default.red("✖")} ${item.path}`);
85663
86885
  }
85664
86886
  if (analysis.toDelete.length > 10) {
85665
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
86887
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
85666
86888
  }
85667
86889
  console.log("");
85668
86890
  }
85669
86891
  if (analysis.toPreserve.length > 0) {
85670
- console.log(import_picocolors27.default.green(import_picocolors27.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
86892
+ console.log(import_picocolors28.default.green(import_picocolors28.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
85671
86893
  const showPreserve = analysis.toPreserve.slice(0, 10);
85672
86894
  for (const item of showPreserve) {
85673
- console.log(` ${import_picocolors27.default.green("✓")} ${item.path} ${import_picocolors27.default.gray(`(${item.reason})`)}`);
86895
+ console.log(` ${import_picocolors28.default.green("✓")} ${item.path} ${import_picocolors28.default.gray(`(${item.reason})`)}`);
85674
86896
  }
85675
86897
  if (analysis.toPreserve.length > 10) {
85676
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
86898
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
85677
86899
  }
85678
86900
  console.log("");
85679
86901
  }
@@ -85734,7 +86956,7 @@ async function removeInstallations(installations, options2) {
85734
86956
  let removedCount = 0;
85735
86957
  let cleanedDirs = 0;
85736
86958
  for (const item of analysis.toDelete) {
85737
- const filePath = join132(installation.path, item.path);
86959
+ const filePath = join135(installation.path, item.path);
85738
86960
  if (!await import_fs_extra37.pathExists(filePath))
85739
86961
  continue;
85740
86962
  if (!await isPathSafeToRemove(filePath, installation.path)) {
@@ -85795,15 +87017,15 @@ function displayInstallations(installations, scope) {
85795
87017
  const hasLegacy = installations.some((i) => !i.hasMetadata);
85796
87018
  const lines = installations.map((i) => {
85797
87019
  const typeLabel = i.type === "local" ? "Local " : "Global";
85798
- const legacyTag = !i.hasMetadata ? import_picocolors28.default.yellow(" [legacy]") : "";
87020
+ const legacyTag = !i.hasMetadata ? import_picocolors29.default.yellow(" [legacy]") : "";
85799
87021
  const components = formatComponentSummary(i);
85800
87022
  return ` ${typeLabel}: ${i.path}${legacyTag}${components}`;
85801
87023
  });
85802
87024
  prompts.note(lines.join(`
85803
87025
  `), `Detected Takumi installations (${scopeLabel})`);
85804
87026
  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."));
87027
+ log.warn(import_picocolors29.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
87028
+ `) + import_picocolors29.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
85807
87029
  }
85808
87030
  log.warn("[!] This will permanently delete Takumi files from the above paths.");
85809
87031
  }
@@ -85863,7 +87085,7 @@ async function uninstallCommand(options2) {
85863
87085
  }
85864
87086
  const isAtHome = isLocalSameAsGlobal();
85865
87087
  if (validOptions.local && !validOptions.global && isAtHome) {
85866
- log.warn(import_picocolors28.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
87088
+ log.warn(import_picocolors29.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
85867
87089
  log.info("Use -g/--global or run from a project directory.");
85868
87090
  return;
85869
87091
  }
@@ -85875,7 +87097,7 @@ async function uninstallCommand(options2) {
85875
87097
  } else if (validOptions.global) {
85876
87098
  scope = "global";
85877
87099
  } else if (isAtHome) {
85878
- log.info(import_picocolors28.default.cyan("Running at HOME directory - targeting global installation"));
87100
+ log.info(import_picocolors29.default.cyan("Running at HOME directory - targeting global installation"));
85879
87101
  scope = "global";
85880
87102
  } else {
85881
87103
  const promptedScope = await promptScope(allInstallations);
@@ -85897,10 +87119,10 @@ async function uninstallCommand(options2) {
85897
87119
  }
85898
87120
  displayInstallations(installations, scope);
85899
87121
  if (validOptions.kit) {
85900
- log.info(import_picocolors28.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
87122
+ log.info(import_picocolors29.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
85901
87123
  }
85902
87124
  if (validOptions.dryRun) {
85903
- log.info(import_picocolors28.default.yellow("DRY RUN MODE - No files will be deleted"));
87125
+ log.info(import_picocolors29.default.yellow("DRY RUN MODE - No files will be deleted"));
85904
87126
  await removeInstallations(installations, {
85905
87127
  dryRun: true,
85906
87128
  forceOverwrite: validOptions.forceOverwrite,
@@ -85910,8 +87132,8 @@ async function uninstallCommand(options2) {
85910
87132
  return;
85911
87133
  }
85912
87134
  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!")}`);
87135
+ log.warn(`${import_picocolors29.default.yellow(import_picocolors29.default.bold("FORCE MODE ENABLED"))}
87136
+ ${import_picocolors29.default.yellow("User modifications will be permanently deleted!")}`);
85915
87137
  }
85916
87138
  if (!validOptions.yes) {
85917
87139
  const kitLabel = validOptions.kit ? ` (${validOptions.kit} kit only)` : "";
@@ -86364,7 +87586,7 @@ init_auth_client();
86364
87586
  init_github_client();
86365
87587
  init_logger();
86366
87588
  init_types2();
86367
- var import_picocolors29 = __toESM(require_picocolors(), 1);
87589
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
86368
87590
  function formatRelativeTime(dateString) {
86369
87591
  if (!dateString)
86370
87592
  return "Unknown";
@@ -86386,21 +87608,21 @@ function formatRelativeTime(dateString) {
86386
87608
  }
86387
87609
  function displayKitReleases(kitName, releases) {
86388
87610
  console.log(`
86389
- ${import_picocolors29.default.bold(import_picocolors29.default.cyan(kitName))} - Available Versions:
87611
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(kitName))} - Available Versions:
86390
87612
  `);
86391
87613
  if (releases.length === 0) {
86392
- console.log(import_picocolors29.default.dim(" No releases found"));
87614
+ console.log(import_picocolors30.default.dim(" No releases found"));
86393
87615
  return;
86394
87616
  }
86395
87617
  for (const release of releases) {
86396
- const version3 = import_picocolors29.default.green(release.tag);
87618
+ const version3 = import_picocolors30.default.green(release.tag);
86397
87619
  const publishedAt = formatRelativeTime(release.publishedAt);
86398
- const badge = release.prerelease ? ` ${import_picocolors29.default.yellow("[prerelease]")}` : "";
87620
+ const badge = release.prerelease ? ` ${import_picocolors30.default.yellow("[prerelease]")}` : "";
86399
87621
  const versionPart = version3.padEnd(20);
86400
- const timePart = import_picocolors29.default.dim(publishedAt.padEnd(20));
87622
+ const timePart = import_picocolors30.default.dim(publishedAt.padEnd(20));
86401
87623
  console.log(` ${versionPart} ${timePart}${badge}`);
86402
87624
  }
86403
- console.log(import_picocolors29.default.dim(`
87625
+ console.log(import_picocolors30.default.dim(`
86404
87626
  Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
86405
87627
  }
86406
87628
  async function fetchReleasesForKit(kitType, options2) {
@@ -86445,8 +87667,8 @@ async function versionCommand(options2) {
86445
87667
  for (const result of results) {
86446
87668
  if (result.error) {
86447
87669
  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}`));
87670
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(result.kitConfig.name))} - ${import_picocolors30.default.red("Error")}`);
87671
+ console.log(import_picocolors30.default.dim(` ${result.error}`));
86450
87672
  } else {
86451
87673
  displayKitReleases(result.kitConfig.name, result.releases);
86452
87674
  }
@@ -86592,6 +87814,9 @@ function registerCommands(cli) {
86592
87814
  process.exitCode = 1;
86593
87815
  }
86594
87816
  });
87817
+ 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 = {}) => {
87818
+ await mcpCommand(action, service, options2);
87819
+ });
86595
87820
  }
86596
87821
 
86597
87822
  // src/cli/version-display.ts
@@ -86603,7 +87828,7 @@ init_manifest_path_resolver();
86603
87828
  init_logger();
86604
87829
  init_types2();
86605
87830
  import { readFileSync as readFileSync27 } from "node:fs";
86606
- import { join as join133 } from "node:path";
87831
+ import { join as join136 } from "node:path";
86607
87832
  var PROVIDER_LOCAL_SUBDIRS = {
86608
87833
  "claude-code": ".claude",
86609
87834
  codex: ".codex"
@@ -86658,7 +87883,7 @@ async function displayVersion() {
86658
87883
  const localSubdir = PROVIDER_LOCAL_SUBDIRS[provider];
86659
87884
  if (!localSubdir)
86660
87885
  continue;
86661
- const localRoot = join133(process.cwd(), localSubdir);
87886
+ const localRoot = join136(process.cwd(), localSubdir);
86662
87887
  if (localRoot === inst.globalRoot())
86663
87888
  continue;
86664
87889
  const resolved = findManifestPathSync(localRoot);
@@ -86739,7 +87964,7 @@ function getPackageVersion3() {
86739
87964
 
86740
87965
  // src/shared/logger.ts
86741
87966
  init_output_manager();
86742
- var import_picocolors30 = __toESM(require_picocolors(), 1);
87967
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
86743
87968
  import { createWriteStream as createWriteStream4 } from "node:fs";
86744
87969
 
86745
87970
  class Logger2 {
@@ -86748,23 +87973,23 @@ class Logger2 {
86748
87973
  exitHandlerRegistered = false;
86749
87974
  info(message) {
86750
87975
  const symbols = output.getSymbols();
86751
- console.log(import_picocolors30.default.blue(symbols.info), message);
87976
+ console.log(import_picocolors31.default.blue(symbols.info), message);
86752
87977
  }
86753
87978
  success(message) {
86754
87979
  const symbols = output.getSymbols();
86755
- console.log(import_picocolors30.default.green(symbols.success), message);
87980
+ console.log(import_picocolors31.default.green(symbols.success), message);
86756
87981
  }
86757
87982
  warning(message) {
86758
87983
  const symbols = output.getSymbols();
86759
- console.log(import_picocolors30.default.yellow(symbols.warning), message);
87984
+ console.log(import_picocolors31.default.yellow(symbols.warning), message);
86760
87985
  }
86761
87986
  error(message) {
86762
87987
  const symbols = output.getSymbols();
86763
- console.error(import_picocolors30.default.red(symbols.error), message);
87988
+ console.error(import_picocolors31.default.red(symbols.error), message);
86764
87989
  }
86765
87990
  debug(message) {
86766
87991
  if (process.env.DEBUG) {
86767
- console.log(import_picocolors30.default.gray("[DEBUG]"), message);
87992
+ console.log(import_picocolors31.default.gray("[DEBUG]"), message);
86768
87993
  }
86769
87994
  }
86770
87995
  verbose(message, context) {
@@ -86773,7 +87998,7 @@ class Logger2 {
86773
87998
  const timestamp = this.getTimestamp();
86774
87999
  const sanitizedMessage = this.sanitize(message);
86775
88000
  const formattedContext = context ? this.formatContext(context) : "";
86776
- const logLine = `${timestamp} ${import_picocolors30.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
88001
+ const logLine = `${timestamp} ${import_picocolors31.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
86777
88002
  console.error(logLine);
86778
88003
  if (this.logFileStream) {
86779
88004
  const plainLogLine = `${timestamp} [VERBOSE] ${sanitizedMessage}${formattedContext}`;
@@ -86876,7 +88101,7 @@ var logger3 = new Logger2;
86876
88101
 
86877
88102
  // src/shared/output-manager.ts
86878
88103
  init_terminal_utils();
86879
- var import_picocolors31 = __toESM(require_picocolors(), 1);
88104
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
86880
88105
  var SYMBOLS2 = {
86881
88106
  unicode: {
86882
88107
  prompt: "◇",
@@ -86957,7 +88182,7 @@ class OutputManager2 {
86957
88182
  if (this.config.quiet)
86958
88183
  return;
86959
88184
  const symbol = this.getSymbols().success;
86960
- console.log(import_picocolors31.default.green(`${symbol} ${message}`));
88185
+ console.log(import_picocolors32.default.green(`${symbol} ${message}`));
86961
88186
  }
86962
88187
  error(message, data) {
86963
88188
  if (this.config.json) {
@@ -86965,7 +88190,7 @@ class OutputManager2 {
86965
88190
  return;
86966
88191
  }
86967
88192
  const symbol = this.getSymbols().error;
86968
- console.error(import_picocolors31.default.red(`${symbol} ${message}`));
88193
+ console.error(import_picocolors32.default.red(`${symbol} ${message}`));
86969
88194
  }
86970
88195
  warning(message, data) {
86971
88196
  if (this.config.json) {
@@ -86975,7 +88200,7 @@ class OutputManager2 {
86975
88200
  if (this.config.quiet)
86976
88201
  return;
86977
88202
  const symbol = this.getSymbols().warning;
86978
- console.log(import_picocolors31.default.yellow(`${symbol} ${message}`));
88203
+ console.log(import_picocolors32.default.yellow(`${symbol} ${message}`));
86979
88204
  }
86980
88205
  info(message, data) {
86981
88206
  if (this.config.json) {
@@ -86985,7 +88210,7 @@ class OutputManager2 {
86985
88210
  if (this.config.quiet)
86986
88211
  return;
86987
88212
  const symbol = this.getSymbols().info;
86988
- console.log(import_picocolors31.default.blue(`${symbol} ${message}`));
88213
+ console.log(import_picocolors32.default.blue(`${symbol} ${message}`));
86989
88214
  }
86990
88215
  verbose(message, data) {
86991
88216
  if (!this.config.verbose)
@@ -86994,7 +88219,7 @@ class OutputManager2 {
86994
88219
  this.addJsonEntry({ type: "info", message, data });
86995
88220
  return;
86996
88221
  }
86997
- console.log(import_picocolors31.default.dim(` ${message}`));
88222
+ console.log(import_picocolors32.default.dim(` ${message}`));
86998
88223
  }
86999
88224
  indent(message) {
87000
88225
  if (this.config.json)
@@ -87019,7 +88244,7 @@ class OutputManager2 {
87019
88244
  return;
87020
88245
  const symbols = this.getSymbols();
87021
88246
  console.log();
87022
- console.log(import_picocolors31.default.bold(import_picocolors31.default.cyan(`${symbols.line} ${title}`)));
88247
+ console.log(import_picocolors32.default.bold(import_picocolors32.default.cyan(`${symbols.line} ${title}`)));
87023
88248
  }
87024
88249
  addJsonEntry(entry) {
87025
88250
  this.jsonBuffer.push({