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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1148 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19815,7 +19815,7 @@ var package_default;
19815
19815
  var init_package = __esm(() => {
19816
19816
  package_default = {
19817
19817
  name: "@sunasteriskrnd/takumi",
19818
- version: "1.0.0-dev.44",
19818
+ version: "1.0.0-dev.45",
19819
19819
  description: "CLI tool for bootstrapping and managing Takumi projects",
19820
19820
  type: "module",
19821
19821
  repository: {
@@ -49807,6 +49807,73 @@ var init_artifact_command_help = __esm(() => {
49807
49807
  };
49808
49808
  });
49809
49809
 
49810
+ // src/domains/help/commands/mcp-command-help.ts
49811
+ var mcpCommandHelp;
49812
+ var init_mcp_command_help = __esm(() => {
49813
+ mcpCommandHelp = {
49814
+ name: "mcp",
49815
+ description: "Manage internal MCP connectors for coding agents (add|list|remove)",
49816
+ usage: "tkm mcp <add|list|remove> [service] [options] [-- <extra args...>]",
49817
+ examples: [
49818
+ {
49819
+ command: "tkm mcp list",
49820
+ description: "Show available internal MCP services and per-agent configured state"
49821
+ },
49822
+ {
49823
+ command: "tkm mcp add meet-plus",
49824
+ description: "Register the 'meet-plus' MCP with the current project's coding agent(s)"
49825
+ },
49826
+ {
49827
+ command: "tkm mcp add playwright -- --browser msedge --headless",
49828
+ description: "Add a stdio MCP with extra launch args appended after the registry defaults"
49829
+ }
49830
+ ],
49831
+ optionGroups: [
49832
+ {
49833
+ title: "Actions",
49834
+ options: [
49835
+ {
49836
+ flags: "add <service> [-- <extra args...>]",
49837
+ description: "Add a registry service to detected agent(s); argv after -- is appended to a stdio service's args (e.g. -- --browser msedge)"
49838
+ },
49839
+ {
49840
+ flags: "list",
49841
+ description: "List registry services with per-agent configured status"
49842
+ },
49843
+ {
49844
+ flags: "remove <service>",
49845
+ description: "Remove a service from detected agent(s); absent entries are skipped"
49846
+ }
49847
+ ]
49848
+ },
49849
+ {
49850
+ title: "Options",
49851
+ options: [
49852
+ {
49853
+ flags: "-a, --agent <agents...>",
49854
+ description: "Target specific agent(s): claude-code | codex (repeatable)"
49855
+ },
49856
+ {
49857
+ flags: "-s, --scope <scope>",
49858
+ description: "Config scope: local | user | project (default: user; agent-dependent)"
49859
+ },
49860
+ { flags: "-y, --yes", description: "Non-interactive mode: skip selection prompts" },
49861
+ { flags: "--json", description: "Machine-readable JSON output (list only)" }
49862
+ ]
49863
+ }
49864
+ ],
49865
+ sections: [
49866
+ {
49867
+ title: "Registry source",
49868
+ content: `Cache-first: an existing local cache (<TAKUMI_HOME>/mcp/registry-cache.json) is
49869
+ ` + `served immediately while a background request refreshes it for the next run.
49870
+ ` + `With no cache yet, the CLI fetches the Takumi server directly and reports an
49871
+ ` + "error when the server is unreachable (no stale in-package fallback)."
49872
+ }
49873
+ ]
49874
+ };
49875
+ });
49876
+
49810
49877
  // src/domains/help/commands/index.ts
49811
49878
  var init_commands2 = __esm(() => {
49812
49879
  init_init_command_help();
@@ -49817,6 +49884,7 @@ var init_commands2 = __esm(() => {
49817
49884
  init_config_command_help();
49818
49885
  init_auth_command_help();
49819
49886
  init_artifact_command_help();
49887
+ init_mcp_command_help();
49820
49888
  init_common_options();
49821
49889
  });
49822
49890
 
@@ -49836,7 +49904,8 @@ var init_help_commands = __esm(() => {
49836
49904
  doctor: doctorCommandHelp,
49837
49905
  uninstall: uninstallCommandHelp,
49838
49906
  auth: authCommandHelp,
49839
- artifact: artifactCommandHelp
49907
+ artifact: artifactCommandHelp,
49908
+ mcp: mcpCommandHelp
49840
49909
  };
49841
49910
  });
49842
49911
 
@@ -84261,22 +84330,984 @@ async function initCommand(options2) {
84261
84330
  throw error;
84262
84331
  }
84263
84332
  }
84333
+ // src/domains/mcp/mcp-service.ts
84334
+ init_logger();
84335
+
84336
+ // src/domains/mcp/mcp-service-core.ts
84337
+ init_environment();
84338
+ init_logger();
84339
+ init_dist2();
84340
+
84341
+ // src/domains/mcp/agent-detector.ts
84342
+ init_environment();
84343
+ import { existsSync as existsSync58 } from "node:fs";
84344
+ import { join as join126 } from "node:path";
84345
+
84346
+ // src/domains/mcp/shell-out.ts
84347
+ import { spawnSync as spawnSync5 } from "node:child_process";
84348
+ var DEFAULT_TIMEOUT_MS7 = 60000;
84349
+ var PROBE_TIMEOUT_MS = 5000;
84350
+ var SERVICE_NAME_PATTERN = /^[a-z0-9-]+$/;
84351
+ function isValidServiceName(name2) {
84352
+ return SERVICE_NAME_PATTERN.test(name2);
84353
+ }
84354
+ function runAgentCli(bin, args, timeoutMs = DEFAULT_TIMEOUT_MS7) {
84355
+ const result = spawnSync5(bin, args, {
84356
+ timeout: timeoutMs,
84357
+ encoding: "utf-8",
84358
+ shell: false,
84359
+ stdio: ["ignore", "pipe", "pipe"]
84360
+ });
84361
+ if (result.error) {
84362
+ return {
84363
+ ok: false,
84364
+ code: null,
84365
+ stdout: "",
84366
+ stderr: result.error.message
84367
+ };
84368
+ }
84369
+ return {
84370
+ ok: result.status === 0,
84371
+ code: result.status,
84372
+ stdout: result.stdout ?? "",
84373
+ stderr: result.stderr ?? ""
84374
+ };
84375
+ }
84376
+ function isBinaryOnPath(bin) {
84377
+ const probeCmd = process.platform === "win32" ? "where" : "which";
84378
+ const result = spawnSync5(probeCmd, [bin], {
84379
+ timeout: PROBE_TIMEOUT_MS,
84380
+ encoding: "utf-8",
84381
+ shell: false
84382
+ });
84383
+ if (result.error || result.status !== 0)
84384
+ return false;
84385
+ if (process.platform !== "win32")
84386
+ return true;
84387
+ return (result.stdout ?? "").split(/\r?\n/).some((line) => /\.(exe|com)$/i.test(line.trim()));
84388
+ }
84389
+ function isAlreadyExistsError(result) {
84390
+ return /already exists/i.test(`${result.stderr}
84391
+ ${result.stdout}`);
84392
+ }
84393
+
84394
+ // src/domains/mcp/types.ts
84395
+ init_zod();
84396
+ var McpAgentSchema = exports_external.enum(["claude-code", "codex"]);
84397
+ var ALL_MCP_AGENTS = ["claude-code", "codex"];
84398
+ var McpScopeSchema = exports_external.enum(["local", "user", "project"]);
84399
+ var ALL_MCP_SCOPES = ["local", "user", "project"];
84400
+ var DEFAULT_MCP_SCOPE = "user";
84401
+ var McpTransportSchema = exports_external.enum(["http", "sse", "stdio"]);
84402
+ var McpRemoteServiceEntrySchema = exports_external.object({
84403
+ name: exports_external.string(),
84404
+ description: exports_external.string(),
84405
+ transport: exports_external.enum(["http", "sse"]),
84406
+ url: exports_external.string().url()
84407
+ });
84408
+ var McpStdioServiceEntrySchema = exports_external.object({
84409
+ name: exports_external.string(),
84410
+ description: exports_external.string(),
84411
+ transport: exports_external.literal("stdio"),
84412
+ command: exports_external.string().min(1),
84413
+ args: exports_external.array(exports_external.string()).nullish()
84414
+ });
84415
+ var McpServiceEntrySchema = exports_external.union([
84416
+ McpRemoteServiceEntrySchema,
84417
+ McpStdioServiceEntrySchema
84418
+ ]);
84419
+ var McpRegistrySchema = exports_external.object({
84420
+ services: exports_external.array(McpServiceEntrySchema)
84421
+ });
84422
+
84423
+ // src/domains/mcp/agent-detector.ts
84424
+ var AGENT_BINARY = {
84425
+ "claude-code": "claude",
84426
+ codex: "codex"
84427
+ };
84428
+ function getAgentConfigPath(agent) {
84429
+ const home6 = getHomeDirectoryFromEnv();
84430
+ if (!home6)
84431
+ return null;
84432
+ switch (agent) {
84433
+ case "claude-code":
84434
+ return join126(home6, ".claude.json");
84435
+ case "codex":
84436
+ return join126(home6, ".codex", "config.toml");
84437
+ default: {
84438
+ const _exhaustive = agent;
84439
+ return _exhaustive;
84440
+ }
84441
+ }
84442
+ }
84443
+ var defaultCheckers = {
84444
+ binaryOnPath: isBinaryOnPath,
84445
+ configExists: existsSync58
84446
+ };
84447
+ function isAgentPresent(agent, checkers = defaultCheckers) {
84448
+ if (checkers.binaryOnPath(AGENT_BINARY[agent])) {
84449
+ return true;
84450
+ }
84451
+ const configPath = getAgentConfigPath(agent);
84452
+ return configPath !== null && checkers.configExists(configPath);
84453
+ }
84454
+ function detectAgents(checkers = defaultCheckers) {
84455
+ return ALL_MCP_AGENTS.filter((agent) => isAgentPresent(agent, checkers));
84456
+ }
84457
+ var PROJECT_MARKERS = {
84458
+ "claude-code": [".claude", "CLAUDE.md"],
84459
+ codex: [".codex", "AGENTS.md"]
84460
+ };
84461
+ function detectProjectAgents(cwd2 = process.cwd(), exists2 = existsSync58) {
84462
+ return ALL_MCP_AGENTS.filter((agent) => PROJECT_MARKERS[agent].some((marker) => exists2(join126(cwd2, marker))));
84463
+ }
84464
+
84465
+ // src/domains/mcp/registry-client.ts
84466
+ init_zod();
84467
+ init_logger();
84468
+ init_auth_client();
84469
+
84470
+ // src/domains/mcp/registry-cache.ts
84471
+ init_logger();
84472
+ init_paths2();
84473
+ import { promises as fs33 } from "node:fs";
84474
+ import { join as join127 } from "node:path";
84475
+ function getCacheDir() {
84476
+ return join127(getConfigDir(), "mcp");
84477
+ }
84478
+ function getCachePath2() {
84479
+ return join127(getCacheDir(), "registry-cache.json");
84480
+ }
84481
+ async function readCachedRegistry() {
84482
+ try {
84483
+ const raw = await fs33.readFile(getCachePath2(), "utf8");
84484
+ const parsed = JSON.parse(raw);
84485
+ const result = McpRegistrySchema.safeParse({ services: parsed.services });
84486
+ if (!result.success)
84487
+ return null;
84488
+ return {
84489
+ registry: result.data,
84490
+ cachedAt: typeof parsed.cachedAt === "number" ? parsed.cachedAt : 0
84491
+ };
84492
+ } catch (err) {
84493
+ if (err?.code === "ENOENT")
84494
+ return null;
84495
+ logger.verbose(`registry-cache: read failed (${err instanceof Error ? err.message : String(err)})`);
84496
+ return null;
84497
+ }
84498
+ }
84499
+ async function writeCachedRegistry(registry) {
84500
+ const dir = getCacheDir();
84501
+ await fs33.mkdir(dir, { recursive: true, mode: 448 });
84502
+ const body = { services: registry.services, cachedAt: Date.now() };
84503
+ await fs33.writeFile(getCachePath2(), JSON.stringify(body, null, 2), {
84504
+ mode: 384,
84505
+ encoding: "utf8"
84506
+ });
84507
+ }
84508
+
84509
+ // src/domains/mcp/writers/json-config-file.ts
84510
+ import { readFile as readFile44 } from "node:fs/promises";
84511
+ function toMessage(error) {
84512
+ return error instanceof Error ? error.message : String(error);
84513
+ }
84514
+ function isEnoent(error) {
84515
+ return error?.code === "ENOENT";
84516
+ }
84517
+ async function readJsonConfigFile(path11) {
84518
+ let content;
84519
+ try {
84520
+ content = await readFile44(path11, "utf-8");
84521
+ } catch (error) {
84522
+ if (isEnoent(error))
84523
+ return { ok: true, raw: {} };
84524
+ return { ok: false, detail: toMessage(error) };
84525
+ }
84526
+ try {
84527
+ const parsed = JSON.parse(content);
84528
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
84529
+ return { ok: true, raw: parsed };
84530
+ }
84531
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: not a JSON object` };
84532
+ } catch (error) {
84533
+ return { ok: false, detail: `Refusing to overwrite malformed ${path11}: ${toMessage(error)}` };
84534
+ }
84535
+ }
84536
+ function extractMcpServers(raw) {
84537
+ const servers = raw.mcpServers;
84538
+ return servers && typeof servers === "object" && !Array.isArray(servers) ? servers : {};
84539
+ }
84540
+
84541
+ // src/domains/mcp/registry-client.ts
84542
+ class RegistryUnavailableError extends Error {
84543
+ constructor(cause) {
84544
+ super(`Cannot load the MCP registry: ${cause}. Check your network connection (the registry is fetched from the Takumi server) and retry.`);
84545
+ this.name = "RegistryUnavailableError";
84546
+ }
84547
+ }
84548
+ var REGISTRY_PATH2 = "/api/v1/mcp/registry";
84549
+ var REGISTRY_FETCH_TIMEOUT_MS = 3000;
84550
+ var LooseRegistrySchema = exports_external.object({ services: exports_external.array(exports_external.unknown()) });
84551
+ function parseRegistryLenient(data) {
84552
+ const loose = LooseRegistrySchema.parse(data);
84553
+ const services = [];
84554
+ for (const raw of loose.services) {
84555
+ const parsed = McpServiceEntrySchema.safeParse(raw);
84556
+ if (parsed.success) {
84557
+ services.push(parsed.data);
84558
+ } else {
84559
+ const name2 = raw?.name;
84560
+ logger.verbose(`registry: dropped invalid entry ${typeof name2 === "string" ? `"${name2}"` : "(unnamed)"}`);
84561
+ }
84562
+ }
84563
+ if (loose.services.length > 0 && services.length === 0) {
84564
+ throw new Error("registry: every entry failed validation (schema drift?)");
84565
+ }
84566
+ return { services };
84567
+ }
84568
+ async function fetchRemoteRegistry() {
84569
+ const res = await fetch(`${getServerUrl()}${REGISTRY_PATH2}`, {
84570
+ headers: { "X-TKM-Client": "takumi-cli/mcp" },
84571
+ signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
84572
+ });
84573
+ if (!res.ok)
84574
+ throw new Error(`registry: HTTP ${res.status}`);
84575
+ return parseRegistryLenient(await res.json());
84576
+ }
84577
+ async function refreshCache() {
84578
+ try {
84579
+ await writeCachedRegistry(await fetchRemoteRegistry());
84580
+ return true;
84581
+ } catch (err) {
84582
+ logger.verbose(`registry: revalidate skipped (${toMessage(err)})`);
84583
+ return false;
84584
+ }
84585
+ }
84586
+ async function getRegistryWithSource() {
84587
+ const cached = await readCachedRegistry();
84588
+ if (cached) {
84589
+ return { registry: cached.registry, source: "cache", revalidated: refreshCache() };
84590
+ }
84591
+ try {
84592
+ const remote = await fetchRemoteRegistry();
84593
+ try {
84594
+ await writeCachedRegistry(remote);
84595
+ } catch (err) {
84596
+ logger.verbose(`registry: cache write skipped (${toMessage(err)})`);
84597
+ }
84598
+ return { registry: remote, source: "server" };
84599
+ } catch (err) {
84600
+ throw new RegistryUnavailableError(toMessage(err));
84601
+ }
84602
+ }
84603
+ function resolveService(registry, name2) {
84604
+ return registry.services.find((service) => service.name === name2);
84605
+ }
84606
+
84607
+ // src/domains/mcp/writers/claude-config-file.ts
84608
+ import { existsSync as existsSync59 } from "node:fs";
84609
+ import { mkdir as mkdir29, writeFile as writeFile31 } from "node:fs/promises";
84610
+ import { dirname as dirname36, join as join128 } from "node:path";
84611
+ var AGENT = "claude-code";
84612
+ var LOCAL_SCOPE_FALLBACK_WARNING = "claude CLI not found; local scope isn't representable via direct file write, wrote to user-level ~/.claude.json instead";
84613
+ function userConfigPath() {
84614
+ return getAgentConfigPath(AGENT);
84615
+ }
84616
+ function projectConfigPath() {
84617
+ return join128(process.cwd(), ".mcp.json");
84618
+ }
84619
+ function configPathFor(scope) {
84620
+ return scope === "project" ? projectConfigPath() : userConfigPath();
84621
+ }
84622
+ function withLocalScopeFallbackWarning(detail, scope) {
84623
+ if (scope !== "local")
84624
+ return detail;
84625
+ return detail ? `${detail} (${LOCAL_SCOPE_FALLBACK_WARNING})` : LOCAL_SCOPE_FALLBACK_WARNING;
84626
+ }
84627
+ async function fileAdd(entry, scope) {
84628
+ const path11 = configPathFor(scope);
84629
+ if (!path11) {
84630
+ return { agent: AGENT, status: "failed", detail: "Could not resolve home directory" };
84631
+ }
84632
+ const existing = await readJsonConfigFile(path11);
84633
+ if (!existing.ok) {
84634
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84635
+ }
84636
+ try {
84637
+ const servers = extractMcpServers(existing.raw);
84638
+ servers[entry.name] = entry.transport === "stdio" ? {
84639
+ type: "stdio",
84640
+ command: entry.command,
84641
+ ...entry.args?.length ? { args: entry.args } : {}
84642
+ } : { type: entry.transport, url: entry.url };
84643
+ await mkdir29(dirname36(path11), { recursive: true });
84644
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84645
+ return {
84646
+ agent: AGENT,
84647
+ status: "added",
84648
+ detail: withLocalScopeFallbackWarning(`Wrote ${path11} (file fallback)`, scope)
84649
+ };
84650
+ } catch (error) {
84651
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84652
+ }
84653
+ }
84654
+ async function fileRemove(name2, scope) {
84655
+ const path11 = configPathFor(scope);
84656
+ if (!path11 || !existsSync59(path11)) {
84657
+ return { agent: AGENT, status: "skipped", detail: "No config file present" };
84658
+ }
84659
+ const existing = await readJsonConfigFile(path11);
84660
+ if (!existing.ok) {
84661
+ return { agent: AGENT, status: "failed", detail: existing.detail };
84662
+ }
84663
+ try {
84664
+ const servers = extractMcpServers(existing.raw);
84665
+ if (!(name2 in servers)) {
84666
+ return { agent: AGENT, status: "skipped", detail: "Not configured" };
84667
+ }
84668
+ delete servers[name2];
84669
+ await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
84670
+ return {
84671
+ agent: AGENT,
84672
+ status: "removed",
84673
+ detail: withLocalScopeFallbackWarning(`Updated ${path11} (file fallback)`, scope)
84674
+ };
84675
+ } catch (error) {
84676
+ return { agent: AGENT, status: "failed", detail: toMessage(error) };
84677
+ }
84678
+ }
84679
+ async function fileConfigured(name2, scope) {
84680
+ const path11 = configPathFor(scope);
84681
+ if (!path11)
84682
+ return false;
84683
+ const existing = await readJsonConfigFile(path11);
84684
+ return existing.ok && name2 in extractMcpServers(existing.raw);
84685
+ }
84686
+ async function listUserConfigured() {
84687
+ const path11 = userConfigPath();
84688
+ if (!path11)
84689
+ return { has: () => false };
84690
+ const existing = await readJsonConfigFile(path11);
84691
+ if (!existing.ok)
84692
+ return { has: () => false };
84693
+ const servers = extractMcpServers(existing.raw);
84694
+ return { has: (name2) => (name2 in servers) };
84695
+ }
84696
+
84697
+ // src/domains/mcp/writers/claude-writer.ts
84698
+ var AGENT2 = "claude-code";
84699
+ var BIN = "claude";
84700
+ async function add(entry, opts) {
84701
+ if (!isValidServiceName(entry.name)) {
84702
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${entry.name}` };
84703
+ }
84704
+ if (opts.scope !== "local" && await fileConfigured(entry.name, opts.scope)) {
84705
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84706
+ }
84707
+ if (isBinaryOnPath(BIN)) {
84708
+ const argv = entry.transport === "stdio" ? [
84709
+ "mcp",
84710
+ "add",
84711
+ "--scope",
84712
+ opts.scope,
84713
+ entry.name,
84714
+ "--",
84715
+ entry.command,
84716
+ ...entry.args ?? []
84717
+ ] : [
84718
+ "mcp",
84719
+ "add",
84720
+ "--transport",
84721
+ entry.transport,
84722
+ entry.name,
84723
+ entry.url,
84724
+ "--scope",
84725
+ opts.scope
84726
+ ];
84727
+ const result = runAgentCli(BIN, argv);
84728
+ if (result.ok) {
84729
+ return { agent: AGENT2, status: "added", detail: result.stdout.trim() || undefined };
84730
+ }
84731
+ if (isAlreadyExistsError(result)) {
84732
+ return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
84733
+ }
84734
+ return {
84735
+ agent: AGENT2,
84736
+ status: "failed",
84737
+ detail: result.stderr.trim() || `exit code ${result.code}`
84738
+ };
84739
+ }
84740
+ return fileAdd(entry, opts.scope);
84741
+ }
84742
+ async function remove10(name2, opts) {
84743
+ if (!isValidServiceName(name2)) {
84744
+ return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${name2}` };
84745
+ }
84746
+ if (isBinaryOnPath(BIN)) {
84747
+ if (opts.scope !== "local" && !await fileConfigured(name2, opts.scope)) {
84748
+ return { agent: AGENT2, status: "skipped", detail: "Not configured" };
84749
+ }
84750
+ const result = runAgentCli(BIN, ["mcp", "remove", name2, "--scope", opts.scope]);
84751
+ if (result.ok) {
84752
+ return { agent: AGENT2, status: "removed", detail: result.stdout.trim() || undefined };
84753
+ }
84754
+ return {
84755
+ agent: AGENT2,
84756
+ status: "failed",
84757
+ detail: result.stderr.trim() || `exit code ${result.code}`
84758
+ };
84759
+ }
84760
+ return fileRemove(name2, opts.scope);
84761
+ }
84762
+ var claudeWriter = {
84763
+ agent: AGENT2,
84764
+ add,
84765
+ remove: remove10,
84766
+ listConfigured: listUserConfigured
84767
+ };
84768
+
84769
+ // src/domains/mcp/writers/codex-writer.ts
84770
+ init_path_safety();
84771
+ import { existsSync as existsSync60 } from "node:fs";
84772
+ import { readFile as readFile45, writeFile as writeFile32 } from "node:fs/promises";
84773
+
84774
+ // src/domains/mcp/writers/codex-config-file.ts
84775
+ function escapeRegex2(value) {
84776
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84777
+ }
84778
+ function sectionRegex(header) {
84779
+ const escaped = escapeRegex2(header);
84780
+ return new RegExp(`\\n?^\\[${escaped}\\]\\s*\\r?\\n(?:(?!\\[)[^\\r\\n]*\\r?\\n?)*`, "gm");
84781
+ }
84782
+ function tomlString(value) {
84783
+ return JSON.stringify(value).replace(/\u007f/g, "\\u007F");
84784
+ }
84785
+ function buildMcpServerSection(name2, entry) {
84786
+ if (!isValidServiceName(name2)) {
84787
+ throw new Error(`Refusing to build TOML section for invalid service name: ${name2}`);
84788
+ }
84789
+ if (entry.transport === "stdio") {
84790
+ const argsLine = entry.args?.length ? `args = [${entry.args.map(tomlString).join(", ")}]
84791
+ ` : "";
84792
+ return `[mcp_servers.${name2}]
84793
+ command = ${tomlString(entry.command)}
84794
+ ${argsLine}`;
84795
+ }
84796
+ return `[mcp_servers.${name2}]
84797
+ url = ${tomlString(entry.url)}
84798
+ `;
84799
+ }
84800
+ function upsertMcpServerSection(content, name2, section) {
84801
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84802
+ if (regex2.test(content)) {
84803
+ return content.replace(regex2, () => `
84804
+ ${section}`).trimStart();
84805
+ }
84806
+ const separator = content.trim().length > 0 ? `
84807
+
84808
+ ` : "";
84809
+ return `${content.trimEnd()}${separator}${section}`;
84810
+ }
84811
+ function removeMcpServerSection(content, name2) {
84812
+ const regex2 = sectionRegex(`mcp_servers.${name2}`);
84813
+ if (!regex2.test(content)) {
84814
+ return { content, existed: false };
84815
+ }
84816
+ return { content: content.replace(regex2, ""), existed: true };
84817
+ }
84818
+ function hasMcpServerSection(content, name2) {
84819
+ return sectionRegex(`mcp_servers.${name2}`).test(content);
84820
+ }
84821
+
84822
+ // src/domains/mcp/writers/codex-writer.ts
84823
+ var AGENT3 = "codex";
84824
+ var BIN2 = "codex";
84825
+ var SCOPE_WARNING = "codex has no local/project scope; wrote to user-level ~/.codex/config.toml";
84826
+ function configPath() {
84827
+ return getAgentConfigPath(AGENT3);
84828
+ }
84829
+ function withScopeWarning(detail, scope) {
84830
+ if (scope === "user")
84831
+ return detail;
84832
+ return detail ? `${detail} (${SCOPE_WARNING})` : SCOPE_WARNING;
84833
+ }
84834
+ async function fileAdd2(entry, scope) {
84835
+ const path11 = configPath();
84836
+ if (!path11) {
84837
+ return { agent: AGENT3, status: "failed", detail: "Could not resolve home directory" };
84838
+ }
84839
+ try {
84840
+ return await withCodexTargetLock2(path11, async () => {
84841
+ let existing = "";
84842
+ try {
84843
+ existing = await readFile45(path11, "utf-8");
84844
+ } catch {
84845
+ existing = "";
84846
+ }
84847
+ const section = buildMcpServerSection(entry.name, entry);
84848
+ const updated = upsertMcpServerSection(existing, entry.name, section);
84849
+ await writeFile32(path11, updated, "utf-8");
84850
+ return {
84851
+ agent: AGENT3,
84852
+ status: "added",
84853
+ detail: withScopeWarning(`Wrote ${path11} (file fallback)`, scope)
84854
+ };
84855
+ });
84856
+ } catch (error) {
84857
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84858
+ }
84859
+ }
84860
+ async function fileRemove2(name2) {
84861
+ const path11 = configPath();
84862
+ if (!path11 || !existsSync60(path11)) {
84863
+ return { agent: AGENT3, status: "skipped", detail: "No config file present" };
84864
+ }
84865
+ try {
84866
+ return await withCodexTargetLock2(path11, async () => {
84867
+ const existing = await readFile45(path11, "utf-8");
84868
+ const { content, existed } = removeMcpServerSection(existing, name2);
84869
+ if (!existed) {
84870
+ return {
84871
+ agent: AGENT3,
84872
+ status: "skipped",
84873
+ detail: "Not configured"
84874
+ };
84875
+ }
84876
+ await writeFile32(path11, content, "utf-8");
84877
+ return {
84878
+ agent: AGENT3,
84879
+ status: "removed",
84880
+ detail: `Updated ${path11} (file fallback)`
84881
+ };
84882
+ });
84883
+ } catch (error) {
84884
+ return { agent: AGENT3, status: "failed", detail: toMessage(error) };
84885
+ }
84886
+ }
84887
+ async function add2(entry, opts) {
84888
+ if (!isValidServiceName(entry.name)) {
84889
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${entry.name}` };
84890
+ }
84891
+ if (await isConfigured(entry.name)) {
84892
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84893
+ }
84894
+ if (isBinaryOnPath(BIN2)) {
84895
+ const argv = entry.transport === "stdio" ? ["mcp", "add", entry.name, "--", entry.command, ...entry.args ?? []] : ["mcp", "add", entry.name, "--url", entry.url];
84896
+ const result = runAgentCli(BIN2, argv, 300000);
84897
+ if (result.ok) {
84898
+ return {
84899
+ agent: AGENT3,
84900
+ status: "added",
84901
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
84902
+ };
84903
+ }
84904
+ if (isAlreadyExistsError(result)) {
84905
+ return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
84906
+ }
84907
+ return {
84908
+ agent: AGENT3,
84909
+ status: "failed",
84910
+ detail: result.stderr.trim() || `exit code ${result.code}`
84911
+ };
84912
+ }
84913
+ return fileAdd2(entry, opts.scope);
84914
+ }
84915
+ async function remove11(name2, opts) {
84916
+ if (!isValidServiceName(name2)) {
84917
+ return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${name2}` };
84918
+ }
84919
+ if (isBinaryOnPath(BIN2)) {
84920
+ if (!(await listConfigured()).has(name2)) {
84921
+ return { agent: AGENT3, status: "skipped", detail: "Not configured" };
84922
+ }
84923
+ const result = runAgentCli(BIN2, ["mcp", "remove", name2]);
84924
+ if (result.ok) {
84925
+ return {
84926
+ agent: AGENT3,
84927
+ status: "removed",
84928
+ detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
84929
+ };
84930
+ }
84931
+ return {
84932
+ agent: AGENT3,
84933
+ status: "failed",
84934
+ detail: result.stderr.trim() || `exit code ${result.code}`
84935
+ };
84936
+ }
84937
+ return fileRemove2(name2);
84938
+ }
84939
+ async function listConfigured() {
84940
+ const path11 = configPath();
84941
+ if (!path11 || !existsSync60(path11))
84942
+ return { has: () => false };
84943
+ try {
84944
+ const content = await readFile45(path11, "utf-8");
84945
+ return { has: (name2) => hasMcpServerSection(content, name2) };
84946
+ } catch {
84947
+ return { has: () => false };
84948
+ }
84949
+ }
84950
+ async function isConfigured(name2) {
84951
+ return (await listConfigured()).has(name2);
84952
+ }
84953
+ var codexWriter = {
84954
+ agent: AGENT3,
84955
+ add: add2,
84956
+ remove: remove11,
84957
+ listConfigured
84958
+ };
84959
+
84960
+ // src/domains/mcp/writers/writer-registry.ts
84961
+ var writers = {
84962
+ "claude-code": claudeWriter,
84963
+ codex: codexWriter
84964
+ };
84965
+ function getWriter(agent) {
84966
+ return writers[agent];
84967
+ }
84968
+
84969
+ // src/domains/mcp/mcp-service-core.ts
84970
+ var defaultDeps2 = {
84971
+ getRegistry: getRegistryWithSource,
84972
+ detectAgents,
84973
+ detectProjectAgents,
84974
+ getWriter
84975
+ };
84976
+ function renderResults(results) {
84977
+ for (const result of results) {
84978
+ const line = `${result.agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ""}`;
84979
+ switch (result.status) {
84980
+ case "added":
84981
+ case "removed":
84982
+ logger.success(line);
84983
+ break;
84984
+ case "skipped":
84985
+ logger.warning(line);
84986
+ break;
84987
+ case "failed":
84988
+ logger.error(line);
84989
+ break;
84990
+ default: {
84991
+ const _exhaustive = result.status;
84992
+ }
84993
+ }
84994
+ }
84995
+ }
84996
+ async function selectAgents2(verb, entryName, candidates, opts) {
84997
+ if (opts.yes || isNonInteractive() || candidates.length <= 1) {
84998
+ return candidates;
84999
+ }
85000
+ const preposition = verb === "add" ? "to" : "from";
85001
+ const selected = await ae({
85002
+ message: `Select agents to ${verb} "${entryName}" ${preposition}:`,
85003
+ options: candidates.map((agent) => ({ value: agent, label: agent })),
85004
+ initialValues: candidates,
85005
+ required: false
85006
+ });
85007
+ if (lD(selected))
85008
+ return null;
85009
+ return selected;
85010
+ }
85011
+ async function resolveAgentTargets(verb, service, agentFilter, deps) {
85012
+ const { registry, revalidated } = await deps.getRegistry();
85013
+ const entry = resolveService(registry, service);
85014
+ if (!entry) {
85015
+ logger.error(`Unknown MCP service: ${service}`);
85016
+ logger.info(`Known services: ${registry.services.map((s3) => s3.name).join(", ") || "(none)"}`);
85017
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85018
+ }
85019
+ if (agentFilter && agentFilter.length > 0) {
85020
+ return { ok: true, entry, targets: agentFilter, prompt: false, revalidated };
85021
+ }
85022
+ const installed = deps.detectAgents();
85023
+ const projectTargets = deps.detectProjectAgents().filter((agent) => installed.includes(agent));
85024
+ if (projectTargets.length > 0) {
85025
+ const action = verb === "add" ? "adding to" : "removing from";
85026
+ logger.info(`Detected ${projectTargets.join(" + ")} project → ${action} ${projectTargets.join(", ")}.`);
85027
+ return { ok: true, entry, targets: projectTargets, prompt: false, revalidated };
85028
+ }
85029
+ if (installed.length === 0) {
85030
+ logger.warning("No coding agents detected (claude-code / codex).");
85031
+ return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
85032
+ }
85033
+ return { ok: true, entry, targets: installed, prompt: true, revalidated };
85034
+ }
85035
+
85036
+ // src/domains/mcp/mcp-service.ts
85037
+ async function runMutation(verb, opts, deps) {
85038
+ const resolved = await resolveAgentTargets(verb, opts.service, opts.agents, deps);
85039
+ try {
85040
+ if (!resolved.ok)
85041
+ return resolved.result;
85042
+ const { prompt } = resolved;
85043
+ let { entry } = resolved;
85044
+ let targets = resolved.targets;
85045
+ if (verb === "add" && opts.extraArgs && opts.extraArgs.length > 0) {
85046
+ if (entry.transport !== "stdio") {
85047
+ logger.error(`Extra args after "--" only apply to stdio MCP services; "${entry.name}" is ${entry.transport} (remote).`);
85048
+ return { results: [], exitCode: 1 };
85049
+ }
85050
+ entry = { ...entry, args: [...entry.args ?? [], ...opts.extraArgs] };
85051
+ }
85052
+ if (prompt) {
85053
+ const chosen = await selectAgents2(verb, entry.name, targets, { yes: opts.yes });
85054
+ if (chosen === null) {
85055
+ logger.info("Cancelled.");
85056
+ return { results: [], exitCode: 1 };
85057
+ }
85058
+ if (chosen.length === 0) {
85059
+ logger.warning(`No agents selected — nothing to ${verb}.`);
85060
+ return { results: [], exitCode: 1 };
85061
+ }
85062
+ targets = chosen;
85063
+ }
85064
+ const writerOpts = { scope: opts.scope };
85065
+ const results = [];
85066
+ for (const agent of targets) {
85067
+ const writer = deps.getWriter(agent);
85068
+ results.push(verb === "add" ? await writer.add(entry, writerOpts) : await writer.remove(entry.name, writerOpts));
85069
+ }
85070
+ renderResults(results);
85071
+ const allFailed = results.length > 0 && results.every((r2) => r2.status === "failed");
85072
+ return { results, exitCode: allFailed ? 1 : 0 };
85073
+ } finally {
85074
+ await resolved.revalidated;
85075
+ }
85076
+ }
85077
+ async function runAdd(opts, deps = defaultDeps2) {
85078
+ return runMutation("add", opts, deps);
85079
+ }
85080
+ async function runRemove(opts, deps = defaultDeps2) {
85081
+ return runMutation("remove", opts, deps);
85082
+ }
85083
+ async function runList(deps = defaultDeps2) {
85084
+ const { registry, source, revalidated } = await deps.getRegistry();
85085
+ const projectAgents = deps.detectProjectAgents();
85086
+ const detected = projectAgents.length > 0 ? projectAgents : deps.detectAgents();
85087
+ const lookups = await Promise.all(detected.map(async (agent) => ({
85088
+ agent,
85089
+ lookup: await deps.getWriter(agent).listConfigured()
85090
+ })));
85091
+ const entries = registry.services.map((entry) => ({
85092
+ entry,
85093
+ perAgent: lookups.map(({ agent, lookup }) => ({
85094
+ agent,
85095
+ configured: lookup.has(entry.name)
85096
+ }))
85097
+ }));
85098
+ return { entries, source, revalidated };
85099
+ }
85100
+
85101
+ // src/commands/mcp/mutation-command.ts
85102
+ init_logger();
85103
+
85104
+ // src/commands/mcp/agent-option.ts
85105
+ class InvalidAgentOptionError extends Error {
85106
+ invalidValues;
85107
+ constructor(invalidValues) {
85108
+ super(`Invalid --agent value(s): ${invalidValues.join(", ")}. Accepted agents: ${ALL_MCP_AGENTS.join(", ")}.`);
85109
+ this.invalidValues = invalidValues;
85110
+ this.name = "InvalidAgentOptionError";
85111
+ }
85112
+ }
85113
+ function normalizeAgentOption(agent) {
85114
+ if (agent === undefined)
85115
+ return;
85116
+ const values = Array.isArray(agent) ? agent : [agent];
85117
+ const invalid = values.filter((value) => !McpAgentSchema.safeParse(value).success);
85118
+ if (invalid.length > 0) {
85119
+ throw new InvalidAgentOptionError(invalid);
85120
+ }
85121
+ return values;
85122
+ }
85123
+
85124
+ // src/commands/mcp/scope-option.ts
85125
+ class InvalidScopeOptionError extends Error {
85126
+ invalidValue;
85127
+ constructor(invalidValue) {
85128
+ super(`Invalid --scope value: ${invalidValue}. Accepted scopes: ${ALL_MCP_SCOPES.join(", ")}.`);
85129
+ this.invalidValue = invalidValue;
85130
+ this.name = "InvalidScopeOptionError";
85131
+ }
85132
+ }
85133
+ function normalizeScopeOption(scope) {
85134
+ if (scope === undefined)
85135
+ return DEFAULT_MCP_SCOPE;
85136
+ const parsed = McpScopeSchema.safeParse(scope);
85137
+ if (!parsed.success) {
85138
+ throw new InvalidScopeOptionError(scope);
85139
+ }
85140
+ return parsed.data;
85141
+ }
85142
+
85143
+ // src/commands/mcp/mutation-command.ts
85144
+ async function mutationCommand(verb, service, options2 = {}) {
85145
+ if (!service) {
85146
+ logger.error(`Usage: tkm mcp ${verb} <service> [--agent <name>...] [-s, --scope local|user|project] [--yes]${verb === "add" ? " [-- <extra args...>]" : ""}`);
85147
+ process.exitCode = 1;
85148
+ return;
85149
+ }
85150
+ const extraArgs = options2["--"] ?? [];
85151
+ if (verb === "remove" && extraArgs.length > 0) {
85152
+ logger.warning(`Extra args after "--" only apply to add — ignored for remove.`);
85153
+ }
85154
+ let agents;
85155
+ try {
85156
+ agents = normalizeAgentOption(options2.agent);
85157
+ } catch (error) {
85158
+ if (error instanceof InvalidAgentOptionError) {
85159
+ logger.error(error.message);
85160
+ process.exitCode = 1;
85161
+ return;
85162
+ }
85163
+ throw error;
85164
+ }
85165
+ let scope;
85166
+ try {
85167
+ scope = normalizeScopeOption(options2.scope);
85168
+ } catch (error) {
85169
+ if (error instanceof InvalidScopeOptionError) {
85170
+ logger.error(error.message);
85171
+ process.exitCode = 1;
85172
+ return;
85173
+ }
85174
+ throw error;
85175
+ }
85176
+ const run2 = verb === "add" ? runAdd : runRemove;
85177
+ try {
85178
+ const { exitCode } = await run2({
85179
+ service,
85180
+ agents,
85181
+ scope,
85182
+ yes: options2.yes,
85183
+ extraArgs: verb === "add" ? extraArgs : undefined
85184
+ });
85185
+ process.exitCode = exitCode;
85186
+ } catch (error) {
85187
+ if (error instanceof RegistryUnavailableError) {
85188
+ logger.error(error.message);
85189
+ process.exitCode = 1;
85190
+ return;
85191
+ }
85192
+ throw error;
85193
+ }
85194
+ }
85195
+
85196
+ // src/commands/mcp/add-command.ts
85197
+ async function addCommand(service, options2 = {}) {
85198
+ await mutationCommand("add", service, options2);
85199
+ }
85200
+ // src/commands/mcp/list-command.ts
85201
+ init_logger();
85202
+ var import_picocolors25 = __toESM(require_picocolors(), 1);
85203
+ function statusCell(configured) {
85204
+ return configured ? { value: "● configured", paint: import_picocolors25.default.green } : { value: "○ not set", paint: import_picocolors25.default.dim };
85205
+ }
85206
+ function renderTable(header, rows) {
85207
+ const lastCol = header.length - 1;
85208
+ const widths = header.map((h2, col) => Math.max(h2.length, ...rows.map((r2) => r2[col]?.value.length ?? 0)));
85209
+ const padPlain = (text, col) => col === lastCol ? text : text.padEnd(widths[col]);
85210
+ const headerLine = import_picocolors25.default.bold(header.map((h2, col) => padPlain(h2, col)).join(" ").trimEnd());
85211
+ const rowLine = (cells) => cells.map((cell, col) => {
85212
+ const padded = padPlain(cell.value, col);
85213
+ return cell.paint ? cell.paint(padded) : padded;
85214
+ }).join(" ").trimEnd();
85215
+ return [headerLine, ...rows.map(rowLine)].join(`
85216
+ `);
85217
+ }
85218
+ async function listCommand(options2 = {}) {
85219
+ let listed;
85220
+ try {
85221
+ listed = await runList();
85222
+ } catch (error) {
85223
+ if (error instanceof RegistryUnavailableError) {
85224
+ logger.error(error.message);
85225
+ process.exitCode = 1;
85226
+ return;
85227
+ }
85228
+ throw error;
85229
+ }
85230
+ const { entries, source, revalidated } = listed;
85231
+ if (options2.json) {
85232
+ const services = entries.map(({ entry, perAgent }) => ({
85233
+ name: entry.name,
85234
+ description: entry.description,
85235
+ transport: entry.transport,
85236
+ configured: Object.fromEntries(perAgent.map((p2) => [p2.agent, p2.configured]))
85237
+ }));
85238
+ process.stdout.write(`${JSON.stringify({ source, services })}
85239
+ `);
85240
+ await revalidated;
85241
+ return;
85242
+ }
85243
+ if (entries.length === 0) {
85244
+ logger.info("No MCP services in the registry.");
85245
+ return;
85246
+ }
85247
+ const agents = entries[0].perAgent.map((p2) => p2.agent);
85248
+ const header = ["NAME", "TRANSPORT", ...agents.map((a3) => a3.toUpperCase()), "DESCRIPTION"];
85249
+ const rows = entries.map(({ entry, perAgent }) => [
85250
+ { value: entry.name },
85251
+ { value: entry.transport },
85252
+ ...perAgent.map((p2) => statusCell(p2.configured)),
85253
+ { value: entry.description }
85254
+ ]);
85255
+ logger.info(`MCP registry — ${entries.length} services (source: ${source})`);
85256
+ if (agents.length === 0) {
85257
+ logger.info("No coding agents detected (claude-code / codex).");
85258
+ }
85259
+ process.stdout.write(`${renderTable(header, rows)}
85260
+ `);
85261
+ await revalidated;
85262
+ }
85263
+ // src/commands/mcp/mcp-command.ts
85264
+ init_logger();
85265
+
85266
+ // src/commands/mcp/remove-command.ts
85267
+ async function removeCommand(service, options2 = {}) {
85268
+ await mutationCommand("remove", service, options2);
85269
+ }
85270
+
85271
+ // src/commands/mcp/mcp-command.ts
85272
+ function printUsage() {
85273
+ logger.error("Usage: tkm mcp <add|list|remove> [service] [options] — see `tkm mcp --help`");
85274
+ }
85275
+ async function mcpCommand(action, service, options2 = {}) {
85276
+ switch (action) {
85277
+ case "add":
85278
+ await addCommand(service, options2);
85279
+ break;
85280
+ case "list":
85281
+ await listCommand(options2);
85282
+ break;
85283
+ case "remove":
85284
+ await removeCommand(service, options2);
85285
+ break;
85286
+ case undefined:
85287
+ printUsage();
85288
+ process.exitCode = 1;
85289
+ break;
85290
+ default:
85291
+ logger.error(`Unknown mcp action: ${action}. Available: add, list, remove`);
85292
+ process.exitCode = 1;
85293
+ }
85294
+ }
84264
85295
  // src/commands/plan/plan-command.ts
84265
85296
  init_output_manager();
84266
- import { existsSync as existsSync62, statSync as statSync11 } from "node:fs";
84267
- import { dirname as dirname41, join as join129, parse as parse4, resolve as resolve33 } from "node:path";
85297
+ import { existsSync as existsSync65, statSync as statSync11 } from "node:fs";
85298
+ import { dirname as dirname42, join as join132, parse as parse4, resolve as resolve33 } from "node:path";
84268
85299
 
84269
85300
  // src/commands/plan/plan-read-handlers.ts
84270
- import { existsSync as existsSync61, statSync as statSync10 } from "node:fs";
84271
- import { basename as basename21, dirname as dirname40, join as join128, relative as relative20, resolve as resolve31 } from "node:path";
85301
+ import { existsSync as existsSync64, statSync as statSync10 } from "node:fs";
85302
+ import { basename as basename21, dirname as dirname41, join as join131, relative as relative20, resolve as resolve31 } from "node:path";
84272
85303
 
84273
85304
  // src/domains/plan-parser/index.ts
84274
- import { dirname as dirname39 } from "node:path";
85305
+ import { dirname as dirname40 } from "node:path";
84275
85306
 
84276
85307
  // src/domains/plan-parser/plan-table-parser.ts
84277
85308
  var import_gray_matter5 = __toESM(require_gray_matter(), 1);
84278
85309
  import { readFileSync as readFileSync23 } from "node:fs";
84279
- import { dirname as dirname36, resolve as resolve30 } from "node:path";
85310
+ import { dirname as dirname37, resolve as resolve30 } from "node:path";
84280
85311
  function normalizeStatus(raw) {
84281
85312
  const s3 = raw.toLowerCase().trim();
84282
85313
  if (s3.includes("complete") || s3.includes("done") || s3.includes("✓") || s3.includes("✅")) {
@@ -84537,7 +85568,7 @@ function parseFormat4(content, planFilePath, options2) {
84537
85568
  const hasCheck = /[✅✓]/.test(line);
84538
85569
  current = { name: name2, status: hasCheck ? "completed" : "pending" };
84539
85570
  } else if (fileMatch && current) {
84540
- const planDir = dirname36(planFilePath);
85571
+ const planDir = dirname37(planFilePath);
84541
85572
  current.file = resolve30(planDir, fileMatch[1].trim());
84542
85573
  } else if (statusMatch && current) {
84543
85574
  current.status = normalizeStatus(statusMatch[2]);
@@ -84642,30 +85673,30 @@ function parsePhasesFromBody(body, dir, options2) {
84642
85673
  }
84643
85674
  function parsePlanFile(planFilePath, options2) {
84644
85675
  const content = readFileSync23(planFilePath, "utf8");
84645
- const dir = dirname36(planFilePath);
85676
+ const dir = dirname37(planFilePath);
84646
85677
  const { data: frontmatter, content: body } = import_gray_matter5.default(content);
84647
85678
  const phases = parsePhasesFromBody(body, dir, options2);
84648
85679
  return { frontmatter, phases };
84649
85680
  }
84650
85681
  // src/domains/plan-parser/plan-scanner.ts
84651
- import { existsSync as existsSync58, readdirSync as readdirSync10 } from "node:fs";
84652
- import { join as join126 } from "node:path";
85682
+ import { existsSync as existsSync61, readdirSync as readdirSync10 } from "node:fs";
85683
+ import { join as join129 } from "node:path";
84653
85684
  function scanPlanDir(dir) {
84654
- if (!existsSync58(dir))
85685
+ if (!existsSync61(dir))
84655
85686
  return [];
84656
85687
  try {
84657
- return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join126(dir, entry.name, "plan.md")).filter(existsSync58);
85688
+ return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join129(dir, entry.name, "plan.md")).filter(existsSync61);
84658
85689
  } catch {
84659
85690
  return [];
84660
85691
  }
84661
85692
  }
84662
85693
  // src/domains/plan-parser/plan-validator.ts
84663
85694
  var import_gray_matter6 = __toESM(require_gray_matter(), 1);
84664
- import { existsSync as existsSync59, readFileSync as readFileSync24 } from "node:fs";
84665
- import { basename as basename19, dirname as dirname37 } from "node:path";
85695
+ import { existsSync as existsSync62, readFileSync as readFileSync24 } from "node:fs";
85696
+ import { basename as basename19, dirname as dirname38 } from "node:path";
84666
85697
  function validatePlanFile(filePath, strict = false) {
84667
85698
  const content = readFileSync24(filePath, "utf8");
84668
- const dir = dirname37(filePath);
85699
+ const dir = dirname38(filePath);
84669
85700
  const issues = [];
84670
85701
  const lines = content.split(`
84671
85702
  `);
@@ -84701,7 +85732,7 @@ function validatePlanFile(filePath, strict = false) {
84701
85732
  });
84702
85733
  }
84703
85734
  for (const phase of phases) {
84704
- if (phase.file && !existsSync59(phase.file)) {
85735
+ if (phase.file && !existsSync62(phase.file)) {
84705
85736
  const fileBasename = basename19(phase.file);
84706
85737
  const refLine = lines.findIndex((l2) => l2.includes(fileBasename));
84707
85738
  issues.push({
@@ -84722,8 +85753,8 @@ function validatePlanFile(filePath, strict = false) {
84722
85753
  // src/domains/plan-parser/plan-writer.ts
84723
85754
  var import_gray_matter7 = __toESM(require_gray_matter(), 1);
84724
85755
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync25, writeFileSync as writeFileSync12 } from "node:fs";
84725
- import { existsSync as existsSync60 } from "node:fs";
84726
- import { basename as basename20, dirname as dirname38, join as join127 } from "node:path";
85756
+ import { existsSync as existsSync63 } from "node:fs";
85757
+ import { basename as basename20, dirname as dirname39, join as join130 } from "node:path";
84727
85758
  function phaseNameToFilename(id, name2) {
84728
85759
  const numMatch = /^(\d+)([a-z]*)$/i.exec(id);
84729
85760
  const num4 = numMatch ? numMatch[1] : id;
@@ -84831,12 +85862,12 @@ function scaffoldPlan(options2) {
84831
85862
  mkdirSync9(dir, { recursive: true });
84832
85863
  const resolvedPhases = resolvePhaseIds(options2.phases);
84833
85864
  const optionsWithResolved = { ...options2, phases: resolvedPhases };
84834
- const planFile = join127(dir, "plan.md");
85865
+ const planFile = join130(dir, "plan.md");
84835
85866
  writeFileSync12(planFile, generatePlanMd(optionsWithResolved), "utf8");
84836
85867
  const phaseFiles = [];
84837
85868
  for (const phase of resolvedPhases) {
84838
85869
  const filename = phaseNameToFilename(phase.id, phase.name);
84839
- const phaseFile = join127(dir, filename);
85870
+ const phaseFile = join130(dir, filename);
84840
85871
  writeFileSync12(phaseFile, generatePhaseTemplate(phase), "utf8");
84841
85872
  phaseFiles.push(phaseFile);
84842
85873
  }
@@ -84902,9 +85933,9 @@ function updatePhaseStatus(planFile, phaseId, newStatus) {
84902
85933
  const updatedFrontmatter = { ...frontmatter, status: planStatus };
84903
85934
  const updatedContent = import_gray_matter7.default.stringify(updatedBody, updatedFrontmatter);
84904
85935
  writeFileSync12(planFile, updatedContent, "utf8");
84905
- const planDir = dirname38(planFile);
85936
+ const planDir = dirname39(planFile);
84906
85937
  const phaseFilename = phaseNameFilenameFromTableRow(updatedBody, phaseId, planDir);
84907
- if (phaseFilename && existsSync60(phaseFilename)) {
85938
+ if (phaseFilename && existsSync63(phaseFilename)) {
84908
85939
  updatePhaseFileFrontmatter(phaseFilename, newStatus);
84909
85940
  }
84910
85941
  }
@@ -84916,7 +85947,7 @@ function phaseNameFilenameFromTableRow(body, phaseId, planDir) {
84916
85947
  continue;
84917
85948
  const linkMatch = /\[([^\]]+)\]\(\.\/([^)]+)\)/.exec(row);
84918
85949
  if (linkMatch)
84919
- return join127(planDir, linkMatch[2]);
85950
+ return join130(planDir, linkMatch[2]);
84920
85951
  }
84921
85952
  return null;
84922
85953
  }
@@ -84934,7 +85965,7 @@ function addPhase(planFile, name2, afterId) {
84934
85965
  throw new Error("Non-canonical plan.md — cannot add phase");
84935
85966
  }
84936
85967
  const { data: frontmatter, content: body } = import_gray_matter7.default(raw);
84937
- const planDir = dirname38(planFile);
85968
+ const planDir = dirname39(planFile);
84938
85969
  const existingIds = [];
84939
85970
  for (const match of body.matchAll(/^\|\s*(\d+[a-z]?)\s*\|/gim)) {
84940
85971
  existingIds.push(match[1].toLowerCase());
@@ -84997,7 +86028,7 @@ function addPhase(planFile, name2, afterId) {
84997
86028
  `);
84998
86029
  }
84999
86030
  writeFileSync12(planFile, import_gray_matter7.default.stringify(updatedBody, frontmatter), "utf8");
85000
- const phaseFilePath = join127(planDir, filename);
86031
+ const phaseFilePath = join130(planDir, filename);
85001
86032
  writeFileSync12(phaseFilePath, generatePhaseTemplate({ id: phaseId, name: name2 }), "utf8");
85002
86033
  return { phaseId, phaseFile: phaseFilePath };
85003
86034
  }
