@mrciphersmith/keryx 0.2.115 → 0.2.117
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 +341 -176
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50252,7 +50252,7 @@ import path47 from "path";
|
|
|
50252
50252
|
// package.json
|
|
50253
50253
|
var package_default = {
|
|
50254
50254
|
name: "@mrciphersmith/keryx",
|
|
50255
|
-
version: "0.2.
|
|
50255
|
+
version: "0.2.117",
|
|
50256
50256
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
50257
50257
|
private: false,
|
|
50258
50258
|
publishConfig: {
|
|
@@ -82502,8 +82502,6 @@ function realSpawner() {
|
|
|
82502
82502
|
env,
|
|
82503
82503
|
detached: true
|
|
82504
82504
|
});
|
|
82505
|
-
let dataCb;
|
|
82506
|
-
let exitCb;
|
|
82507
82505
|
let netClosed = false;
|
|
82508
82506
|
const closeNetOnce = async () => {
|
|
82509
82507
|
if (netClosed)
|
|
@@ -82511,43 +82509,81 @@ function realSpawner() {
|
|
|
82511
82509
|
netClosed = true;
|
|
82512
82510
|
await netClose();
|
|
82513
82511
|
};
|
|
82514
|
-
|
|
82515
|
-
const errDecoder = new TextDecoder;
|
|
82516
|
-
const pump = async (stream, kind, decoder) => {
|
|
82517
|
-
if (stream === undefined)
|
|
82518
|
-
return;
|
|
82519
|
-
const reader = stream.getReader();
|
|
82520
|
-
try {
|
|
82521
|
-
for (;; ) {
|
|
82522
|
-
const chunk = await reader.read();
|
|
82523
|
-
if (chunk.done)
|
|
82524
|
-
break;
|
|
82525
|
-
if (chunk.value !== undefined)
|
|
82526
|
-
dataCb?.(decoder.decode(chunk.value, { stream: true }), kind);
|
|
82527
|
-
}
|
|
82528
|
-
} catch {} finally {
|
|
82529
|
-
reader.releaseLock();
|
|
82530
|
-
}
|
|
82531
|
-
};
|
|
82532
|
-
Promise.all([pump(proc.stdout, "stdout", outDecoder), pump(proc.stderr, "stderr", errDecoder)]);
|
|
82533
|
-
proc.exited.then(async (exitCode) => {
|
|
82534
|
-
exitCb?.({ exitCode });
|
|
82535
|
-
await closeNetOnce();
|
|
82536
|
-
});
|
|
82537
|
-
return {
|
|
82538
|
-
pid: proc.pid,
|
|
82539
|
-
onOutput: (cb) => {
|
|
82540
|
-
dataCb = cb;
|
|
82541
|
-
},
|
|
82542
|
-
onExit: (cb) => {
|
|
82543
|
-
exitCb = cb;
|
|
82544
|
-
},
|
|
82512
|
+
return superviseSpawnedProcess(proc, {
|
|
82545
82513
|
kill: (signal) => {
|
|
82546
82514
|
try {
|
|
82547
82515
|
process.kill(-proc.pid, signal);
|
|
82548
82516
|
} catch {}
|
|
82517
|
+
},
|
|
82518
|
+
afterExit: closeNetOnce
|
|
82519
|
+
});
|
|
82520
|
+
};
|
|
82521
|
+
}
|
|
82522
|
+
var EXIT_OUTPUT_DRAIN_MS = 2000;
|
|
82523
|
+
function superviseSpawnedProcess(proc, opts) {
|
|
82524
|
+
const drainMs = opts.drainMs ?? EXIT_OUTPUT_DRAIN_MS;
|
|
82525
|
+
let dataCb;
|
|
82526
|
+
let exitCb;
|
|
82527
|
+
const pendingChunks = [];
|
|
82528
|
+
let pendingExit;
|
|
82529
|
+
const emitData = (chunk, stream) => {
|
|
82530
|
+
if (dataCb === undefined)
|
|
82531
|
+
pendingChunks.push({ chunk, stream });
|
|
82532
|
+
else
|
|
82533
|
+
dataCb(chunk, stream);
|
|
82534
|
+
};
|
|
82535
|
+
const outDecoder = new TextDecoder;
|
|
82536
|
+
const errDecoder = new TextDecoder;
|
|
82537
|
+
const pump = async (stream, kind, decoder) => {
|
|
82538
|
+
if (stream === undefined)
|
|
82539
|
+
return;
|
|
82540
|
+
const reader = stream.getReader();
|
|
82541
|
+
try {
|
|
82542
|
+
for (;; ) {
|
|
82543
|
+
const chunk = await reader.read();
|
|
82544
|
+
if (chunk.done)
|
|
82545
|
+
break;
|
|
82546
|
+
if (chunk.value !== undefined)
|
|
82547
|
+
emitData(decoder.decode(chunk.value, { stream: true }), kind);
|
|
82549
82548
|
}
|
|
82550
|
-
}
|
|
82549
|
+
} catch {} finally {
|
|
82550
|
+
reader.releaseLock();
|
|
82551
|
+
}
|
|
82552
|
+
};
|
|
82553
|
+
const drained = Promise.all([
|
|
82554
|
+
pump(proc.stdout, "stdout", outDecoder),
|
|
82555
|
+
pump(proc.stderr, "stderr", errDecoder)
|
|
82556
|
+
]);
|
|
82557
|
+
proc.exited.then(async (exitCode) => {
|
|
82558
|
+
let timer;
|
|
82559
|
+
await Promise.race([
|
|
82560
|
+
drained,
|
|
82561
|
+
new Promise((resolve) => {
|
|
82562
|
+
timer = setTimeout(resolve, drainMs);
|
|
82563
|
+
timer.unref?.();
|
|
82564
|
+
})
|
|
82565
|
+
]);
|
|
82566
|
+
if (timer !== undefined)
|
|
82567
|
+
clearTimeout(timer);
|
|
82568
|
+
if (exitCb === undefined)
|
|
82569
|
+
pendingExit = { exitCode };
|
|
82570
|
+
else
|
|
82571
|
+
exitCb({ exitCode });
|
|
82572
|
+
await opts.afterExit?.();
|
|
82573
|
+
});
|
|
82574
|
+
return {
|
|
82575
|
+
pid: proc.pid,
|
|
82576
|
+
onOutput: (cb) => {
|
|
82577
|
+
dataCb = cb;
|
|
82578
|
+
for (const { chunk, stream } of pendingChunks.splice(0))
|
|
82579
|
+
cb(chunk, stream);
|
|
82580
|
+
},
|
|
82581
|
+
onExit: (cb) => {
|
|
82582
|
+
exitCb = cb;
|
|
82583
|
+
if (pendingExit !== undefined)
|
|
82584
|
+
cb(pendingExit);
|
|
82585
|
+
},
|
|
82586
|
+
kill: opts.kill
|
|
82551
82587
|
};
|
|
82552
82588
|
}
|
|
82553
82589
|
function createJobRegistry(options) {
|
|
@@ -88889,7 +88925,6 @@ function createBlockNavController(options) {
|
|
|
88889
88925
|
const schedule = options.schedule ?? ((run) => void setTimeout(run, 0));
|
|
88890
88926
|
const onChange = options.onChange ?? (() => {});
|
|
88891
88927
|
let focusOwner = "composer";
|
|
88892
|
-
let savedScrollTop = 0;
|
|
88893
88928
|
const active = () => focusOwner === "blocks";
|
|
88894
88929
|
const paint = (id) => {
|
|
88895
88930
|
const state = registry.get(id);
|
|
@@ -88977,7 +89012,6 @@ function createBlockNavController(options) {
|
|
|
88977
89012
|
}
|
|
88978
89013
|
focusOwner = "blocks";
|
|
88979
89014
|
registry.focus(target.id);
|
|
88980
|
-
savedScrollTop = scroll.scrollTop;
|
|
88981
89015
|
scroll.stickyScroll = false;
|
|
88982
89016
|
blurComposer();
|
|
88983
89017
|
paintAll();
|
|
@@ -88990,8 +89024,12 @@ function createBlockNavController(options) {
|
|
|
88990
89024
|
}
|
|
88991
89025
|
focusOwner = "composer";
|
|
88992
89026
|
paintAll();
|
|
88993
|
-
scroll.
|
|
88994
|
-
|
|
89027
|
+
if (typeof scroll.scrollHeight === "number" && typeof scroll.height === "number") {
|
|
89028
|
+
const atBottom = scroll.scrollTop >= scroll.scrollHeight - scroll.height - 3;
|
|
89029
|
+
scroll.stickyScroll = atBottom;
|
|
89030
|
+
} else {
|
|
89031
|
+
scroll.stickyScroll = true;
|
|
89032
|
+
}
|
|
88995
89033
|
focusComposer();
|
|
88996
89034
|
onChange();
|
|
88997
89035
|
};
|
|
@@ -89073,7 +89111,7 @@ function onKeypress(r, handler) {
|
|
|
89073
89111
|
}
|
|
89074
89112
|
var BACKDROP_ID = "modal-backdrop";
|
|
89075
89113
|
var PANEL_ID = "modal-panel";
|
|
89076
|
-
var BACKDROP_ALPHA =
|
|
89114
|
+
var BACKDROP_ALPHA = 1;
|
|
89077
89115
|
function backdropFillColor(otui) {
|
|
89078
89116
|
const color = otui.RGBA.fromHex(getTheme().bg);
|
|
89079
89117
|
color.a = BACKDROP_ALPHA;
|
|
@@ -89084,13 +89122,23 @@ var MODAL_PANEL_CHROME_X = 4;
|
|
|
89084
89122
|
var MODAL_PANEL_MIN_WIDTH = 72;
|
|
89085
89123
|
var MODAL_PANEL_MIN_HEIGHT = 18;
|
|
89086
89124
|
var MODAL_PANEL_SIZE_RATIO = 0.95;
|
|
89125
|
+
var MODAL_PANEL_MAX_HEIGHT_RATIO = 0.85;
|
|
89087
89126
|
var MODAL_CHROME_ROWS = 5;
|
|
89088
89127
|
var MODAL_PANEL_WIDTH = MODAL_PANEL_MIN_WIDTH;
|
|
89089
89128
|
var MODAL_PANEL_HEIGHT = MODAL_PANEL_MIN_HEIGHT;
|
|
89090
89129
|
var MODAL_PANEL_INNER_WIDTH = MODAL_PANEL_MIN_WIDTH - MODAL_PANEL_CHROME_X;
|
|
89091
|
-
function resolveModalPanelSize(cols, rows) {
|
|
89130
|
+
function resolveModalPanelSize(cols, rows, contentRows) {
|
|
89131
|
+
const width = Math.max(MODAL_PANEL_MIN_WIDTH, Math.round(cols * MODAL_PANEL_SIZE_RATIO));
|
|
89132
|
+
if (contentRows !== undefined) {
|
|
89133
|
+
const maxHeight = Math.max(6, Math.round(rows * MODAL_PANEL_MAX_HEIGHT_RATIO));
|
|
89134
|
+
const needed = Math.max(6, contentRows + MODAL_CHROME_ROWS);
|
|
89135
|
+
return {
|
|
89136
|
+
width,
|
|
89137
|
+
height: Math.min(maxHeight, needed)
|
|
89138
|
+
};
|
|
89139
|
+
}
|
|
89092
89140
|
return {
|
|
89093
|
-
width
|
|
89141
|
+
width,
|
|
89094
89142
|
height: Math.max(MODAL_PANEL_MIN_HEIGHT, Math.round(rows * MODAL_PANEL_SIZE_RATIO))
|
|
89095
89143
|
};
|
|
89096
89144
|
}
|
|
@@ -89098,6 +89146,19 @@ function modalBodyRows(panelHeight) {
|
|
|
89098
89146
|
return Math.max(1, panelHeight - MODAL_CHROME_ROWS);
|
|
89099
89147
|
}
|
|
89100
89148
|
var CLOSE_HINT = "[x] esc";
|
|
89149
|
+
var UNMEASURED_SIDEBAR_WIDTH = 30;
|
|
89150
|
+
function resolveModalAvailableWidth(chrome) {
|
|
89151
|
+
const rWidth = chrome.renderer.width;
|
|
89152
|
+
const mainWidth = chrome.main?.width ?? 0;
|
|
89153
|
+
if (mainWidth > 0) {
|
|
89154
|
+
return Math.min(rWidth, mainWidth);
|
|
89155
|
+
}
|
|
89156
|
+
if (chrome.sidebar !== undefined) {
|
|
89157
|
+
const sidebarWidth = chrome.sidebar.width > 0 ? chrome.sidebar.width : UNMEASURED_SIDEBAR_WIDTH;
|
|
89158
|
+
return Math.max(20, rWidth - sidebarWidth);
|
|
89159
|
+
}
|
|
89160
|
+
return rWidth;
|
|
89161
|
+
}
|
|
89101
89162
|
function resolveModalInnerWidth(availableWidth) {
|
|
89102
89163
|
return Math.max(20, availableWidth - MODAL_PANEL_CHROME_X);
|
|
89103
89164
|
}
|
|
@@ -89181,9 +89242,11 @@ function mountTab(state, input, tabId) {
|
|
|
89181
89242
|
unmountActiveTab(state);
|
|
89182
89243
|
state.active = tabId;
|
|
89183
89244
|
paintTabs(state);
|
|
89184
|
-
const
|
|
89245
|
+
const availWidth = resolveModalAvailableWidth(state.chrome);
|
|
89246
|
+
const size = resolveModalPanelSize(availWidth, state.chrome.renderer.height, input.contentRows);
|
|
89247
|
+
const panelWidth = Math.min(availWidth, size.width);
|
|
89185
89248
|
const cleanup = input.renderTab(tabId, state.body, {
|
|
89186
|
-
width: resolveModalInnerWidth(
|
|
89249
|
+
width: resolveModalInnerWidth(panelWidth),
|
|
89187
89250
|
height: modalBodyRows(size.height)
|
|
89188
89251
|
});
|
|
89189
89252
|
state.tabCleanup = typeof cleanup === "function" ? cleanup : undefined;
|
|
@@ -89305,7 +89368,8 @@ function ensureHost(otui, chrome) {
|
|
|
89305
89368
|
panel.add(scroll);
|
|
89306
89369
|
panel.add(footer);
|
|
89307
89370
|
backdrop.add(panel);
|
|
89308
|
-
r.root
|
|
89371
|
+
const mountTarget = chrome.main ?? r.root;
|
|
89372
|
+
mountTarget.add(backdrop);
|
|
89309
89373
|
const state = {
|
|
89310
89374
|
otui,
|
|
89311
89375
|
chrome,
|
|
@@ -89425,8 +89489,9 @@ function openModal(otui, chrome, input) {
|
|
|
89425
89489
|
return;
|
|
89426
89490
|
}
|
|
89427
89491
|
const state = ensureHost(otui, chrome);
|
|
89428
|
-
const
|
|
89429
|
-
|
|
89492
|
+
const availWidth = resolveModalAvailableWidth(chrome);
|
|
89493
|
+
const size = resolveModalPanelSize(availWidth, chrome.renderer.height, input.contentRows);
|
|
89494
|
+
state.panel.width = Math.min(availWidth, size.width);
|
|
89430
89495
|
state.panel.height = size.height;
|
|
89431
89496
|
if (state.open) {
|
|
89432
89497
|
unmountActiveTab(state);
|
|
@@ -89448,7 +89513,10 @@ function openModal(otui, chrome, input) {
|
|
|
89448
89513
|
paintHeader(state, input.title);
|
|
89449
89514
|
paintFooter(state, input.footer);
|
|
89450
89515
|
mountTab(state, input, resolveInitialTab(input.tabs, input.initialTab));
|
|
89451
|
-
|
|
89516
|
+
const focused = chrome.renderer.currentFocusedRenderable;
|
|
89517
|
+
if (focused === null || !containsNode(state.scroll, focused)) {
|
|
89518
|
+
state.tabStrip.focus();
|
|
89519
|
+
}
|
|
89452
89520
|
return makeHandle(state, generation);
|
|
89453
89521
|
}
|
|
89454
89522
|
|
|
@@ -90760,6 +90828,10 @@ var REVIEW_FOOTER = [
|
|
|
90760
90828
|
{ key: "\u2191/\u2193", label: "scroll" },
|
|
90761
90829
|
{ key: "esc", label: "close" }
|
|
90762
90830
|
];
|
|
90831
|
+
var REVIEW_EMPTY_FOOTER = [
|
|
90832
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
90833
|
+
{ key: "esc", label: "close" }
|
|
90834
|
+
];
|
|
90763
90835
|
function isReviewCommand(line) {
|
|
90764
90836
|
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
90765
90837
|
return token === REVIEW_COMMAND;
|
|
@@ -91061,6 +91133,7 @@ function presentReview(openModal, otui, chrome, options) {
|
|
|
91061
91133
|
paintSelection();
|
|
91062
91134
|
});
|
|
91063
91135
|
};
|
|
91136
|
+
const isCompactEmpty = items.length === 0;
|
|
91064
91137
|
const handle = openModal(otui, chrome, {
|
|
91065
91138
|
title: REVIEW_COMMAND,
|
|
91066
91139
|
tabs: [
|
|
@@ -91068,7 +91141,8 @@ function presentReview(openModal, otui, chrome, options) {
|
|
|
91068
91141
|
{ id: "detail", label: "Detail" }
|
|
91069
91142
|
],
|
|
91070
91143
|
initialTab: "list",
|
|
91071
|
-
footer: REVIEW_FOOTER,
|
|
91144
|
+
footer: isCompactEmpty ? REVIEW_EMPTY_FOOTER : REVIEW_FOOTER,
|
|
91145
|
+
...isCompactEmpty ? { contentRows: 3 } : {},
|
|
91072
91146
|
onArrowKeys: (key, direction) => {
|
|
91073
91147
|
if (items[selected]?.type === "proposal" && status.kind !== "running" && status.kind !== "done" && handle?.activeTab() === "detail") {
|
|
91074
91148
|
focusedAction = direction === "left" ? "accept" : "decline";
|
|
@@ -92740,14 +92814,36 @@ function describeUnavailableCommand(line, mode) {
|
|
|
92740
92814
|
return `${command.name} is only available in ${where} \u2014 this is ${mode} mode. ` + `Type /help for the commands available here.
|
|
92741
92815
|
`;
|
|
92742
92816
|
}
|
|
92743
|
-
function renderCommandHelp(mode, only) {
|
|
92817
|
+
function renderCommandHelp(mode, only, maxColumns) {
|
|
92744
92818
|
const allow = only === undefined ? undefined : new Set(only);
|
|
92745
92819
|
const options = commandsForMode(mode).filter((c) => allow === undefined || allow.has(c.name));
|
|
92746
92820
|
const width = options.reduce((w, c) => Math.max(w, c.name.length), 0);
|
|
92747
|
-
|
|
92821
|
+
const descColumn = width + 4;
|
|
92822
|
+
const wrapBudget = maxColumns !== undefined && maxColumns - descColumn >= MIN_HELP_WRAP_COLUMNS ? maxColumns - descColumn : undefined;
|
|
92823
|
+
const rows = options.flatMap((c) => {
|
|
92824
|
+
const lines = c.description.split(`
|
|
92825
|
+
`).flatMap((line) => wrapBudget === undefined ? [line] : wrapWords(line, wrapBudget));
|
|
92826
|
+
return lines.map((line, i) => (i === 0 ? ` ${c.name.padEnd(width)} ${line}` : `${" ".repeat(descColumn)}${line}`).trimEnd());
|
|
92827
|
+
});
|
|
92828
|
+
return ["Commands:", ...rows].join(`
|
|
92748
92829
|
`) + `
|
|
92749
92830
|
`;
|
|
92750
92831
|
}
|
|
92832
|
+
var MIN_HELP_WRAP_COLUMNS = 10;
|
|
92833
|
+
function wrapWords(line, budget) {
|
|
92834
|
+
const out = [];
|
|
92835
|
+
let cur = "";
|
|
92836
|
+
for (const word of line.split(/\s+/).filter(Boolean)) {
|
|
92837
|
+
if (cur.length > 0 && cur.length + 1 + word.length > budget) {
|
|
92838
|
+
out.push(cur);
|
|
92839
|
+
cur = word;
|
|
92840
|
+
} else {
|
|
92841
|
+
cur = cur.length > 0 ? `${cur} ${word}` : word;
|
|
92842
|
+
}
|
|
92843
|
+
}
|
|
92844
|
+
out.push(cur);
|
|
92845
|
+
return out;
|
|
92846
|
+
}
|
|
92751
92847
|
|
|
92752
92848
|
// src/tui/theme-picker.ts
|
|
92753
92849
|
function isThemeCommand(line) {
|
|
@@ -95100,6 +95196,10 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
95100
95196
|
syncComposerHeight();
|
|
95101
95197
|
const submitHandlers = new Set;
|
|
95102
95198
|
const emitSubmit = (line) => {
|
|
95199
|
+
if (line.length > 0) {
|
|
95200
|
+
scroll.scrollTop = scroll.scrollHeight;
|
|
95201
|
+
scroll.stickyScroll = true;
|
|
95202
|
+
}
|
|
95103
95203
|
for (const handler of [...submitHandlers]) {
|
|
95104
95204
|
handler(line);
|
|
95105
95205
|
}
|
|
@@ -98524,7 +98624,12 @@ function pickProviderStep(otui, r, detected) {
|
|
|
98524
98624
|
});
|
|
98525
98625
|
});
|
|
98526
98626
|
}
|
|
98527
|
-
function
|
|
98627
|
+
function isModalChrome(target) {
|
|
98628
|
+
return target !== null && typeof target === "object" && "renderer" in target && "focusComposer" in target;
|
|
98629
|
+
}
|
|
98630
|
+
function selectProviderModelInTui(otui, rOrChrome, detected, options = {}) {
|
|
98631
|
+
const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
|
|
98632
|
+
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
98528
98633
|
return new Promise((resolve) => {
|
|
98529
98634
|
if (detected.length === 0) {
|
|
98530
98635
|
resolve(undefined);
|
|
@@ -98657,7 +98762,9 @@ function selectProviderModelInTui(otui, r, detected, options = {}) {
|
|
|
98657
98762
|
models = await modelsForPicker(selectedProvider, modelDeps);
|
|
98658
98763
|
}
|
|
98659
98764
|
}
|
|
98660
|
-
const model = await pickModelInTui(otui, r, models.models, modelPickerNotice(label, models)
|
|
98765
|
+
const model = await pickModelInTui(otui, chrome ?? r, models.models, modelPickerNotice(label, models), {
|
|
98766
|
+
escLabel: "back"
|
|
98767
|
+
});
|
|
98661
98768
|
if (model === undefined) {
|
|
98662
98769
|
continue;
|
|
98663
98770
|
}
|
|
@@ -98671,49 +98778,120 @@ function adaptiveSelectHeight(count, available, per = 1) {
|
|
|
98671
98778
|
const min = Math.max(1, Math.floor(available / 4));
|
|
98672
98779
|
return Math.min(available, Math.max(min, count * per));
|
|
98673
98780
|
}
|
|
98674
|
-
function pickModelInTui(otui,
|
|
98781
|
+
function pickModelInTui(otui, rOrChrome, models, notice, options = {}) {
|
|
98782
|
+
const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
|
|
98783
|
+
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
98784
|
+
const showNotice = models.length === 0 && notice !== undefined;
|
|
98785
|
+
const noticeText = () => new otui.TextRenderable(r, { id: "mp-notice", content: otui.t`${otui.red("\u2717")} ${notice ?? ""}` });
|
|
98786
|
+
const listSpec = {
|
|
98787
|
+
idPrefix: "mp",
|
|
98788
|
+
items: models,
|
|
98789
|
+
toOption: (m) => ({ name: m, description: "" }),
|
|
98790
|
+
matches: (m, q) => m.toLowerCase().includes(q),
|
|
98791
|
+
emptyLabel: notice ?? "(no models found)",
|
|
98792
|
+
filterHint: (filter, shown, total) => `filter: ${filter} (${shown}/${total})`,
|
|
98793
|
+
showDescription: false
|
|
98794
|
+
};
|
|
98795
|
+
const fixedRows = 1 + (showNotice ? 1 : 0);
|
|
98796
|
+
if (chrome !== undefined) {
|
|
98797
|
+
return new Promise((resolve) => {
|
|
98798
|
+
let chosen;
|
|
98799
|
+
const handle = openModal(otui, chrome, {
|
|
98800
|
+
title: "Select a model",
|
|
98801
|
+
tabs: [{ id: "models", label: "Models" }],
|
|
98802
|
+
footer: [
|
|
98803
|
+
{ key: "\u2191/\u2193", label: "select" },
|
|
98804
|
+
{ key: "Enter", label: "confirm" },
|
|
98805
|
+
{ key: "esc", label: options.escLabel ?? "close" }
|
|
98806
|
+
],
|
|
98807
|
+
contentRows: fixedRows + Math.max(1, models.length),
|
|
98808
|
+
renderTab: (_tabId, body, ctx) => {
|
|
98809
|
+
const parent = body;
|
|
98810
|
+
if (showNotice) {
|
|
98811
|
+
parent.add(noticeText());
|
|
98812
|
+
}
|
|
98813
|
+
const list = mountFilterList(otui, r, parent, {
|
|
98814
|
+
...listSpec,
|
|
98815
|
+
idleHint: "type to filter by name",
|
|
98816
|
+
width: "100%",
|
|
98817
|
+
height: adaptiveSelectHeight(models.length, Math.max(1, ctx.height - fixedRows)),
|
|
98818
|
+
onPick: (m) => {
|
|
98819
|
+
chosen = m;
|
|
98820
|
+
handle?.close();
|
|
98821
|
+
}
|
|
98822
|
+
});
|
|
98823
|
+
return onKeypress4(r, list.onKey);
|
|
98824
|
+
},
|
|
98825
|
+
onClose: () => resolve(chosen)
|
|
98826
|
+
});
|
|
98827
|
+
if (handle === undefined) {
|
|
98828
|
+
resolve(undefined);
|
|
98829
|
+
}
|
|
98830
|
+
});
|
|
98831
|
+
}
|
|
98675
98832
|
return new Promise((resolve) => {
|
|
98676
|
-
const all = models;
|
|
98677
|
-
const NO_MODELS = notice ?? "(no models found)";
|
|
98678
98833
|
const box = overlayBox(otui, r, "model-picker");
|
|
98679
98834
|
r.root.add(box);
|
|
98680
98835
|
box.add(new otui.TextRenderable(r, { id: "mp-title", content: otui.t`${otui.bold("Select a model")}` }));
|
|
98681
|
-
if (
|
|
98682
|
-
box.add(
|
|
98836
|
+
if (showNotice) {
|
|
98837
|
+
box.add(noticeText());
|
|
98683
98838
|
}
|
|
98684
|
-
const filterLine = new otui.TextRenderable(r, { id: "mp-filter", content: otui.t`${otui.dim("type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to go back")}` });
|
|
98685
|
-
box.add(filterLine);
|
|
98686
|
-
const NO_MATCH = "(no match)";
|
|
98687
98839
|
const rHeight = r.height;
|
|
98688
98840
|
const available = typeof rHeight === "number" && rHeight > 0 ? Math.max(4, rHeight - 4) : 16;
|
|
98689
|
-
|
|
98690
|
-
const
|
|
98691
|
-
|
|
98841
|
+
let unsub = () => {};
|
|
98842
|
+
const finish = (m) => {
|
|
98843
|
+
unsub();
|
|
98844
|
+
r.root.remove(box);
|
|
98845
|
+
resolve(m);
|
|
98846
|
+
};
|
|
98847
|
+
const list = mountFilterList(otui, r, box, {
|
|
98848
|
+
...listSpec,
|
|
98849
|
+
idleHint: "type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to go back",
|
|
98692
98850
|
width: 72,
|
|
98693
|
-
|
|
98694
|
-
|
|
98695
|
-
showScrollIndicator: true,
|
|
98696
|
-
wrapSelection: true,
|
|
98697
|
-
options: (all.length > 0 ? all : [NO_MODELS]).map((m) => ({ name: m, description: "" })),
|
|
98698
|
-
selectedTextColor: "#ffd166"
|
|
98851
|
+
height: adaptiveSelectHeight(models.length, available),
|
|
98852
|
+
onPick: finish
|
|
98699
98853
|
});
|
|
98700
|
-
|
|
98701
|
-
sel.focus();
|
|
98702
|
-
let filter = "";
|
|
98703
|
-
const apply = () => {
|
|
98704
|
-
const q = filter.trim().toLowerCase();
|
|
98705
|
-
const matches = q.length > 0 ? all.filter((m) => m.toLowerCase().includes(q)) : all;
|
|
98706
|
-
sel.options = matches.length > 0 ? matches.map((m) => ({ name: m, description: "" })) : [{ name: NO_MATCH, description: "" }];
|
|
98707
|
-
filterLine.content = otui.t`${otui.dim(q.length > 0 ? `filter: ${filter} (${matches.length}/${all.length})` : "type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to go back")}`;
|
|
98708
|
-
};
|
|
98709
|
-
const onKey = (key) => {
|
|
98854
|
+
unsub = onKeypress4(r, (key) => {
|
|
98710
98855
|
if (key.name === "escape") {
|
|
98711
|
-
|
|
98712
|
-
resolve(undefined);
|
|
98856
|
+
finish(undefined);
|
|
98713
98857
|
key.preventDefault();
|
|
98714
98858
|
key.stopPropagation();
|
|
98715
98859
|
return;
|
|
98716
98860
|
}
|
|
98861
|
+
list.onKey(key);
|
|
98862
|
+
});
|
|
98863
|
+
});
|
|
98864
|
+
}
|
|
98865
|
+
function mountFilterList(otui, r, parent, spec) {
|
|
98866
|
+
const filterLine = new otui.TextRenderable(r, { id: `${spec.idPrefix}-filter`, content: "" });
|
|
98867
|
+
parent.add(filterLine);
|
|
98868
|
+
const sel = new otui.SelectRenderable(r, {
|
|
98869
|
+
id: `${spec.idPrefix}-sel`,
|
|
98870
|
+
width: spec.width,
|
|
98871
|
+
showDescription: spec.showDescription,
|
|
98872
|
+
height: spec.height,
|
|
98873
|
+
showScrollIndicator: true,
|
|
98874
|
+
wrapSelection: true,
|
|
98875
|
+
options: [],
|
|
98876
|
+
selectedTextColor: "#ffd166"
|
|
98877
|
+
});
|
|
98878
|
+
parent.add(sel);
|
|
98879
|
+
sel.focus();
|
|
98880
|
+
let filter = "";
|
|
98881
|
+
let shown = spec.items;
|
|
98882
|
+
const apply = () => {
|
|
98883
|
+
const q = filter.trim().toLowerCase();
|
|
98884
|
+
shown = q.length > 0 ? spec.items.filter((item) => spec.matches(item, q)) : spec.items;
|
|
98885
|
+
sel.options = shown.length > 0 ? shown.map(spec.toOption) : [{ name: spec.items.length === 0 ? spec.emptyLabel : "(no match)", description: "" }];
|
|
98886
|
+
sel.selectedIndex = 0;
|
|
98887
|
+
filterLine.content = otui.t`${otui.dim(q.length > 0 ? spec.filterHint(filter, shown.length, spec.items.length) : spec.idleHint)}`;
|
|
98888
|
+
};
|
|
98889
|
+
apply();
|
|
98890
|
+
sel.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98891
|
+
spec.onPick(shown[sel.getSelectedIndex()]);
|
|
98892
|
+
});
|
|
98893
|
+
return {
|
|
98894
|
+
onKey: (key) => {
|
|
98717
98895
|
if (key.name === "backspace") {
|
|
98718
98896
|
filter = filter.slice(0, -1);
|
|
98719
98897
|
apply();
|
|
@@ -98728,113 +98906,95 @@ function pickModelInTui(otui, r, models, notice) {
|
|
|
98728
98906
|
key.preventDefault();
|
|
98729
98907
|
key.stopPropagation();
|
|
98730
98908
|
}
|
|
98731
|
-
}
|
|
98732
|
-
|
|
98733
|
-
const cleanup = () => {
|
|
98734
|
-
unsub();
|
|
98735
|
-
r.root.remove(box);
|
|
98736
|
-
};
|
|
98737
|
-
sel.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98738
|
-
const chosen = sel.getSelectedOption();
|
|
98739
|
-
cleanup();
|
|
98740
|
-
resolve(chosen === null || chosen.name === NO_MATCH || chosen.name === NO_MODELS ? undefined : chosen.name);
|
|
98741
|
-
});
|
|
98742
|
-
});
|
|
98909
|
+
}
|
|
98910
|
+
};
|
|
98743
98911
|
}
|
|
98744
98912
|
function formatSessionDate(iso) {
|
|
98745
98913
|
const short = iso.trim().replace("T", " ");
|
|
98746
98914
|
return short.length >= 16 ? short.slice(0, 16) : short;
|
|
98747
98915
|
}
|
|
98748
|
-
function pickSessionInTui(otui,
|
|
98749
|
-
|
|
98750
|
-
|
|
98751
|
-
|
|
98752
|
-
|
|
98753
|
-
|
|
98754
|
-
|
|
98755
|
-
|
|
98756
|
-
|
|
98757
|
-
|
|
98758
|
-
|
|
98759
|
-
|
|
98760
|
-
}
|
|
98916
|
+
function pickSessionInTui(otui, rOrChrome, sessions) {
|
|
98917
|
+
const chrome = isModalChrome(rOrChrome) ? rOrChrome : undefined;
|
|
98918
|
+
const r = chrome !== undefined ? chrome.renderer : rOrChrome;
|
|
98919
|
+
const all = sessions.map((s) => {
|
|
98920
|
+
const created = formatSessionDate(s.createdAt);
|
|
98921
|
+
const updated = formatSessionDate(s.updatedAt);
|
|
98922
|
+
const short = shortSessionId(s.id);
|
|
98923
|
+
const title = s.title.length > 52 ? `${s.title.slice(0, 49)}\u2026` : s.title;
|
|
98924
|
+
return {
|
|
98925
|
+
value: s.id,
|
|
98926
|
+
label: `${short} \xB7 ${title}`,
|
|
98927
|
+
description: `${s.projectPath} \xB7 created ${created} \xB7 updated ${updated}`,
|
|
98928
|
+
search: `${s.id} ${short} ${s.projectPath} ${s.title} ${created} ${updated}`.toLowerCase()
|
|
98929
|
+
};
|
|
98930
|
+
});
|
|
98931
|
+
const listSpec = {
|
|
98932
|
+
idPrefix: "sp",
|
|
98933
|
+
items: all,
|
|
98934
|
+
toOption: (row) => ({ name: row.label, description: row.description }),
|
|
98935
|
+
matches: (row, q) => row.search.includes(q),
|
|
98936
|
+
emptyLabel: "(no match)",
|
|
98937
|
+
filterHint: (filter, shown) => `filter: ${filter} (${shown})`,
|
|
98938
|
+
showDescription: true
|
|
98939
|
+
};
|
|
98940
|
+
const ROWS_PER_SESSION = 2;
|
|
98941
|
+
if (chrome !== undefined) {
|
|
98942
|
+
return new Promise((resolve) => {
|
|
98943
|
+
let chosen;
|
|
98944
|
+
const handle = openModal(otui, chrome, {
|
|
98945
|
+
title: "Session Switcher",
|
|
98946
|
+
tabs: [{ id: "sessions", label: "Sessions" }],
|
|
98947
|
+
footer: [
|
|
98948
|
+
{ key: "\u2191/\u2193", label: "select" },
|
|
98949
|
+
{ key: "Enter", label: "open" },
|
|
98950
|
+
{ key: "esc", label: "cancel" }
|
|
98951
|
+
],
|
|
98952
|
+
contentRows: 1 + Math.max(1, all.length) * ROWS_PER_SESSION,
|
|
98953
|
+
renderTab: (_tabId, body, ctx) => {
|
|
98954
|
+
const list = mountFilterList(otui, r, body, {
|
|
98955
|
+
...listSpec,
|
|
98956
|
+
idleHint: "type to filter by id, title, project, created, updated",
|
|
98957
|
+
width: "100%",
|
|
98958
|
+
height: adaptiveSelectHeight(all.length, Math.max(1, ctx.height - 1), ROWS_PER_SESSION),
|
|
98959
|
+
onPick: (row) => {
|
|
98960
|
+
chosen = row?.value;
|
|
98961
|
+
handle?.close();
|
|
98962
|
+
}
|
|
98963
|
+
});
|
|
98964
|
+
return onKeypress4(r, list.onKey);
|
|
98965
|
+
},
|
|
98966
|
+
onClose: () => resolve(chosen)
|
|
98967
|
+
});
|
|
98968
|
+
if (handle === undefined) {
|
|
98969
|
+
resolve(undefined);
|
|
98970
|
+
}
|
|
98761
98971
|
});
|
|
98972
|
+
}
|
|
98973
|
+
return new Promise((resolve) => {
|
|
98762
98974
|
const box = overlayBox(otui, r, "session-picker");
|
|
98763
98975
|
r.root.add(box);
|
|
98764
98976
|
box.add(new otui.TextRenderable(r, { id: "sp-title", content: otui.t`${otui.bold("Open session")} ${otui.dim("\u2191/\u2193 Enter \xB7 Esc to cancel")}` }));
|
|
98765
|
-
|
|
98766
|
-
|
|
98767
|
-
|
|
98768
|
-
|
|
98769
|
-
|
|
98770
|
-
|
|
98771
|
-
const
|
|
98772
|
-
|
|
98977
|
+
let unsub = () => {};
|
|
98978
|
+
const finish = (row) => {
|
|
98979
|
+
unsub();
|
|
98980
|
+
r.root.remove(box);
|
|
98981
|
+
resolve(row?.value);
|
|
98982
|
+
};
|
|
98983
|
+
const list = mountFilterList(otui, r, box, {
|
|
98984
|
+
...listSpec,
|
|
98985
|
+
idleHint: "type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to cancel",
|
|
98773
98986
|
width: "100%",
|
|
98774
|
-
showDescription: true,
|
|
98775
98987
|
height: 14,
|
|
98776
|
-
|
|
98777
|
-
wrapSelection: true,
|
|
98778
|
-
options: [],
|
|
98779
|
-
selectedTextColor: "#ffd166"
|
|
98988
|
+
onPick: finish
|
|
98780
98989
|
});
|
|
98781
|
-
|
|
98782
|
-
sel.focus();
|
|
98783
|
-
let filter = "";
|
|
98784
|
-
let matches = all;
|
|
98785
|
-
const apply = () => {
|
|
98786
|
-
const q = filter.trim().toLowerCase();
|
|
98787
|
-
matches = q.length > 0 ? all.filter((row) => row.search.includes(q)) : all;
|
|
98788
|
-
const items = matches.length > 0 ? matches : [
|
|
98789
|
-
{
|
|
98790
|
-
value: "",
|
|
98791
|
-
label: NO_MATCH,
|
|
98792
|
-
description: "",
|
|
98793
|
-
search: ""
|
|
98794
|
-
}
|
|
98795
|
-
];
|
|
98796
|
-
sel.options = items.map((row) => ({ name: row.label, description: row.description, value: row.value }));
|
|
98797
|
-
filterLine.content = otui.t`${otui.dim(q.length > 0 ? `filter: ${filter} (${matches.length})` : "type to filter \xB7 \u2191/\u2193 Enter \xB7 Esc to cancel")}`;
|
|
98798
|
-
sel.selectedIndex = 0;
|
|
98799
|
-
};
|
|
98800
|
-
apply();
|
|
98801
|
-
const onKey = (key) => {
|
|
98990
|
+
unsub = onKeypress4(r, (key) => {
|
|
98802
98991
|
if (key.name === "escape") {
|
|
98803
|
-
|
|
98804
|
-
resolve(undefined);
|
|
98805
|
-
key.preventDefault();
|
|
98806
|
-
key.stopPropagation();
|
|
98807
|
-
return;
|
|
98808
|
-
}
|
|
98809
|
-
if (key.name === "backspace") {
|
|
98810
|
-
filter = filter.slice(0, -1);
|
|
98811
|
-
apply();
|
|
98992
|
+
finish(undefined);
|
|
98812
98993
|
key.preventDefault();
|
|
98813
98994
|
key.stopPropagation();
|
|
98814
98995
|
return;
|
|
98815
98996
|
}
|
|
98816
|
-
|
|
98817
|
-
if (!key.ctrl && !key.meta && typeof ch === "string" && ch.length === 1 && ch >= " ") {
|
|
98818
|
-
filter += ch;
|
|
98819
|
-
apply();
|
|
98820
|
-
key.preventDefault();
|
|
98821
|
-
key.stopPropagation();
|
|
98822
|
-
}
|
|
98823
|
-
};
|
|
98824
|
-
const unsub = onKeypress4(r, onKey);
|
|
98825
|
-
const cleanup = () => {
|
|
98826
|
-
unsub();
|
|
98827
|
-
r.root.remove(box);
|
|
98828
|
-
};
|
|
98829
|
-
sel.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
|
|
98830
|
-
const chosen = sel.getSelectedOption();
|
|
98831
|
-
cleanup();
|
|
98832
|
-
if (chosen === null || chosen.value === "" || chosen.name === NO_MATCH) {
|
|
98833
|
-
resolve(undefined);
|
|
98834
|
-
return;
|
|
98835
|
-
}
|
|
98836
|
-
const matched = matches.find((row) => row.value === chosen.value);
|
|
98837
|
-
resolve(matched?.value);
|
|
98997
|
+
list.onKey(key);
|
|
98838
98998
|
});
|
|
98839
98999
|
});
|
|
98840
99000
|
}
|
|
@@ -99554,7 +99714,12 @@ async function launchTuiAgentShell(opts) {
|
|
|
99554
99714
|
return chosen;
|
|
99555
99715
|
};
|
|
99556
99716
|
setAskUserHost(askUserInteractive);
|
|
99557
|
-
const
|
|
99717
|
+
const TRANSCRIPT_CHROME_COLS = 4;
|
|
99718
|
+
const HELP_MIN_COLS = 40;
|
|
99719
|
+
const helpText = () => {
|
|
99720
|
+
const mainWidth = chrome.main.width > 0 ? chrome.main.width : r.width - SIDEBAR_WIDTH;
|
|
99721
|
+
return renderCommandHelp("agent", undefined, Math.max(HELP_MIN_COLS, mainWidth - TRANSCRIPT_CHROME_COLS));
|
|
99722
|
+
};
|
|
99558
99723
|
const sessionCwd = opts.session?.cwd ?? process.cwd();
|
|
99559
99724
|
let permissionMode = opts.initialPermissionMode ?? getProjectPermissionMode(sessionCwd) ?? DEFAULT_PERMISSION_MODE;
|
|
99560
99725
|
io.permissionMode = () => permissionMode;
|
|
@@ -99601,7 +99766,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
99601
99766
|
return;
|
|
99602
99767
|
}
|
|
99603
99768
|
chrome.hideMenu();
|
|
99604
|
-
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui,
|
|
99769
|
+
const pickId = await chrome.withOverlay(() => pickSessionInTui(otui, chrome, rows));
|
|
99605
99770
|
input.focus();
|
|
99606
99771
|
if (pickId === undefined) {
|
|
99607
99772
|
return;
|
|
@@ -100931,7 +101096,7 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
100931
101096
|
const prov = detected.find((d) => d.name === currentSel.provider);
|
|
100932
101097
|
const models = prov !== undefined ? await modelsForPicker(prov) : undefined;
|
|
100933
101098
|
const notice = prov === undefined || models === undefined ? undefined : modelPickerNotice(prov.label ?? prov.name, models);
|
|
100934
|
-
const chosen = await chrome.withOverlay(() => pickModelInTui(otui,
|
|
101099
|
+
const chosen = await chrome.withOverlay(() => pickModelInTui(otui, chrome, models?.models ?? [], notice));
|
|
100935
101100
|
if (chosen !== undefined) {
|
|
100936
101101
|
await switchTo(currentSel.baseUrl === undefined ? { provider: currentSel.provider, model: chosen } : { provider: currentSel.provider, model: chosen, baseUrl: currentSel.baseUrl });
|
|
100937
101102
|
try {
|
|
@@ -100968,7 +101133,7 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
100968
101133
|
if (command.name === "/connect" || command.name === "/provider") {
|
|
100969
101134
|
(async () => {
|
|
100970
101135
|
const detected = opts.redetect !== undefined ? await opts.redetect() : opts.detected;
|
|
100971
|
-
const ns = await chrome.withOverlay(() => command.name === "/connect" ? selectProviderModelInTui(otui,
|
|
101136
|
+
const ns = await chrome.withOverlay(() => command.name === "/connect" ? selectProviderModelInTui(otui, chrome, detected, { onlyConnected: true, env: process.env }) : selectProviderModelInTui(otui, chrome, detected));
|
|
100972
101137
|
if (ns !== undefined) {
|
|
100973
101138
|
await switchTo(ns);
|
|
100974
101139
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.117",
|
|
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": {
|