@mrciphersmith/keryx 0.2.35 → 0.2.36

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.
Files changed (2) hide show
  1. package/dist/cli.js +573 -87
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -44796,6 +44796,492 @@ class LiveMarkdownBlock {
44796
44796
 
44797
44797
  // src/tui/tui-shell.ts
44798
44798
  import { spawnSync as spawnSync2 } from "child_process";
44799
+ // package.json
44800
+ var package_default = {
44801
+ name: "@mrciphersmith/keryx",
44802
+ version: "0.2.36",
44803
+ description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
44804
+ private: false,
44805
+ publishConfig: {
44806
+ access: "public"
44807
+ },
44808
+ license: "MIT",
44809
+ type: "module",
44810
+ repository: {
44811
+ type: "git",
44812
+ url: "git+ssh://git@github.com/MrCipherSmith/keryx.git"
44813
+ },
44814
+ keywords: [
44815
+ "ai-agents",
44816
+ "coding-agents",
44817
+ "agent-harness",
44818
+ "agent-context",
44819
+ "repository-context",
44820
+ "code-graph",
44821
+ "project-memory",
44822
+ "test-impact-analysis",
44823
+ "developer-tools",
44824
+ "model-context-protocol",
44825
+ "mcp",
44826
+ "claude-code",
44827
+ "cursor",
44828
+ "codex",
44829
+ "cli",
44830
+ "bun"
44831
+ ],
44832
+ bin: {
44833
+ keryx: "./dist/cli.js"
44834
+ },
44835
+ scripts: {
44836
+ keryx: "bun ./src/cli.ts",
44837
+ build: "bun build ./src/cli.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core && bun build ./src/harness/process/sandbox/proxy-worker.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core",
44838
+ prepare: "bun run build",
44839
+ typecheck: "tsc --noEmit",
44840
+ test: "bun test",
44841
+ check: "tsc --noEmit && bun test",
44842
+ "check:doc-links": "bun scripts/check-doc-links.ts",
44843
+ "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
44844
+ },
44845
+ files: [
44846
+ "dist",
44847
+ "docs/requirements/shared-agent-context/schemas",
44848
+ "src/gdgraph",
44849
+ "src/gdskills/bundled",
44850
+ "src/gdskills/contracts",
44851
+ "LICENSE",
44852
+ "README.md",
44853
+ "package.json"
44854
+ ],
44855
+ dependencies: {},
44856
+ optionalDependencies: {
44857
+ "@modelcontextprotocol/sdk": "^1.0.0",
44858
+ "@opentui/core": "^0.4.5",
44859
+ "web-tree-sitter": "^0.22.0"
44860
+ },
44861
+ devDependencies: {
44862
+ "@types/bun": "latest",
44863
+ "@xenova/transformers": "^2.17.2",
44864
+ "bun-types": "latest",
44865
+ typescript: "^5"
44866
+ },
44867
+ engines: {
44868
+ bun: ">=1.1.0"
44869
+ },
44870
+ trustedDependencies: [
44871
+ "protobufjs",
44872
+ "sharp"
44873
+ ]
44874
+ };
44875
+
44876
+ // src/tui/modal-host.ts
44877
+ function onKeypress(r, handler) {
44878
+ r._internalKeyInput.onInternal("keypress", handler);
44879
+ return () => r._internalKeyInput.offInternal("keypress", handler);
44880
+ }
44881
+ var BACKDROP_ID = "modal-backdrop";
44882
+ var PANEL_ID = "modal-panel";
44883
+ var BACKDROP_OPACITY = 0.45;
44884
+ var hosts = new WeakMap;
44885
+ function clearChildren(box) {
44886
+ for (const child of [...box.getChildren()]) {
44887
+ box.remove(child);
44888
+ }
44889
+ }
44890
+ function containsNode(root, node) {
44891
+ if (root === node) {
44892
+ return true;
44893
+ }
44894
+ for (const child of root.getChildren()) {
44895
+ if (containsNode(child, node)) {
44896
+ return true;
44897
+ }
44898
+ }
44899
+ return false;
44900
+ }
44901
+ function resolveInitialTab(tabs, initialTab) {
44902
+ const first = tabs[0];
44903
+ if (first === undefined) {
44904
+ return "";
44905
+ }
44906
+ if (initialTab !== undefined && tabs.some((tab) => tab.id === initialTab)) {
44907
+ return initialTab;
44908
+ }
44909
+ return first.id;
44910
+ }
44911
+ function paintTabs(state) {
44912
+ clearChildren(state.tabStrip);
44913
+ const labels = state.tabs.map((tab) => tab.id === state.active ? `[${tab.label}]` : ` ${tab.label} `).join(" ");
44914
+ state.tabStrip.add(new state.otui.TextRenderable(state.chrome.renderer, {
44915
+ id: "modal-tabs",
44916
+ content: state.otui.t`${state.otui.dim(labels)}`
44917
+ }));
44918
+ }
44919
+ function unmountActiveTab(state) {
44920
+ const cleanup = state.tabCleanup;
44921
+ state.tabCleanup = undefined;
44922
+ if (cleanup !== undefined) {
44923
+ cleanup();
44924
+ }
44925
+ clearChildren(state.body);
44926
+ }
44927
+ function mountTab(state, input2, tabId) {
44928
+ unmountActiveTab(state);
44929
+ state.active = tabId;
44930
+ paintTabs(state);
44931
+ const cleanup = input2.renderTab(tabId, state.body);
44932
+ state.tabCleanup = typeof cleanup === "function" ? cleanup : undefined;
44933
+ }
44934
+ function closeHost(state, opts) {
44935
+ if (!state.open) {
44936
+ return;
44937
+ }
44938
+ unmountActiveTab(state);
44939
+ state.open = false;
44940
+ state.input = undefined;
44941
+ state.backdrop.visible = false;
44942
+ const onClose = state.onClose;
44943
+ state.onClose = undefined;
44944
+ if (opts.restoreFocus) {
44945
+ const scroll = state.chrome.scroll;
44946
+ if (state.savedScrollTop !== undefined && scroll !== undefined) {
44947
+ scroll.scrollTop = state.savedScrollTop;
44948
+ }
44949
+ state.chrome.focusComposer();
44950
+ }
44951
+ if (opts.runOnClose && onClose !== undefined) {
44952
+ onClose();
44953
+ }
44954
+ }
44955
+ function ensureHost(otui, chrome) {
44956
+ const existing = hosts.get(chrome.renderer);
44957
+ if (existing !== undefined) {
44958
+ return existing;
44959
+ }
44960
+ const r = chrome.renderer;
44961
+ const backdrop = new otui.BoxRenderable(r, {
44962
+ id: BACKDROP_ID,
44963
+ position: "absolute",
44964
+ top: 0,
44965
+ left: 0,
44966
+ width: "100%",
44967
+ height: "100%",
44968
+ backgroundColor: "#000000",
44969
+ opacity: BACKDROP_OPACITY,
44970
+ zIndex: 100,
44971
+ flexDirection: "column",
44972
+ justifyContent: "center",
44973
+ alignItems: "center",
44974
+ visible: false
44975
+ });
44976
+ const panel = new otui.BoxRenderable(r, {
44977
+ id: PANEL_ID,
44978
+ width: "80%",
44979
+ maxWidth: 72,
44980
+ maxHeight: "80%",
44981
+ flexShrink: 0,
44982
+ flexDirection: "column",
44983
+ borderStyle: "rounded",
44984
+ border: true,
44985
+ borderColor: "#3a4a4a",
44986
+ backgroundColor: "#0f1b1b",
44987
+ paddingLeft: 1,
44988
+ paddingRight: 1,
44989
+ zIndex: 101
44990
+ });
44991
+ const titleText = new otui.TextRenderable(r, {
44992
+ id: "modal-title",
44993
+ content: ""
44994
+ });
44995
+ const tabStrip = new otui.BoxRenderable(r, {
44996
+ id: "modal-tab-strip",
44997
+ flexShrink: 0,
44998
+ width: "100%",
44999
+ flexDirection: "row",
45000
+ focusable: true
45001
+ });
45002
+ const body = new otui.BoxRenderable(r, {
45003
+ id: "modal-body",
45004
+ width: "100%",
45005
+ minHeight: 1,
45006
+ flexDirection: "column"
45007
+ });
45008
+ panel.add(titleText);
45009
+ panel.add(tabStrip);
45010
+ panel.add(body);
45011
+ backdrop.add(panel);
45012
+ r.root.add(backdrop);
45013
+ const state = {
45014
+ otui,
45015
+ chrome,
45016
+ backdrop,
45017
+ panel,
45018
+ titleText,
45019
+ tabStrip,
45020
+ body,
45021
+ open: false,
45022
+ generation: 0,
45023
+ tabs: [],
45024
+ active: "",
45025
+ tabCleanup: undefined,
45026
+ onClose: undefined,
45027
+ input: undefined,
45028
+ releaseOverlay: undefined,
45029
+ unsubKeys: undefined,
45030
+ savedScrollTop: undefined
45031
+ };
45032
+ state.releaseOverlay = chrome.addOverlaySource(() => state.open);
45033
+ state.unsubKeys = onKeypress(r, (key) => {
45034
+ if (!state.open || state.input === undefined) {
45035
+ return;
45036
+ }
45037
+ if (key.name === "escape") {
45038
+ closeHost(state, { restoreFocus: true, runOnClose: true });
45039
+ key.preventDefault();
45040
+ key.stopPropagation();
45041
+ return;
45042
+ }
45043
+ const idx = state.tabs.findIndex((tab) => tab.id === state.active);
45044
+ const focused = r.currentFocusedRenderable;
45045
+ const onStrip = focused !== null && containsNode(state.tabStrip, focused);
45046
+ if (key.name === "left" || onStrip && key.name === "tab" && key.shift === true) {
45047
+ const prev = idx > 0 ? state.tabs[idx - 1] : undefined;
45048
+ if (prev !== undefined) {
45049
+ mountTab(state, state.input, prev.id);
45050
+ }
45051
+ key.preventDefault();
45052
+ key.stopPropagation();
45053
+ return;
45054
+ }
45055
+ if (key.name === "right" || onStrip && key.name === "tab") {
45056
+ const next = state.tabs[idx + 1];
45057
+ if (next !== undefined) {
45058
+ mountTab(state, state.input, next.id);
45059
+ }
45060
+ key.preventDefault();
45061
+ key.stopPropagation();
45062
+ return;
45063
+ }
45064
+ if (focused !== null && containsNode(state.body, focused)) {
45065
+ return;
45066
+ }
45067
+ const digit = key.sequence.length === 1 ? key.sequence : key.name;
45068
+ if (digit.length === 1 && digit >= "1" && digit <= "9") {
45069
+ const jump = state.tabs[Number(digit) - 1];
45070
+ if (jump !== undefined) {
45071
+ mountTab(state, state.input, jump.id);
45072
+ key.preventDefault();
45073
+ key.stopPropagation();
45074
+ }
45075
+ }
45076
+ });
45077
+ hosts.set(r, state);
45078
+ return state;
45079
+ }
45080
+ function makeHandle(state, generation) {
45081
+ return {
45082
+ close() {
45083
+ if (state.generation !== generation) {
45084
+ return;
45085
+ }
45086
+ closeHost(state, { restoreFocus: true, runOnClose: true });
45087
+ },
45088
+ setTab(id) {
45089
+ if (state.generation !== generation || !state.open || state.input === undefined) {
45090
+ return;
45091
+ }
45092
+ if (!state.tabs.some((tab) => tab.id === id) || id === state.active) {
45093
+ return;
45094
+ }
45095
+ mountTab(state, state.input, id);
45096
+ },
45097
+ activeTab() {
45098
+ return state.active;
45099
+ }
45100
+ };
45101
+ }
45102
+ function openModal(otui, chrome, input2) {
45103
+ if (otui === undefined || chrome === undefined || input2.tabs.length < 1) {
45104
+ return;
45105
+ }
45106
+ const state = ensureHost(otui, chrome);
45107
+ if (state.open) {
45108
+ unmountActiveTab(state);
45109
+ const previousClose = state.onClose;
45110
+ state.onClose = undefined;
45111
+ previousClose?.();
45112
+ } else {
45113
+ state.savedScrollTop = chrome.scroll.scrollTop;
45114
+ chrome.hideMenu();
45115
+ chrome.blurComposer();
45116
+ state.backdrop.visible = true;
45117
+ state.open = true;
45118
+ }
45119
+ state.generation += 1;
45120
+ const generation = state.generation;
45121
+ state.input = input2;
45122
+ state.tabs = input2.tabs;
45123
+ state.onClose = input2.onClose;
45124
+ state.titleText.content = otui.t`${otui.bold(input2.title)}`;
45125
+ mountTab(state, input2, resolveInitialTab(input2.tabs, input2.initialTab));
45126
+ state.tabStrip.focus();
45127
+ return makeHandle(state, generation);
45128
+ }
45129
+
45130
+ // src/tui/session-info.ts
45131
+ var SESSION_INFO_COMMANDS = ["/session-info", "/status", "/info"];
45132
+ var MISSING = "\u2014";
45133
+ function isSessionInfoCommand(line) {
45134
+ const token = line.trim().split(/\s+/)[0] ?? "";
45135
+ return SESSION_INFO_COMMANDS.includes(token);
45136
+ }
45137
+ function displayOrMissing(value) {
45138
+ return value !== undefined && value.length > 0 ? value : MISSING;
45139
+ }
45140
+ function formatUtc(iso) {
45141
+ if (iso === undefined || iso.length === 0) {
45142
+ return MISSING;
45143
+ }
45144
+ const date = new Date(iso);
45145
+ if (Number.isNaN(date.getTime())) {
45146
+ return MISSING;
45147
+ }
45148
+ return `${date.toISOString()} UTC`;
45149
+ }
45150
+ function formatCount(value) {
45151
+ return value === undefined ? MISSING : String(value);
45152
+ }
45153
+ function usageUsed(usage) {
45154
+ if (usage === undefined) {
45155
+ return;
45156
+ }
45157
+ if (usage.totalTokens !== undefined) {
45158
+ return usage.totalTokens;
45159
+ }
45160
+ if (usage.inputTokens === undefined && usage.outputTokens === undefined) {
45161
+ return;
45162
+ }
45163
+ return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
45164
+ }
45165
+ function contextRow(source) {
45166
+ const used = usageUsed(source.usage);
45167
+ if (used !== undefined) {
45168
+ return `${used} tokens`;
45169
+ }
45170
+ if (source.estimateTokens !== undefined) {
45171
+ return `${source.estimateTokens} tokens (estimate)`;
45172
+ }
45173
+ return MISSING;
45174
+ }
45175
+ function buildSessionInfoSnapshot(source) {
45176
+ const id = source.summary?.id ?? "";
45177
+ const provider = source.selection?.provider ?? source.summary?.provider;
45178
+ const model = source.selection?.model ?? source.summary?.model;
45179
+ const sessionRows = [
45180
+ { label: "Title", value: displayOrMissing(source.summary?.title) },
45181
+ { label: "Version", value: displayOrMissing(source.version) },
45182
+ { label: "Session id", value: displayOrMissing(source.summary?.id) },
45183
+ { label: "Project", value: displayOrMissing(source.summary?.projectPath) },
45184
+ { label: "Provider", value: displayOrMissing(provider) },
45185
+ { label: "Model", value: displayOrMissing(model) }
45186
+ ];
45187
+ if (source.summary?.parentSessionId !== undefined && source.summary.parentSessionId.length > 0) {
45188
+ sessionRows.push({ label: "Parent", value: source.summary.parentSessionId });
45189
+ }
45190
+ const messages = source.summary?.messageCount === undefined && source.summary?.archiveMessageCount === undefined ? MISSING : `${formatCount(source.summary.messageCount)} / ${formatCount(source.summary.archiveMessageCount)}`;
45191
+ sessionRows.push({ label: "Created", value: formatUtc(source.summary?.createdAt) }, { label: "Updated", value: formatUtc(source.summary?.updatedAt) }, { label: "Messages", value: messages }, { label: "Compactions", value: formatCount(source.summary?.compactCount) }, { label: "Context", value: contextRow(source) });
45192
+ const estimate = source.estimateTokens === undefined ? MISSING : `${source.estimateTokens} tokens (estimate)`;
45193
+ const usageRows = [
45194
+ {
45195
+ label: "Last turn input",
45196
+ value: source.usage?.inputTokens === undefined ? MISSING : String(source.usage.inputTokens)
45197
+ },
45198
+ {
45199
+ label: "Last turn output",
45200
+ value: source.usage?.outputTokens === undefined ? MISSING : String(source.usage.outputTokens)
45201
+ },
45202
+ { label: "Context estimate", value: estimate }
45203
+ ];
45204
+ return { sessionId: id, sessionRows, usageRows };
45205
+ }
45206
+ function formatSection(title, rows) {
45207
+ const width = rows.reduce((max, row) => Math.max(max, row.label.length), 0);
45208
+ return [title, ...rows.map((row) => ` ${row.label.padEnd(width)} ${row.value}`)].join(`
45209
+ `);
45210
+ }
45211
+ function formatSessionInfoText(snapshot) {
45212
+ return `${formatSection("Session", snapshot.sessionRows)}
45213
+
45214
+ ${formatSection("Usage", snapshot.usageRows)}
45215
+ `;
45216
+ }
45217
+ function sessionIdCopyText(snapshot) {
45218
+ return snapshot.sessionId;
45219
+ }
45220
+ function sessionBlockCopyText(snapshot) {
45221
+ return formatSessionInfoText(snapshot);
45222
+ }
45223
+ function paintRows(otui, renderer, body, rows) {
45224
+ if (otui === undefined || otui === null || body === undefined || body === null) {
45225
+ return;
45226
+ }
45227
+ const parent = body;
45228
+ const ctor = otui.TextRenderable;
45229
+ if (parent.add === undefined || ctor === undefined) {
45230
+ return;
45231
+ }
45232
+ const width = rows.reduce((max, row) => Math.max(max, row.label.length), 0);
45233
+ parent.add(new ctor(renderer, {
45234
+ id: "session-info-body",
45235
+ content: rows.map((row) => `${row.label.padEnd(width)} ${row.value}`).join(`
45236
+ `)
45237
+ }));
45238
+ }
45239
+ function presentSessionInfo(openModal2, otui, chrome, options) {
45240
+ const { snapshot, toast } = options;
45241
+ const copy = (text) => {
45242
+ if (text.length === 0) {
45243
+ return;
45244
+ }
45245
+ try {
45246
+ options.copyText(text);
45247
+ toast("Copied to clipboard");
45248
+ } catch {}
45249
+ };
45250
+ let unsubscribeKey;
45251
+ const handle = openModal2(otui, chrome, {
45252
+ title: "Session",
45253
+ tabs: [
45254
+ { id: "session", label: "Session" },
45255
+ { id: "usage", label: "Usage" }
45256
+ ],
45257
+ initialTab: "session",
45258
+ renderTab: (tabId, body) => {
45259
+ const rows = tabId === "usage" ? snapshot.usageRows : snapshot.sessionRows;
45260
+ const renderer = options.renderer ?? chrome?.renderer;
45261
+ paintRows(otui, renderer, body, rows);
45262
+ },
45263
+ onClose: () => {
45264
+ unsubscribeKey?.();
45265
+ }
45266
+ });
45267
+ if (handle === undefined) {
45268
+ return;
45269
+ }
45270
+ if (options.onKeypress !== undefined) {
45271
+ unsubscribeKey = options.onKeypress((key) => {
45272
+ const token = key.name || key.sequence;
45273
+ if (token === "c") {
45274
+ copy(sessionIdCopyText(snapshot));
45275
+ } else if (token === "y") {
45276
+ copy(sessionBlockCopyText(snapshot));
45277
+ }
45278
+ });
45279
+ }
45280
+ return handle;
45281
+ }
45282
+ function openSessionInfo(otui, chrome, options) {
45283
+ return presentSessionInfo((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
45284
+ }
44799
45285
 
44800
45286
  // src/commands/agent-commands.ts
44801
45287
  var CHAT_ONLY = ["chat"];
@@ -44854,6 +45340,13 @@ var AGENT_SLASH_COMMANDS = [
44854
45340
  { name: "/new", description: "Start a new session (old kept on disk)", modes: BOTH },
44855
45341
  { name: "/resume", description: "Resume a prior session in this project", modes: AGENT_ONLY },
44856
45342
  { name: "/sessions", description: "Open the session list and switch to one", modes: AGENT_ONLY },
45343
+ {
45344
+ name: "/session-info",
45345
+ description: "Show session identity and context usage",
45346
+ modes: BOTH
45347
+ },
45348
+ { name: "/status", description: "Alias of /session-info", modes: BOTH },
45349
+ { name: "/info", description: "Alias of /session-info", modes: BOTH },
44857
45350
  {
44858
45351
  name: "/compact",
44859
45352
  description: "Compact model context \u2014 /compact [focus] (archive kept)",
@@ -45334,7 +45827,7 @@ function selectBoxHeight(count, withDescription) {
45334
45827
  const per = withDescription ? 2 : 1;
45335
45828
  return Math.min(Math.max(count * per, per), 16);
45336
45829
  }
45337
- function onKeypress(r, handler) {
45830
+ function onKeypress2(r, handler) {
45338
45831
  r._internalKeyInput.onInternal("keypress", handler);
45339
45832
  return () => r._internalKeyInput.offInternal("keypress", handler);
45340
45833
  }
@@ -45435,7 +45928,7 @@ function showComposerChoice(otui, r, dock, request) {
45435
45928
  key.stopPropagation();
45436
45929
  }
45437
45930
  };
45438
- const unsub = onKeypress(r, onKey);
45931
+ const unsub = onKeypress2(r, onKey);
45439
45932
  sel.on(otui.SelectRenderableEvents.ITEM_SELECTED, () => {
45440
45933
  const chosen = sel.getSelectedOption();
45441
45934
  const value = chosen?.value;
@@ -45723,7 +46216,7 @@ async function checkVersion(options) {
45723
46216
  }
45724
46217
 
45725
46218
  // src/tui/shell-chrome.ts
45726
- function onKeypress2(r, handler) {
46219
+ function onKeypress3(r, handler) {
45727
46220
  r._internalKeyInput.onInternal("keypress", handler);
45728
46221
  return () => r._internalKeyInput.offInternal("keypress", handler);
45729
46222
  }
@@ -46153,7 +46646,7 @@ async function createShellChrome(otui, renderer, opts) {
46153
46646
  syncComposerHeight();
46154
46647
  emitSubmit(line);
46155
46648
  };
46156
- const unsubscribeMenuKeys = onKeypress2(r, (key) => {
46649
+ const unsubscribeMenuKeys = onKeypress3(r, (key) => {
46157
46650
  if (!menu.visible || !menuNav || overlayActive()) {
46158
46651
  return;
46159
46652
  }
@@ -47601,7 +48094,7 @@ function overlayBox(otui, r, id) {
47601
48094
  padding: 1
47602
48095
  });
47603
48096
  }
47604
- function onKeypress3(r, handler) {
48097
+ function onKeypress4(r, handler) {
47605
48098
  r._internalKeyInput.onInternal("keypress", handler);
47606
48099
  return () => r._internalKeyInput.offInternal("keypress", handler);
47607
48100
  }
@@ -47646,7 +48139,7 @@ function promptBaseUrlStep(otui, r, label, baseUrl2) {
47646
48139
  unsub();
47647
48140
  r.root.remove(box);
47648
48141
  };
47649
- const unsub = onKeypress3(r, (key) => {
48142
+ const unsub = onKeypress4(r, (key) => {
47650
48143
  if (key.name === "escape") {
47651
48144
  cleanup();
47652
48145
  resolve3(undefined);
@@ -47682,7 +48175,7 @@ function promptApiKeyStep(otui, r, opts) {
47682
48175
  key.stopPropagation();
47683
48176
  }
47684
48177
  };
47685
- const unsub = onKeypress3(r, onKey);
48178
+ const unsub = onKeypress4(r, onKey);
47686
48179
  const cleanup = () => {
47687
48180
  unsub();
47688
48181
  r.root.remove(box);
@@ -47722,7 +48215,7 @@ function pickProviderStep(otui, r, detected) {
47722
48215
  key.stopPropagation();
47723
48216
  }
47724
48217
  };
47725
- const unsub = onKeypress3(r, onKey);
48218
+ const unsub = onKeypress4(r, onKey);
47726
48219
  const cleanup = () => {
47727
48220
  unsub();
47728
48221
  r.root.remove(box);
@@ -47840,7 +48333,7 @@ function pickModelInTui(otui, r, models) {
47840
48333
  key.stopPropagation();
47841
48334
  }
47842
48335
  };
47843
- const unsub = onKeypress3(r, onKey);
48336
+ const unsub = onKeypress4(r, onKey);
47844
48337
  const cleanup = () => {
47845
48338
  unsub();
47846
48339
  r.root.remove(box);
@@ -47932,7 +48425,7 @@ function pickSessionInTui(otui, r, sessions) {
47932
48425
  key.stopPropagation();
47933
48426
  }
47934
48427
  };
47935
- const unsub = onKeypress3(r, onKey);
48428
+ const unsub = onKeypress4(r, onKey);
47936
48429
  const cleanup = () => {
47937
48430
  unsub();
47938
48431
  r.root.remove(box);
@@ -48117,6 +48610,12 @@ async function launchTuiAgentShell(opts) {
48117
48610
  hasExactUsage = true;
48118
48611
  }
48119
48612
  });
48613
+ let lastUsage;
48614
+ const recordedUsage = io.onUsage?.bind(io);
48615
+ io.onUsage = (usage) => {
48616
+ lastUsage = usage;
48617
+ recordedUsage?.(usage);
48618
+ };
48120
48619
  attachBlockIo(io, addBlock, {
48121
48620
  onReasoning: () => {
48122
48621
  setBusyPhase("thinking");
@@ -48513,6 +49012,22 @@ Staying in the current session.
48513
49012
  `);
48514
49013
  };
48515
49014
  paintSessionHeader();
49015
+ const showSessionInfo = () => {
49016
+ const snapshot = buildSessionInfoSnapshot({
49017
+ summary: liveSession.summary,
49018
+ selection: currentSel,
49019
+ version: package_default.version,
49020
+ usage: lastUsage,
49021
+ estimateTokens: estimateContextTokens(history)
49022
+ });
49023
+ openSessionInfo(otui, chrome, {
49024
+ snapshot,
49025
+ copyText: (text) => r.copyToClipboardOSC52(text),
49026
+ toast: (message2) => chrome.showToast(message2),
49027
+ renderer: r,
49028
+ onKeypress: (handler) => onKeypress4(r, (key) => handler(key))
49029
+ });
49030
+ };
48516
49031
  const updateModelLabels = () => {
48517
49032
  paintSessionHeader();
48518
49033
  const label = `${currentSel.provider}/${currentSel.model}`;
@@ -48718,6 +49233,10 @@ Staying in the current session.
48718
49233
  `);
48719
49234
  return;
48720
49235
  }
49236
+ if (command2 !== undefined && isSessionInfoCommand(command2.name)) {
49237
+ showSessionInfo();
49238
+ return;
49239
+ }
48721
49240
  if (command2 !== undefined || line.startsWith("/")) {
48722
49241
  transcript.add(new otui.TextRenderable(r, {
48723
49242
  id: `c${uid++}`,
@@ -48859,6 +49378,10 @@ Staying in the current session.
48859
49378
  }
48860
49379
  return;
48861
49380
  }
49381
+ if (isSessionInfoCommand(command.name)) {
49382
+ showSessionInfo();
49383
+ return;
49384
+ }
48862
49385
  if (command.name === "/copy") {
48863
49386
  const target = newestBlock();
48864
49387
  if (target === undefined || !copyBlock(target.id)) {
@@ -49078,7 +49601,7 @@ Staying in the current session.
49078
49601
  focusComposer();
49079
49602
  });
49080
49603
  };
49081
- onKeypress3(r, (key) => {
49604
+ onKeypress4(r, (key) => {
49082
49605
  nav.handleKey(key);
49083
49606
  });
49084
49607
  chrome.onSubmit((line) => {
@@ -49204,6 +49727,9 @@ function createChatBridge(hooks = {}) {
49204
49727
  close();
49205
49728
  return "exit";
49206
49729
  }
49730
+ if (isSessionInfoCommand(value)) {
49731
+ return "local";
49732
+ }
49207
49733
  if ((turn || queue.length > 0) && value.startsWith("/")) {
49208
49734
  return "deferred";
49209
49735
  }
@@ -49327,6 +49853,22 @@ async function mountChatShell(otui, renderer, opts) {
49327
49853
  opts.onExit?.();
49328
49854
  return;
49329
49855
  }
49856
+ if (result === "local") {
49857
+ const snapshot = buildSessionInfoSnapshot({
49858
+ summary: opts.deps.session !== undefined ? latestSession(opts.deps.session.cwd) : undefined,
49859
+ selection,
49860
+ version: package_default.version,
49861
+ estimateTokens: estimateContextTokens(seen)
49862
+ });
49863
+ openSessionInfo(otui, chrome, {
49864
+ snapshot,
49865
+ copyText: (text) => r.copyToClipboardOSC52(text),
49866
+ toast: (message2) => chrome.showToast(message2),
49867
+ renderer: r,
49868
+ onKeypress: (handler) => onKeypress4(r, (key) => handler(key))
49869
+ });
49870
+ return;
49871
+ }
49330
49872
  if (result === "deferred") {
49331
49873
  append(otui.t`${otui.yellow("\u25C7 a reply is still streaming \u2014 command deferred. Wait for it to finish.")}`);
49332
49874
  }
@@ -49413,82 +49955,6 @@ async function launchTuiChatShell(opts) {
49413
49955
 
49414
49956
  // src/commands/shell.ts
49415
49957
  init_shell_config();
49416
- // package.json
49417
- var package_default = {
49418
- name: "@mrciphersmith/keryx",
49419
- version: "0.2.35",
49420
- description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
49421
- private: false,
49422
- publishConfig: {
49423
- access: "public"
49424
- },
49425
- license: "MIT",
49426
- type: "module",
49427
- repository: {
49428
- type: "git",
49429
- url: "git+ssh://git@github.com/MrCipherSmith/keryx.git"
49430
- },
49431
- keywords: [
49432
- "ai-agents",
49433
- "coding-agents",
49434
- "agent-harness",
49435
- "agent-context",
49436
- "repository-context",
49437
- "code-graph",
49438
- "project-memory",
49439
- "test-impact-analysis",
49440
- "developer-tools",
49441
- "model-context-protocol",
49442
- "mcp",
49443
- "claude-code",
49444
- "cursor",
49445
- "codex",
49446
- "cli",
49447
- "bun"
49448
- ],
49449
- bin: {
49450
- keryx: "./dist/cli.js"
49451
- },
49452
- scripts: {
49453
- keryx: "bun ./src/cli.ts",
49454
- build: "bun build ./src/cli.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core && bun build ./src/harness/process/sandbox/proxy-worker.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core",
49455
- prepare: "bun run build",
49456
- typecheck: "tsc --noEmit",
49457
- test: "bun test",
49458
- check: "tsc --noEmit && bun test",
49459
- "check:doc-links": "bun scripts/check-doc-links.ts",
49460
- "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
49461
- },
49462
- files: [
49463
- "dist",
49464
- "docs/requirements/shared-agent-context/schemas",
49465
- "src/gdgraph",
49466
- "src/gdskills/bundled",
49467
- "src/gdskills/contracts",
49468
- "LICENSE",
49469
- "README.md",
49470
- "package.json"
49471
- ],
49472
- dependencies: {},
49473
- optionalDependencies: {
49474
- "@modelcontextprotocol/sdk": "^1.0.0",
49475
- "@opentui/core": "^0.4.5",
49476
- "web-tree-sitter": "^0.22.0"
49477
- },
49478
- devDependencies: {
49479
- "@types/bun": "latest",
49480
- "@xenova/transformers": "^2.17.2",
49481
- "bun-types": "latest",
49482
- typescript: "^5"
49483
- },
49484
- engines: {
49485
- bun: ">=1.1.0"
49486
- },
49487
- trustedDependencies: [
49488
- "protobufjs",
49489
- "sharp"
49490
- ]
49491
- };
49492
49958
 
49493
49959
  // src/commands/select.ts
49494
49960
  init_guard2();
@@ -49727,6 +50193,9 @@ var READLINE_AGENT_COMMANDS = [
49727
50193
  "/new",
49728
50194
  "/clear",
49729
50195
  "/compact",
50196
+ "/session-info",
50197
+ "/status",
50198
+ "/info",
49730
50199
  "/exit"
49731
50200
  ];
49732
50201
  function readlineAgentHelpText() {
@@ -49815,6 +50284,15 @@ Starting a new session.
49815
50284
  system(HELP_TEXT);
49816
50285
  continue;
49817
50286
  }
50287
+ if (isSessionInfoCommand(command)) {
50288
+ system(formatSessionInfoText(buildSessionInfoSnapshot({
50289
+ summary: live?.summary,
50290
+ selection: { provider: providerName, model: modelName },
50291
+ version: package_default.version,
50292
+ estimateTokens: estimateContextTokens(history)
50293
+ })));
50294
+ continue;
50295
+ }
49818
50296
  if (command === "/clear" || command === "/new") {
49819
50297
  if (sessionsOn) {
49820
50298
  live = createSession({ cwd: sessionCwd, provider: providerName, model: modelName });
@@ -50462,6 +50940,14 @@ New session ${shortSessionId(live.summary.id)}.
50462
50940
  }
50463
50941
  if (command === "/help") {
50464
50942
  agentIo.onSystem?.(readlineAgentHelpText());
50943
+ } else if (isSessionInfoCommand(command)) {
50944
+ agentIo.onSystem?.(formatSessionInfoText(buildSessionInfoSnapshot({
50945
+ summary: live?.summary,
50946
+ selection: { provider: deps.providerId, model: deps.modelId },
50947
+ version: package_default.version,
50948
+ usage: lastUsage,
50949
+ estimateTokens: estimateContextTokens(history)
50950
+ })));
50465
50951
  } else if (command === "/expand") {
50466
50952
  const expanded = expandedToolOutput(lastToolName, lastToolOutput);
50467
50953
  if (expanded !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.35",
3
+ "version": "0.2.36",
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": {