@sunasteriskrnd/takumi 1.0.0-dev.44 → 1.0.0-dev.46
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 +1190 -122
- 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.46",
|
|
19819
19819
|
description: "CLI tool for bootstrapping and managing Takumi projects",
|
|
19820
19820
|
type: "module",
|
|
19821
19821
|
repository: {
|
|
@@ -48344,7 +48344,12 @@ async function runPkceFlow(options2) {
|
|
|
48344
48344
|
codeChallenge: run.pair.challenge,
|
|
48345
48345
|
port: run.port
|
|
48346
48346
|
});
|
|
48347
|
-
|
|
48347
|
+
options2.onAuthUrl?.(url);
|
|
48348
|
+
try {
|
|
48349
|
+
await openBrowser(url);
|
|
48350
|
+
} catch (err) {
|
|
48351
|
+
options2.onOpenBrowserError?.(err instanceof Error ? err : new Error(String(err)));
|
|
48352
|
+
}
|
|
48348
48353
|
const callback = await Promise.race([received, timeoutPromise]);
|
|
48349
48354
|
const code = callback.searchParams.get("code");
|
|
48350
48355
|
const callbackState = callback.searchParams.get("state");
|
|
@@ -48364,7 +48369,9 @@ var init_pkce_flow = __esm(() => {
|
|
|
48364
48369
|
PkceTimeoutError = class PkceTimeoutError extends Error {
|
|
48365
48370
|
constructor() {
|
|
48366
48371
|
super(`Login timed out after 90s waiting for browser callback.
|
|
48367
|
-
` + " - Re-run `tkm auth login` if the browser opened but you didn't complete sign-in.\n" +
|
|
48372
|
+
` + " - Re-run `tkm auth login` if the browser opened but you didn't complete sign-in.\n" + ` - If the browser didn't open, copy the sign-in link printed above and open it
|
|
48373
|
+
` + ` manually in a browser on this machine.
|
|
48374
|
+
` + " - On a headless / WSL2 box without a browser, run `tkm auth login` from your\n" + " laptop and copy the token file to `<home>/.takumi/auth/token.json`.");
|
|
48368
48375
|
this.name = "PkceTimeoutError";
|
|
48369
48376
|
}
|
|
48370
48377
|
};
|
|
@@ -48397,7 +48404,7 @@ async function exchangeCodeForSession(webUrl, code, codeVerifier) {
|
|
|
48397
48404
|
}
|
|
48398
48405
|
return await res.json();
|
|
48399
48406
|
}
|
|
48400
|
-
async function runLoginFlow() {
|
|
48407
|
+
async function runLoginFlow(hooks = {}) {
|
|
48401
48408
|
const serverUrl = getServerUrl();
|
|
48402
48409
|
const pair = generatePkcePair();
|
|
48403
48410
|
const state = generateState();
|
|
@@ -48405,6 +48412,8 @@ async function runLoginFlow() {
|
|
|
48405
48412
|
const callback = await runPkceFlow({
|
|
48406
48413
|
state,
|
|
48407
48414
|
pair,
|
|
48415
|
+
onAuthUrl: hooks.onAuthUrl,
|
|
48416
|
+
onOpenBrowserError: hooks.onOpenBrowserError,
|
|
48408
48417
|
authUrl: ({ state: s, codeChallenge, port }) => `${serverUrl}/cli/auth?state=${encodeURIComponent(s)}` + `&code_challenge=${encodeURIComponent(codeChallenge)}` + `&port=${port}&prompt=1` + `&cli_version=${encodeURIComponent(cliVersion)}`
|
|
48409
48418
|
});
|
|
48410
48419
|
const tokens = await exchangeCodeForSession(serverUrl, callback.code, pair.verifier);
|
|
@@ -48439,15 +48448,29 @@ function reportLoginError(err) {
|
|
|
48439
48448
|
async function login() {
|
|
48440
48449
|
oe("Takumi login");
|
|
48441
48450
|
const spinner = de();
|
|
48442
|
-
|
|
48451
|
+
let spinning = false;
|
|
48443
48452
|
try {
|
|
48444
|
-
const result = await runLoginFlow(
|
|
48445
|
-
|
|
48453
|
+
const result = await runLoginFlow({
|
|
48454
|
+
onAuthUrl: (url) => {
|
|
48455
|
+
f2.step("Open this link to sign in (copy it if the browser doesn't open):");
|
|
48456
|
+
console.log(`
|
|
48457
|
+
${url}
|
|
48458
|
+
`);
|
|
48459
|
+
spinner.start("Waiting for sign-in in browser (90s timeout)…");
|
|
48460
|
+
spinning = true;
|
|
48461
|
+
},
|
|
48462
|
+
onOpenBrowserError: () => {
|
|
48463
|
+
spinner.message("Browser didn't open — copy the link above to sign in (90s timeout)…");
|
|
48464
|
+
}
|
|
48465
|
+
});
|
|
48466
|
+
if (spinning)
|
|
48467
|
+
spinner.stop("Signed in.");
|
|
48446
48468
|
if (result.email)
|
|
48447
48469
|
f2.success(`Signed in as ${result.email}`);
|
|
48448
48470
|
$e("You're ready to go.");
|
|
48449
48471
|
} catch (err) {
|
|
48450
|
-
|
|
48472
|
+
if (spinning)
|
|
48473
|
+
spinner.stop("Login failed.", 1);
|
|
48451
48474
|
reportLoginError(err);
|
|
48452
48475
|
process.exitCode = 1;
|
|
48453
48476
|
}
|
|
@@ -48631,7 +48654,18 @@ async function ensureAuthenticated(options2) {
|
|
|
48631
48654
|
if (!mod.runLoginFlow) {
|
|
48632
48655
|
throw new Error("Login command is not available in this build. Run `tkm auth login` manually.");
|
|
48633
48656
|
}
|
|
48634
|
-
await
|
|
48657
|
+
const { log: log2 } = await Promise.resolve().then(() => (init_dist2(), exports_dist));
|
|
48658
|
+
await mod.runLoginFlow({
|
|
48659
|
+
onAuthUrl: (url) => {
|
|
48660
|
+
log2.step("Open this link to sign in (copy it if the browser doesn't open):");
|
|
48661
|
+
console.log(`
|
|
48662
|
+
${url}
|
|
48663
|
+
`);
|
|
48664
|
+
},
|
|
48665
|
+
onOpenBrowserError: () => {
|
|
48666
|
+
log2.warn("Browser didn't open — copy the link above to sign in.");
|
|
48667
|
+
}
|
|
48668
|
+
});
|
|
48635
48669
|
});
|
|
48636
48670
|
await runLogin();
|
|
48637
48671
|
const token = await getToken({ ...options2, forceConfigRefresh: false });
|
|
@@ -49807,6 +49841,73 @@ var init_artifact_command_help = __esm(() => {
|
|
|
49807
49841
|
};
|
|
49808
49842
|
});
|
|
49809
49843
|
|
|
49844
|
+
// src/domains/help/commands/mcp-command-help.ts
|
|
49845
|
+
var mcpCommandHelp;
|
|
49846
|
+
var init_mcp_command_help = __esm(() => {
|
|
49847
|
+
mcpCommandHelp = {
|
|
49848
|
+
name: "mcp",
|
|
49849
|
+
description: "Manage internal MCP connectors for coding agents (add|list|remove)",
|
|
49850
|
+
usage: "tkm mcp <add|list|remove> [service] [options] [-- <extra args...>]",
|
|
49851
|
+
examples: [
|
|
49852
|
+
{
|
|
49853
|
+
command: "tkm mcp list",
|
|
49854
|
+
description: "Show available internal MCP services and per-agent configured state"
|
|
49855
|
+
},
|
|
49856
|
+
{
|
|
49857
|
+
command: "tkm mcp add meet-plus",
|
|
49858
|
+
description: "Register the 'meet-plus' MCP with the current project's coding agent(s)"
|
|
49859
|
+
},
|
|
49860
|
+
{
|
|
49861
|
+
command: "tkm mcp add playwright -- --browser msedge --headless",
|
|
49862
|
+
description: "Add a stdio MCP with extra launch args appended after the registry defaults"
|
|
49863
|
+
}
|
|
49864
|
+
],
|
|
49865
|
+
optionGroups: [
|
|
49866
|
+
{
|
|
49867
|
+
title: "Actions",
|
|
49868
|
+
options: [
|
|
49869
|
+
{
|
|
49870
|
+
flags: "add <service> [-- <extra args...>]",
|
|
49871
|
+
description: "Add a registry service to detected agent(s); argv after -- is appended to a stdio service's args (e.g. -- --browser msedge)"
|
|
49872
|
+
},
|
|
49873
|
+
{
|
|
49874
|
+
flags: "list",
|
|
49875
|
+
description: "List registry services with per-agent configured status"
|
|
49876
|
+
},
|
|
49877
|
+
{
|
|
49878
|
+
flags: "remove <service>",
|
|
49879
|
+
description: "Remove a service from detected agent(s); absent entries are skipped"
|
|
49880
|
+
}
|
|
49881
|
+
]
|
|
49882
|
+
},
|
|
49883
|
+
{
|
|
49884
|
+
title: "Options",
|
|
49885
|
+
options: [
|
|
49886
|
+
{
|
|
49887
|
+
flags: "-a, --agent <agents...>",
|
|
49888
|
+
description: "Target specific agent(s): claude-code | codex (repeatable)"
|
|
49889
|
+
},
|
|
49890
|
+
{
|
|
49891
|
+
flags: "-s, --scope <scope>",
|
|
49892
|
+
description: "Config scope: local | user | project (default: user; agent-dependent)"
|
|
49893
|
+
},
|
|
49894
|
+
{ flags: "-y, --yes", description: "Non-interactive mode: skip selection prompts" },
|
|
49895
|
+
{ flags: "--json", description: "Machine-readable JSON output (list only)" }
|
|
49896
|
+
]
|
|
49897
|
+
}
|
|
49898
|
+
],
|
|
49899
|
+
sections: [
|
|
49900
|
+
{
|
|
49901
|
+
title: "Registry source",
|
|
49902
|
+
content: `Cache-first: an existing local cache (<TAKUMI_HOME>/mcp/registry-cache.json) is
|
|
49903
|
+
` + `served immediately while a background request refreshes it for the next run.
|
|
49904
|
+
` + `With no cache yet, the CLI fetches the Takumi server directly and reports an
|
|
49905
|
+
` + "error when the server is unreachable (no stale in-package fallback)."
|
|
49906
|
+
}
|
|
49907
|
+
]
|
|
49908
|
+
};
|
|
49909
|
+
});
|
|
49910
|
+
|
|
49810
49911
|
// src/domains/help/commands/index.ts
|
|
49811
49912
|
var init_commands2 = __esm(() => {
|
|
49812
49913
|
init_init_command_help();
|
|
@@ -49817,6 +49918,7 @@ var init_commands2 = __esm(() => {
|
|
|
49817
49918
|
init_config_command_help();
|
|
49818
49919
|
init_auth_command_help();
|
|
49819
49920
|
init_artifact_command_help();
|
|
49921
|
+
init_mcp_command_help();
|
|
49820
49922
|
init_common_options();
|
|
49821
49923
|
});
|
|
49822
49924
|
|
|
@@ -49836,7 +49938,8 @@ var init_help_commands = __esm(() => {
|
|
|
49836
49938
|
doctor: doctorCommandHelp,
|
|
49837
49939
|
uninstall: uninstallCommandHelp,
|
|
49838
49940
|
auth: authCommandHelp,
|
|
49839
|
-
artifact: artifactCommandHelp
|
|
49941
|
+
artifact: artifactCommandHelp,
|
|
49942
|
+
mcp: mcpCommandHelp
|
|
49840
49943
|
};
|
|
49841
49944
|
});
|
|
49842
49945
|
|
|
@@ -84261,22 +84364,984 @@ async function initCommand(options2) {
|
|
|
84261
84364
|
throw error;
|
|
84262
84365
|
}
|
|
84263
84366
|
}
|
|
84367
|
+
// src/domains/mcp/mcp-service.ts
|
|
84368
|
+
init_logger();
|
|
84369
|
+
|
|
84370
|
+
// src/domains/mcp/mcp-service-core.ts
|
|
84371
|
+
init_environment();
|
|
84372
|
+
init_logger();
|
|
84373
|
+
init_dist2();
|
|
84374
|
+
|
|
84375
|
+
// src/domains/mcp/agent-detector.ts
|
|
84376
|
+
init_environment();
|
|
84377
|
+
import { existsSync as existsSync58 } from "node:fs";
|
|
84378
|
+
import { join as join126 } from "node:path";
|
|
84379
|
+
|
|
84380
|
+
// src/domains/mcp/shell-out.ts
|
|
84381
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
84382
|
+
var DEFAULT_TIMEOUT_MS7 = 60000;
|
|
84383
|
+
var PROBE_TIMEOUT_MS = 5000;
|
|
84384
|
+
var SERVICE_NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
84385
|
+
function isValidServiceName(name2) {
|
|
84386
|
+
return SERVICE_NAME_PATTERN.test(name2);
|
|
84387
|
+
}
|
|
84388
|
+
function runAgentCli(bin, args, timeoutMs = DEFAULT_TIMEOUT_MS7) {
|
|
84389
|
+
const result = spawnSync5(bin, args, {
|
|
84390
|
+
timeout: timeoutMs,
|
|
84391
|
+
encoding: "utf-8",
|
|
84392
|
+
shell: false,
|
|
84393
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
84394
|
+
});
|
|
84395
|
+
if (result.error) {
|
|
84396
|
+
return {
|
|
84397
|
+
ok: false,
|
|
84398
|
+
code: null,
|
|
84399
|
+
stdout: "",
|
|
84400
|
+
stderr: result.error.message
|
|
84401
|
+
};
|
|
84402
|
+
}
|
|
84403
|
+
return {
|
|
84404
|
+
ok: result.status === 0,
|
|
84405
|
+
code: result.status,
|
|
84406
|
+
stdout: result.stdout ?? "",
|
|
84407
|
+
stderr: result.stderr ?? ""
|
|
84408
|
+
};
|
|
84409
|
+
}
|
|
84410
|
+
function isBinaryOnPath(bin) {
|
|
84411
|
+
const probeCmd = process.platform === "win32" ? "where" : "which";
|
|
84412
|
+
const result = spawnSync5(probeCmd, [bin], {
|
|
84413
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
84414
|
+
encoding: "utf-8",
|
|
84415
|
+
shell: false
|
|
84416
|
+
});
|
|
84417
|
+
if (result.error || result.status !== 0)
|
|
84418
|
+
return false;
|
|
84419
|
+
if (process.platform !== "win32")
|
|
84420
|
+
return true;
|
|
84421
|
+
return (result.stdout ?? "").split(/\r?\n/).some((line) => /\.(exe|com)$/i.test(line.trim()));
|
|
84422
|
+
}
|
|
84423
|
+
function isAlreadyExistsError(result) {
|
|
84424
|
+
return /already exists/i.test(`${result.stderr}
|
|
84425
|
+
${result.stdout}`);
|
|
84426
|
+
}
|
|
84427
|
+
|
|
84428
|
+
// src/domains/mcp/types.ts
|
|
84429
|
+
init_zod();
|
|
84430
|
+
var McpAgentSchema = exports_external.enum(["claude-code", "codex"]);
|
|
84431
|
+
var ALL_MCP_AGENTS = ["claude-code", "codex"];
|
|
84432
|
+
var McpScopeSchema = exports_external.enum(["local", "user", "project"]);
|
|
84433
|
+
var ALL_MCP_SCOPES = ["local", "user", "project"];
|
|
84434
|
+
var DEFAULT_MCP_SCOPE = "user";
|
|
84435
|
+
var McpTransportSchema = exports_external.enum(["http", "sse", "stdio"]);
|
|
84436
|
+
var McpRemoteServiceEntrySchema = exports_external.object({
|
|
84437
|
+
name: exports_external.string(),
|
|
84438
|
+
description: exports_external.string(),
|
|
84439
|
+
transport: exports_external.enum(["http", "sse"]),
|
|
84440
|
+
url: exports_external.string().url()
|
|
84441
|
+
});
|
|
84442
|
+
var McpStdioServiceEntrySchema = exports_external.object({
|
|
84443
|
+
name: exports_external.string(),
|
|
84444
|
+
description: exports_external.string(),
|
|
84445
|
+
transport: exports_external.literal("stdio"),
|
|
84446
|
+
command: exports_external.string().min(1),
|
|
84447
|
+
args: exports_external.array(exports_external.string()).nullish()
|
|
84448
|
+
});
|
|
84449
|
+
var McpServiceEntrySchema = exports_external.union([
|
|
84450
|
+
McpRemoteServiceEntrySchema,
|
|
84451
|
+
McpStdioServiceEntrySchema
|
|
84452
|
+
]);
|
|
84453
|
+
var McpRegistrySchema = exports_external.object({
|
|
84454
|
+
services: exports_external.array(McpServiceEntrySchema)
|
|
84455
|
+
});
|
|
84456
|
+
|
|
84457
|
+
// src/domains/mcp/agent-detector.ts
|
|
84458
|
+
var AGENT_BINARY = {
|
|
84459
|
+
"claude-code": "claude",
|
|
84460
|
+
codex: "codex"
|
|
84461
|
+
};
|
|
84462
|
+
function getAgentConfigPath(agent) {
|
|
84463
|
+
const home6 = getHomeDirectoryFromEnv();
|
|
84464
|
+
if (!home6)
|
|
84465
|
+
return null;
|
|
84466
|
+
switch (agent) {
|
|
84467
|
+
case "claude-code":
|
|
84468
|
+
return join126(home6, ".claude.json");
|
|
84469
|
+
case "codex":
|
|
84470
|
+
return join126(home6, ".codex", "config.toml");
|
|
84471
|
+
default: {
|
|
84472
|
+
const _exhaustive = agent;
|
|
84473
|
+
return _exhaustive;
|
|
84474
|
+
}
|
|
84475
|
+
}
|
|
84476
|
+
}
|
|
84477
|
+
var defaultCheckers = {
|
|
84478
|
+
binaryOnPath: isBinaryOnPath,
|
|
84479
|
+
configExists: existsSync58
|
|
84480
|
+
};
|
|
84481
|
+
function isAgentPresent(agent, checkers = defaultCheckers) {
|
|
84482
|
+
if (checkers.binaryOnPath(AGENT_BINARY[agent])) {
|
|
84483
|
+
return true;
|
|
84484
|
+
}
|
|
84485
|
+
const configPath = getAgentConfigPath(agent);
|
|
84486
|
+
return configPath !== null && checkers.configExists(configPath);
|
|
84487
|
+
}
|
|
84488
|
+
function detectAgents(checkers = defaultCheckers) {
|
|
84489
|
+
return ALL_MCP_AGENTS.filter((agent) => isAgentPresent(agent, checkers));
|
|
84490
|
+
}
|
|
84491
|
+
var PROJECT_MARKERS = {
|
|
84492
|
+
"claude-code": [".claude", "CLAUDE.md"],
|
|
84493
|
+
codex: [".codex", "AGENTS.md"]
|
|
84494
|
+
};
|
|
84495
|
+
function detectProjectAgents(cwd2 = process.cwd(), exists2 = existsSync58) {
|
|
84496
|
+
return ALL_MCP_AGENTS.filter((agent) => PROJECT_MARKERS[agent].some((marker) => exists2(join126(cwd2, marker))));
|
|
84497
|
+
}
|
|
84498
|
+
|
|
84499
|
+
// src/domains/mcp/registry-client.ts
|
|
84500
|
+
init_zod();
|
|
84501
|
+
init_logger();
|
|
84502
|
+
init_auth_client();
|
|
84503
|
+
|
|
84504
|
+
// src/domains/mcp/registry-cache.ts
|
|
84505
|
+
init_logger();
|
|
84506
|
+
init_paths2();
|
|
84507
|
+
import { promises as fs33 } from "node:fs";
|
|
84508
|
+
import { join as join127 } from "node:path";
|
|
84509
|
+
function getCacheDir() {
|
|
84510
|
+
return join127(getConfigDir(), "mcp");
|
|
84511
|
+
}
|
|
84512
|
+
function getCachePath2() {
|
|
84513
|
+
return join127(getCacheDir(), "registry-cache.json");
|
|
84514
|
+
}
|
|
84515
|
+
async function readCachedRegistry() {
|
|
84516
|
+
try {
|
|
84517
|
+
const raw = await fs33.readFile(getCachePath2(), "utf8");
|
|
84518
|
+
const parsed = JSON.parse(raw);
|
|
84519
|
+
const result = McpRegistrySchema.safeParse({ services: parsed.services });
|
|
84520
|
+
if (!result.success)
|
|
84521
|
+
return null;
|
|
84522
|
+
return {
|
|
84523
|
+
registry: result.data,
|
|
84524
|
+
cachedAt: typeof parsed.cachedAt === "number" ? parsed.cachedAt : 0
|
|
84525
|
+
};
|
|
84526
|
+
} catch (err) {
|
|
84527
|
+
if (err?.code === "ENOENT")
|
|
84528
|
+
return null;
|
|
84529
|
+
logger.verbose(`registry-cache: read failed (${err instanceof Error ? err.message : String(err)})`);
|
|
84530
|
+
return null;
|
|
84531
|
+
}
|
|
84532
|
+
}
|
|
84533
|
+
async function writeCachedRegistry(registry) {
|
|
84534
|
+
const dir = getCacheDir();
|
|
84535
|
+
await fs33.mkdir(dir, { recursive: true, mode: 448 });
|
|
84536
|
+
const body = { services: registry.services, cachedAt: Date.now() };
|
|
84537
|
+
await fs33.writeFile(getCachePath2(), JSON.stringify(body, null, 2), {
|
|
84538
|
+
mode: 384,
|
|
84539
|
+
encoding: "utf8"
|
|
84540
|
+
});
|
|
84541
|
+
}
|
|
84542
|
+
|
|
84543
|
+
// src/domains/mcp/writers/json-config-file.ts
|
|
84544
|
+
import { readFile as readFile44 } from "node:fs/promises";
|
|
84545
|
+
function toMessage(error) {
|
|
84546
|
+
return error instanceof Error ? error.message : String(error);
|
|
84547
|
+
}
|
|
84548
|
+
function isEnoent(error) {
|
|
84549
|
+
return error?.code === "ENOENT";
|
|
84550
|
+
}
|
|
84551
|
+
async function readJsonConfigFile(path11) {
|
|
84552
|
+
let content;
|
|
84553
|
+
try {
|
|
84554
|
+
content = await readFile44(path11, "utf-8");
|
|
84555
|
+
} catch (error) {
|
|
84556
|
+
if (isEnoent(error))
|
|
84557
|
+
return { ok: true, raw: {} };
|
|
84558
|
+
return { ok: false, detail: toMessage(error) };
|
|
84559
|
+
}
|
|
84560
|
+
try {
|
|
84561
|
+
const parsed = JSON.parse(content);
|
|
84562
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
84563
|
+
return { ok: true, raw: parsed };
|
|
84564
|
+
}
|
|
84565
|
+
return { ok: false, detail: `Refusing to overwrite malformed ${path11}: not a JSON object` };
|
|
84566
|
+
} catch (error) {
|
|
84567
|
+
return { ok: false, detail: `Refusing to overwrite malformed ${path11}: ${toMessage(error)}` };
|
|
84568
|
+
}
|
|
84569
|
+
}
|
|
84570
|
+
function extractMcpServers(raw) {
|
|
84571
|
+
const servers = raw.mcpServers;
|
|
84572
|
+
return servers && typeof servers === "object" && !Array.isArray(servers) ? servers : {};
|
|
84573
|
+
}
|
|
84574
|
+
|
|
84575
|
+
// src/domains/mcp/registry-client.ts
|
|
84576
|
+
class RegistryUnavailableError extends Error {
|
|
84577
|
+
constructor(cause) {
|
|
84578
|
+
super(`Cannot load the MCP registry: ${cause}. Check your network connection (the registry is fetched from the Takumi server) and retry.`);
|
|
84579
|
+
this.name = "RegistryUnavailableError";
|
|
84580
|
+
}
|
|
84581
|
+
}
|
|
84582
|
+
var REGISTRY_PATH2 = "/api/v1/mcp/registry";
|
|
84583
|
+
var REGISTRY_FETCH_TIMEOUT_MS = 3000;
|
|
84584
|
+
var LooseRegistrySchema = exports_external.object({ services: exports_external.array(exports_external.unknown()) });
|
|
84585
|
+
function parseRegistryLenient(data) {
|
|
84586
|
+
const loose = LooseRegistrySchema.parse(data);
|
|
84587
|
+
const services = [];
|
|
84588
|
+
for (const raw of loose.services) {
|
|
84589
|
+
const parsed = McpServiceEntrySchema.safeParse(raw);
|
|
84590
|
+
if (parsed.success) {
|
|
84591
|
+
services.push(parsed.data);
|
|
84592
|
+
} else {
|
|
84593
|
+
const name2 = raw?.name;
|
|
84594
|
+
logger.verbose(`registry: dropped invalid entry ${typeof name2 === "string" ? `"${name2}"` : "(unnamed)"}`);
|
|
84595
|
+
}
|
|
84596
|
+
}
|
|
84597
|
+
if (loose.services.length > 0 && services.length === 0) {
|
|
84598
|
+
throw new Error("registry: every entry failed validation (schema drift?)");
|
|
84599
|
+
}
|
|
84600
|
+
return { services };
|
|
84601
|
+
}
|
|
84602
|
+
async function fetchRemoteRegistry() {
|
|
84603
|
+
const res = await fetch(`${getServerUrl()}${REGISTRY_PATH2}`, {
|
|
84604
|
+
headers: { "X-TKM-Client": "takumi-cli/mcp" },
|
|
84605
|
+
signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS)
|
|
84606
|
+
});
|
|
84607
|
+
if (!res.ok)
|
|
84608
|
+
throw new Error(`registry: HTTP ${res.status}`);
|
|
84609
|
+
return parseRegistryLenient(await res.json());
|
|
84610
|
+
}
|
|
84611
|
+
async function refreshCache() {
|
|
84612
|
+
try {
|
|
84613
|
+
await writeCachedRegistry(await fetchRemoteRegistry());
|
|
84614
|
+
return true;
|
|
84615
|
+
} catch (err) {
|
|
84616
|
+
logger.verbose(`registry: revalidate skipped (${toMessage(err)})`);
|
|
84617
|
+
return false;
|
|
84618
|
+
}
|
|
84619
|
+
}
|
|
84620
|
+
async function getRegistryWithSource() {
|
|
84621
|
+
const cached = await readCachedRegistry();
|
|
84622
|
+
if (cached) {
|
|
84623
|
+
return { registry: cached.registry, source: "cache", revalidated: refreshCache() };
|
|
84624
|
+
}
|
|
84625
|
+
try {
|
|
84626
|
+
const remote = await fetchRemoteRegistry();
|
|
84627
|
+
try {
|
|
84628
|
+
await writeCachedRegistry(remote);
|
|
84629
|
+
} catch (err) {
|
|
84630
|
+
logger.verbose(`registry: cache write skipped (${toMessage(err)})`);
|
|
84631
|
+
}
|
|
84632
|
+
return { registry: remote, source: "server" };
|
|
84633
|
+
} catch (err) {
|
|
84634
|
+
throw new RegistryUnavailableError(toMessage(err));
|
|
84635
|
+
}
|
|
84636
|
+
}
|
|
84637
|
+
function resolveService(registry, name2) {
|
|
84638
|
+
return registry.services.find((service) => service.name === name2);
|
|
84639
|
+
}
|
|
84640
|
+
|
|
84641
|
+
// src/domains/mcp/writers/claude-config-file.ts
|
|
84642
|
+
import { existsSync as existsSync59 } from "node:fs";
|
|
84643
|
+
import { mkdir as mkdir29, writeFile as writeFile31 } from "node:fs/promises";
|
|
84644
|
+
import { dirname as dirname36, join as join128 } from "node:path";
|
|
84645
|
+
var AGENT = "claude-code";
|
|
84646
|
+
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";
|
|
84647
|
+
function userConfigPath() {
|
|
84648
|
+
return getAgentConfigPath(AGENT);
|
|
84649
|
+
}
|
|
84650
|
+
function projectConfigPath() {
|
|
84651
|
+
return join128(process.cwd(), ".mcp.json");
|
|
84652
|
+
}
|
|
84653
|
+
function configPathFor(scope) {
|
|
84654
|
+
return scope === "project" ? projectConfigPath() : userConfigPath();
|
|
84655
|
+
}
|
|
84656
|
+
function withLocalScopeFallbackWarning(detail, scope) {
|
|
84657
|
+
if (scope !== "local")
|
|
84658
|
+
return detail;
|
|
84659
|
+
return detail ? `${detail} (${LOCAL_SCOPE_FALLBACK_WARNING})` : LOCAL_SCOPE_FALLBACK_WARNING;
|
|
84660
|
+
}
|
|
84661
|
+
async function fileAdd(entry, scope) {
|
|
84662
|
+
const path11 = configPathFor(scope);
|
|
84663
|
+
if (!path11) {
|
|
84664
|
+
return { agent: AGENT, status: "failed", detail: "Could not resolve home directory" };
|
|
84665
|
+
}
|
|
84666
|
+
const existing = await readJsonConfigFile(path11);
|
|
84667
|
+
if (!existing.ok) {
|
|
84668
|
+
return { agent: AGENT, status: "failed", detail: existing.detail };
|
|
84669
|
+
}
|
|
84670
|
+
try {
|
|
84671
|
+
const servers = extractMcpServers(existing.raw);
|
|
84672
|
+
servers[entry.name] = entry.transport === "stdio" ? {
|
|
84673
|
+
type: "stdio",
|
|
84674
|
+
command: entry.command,
|
|
84675
|
+
...entry.args?.length ? { args: entry.args } : {}
|
|
84676
|
+
} : { type: entry.transport, url: entry.url };
|
|
84677
|
+
await mkdir29(dirname36(path11), { recursive: true });
|
|
84678
|
+
await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
|
|
84679
|
+
return {
|
|
84680
|
+
agent: AGENT,
|
|
84681
|
+
status: "added",
|
|
84682
|
+
detail: withLocalScopeFallbackWarning(`Wrote ${path11} (file fallback)`, scope)
|
|
84683
|
+
};
|
|
84684
|
+
} catch (error) {
|
|
84685
|
+
return { agent: AGENT, status: "failed", detail: toMessage(error) };
|
|
84686
|
+
}
|
|
84687
|
+
}
|
|
84688
|
+
async function fileRemove(name2, scope) {
|
|
84689
|
+
const path11 = configPathFor(scope);
|
|
84690
|
+
if (!path11 || !existsSync59(path11)) {
|
|
84691
|
+
return { agent: AGENT, status: "skipped", detail: "No config file present" };
|
|
84692
|
+
}
|
|
84693
|
+
const existing = await readJsonConfigFile(path11);
|
|
84694
|
+
if (!existing.ok) {
|
|
84695
|
+
return { agent: AGENT, status: "failed", detail: existing.detail };
|
|
84696
|
+
}
|
|
84697
|
+
try {
|
|
84698
|
+
const servers = extractMcpServers(existing.raw);
|
|
84699
|
+
if (!(name2 in servers)) {
|
|
84700
|
+
return { agent: AGENT, status: "skipped", detail: "Not configured" };
|
|
84701
|
+
}
|
|
84702
|
+
delete servers[name2];
|
|
84703
|
+
await writeFile31(path11, JSON.stringify({ ...existing.raw, mcpServers: servers }, null, 2), "utf-8");
|
|
84704
|
+
return {
|
|
84705
|
+
agent: AGENT,
|
|
84706
|
+
status: "removed",
|
|
84707
|
+
detail: withLocalScopeFallbackWarning(`Updated ${path11} (file fallback)`, scope)
|
|
84708
|
+
};
|
|
84709
|
+
} catch (error) {
|
|
84710
|
+
return { agent: AGENT, status: "failed", detail: toMessage(error) };
|
|
84711
|
+
}
|
|
84712
|
+
}
|
|
84713
|
+
async function fileConfigured(name2, scope) {
|
|
84714
|
+
const path11 = configPathFor(scope);
|
|
84715
|
+
if (!path11)
|
|
84716
|
+
return false;
|
|
84717
|
+
const existing = await readJsonConfigFile(path11);
|
|
84718
|
+
return existing.ok && name2 in extractMcpServers(existing.raw);
|
|
84719
|
+
}
|
|
84720
|
+
async function listUserConfigured() {
|
|
84721
|
+
const path11 = userConfigPath();
|
|
84722
|
+
if (!path11)
|
|
84723
|
+
return { has: () => false };
|
|
84724
|
+
const existing = await readJsonConfigFile(path11);
|
|
84725
|
+
if (!existing.ok)
|
|
84726
|
+
return { has: () => false };
|
|
84727
|
+
const servers = extractMcpServers(existing.raw);
|
|
84728
|
+
return { has: (name2) => (name2 in servers) };
|
|
84729
|
+
}
|
|
84730
|
+
|
|
84731
|
+
// src/domains/mcp/writers/claude-writer.ts
|
|
84732
|
+
var AGENT2 = "claude-code";
|
|
84733
|
+
var BIN = "claude";
|
|
84734
|
+
async function add(entry, opts) {
|
|
84735
|
+
if (!isValidServiceName(entry.name)) {
|
|
84736
|
+
return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${entry.name}` };
|
|
84737
|
+
}
|
|
84738
|
+
if (opts.scope !== "local" && await fileConfigured(entry.name, opts.scope)) {
|
|
84739
|
+
return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
|
|
84740
|
+
}
|
|
84741
|
+
if (isBinaryOnPath(BIN)) {
|
|
84742
|
+
const argv = entry.transport === "stdio" ? [
|
|
84743
|
+
"mcp",
|
|
84744
|
+
"add",
|
|
84745
|
+
"--scope",
|
|
84746
|
+
opts.scope,
|
|
84747
|
+
entry.name,
|
|
84748
|
+
"--",
|
|
84749
|
+
entry.command,
|
|
84750
|
+
...entry.args ?? []
|
|
84751
|
+
] : [
|
|
84752
|
+
"mcp",
|
|
84753
|
+
"add",
|
|
84754
|
+
"--transport",
|
|
84755
|
+
entry.transport,
|
|
84756
|
+
entry.name,
|
|
84757
|
+
entry.url,
|
|
84758
|
+
"--scope",
|
|
84759
|
+
opts.scope
|
|
84760
|
+
];
|
|
84761
|
+
const result = runAgentCli(BIN, argv);
|
|
84762
|
+
if (result.ok) {
|
|
84763
|
+
return { agent: AGENT2, status: "added", detail: result.stdout.trim() || undefined };
|
|
84764
|
+
}
|
|
84765
|
+
if (isAlreadyExistsError(result)) {
|
|
84766
|
+
return { agent: AGENT2, status: "skipped", detail: "already configured — no change" };
|
|
84767
|
+
}
|
|
84768
|
+
return {
|
|
84769
|
+
agent: AGENT2,
|
|
84770
|
+
status: "failed",
|
|
84771
|
+
detail: result.stderr.trim() || `exit code ${result.code}`
|
|
84772
|
+
};
|
|
84773
|
+
}
|
|
84774
|
+
return fileAdd(entry, opts.scope);
|
|
84775
|
+
}
|
|
84776
|
+
async function remove10(name2, opts) {
|
|
84777
|
+
if (!isValidServiceName(name2)) {
|
|
84778
|
+
return { agent: AGENT2, status: "failed", detail: `Invalid service name: ${name2}` };
|
|
84779
|
+
}
|
|
84780
|
+
if (isBinaryOnPath(BIN)) {
|
|
84781
|
+
if (opts.scope !== "local" && !await fileConfigured(name2, opts.scope)) {
|
|
84782
|
+
return { agent: AGENT2, status: "skipped", detail: "Not configured" };
|
|
84783
|
+
}
|
|
84784
|
+
const result = runAgentCli(BIN, ["mcp", "remove", name2, "--scope", opts.scope]);
|
|
84785
|
+
if (result.ok) {
|
|
84786
|
+
return { agent: AGENT2, status: "removed", detail: result.stdout.trim() || undefined };
|
|
84787
|
+
}
|
|
84788
|
+
return {
|
|
84789
|
+
agent: AGENT2,
|
|
84790
|
+
status: "failed",
|
|
84791
|
+
detail: result.stderr.trim() || `exit code ${result.code}`
|
|
84792
|
+
};
|
|
84793
|
+
}
|
|
84794
|
+
return fileRemove(name2, opts.scope);
|
|
84795
|
+
}
|
|
84796
|
+
var claudeWriter = {
|
|
84797
|
+
agent: AGENT2,
|
|
84798
|
+
add,
|
|
84799
|
+
remove: remove10,
|
|
84800
|
+
listConfigured: listUserConfigured
|
|
84801
|
+
};
|
|
84802
|
+
|
|
84803
|
+
// src/domains/mcp/writers/codex-writer.ts
|
|
84804
|
+
init_path_safety();
|
|
84805
|
+
import { existsSync as existsSync60 } from "node:fs";
|
|
84806
|
+
import { readFile as readFile45, writeFile as writeFile32 } from "node:fs/promises";
|
|
84807
|
+
|
|
84808
|
+
// src/domains/mcp/writers/codex-config-file.ts
|
|
84809
|
+
function escapeRegex2(value) {
|
|
84810
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
84811
|
+
}
|
|
84812
|
+
function sectionRegex(header) {
|
|
84813
|
+
const escaped = escapeRegex2(header);
|
|
84814
|
+
return new RegExp(`\\n?^\\[${escaped}\\]\\s*\\r?\\n(?:(?!\\[)[^\\r\\n]*\\r?\\n?)*`, "gm");
|
|
84815
|
+
}
|
|
84816
|
+
function tomlString(value) {
|
|
84817
|
+
return JSON.stringify(value).replace(/\u007f/g, "\\u007F");
|
|
84818
|
+
}
|
|
84819
|
+
function buildMcpServerSection(name2, entry) {
|
|
84820
|
+
if (!isValidServiceName(name2)) {
|
|
84821
|
+
throw new Error(`Refusing to build TOML section for invalid service name: ${name2}`);
|
|
84822
|
+
}
|
|
84823
|
+
if (entry.transport === "stdio") {
|
|
84824
|
+
const argsLine = entry.args?.length ? `args = [${entry.args.map(tomlString).join(", ")}]
|
|
84825
|
+
` : "";
|
|
84826
|
+
return `[mcp_servers.${name2}]
|
|
84827
|
+
command = ${tomlString(entry.command)}
|
|
84828
|
+
${argsLine}`;
|
|
84829
|
+
}
|
|
84830
|
+
return `[mcp_servers.${name2}]
|
|
84831
|
+
url = ${tomlString(entry.url)}
|
|
84832
|
+
`;
|
|
84833
|
+
}
|
|
84834
|
+
function upsertMcpServerSection(content, name2, section) {
|
|
84835
|
+
const regex2 = sectionRegex(`mcp_servers.${name2}`);
|
|
84836
|
+
if (regex2.test(content)) {
|
|
84837
|
+
return content.replace(regex2, () => `
|
|
84838
|
+
${section}`).trimStart();
|
|
84839
|
+
}
|
|
84840
|
+
const separator = content.trim().length > 0 ? `
|
|
84841
|
+
|
|
84842
|
+
` : "";
|
|
84843
|
+
return `${content.trimEnd()}${separator}${section}`;
|
|
84844
|
+
}
|
|
84845
|
+
function removeMcpServerSection(content, name2) {
|
|
84846
|
+
const regex2 = sectionRegex(`mcp_servers.${name2}`);
|
|
84847
|
+
if (!regex2.test(content)) {
|
|
84848
|
+
return { content, existed: false };
|
|
84849
|
+
}
|
|
84850
|
+
return { content: content.replace(regex2, ""), existed: true };
|
|
84851
|
+
}
|
|
84852
|
+
function hasMcpServerSection(content, name2) {
|
|
84853
|
+
return sectionRegex(`mcp_servers.${name2}`).test(content);
|
|
84854
|
+
}
|
|
84855
|
+
|
|
84856
|
+
// src/domains/mcp/writers/codex-writer.ts
|
|
84857
|
+
var AGENT3 = "codex";
|
|
84858
|
+
var BIN2 = "codex";
|
|
84859
|
+
var SCOPE_WARNING = "codex has no local/project scope; wrote to user-level ~/.codex/config.toml";
|
|
84860
|
+
function configPath() {
|
|
84861
|
+
return getAgentConfigPath(AGENT3);
|
|
84862
|
+
}
|
|
84863
|
+
function withScopeWarning(detail, scope) {
|
|
84864
|
+
if (scope === "user")
|
|
84865
|
+
return detail;
|
|
84866
|
+
return detail ? `${detail} (${SCOPE_WARNING})` : SCOPE_WARNING;
|
|
84867
|
+
}
|
|
84868
|
+
async function fileAdd2(entry, scope) {
|
|
84869
|
+
const path11 = configPath();
|
|
84870
|
+
if (!path11) {
|
|
84871
|
+
return { agent: AGENT3, status: "failed", detail: "Could not resolve home directory" };
|
|
84872
|
+
}
|
|
84873
|
+
try {
|
|
84874
|
+
return await withCodexTargetLock2(path11, async () => {
|
|
84875
|
+
let existing = "";
|
|
84876
|
+
try {
|
|
84877
|
+
existing = await readFile45(path11, "utf-8");
|
|
84878
|
+
} catch {
|
|
84879
|
+
existing = "";
|
|
84880
|
+
}
|
|
84881
|
+
const section = buildMcpServerSection(entry.name, entry);
|
|
84882
|
+
const updated = upsertMcpServerSection(existing, entry.name, section);
|
|
84883
|
+
await writeFile32(path11, updated, "utf-8");
|
|
84884
|
+
return {
|
|
84885
|
+
agent: AGENT3,
|
|
84886
|
+
status: "added",
|
|
84887
|
+
detail: withScopeWarning(`Wrote ${path11} (file fallback)`, scope)
|
|
84888
|
+
};
|
|
84889
|
+
});
|
|
84890
|
+
} catch (error) {
|
|
84891
|
+
return { agent: AGENT3, status: "failed", detail: toMessage(error) };
|
|
84892
|
+
}
|
|
84893
|
+
}
|
|
84894
|
+
async function fileRemove2(name2) {
|
|
84895
|
+
const path11 = configPath();
|
|
84896
|
+
if (!path11 || !existsSync60(path11)) {
|
|
84897
|
+
return { agent: AGENT3, status: "skipped", detail: "No config file present" };
|
|
84898
|
+
}
|
|
84899
|
+
try {
|
|
84900
|
+
return await withCodexTargetLock2(path11, async () => {
|
|
84901
|
+
const existing = await readFile45(path11, "utf-8");
|
|
84902
|
+
const { content, existed } = removeMcpServerSection(existing, name2);
|
|
84903
|
+
if (!existed) {
|
|
84904
|
+
return {
|
|
84905
|
+
agent: AGENT3,
|
|
84906
|
+
status: "skipped",
|
|
84907
|
+
detail: "Not configured"
|
|
84908
|
+
};
|
|
84909
|
+
}
|
|
84910
|
+
await writeFile32(path11, content, "utf-8");
|
|
84911
|
+
return {
|
|
84912
|
+
agent: AGENT3,
|
|
84913
|
+
status: "removed",
|
|
84914
|
+
detail: `Updated ${path11} (file fallback)`
|
|
84915
|
+
};
|
|
84916
|
+
});
|
|
84917
|
+
} catch (error) {
|
|
84918
|
+
return { agent: AGENT3, status: "failed", detail: toMessage(error) };
|
|
84919
|
+
}
|
|
84920
|
+
}
|
|
84921
|
+
async function add2(entry, opts) {
|
|
84922
|
+
if (!isValidServiceName(entry.name)) {
|
|
84923
|
+
return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${entry.name}` };
|
|
84924
|
+
}
|
|
84925
|
+
if (await isConfigured(entry.name)) {
|
|
84926
|
+
return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
|
|
84927
|
+
}
|
|
84928
|
+
if (isBinaryOnPath(BIN2)) {
|
|
84929
|
+
const argv = entry.transport === "stdio" ? ["mcp", "add", entry.name, "--", entry.command, ...entry.args ?? []] : ["mcp", "add", entry.name, "--url", entry.url];
|
|
84930
|
+
const result = runAgentCli(BIN2, argv, 300000);
|
|
84931
|
+
if (result.ok) {
|
|
84932
|
+
return {
|
|
84933
|
+
agent: AGENT3,
|
|
84934
|
+
status: "added",
|
|
84935
|
+
detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
|
|
84936
|
+
};
|
|
84937
|
+
}
|
|
84938
|
+
if (isAlreadyExistsError(result)) {
|
|
84939
|
+
return { agent: AGENT3, status: "skipped", detail: "already configured — no change" };
|
|
84940
|
+
}
|
|
84941
|
+
return {
|
|
84942
|
+
agent: AGENT3,
|
|
84943
|
+
status: "failed",
|
|
84944
|
+
detail: result.stderr.trim() || `exit code ${result.code}`
|
|
84945
|
+
};
|
|
84946
|
+
}
|
|
84947
|
+
return fileAdd2(entry, opts.scope);
|
|
84948
|
+
}
|
|
84949
|
+
async function remove11(name2, opts) {
|
|
84950
|
+
if (!isValidServiceName(name2)) {
|
|
84951
|
+
return { agent: AGENT3, status: "failed", detail: `Invalid service name: ${name2}` };
|
|
84952
|
+
}
|
|
84953
|
+
if (isBinaryOnPath(BIN2)) {
|
|
84954
|
+
if (!(await listConfigured()).has(name2)) {
|
|
84955
|
+
return { agent: AGENT3, status: "skipped", detail: "Not configured" };
|
|
84956
|
+
}
|
|
84957
|
+
const result = runAgentCli(BIN2, ["mcp", "remove", name2]);
|
|
84958
|
+
if (result.ok) {
|
|
84959
|
+
return {
|
|
84960
|
+
agent: AGENT3,
|
|
84961
|
+
status: "removed",
|
|
84962
|
+
detail: withScopeWarning(result.stdout.trim() || undefined, opts.scope)
|
|
84963
|
+
};
|
|
84964
|
+
}
|
|
84965
|
+
return {
|
|
84966
|
+
agent: AGENT3,
|
|
84967
|
+
status: "failed",
|
|
84968
|
+
detail: result.stderr.trim() || `exit code ${result.code}`
|
|
84969
|
+
};
|
|
84970
|
+
}
|
|
84971
|
+
return fileRemove2(name2);
|
|
84972
|
+
}
|
|
84973
|
+
async function listConfigured() {
|
|
84974
|
+
const path11 = configPath();
|
|
84975
|
+
if (!path11 || !existsSync60(path11))
|
|
84976
|
+
return { has: () => false };
|
|
84977
|
+
try {
|
|
84978
|
+
const content = await readFile45(path11, "utf-8");
|
|
84979
|
+
return { has: (name2) => hasMcpServerSection(content, name2) };
|
|
84980
|
+
} catch {
|
|
84981
|
+
return { has: () => false };
|
|
84982
|
+
}
|
|
84983
|
+
}
|
|
84984
|
+
async function isConfigured(name2) {
|
|
84985
|
+
return (await listConfigured()).has(name2);
|
|
84986
|
+
}
|
|
84987
|
+
var codexWriter = {
|
|
84988
|
+
agent: AGENT3,
|
|
84989
|
+
add: add2,
|
|
84990
|
+
remove: remove11,
|
|
84991
|
+
listConfigured
|
|
84992
|
+
};
|
|
84993
|
+
|
|
84994
|
+
// src/domains/mcp/writers/writer-registry.ts
|
|
84995
|
+
var writers = {
|
|
84996
|
+
"claude-code": claudeWriter,
|
|
84997
|
+
codex: codexWriter
|
|
84998
|
+
};
|
|
84999
|
+
function getWriter(agent) {
|
|
85000
|
+
return writers[agent];
|
|
85001
|
+
}
|
|
85002
|
+
|
|
85003
|
+
// src/domains/mcp/mcp-service-core.ts
|
|
85004
|
+
var defaultDeps2 = {
|
|
85005
|
+
getRegistry: getRegistryWithSource,
|
|
85006
|
+
detectAgents,
|
|
85007
|
+
detectProjectAgents,
|
|
85008
|
+
getWriter
|
|
85009
|
+
};
|
|
85010
|
+
function renderResults(results) {
|
|
85011
|
+
for (const result of results) {
|
|
85012
|
+
const line = `${result.agent}: ${result.status}${result.detail ? ` — ${result.detail}` : ""}`;
|
|
85013
|
+
switch (result.status) {
|
|
85014
|
+
case "added":
|
|
85015
|
+
case "removed":
|
|
85016
|
+
logger.success(line);
|
|
85017
|
+
break;
|
|
85018
|
+
case "skipped":
|
|
85019
|
+
logger.warning(line);
|
|
85020
|
+
break;
|
|
85021
|
+
case "failed":
|
|
85022
|
+
logger.error(line);
|
|
85023
|
+
break;
|
|
85024
|
+
default: {
|
|
85025
|
+
const _exhaustive = result.status;
|
|
85026
|
+
}
|
|
85027
|
+
}
|
|
85028
|
+
}
|
|
85029
|
+
}
|
|
85030
|
+
async function selectAgents2(verb, entryName, candidates, opts) {
|
|
85031
|
+
if (opts.yes || isNonInteractive() || candidates.length <= 1) {
|
|
85032
|
+
return candidates;
|
|
85033
|
+
}
|
|
85034
|
+
const preposition = verb === "add" ? "to" : "from";
|
|
85035
|
+
const selected = await ae({
|
|
85036
|
+
message: `Select agents to ${verb} "${entryName}" ${preposition}:`,
|
|
85037
|
+
options: candidates.map((agent) => ({ value: agent, label: agent })),
|
|
85038
|
+
initialValues: candidates,
|
|
85039
|
+
required: false
|
|
85040
|
+
});
|
|
85041
|
+
if (lD(selected))
|
|
85042
|
+
return null;
|
|
85043
|
+
return selected;
|
|
85044
|
+
}
|
|
85045
|
+
async function resolveAgentTargets(verb, service, agentFilter, deps) {
|
|
85046
|
+
const { registry, revalidated } = await deps.getRegistry();
|
|
85047
|
+
const entry = resolveService(registry, service);
|
|
85048
|
+
if (!entry) {
|
|
85049
|
+
logger.error(`Unknown MCP service: ${service}`);
|
|
85050
|
+
logger.info(`Known services: ${registry.services.map((s3) => s3.name).join(", ") || "(none)"}`);
|
|
85051
|
+
return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
|
|
85052
|
+
}
|
|
85053
|
+
if (agentFilter && agentFilter.length > 0) {
|
|
85054
|
+
return { ok: true, entry, targets: agentFilter, prompt: false, revalidated };
|
|
85055
|
+
}
|
|
85056
|
+
const installed = deps.detectAgents();
|
|
85057
|
+
const projectTargets = deps.detectProjectAgents().filter((agent) => installed.includes(agent));
|
|
85058
|
+
if (projectTargets.length > 0) {
|
|
85059
|
+
const action = verb === "add" ? "adding to" : "removing from";
|
|
85060
|
+
logger.info(`Detected ${projectTargets.join(" + ")} project → ${action} ${projectTargets.join(", ")}.`);
|
|
85061
|
+
return { ok: true, entry, targets: projectTargets, prompt: false, revalidated };
|
|
85062
|
+
}
|
|
85063
|
+
if (installed.length === 0) {
|
|
85064
|
+
logger.warning("No coding agents detected (claude-code / codex).");
|
|
85065
|
+
return { ok: false, result: { results: [], exitCode: 1 }, revalidated };
|
|
85066
|
+
}
|
|
85067
|
+
return { ok: true, entry, targets: installed, prompt: true, revalidated };
|
|
85068
|
+
}
|
|
85069
|
+
|
|
85070
|
+
// src/domains/mcp/mcp-service.ts
|
|
85071
|
+
async function runMutation(verb, opts, deps) {
|
|
85072
|
+
const resolved = await resolveAgentTargets(verb, opts.service, opts.agents, deps);
|
|
85073
|
+
try {
|
|
85074
|
+
if (!resolved.ok)
|
|
85075
|
+
return resolved.result;
|
|
85076
|
+
const { prompt } = resolved;
|
|
85077
|
+
let { entry } = resolved;
|
|
85078
|
+
let targets = resolved.targets;
|
|
85079
|
+
if (verb === "add" && opts.extraArgs && opts.extraArgs.length > 0) {
|
|
85080
|
+
if (entry.transport !== "stdio") {
|
|
85081
|
+
logger.error(`Extra args after "--" only apply to stdio MCP services; "${entry.name}" is ${entry.transport} (remote).`);
|
|
85082
|
+
return { results: [], exitCode: 1 };
|
|
85083
|
+
}
|
|
85084
|
+
entry = { ...entry, args: [...entry.args ?? [], ...opts.extraArgs] };
|
|
85085
|
+
}
|
|
85086
|
+
if (prompt) {
|
|
85087
|
+
const chosen = await selectAgents2(verb, entry.name, targets, { yes: opts.yes });
|
|
85088
|
+
if (chosen === null) {
|
|
85089
|
+
logger.info("Cancelled.");
|
|
85090
|
+
return { results: [], exitCode: 1 };
|
|
85091
|
+
}
|
|
85092
|
+
if (chosen.length === 0) {
|
|
85093
|
+
logger.warning(`No agents selected — nothing to ${verb}.`);
|
|
85094
|
+
return { results: [], exitCode: 1 };
|
|
85095
|
+
}
|
|
85096
|
+
targets = chosen;
|
|
85097
|
+
}
|
|
85098
|
+
const writerOpts = { scope: opts.scope };
|
|
85099
|
+
const results = [];
|
|
85100
|
+
for (const agent of targets) {
|
|
85101
|
+
const writer = deps.getWriter(agent);
|
|
85102
|
+
results.push(verb === "add" ? await writer.add(entry, writerOpts) : await writer.remove(entry.name, writerOpts));
|
|
85103
|
+
}
|
|
85104
|
+
renderResults(results);
|
|
85105
|
+
const allFailed = results.length > 0 && results.every((r2) => r2.status === "failed");
|
|
85106
|
+
return { results, exitCode: allFailed ? 1 : 0 };
|
|
85107
|
+
} finally {
|
|
85108
|
+
await resolved.revalidated;
|
|
85109
|
+
}
|
|
85110
|
+
}
|
|
85111
|
+
async function runAdd(opts, deps = defaultDeps2) {
|
|
85112
|
+
return runMutation("add", opts, deps);
|
|
85113
|
+
}
|
|
85114
|
+
async function runRemove(opts, deps = defaultDeps2) {
|
|
85115
|
+
return runMutation("remove", opts, deps);
|
|
85116
|
+
}
|
|
85117
|
+
async function runList(deps = defaultDeps2) {
|
|
85118
|
+
const { registry, source, revalidated } = await deps.getRegistry();
|
|
85119
|
+
const projectAgents = deps.detectProjectAgents();
|
|
85120
|
+
const detected = projectAgents.length > 0 ? projectAgents : deps.detectAgents();
|
|
85121
|
+
const lookups = await Promise.all(detected.map(async (agent) => ({
|
|
85122
|
+
agent,
|
|
85123
|
+
lookup: await deps.getWriter(agent).listConfigured()
|
|
85124
|
+
})));
|
|
85125
|
+
const entries = registry.services.map((entry) => ({
|
|
85126
|
+
entry,
|
|
85127
|
+
perAgent: lookups.map(({ agent, lookup }) => ({
|
|
85128
|
+
agent,
|
|
85129
|
+
configured: lookup.has(entry.name)
|
|
85130
|
+
}))
|
|
85131
|
+
}));
|
|
85132
|
+
return { entries, source, revalidated };
|
|
85133
|
+
}
|
|
85134
|
+
|
|
85135
|
+
// src/commands/mcp/mutation-command.ts
|
|
85136
|
+
init_logger();
|
|
85137
|
+
|
|
85138
|
+
// src/commands/mcp/agent-option.ts
|
|
85139
|
+
class InvalidAgentOptionError extends Error {
|
|
85140
|
+
invalidValues;
|
|
85141
|
+
constructor(invalidValues) {
|
|
85142
|
+
super(`Invalid --agent value(s): ${invalidValues.join(", ")}. Accepted agents: ${ALL_MCP_AGENTS.join(", ")}.`);
|
|
85143
|
+
this.invalidValues = invalidValues;
|
|
85144
|
+
this.name = "InvalidAgentOptionError";
|
|
85145
|
+
}
|
|
85146
|
+
}
|
|
85147
|
+
function normalizeAgentOption(agent) {
|
|
85148
|
+
if (agent === undefined)
|
|
85149
|
+
return;
|
|
85150
|
+
const values = Array.isArray(agent) ? agent : [agent];
|
|
85151
|
+
const invalid = values.filter((value) => !McpAgentSchema.safeParse(value).success);
|
|
85152
|
+
if (invalid.length > 0) {
|
|
85153
|
+
throw new InvalidAgentOptionError(invalid);
|
|
85154
|
+
}
|
|
85155
|
+
return values;
|
|
85156
|
+
}
|
|
85157
|
+
|
|
85158
|
+
// src/commands/mcp/scope-option.ts
|
|
85159
|
+
class InvalidScopeOptionError extends Error {
|
|
85160
|
+
invalidValue;
|
|
85161
|
+
constructor(invalidValue) {
|
|
85162
|
+
super(`Invalid --scope value: ${invalidValue}. Accepted scopes: ${ALL_MCP_SCOPES.join(", ")}.`);
|
|
85163
|
+
this.invalidValue = invalidValue;
|
|
85164
|
+
this.name = "InvalidScopeOptionError";
|
|
85165
|
+
}
|
|
85166
|
+
}
|
|
85167
|
+
function normalizeScopeOption(scope) {
|
|
85168
|
+
if (scope === undefined)
|
|
85169
|
+
return DEFAULT_MCP_SCOPE;
|
|
85170
|
+
const parsed = McpScopeSchema.safeParse(scope);
|
|
85171
|
+
if (!parsed.success) {
|
|
85172
|
+
throw new InvalidScopeOptionError(scope);
|
|
85173
|
+
}
|
|
85174
|
+
return parsed.data;
|
|
85175
|
+
}
|
|
85176
|
+
|
|
85177
|
+
// src/commands/mcp/mutation-command.ts
|
|
85178
|
+
async function mutationCommand(verb, service, options2 = {}) {
|
|
85179
|
+
if (!service) {
|
|
85180
|
+
logger.error(`Usage: tkm mcp ${verb} <service> [--agent <name>...] [-s, --scope local|user|project] [--yes]${verb === "add" ? " [-- <extra args...>]" : ""}`);
|
|
85181
|
+
process.exitCode = 1;
|
|
85182
|
+
return;
|
|
85183
|
+
}
|
|
85184
|
+
const extraArgs = options2["--"] ?? [];
|
|
85185
|
+
if (verb === "remove" && extraArgs.length > 0) {
|
|
85186
|
+
logger.warning(`Extra args after "--" only apply to add — ignored for remove.`);
|
|
85187
|
+
}
|
|
85188
|
+
let agents;
|
|
85189
|
+
try {
|
|
85190
|
+
agents = normalizeAgentOption(options2.agent);
|
|
85191
|
+
} catch (error) {
|
|
85192
|
+
if (error instanceof InvalidAgentOptionError) {
|
|
85193
|
+
logger.error(error.message);
|
|
85194
|
+
process.exitCode = 1;
|
|
85195
|
+
return;
|
|
85196
|
+
}
|
|
85197
|
+
throw error;
|
|
85198
|
+
}
|
|
85199
|
+
let scope;
|
|
85200
|
+
try {
|
|
85201
|
+
scope = normalizeScopeOption(options2.scope);
|
|
85202
|
+
} catch (error) {
|
|
85203
|
+
if (error instanceof InvalidScopeOptionError) {
|
|
85204
|
+
logger.error(error.message);
|
|
85205
|
+
process.exitCode = 1;
|
|
85206
|
+
return;
|
|
85207
|
+
}
|
|
85208
|
+
throw error;
|
|
85209
|
+
}
|
|
85210
|
+
const run2 = verb === "add" ? runAdd : runRemove;
|
|
85211
|
+
try {
|
|
85212
|
+
const { exitCode } = await run2({
|
|
85213
|
+
service,
|
|
85214
|
+
agents,
|
|
85215
|
+
scope,
|
|
85216
|
+
yes: options2.yes,
|
|
85217
|
+
extraArgs: verb === "add" ? extraArgs : undefined
|
|
85218
|
+
});
|
|
85219
|
+
process.exitCode = exitCode;
|
|
85220
|
+
} catch (error) {
|
|
85221
|
+
if (error instanceof RegistryUnavailableError) {
|
|
85222
|
+
logger.error(error.message);
|
|
85223
|
+
process.exitCode = 1;
|
|
85224
|
+
return;
|
|
85225
|
+
}
|
|
85226
|
+
throw error;
|
|
85227
|
+
}
|
|
85228
|
+
}
|
|
85229
|
+
|
|
85230
|
+
// src/commands/mcp/add-command.ts
|
|
85231
|
+
async function addCommand(service, options2 = {}) {
|
|
85232
|
+
await mutationCommand("add", service, options2);
|
|
85233
|
+
}
|
|
85234
|
+
// src/commands/mcp/list-command.ts
|
|
85235
|
+
init_logger();
|
|
85236
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
85237
|
+
function statusCell(configured) {
|
|
85238
|
+
return configured ? { value: "● configured", paint: import_picocolors25.default.green } : { value: "○ not set", paint: import_picocolors25.default.dim };
|
|
85239
|
+
}
|
|
85240
|
+
function renderTable(header, rows) {
|
|
85241
|
+
const lastCol = header.length - 1;
|
|
85242
|
+
const widths = header.map((h2, col) => Math.max(h2.length, ...rows.map((r2) => r2[col]?.value.length ?? 0)));
|
|
85243
|
+
const padPlain = (text, col) => col === lastCol ? text : text.padEnd(widths[col]);
|
|
85244
|
+
const headerLine = import_picocolors25.default.bold(header.map((h2, col) => padPlain(h2, col)).join(" ").trimEnd());
|
|
85245
|
+
const rowLine = (cells) => cells.map((cell, col) => {
|
|
85246
|
+
const padded = padPlain(cell.value, col);
|
|
85247
|
+
return cell.paint ? cell.paint(padded) : padded;
|
|
85248
|
+
}).join(" ").trimEnd();
|
|
85249
|
+
return [headerLine, ...rows.map(rowLine)].join(`
|
|
85250
|
+
`);
|
|
85251
|
+
}
|
|
85252
|
+
async function listCommand(options2 = {}) {
|
|
85253
|
+
let listed;
|
|
85254
|
+
try {
|
|
85255
|
+
listed = await runList();
|
|
85256
|
+
} catch (error) {
|
|
85257
|
+
if (error instanceof RegistryUnavailableError) {
|
|
85258
|
+
logger.error(error.message);
|
|
85259
|
+
process.exitCode = 1;
|
|
85260
|
+
return;
|
|
85261
|
+
}
|
|
85262
|
+
throw error;
|
|
85263
|
+
}
|
|
85264
|
+
const { entries, source, revalidated } = listed;
|
|
85265
|
+
if (options2.json) {
|
|
85266
|
+
const services = entries.map(({ entry, perAgent }) => ({
|
|
85267
|
+
name: entry.name,
|
|
85268
|
+
description: entry.description,
|
|
85269
|
+
transport: entry.transport,
|
|
85270
|
+
configured: Object.fromEntries(perAgent.map((p2) => [p2.agent, p2.configured]))
|
|
85271
|
+
}));
|
|
85272
|
+
process.stdout.write(`${JSON.stringify({ source, services })}
|
|
85273
|
+
`);
|
|
85274
|
+
await revalidated;
|
|
85275
|
+
return;
|
|
85276
|
+
}
|
|
85277
|
+
if (entries.length === 0) {
|
|
85278
|
+
logger.info("No MCP services in the registry.");
|
|
85279
|
+
return;
|
|
85280
|
+
}
|
|
85281
|
+
const agents = entries[0].perAgent.map((p2) => p2.agent);
|
|
85282
|
+
const header = ["NAME", "TRANSPORT", ...agents.map((a3) => a3.toUpperCase()), "DESCRIPTION"];
|
|
85283
|
+
const rows = entries.map(({ entry, perAgent }) => [
|
|
85284
|
+
{ value: entry.name },
|
|
85285
|
+
{ value: entry.transport },
|
|
85286
|
+
...perAgent.map((p2) => statusCell(p2.configured)),
|
|
85287
|
+
{ value: entry.description }
|
|
85288
|
+
]);
|
|
85289
|
+
logger.info(`MCP registry — ${entries.length} services (source: ${source})`);
|
|
85290
|
+
if (agents.length === 0) {
|
|
85291
|
+
logger.info("No coding agents detected (claude-code / codex).");
|
|
85292
|
+
}
|
|
85293
|
+
process.stdout.write(`${renderTable(header, rows)}
|
|
85294
|
+
`);
|
|
85295
|
+
await revalidated;
|
|
85296
|
+
}
|
|
85297
|
+
// src/commands/mcp/mcp-command.ts
|
|
85298
|
+
init_logger();
|
|
85299
|
+
|
|
85300
|
+
// src/commands/mcp/remove-command.ts
|
|
85301
|
+
async function removeCommand(service, options2 = {}) {
|
|
85302
|
+
await mutationCommand("remove", service, options2);
|
|
85303
|
+
}
|
|
85304
|
+
|
|
85305
|
+
// src/commands/mcp/mcp-command.ts
|
|
85306
|
+
function printUsage() {
|
|
85307
|
+
logger.error("Usage: tkm mcp <add|list|remove> [service] [options] — see `tkm mcp --help`");
|
|
85308
|
+
}
|
|
85309
|
+
async function mcpCommand(action, service, options2 = {}) {
|
|
85310
|
+
switch (action) {
|
|
85311
|
+
case "add":
|
|
85312
|
+
await addCommand(service, options2);
|
|
85313
|
+
break;
|
|
85314
|
+
case "list":
|
|
85315
|
+
await listCommand(options2);
|
|
85316
|
+
break;
|
|
85317
|
+
case "remove":
|
|
85318
|
+
await removeCommand(service, options2);
|
|
85319
|
+
break;
|
|
85320
|
+
case undefined:
|
|
85321
|
+
printUsage();
|
|
85322
|
+
process.exitCode = 1;
|
|
85323
|
+
break;
|
|
85324
|
+
default:
|
|
85325
|
+
logger.error(`Unknown mcp action: ${action}. Available: add, list, remove`);
|
|
85326
|
+
process.exitCode = 1;
|
|
85327
|
+
}
|
|
85328
|
+
}
|
|
84264
85329
|
// src/commands/plan/plan-command.ts
|
|
84265
85330
|
init_output_manager();
|
|
84266
|
-
import { existsSync as
|
|
84267
|
-
import { dirname as
|
|
85331
|
+
import { existsSync as existsSync65, statSync as statSync11 } from "node:fs";
|
|
85332
|
+
import { dirname as dirname42, join as join132, parse as parse4, resolve as resolve33 } from "node:path";
|
|
84268
85333
|
|
|
84269
85334
|
// src/commands/plan/plan-read-handlers.ts
|
|
84270
|
-
import { existsSync as
|
|
84271
|
-
import { basename as basename21, dirname as
|
|
85335
|
+
import { existsSync as existsSync64, statSync as statSync10 } from "node:fs";
|
|
85336
|
+
import { basename as basename21, dirname as dirname41, join as join131, relative as relative20, resolve as resolve31 } from "node:path";
|
|
84272
85337
|
|
|
84273
85338
|
// src/domains/plan-parser/index.ts
|
|
84274
|
-
import { dirname as
|
|
85339
|
+
import { dirname as dirname40 } from "node:path";
|
|
84275
85340
|
|
|
84276
85341
|
// src/domains/plan-parser/plan-table-parser.ts
|
|
84277
85342
|
var import_gray_matter5 = __toESM(require_gray_matter(), 1);
|
|
84278
85343
|
import { readFileSync as readFileSync23 } from "node:fs";
|
|
84279
|
-
import { dirname as
|
|
85344
|
+
import { dirname as dirname37, resolve as resolve30 } from "node:path";
|
|
84280
85345
|
function normalizeStatus(raw) {
|
|
84281
85346
|
const s3 = raw.toLowerCase().trim();
|
|
84282
85347
|
if (s3.includes("complete") || s3.includes("done") || s3.includes("✓") || s3.includes("✅")) {
|
|
@@ -84537,7 +85602,7 @@ function parseFormat4(content, planFilePath, options2) {
|
|
|
84537
85602
|
const hasCheck = /[✅✓]/.test(line);
|
|
84538
85603
|
current = { name: name2, status: hasCheck ? "completed" : "pending" };
|
|
84539
85604
|
} else if (fileMatch && current) {
|
|
84540
|
-
const planDir =
|
|
85605
|
+
const planDir = dirname37(planFilePath);
|
|
84541
85606
|
current.file = resolve30(planDir, fileMatch[1].trim());
|
|
84542
85607
|
} else if (statusMatch && current) {
|
|
84543
85608
|
current.status = normalizeStatus(statusMatch[2]);
|
|
@@ -84642,30 +85707,30 @@ function parsePhasesFromBody(body, dir, options2) {
|
|
|
84642
85707
|
}
|
|
84643
85708
|
function parsePlanFile(planFilePath, options2) {
|
|
84644
85709
|
const content = readFileSync23(planFilePath, "utf8");
|
|
84645
|
-
const dir =
|
|
85710
|
+
const dir = dirname37(planFilePath);
|
|
84646
85711
|
const { data: frontmatter, content: body } = import_gray_matter5.default(content);
|
|
84647
85712
|
const phases = parsePhasesFromBody(body, dir, options2);
|
|
84648
85713
|
return { frontmatter, phases };
|
|
84649
85714
|
}
|
|
84650
85715
|
// src/domains/plan-parser/plan-scanner.ts
|
|
84651
|
-
import { existsSync as
|
|
84652
|
-
import { join as
|
|
85716
|
+
import { existsSync as existsSync61, readdirSync as readdirSync10 } from "node:fs";
|
|
85717
|
+
import { join as join129 } from "node:path";
|
|
84653
85718
|
function scanPlanDir(dir) {
|
|
84654
|
-
if (!
|
|
85719
|
+
if (!existsSync61(dir))
|
|
84655
85720
|
return [];
|
|
84656
85721
|
try {
|
|
84657
|
-
return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
85722
|
+
return readdirSync10(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join129(dir, entry.name, "plan.md")).filter(existsSync61);
|
|
84658
85723
|
} catch {
|
|
84659
85724
|
return [];
|
|
84660
85725
|
}
|
|
84661
85726
|
}
|
|
84662
85727
|
// src/domains/plan-parser/plan-validator.ts
|
|
84663
85728
|
var import_gray_matter6 = __toESM(require_gray_matter(), 1);
|
|
84664
|
-
import { existsSync as
|
|
84665
|
-
import { basename as basename19, dirname as
|
|
85729
|
+
import { existsSync as existsSync62, readFileSync as readFileSync24 } from "node:fs";
|
|
85730
|
+
import { basename as basename19, dirname as dirname38 } from "node:path";
|
|
84666
85731
|
function validatePlanFile(filePath, strict = false) {
|
|
84667
85732
|
const content = readFileSync24(filePath, "utf8");
|
|
84668
|
-
const dir =
|
|
85733
|
+
const dir = dirname38(filePath);
|
|
84669
85734
|
const issues = [];
|
|
84670
85735
|
const lines = content.split(`
|
|
84671
85736
|
`);
|
|
@@ -84701,7 +85766,7 @@ function validatePlanFile(filePath, strict = false) {
|
|
|
84701
85766
|
});
|
|
84702
85767
|
}
|
|
84703
85768
|
for (const phase of phases) {
|
|
84704
|
-
if (phase.file && !
|
|
85769
|
+
if (phase.file && !existsSync62(phase.file)) {
|
|
84705
85770
|
const fileBasename = basename19(phase.file);
|
|
84706
85771
|
const refLine = lines.findIndex((l2) => l2.includes(fileBasename));
|
|
84707
85772
|
issues.push({
|
|
@@ -84722,8 +85787,8 @@ function validatePlanFile(filePath, strict = false) {
|
|
|
84722
85787
|
// src/domains/plan-parser/plan-writer.ts
|
|
84723
85788
|
var import_gray_matter7 = __toESM(require_gray_matter(), 1);
|
|
84724
85789
|
import { mkdirSync as mkdirSync9, readFileSync as readFileSync25, writeFileSync as writeFileSync12 } from "node:fs";
|
|
84725
|
-
import { existsSync as
|
|
84726
|
-
import { basename as basename20, dirname as
|
|
85790
|
+
import { existsSync as existsSync63 } from "node:fs";
|
|
85791
|
+
import { basename as basename20, dirname as dirname39, join as join130 } from "node:path";
|
|
84727
85792
|
function phaseNameToFilename(id, name2) {
|
|
84728
85793
|
const numMatch = /^(\d+)([a-z]*)$/i.exec(id);
|
|
84729
85794
|
const num4 = numMatch ? numMatch[1] : id;
|
|
@@ -84831,12 +85896,12 @@ function scaffoldPlan(options2) {
|
|
|
84831
85896
|
mkdirSync9(dir, { recursive: true });
|
|
84832
85897
|
const resolvedPhases = resolvePhaseIds(options2.phases);
|
|
84833
85898
|
const optionsWithResolved = { ...options2, phases: resolvedPhases };
|
|
84834
|
-
const planFile =
|
|
85899
|
+
const planFile = join130(dir, "plan.md");
|
|
84835
85900
|
writeFileSync12(planFile, generatePlanMd(optionsWithResolved), "utf8");
|
|
84836
85901
|
const phaseFiles = [];
|
|
84837
85902
|
for (const phase of resolvedPhases) {
|
|
84838
85903
|
const filename = phaseNameToFilename(phase.id, phase.name);
|
|
84839
|
-
const phaseFile =
|
|
85904
|
+
const phaseFile = join130(dir, filename);
|
|
84840
85905
|
writeFileSync12(phaseFile, generatePhaseTemplate(phase), "utf8");
|
|
84841
85906
|
phaseFiles.push(phaseFile);
|
|
84842
85907
|
}
|
|
@@ -84902,9 +85967,9 @@ function updatePhaseStatus(planFile, phaseId, newStatus) {
|
|
|
84902
85967
|
const updatedFrontmatter = { ...frontmatter, status: planStatus };
|
|
84903
85968
|
const updatedContent = import_gray_matter7.default.stringify(updatedBody, updatedFrontmatter);
|
|
84904
85969
|
writeFileSync12(planFile, updatedContent, "utf8");
|
|
84905
|
-
const planDir =
|
|
85970
|
+
const planDir = dirname39(planFile);
|
|
84906
85971
|
const phaseFilename = phaseNameFilenameFromTableRow(updatedBody, phaseId, planDir);
|
|
84907
|
-
if (phaseFilename &&
|
|
85972
|
+
if (phaseFilename && existsSync63(phaseFilename)) {
|
|
84908
85973
|
updatePhaseFileFrontmatter(phaseFilename, newStatus);
|
|
84909
85974
|
}
|
|
84910
85975
|
}
|
|
@@ -84916,7 +85981,7 @@ function phaseNameFilenameFromTableRow(body, phaseId, planDir) {
|
|
|
84916
85981
|
continue;
|
|
84917
85982
|
const linkMatch = /\[([^\]]+)\]\(\.\/([^)]+)\)/.exec(row);
|
|
84918
85983
|
if (linkMatch)
|
|
84919
|
-
return
|
|
85984
|
+
return join130(planDir, linkMatch[2]);
|
|
84920
85985
|
}
|
|
84921
85986
|
return null;
|
|
84922
85987
|
}
|
|
@@ -84934,7 +85999,7 @@ function addPhase(planFile, name2, afterId) {
|
|
|
84934
85999
|
throw new Error("Non-canonical plan.md — cannot add phase");
|
|
84935
86000
|
}
|
|
84936
86001
|
const { data: frontmatter, content: body } = import_gray_matter7.default(raw);
|
|
84937
|
-
const planDir =
|
|
86002
|
+
const planDir = dirname39(planFile);
|
|
84938
86003
|
const existingIds = [];
|
|
84939
86004
|
for (const match of body.matchAll(/^\|\s*(\d+[a-z]?)\s*\|/gim)) {
|
|
84940
86005
|
existingIds.push(match[1].toLowerCase());
|
|
@@ -84997,7 +86062,7 @@ function addPhase(planFile, name2, afterId) {
|
|
|
84997
86062
|
`);
|
|
84998
86063
|
}
|
|
84999
86064
|
writeFileSync12(planFile, import_gray_matter7.default.stringify(updatedBody, frontmatter), "utf8");
|
|
85000
|
-
const phaseFilePath =
|
|
86065
|
+
const phaseFilePath = join130(planDir, filename);
|
|
85001
86066
|
writeFileSync12(phaseFilePath, generatePhaseTemplate({ id: phaseId, name: name2 }), "utf8");
|
|
85002
86067
|
return { phaseId, phaseFile: phaseFilePath };
|
|
85003
86068
|
}
|
|
@@ -85009,7 +86074,7 @@ function buildPlanSummary(planFile) {
|
|
|
85009
86074
|
const inProgress = phases.filter((p2) => p2.status === "in-progress").length;
|
|
85010
86075
|
const pending = phases.filter((p2) => p2.status === "pending").length;
|
|
85011
86076
|
return {
|
|
85012
|
-
planDir:
|
|
86077
|
+
planDir: dirname40(planFile),
|
|
85013
86078
|
planFile,
|
|
85014
86079
|
title: typeof frontmatter.title === "string" ? frontmatter.title : undefined,
|
|
85015
86080
|
description: typeof frontmatter.description === "string" ? frontmatter.description : undefined,
|
|
@@ -85025,7 +86090,7 @@ function buildPlanSummary(planFile) {
|
|
|
85025
86090
|
// src/commands/plan/plan-read-handlers.ts
|
|
85026
86091
|
init_logger();
|
|
85027
86092
|
init_output_manager();
|
|
85028
|
-
var
|
|
86093
|
+
var import_picocolors26 = __toESM(require_picocolors(), 1);
|
|
85029
86094
|
async function handleParse(target, options2) {
|
|
85030
86095
|
const planFile = resolvePlanFile(target);
|
|
85031
86096
|
if (!planFile) {
|
|
@@ -85046,9 +86111,9 @@ async function handleParse(target, options2) {
|
|
|
85046
86111
|
console.log(JSON.stringify({ file: relative20(process.cwd(), planFile), frontmatter, phases }, null, 2));
|
|
85047
86112
|
return;
|
|
85048
86113
|
}
|
|
85049
|
-
const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(
|
|
86114
|
+
const title = typeof frontmatter.title === "string" ? frontmatter.title : basename21(dirname41(planFile));
|
|
85050
86115
|
console.log();
|
|
85051
|
-
console.log(
|
|
86116
|
+
console.log(import_picocolors26.default.bold(` Plan: ${title}`));
|
|
85052
86117
|
console.log(` File: ${planFile}`);
|
|
85053
86118
|
console.log(` Phases found: ${phases.length}`);
|
|
85054
86119
|
console.log();
|
|
@@ -85079,7 +86144,7 @@ async function handleValidate(target, options2) {
|
|
|
85079
86144
|
return;
|
|
85080
86145
|
}
|
|
85081
86146
|
console.log();
|
|
85082
|
-
console.log(
|
|
86147
|
+
console.log(import_picocolors26.default.bold(` Validating: ${planFile}`));
|
|
85083
86148
|
console.log();
|
|
85084
86149
|
if (result.issues.length === 0) {
|
|
85085
86150
|
console.log(` [OK] No issues found — ${result.phases.length} phases detected`);
|
|
@@ -85093,7 +86158,7 @@ async function handleValidate(target, options2) {
|
|
|
85093
86158
|
}
|
|
85094
86159
|
}
|
|
85095
86160
|
console.log();
|
|
85096
|
-
const validStr = result.valid ?
|
|
86161
|
+
const validStr = result.valid ? import_picocolors26.default.green("[OK] Valid") : import_picocolors26.default.red("[X] Invalid");
|
|
85097
86162
|
console.log(` ${validStr} — ${result.issues.filter((i) => i.severity === "error").length} errors, ${result.issues.filter((i) => i.severity === "warning").length} warnings`);
|
|
85098
86163
|
console.log();
|
|
85099
86164
|
if (!result.valid)
|
|
@@ -85101,7 +86166,7 @@ async function handleValidate(target, options2) {
|
|
|
85101
86166
|
}
|
|
85102
86167
|
async function handleStatus(target, options2) {
|
|
85103
86168
|
const t = target ? resolve31(target) : null;
|
|
85104
|
-
const plansDir = t &&
|
|
86169
|
+
const plansDir = t && existsSync64(t) && statSync10(t).isDirectory() && !existsSync64(join131(t, "plan.md")) ? t : null;
|
|
85105
86170
|
if (plansDir) {
|
|
85106
86171
|
const planFiles = scanPlanDir(plansDir);
|
|
85107
86172
|
if (planFiles.length === 0) {
|
|
@@ -85120,20 +86185,20 @@ async function handleStatus(target, options2) {
|
|
|
85120
86185
|
return;
|
|
85121
86186
|
}
|
|
85122
86187
|
console.log();
|
|
85123
|
-
console.log(
|
|
86188
|
+
console.log(import_picocolors26.default.bold(` Plans in: ${plansDir}`));
|
|
85124
86189
|
console.log();
|
|
85125
86190
|
for (const pf of planFiles) {
|
|
85126
86191
|
try {
|
|
85127
86192
|
const s3 = buildPlanSummary(pf);
|
|
85128
86193
|
const bar = progressBar(s3.completed, s3.totalPhases);
|
|
85129
|
-
const title2 = s3.title ?? basename21(
|
|
85130
|
-
console.log(` ${
|
|
86194
|
+
const title2 = s3.title ?? basename21(dirname41(pf));
|
|
86195
|
+
console.log(` ${import_picocolors26.default.bold(title2)}`);
|
|
85131
86196
|
console.log(` ${bar}`);
|
|
85132
86197
|
if (s3.inProgress > 0)
|
|
85133
86198
|
console.log(` [~] ${s3.inProgress} in progress`);
|
|
85134
86199
|
console.log();
|
|
85135
86200
|
} catch {
|
|
85136
|
-
console.log(` [X] Failed to read: ${basename21(
|
|
86201
|
+
console.log(` [X] Failed to read: ${basename21(dirname41(pf))}`);
|
|
85137
86202
|
console.log();
|
|
85138
86203
|
}
|
|
85139
86204
|
}
|
|
@@ -85157,9 +86222,9 @@ async function handleStatus(target, options2) {
|
|
|
85157
86222
|
console.log(JSON.stringify(summary, null, 2));
|
|
85158
86223
|
return;
|
|
85159
86224
|
}
|
|
85160
|
-
const title = summary.title ?? basename21(
|
|
86225
|
+
const title = summary.title ?? basename21(dirname41(planFile));
|
|
85161
86226
|
console.log();
|
|
85162
|
-
console.log(
|
|
86227
|
+
console.log(import_picocolors26.default.bold(` ${title}`));
|
|
85163
86228
|
if (summary.status)
|
|
85164
86229
|
console.log(` Status: ${summary.status}`);
|
|
85165
86230
|
console.log();
|
|
@@ -85185,7 +86250,7 @@ async function handleKanban(target, _options) {
|
|
|
85185
86250
|
// src/commands/plan/plan-write-handlers.ts
|
|
85186
86251
|
import { basename as basename22, relative as relative21, resolve as resolve32 } from "node:path";
|
|
85187
86252
|
init_output_manager();
|
|
85188
|
-
var
|
|
86253
|
+
var import_picocolors27 = __toESM(require_picocolors(), 1);
|
|
85189
86254
|
async function handleCreate(target, options2) {
|
|
85190
86255
|
if (!options2.title) {
|
|
85191
86256
|
output.error("[X] --title is required for create");
|
|
@@ -85232,7 +86297,7 @@ async function handleCreate(target, options2) {
|
|
|
85232
86297
|
return;
|
|
85233
86298
|
}
|
|
85234
86299
|
console.log();
|
|
85235
|
-
console.log(
|
|
86300
|
+
console.log(import_picocolors27.default.bold(` [OK] Plan created: ${options2.title}`));
|
|
85236
86301
|
console.log(` Directory: ${resolve32(dir)}`);
|
|
85237
86302
|
console.log(` Phases: ${result.phaseFiles.length}`);
|
|
85238
86303
|
for (const f4 of result.phaseFiles) {
|
|
@@ -85332,22 +86397,22 @@ async function handleAddPhase(target, options2) {
|
|
|
85332
86397
|
// src/commands/plan/plan-command.ts
|
|
85333
86398
|
function resolvePlanFile(target) {
|
|
85334
86399
|
const t = target ? resolve33(target) : process.cwd();
|
|
85335
|
-
if (
|
|
86400
|
+
if (existsSync65(t)) {
|
|
85336
86401
|
const stat14 = statSync11(t);
|
|
85337
86402
|
if (stat14.isFile())
|
|
85338
86403
|
return t;
|
|
85339
|
-
const candidate =
|
|
85340
|
-
if (
|
|
86404
|
+
const candidate = join132(t, "plan.md");
|
|
86405
|
+
if (existsSync65(candidate))
|
|
85341
86406
|
return candidate;
|
|
85342
86407
|
}
|
|
85343
86408
|
if (!target) {
|
|
85344
86409
|
let dir = process.cwd();
|
|
85345
86410
|
const root = parse4(dir).root;
|
|
85346
86411
|
while (dir !== root) {
|
|
85347
|
-
const candidate =
|
|
85348
|
-
if (
|
|
86412
|
+
const candidate = join132(dir, "plan.md");
|
|
86413
|
+
if (existsSync65(candidate))
|
|
85349
86414
|
return candidate;
|
|
85350
|
-
dir =
|
|
86415
|
+
dir = dirname42(dir);
|
|
85351
86416
|
}
|
|
85352
86417
|
}
|
|
85353
86418
|
return null;
|
|
@@ -85395,7 +86460,7 @@ async function planCommand(action, target, options2) {
|
|
|
85395
86460
|
let resolvedTarget = target;
|
|
85396
86461
|
if (resolvedAction && !knownActions.has(resolvedAction)) {
|
|
85397
86462
|
const looksLikePath = resolvedAction.includes("/") || resolvedAction.includes("\\") || resolvedAction.endsWith(".md") || resolvedAction === "." || resolvedAction === "..";
|
|
85398
|
-
const existsOnDisk = !looksLikePath &&
|
|
86463
|
+
const existsOnDisk = !looksLikePath && existsSync65(resolve33(resolvedAction));
|
|
85399
86464
|
if (looksLikePath || existsOnDisk) {
|
|
85400
86465
|
resolvedTarget = resolvedAction;
|
|
85401
86466
|
resolvedAction = undefined;
|
|
@@ -85439,22 +86504,22 @@ init_logger();
|
|
|
85439
86504
|
init_logger();
|
|
85440
86505
|
|
|
85441
86506
|
// src/commands/telemetry/shared.ts
|
|
85442
|
-
import { existsSync as
|
|
86507
|
+
import { existsSync as existsSync66, readFileSync as readFileSync26, readdirSync as readdirSync11 } from "node:fs";
|
|
85443
86508
|
import { homedir as homedir29 } from "node:os";
|
|
85444
|
-
import { join as
|
|
86509
|
+
import { join as join133 } from "node:path";
|
|
85445
86510
|
init_token_store();
|
|
85446
86511
|
init_manifest_path_resolver();
|
|
85447
86512
|
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 =
|
|
86513
|
+
var USER_CACHE_PATH = join133(homedir29(), ".claude", "sk-user.json");
|
|
86514
|
+
var EVENT_BUFFER_DIR = join133(homedir29(), ".claude", "sk-events");
|
|
86515
|
+
var RATE_STATE_PATH = join133(homedir29(), ".claude", "sk-rate-state.json");
|
|
86516
|
+
var TAKUMI_MANIFEST_PATH = join133(homedir29(), ".claude", MANIFEST_FILENAME);
|
|
86517
|
+
var LEGACY_METADATA_PATH = join133(homedir29(), ".claude", LEGACY_MANIFEST_FILENAME);
|
|
85453
86518
|
var TELEMETRY_HOOK_FIELD = "hooks.telemetry";
|
|
85454
86519
|
var TOKEN_PLACEHOLDER = "__INJECT_AT_RELEASE__";
|
|
85455
86520
|
function readUserCache() {
|
|
85456
86521
|
try {
|
|
85457
|
-
if (!
|
|
86522
|
+
if (!existsSync66(USER_CACHE_PATH))
|
|
85458
86523
|
return null;
|
|
85459
86524
|
const parsed = JSON.parse(readFileSync26(USER_CACHE_PATH, "utf8"));
|
|
85460
86525
|
if (!parsed || typeof parsed !== "object")
|
|
@@ -85466,7 +86531,7 @@ function readUserCache() {
|
|
|
85466
86531
|
}
|
|
85467
86532
|
function countBufferFiles() {
|
|
85468
86533
|
try {
|
|
85469
|
-
if (!
|
|
86534
|
+
if (!existsSync66(EVENT_BUFFER_DIR))
|
|
85470
86535
|
return 0;
|
|
85471
86536
|
return readdirSync11(EVENT_BUFFER_DIR).filter((f4) => f4.endsWith(".jsonl")).length;
|
|
85472
86537
|
} catch {
|
|
@@ -85478,7 +86543,7 @@ function readTelemetryConfig() {
|
|
|
85478
86543
|
const envToken = process.env.TAKUMI_TELEMETRY_TOKEN;
|
|
85479
86544
|
let metadata = null;
|
|
85480
86545
|
try {
|
|
85481
|
-
const resolved = findManifestPathSync(
|
|
86546
|
+
const resolved = findManifestPathSync(join133(homedir29(), ".claude"));
|
|
85482
86547
|
if (resolved) {
|
|
85483
86548
|
metadata = JSON.parse(readFileSync26(resolved.path, "utf8"));
|
|
85484
86549
|
}
|
|
@@ -85504,8 +86569,8 @@ function collectRuntimeContext() {
|
|
|
85504
86569
|
cacheSource: cache3?.source === "gh" || cache3?.source === "manual" ? cache3.source : null,
|
|
85505
86570
|
bufferFileCount: countBufferFiles(),
|
|
85506
86571
|
bufferDir: EVENT_BUFFER_DIR,
|
|
85507
|
-
rateStateExists:
|
|
85508
|
-
userCacheExists:
|
|
86572
|
+
rateStateExists: existsSync66(RATE_STATE_PATH),
|
|
86573
|
+
userCacheExists: existsSync66(USER_CACHE_PATH),
|
|
85509
86574
|
endpoint,
|
|
85510
86575
|
tokenConfigured: Boolean(token)
|
|
85511
86576
|
};
|
|
@@ -85606,7 +86671,7 @@ init_manifest_writer();
|
|
|
85606
86671
|
init_logger();
|
|
85607
86672
|
init_safe_prompts();
|
|
85608
86673
|
init_types2();
|
|
85609
|
-
var
|
|
86674
|
+
var import_picocolors29 = __toESM(require_picocolors(), 1);
|
|
85610
86675
|
|
|
85611
86676
|
// src/commands/uninstall/installation-detector.ts
|
|
85612
86677
|
init_paths();
|
|
@@ -85656,7 +86721,7 @@ init_safe_prompts();
|
|
|
85656
86721
|
init_safe_spinner();
|
|
85657
86722
|
var import_fs_extra37 = __toESM(require_lib(), 1);
|
|
85658
86723
|
import { readdirSync as readdirSync13, rmSync as rmSync9 } from "node:fs";
|
|
85659
|
-
import { join as
|
|
86724
|
+
import { join as join135, resolve as resolve34, sep as sep9 } from "node:path";
|
|
85660
86725
|
|
|
85661
86726
|
// src/commands/uninstall/analysis-handler.ts
|
|
85662
86727
|
init_metadata_migration();
|
|
@@ -85665,14 +86730,14 @@ init_ownership_checker();
|
|
|
85665
86730
|
init_logger();
|
|
85666
86731
|
init_safe_prompts();
|
|
85667
86732
|
init_takumi_constants();
|
|
85668
|
-
var
|
|
85669
|
-
import { existsSync as
|
|
85670
|
-
import { dirname as
|
|
86733
|
+
var import_picocolors28 = __toESM(require_picocolors(), 1);
|
|
86734
|
+
import { existsSync as existsSync67, readdirSync as readdirSync12, rmSync as rmSync8 } from "node:fs";
|
|
86735
|
+
import { dirname as dirname43, join as join134 } from "node:path";
|
|
85671
86736
|
function listPresentManifestNames(installPath) {
|
|
85672
86737
|
const present = [];
|
|
85673
|
-
if (
|
|
86738
|
+
if (existsSync67(getManifestPath(installPath)))
|
|
85674
86739
|
present.push(MANIFEST_FILENAME);
|
|
85675
|
-
if (
|
|
86740
|
+
if (existsSync67(getLegacyManifestPath(installPath)))
|
|
85676
86741
|
present.push(LEGACY_MANIFEST_FILENAME);
|
|
85677
86742
|
return present;
|
|
85678
86743
|
}
|
|
@@ -85690,7 +86755,7 @@ function classifyFileByOwnership(ownership, forceOverwrite, deleteReason) {
|
|
|
85690
86755
|
}
|
|
85691
86756
|
async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
85692
86757
|
let cleaned = 0;
|
|
85693
|
-
let currentDir =
|
|
86758
|
+
let currentDir = dirname43(filePath);
|
|
85694
86759
|
while (currentDir !== installationRoot && currentDir.startsWith(installationRoot)) {
|
|
85695
86760
|
try {
|
|
85696
86761
|
const entries = readdirSync12(currentDir);
|
|
@@ -85698,7 +86763,7 @@ async function cleanupEmptyDirectories3(filePath, installationRoot) {
|
|
|
85698
86763
|
rmSync8(currentDir, { recursive: true });
|
|
85699
86764
|
cleaned++;
|
|
85700
86765
|
logger.debug(`Removed empty directory: ${currentDir}`);
|
|
85701
|
-
currentDir =
|
|
86766
|
+
currentDir = dirname43(currentDir);
|
|
85702
86767
|
} else {
|
|
85703
86768
|
break;
|
|
85704
86769
|
}
|
|
@@ -85720,7 +86785,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
85720
86785
|
if (uninstallManifest.isMultiKit && kit && metadata?.kits?.[kit]) {
|
|
85721
86786
|
const kitFiles = metadata.kits[kit].files || [];
|
|
85722
86787
|
for (const trackedFile of kitFiles) {
|
|
85723
|
-
const filePath =
|
|
86788
|
+
const filePath = join134(installation.path, trackedFile.path);
|
|
85724
86789
|
if (uninstallManifest.filesToPreserve.includes(trackedFile.path)) {
|
|
85725
86790
|
result.toPreserve.push({ path: trackedFile.path, reason: "shared with other kit" });
|
|
85726
86791
|
continue;
|
|
@@ -85752,7 +86817,7 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
85752
86817
|
return result;
|
|
85753
86818
|
}
|
|
85754
86819
|
for (const trackedFile of allTrackedFiles) {
|
|
85755
|
-
const filePath =
|
|
86820
|
+
const filePath = join134(installation.path, trackedFile.path);
|
|
85756
86821
|
const ownershipResult = await OwnershipChecker.checkOwnership(filePath, metadata, installation.path);
|
|
85757
86822
|
if (!ownershipResult.exists)
|
|
85758
86823
|
continue;
|
|
@@ -85770,27 +86835,27 @@ async function analyzeInstallation(installation, forceOverwrite, kit) {
|
|
|
85770
86835
|
}
|
|
85771
86836
|
function displayDryRunPreview(analysis, installationType) {
|
|
85772
86837
|
console.log("");
|
|
85773
|
-
log.info(
|
|
86838
|
+
log.info(import_picocolors28.default.bold(`DRY RUN - Preview for ${installationType} installation:`));
|
|
85774
86839
|
console.log("");
|
|
85775
86840
|
if (analysis.toDelete.length > 0) {
|
|
85776
|
-
console.log(
|
|
86841
|
+
console.log(import_picocolors28.default.red(import_picocolors28.default.bold(`Files to DELETE (${analysis.toDelete.length}):`)));
|
|
85777
86842
|
const showDelete = analysis.toDelete.slice(0, 10);
|
|
85778
86843
|
for (const item of showDelete) {
|
|
85779
|
-
console.log(` ${
|
|
86844
|
+
console.log(` ${import_picocolors28.default.red("✖")} ${item.path}`);
|
|
85780
86845
|
}
|
|
85781
86846
|
if (analysis.toDelete.length > 10) {
|
|
85782
|
-
console.log(
|
|
86847
|
+
console.log(import_picocolors28.default.gray(` ... and ${analysis.toDelete.length - 10} more`));
|
|
85783
86848
|
}
|
|
85784
86849
|
console.log("");
|
|
85785
86850
|
}
|
|
85786
86851
|
if (analysis.toPreserve.length > 0) {
|
|
85787
|
-
console.log(
|
|
86852
|
+
console.log(import_picocolors28.default.green(import_picocolors28.default.bold(`Files to PRESERVE (${analysis.toPreserve.length}):`)));
|
|
85788
86853
|
const showPreserve = analysis.toPreserve.slice(0, 10);
|
|
85789
86854
|
for (const item of showPreserve) {
|
|
85790
|
-
console.log(` ${
|
|
86855
|
+
console.log(` ${import_picocolors28.default.green("✓")} ${item.path} ${import_picocolors28.default.gray(`(${item.reason})`)}`);
|
|
85791
86856
|
}
|
|
85792
86857
|
if (analysis.toPreserve.length > 10) {
|
|
85793
|
-
console.log(
|
|
86858
|
+
console.log(import_picocolors28.default.gray(` ... and ${analysis.toPreserve.length - 10} more`));
|
|
85794
86859
|
}
|
|
85795
86860
|
console.log("");
|
|
85796
86861
|
}
|
|
@@ -85851,7 +86916,7 @@ async function removeInstallations(installations, options2) {
|
|
|
85851
86916
|
let removedCount = 0;
|
|
85852
86917
|
let cleanedDirs = 0;
|
|
85853
86918
|
for (const item of analysis.toDelete) {
|
|
85854
|
-
const filePath =
|
|
86919
|
+
const filePath = join135(installation.path, item.path);
|
|
85855
86920
|
if (!await import_fs_extra37.pathExists(filePath))
|
|
85856
86921
|
continue;
|
|
85857
86922
|
if (!await isPathSafeToRemove(filePath, installation.path)) {
|
|
@@ -85912,15 +86977,15 @@ function displayInstallations(installations, scope) {
|
|
|
85912
86977
|
const hasLegacy = installations.some((i) => !i.hasMetadata);
|
|
85913
86978
|
const lines = installations.map((i) => {
|
|
85914
86979
|
const typeLabel = i.type === "local" ? "Local " : "Global";
|
|
85915
|
-
const legacyTag = !i.hasMetadata ?
|
|
86980
|
+
const legacyTag = !i.hasMetadata ? import_picocolors29.default.yellow(" [legacy]") : "";
|
|
85916
86981
|
const components = formatComponentSummary(i);
|
|
85917
86982
|
return ` ${typeLabel}: ${i.path}${legacyTag}${components}`;
|
|
85918
86983
|
});
|
|
85919
86984
|
prompts.note(lines.join(`
|
|
85920
86985
|
`), `Detected Takumi installations (${scopeLabel})`);
|
|
85921
86986
|
if (hasLegacy) {
|
|
85922
|
-
log.warn(
|
|
85923
|
-
`) +
|
|
86987
|
+
log.warn(import_picocolors29.default.yellow(`[!] Legacy installation(s) detected without metadata.json.
|
|
86988
|
+
`) + import_picocolors29.default.yellow(" These files cannot be selectively removed. Full directory cleanup will be performed."));
|
|
85924
86989
|
}
|
|
85925
86990
|
log.warn("[!] This will permanently delete Takumi files from the above paths.");
|
|
85926
86991
|
}
|
|
@@ -85980,7 +87045,7 @@ async function uninstallCommand(options2) {
|
|
|
85980
87045
|
}
|
|
85981
87046
|
const isAtHome = isLocalSameAsGlobal();
|
|
85982
87047
|
if (validOptions.local && !validOptions.global && isAtHome) {
|
|
85983
|
-
log.warn(
|
|
87048
|
+
log.warn(import_picocolors29.default.yellow("Cannot use --local at HOME directory (local path equals global path)."));
|
|
85984
87049
|
log.info("Use -g/--global or run from a project directory.");
|
|
85985
87050
|
return;
|
|
85986
87051
|
}
|
|
@@ -85992,7 +87057,7 @@ async function uninstallCommand(options2) {
|
|
|
85992
87057
|
} else if (validOptions.global) {
|
|
85993
87058
|
scope = "global";
|
|
85994
87059
|
} else if (isAtHome) {
|
|
85995
|
-
log.info(
|
|
87060
|
+
log.info(import_picocolors29.default.cyan("Running at HOME directory - targeting global installation"));
|
|
85996
87061
|
scope = "global";
|
|
85997
87062
|
} else {
|
|
85998
87063
|
const promptedScope = await promptScope(allInstallations);
|
|
@@ -86014,10 +87079,10 @@ async function uninstallCommand(options2) {
|
|
|
86014
87079
|
}
|
|
86015
87080
|
displayInstallations(installations, scope);
|
|
86016
87081
|
if (validOptions.kit) {
|
|
86017
|
-
log.info(
|
|
87082
|
+
log.info(import_picocolors29.default.cyan(`Kit-scoped uninstall: ${validOptions.kit} kit only`));
|
|
86018
87083
|
}
|
|
86019
87084
|
if (validOptions.dryRun) {
|
|
86020
|
-
log.info(
|
|
87085
|
+
log.info(import_picocolors29.default.yellow("DRY RUN MODE - No files will be deleted"));
|
|
86021
87086
|
await removeInstallations(installations, {
|
|
86022
87087
|
dryRun: true,
|
|
86023
87088
|
forceOverwrite: validOptions.forceOverwrite,
|
|
@@ -86027,8 +87092,8 @@ async function uninstallCommand(options2) {
|
|
|
86027
87092
|
return;
|
|
86028
87093
|
}
|
|
86029
87094
|
if (validOptions.forceOverwrite) {
|
|
86030
|
-
log.warn(`${
|
|
86031
|
-
${
|
|
87095
|
+
log.warn(`${import_picocolors29.default.yellow(import_picocolors29.default.bold("FORCE MODE ENABLED"))}
|
|
87096
|
+
${import_picocolors29.default.yellow("User modifications will be permanently deleted!")}`);
|
|
86032
87097
|
}
|
|
86033
87098
|
if (!validOptions.yes) {
|
|
86034
87099
|
const kitLabel = validOptions.kit ? ` (${validOptions.kit} kit only)` : "";
|
|
@@ -86481,7 +87546,7 @@ init_auth_client();
|
|
|
86481
87546
|
init_github_client();
|
|
86482
87547
|
init_logger();
|
|
86483
87548
|
init_types2();
|
|
86484
|
-
var
|
|
87549
|
+
var import_picocolors30 = __toESM(require_picocolors(), 1);
|
|
86485
87550
|
function formatRelativeTime(dateString) {
|
|
86486
87551
|
if (!dateString)
|
|
86487
87552
|
return "Unknown";
|
|
@@ -86503,21 +87568,21 @@ function formatRelativeTime(dateString) {
|
|
|
86503
87568
|
}
|
|
86504
87569
|
function displayKitReleases(kitName, releases) {
|
|
86505
87570
|
console.log(`
|
|
86506
|
-
${
|
|
87571
|
+
${import_picocolors30.default.bold(import_picocolors30.default.cyan(kitName))} - Available Versions:
|
|
86507
87572
|
`);
|
|
86508
87573
|
if (releases.length === 0) {
|
|
86509
|
-
console.log(
|
|
87574
|
+
console.log(import_picocolors30.default.dim(" No releases found"));
|
|
86510
87575
|
return;
|
|
86511
87576
|
}
|
|
86512
87577
|
for (const release of releases) {
|
|
86513
|
-
const version3 =
|
|
87578
|
+
const version3 = import_picocolors30.default.green(release.tag);
|
|
86514
87579
|
const publishedAt = formatRelativeTime(release.publishedAt);
|
|
86515
|
-
const badge = release.prerelease ? ` ${
|
|
87580
|
+
const badge = release.prerelease ? ` ${import_picocolors30.default.yellow("[prerelease]")}` : "";
|
|
86516
87581
|
const versionPart = version3.padEnd(20);
|
|
86517
|
-
const timePart =
|
|
87582
|
+
const timePart = import_picocolors30.default.dim(publishedAt.padEnd(20));
|
|
86518
87583
|
console.log(` ${versionPart} ${timePart}${badge}`);
|
|
86519
87584
|
}
|
|
86520
|
-
console.log(
|
|
87585
|
+
console.log(import_picocolors30.default.dim(`
|
|
86521
87586
|
Showing ${releases.length} ${releases.length === 1 ? "release" : "releases"}`));
|
|
86522
87587
|
}
|
|
86523
87588
|
async function fetchReleasesForKit(kitType, options2) {
|
|
@@ -86562,8 +87627,8 @@ async function versionCommand(options2) {
|
|
|
86562
87627
|
for (const result of results) {
|
|
86563
87628
|
if (result.error) {
|
|
86564
87629
|
console.log(`
|
|
86565
|
-
${
|
|
86566
|
-
console.log(
|
|
87630
|
+
${import_picocolors30.default.bold(import_picocolors30.default.cyan(result.kitConfig.name))} - ${import_picocolors30.default.red("Error")}`);
|
|
87631
|
+
console.log(import_picocolors30.default.dim(` ${result.error}`));
|
|
86567
87632
|
} else {
|
|
86568
87633
|
displayKitReleases(result.kitConfig.name, result.releases);
|
|
86569
87634
|
}
|
|
@@ -86709,6 +87774,9 @@ function registerCommands(cli) {
|
|
|
86709
87774
|
process.exitCode = 1;
|
|
86710
87775
|
}
|
|
86711
87776
|
});
|
|
87777
|
+
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 = {}) => {
|
|
87778
|
+
await mcpCommand(action, service, options2);
|
|
87779
|
+
});
|
|
86712
87780
|
}
|
|
86713
87781
|
|
|
86714
87782
|
// src/cli/version-display.ts
|
|
@@ -86720,7 +87788,7 @@ init_manifest_path_resolver();
|
|
|
86720
87788
|
init_logger();
|
|
86721
87789
|
init_types2();
|
|
86722
87790
|
import { readFileSync as readFileSync27 } from "node:fs";
|
|
86723
|
-
import { join as
|
|
87791
|
+
import { join as join136 } from "node:path";
|
|
86724
87792
|
var PROVIDER_LOCAL_SUBDIRS = {
|
|
86725
87793
|
"claude-code": ".claude",
|
|
86726
87794
|
codex: ".codex"
|
|
@@ -86775,7 +87843,7 @@ async function displayVersion() {
|
|
|
86775
87843
|
const localSubdir = PROVIDER_LOCAL_SUBDIRS[provider];
|
|
86776
87844
|
if (!localSubdir)
|
|
86777
87845
|
continue;
|
|
86778
|
-
const localRoot =
|
|
87846
|
+
const localRoot = join136(process.cwd(), localSubdir);
|
|
86779
87847
|
if (localRoot === inst.globalRoot())
|
|
86780
87848
|
continue;
|
|
86781
87849
|
const resolved = findManifestPathSync(localRoot);
|
|
@@ -86856,7 +87924,7 @@ function getPackageVersion3() {
|
|
|
86856
87924
|
|
|
86857
87925
|
// src/shared/logger.ts
|
|
86858
87926
|
init_output_manager();
|
|
86859
|
-
var
|
|
87927
|
+
var import_picocolors31 = __toESM(require_picocolors(), 1);
|
|
86860
87928
|
import { createWriteStream as createWriteStream4 } from "node:fs";
|
|
86861
87929
|
|
|
86862
87930
|
class Logger2 {
|
|
@@ -86865,23 +87933,23 @@ class Logger2 {
|
|
|
86865
87933
|
exitHandlerRegistered = false;
|
|
86866
87934
|
info(message) {
|
|
86867
87935
|
const symbols = output.getSymbols();
|
|
86868
|
-
console.log(
|
|
87936
|
+
console.log(import_picocolors31.default.blue(symbols.info), message);
|
|
86869
87937
|
}
|
|
86870
87938
|
success(message) {
|
|
86871
87939
|
const symbols = output.getSymbols();
|
|
86872
|
-
console.log(
|
|
87940
|
+
console.log(import_picocolors31.default.green(symbols.success), message);
|
|
86873
87941
|
}
|
|
86874
87942
|
warning(message) {
|
|
86875
87943
|
const symbols = output.getSymbols();
|
|
86876
|
-
console.log(
|
|
87944
|
+
console.log(import_picocolors31.default.yellow(symbols.warning), message);
|
|
86877
87945
|
}
|
|
86878
87946
|
error(message) {
|
|
86879
87947
|
const symbols = output.getSymbols();
|
|
86880
|
-
console.error(
|
|
87948
|
+
console.error(import_picocolors31.default.red(symbols.error), message);
|
|
86881
87949
|
}
|
|
86882
87950
|
debug(message) {
|
|
86883
87951
|
if (process.env.DEBUG) {
|
|
86884
|
-
console.log(
|
|
87952
|
+
console.log(import_picocolors31.default.gray("[DEBUG]"), message);
|
|
86885
87953
|
}
|
|
86886
87954
|
}
|
|
86887
87955
|
verbose(message, context) {
|
|
@@ -86890,7 +87958,7 @@ class Logger2 {
|
|
|
86890
87958
|
const timestamp = this.getTimestamp();
|
|
86891
87959
|
const sanitizedMessage = this.sanitize(message);
|
|
86892
87960
|
const formattedContext = context ? this.formatContext(context) : "";
|
|
86893
|
-
const logLine = `${timestamp} ${
|
|
87961
|
+
const logLine = `${timestamp} ${import_picocolors31.default.gray("[VERBOSE]")} ${sanitizedMessage}${formattedContext}`;
|
|
86894
87962
|
console.error(logLine);
|
|
86895
87963
|
if (this.logFileStream) {
|
|
86896
87964
|
const plainLogLine = `${timestamp} [VERBOSE] ${sanitizedMessage}${formattedContext}`;
|
|
@@ -86993,7 +88061,7 @@ var logger3 = new Logger2;
|
|
|
86993
88061
|
|
|
86994
88062
|
// src/shared/output-manager.ts
|
|
86995
88063
|
init_terminal_utils();
|
|
86996
|
-
var
|
|
88064
|
+
var import_picocolors32 = __toESM(require_picocolors(), 1);
|
|
86997
88065
|
var SYMBOLS2 = {
|
|
86998
88066
|
unicode: {
|
|
86999
88067
|
prompt: "◇",
|
|
@@ -87074,7 +88142,7 @@ class OutputManager2 {
|
|
|
87074
88142
|
if (this.config.quiet)
|
|
87075
88143
|
return;
|
|
87076
88144
|
const symbol = this.getSymbols().success;
|
|
87077
|
-
console.log(
|
|
88145
|
+
console.log(import_picocolors32.default.green(`${symbol} ${message}`));
|
|
87078
88146
|
}
|
|
87079
88147
|
error(message, data) {
|
|
87080
88148
|
if (this.config.json) {
|
|
@@ -87082,7 +88150,7 @@ class OutputManager2 {
|
|
|
87082
88150
|
return;
|
|
87083
88151
|
}
|
|
87084
88152
|
const symbol = this.getSymbols().error;
|
|
87085
|
-
console.error(
|
|
88153
|
+
console.error(import_picocolors32.default.red(`${symbol} ${message}`));
|
|
87086
88154
|
}
|
|
87087
88155
|
warning(message, data) {
|
|
87088
88156
|
if (this.config.json) {
|
|
@@ -87092,7 +88160,7 @@ class OutputManager2 {
|
|
|
87092
88160
|
if (this.config.quiet)
|
|
87093
88161
|
return;
|
|
87094
88162
|
const symbol = this.getSymbols().warning;
|
|
87095
|
-
console.log(
|
|
88163
|
+
console.log(import_picocolors32.default.yellow(`${symbol} ${message}`));
|
|
87096
88164
|
}
|
|
87097
88165
|
info(message, data) {
|
|
87098
88166
|
if (this.config.json) {
|
|
@@ -87102,7 +88170,7 @@ class OutputManager2 {
|
|
|
87102
88170
|
if (this.config.quiet)
|
|
87103
88171
|
return;
|
|
87104
88172
|
const symbol = this.getSymbols().info;
|
|
87105
|
-
console.log(
|
|
88173
|
+
console.log(import_picocolors32.default.blue(`${symbol} ${message}`));
|
|
87106
88174
|
}
|
|
87107
88175
|
verbose(message, data) {
|
|
87108
88176
|
if (!this.config.verbose)
|
|
@@ -87111,7 +88179,7 @@ class OutputManager2 {
|
|
|
87111
88179
|
this.addJsonEntry({ type: "info", message, data });
|
|
87112
88180
|
return;
|
|
87113
88181
|
}
|
|
87114
|
-
console.log(
|
|
88182
|
+
console.log(import_picocolors32.default.dim(` ${message}`));
|
|
87115
88183
|
}
|
|
87116
88184
|
indent(message) {
|
|
87117
88185
|
if (this.config.json)
|
|
@@ -87136,7 +88204,7 @@ class OutputManager2 {
|
|
|
87136
88204
|
return;
|
|
87137
88205
|
const symbols = this.getSymbols();
|
|
87138
88206
|
console.log();
|
|
87139
|
-
console.log(
|
|
88207
|
+
console.log(import_picocolors32.default.bold(import_picocolors32.default.cyan(`${symbols.line} ${title}`)));
|
|
87140
88208
|
}
|
|
87141
88209
|
addJsonEntry(entry) {
|
|
87142
88210
|
this.jsonBuffer.push({
|