@@ -85009,7 +86040,7 @@ function buildPlanSummary(planFile) {
85009
86040
  const inProgress = phases.filter((p2) => p2.status === "in-progress").length;
85010
86041
  const pending = phases.filter((p2) => p2.status === "pending").length;
85011
86042
  return {
85012
- planDir: dirname39(planFile),
86043
+ planDir: dirname40(planFile),
85013
86044
  planFile,
85014
86045
  title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
85015
86046
  description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
@@ -85025,7 +86056,7 @@ function buildPlanSummary(planFile) {
85025
86056
  // src/commands/plan/plan-read-handlers.ts
85026
86057
  init_logger();
85027
86058
  init_output_manager();
85028
- var import_picocolors25 = __toESM(require_picocolors(), 1);
86059
+ var import_picocolors26 = __toESM(require_picocolors(), 1);
85029
86060
  async function handleParse(target, options2) {
85030
86061
  const planFile = resolvePlanFile(target);
85031
86062
  if (!planFile) {
@@ -85046,9 +86077,9 @@ async function handleParse(target, options2) {
85046
86077
  console.log(JSON.stringify({ file: relative20(process.cwd(), planFile), frontmatter, phases }, null, 2));
85047
86078
  return;
85048
86079
  }
85049
- const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname40(planFile));
86080
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname41(planFile));
85050
86081
  console.log();
85051
- console.log(import_picocolors25.default.bold(` Plan: ${title}`));
86082
+ console.log(import_picocolors26.default.bold(` Plan: ${title}`));
85052
86083
  console.log(` File: ${planFile}`);
85053
86084
  console.log(` Phases found: ${phases.length}`);
85054
86085
  console.log();
@@ -85079,7 +86110,7 @@ async function handleValidate(target, options2) {
85079
86110
  return;
85080
86111
  }
85081
86112
  console.log();
85082
- console.log(import_picocolors25.default.bold(` Validating: ${planFile}`));
86113
+ console.log(import_picocolors26.default.bold(` Validating: ${planFile}`));
85083
86114
  console.log();
85084
86115
  if (result.issues.length === 0) {
85085
86116
  console.log(` [OK] No issues found — ${result.phases.length} phases detected`);
@@ -85093,7 +86124,7 @@ async function handleValidate(target, options2) {
85093
86124
  }
85094
86125
  }
85095
86126
  console.log();
85096
- const validStr = result.valid ? import_picocolors25.default.green("[OK] Valid") : import_picocolors25.default.red("[X] Invalid");
86127
+ const validStr = result.valid ? import_picocolors26.default.green("[OK] Valid") : import_picocolors26.default.red("[X] Invalid");
85097
86128
  console.log(` ${validStr} — ${result.issues.filter((i) => i.severity === "error").length} errors, ${result.issues.filter((i) => i.severity === "warning").length} warnings`);
85098
86129
  console.log();
85099
86130
  if (!result.valid)
@@ -85101,7 +86132,7 @@ async function handleValidate(target, options2) {
85101
86132
  }
85102
86133
  async function handleStatus(target, options2) {
85103
86134
  const t = target ? resolve31(target) : null;
85104
- const plansDir = t && existsSync61(t) && statSync10(t).isDirectory() && !existsSync61(join128(t, "plan.md")) ? t : null;
86135
+ const plansDir = t && existsSync64(t) && statSync10(t).isDirectory() && !existsSync64(join131(t, "plan.md")) ? t : null;
85105
86136
  if (plansDir) {
85106
86137
  const planFiles = scanPlanDir(plansDir);
85107
86138
  if (planFiles.length === 0) {
@@ -85120,20 +86151,20 @@ async function handleStatus(target, options2) {
85120
86151
  return;
85121
86152
  }
85122
86153
  console.log();
85123
- console.log(import_picocolors25.default.bold(` Plans in: ${plansDir}`));
86154
+ console.log(import_picocolors26.default.bold(` Plans in: ${plansDir}`));
85124
86155
  console.log();
85125
86156
  for (const pf of planFiles) {
85126
86157
  try {
85127
86158
  const s3 = buildPlanSummary(pf);
85128
86159
  const bar = progressBar(s3.completed, s3.totalPhases);
85129
- const title2 = s3.title ?? basename21(dirname40(pf));
85130
- console.log(` ${import_picocolors25.default.bold(title2)}`);
86160
+ const title2 = s3.title ?? basename21(dirname41(pf));
86161
+ console.log(` ${import_picocolors26.default.bold(title2)}`);
85131
86162
  console.log(` ${bar}`);
85132
86163
  if (s3.inProgress > 0)
85133
86164
  console.log(` [~] ${s3.inProgress} in progress`);
85134
86165
  console.log();
85135
86166
  } catch {
85136
- console.log(` [X] Failed to read: ${basename21(dirname40(pf))}`);
86167
+ console.log(` [X] Failed to read: ${basename21(dirname41(pf))}`);
85137
86168
  console.log();
85138
86169
  }
85139
86170
  }
@@ -85157,9 +86188,9 @@ async function handleStatus(target, options2) {
85157
86188
  console.log(JSON.stringify(summary, null, 2));
85158
86189
  return;
85159
86190
  }
85160
- const title = summary.title ?? basename21(dirname40(planFile));
86191
+ const title = summary.title ?? basename21(dirname41(planFile));
85161
86192
  console.log();
85162
- console.log(import_picocolors25.default.bold(` ${title}`));
86193
+ console.log(import_picocolors26.default.bold(` ${title}`));
85163
86194
  if (summary.status)
85164
86195
  console.log(` Status: ${summary.status}`);
85165
86196
  console.log();
@@ -85185,7 +86216,7 @@ async function handleKanban(target, _options) {
85185
86216
  // src/commands/plan/plan-write-handlers.ts
85186
86217
  import { basename as basename22, relative as relative21, resolve as resolve32 } from "node:path";
85187
86218
  init_output_manager();
85188
- var import_picocolors26 = __toESM(require_picocolors(), 1);
86219
+ var import_picocolors27 = __toESM(require_picocolors(), 1);
85189
86220
  async function handleCreate(target, options2) {
85190
86221
  if (!options2.title) {
85191
86222
  output.error("[X] --title is required for create");
@@ -85232,7 +86263,7 @@ async function handleCreate(target, options2) {
85232
86263
  return;
85233
86264
  }
85234
86265
  console.log();
85235
- console.log(import_picocolors26.default.bold(` [OK] Plan created: ${options2.title}`));
86266
+ console.log(import_picocolors27.default.bold(` [OK] Plan created: ${options2.title}`));
85236
86267
  console.log(` Directory: ${resolve32(dir)}`);
85237
86268
  console.log(` Phases: ${result.phaseFiles.length}`);
85238
86269
  for (const f4 of result.phaseFiles) {
@@ -85332,22 +86363,22 @@ async function handleAddPhase(target, options2) {
85332
86363
  // src/commands/plan/plan-command.ts
85333
86364
  function resolvePlanFile(target) {
85334
86365
  const t = target ? resolve33(target) : process.cwd();
85335
- if (existsSync62(t)) {
86366
+ if (existsSync65(t)) {
85336
86367
  const stat14 = statSync11(t);
85337
86368
  if (stat14.isFile())
85338
86369
  return t;
85339
- const candidate = join129(t, "plan.md");
85340
- if (existsSync62(candidate))
86370
+ const candidate = join132(t, "plan.md");
86371
+ if (existsSync65(candidate))
85341
86372
  return candidate;
85342
86373
  }
85343
86374
  if (!target) {
85344
86375
  let dir = process.cwd();
85345
86376
  const root = parse4(dir).root;
85346
86377
  while (dir !== root) {
85347
- const candidate = join129(dir, "plan.md");
85348
- if (existsSync62(candidate))
86378
+ const candidate = join132(dir, "plan.md");
86379
+ if (existsSync65(candidate))
85349
86380
  return candidate;
85350
- dir = dirname41(dir);
86381
+ dir = dirname42(dir);
85351
86382
  }
85352
86383
  }
85353
86384
  return null;
@@ -85395,7 +86426,7 @@ async function planCommand(action, target, options2) {
85395
86426
  let resolvedTarget = target;
85396
86427
  if (resolvedAction && !knownActions.has(resolvedAction)) {
85397
86428
  const looksLikePath = resolvedAction.includes("/") || resolvedAction.includes("\\") || resolvedAction.endsWith(".md") || resolvedAction === "." || resolvedAction === "..";
85398
- const existsOnDisk = !looksLikePath && existsSync62(resolve33(resolvedAction));
86429
+ const existsOnDisk = !looksLikePath && existsSync65(resolve33(resolvedAction));
85399
86430
  if (looksLikePath || existsOnDisk) {
85400
86431
  resolvedTarget = resolvedAction;
85401
86432
  resolvedAction = undefined;
@@ -85439,22 +86470,22 @@ init_logger();
85439
86470
  init_logger();
85440
86471
 
85441
86472
  // src/commands/telemetry/shared.ts
85442
- import { existsSync as existsSync63, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
86473
+ import { existsSync as existsSync66, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
85443
86474
  import { homedir as homedir29 } from "node:os";
85444
- import { join as join130 } from "node:path";
86475
+ import { join as join133 } from "node:path";
85445
86476
  init_token_store();
85446
86477
  init_manifest_path_resolver();
85447
86478
  init_takumi_constants();
85448
- var USER_CACHE_PATH = join130(homedir29(), ".claude", "sk-user.json");
85449
- var EVENT_BUFFER_DIR = join130(homedir29(), ".claude", "sk-events");
85450
- var RATE_STATE_PATH = join130(homedir29(), ".claude", "sk-rate-state.json");
85451
- var TAKUMI_MANIFEST_PATH = join130(homedir29(), ".claude", MANIFEST_FILENAME);
85452
- var LEGACY_METADATA_PATH = join130(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
86479
+ var USER_CACHE_PATH = join133(homedir29(), ".claude", "sk-user.json");
86480
+ var EVENT_BUFFER_DIR = join133(homedir29(), ".claude", "sk-events");
86481
+ var RATE_STATE_PATH = join133(homedir29(), ".claude", "sk-rate-state.json");
86482
+ var TAKUMI_MANIFEST_PATH = join133(homedir29(), ".claude", MANIFEST_FILENAME);
86483
+ var LEGACY_METADATA_PATH = join133(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
85453
86484
  var TELEMETRY_HOOK_FIELD = "hooks.telemetry";
85454
86485
  var TOKEN_PLACEHOLDER = "__INJECT_AT_RELEASE__";
85455
86486
  function readUserCache() {
85456
86487
  try {
85457
- if (!existsSync63(USER_CACHE_PATH))
86488
+ if (!existsSync66(USER_CACHE_PATH))
85458
86489
  return null;
85459
86490
  const parsed = JSON.parse(readFileSync26(USER_CACHE_PATH, "utf8"));
85460
86491
  if (!parsed || typeof parsed !== "object")
@@ -85466,7 +86497,7 @@ function readUserCache() {
85466
86497
  }
85467
86498
  function countBufferFiles() {
85468
86499
  try {
85469
- if (!existsSync63(EVENT_BUFFER_DIR))
86500
+ if (!existsSync66(EVENT_BUFFER_DIR))
85470
86501
  return 0;
85471
86502
  return readdirSync11(EVENT_BUFFER_DIR).filter((f4) => f4.endsWith(".jsonl")).length;
85472
86503
  } catch {
@@ -85478,7 +86509,7 @@ function readTelemetryConfig() {
85478
86509
  const envToken = process.env.TAKUMI_TELEMETRY_TOKEN;
85479
86510
  let metadata = null;
85480
86511
  try {
85481
- const resolved = findManifestPathSync(join130(homedir29(), ".claude"));
86512
+ const resolved = findManifestPathSync(join133(homedir29(), ".claude"));
85482
86513
  if (resolved) {
85483
86514
  metadata = JSON.parse(readFileSync26(resolved.path, "utf8"));
85484
86515
  }
@@ -85504,8 +86535,8 @@ function collectRuntimeContext() {
85504
86535
  cacheSource: cache3?.source === "gh" || cache3?.source === "manual" ? cache3.source : null,
85505
86536
  bufferFileCount: countBufferFiles(),
85506
86537
  bufferDir: EVENT_BUFFER_DIR,
85507
- rateStateExists: existsSync63(RATE_STATE_PATH),
85508
- userCacheExists: existsSync63(USER_CACHE_PATH),
86538
+ rateStateExists: existsSync66(RATE_STATE_PATH),
86539
+ userCacheExists: existsSync66(USER_CACHE_PATH),
85509
86540
  endpoint,
85510
86541
  tokenConfigured: Boolean(token)
85511
86542
  };
@@ -85606,7 +86637,7 @@ init_manifest_writer();
85606
86637
  init_logger();
85607
86638
  init_safe_prompts();
85608
86639
  init_types2();
85609
- var import_picocolors28 = __toESM(require_picocolors(), 1);
86640
+ var import_picocolors29 = __toESM(require_picocolors(), 1);
85610
86641
 
85611
86642
  // src/commands/uninstall/installation-detector.ts
85612
86643
  init_paths();
@@ -85656,7 +86687,7 @@ init_safe_prompts();
85656
86687
  init_safe_spinner();
85657
86688
  var import_fs_extra37 = __toESM(require_lib(), 1);
85658
86689
  import { readdirSync as readdirSync13, rmSync as rmSync9 } from "node:fs";
85659
- import { join as join132, resolve as resolve34, sep as sep9 } from "node:path";
86690
+ import { join as join135, resolve as resolve34, sep as sep9 } from "node:path";
85660
86691
 
85661
86692
  // src/commands/uninstall/analysis-handler.ts
85662
86693
  init_metadata_migration();
@@ -85665,14 +86696,14 @@ init_ownership_checker();
85665
86696
  init_logger();
85666
86697
  init_safe_prompts();
85667
86698
  init_takumi_constants();
85668
- var import_picocolors27 = __toESM(require_picocolors(), 1);
85669
- import { existsSync as existsSync64, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
85670
- import { dirname as dirname42, join as join131 } from "node:path";
86699
+ var import_picocolors28 = __toESM(require_picocolors(), 1);
86700
+ import { existsSync as existsSync67, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
86701
+ import { dirname as dirname43, join as join134 } from "node:path";
85671
86702
  function listPresentManifestNames(installPath) {
85672
86703
  const present = [];
85673
- if (existsSync64(getManifestPath(installPath)))
86704
+ if (existsSync67(getManifestPath(installPath)))
85674
86705
  present.push(MANIFEST_FILENAME);
85675
- if (existsSync64(getLegacyManifestPath(installPath)))
86706
+ if (existsSync67(getLegacyManifestPath(installPath)))
85676
86707
  present.push(LEGACY_MANIFEST_FILENAME);
85677
86708
  return present;
85678
86709
  }
@@ -85690,7 +86721,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
85690
86721
  }
85691
86722
  async function cleanupEmptyDirectories3(filePath, installationRoot) {
85692
86723
  let cleaned = 0;
85693
- let currentDir = dirname42(filePath);
86724
+ let currentDir = dirname43(filePath);
85694
86725
  while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
85695
86726
  try {
85696
86727
  const entries = readdirSync12(currentDir);
@@ -85698,7 +86729,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
85698
86729
  rmSync8(currentDir, { recursive: true });
85699
86730
  cleaned++;
85700
86731
  logger.debug(`Removed empty directory: ${currentDir}`);
85701
- currentDir = dirname42(currentDir);
86732
+ currentDir = dirname43(currentDir);
85702
86733
  } else {
85703
86734
  break;
85704
86735
  }
@@ -85720,7 +86751,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85720
86751
  if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
85721
86752
  const kitFiles = metadata.kits[kit].files || [];
85722
86753
  for (const trackedFile of kitFiles) {
85723
- const filePath = join131(installation.path, trackedFile.path);
86754
+ const filePath = join134(installation.path, trackedFile.path);
85724
86755
  if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
85725
86756
  result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
85726
86757
  continue;
@@ -85752,7 +86783,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85752
86783
  return result;
85753
86784
  }
85754
86785
  for (const trackedFile of allTrackedFiles) {
85755
- const filePath = join131(installation.path, trackedFile.path);
86786
+ const filePath = join134(installation.path, trackedFile.path);
85756
86787
  const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
85757
86788
  if (!ownershipResult.exists)
85758
86789
  continue;
@@ -85770,27 +86801,27 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
85770
86801
  }
85771
86802
  function displayDryRunPreview(analysis, installationType) {
85772
86803
  console.log("");
85773
- log.info(import_picocolors27.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
86804
+ log.info(import_picocolors28.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
85774
86805
  console.log("");
85775
86806
  if (analysis.toDelete.length > 0) {
85776
- console.log(import_picocolors27.default.red(import_picocolors27.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
86807
+ console.log(import_picocolors28.default.red(import_picocolors28.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
85777
86808
  const showDelete = analysis.toDelete.slice(0, 10);
85778
86809
  for (const item of showDelete) {
85779
- console.log(` ${import_picocolors27.default.red("✖")} ${item.path}`);
86810
+ console.log(` ${import_picocolors28.default.red("✖")} ${item.path}`);
85780
86811
  }
85781
86812
  if (analysis.toDelete.length > 10) {
85782
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
86813
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
85783
86814
  }
85784
86815
  console.log("");
85785
86816
  }
85786
86817
  if (analysis.toPreserve.length > 0) {
85787
- console.log(import_picocolors27.default.green(import_picocolors27.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
86818
+ console.log(import_picocolors28.default.green(import_picocolors28.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
85788
86819
  const showPreserve = analysis.toPreserve.slice(0, 10);
85789
86820
  for (const item of showPreserve) {
85790
- console.log(` ${import_picocolors27.default.green("✓")} ${item.path} ${import_picocolors27.default.gray(`(${item.reason})`)}`);
86821
+ console.log(` ${import_picocolors28.default.green("✓")} ${item.path} ${import_picocolors28.default.gray(`(${item.reason})`)}`);
85791
86822
  }
85792
86823
  if (analysis.toPreserve.length > 10) {
85793
- console.log(import_picocolors27.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
86824
+ console.log(import_picocolors28.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
85794
86825
  }
85795
86826
  console.log("");
85796
86827
  }
@@ -85851,7 +86882,7 @@ async function removeInstallations(installations, options2) {
85851
86882
  let removedCount = 0;
85852
86883
  let cleanedDirs = 0;
85853
86884
  for (const item of analysis.toDelete) {
85854
- const filePath = join132(installation.path, item.path);
86885
+ const filePath = join135(installation.path, item.path);
85855
86886
  if (!await import_fs_extra37.pathExists(filePath))
85856
86887
  continue;
85857
86888
  if (!await isPathSafeToRemove(filePath, installation.path)) {
@@ -85912,15 +86943,15 @@ function displayInstallations(installations, scope) {
85912
86943
  const hasLegacy = installations.some((i) => !i.hasMetadata);
85913
86944
  const lines = installations.map((i) => {
85914
86945
  const typeLabel = i.type === "local" ? "Local " : "Global";
85915
- const legacyTag = !i.hasMetadata ? import_picocolors28.default.yellow(" [legacy]") : "";
86946
+ const legacyTag = !i.hasMetadata ? import_picocolors29.default.yellow(" [legacy]") : "";
85916
86947
  const components = formatComponentSummary(i);
85917
86948
  return ` ${typeLabel}: ${i.path}${legacyTag}${components}`;
85918
86949
  });
85919
86950
  prompts.note(lines.join(`
85920
86951
  `), `Detected Takumi installations (${scopeLabel})`);
85921
86952
  if (hasLegacy) {
85922
- log.warn(import_picocolors28.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
85923
- `) + import_picocolors28.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
86953
+ log.warn(import_picocolors29.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
86954
+ `) + import_picocolors29.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
85924
86955
  }
85925
86956
  log.warn("[!] This will permanently delete Takumi files from the above paths.");
85926
86957
  }
@@ -85980,7 +87011,7 @@ async function uninstallCommand(options2) {
85980
87011
  }
85981
87012
  const isAtHome = isLocalSameAsGlobal();
85982
87013
  if (validOptions.local && !validOptions.global && isAtHome) {
85983
- log.warn(import_picocolors28.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
87014
+ log.warn(import_picocolors29.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
85984
87015
  log.info("Use -g/--global or run from a project directory.");
85985
87016
  return;
85986
87017
  }
@@ -85992,7 +87023,7 @@ async function uninstallCommand(options2) {
85992
87023
  } else if (validOptions.global) {
85993
87024
  scope = "global";
85994
87025
  } else if (isAtHome) {
85995
- log.info(import_picocolors28.default.cyan("Running at HOME directory - targeting global installation"));
87026
+ log.info(import_picocolors29.default.cyan("Running at HOME directory - targeting global installation"));
85996
87027
  scope = "global";
85997
87028
  } else {
85998
87029
  const promptedScope = await promptScope(allInstallations);
@@ -86014,10 +87045,10 @@ async function uninstallCommand(options2) {
86014
87045
  }
86015
87046
  displayInstallations(installations, scope);
86016
87047
  if (validOptions.kit) {
86017
- log.info(import_picocolors28.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
87048
+ log.info(import_picocolors29.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
86018
87049
  }
86019
87050
  if (validOptions.dryRun) {
86020
- log.info(import_picocolors28.default.yellow("DRY RUN MODE - No files will be deleted"));
87051
+ log.info(import_picocolors29.default.yellow("DRY RUN MODE - No files will be deleted"));
86021
87052
  await removeInstallations(installations, {
86022
87053
  dryRun: true,
86023
87054
  forceOverwrite: validOptions.forceOverwrite,
@@ -86027,8 +87058,8 @@ async function uninstallCommand(options2) {
86027
87058
  return;
86028
87059
  }
86029
87060
  if (validOptions.forceOverwrite) {
86030
- log.warn(`${import_picocolors28.default.yellow(import_picocolors28.default.bold("FORCE MODE ENABLED"))}
86031
- ${import_picocolors28.default.yellow("User modifications will be permanently deleted!")}`);
87061
+ log.warn(`${import_picocolors29.default.yellow(import_picocolors29.default.bold("FORCE MODE ENABLED"))}
87062
+ ${import_picocolors29.default.yellow("User modifications will be permanently deleted!")}`);
86032
87063
  }
86033
87064
  if (!validOptions.yes) {
86034
87065
  const kitLabel = validOptions.kit ? ` (${validOptions.kit} kit only)` : "";
@@ -86481,7 +87512,7 @@ init_auth_client();
86481
87512
  init_github_client();
86482
87513
  init_logger();
86483
87514
  init_types2();
86484
- var import_picocolors29 = __toESM(require_picocolors(), 1);
87515
+ var import_picocolors30 = __toESM(require_picocolors(), 1);
86485
87516
  function formatRelativeTime(dateString) {
86486
87517
  if (!dateString)
86487
87518
  return "Unknown";
@@ -86503,21 +87534,21 @@ function formatRelativeTime(dateString) {
86503
87534
  }
86504
87535
  function displayKitReleases(kitName, releases) {
86505
87536
  console.log(`
86506
- ${import_picocolors29.default.bold(import_picocolors29.default.cyan(kitName))} - Available Versions:
87537
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(kitName))} - Available Versions:
86507
87538
  `);
86508
87539
  if (releases.length === 0) {
86509
- console.log(import_picocolors29.default.dim(" No releases found"));
87540
+ console.log(import_picocolors30.default.dim(" No releases found"));
86510
87541
  return;
86511
87542
  }
86512
87543
  for (const release of releases) {
86513
- const version3 = import_picocolors29.default.green(release.tag);
87544
+ const version3 = import_picocolors30.default.green(release.tag);
86514
87545
  const publishedAt = formatRelativeTime(release.publishedAt);
86515
- const badge = release.prerelease ? ` ${import_picocolors29.default.yellow("[prerelease]")}` : "";
87546
+ const badge = release.prerelease ? ` ${import_picocolors30.default.yellow("[prerelease]")}` : "";
86516
87547
  const versionPart = version3.padEnd(20);
86517
- const timePart = import_picocolors29.default.dim(publishedAt.padEnd(20));
87548
+ const timePart = import_picocolors30.default.dim(publishedAt.padEnd(20));
86518
87549
  console.log(` ${versionPart} ${timePart}${badge}`);
86519
87550
  }
86520
- console.log(import_picocolors29.default.dim(`
87551
+ console.log(import_picocolors30.default.dim(`
86521
87552
  Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
86522
87553
  }
86523
87554
  async function fetchReleasesForKit(kitType, options2) {
@@ -86562,8 +87593,8 @@ async function versionCommand(options2) {
86562
87593
  for (const result of results) {
86563
87594
  if (result.error) {
86564
87595
  console.log(`
86565
- ${import_picocolors29.default.bold(import_picocolors29.default.cyan(result.kitConfig.name))} - ${import_picocolors29.default.red("Error")}`);
86566
- console.log(import_picocolors29.default.dim(` ${result.error}`));
87596
+ ${import_picocolors30.default.bold(import_picocolors30.default.cyan(result.kitConfig.name))} - ${import_picocolors30.default.red("Error")}`);
87597
+ console.log(import_picocolors30.default.dim(` ${result.error}`));
86567
87598
  } else {
86568
87599
  displayKitReleases(result.kitConfig.name, result.releases);
86569
87600
  }
@@ -86709,6 +87740,9 @@ function registerCommands(cli) {
86709
87740
  process.exitCode = 1;
86710
87741
  }
86711
87742
  });
87743
+ cli.command("mcp [action] [service]", "Manage internal MCP connectors for coding agents (add|list|remove)").option("-a, --agent <agents...>", "Target agent(s): claude-code, codex").option("-s, --scope <scope>", "Config scope: local | user | project (default: user)").option("-y, --yes", "Non-interactive mode: skip confirmation prompts").option("--json", "Machine-readable JSON output (list only; ignored by add/remove)").action(async (action, service, options2 = {}) => {
87744
+ await mcpCommand(action, service, options2);
87745
+ });
86712
87746
  }
86713
87747
 
86714
87748
  // src/cli/version-display.ts
@@ -86720,7 +87754,7 @@ init_manifest_path_resolver();
86720
87754
  init_logger();
86721
87755
  init_types2();
86722
87756
  import { readFileSync as readFileSync27 } from "node:fs";
86723
- import { join as join133 } from "node:path";
87757
+ import { join as join136 } from "node:path";
86724
87758
  var PROVIDER_LOCAL_SUBDIRS = {
86725
87759
  "claude-code": ".claude",
86726
87760
  codex: ".codex"
@@ -86775,7 +87809,7 @@ async function displayVersion() {
86775
87809
  const localSubdir = PROVIDER_LOCAL_SUBDIRS[provider];
86776
87810
  if (!localSubdir)
86777
87811
  continue;
86778
- const localRoot = join133(process.cwd(), localSubdir);
87812
+ const localRoot = join136(process.cwd(), localSubdir);
86779
87813
  if (localRoot === inst.globalRoot())
86780
87814
  continue;
86781
87815
  const resolved = findManifestPathSync(localRoot);
@@ -86856,7 +87890,7 @@ function getPackageVersion3() {
86856
87890
 
86857
87891
  // src/shared/logger.ts
86858
87892
  init_output_manager();
86859
- var import_picocolors30 = __toESM(require_picocolors(), 1);
87893
+ var import_picocolors31 = __toESM(require_picocolors(), 1);
86860
87894
  import { createWriteStream as createWriteStream4 } from "node:fs";
86861
87895
 
86862
87896
  class Logger2 {
@@ -86865,23 +87899,23 @@ class Logger2 {
86865
87899
  exitHandlerRegistered = false;
86866
87900
  info(message) {
86867
87901
  const symbols = output.getSymbols();
86868
- console.log(import_picocolors30.default.blue(symbols.info), message);
87902
+ console.log(import_picocolors31.default.blue(symbols.info), message);
86869
87903
  }
86870
87904
  success(message) {
86871
87905
  const symbols = output.getSymbols();
86872
- console.log(import_picocolors30.default.green(symbols.success), message);
87906
+ console.log(import_picocolors31.default.green(symbols.success), message);
86873
87907
  }
86874
87908
  warning(message) {
86875
87909
  const symbols = output.getSymbols();
86876
- console.log(import_picocolors30.default.yellow(symbols.warning), message);
87910
+ console.log(import_picocolors31.default.yellow(symbols.warning), message);
86877
87911
  }
86878
87912
  error(message) {
86879
87913
  const symbols = output.getSymbols();
86880
- console.error(import_picocolors30.default.red(symbols.error), message);
87914
+ console.error(import_picocolors31.default.red(symbols.error), message);
86881
87915
  }
86882
87916
  debug(message) {
86883
87917
  if (process.env.DEBUG) {
86884
- console.log(import_picocolors30.default.gray("[DEBUG]"), message);
87918
+ console.log(import_picocolors31.default.gray("[DEBUG]"), message);
86885
87919
  }
86886
87920
  }
86887
87921
  verbose(message, context) {
@@ -86890,7 +87924,7 @@ class Logger2 {
86890
87924
  const timestamp = this.getTimestamp();
86891
87925
  const sanitizedMessage = this.sanitize(message);
86892
87926
  const formattedContext = context ? this.formatContext(context) : "";
86893
- const logLine = `${timestamp} ${import_picocolors30.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
87927
+ const logLine = `${timestamp} ${import_picocolors31.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
86894
87928
  console.error(logLine);
86895
87929
  if (this.logFileStream) {
86896
87930
  const plainLogLine = `${timestamp} [VERBOSE] ${sanitizedMessage}${formattedContext}`;
@@ -86993,7 +88027,7 @@ var logger3 = new Logger2;
86993
88027
 
86994
88028
  // src/shared/output-manager.ts
86995
88029
  init_terminal_utils();
86996
- var import_picocolors31 = __toESM(require_picocolors(), 1);
88030
+ var import_picocolors32 = __toESM(require_picocolors(), 1);
86997
88031
  var SYMBOLS2 = {
86998
88032
  unicode: {
86999
88033
  prompt: "◇",
@@ -87074,7 +88108,7 @@ class OutputManager2 {
87074
88108
  if (this.config.quiet)
87075
88109
  return;
87076
88110
  const symbol = this.getSymbols().success;
87077
- console.log(import_picocolors31.default.green(`${symbol} ${message}`));
88111
+ console.log(import_picocolors32.default.green(`${symbol} ${message}`));
87078
88112
  }
87079
88113
  error(message, data) {
87080
88114
  if (this.config.json) {
@@ -87082,7 +88116,7 @@ class OutputManager2 {
87082
88116
  return;
87083
88117
  }
87084
88118
  const symbol = this.getSymbols().error;
87085
- console.error(import_picocolors31.default.red(`${symbol} ${message}`));
88119
+ console.error(import_picocolors32.default.red(`${symbol} ${message}`));
87086
88120
  }
87087
88121
  warning(message, data) {
87088
88122
  if (this.config.json) {
@@ -87092,7 +88126,7 @@ class OutputManager2 {
87092
88126
  if (this.config.quiet)
87093
88127
  return;
87094
88128
  const symbol = this.getSymbols().warning;
87095
- console.log(import_picocolors31.default.yellow(`${symbol} ${message}`));
88129
+ console.log(import_picocolors32.default.yellow(`${symbol} ${message}`));
87096
88130
  }
87097
88131
  info(message, data) {
87098
88132
  if (this.config.json) {
@@ -87102,7 +88136,7 @@ class OutputManager2 {
87102
88136
  if (this.config.quiet)
87103
88137
  return;
87104
88138
  const symbol = this.getSymbols().info;
87105
- console.log(import_picocolors31.default.blue(`${symbol} ${message}`));
88139
+ console.log(import_picocolors32.default.blue(`${symbol} ${message}`));
87106
88140
  }
87107
88141
  verbose(message, data) {
87108
88142
  if (!this.config.verbose)
@@ -87111,7 +88145,7 @@ class OutputManager2 {
87111
88145
  this.addJsonEntry({ type: "info", message, data });
87112
88146
  return;
87113
88147
  }
87114
- console.log(import_picocolors31.default.dim(` ${message}`));
88148
+ console.log(import_picocolors32.default.dim(` ${message}`));
87115
88149
  }
87116
88150
  indent(message) {
87117
88151
  if (this.config.json)
@@ -87136,7 +88170,7 @@ class OutputManager2 {
87136
88170
  return;
87137
88171
  const symbols = this.getSymbols();
87138
88172
  console.log();
87139
- console.log(import_picocolors31.default.bold(import_picocolors31.default.cyan(`${symbols.line} ${title}`)));
88173
+ console.log(import_picocolors32.default.bold(import_picocolors32.default.cyan(`${symbols.line} ${title}`)));
87140
88174
  }
87141
88175
  addJsonEntry(entry) {
87142
88176
  this.jsonBuffer.push({