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