@mrciphersmith/keryx 0.2.50 → 0.2.51
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/cli.js +403 -32
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -17055,7 +17055,7 @@ function renderWrapUpMemoryEntry(input2) {
|
|
|
17055
17055
|
|
|
17056
17056
|
Version: 0.1.0
|
|
17057
17057
|
Type: task-note
|
|
17058
|
-
Status:
|
|
17058
|
+
Status: accepted
|
|
17059
17059
|
Confidence: medium
|
|
17060
17060
|
|
|
17061
17061
|
## Summary
|
|
@@ -17155,7 +17155,7 @@ function renderWrapUpDecisionPage(input2) {
|
|
|
17155
17155
|
|
|
17156
17156
|
Version: 0.1.0
|
|
17157
17157
|
Type: decision
|
|
17158
|
-
Status:
|
|
17158
|
+
Status: accepted
|
|
17159
17159
|
|
|
17160
17160
|
## Summary
|
|
17161
17161
|
|
|
@@ -33709,6 +33709,21 @@ function renderMcpClientSnippet(projectRoot) {
|
|
|
33709
33709
|
return `${JSON.stringify({ mcpServers: { [MCP_SERVER_NAME]: mcpServerEntry(projectRoot) } }, null, 2)}
|
|
33710
33710
|
`;
|
|
33711
33711
|
}
|
|
33712
|
+
async function mcpClientStatus(projectRoot, ids = mcpRuntimeIds()) {
|
|
33713
|
+
const absoluteProjectRoot = path40.resolve(projectRoot);
|
|
33714
|
+
const { runtimes } = resolveMcpRuntimes(ids);
|
|
33715
|
+
const statuses = [];
|
|
33716
|
+
for (const runtime of runtimes) {
|
|
33717
|
+
const file = runtime.settingsPath(absoluteProjectRoot);
|
|
33718
|
+
if (file === null) {
|
|
33719
|
+
statuses.push({ id: runtime.id, filePath: null, connected: false });
|
|
33720
|
+
continue;
|
|
33721
|
+
}
|
|
33722
|
+
const settings = await readSettings2(file);
|
|
33723
|
+
statuses.push({ id: runtime.id, filePath: file, connected: runtime.hasManaged(settings) });
|
|
33724
|
+
}
|
|
33725
|
+
return statuses;
|
|
33726
|
+
}
|
|
33712
33727
|
async function readSettings2(file) {
|
|
33713
33728
|
if (!await pathExists(file)) {
|
|
33714
33729
|
return {};
|
|
@@ -33752,7 +33767,7 @@ function buildMcpModuleEntry() {
|
|
|
33752
33767
|
expose: {
|
|
33753
33768
|
tools: true,
|
|
33754
33769
|
resources: true,
|
|
33755
|
-
modules: ["gdgraph", "gdctx", "security", "flow", "memory", "health", "testing", "wiki", "standard", "sac"]
|
|
33770
|
+
modules: ["gdgraph", "gdctx", "security", "flow", "memory", "health", "testing", "wiki", "standard", "sac", "gdskills"]
|
|
33756
33771
|
}
|
|
33757
33772
|
};
|
|
33758
33773
|
}
|
|
@@ -47120,6 +47135,8 @@ init_workspace_service();
|
|
|
47120
47135
|
var HARNESS_PROVIDER_OPTIONS = [
|
|
47121
47136
|
"fake",
|
|
47122
47137
|
"anthropic",
|
|
47138
|
+
"openai",
|
|
47139
|
+
"gemini",
|
|
47123
47140
|
"ollama",
|
|
47124
47141
|
...OPENAI_COMPAT_PROVIDERS.map((provider) => provider.name)
|
|
47125
47142
|
];
|
|
@@ -47285,6 +47302,20 @@ async function harnessCommand(args2, deps) {
|
|
|
47285
47302
|
return;
|
|
47286
47303
|
}
|
|
47287
47304
|
}
|
|
47305
|
+
if (provider === "openai") {
|
|
47306
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
47307
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
47308
|
+
console.log("OPENAI_API_KEY is not set: the openai provider is required to have a credential and fails closed (no network was contacted).");
|
|
47309
|
+
return;
|
|
47310
|
+
}
|
|
47311
|
+
}
|
|
47312
|
+
if (provider === "gemini") {
|
|
47313
|
+
const apiKey = env.GEMINI_API_KEY !== undefined && env.GEMINI_API_KEY.length > 0 ? env.GEMINI_API_KEY : env.GOOGLE_API_KEY;
|
|
47314
|
+
if (apiKey === undefined || apiKey.length === 0) {
|
|
47315
|
+
console.log("GEMINI_API_KEY (or GOOGLE_API_KEY) is not set: the gemini provider is required to have a credential and fails closed (no network was contacted).");
|
|
47316
|
+
return;
|
|
47317
|
+
}
|
|
47318
|
+
}
|
|
47288
47319
|
const providerPort = makeProvider(provider, model, {
|
|
47289
47320
|
fetch: fetchImpl,
|
|
47290
47321
|
env,
|
|
@@ -52920,7 +52951,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
52920
52951
|
// package.json
|
|
52921
52952
|
var package_default = {
|
|
52922
52953
|
name: "@mrciphersmith/keryx",
|
|
52923
|
-
version: "0.2.
|
|
52954
|
+
version: "0.2.51",
|
|
52924
52955
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
52925
52956
|
private: false,
|
|
52926
52957
|
publishConfig: {
|
|
@@ -54231,6 +54262,7 @@ async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
|
54231
54262
|
}
|
|
54232
54263
|
|
|
54233
54264
|
// src/sac/catch-up.ts
|
|
54265
|
+
init_proposal_evidence();
|
|
54234
54266
|
async function buildCatchUp(input2) {
|
|
54235
54267
|
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
54236
54268
|
collectProposals(input2.cwd, input2.workspaceId),
|
|
@@ -54250,8 +54282,20 @@ async function collectProposals(cwd, workspaceId) {
|
|
|
54250
54282
|
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
54251
54283
|
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
54252
54284
|
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
54253
|
-
const fresh = await
|
|
54254
|
-
|
|
54285
|
+
const [fresh, note2] = await Promise.all([
|
|
54286
|
+
proposalService.isEvidenceFresh(proposal, actor),
|
|
54287
|
+
readSidecarNote(cwd, group.workspace.id, proposal.id)
|
|
54288
|
+
]);
|
|
54289
|
+
return {
|
|
54290
|
+
type: "proposal",
|
|
54291
|
+
workspaceId: group.workspace.id,
|
|
54292
|
+
proposalId: proposal.id,
|
|
54293
|
+
fresh,
|
|
54294
|
+
kind: proposal.kind,
|
|
54295
|
+
author: proposal.author,
|
|
54296
|
+
createdAt: proposal.createdAt,
|
|
54297
|
+
note: note2
|
|
54298
|
+
};
|
|
54255
54299
|
}));
|
|
54256
54300
|
}
|
|
54257
54301
|
async function classifySession(session) {
|
|
@@ -54822,7 +54866,7 @@ function openFlows(otui, chrome, options) {
|
|
|
54822
54866
|
|
|
54823
54867
|
// src/tui/busy-dispatch.ts
|
|
54824
54868
|
function classifyBusyDispatch(params) {
|
|
54825
|
-
const { line, commandName, isSessionInfo, isFlows, isWorkspace, isReview } = params;
|
|
54869
|
+
const { line, commandName, isSessionInfo, isFlows, isWorkspace, isReview, isMcp } = params;
|
|
54826
54870
|
if (commandName === "/exit")
|
|
54827
54871
|
return "exit";
|
|
54828
54872
|
if (commandName === "/help")
|
|
@@ -54841,7 +54885,7 @@ function classifyBusyDispatch(params) {
|
|
|
54841
54885
|
return "copy";
|
|
54842
54886
|
if (commandName === "/mode")
|
|
54843
54887
|
return "mode";
|
|
54844
|
-
const isBusyReadonlyCommand = isSessionInfo || isFlows || isWorkspace || isReview;
|
|
54888
|
+
const isBusyReadonlyCommand = isSessionInfo || isFlows || isWorkspace || isReview || isMcp;
|
|
54845
54889
|
if (isBusyReadonlyCommand && isSessionInfo)
|
|
54846
54890
|
return "session-info";
|
|
54847
54891
|
if (isBusyReadonlyCommand && isFlows)
|
|
@@ -54850,6 +54894,8 @@ function classifyBusyDispatch(params) {
|
|
|
54850
54894
|
return "workspace";
|
|
54851
54895
|
if (isBusyReadonlyCommand && isReview)
|
|
54852
54896
|
return "review";
|
|
54897
|
+
if (isBusyReadonlyCommand && isMcp)
|
|
54898
|
+
return "mcp";
|
|
54853
54899
|
if (commandName !== undefined || line.startsWith("/"))
|
|
54854
54900
|
return "deferred";
|
|
54855
54901
|
return "not-a-command";
|
|
@@ -55109,7 +55155,8 @@ function openWorkspace(otui, chrome, options) {
|
|
|
55109
55155
|
var REVIEW_COMMAND = "/review";
|
|
55110
55156
|
var REVIEW_FOOTER = [
|
|
55111
55157
|
{ key: "[/]", label: "item" },
|
|
55112
|
-
{ key: "a y", label: "accept" },
|
|
55158
|
+
{ key: "a y", label: "accept proposal" },
|
|
55159
|
+
{ key: "d y", label: "decline proposal" },
|
|
55113
55160
|
{ key: "\u2191/\u2193", label: "scroll" },
|
|
55114
55161
|
{ key: "\u2190/\u2192", label: "tabs" },
|
|
55115
55162
|
{ key: "esc", label: "close" }
|
|
@@ -55127,7 +55174,7 @@ var TYPE_LABEL = {
|
|
|
55127
55174
|
function summarizeReviewItem(item) {
|
|
55128
55175
|
switch (item.type) {
|
|
55129
55176
|
case "proposal":
|
|
55130
|
-
return `${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
55177
|
+
return `${item.kind} ${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
55131
55178
|
case "blocked":
|
|
55132
55179
|
return `${item.sessionId} \u2014 ${item.terminalState.reason}`;
|
|
55133
55180
|
case "unbound-candidate":
|
|
@@ -55151,9 +55198,13 @@ function describeReviewItem(item) {
|
|
|
55151
55198
|
return [
|
|
55152
55199
|
`Proposal ${item.proposalId}`,
|
|
55153
55200
|
`Workspace ${item.workspaceId}`,
|
|
55201
|
+
`Kind ${item.kind}`,
|
|
55202
|
+
`Author ${item.author}`,
|
|
55203
|
+
`Created ${item.createdAt}`,
|
|
55154
55204
|
`Evidence ${item.fresh ? "fresh" : "stale \u2014 evidence has drifted since this proposal was created; re-run wrap-up before deciding"}`,
|
|
55205
|
+
...item.note !== undefined ? ["", `Note ${item.note}`] : [],
|
|
55155
55206
|
"",
|
|
55156
|
-
`
|
|
55207
|
+
`Dismiss (archive with no decision) from a terminal: keryx workspace review ${item.workspaceId} ${item.proposalId} --decision dismissed`
|
|
55157
55208
|
];
|
|
55158
55209
|
case "blocked":
|
|
55159
55210
|
return [
|
|
@@ -55206,25 +55257,37 @@ function describeGroupOutcome(g) {
|
|
|
55206
55257
|
}
|
|
55207
55258
|
}
|
|
55208
55259
|
}
|
|
55260
|
+
var DECISION_VERB = { accept: "Accept", decline: "Decline" };
|
|
55261
|
+
var DECISION_ING = { accept: "Accepting", decline: "Declining" };
|
|
55262
|
+
var DECISION_DONE = { accept: "Accepted", decline: "Declined" };
|
|
55263
|
+
var DECISION_COMMAND = {
|
|
55264
|
+
accept: "running `keryx workspace confirm-review` then `keryx workspace review`",
|
|
55265
|
+
decline: "running `keryx workspace review --decision rejected`"
|
|
55266
|
+
};
|
|
55209
55267
|
function formatReviewDetailLines(item, status) {
|
|
55210
55268
|
if (item === undefined) {
|
|
55211
55269
|
return ["No item selected.", "", "Press Enter (or click a row) on the Review tab to view one."];
|
|
55212
55270
|
}
|
|
55213
55271
|
const lines = describeReviewItem(item);
|
|
55214
55272
|
if (item.type !== "proposal") {
|
|
55273
|
+
if (status.kind === "unavailable") {
|
|
55274
|
+
return [...lines, "", `[${status.decision === "accept" ? "a" : "d"}] does nothing here \u2014 accept/decline only apply to a pending proposal, not to this item.`];
|
|
55275
|
+
}
|
|
55215
55276
|
return lines;
|
|
55216
55277
|
}
|
|
55217
55278
|
const withAction = [...lines, ""];
|
|
55218
55279
|
if (status.kind === "armed") {
|
|
55219
|
-
withAction.push(
|
|
55280
|
+
withAction.push(`Press [y] to CONFIRM ${status.decision}, any other key cancels.`);
|
|
55220
55281
|
} else if (status.kind === "running") {
|
|
55221
|
-
withAction.push(
|
|
55282
|
+
withAction.push(`${DECISION_ING[status.decision]}\u2026 ${DECISION_COMMAND[status.decision]}.`);
|
|
55222
55283
|
} else if (status.kind === "done" && status.outcome.ok) {
|
|
55223
|
-
withAction.push(
|
|
55284
|
+
withAction.push(`\u2713 ${DECISION_DONE[status.decision]}.`);
|
|
55224
55285
|
} else if (status.kind === "done" && !status.outcome.ok) {
|
|
55225
|
-
withAction.push(`\u2717
|
|
55286
|
+
withAction.push(`\u2717 ${DECISION_VERB[status.decision]} failed: ${status.outcome.message}`);
|
|
55287
|
+
} else if (status.kind === "unavailable") {
|
|
55288
|
+
withAction.push(`[${status.decision === "accept" ? "a" : "d"}] does nothing \u2014 no ${status.decision} handler is configured for this modal.`);
|
|
55226
55289
|
} else {
|
|
55227
|
-
withAction.push("[a] Accept this proposal");
|
|
55290
|
+
withAction.push("[a] Accept this proposal [d] Decline this proposal");
|
|
55228
55291
|
}
|
|
55229
55292
|
return withAction;
|
|
55230
55293
|
}
|
|
@@ -55321,17 +55384,19 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
55321
55384
|
status = { kind: "idle" };
|
|
55322
55385
|
paintSelection();
|
|
55323
55386
|
};
|
|
55324
|
-
const
|
|
55387
|
+
const handlerFor = (decision) => decision === "accept" ? options.acceptProposal : options.declineProposal;
|
|
55388
|
+
const runDecision = (decision) => {
|
|
55325
55389
|
const item = items[selected];
|
|
55326
|
-
|
|
55390
|
+
const run = handlerFor(decision);
|
|
55391
|
+
if (item === undefined || item.type !== "proposal" || run === undefined) {
|
|
55327
55392
|
return;
|
|
55328
55393
|
}
|
|
55329
|
-
status = { kind: "running" };
|
|
55394
|
+
status = { kind: "running", decision };
|
|
55330
55395
|
paintSelection();
|
|
55331
|
-
|
|
55332
|
-
status = { kind: "done", outcome };
|
|
55396
|
+
run(item).then((outcome) => {
|
|
55397
|
+
status = { kind: "done", decision, outcome };
|
|
55333
55398
|
if (outcome.ok) {
|
|
55334
|
-
options.
|
|
55399
|
+
options.onResolved?.(item);
|
|
55335
55400
|
}
|
|
55336
55401
|
paintSelection();
|
|
55337
55402
|
});
|
|
@@ -55371,7 +55436,7 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
55371
55436
|
const onDetail = handle.activeTab() === "detail";
|
|
55372
55437
|
if (onDetail && status.kind === "armed") {
|
|
55373
55438
|
if (token === "y") {
|
|
55374
|
-
|
|
55439
|
+
runDecision(status.decision);
|
|
55375
55440
|
} else {
|
|
55376
55441
|
status = { kind: "idle" };
|
|
55377
55442
|
paintSelection();
|
|
@@ -55390,8 +55455,9 @@ function presentReview(openModal2, otui, chrome, options) {
|
|
|
55390
55455
|
handle.setTab("detail");
|
|
55391
55456
|
return;
|
|
55392
55457
|
}
|
|
55393
|
-
if (onDetail && token === "a"
|
|
55394
|
-
|
|
55458
|
+
if (onDetail && (token === "a" || token === "d") && status.kind !== "running") {
|
|
55459
|
+
const decision = token === "a" ? "accept" : "decline";
|
|
55460
|
+
status = items[selected]?.type === "proposal" && handlerFor(decision) !== undefined ? { kind: "armed", decision } : { kind: "unavailable", decision };
|
|
55395
55461
|
paintSelection();
|
|
55396
55462
|
return;
|
|
55397
55463
|
}
|
|
@@ -55455,6 +55521,228 @@ async function acceptProposalViaShell(run, workspaceId, proposalId) {
|
|
|
55455
55521
|
}
|
|
55456
55522
|
return { ok: true };
|
|
55457
55523
|
}
|
|
55524
|
+
async function declineProposalViaShell(run, workspaceId, proposalId) {
|
|
55525
|
+
const decline = await run(`keryx workspace review ${shQuote(workspaceId)} ${shQuote(proposalId)} --decision rejected`);
|
|
55526
|
+
if (decline.isError) {
|
|
55527
|
+
return { ok: false, message: decline.output };
|
|
55528
|
+
}
|
|
55529
|
+
return { ok: true };
|
|
55530
|
+
}
|
|
55531
|
+
|
|
55532
|
+
// src/tui/mcp-inspector.ts
|
|
55533
|
+
var MCP_INSPECTOR_FOOTER = [
|
|
55534
|
+
{ key: "\u2191/\u2193", label: "select" },
|
|
55535
|
+
{ key: "c/d", label: "connect/disconnect" },
|
|
55536
|
+
{ key: "y", label: "confirm" },
|
|
55537
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
55538
|
+
{ key: "esc", label: "close" }
|
|
55539
|
+
];
|
|
55540
|
+
var MCP_TOOLS_COMMAND = "/mcp";
|
|
55541
|
+
function isMcpToolsCommand(line) {
|
|
55542
|
+
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
55543
|
+
return token === MCP_TOOLS_COMMAND;
|
|
55544
|
+
}
|
|
55545
|
+
var RUNTIME_LABELS = {
|
|
55546
|
+
cursor: "Cursor",
|
|
55547
|
+
claude: "Claude Code",
|
|
55548
|
+
opencode: "opencode",
|
|
55549
|
+
vscode: "VS Code",
|
|
55550
|
+
generic: "Generic (manual)"
|
|
55551
|
+
};
|
|
55552
|
+
function runtimeLabel(id) {
|
|
55553
|
+
return RUNTIME_LABELS[id] ?? id;
|
|
55554
|
+
}
|
|
55555
|
+
function formatToolsListLines(tools) {
|
|
55556
|
+
if (tools.length === 0) {
|
|
55557
|
+
return ["No tools available."];
|
|
55558
|
+
}
|
|
55559
|
+
return tools.map((tool) => {
|
|
55560
|
+
const risk = (tool.risk ?? "read").padEnd(6);
|
|
55561
|
+
const name = tool.name.padEnd(28);
|
|
55562
|
+
return `${name} ${risk} ${tool.description ?? ""}`.trimEnd();
|
|
55563
|
+
});
|
|
55564
|
+
}
|
|
55565
|
+
function isActionable(id) {
|
|
55566
|
+
return id !== "generic";
|
|
55567
|
+
}
|
|
55568
|
+
function formatMcpListLines(runtimes, selected, status) {
|
|
55569
|
+
if (runtimes.length === 0) {
|
|
55570
|
+
return ["No MCP client runtimes registered."];
|
|
55571
|
+
}
|
|
55572
|
+
return runtimes.map((runtime, index) => {
|
|
55573
|
+
const mark = index === selected ? ">" : " ";
|
|
55574
|
+
const label = runtimeLabel(runtime.id).padEnd(20);
|
|
55575
|
+
const statusText = runtime.connected ? "\u25CF connected" : "\u25CB not connected";
|
|
55576
|
+
let action = "";
|
|
55577
|
+
if (!isActionable(runtime.id)) {
|
|
55578
|
+
action = " (copy snippet manually)";
|
|
55579
|
+
} else if (status.kind === "armed" && status.target.id === runtime.id) {
|
|
55580
|
+
action = ` [press y to ${status.target.action}]`;
|
|
55581
|
+
} else if (status.kind === "running" && status.target.id === runtime.id) {
|
|
55582
|
+
action = ` ${status.target.action === "connect" ? "connecting\u2026" : "disconnecting\u2026"}`;
|
|
55583
|
+
} else if (status.kind === "done" && status.target.id === runtime.id) {
|
|
55584
|
+
action = status.outcome.ok ? " \u2713 done" : ` \u2717 ${status.outcome.message}`;
|
|
55585
|
+
} else {
|
|
55586
|
+
action = runtime.connected ? " [d] disconnect" : " [c] connect";
|
|
55587
|
+
}
|
|
55588
|
+
return `${mark} ${label} ${statusText}${action}`;
|
|
55589
|
+
});
|
|
55590
|
+
}
|
|
55591
|
+
function presentMcpTools(openModal2, otui, chrome, options) {
|
|
55592
|
+
const runtimes = options.runtimes.map((r) => ({ ...r }));
|
|
55593
|
+
let mcpSelected = 0;
|
|
55594
|
+
let toolsScroll = 0;
|
|
55595
|
+
let mcpScroll = 0;
|
|
55596
|
+
let status = { kind: "idle" };
|
|
55597
|
+
let toolsNode;
|
|
55598
|
+
let mcpNode;
|
|
55599
|
+
let unsubscribeKey;
|
|
55600
|
+
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
55601
|
+
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
55602
|
+
const toolLines = () => formatToolsListLines(options.tools);
|
|
55603
|
+
const mcpLines = () => formatMcpListLines(runtimes, mcpSelected, status);
|
|
55604
|
+
const paint = () => {
|
|
55605
|
+
toolsScroll = clampScroll3(toolsScroll, toolLines().length, bodyRows);
|
|
55606
|
+
mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
|
|
55607
|
+
mcpScroll = clampScroll3(mcpScroll, mcpLines().length, bodyRows);
|
|
55608
|
+
if (toolsNode !== undefined) {
|
|
55609
|
+
toolsNode.content = windowLines3(toolLines(), toolsScroll, bodyRows).join(`
|
|
55610
|
+
`);
|
|
55611
|
+
}
|
|
55612
|
+
if (mcpNode !== undefined) {
|
|
55613
|
+
mcpNode.content = windowLines3(mcpLines(), mcpScroll, bodyRows).join(`
|
|
55614
|
+
`);
|
|
55615
|
+
}
|
|
55616
|
+
};
|
|
55617
|
+
const moveMcpSelection = (next) => {
|
|
55618
|
+
if (runtimes.length === 0) {
|
|
55619
|
+
return;
|
|
55620
|
+
}
|
|
55621
|
+
const clamped = Math.min(runtimes.length - 1, Math.max(0, next));
|
|
55622
|
+
if (clamped === mcpSelected) {
|
|
55623
|
+
return;
|
|
55624
|
+
}
|
|
55625
|
+
mcpSelected = clamped;
|
|
55626
|
+
status = { kind: "idle" };
|
|
55627
|
+
paint();
|
|
55628
|
+
};
|
|
55629
|
+
const runAction = () => {
|
|
55630
|
+
if (status.kind !== "armed") {
|
|
55631
|
+
return;
|
|
55632
|
+
}
|
|
55633
|
+
const target = status.target;
|
|
55634
|
+
status = { kind: "running", target };
|
|
55635
|
+
paint();
|
|
55636
|
+
const fn = target.action === "connect" ? options.connect : options.disconnect;
|
|
55637
|
+
fn(target.id).then((outcome) => {
|
|
55638
|
+
status = { kind: "done", target, outcome };
|
|
55639
|
+
if (outcome.ok) {
|
|
55640
|
+
const row = runtimes.find((r) => r.id === target.id);
|
|
55641
|
+
if (row !== undefined) {
|
|
55642
|
+
row.connected = target.action === "connect";
|
|
55643
|
+
}
|
|
55644
|
+
}
|
|
55645
|
+
options.onStatusChange?.(runtimes);
|
|
55646
|
+
paint();
|
|
55647
|
+
});
|
|
55648
|
+
};
|
|
55649
|
+
const handle = openModal2(otui, chrome, {
|
|
55650
|
+
title: "Tools & MCP",
|
|
55651
|
+
tabs: [
|
|
55652
|
+
{ id: "tools", label: "Tools" },
|
|
55653
|
+
{ id: "mcp", label: "MCP" }
|
|
55654
|
+
],
|
|
55655
|
+
initialTab: "tools",
|
|
55656
|
+
footer: MCP_INSPECTOR_FOOTER,
|
|
55657
|
+
renderTab: (tabId, body, ctx) => {
|
|
55658
|
+
const renderer = options.renderer ?? chrome?.renderer;
|
|
55659
|
+
const parent = body;
|
|
55660
|
+
const ctor = otui.TextRenderable;
|
|
55661
|
+
if (parent.add === undefined || ctor === undefined) {
|
|
55662
|
+
return;
|
|
55663
|
+
}
|
|
55664
|
+
if (tabId === "tools") {
|
|
55665
|
+
toolsScroll = clampScroll3(toolsScroll, toolLines().length, bodyRows);
|
|
55666
|
+
toolsNode = new ctor(renderer, { id: "mcp-tools-body", content: windowLines3(toolLines(), toolsScroll, bodyRows).join(`
|
|
55667
|
+
`) });
|
|
55668
|
+
parent.add(toolsNode);
|
|
55669
|
+
return;
|
|
55670
|
+
}
|
|
55671
|
+
mcpScroll = scrollToReveal3(mcpSelected, mcpScroll, bodyRows);
|
|
55672
|
+
mcpNode = new ctor(renderer, { id: "mcp-mcp-body", content: windowLines3(mcpLines(), mcpScroll, bodyRows).join(`
|
|
55673
|
+
`) });
|
|
55674
|
+
parent.add(mcpNode);
|
|
55675
|
+
},
|
|
55676
|
+
onClose: () => {
|
|
55677
|
+
unsubscribeKey?.();
|
|
55678
|
+
}
|
|
55679
|
+
});
|
|
55680
|
+
if (handle === undefined) {
|
|
55681
|
+
return;
|
|
55682
|
+
}
|
|
55683
|
+
if (options.onKeypress !== undefined) {
|
|
55684
|
+
unsubscribeKey = options.onKeypress((key) => {
|
|
55685
|
+
const token = key.name || key.sequence;
|
|
55686
|
+
const onMcp = handle.activeTab() === "mcp";
|
|
55687
|
+
if (onMcp && status.kind === "armed") {
|
|
55688
|
+
if (token === "y") {
|
|
55689
|
+
runAction();
|
|
55690
|
+
} else {
|
|
55691
|
+
status = { kind: "idle" };
|
|
55692
|
+
paint();
|
|
55693
|
+
}
|
|
55694
|
+
return;
|
|
55695
|
+
}
|
|
55696
|
+
if (onMcp && token === "c") {
|
|
55697
|
+
const row = runtimes[mcpSelected];
|
|
55698
|
+
if (row !== undefined && isActionable(row.id) && !row.connected && status.kind !== "running") {
|
|
55699
|
+
status = { kind: "armed", target: { id: row.id, action: "connect" } };
|
|
55700
|
+
paint();
|
|
55701
|
+
}
|
|
55702
|
+
return;
|
|
55703
|
+
}
|
|
55704
|
+
if (onMcp && token === "d") {
|
|
55705
|
+
const row = runtimes[mcpSelected];
|
|
55706
|
+
if (row !== undefined && isActionable(row.id) && row.connected && status.kind !== "running") {
|
|
55707
|
+
status = { kind: "armed", target: { id: row.id, action: "disconnect" } };
|
|
55708
|
+
paint();
|
|
55709
|
+
}
|
|
55710
|
+
return;
|
|
55711
|
+
}
|
|
55712
|
+
if (token === "up" || token === "k") {
|
|
55713
|
+
if (onMcp) {
|
|
55714
|
+
moveMcpSelection(mcpSelected - 1);
|
|
55715
|
+
} else {
|
|
55716
|
+
toolsScroll = clampScroll3(toolsScroll - 1, toolLines().length, bodyRows);
|
|
55717
|
+
paint();
|
|
55718
|
+
}
|
|
55719
|
+
return;
|
|
55720
|
+
}
|
|
55721
|
+
if (token === "down" || token === "j") {
|
|
55722
|
+
if (onMcp) {
|
|
55723
|
+
moveMcpSelection(mcpSelected + 1);
|
|
55724
|
+
} else {
|
|
55725
|
+
toolsScroll = clampScroll3(toolsScroll + 1, toolLines().length, bodyRows);
|
|
55726
|
+
paint();
|
|
55727
|
+
}
|
|
55728
|
+
return;
|
|
55729
|
+
}
|
|
55730
|
+
if (token === "pageup" || token === "pagedown") {
|
|
55731
|
+
const step = token === "pageup" ? -bodyRows : bodyRows;
|
|
55732
|
+
if (onMcp) {
|
|
55733
|
+
mcpScroll = clampScroll3(mcpScroll + step, mcpLines().length, bodyRows);
|
|
55734
|
+
} else {
|
|
55735
|
+
toolsScroll = clampScroll3(toolsScroll + step, toolLines().length, bodyRows);
|
|
55736
|
+
}
|
|
55737
|
+
paint();
|
|
55738
|
+
}
|
|
55739
|
+
});
|
|
55740
|
+
}
|
|
55741
|
+
return handle;
|
|
55742
|
+
}
|
|
55743
|
+
function openMcpTools(otui, chrome, options) {
|
|
55744
|
+
return presentMcpTools((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
55745
|
+
}
|
|
55458
55746
|
|
|
55459
55747
|
// src/tui/tui-shell.ts
|
|
55460
55748
|
init_slate();
|
|
@@ -55825,6 +56113,11 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
55825
56113
|
description: "Show project-wide items needing review (proposals, blocked sessions)",
|
|
55826
56114
|
modes: AGENT_ONLY
|
|
55827
56115
|
},
|
|
56116
|
+
{
|
|
56117
|
+
name: "/mcp",
|
|
56118
|
+
description: "Show available tools and MCP client connect status",
|
|
56119
|
+
modes: AGENT_ONLY
|
|
56120
|
+
},
|
|
55828
56121
|
{
|
|
55829
56122
|
name: "/compact",
|
|
55830
56123
|
description: "Compact model context \u2014 /compact [focus] (archive kept)",
|
|
@@ -56403,6 +56696,10 @@ function onKeypress2(r, handler) {
|
|
|
56403
56696
|
return () => r._internalKeyInput.offInternal("keypress", handler);
|
|
56404
56697
|
}
|
|
56405
56698
|
function showComposerChoice(otui, r, dock, request) {
|
|
56699
|
+
if (dock.visible === true) {
|
|
56700
|
+
request.onBusy?.();
|
|
56701
|
+
return Promise.resolve(request.cancelId);
|
|
56702
|
+
}
|
|
56406
56703
|
return new Promise((resolve3) => {
|
|
56407
56704
|
const options = request.options.map((o) => ({
|
|
56408
56705
|
...o,
|
|
@@ -60626,7 +60923,13 @@ async function launchTuiAgentShell(opts) {
|
|
|
60626
60923
|
const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("0 tokens")}` });
|
|
60627
60924
|
sidebar.add(sbContext);
|
|
60628
60925
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-tools-k", content: otui.t`${otui.dim("Tools")}`, marginTop: 1 }));
|
|
60629
|
-
sidebar.add(new otui.TextRenderable(r, {
|
|
60926
|
+
sidebar.add(new otui.TextRenderable(r, {
|
|
60927
|
+
id: "sb-tools-v",
|
|
60928
|
+
content: otui.t`${otui.dim(`${deps.tools.length} available`)}`,
|
|
60929
|
+
onMouseDown: () => {
|
|
60930
|
+
showTools();
|
|
60931
|
+
}
|
|
60932
|
+
}));
|
|
60630
60933
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-status-k", content: otui.t`${otui.dim("Status")}`, marginTop: 1 }));
|
|
60631
60934
|
const sbWorkers = new otui.TextRenderable(r, {
|
|
60632
60935
|
id: "sb-status-v",
|
|
@@ -61401,7 +61704,8 @@ Staying in the current session.
|
|
|
61401
61704
|
openReview(otui, chrome, {
|
|
61402
61705
|
items,
|
|
61403
61706
|
acceptProposal: (item) => acceptProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
61404
|
-
|
|
61707
|
+
declineProposal: (item) => declineProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
61708
|
+
onResolved: () => {
|
|
61405
61709
|
refreshReviewSidebar();
|
|
61406
61710
|
},
|
|
61407
61711
|
renderer: r,
|
|
@@ -61409,6 +61713,38 @@ Staying in the current session.
|
|
|
61409
61713
|
});
|
|
61410
61714
|
})();
|
|
61411
61715
|
};
|
|
61716
|
+
const showTools = () => {
|
|
61717
|
+
(async () => {
|
|
61718
|
+
const cwd = inspectorCwd();
|
|
61719
|
+
const runtimes = await mcpClientStatus(cwd, mcpRuntimeIds());
|
|
61720
|
+
openMcpTools(otui, chrome, {
|
|
61721
|
+
tools: deps.tools.map((t) => t.definition),
|
|
61722
|
+
runtimes,
|
|
61723
|
+
connect: async (id) => {
|
|
61724
|
+
try {
|
|
61725
|
+
const report = await installMcpClient(cwd, [id]);
|
|
61726
|
+
const outcome = report.outcomes[0];
|
|
61727
|
+
if (outcome !== undefined && outcome.errors.length > 0) {
|
|
61728
|
+
return { ok: false, message: outcome.errors.join("; ") };
|
|
61729
|
+
}
|
|
61730
|
+
return { ok: true };
|
|
61731
|
+
} catch (error2) {
|
|
61732
|
+
return { ok: false, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
61733
|
+
}
|
|
61734
|
+
},
|
|
61735
|
+
disconnect: async (id) => {
|
|
61736
|
+
try {
|
|
61737
|
+
await uninstallMcpClient(cwd, [id]);
|
|
61738
|
+
return { ok: true };
|
|
61739
|
+
} catch (error2) {
|
|
61740
|
+
return { ok: false, message: error2 instanceof Error ? error2.message : String(error2) };
|
|
61741
|
+
}
|
|
61742
|
+
},
|
|
61743
|
+
renderer: r,
|
|
61744
|
+
...inspectorKeys
|
|
61745
|
+
});
|
|
61746
|
+
})();
|
|
61747
|
+
};
|
|
61412
61748
|
const runModeCommand = (line) => {
|
|
61413
61749
|
const modeArgs = line.trim().split(/\s+/).slice(1).filter((p) => p.length > 0);
|
|
61414
61750
|
const wanted = modeArgs[0] ?? "";
|
|
@@ -61416,6 +61752,7 @@ Staying in the current session.
|
|
|
61416
61752
|
const applyMode = async (next) => {
|
|
61417
61753
|
if (next === "auto") {
|
|
61418
61754
|
chrome.hideMenu();
|
|
61755
|
+
let blockedByOpenDialog = false;
|
|
61419
61756
|
const confirmId = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
|
|
61420
61757
|
title: "Switch to auto mode?",
|
|
61421
61758
|
subtitle: "Skips confirmation for EVERY action, including destructive commands. Only credential-touching commands still ask.",
|
|
@@ -61423,9 +61760,16 @@ Staying in the current session.
|
|
|
61423
61760
|
options: [
|
|
61424
61761
|
{ id: "confirm", label: "Confirm", description: "I understand the risk" },
|
|
61425
61762
|
{ id: "cancel", label: "Cancel", description: "Keep the current mode", recommended: true }
|
|
61426
|
-
]
|
|
61763
|
+
],
|
|
61764
|
+
onBusy: () => {
|
|
61765
|
+
blockedByOpenDialog = true;
|
|
61766
|
+
chrome.showToast("Answer the open approval first, then retry /mode.");
|
|
61767
|
+
}
|
|
61427
61768
|
}));
|
|
61428
61769
|
input2.focus();
|
|
61770
|
+
if (blockedByOpenDialog) {
|
|
61771
|
+
return;
|
|
61772
|
+
}
|
|
61429
61773
|
if (confirmId !== "confirm") {
|
|
61430
61774
|
chrome.showToast("Cancelled \u2014 mode unchanged.");
|
|
61431
61775
|
return;
|
|
@@ -61455,6 +61799,7 @@ Staying in the current session.
|
|
|
61455
61799
|
const stored = getProjectPermissionMode(sessionCwd);
|
|
61456
61800
|
chrome.hideMenu();
|
|
61457
61801
|
(async () => {
|
|
61802
|
+
let blockedByOpenDialog = false;
|
|
61458
61803
|
const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
|
|
61459
61804
|
title: `Permission mode (current: ${permissionMode})`,
|
|
61460
61805
|
subtitle: stored !== undefined ? `Project default: ${stored}` : "No project default set.",
|
|
@@ -61464,9 +61809,16 @@ Staying in the current session.
|
|
|
61464
61809
|
label: m,
|
|
61465
61810
|
description: MODE_PICKER_DESCRIPTIONS[m],
|
|
61466
61811
|
recommended: m === permissionMode
|
|
61467
|
-
}))
|
|
61812
|
+
})),
|
|
61813
|
+
onBusy: () => {
|
|
61814
|
+
blockedByOpenDialog = true;
|
|
61815
|
+
chrome.showToast("Answer the open approval first, then retry /mode.");
|
|
61816
|
+
}
|
|
61468
61817
|
}));
|
|
61469
61818
|
input2.focus();
|
|
61819
|
+
if (blockedByOpenDialog) {
|
|
61820
|
+
return;
|
|
61821
|
+
}
|
|
61470
61822
|
if (isPermissionMode(id) && id !== permissionMode) {
|
|
61471
61823
|
await applyMode(id);
|
|
61472
61824
|
}
|
|
@@ -61864,7 +62216,8 @@ Staying in the current session.
|
|
|
61864
62216
|
isSessionInfo: isSessionInfoCommand(line),
|
|
61865
62217
|
isFlows: isFlowsCommand(line),
|
|
61866
62218
|
isWorkspace: isWorkspaceCommand(line),
|
|
61867
|
-
isReview: isReviewCommand(line)
|
|
62219
|
+
isReview: isReviewCommand(line),
|
|
62220
|
+
isMcp: isMcpToolsCommand(line)
|
|
61868
62221
|
});
|
|
61869
62222
|
switch (decision) {
|
|
61870
62223
|
case "exit": {
|
|
@@ -61972,6 +62325,10 @@ Staying in the current session.
|
|
|
61972
62325
|
showReview();
|
|
61973
62326
|
return;
|
|
61974
62327
|
}
|
|
62328
|
+
case "mcp": {
|
|
62329
|
+
showTools();
|
|
62330
|
+
return;
|
|
62331
|
+
}
|
|
61975
62332
|
case "deferred": {
|
|
61976
62333
|
transcript.add(new otui.TextRenderable(r, {
|
|
61977
62334
|
id: `c${uid++}`,
|
|
@@ -61995,6 +62352,7 @@ Staying in the current session.
|
|
|
61995
62352
|
return;
|
|
61996
62353
|
}
|
|
61997
62354
|
(async () => {
|
|
62355
|
+
let blockedByOpenDialog = false;
|
|
61998
62356
|
const chosen = await showComposerChoice(otui, r, chrome.dock, {
|
|
61999
62357
|
title: "Main agent is busy",
|
|
62000
62358
|
subtitle: line,
|
|
@@ -62002,8 +62360,17 @@ Staying in the current session.
|
|
|
62002
62360
|
{ id: "main", label: "Main queue", description: "queue for the main agent; remove/edit/force later", recommended: true },
|
|
62003
62361
|
{ id: "side", label: "Side-1", description: "read-only answer, outside main history (as before)" }
|
|
62004
62362
|
],
|
|
62005
|
-
cancelId: "side"
|
|
62363
|
+
cancelId: "side",
|
|
62364
|
+
onBusy: () => {
|
|
62365
|
+
blockedByOpenDialog = true;
|
|
62366
|
+
chrome.showToast("Answer the open approval first, then resend.");
|
|
62367
|
+
}
|
|
62006
62368
|
});
|
|
62369
|
+
if (blockedByOpenDialog) {
|
|
62370
|
+
input2.value = line;
|
|
62371
|
+
input2.focus();
|
|
62372
|
+
return;
|
|
62373
|
+
}
|
|
62007
62374
|
if (chosen === "main") {
|
|
62008
62375
|
const id = `mq${mainQueueSeq++}`;
|
|
62009
62376
|
mainQueue.push({ id, question: line, displayQuestion: displayLine });
|
|
@@ -62182,6 +62549,10 @@ Staying in the current session.
|
|
|
62182
62549
|
showReview();
|
|
62183
62550
|
return;
|
|
62184
62551
|
}
|
|
62552
|
+
if (isMcpToolsCommand(command.name)) {
|
|
62553
|
+
showTools();
|
|
62554
|
+
return;
|
|
62555
|
+
}
|
|
62185
62556
|
if (command.name === "/copy") {
|
|
62186
62557
|
const target = newestBlock();
|
|
62187
62558
|
if (target === undefined || !copyBlock(target.id)) {
|
|
@@ -70868,7 +71239,7 @@ keryx workspace list-proposals [<workspace-id>]`);
|
|
|
70868
71239
|
}
|
|
70869
71240
|
function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
70870
71241
|
const sections = [];
|
|
70871
|
-
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss proposal ${item.proposalId} in workspace ${item.workspaceId}? ` + `Recommendation: ${item.fresh ? "evidence is fresh \u2014 review now (`keryx workspace review " + item.workspaceId + " " + item.proposalId + " --decision <accepted|rejected|dismissed>`)" : "evidence has drifted since this proposal was created \u2014 treat as stale, re-run wrap-up before deciding"}.`));
|
|
71242
|
+
sections.push(renderSection("Pending proposals", report.proposals, (item) => `- Accept, reject, or dismiss ${item.kind} proposal ${item.proposalId} in workspace ${item.workspaceId}` + `${item.note !== undefined ? `: "${item.note}"` : ""}? ` + `Recommendation: ${item.fresh ? "evidence is fresh \u2014 review now (`keryx workspace review " + item.workspaceId + " " + item.proposalId + " --decision <accepted|rejected|dismissed>`)" : "evidence has drifted since this proposal was created \u2014 treat as stale, re-run wrap-up before deciding"}.`));
|
|
70872
71243
|
sections.push(renderSection("Blocked sessions (stopped unattended)", report.blocked, (item) => `- Session ${item.sessionId} stopped unattended (${item.terminalState.reason}) at ${item.terminalState.occurredAt}. Resume it, or archive and move on? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to resume and unblock it.`));
|
|
70873
71244
|
sections.push(renderSection("Unbound candidates (wrap-up ran, no workspace bound)", report.unboundCandidates, (item) => `- Session ${item.sessionId} produced untriaged seeds with no workspace bound (${item.summary}). Bind to a workspace and propose, or discard? ` + `Recommendation: pick a workspace, then \`keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}\` (evidence: ${item.evidencePath}).`));
|
|
70874
71245
|
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt} with no proposal, terminal state, or unbound-candidate artifact recorded. Investigate, or ignore? ` + `Recommendation: \`keryx sessions list\` / \`keryx shell -r ${item.sessionId}\` to see what happened.`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.51",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|