@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.
- package/dist/index.js +1148 -114
- 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.
|
|
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
|
|
84267
|
-
import { dirname as
|
|
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
|
|
84271
|
-
import { basename as basename21, dirname as
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
84652
|
-
import { join as
|
|
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 (!
|
|
85685
|
+
if (!existsSync61(dir))
|
|
84655
85686
|
return [];
|
|
84656
85687
|
try {
|
|
84657
|
-
return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
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
|
|
84665
|
-
import { basename as basename19, dirname as
|
|
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 =
|
|
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 && !
|
|
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
|
|
84726
|
-
import { basename as basename20, dirname as
|
|
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 =
|
|
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 =
|
|
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 =
|
|
85936
|
+
const planDir = dirname39(planFile);
|
|
84906
85937
|
const phaseFilename = phaseNameFilenameFromTableRow(updatedBody, phaseId, planDir);
|
|
84907
|
-
if (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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
86080
|
+
const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname41(planFile));
|
|
85050
86081
|
console.log();
|
|
85051
|
-
console.log(
|
|
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(
|
|
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 ?
|
|
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 &&
|
|
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(
|
|
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(
|
|
85130
|
-
console.log(` ${
|
|
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(
|
|
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(
|
|
86191
|
+
const title = summary.title ?? basename21(dirname41(planFile));
|
|
85161
86192
|
console.log();
|
|
85162
|
-
console.log(
|
|
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
|
|
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(
|
|
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 (
|
|
86366
|
+
if (existsSync65(t)) {
|
|
85336
86367
|
const stat14 = statSync11(t);
|
|
85337
86368
|
if (stat14.isFile())
|
|
85338
86369
|
return t;
|
|
85339
|
-
const candidate =
|
|
85340
|
-
if (
|
|
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 =
|
|
85348
|
-
if (
|
|
86378
|
+
const candidate = join132(dir, "plan.md");
|
|
86379
|
+
if (existsSync65(candidate))
|
|
85349
86380
|
return candidate;
|
|
85350
|
-
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 &&
|
|
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
|
|
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
|
|
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 =
|
|
85449
|
-
var EVENT_BUFFER_DIR =
|
|
85450
|
-
var RATE_STATE_PATH =
|
|
85451
|
-
var TAKUMI_MANIFEST_PATH =
|
|
85452
|
-
var LEGACY_METADATA_PATH =
|
|
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 (!
|
|
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 (!
|
|
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(
|
|
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:
|
|
85508
|
-
userCacheExists:
|
|
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
|
|
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
|
|
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
|
|
85669
|
-
import { existsSync as
|
|
85670
|
-
import { dirname as
|
|
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 (
|
|
86704
|
+
if (existsSync67(getManifestPath(installPath)))
|
|
85674
86705
|
present.push(MANIFEST_FILENAME);
|
|
85675
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(` ${
|
|
86810
|
+
console.log(` ${import_picocolors28.default.red("✖")} ${item.path}`);
|
|
85780
86811
|
}
|
|
85781
86812
|
if (analysis.toDelete.length > 10) {
|
|
85782
|
-
console.log(
|
|
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(
|
|
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(` ${
|
|
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(
|
|
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 =
|
|
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 ?
|
|
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(
|
|
85923
|
-
`) +
|
|
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(
|
|
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(
|
|
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(
|
|
87048
|
+
log.info(import_picocolors29.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
|
|
86018
87049
|
}
|
|
86019
87050
|
if (validOptions.dryRun) {
|
|
86020
|
-
log.info(
|
|
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(`${
|
|
86031
|
-
${
|
|
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
|
|
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
|
-
${
|
|
87537
|
+
${import_picocolors30.default.bold(import_picocolors30.default.cyan(kitName))} - Available Versions:
|
|
86507
87538
|
`);
|
|
86508
87539
|
if (releases.length === 0) {
|
|
86509
|
-
console.log(
|
|
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 =
|
|
87544
|
+
const version3 = import_picocolors30.default.green(release.tag);
|
|
86514
87545
|
const publishedAt = formatRelativeTime(release.publishedAt);
|
|
86515
|
-
const badge = release.prerelease ? ` ${
|
|
87546
|
+
const badge = release.prerelease ? ` ${import_picocolors30.default.yellow("[prerelease]")}` : "";
|
|
86516
87547
|
const versionPart = version3.padEnd(20);
|
|
86517
|
-
const timePart =
|
|
87548
|
+
const timePart = import_picocolors30.default.dim(publishedAt.padEnd(20));
|
|
86518
87549
|
console.log(` ${versionPart} ${timePart}${badge}`);
|
|
86519
87550
|
}
|
|
86520
|
-
console.log(
|
|
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
|
-
${
|
|
86566
|
-
console.log(
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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} ${
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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({
|