@mrciphersmith/keryx 0.2.64 → 0.2.66

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 +476 -81
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -28105,6 +28105,332 @@ function looksLikeUnifiedDiff(text) {
28105
28105
  }
28106
28106
  return false;
28107
28107
  }
28108
+ var CODE_LANG_ALIASES = {
28109
+ js: "javascript",
28110
+ mjs: "javascript",
28111
+ cjs: "javascript",
28112
+ jsx: "javascript",
28113
+ ts: "typescript",
28114
+ tsx: "typescript",
28115
+ py: "python",
28116
+ py3: "python",
28117
+ sh: "bash",
28118
+ shell: "bash",
28119
+ zsh: "bash",
28120
+ console: "bash",
28121
+ yml: "yaml",
28122
+ rb: "ruby",
28123
+ rs: "rust",
28124
+ kt: "kotlin",
28125
+ cs: "csharp",
28126
+ "c++": "cpp",
28127
+ cc: "cpp",
28128
+ cxx: "cpp",
28129
+ hpp: "cpp"
28130
+ };
28131
+ var GENERIC_KEYWORDS = new Set([
28132
+ "if",
28133
+ "else",
28134
+ "for",
28135
+ "while",
28136
+ "return",
28137
+ "function",
28138
+ "class",
28139
+ "const",
28140
+ "let",
28141
+ "var",
28142
+ "import",
28143
+ "export",
28144
+ "from",
28145
+ "true",
28146
+ "false",
28147
+ "null",
28148
+ "new",
28149
+ "this",
28150
+ "try",
28151
+ "catch",
28152
+ "throw",
28153
+ "switch",
28154
+ "case",
28155
+ "break",
28156
+ "continue",
28157
+ "default",
28158
+ "static",
28159
+ "public",
28160
+ "private"
28161
+ ]);
28162
+ var CODE_LANG_KEYWORDS = {
28163
+ javascript: new Set([
28164
+ ...GENERIC_KEYWORDS,
28165
+ "async",
28166
+ "await",
28167
+ "yield",
28168
+ "typeof",
28169
+ "instanceof",
28170
+ "in",
28171
+ "of",
28172
+ "void",
28173
+ "undefined",
28174
+ "super",
28175
+ "extends",
28176
+ "finally",
28177
+ "do",
28178
+ "delete"
28179
+ ]),
28180
+ typescript: new Set([
28181
+ ...GENERIC_KEYWORDS,
28182
+ "async",
28183
+ "await",
28184
+ "yield",
28185
+ "typeof",
28186
+ "instanceof",
28187
+ "in",
28188
+ "of",
28189
+ "void",
28190
+ "undefined",
28191
+ "super",
28192
+ "extends",
28193
+ "implements",
28194
+ "interface",
28195
+ "type",
28196
+ "enum",
28197
+ "namespace",
28198
+ "declare",
28199
+ "readonly",
28200
+ "abstract",
28201
+ "as",
28202
+ "satisfies",
28203
+ "finally",
28204
+ "do",
28205
+ "delete"
28206
+ ]),
28207
+ python: new Set([
28208
+ "def",
28209
+ "return",
28210
+ "if",
28211
+ "elif",
28212
+ "else",
28213
+ "for",
28214
+ "while",
28215
+ "break",
28216
+ "continue",
28217
+ "class",
28218
+ "import",
28219
+ "from",
28220
+ "as",
28221
+ "try",
28222
+ "except",
28223
+ "finally",
28224
+ "raise",
28225
+ "with",
28226
+ "lambda",
28227
+ "yield",
28228
+ "pass",
28229
+ "None",
28230
+ "True",
28231
+ "False",
28232
+ "and",
28233
+ "or",
28234
+ "not",
28235
+ "in",
28236
+ "is",
28237
+ "global",
28238
+ "nonlocal",
28239
+ "assert",
28240
+ "async",
28241
+ "await",
28242
+ "del",
28243
+ "self"
28244
+ ]),
28245
+ bash: new Set([
28246
+ "if",
28247
+ "then",
28248
+ "else",
28249
+ "elif",
28250
+ "fi",
28251
+ "for",
28252
+ "while",
28253
+ "do",
28254
+ "done",
28255
+ "case",
28256
+ "esac",
28257
+ "function",
28258
+ "return",
28259
+ "local",
28260
+ "export",
28261
+ "exit",
28262
+ "break",
28263
+ "continue",
28264
+ "in",
28265
+ "select",
28266
+ "until"
28267
+ ]),
28268
+ go: new Set([
28269
+ "func",
28270
+ "return",
28271
+ "if",
28272
+ "else",
28273
+ "for",
28274
+ "range",
28275
+ "switch",
28276
+ "case",
28277
+ "default",
28278
+ "break",
28279
+ "continue",
28280
+ "package",
28281
+ "import",
28282
+ "var",
28283
+ "const",
28284
+ "type",
28285
+ "struct",
28286
+ "interface",
28287
+ "map",
28288
+ "chan",
28289
+ "go",
28290
+ "defer",
28291
+ "select",
28292
+ "fallthrough",
28293
+ "nil",
28294
+ "true",
28295
+ "false"
28296
+ ]),
28297
+ rust: new Set([
28298
+ "fn",
28299
+ "let",
28300
+ "mut",
28301
+ "return",
28302
+ "if",
28303
+ "else",
28304
+ "for",
28305
+ "while",
28306
+ "loop",
28307
+ "match",
28308
+ "struct",
28309
+ "enum",
28310
+ "impl",
28311
+ "trait",
28312
+ "pub",
28313
+ "use",
28314
+ "mod",
28315
+ "crate",
28316
+ "self",
28317
+ "Self",
28318
+ "super",
28319
+ "const",
28320
+ "static",
28321
+ "async",
28322
+ "await",
28323
+ "move",
28324
+ "ref",
28325
+ "dyn",
28326
+ "where",
28327
+ "unsafe",
28328
+ "true",
28329
+ "false",
28330
+ "None",
28331
+ "Some",
28332
+ "Ok",
28333
+ "Err"
28334
+ ]),
28335
+ json: new Set(["true", "false", "null"])
28336
+ };
28337
+ function codeCommentPrefix(normalizedLang) {
28338
+ switch (normalizedLang) {
28339
+ case "javascript":
28340
+ case "typescript":
28341
+ case "go":
28342
+ case "rust":
28343
+ case "java":
28344
+ case "c":
28345
+ case "cpp":
28346
+ case "csharp":
28347
+ case "swift":
28348
+ case "kotlin":
28349
+ case "scala":
28350
+ case "php":
28351
+ return "//";
28352
+ case "python":
28353
+ case "bash":
28354
+ case "ruby":
28355
+ case "yaml":
28356
+ case "toml":
28357
+ case "r":
28358
+ case "perl":
28359
+ return "#";
28360
+ case "sql":
28361
+ case "lua":
28362
+ case "haskell":
28363
+ return "--";
28364
+ default:
28365
+ return "";
28366
+ }
28367
+ }
28368
+ function normalizeCodeLang(lang) {
28369
+ const key = lang.trim().toLowerCase();
28370
+ return CODE_LANG_ALIASES[key] ?? key;
28371
+ }
28372
+ var CODE_WORD_OR_NUMBER = /[A-Za-z_$][A-Za-z0-9_$]*|\d+(?:\.\d+)?/g;
28373
+ function tokenizeCodeWords(text, keywords) {
28374
+ const tokens = [];
28375
+ let last = 0;
28376
+ CODE_WORD_OR_NUMBER.lastIndex = 0;
28377
+ let m;
28378
+ while ((m = CODE_WORD_OR_NUMBER.exec(text)) !== null) {
28379
+ if (m.index > last) {
28380
+ tokens.push({ kind: "plain", text: text.slice(last, m.index) });
28381
+ }
28382
+ const word = m[0];
28383
+ if (/^[0-9]/.test(word)) {
28384
+ tokens.push({ kind: "number", text: word });
28385
+ } else if (keywords.has(word)) {
28386
+ tokens.push({ kind: "keyword", text: word });
28387
+ } else {
28388
+ tokens.push({ kind: "plain", text: word });
28389
+ }
28390
+ last = m.index + word.length;
28391
+ }
28392
+ if (last < text.length) {
28393
+ tokens.push({ kind: "plain", text: text.slice(last) });
28394
+ }
28395
+ return tokens;
28396
+ }
28397
+ function tokenizeCodeLine(line, lang) {
28398
+ const normalized = normalizeCodeLang(lang);
28399
+ const keywords = CODE_LANG_KEYWORDS[normalized] ?? GENERIC_KEYWORDS;
28400
+ const commentPrefix = codeCommentPrefix(normalized);
28401
+ const tokens = [];
28402
+ let plainBuf = "";
28403
+ const flushPlain = () => {
28404
+ if (plainBuf.length > 0) {
28405
+ tokens.push(...tokenizeCodeWords(plainBuf, keywords));
28406
+ plainBuf = "";
28407
+ }
28408
+ };
28409
+ let i = 0;
28410
+ while (i < line.length) {
28411
+ if (commentPrefix.length > 0 && line.startsWith(commentPrefix, i)) {
28412
+ flushPlain();
28413
+ tokens.push({ kind: "comment", text: line.slice(i) });
28414
+ break;
28415
+ }
28416
+ const ch = line[i];
28417
+ if (ch === '"' || ch === "'" || ch === "`") {
28418
+ flushPlain();
28419
+ let j = i + 1;
28420
+ while (j < line.length && line[j] !== ch) {
28421
+ j += line[j] === "\\" ? 2 : 1;
28422
+ }
28423
+ j = Math.min(j + 1, line.length);
28424
+ tokens.push({ kind: "string", text: line.slice(i, j) });
28425
+ i = j;
28426
+ continue;
28427
+ }
28428
+ plainBuf += ch;
28429
+ i += 1;
28430
+ }
28431
+ flushPlain();
28432
+ return tokens;
28433
+ }
28108
28434
  function payloadKind(lang, _lineCount) {
28109
28435
  const normalized = lang.toLowerCase();
28110
28436
  if (MARKDOWN_LANGS.has(normalized)) {
@@ -28121,6 +28447,18 @@ function blockLabel({ kind, lineCount, collapsed, hint }) {
28121
28447
  const suffix = hint !== undefined && hint.length > 0 ? ` \xB7 ${hint}` : "";
28122
28448
  return `${marker} ${kind} (${lineCount} ${unit})${suffix}`;
28123
28449
  }
28450
+ function summarizeSubmittedLine(line) {
28451
+ const normalized = line.replace(/\r\n/g, `
28452
+ `).replace(/\r/g, `
28453
+ `);
28454
+ const nonEmpty = normalized.split(`
28455
+ `).filter((linePart) => linePart.length > 0);
28456
+ if (nonEmpty.length <= 1) {
28457
+ return line;
28458
+ }
28459
+ const [first, ...rest] = nonEmpty;
28460
+ return `${first} [+ ${rest.length} pasted line${rest.length === 1 ? "" : "s"}]`;
28461
+ }
28124
28462
  var ZERO_WIDTH = /^[\u0300-\u036F\u200B-\u200F\uFE00-\uFE0F\u2060-\u2064]$/u;
28125
28463
  var WIDE_RANGES = [
28126
28464
  [4352, 4447],
@@ -53556,7 +53894,7 @@ import { spawnSync as spawnSync2 } from "child_process";
53556
53894
  // package.json
53557
53895
  var package_default = {
53558
53896
  name: "@mrciphersmith/keryx",
53559
- version: "0.2.64",
53897
+ version: "0.2.66",
53560
53898
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
53561
53899
  private: false,
53562
53900
  publishConfig: {
@@ -53919,7 +54257,7 @@ function diffChunks(otui, text) {
53919
54257
  }
53920
54258
  return out;
53921
54259
  }
53922
- function codeChunks(otui, text) {
54260
+ function flatDimChunks(otui, text) {
53923
54261
  const out = [];
53924
54262
  for (const [index, line] of splitLines(text).entries()) {
53925
54263
  if (index > 0) {
@@ -53930,6 +54268,34 @@ function codeChunks(otui, text) {
53930
54268
  }
53931
54269
  return out;
53932
54270
  }
54271
+ function codeChunks(otui, text, lang) {
54272
+ const out = [];
54273
+ for (const [index, line] of splitLines(text).entries()) {
54274
+ if (index > 0) {
54275
+ out.push(...otui.stringToStyledText(`
54276
+ `).chunks);
54277
+ }
54278
+ for (const token of tokenizeCodeLine(line, lang)) {
54279
+ switch (token.kind) {
54280
+ case "comment":
54281
+ out.push(otui.dim(token.text));
54282
+ break;
54283
+ case "string":
54284
+ out.push(otui.green(token.text));
54285
+ break;
54286
+ case "number":
54287
+ out.push(otui.yellow(token.text));
54288
+ break;
54289
+ case "keyword":
54290
+ out.push(otui.cyan(token.text));
54291
+ break;
54292
+ default:
54293
+ out.push(...otui.stringToStyledText(token.text).chunks);
54294
+ }
54295
+ }
54296
+ }
54297
+ return out;
54298
+ }
53933
54299
  function payloadChunks(otui, text, lang = "") {
53934
54300
  const kind2 = payloadKind(lang, lineCountOf(text));
53935
54301
  if (kind2 === "diff" || looksLikeUnifiedDiff(text)) {
@@ -53938,7 +54304,7 @@ function payloadChunks(otui, text, lang = "") {
53938
54304
  if (kind2 === "markdown" || lang.length === 0) {
53939
54305
  return markdownToChunks(otui, text);
53940
54306
  }
53941
- return codeChunks(otui, text);
54307
+ return codeChunks(otui, text, lang);
53942
54308
  }
53943
54309
  function createSegmentView(otui, renderer, parent, segment) {
53944
54310
  viewSeq += 1;
@@ -53966,7 +54332,7 @@ function createSegmentView(otui, renderer, parent, segment) {
53966
54332
  }
53967
54333
  const tag = (lang, body2) => {
53968
54334
  const n = lineCountOf(body2);
53969
- return `${lang.length > 0 ? lang : "text"} \xB7 ${n} ${n === 1 ? "line" : "lines"}`;
54335
+ return `${lang.length > 0 ? lang : "text"} \xB7 ${n} ${n === 1 ? "line" : "lines"} \xB7 y copy`;
53970
54336
  };
53971
54337
  const frameWidth = (lang, body2) => Math.max(hugWidth(body2, FRAME_CHROME), hugWidth(tag(lang, body2), FRAME_CHROME));
53972
54338
  const frame = new otui.BoxRenderable(renderer, {
@@ -54012,6 +54378,7 @@ function createSegmentView(otui, renderer, parent, segment) {
54012
54378
  var messageSeq = 0;
54013
54379
  function createAssistantMessageStream(otui, renderer, parent) {
54014
54380
  let message2;
54381
+ let lastCode;
54015
54382
  const start = () => {
54016
54383
  messageSeq += 1;
54017
54384
  const container = new otui.BoxRenderable(renderer, {
@@ -54040,6 +54407,11 @@ function createAssistantMessageStream(otui, renderer, parent) {
54040
54407
  stale.destroy();
54041
54408
  }
54042
54409
  m.frozen = frozen;
54410
+ for (const segment of segments2) {
54411
+ if (segment.kind === "code") {
54412
+ lastCode = { lang: segment.lang, body: segment.body };
54413
+ }
54414
+ }
54043
54415
  };
54044
54416
  return {
54045
54417
  push: (chunk) => {
@@ -54077,7 +54449,8 @@ function createAssistantMessageStream(otui, renderer, parent) {
54077
54449
  parent.remove(message2.container);
54078
54450
  } catch {}
54079
54451
  message2 = undefined;
54080
- }
54452
+ },
54453
+ lastCodeSegment: () => lastCode
54081
54454
  };
54082
54455
  }
54083
54456
  function createBlockView(otui, renderer, parent, block, options = {}) {
@@ -54134,7 +54507,7 @@ function createBlockView(otui, renderer, parent, block, options = {}) {
54134
54507
  return;
54135
54508
  }
54136
54509
  const shown = clipBody(text, options.maxLines ?? MAX_BODY_LINES);
54137
- const content = new otui.StyledText(options.dim === true ? codeChunks(otui, shown) : payloadChunks(otui, shown));
54510
+ const content = new otui.StyledText(options.dim === true ? flatDimChunks(otui, shown) : payloadChunks(otui, shown));
54138
54511
  painted = text;
54139
54512
  if (bodyText !== undefined) {
54140
54513
  bodyText.content = content;
@@ -54542,7 +54915,11 @@ function mountTab(state, input2, tabId) {
54542
54915
  unmountActiveTab(state);
54543
54916
  state.active = tabId;
54544
54917
  paintTabs(state);
54545
- const cleanup = input2.renderTab(tabId, state.body, { width: innerWidthOf(state) });
54918
+ const size = resolveModalPanelSize(state.chrome.renderer.width, state.chrome.renderer.height);
54919
+ const cleanup = input2.renderTab(tabId, state.body, {
54920
+ width: resolveModalInnerWidth(size.width),
54921
+ height: modalBodyRows(size.height)
54922
+ });
54546
54923
  state.tabCleanup = typeof cleanup === "function" ? cleanup : undefined;
54547
54924
  }
54548
54925
  function closeHost(state, opts) {
@@ -57575,6 +57952,10 @@ function formatTokens(n) {
57575
57952
 
57576
57953
  // src/tui/games/constants.ts
57577
57954
  var GAME_MODEL_TIMEOUT_MS = 60000;
57955
+ var PROMPT_MIN_ROWS = 5;
57956
+ var PROMPT_MAX_ROWS = 14;
57957
+ var PANEL_FIXED_ROWS = 7;
57958
+ var PANEL_MIN_ROWS = PANEL_FIXED_ROWS + PROMPT_MIN_ROWS;
57578
57959
  var GAMES_FOOTER = [
57579
57960
  { key: "arrows", label: "move" },
57580
57961
  { key: "enter", label: "place" },
@@ -57736,14 +58117,39 @@ var GAME_CELL_GAP = 1;
57736
58117
  var GAME_BOARD_CHROME_X = 4;
57737
58118
  var GAME_CELL_SIZES = {
57738
58119
  large: { width: 9, height: 5 },
57739
- small: { width: 5, height: 3 }
58120
+ medium: { width: 7, height: 4 },
58121
+ small: { width: 5, height: 3 },
58122
+ tiny: { width: 3, height: 2 }
57740
58123
  };
57741
58124
  function gameBoardWidth(cellWidth) {
57742
58125
  return cellWidth * 3 + GAME_CELL_GAP * 2 + GAME_BOARD_CHROME_X;
57743
58126
  }
57744
- function resolveCellSize(availableWidth) {
57745
- const large = GAME_CELL_SIZES.large;
57746
- return gameBoardWidth(large.width) <= availableWidth ? large : GAME_CELL_SIZES.small;
58127
+ function boardRows(cellHeight) {
58128
+ return cellHeight * 3 + 2;
58129
+ }
58130
+ function boardRegionRows(cellHeight) {
58131
+ return boardRows(cellHeight) + 4;
58132
+ }
58133
+ function resolveCellSize(availableWidth, availableHeight) {
58134
+ const candidates = [
58135
+ GAME_CELL_SIZES.large,
58136
+ GAME_CELL_SIZES.medium,
58137
+ GAME_CELL_SIZES.small,
58138
+ GAME_CELL_SIZES.tiny
58139
+ ];
58140
+ for (const size of candidates) {
58141
+ if (gameBoardWidth(size.width) <= availableWidth && boardRegionRows(size.height) <= availableHeight) {
58142
+ return size;
58143
+ }
58144
+ }
58145
+ return GAME_CELL_SIZES.tiny;
58146
+ }
58147
+ function resolveGameBudget(availableWidth, bodyRows) {
58148
+ const boardBudgetRows = Math.max(1, bodyRows - PANEL_MIN_ROWS);
58149
+ const cellSize = resolveCellSize(availableWidth, boardBudgetRows);
58150
+ const boardUsedRows = boardRegionRows(cellSize.height);
58151
+ const promptRows = Math.max(PROMPT_MIN_ROWS, Math.min(PROMPT_MAX_ROWS, bodyRows - PANEL_FIXED_ROWS - boardUsedRows));
58152
+ return { cellSize, boardUsedRows, promptRows, boardBudgetRows };
57747
58153
  }
57748
58154
 
57749
58155
  // src/tui/games/tic-tac-toe/render.ts
@@ -57763,7 +58169,7 @@ function render2(state, ctx) {
57763
58169
  const core = ctx.core;
57764
58170
  const r = ctx.renderer;
57765
58171
  const theme = ctx.theme;
57766
- const cellSize = resolveCellSize(ctx.width);
58172
+ const cellSize = resolveCellSize(ctx.width, ctx.height);
57767
58173
  const wrap2 = new core.BoxRenderable(r, {
57768
58174
  id: "game-wrap",
57769
58175
  width: "100%",
@@ -57925,12 +58331,6 @@ function renderAgentPanel(game, parent, core, renderer, args2) {
57925
58331
  paddingRight: 1,
57926
58332
  ...extra
57927
58333
  });
57928
- const statRow = (owner, label, value, id, valueFg = theme.text) => {
57929
- const rowBox = box({ flexDirection: "row", gap: 1 });
57930
- rowBox.add(text({ content: label, fg: theme.muted }));
57931
- rowBox.add(text({ id, content: value, fg: valueFg }));
57932
- owner.add(rowBox);
57933
- };
57934
58334
  const idle = args2.notice === undefined || args2.notice === "";
57935
58335
  const statusCard = card("game-status-card", { width: "100%", marginTop: 1 });
57936
58336
  statusCard.add(text({
@@ -57938,12 +58338,35 @@ function renderAgentPanel(game, parent, core, renderer, args2) {
57938
58338
  content: args2.modelBusy ? "agent is thinking\u2026" : idle ? "waiting for your move" : args2.notice,
57939
58339
  fg: args2.modelBusy ? theme.focus : idle ? theme.text : theme.error
57940
58340
  }));
58341
+ const lt = args2.lastTurn;
58342
+ const tot = args2.totals;
58343
+ const shownModel = lt !== undefined && lt.provider !== "\u2013" ? truncate4(`${lt.provider}/${lt.model}`, MODEL_MAX) : args2.modelParam;
58344
+ statusCard.add(text({ id: "game-stats-model", content: `model: ${shownModel}`, fg: theme.muted }));
58345
+ const lastCore = lt === undefined ? "no turns yet" : `${formatMs(lt.totalMs)} \xB7 in ${formatTokens(lt.inputTokens)}/out ${formatTokens(lt.outputTokens)}`;
58346
+ const flags = [];
58347
+ if (lt?.reasoning) {
58348
+ flags.push("reasoning");
58349
+ }
58350
+ if (lt?.localFallback) {
58351
+ flags.push("fallback");
58352
+ }
58353
+ if (lt?.error) {
58354
+ flags.push("error");
58355
+ }
58356
+ statusCard.add(text({
58357
+ id: "game-stats-line",
58358
+ content: `last: ${lastCore}${flags.length > 0 ? ` \xB7 ${flags.join(", ")}` : ""} \xB7 session: ${tot.turns} turn${tot.turns === 1 ? "" : "s"} \xB7 ${tot.localFallbacks} fb \xB7 ${tot.errors} err \xB7 in ${formatTokens(tot.inputTokens)}/out ${formatTokens(tot.outputTokens)}`,
58359
+ fg: theme.muted
58360
+ }));
57941
58361
  parent.add(statusCard);
57942
58362
  const sysCard = new core.ScrollBoxRenderable(renderer, {
57943
58363
  id: "game-system-card",
57944
58364
  width: "100%",
57945
- flexGrow: 1,
57946
- minHeight: 0,
58365
+ height: args2.promptRows,
58366
+ minHeight: args2.promptRows,
58367
+ maxHeight: args2.promptRows,
58368
+ flexShrink: 0,
58369
+ flexGrow: 0,
57947
58370
  marginTop: 1,
57948
58371
  border: true,
57949
58372
  borderStyle: "rounded",
@@ -57954,41 +58377,11 @@ function renderAgentPanel(game, parent, core, renderer, args2) {
57954
58377
  scrollY: true,
57955
58378
  contentOptions: { flexDirection: "column" }
57956
58379
  });
58380
+ sysCard.add(text({ id: "game-user-title", content: "your turn prompt (board)", fg: theme.muted }));
58381
+ sysCard.add(text({ id: "game-user-prompt", content: args2.userPrompt, fg: theme.muted }));
57957
58382
  sysCard.add(text({ id: "game-system-title", content: "system prompt", fg: theme.muted }));
57958
58383
  sysCard.add(text({ id: "game-system", content: game.systemPrompt(), fg: theme.muted }));
57959
58384
  parent.add(sysCard);
57960
- const lt = args2.lastTurn;
57961
- const tot = args2.totals;
57962
- const statsRow = box({ id: "game-stats-row", width: "100%", flexDirection: "row", gap: 1, marginTop: 1 });
57963
- const lastTurnCard = card("game-last-turn", { flexGrow: 1, flexShrink: 0 });
57964
- lastTurnCard.add(text({ id: "game-last-turn-title", content: "last turn", fg: theme.muted }));
57965
- if (lt === undefined) {
57966
- lastTurnCard.add(text({ id: "game-stats-empty", content: "no turns yet", fg: theme.muted }));
57967
- } else {
57968
- const model = lt.provider === "\u2013" ? "\u2013" : truncate4(`${lt.provider}/${lt.model}`, MODEL_MAX);
57969
- statRow(lastTurnCard, "model", model, "game-stats-model");
57970
- statRow(lastTurnCard, "first byte", formatMs(lt.latencyMs), "game-stats-latency");
57971
- statRow(lastTurnCard, "total", formatMs(lt.totalMs), "game-stats-total");
57972
- statRow(lastTurnCard, "tokens", `in ${formatTokens(lt.inputTokens)} \xB7 out ${formatTokens(lt.outputTokens)}`, "game-stats-tokens");
57973
- if (lt.reasoning) {
57974
- statRow(lastTurnCard, "reasoning", "yes", "game-stats-reasoning", theme.focus);
57975
- }
57976
- if (lt.localFallback) {
57977
- statRow(lastTurnCard, "fallback", "local", "game-stats-fallback", theme.error);
57978
- }
57979
- if (lt.error) {
57980
- statRow(lastTurnCard, "error", "yes", "game-stats-error", theme.error);
57981
- }
57982
- }
57983
- const sessionCard = card("game-session", { flexGrow: 1, flexShrink: 0 });
57984
- sessionCard.add(text({ id: "game-session-title", content: "session", fg: theme.muted }));
57985
- statRow(sessionCard, "turns", String(tot.turns), "game-session-turns");
57986
- statRow(sessionCard, "fallbacks", String(tot.localFallbacks), "game-session-fallbacks");
57987
- statRow(sessionCard, "errors", String(tot.errors), "game-session-errors");
57988
- statRow(sessionCard, "tokens", `in ${formatTokens(tot.inputTokens)} \xB7 out ${formatTokens(tot.outputTokens)}`, "game-session-tokens");
57989
- statsRow.add(lastTurnCard);
57990
- statsRow.add(sessionCard);
57991
- parent.add(statsRow);
57992
58385
  }
57993
58386
 
57994
58387
  // src/tui/games/otui.ts
@@ -58023,6 +58416,8 @@ function presentGamesModal(openModalFn, otui, chrome, options = {}, registry = c
58023
58416
  let unsubscribeKey;
58024
58417
  let bodyRef;
58025
58418
  let bodyWidth = 0;
58419
+ let bodyHeight = 0;
58420
+ const modelParam = `${options.provider ?? "auto"}/${options.model ?? "auto"}`;
58026
58421
  const stateOf = (id) => {
58027
58422
  const game = registry.get(id);
58028
58423
  if (game === undefined) {
@@ -58053,19 +58448,24 @@ function presentGamesModal(openModalFn, otui, chrome, options = {}, registry = c
58053
58448
  return;
58054
58449
  }
58055
58450
  const state = stateOf(activeId);
58451
+ const budget = resolveGameBudget(bodyWidth, Math.max(1, bodyHeight));
58056
58452
  const ctx = {
58057
58453
  core,
58058
58454
  renderer,
58059
58455
  theme: getTheme(),
58060
58456
  parent: bodyRef,
58061
- width: bodyWidth
58457
+ width: bodyWidth,
58458
+ height: budget.boardBudgetRows
58062
58459
  };
58063
58460
  game.render(state, ctx);
58064
58461
  renderAgentPanel(game, bodyRef, core, renderer, {
58065
58462
  notice,
58066
58463
  modelBusy,
58067
58464
  lastTurn: lastTurn.get(activeId),
58068
- totals: totalsOf(activeId)
58465
+ totals: totalsOf(activeId),
58466
+ userPrompt: game.stateForModel(state),
58467
+ promptRows: budget.promptRows,
58468
+ modelParam
58069
58469
  });
58070
58470
  };
58071
58471
  const restart = () => {
@@ -58172,6 +58572,7 @@ function presentGamesModal(openModalFn, otui, chrome, options = {}, registry = c
58172
58572
  }
58173
58573
  bodyRef = body;
58174
58574
  bodyWidth = ctx.width;
58575
+ bodyHeight = ctx.height;
58175
58576
  paint();
58176
58577
  },
58177
58578
  onClose: () => {
@@ -61712,7 +62113,8 @@ function createTuiAgentIo(otui, renderer, transcript) {
61712
62113
  onSystem: (text) => append(text.includes("[error]") ? otui.t`${otui.red(text)}` : otui.t`${otui.dim(text)}`),
61713
62114
  resetStream: () => {
61714
62115
  messages.reset();
61715
- }
62116
+ },
62117
+ lastCodeSegment: () => messages.lastCodeSegment()
61716
62118
  };
61717
62119
  }
61718
62120
  function attachBlockIo(io, addBlock, chrome = {}) {
@@ -62898,6 +63300,23 @@ async function launchTuiAgentShell(opts) {
62898
63300
  const newestBlock = (kind2) => nav.newest(kind2);
62899
63301
  const toggleNewestBlock = (kind2) => nav.toggleNewest(kind2);
62900
63302
  const copyBlock = (id) => nav.copy(id);
63303
+ const copyNewestOrLastCode = () => {
63304
+ const target = newestBlock();
63305
+ if (target !== undefined) {
63306
+ return copyBlock(target.id);
63307
+ }
63308
+ const code = io.lastCodeSegment();
63309
+ if (code === undefined) {
63310
+ return false;
63311
+ }
63312
+ try {
63313
+ r.copyToClipboardOSC52(code.body);
63314
+ chrome.showToast("Copied to clipboard");
63315
+ return true;
63316
+ } catch {
63317
+ return false;
63318
+ }
63319
+ };
62901
63320
  const addBlock = (input3, options = {}) => {
62902
63321
  const id = blockMount.add(input3, options);
62903
63322
  nav.paint(id);
@@ -63889,17 +64308,6 @@ Staying in the current session.
63889
64308
  } catch {}
63890
64309
  }, 12000);
63891
64310
  };
63892
- const summarizeSubmittedLine = (line) => {
63893
- const normalized = line.replace(/\r\n/g, `
63894
- `).replace(/\r/g, `
63895
- `);
63896
- const count = normalized.split(`
63897
- `).filter((linePart) => linePart.length > 0).length;
63898
- if (count <= 1) {
63899
- return line;
63900
- }
63901
- return `[pasted ${count} lines]`;
63902
- };
63903
64311
  const spawnSideWorker = (question, displayQuestion = question) => {
63904
64312
  sideQueue.push({ question, displayQuestion });
63905
64313
  if (sideQueue.length > 1 || sideWorkerRunning) {
@@ -64112,8 +64520,7 @@ Staying in the current session.
64112
64520
  return;
64113
64521
  }
64114
64522
  case "copy": {
64115
- const target = newestBlock();
64116
- if (target === undefined || !copyBlock(target.id)) {
64523
+ if (!copyNewestOrLastCode()) {
64117
64524
  io.onSystem?.(`Nothing to copy yet.
64118
64525
  `);
64119
64526
  }
@@ -64372,8 +64779,7 @@ Staying in the current session.
64372
64779
  return;
64373
64780
  }
64374
64781
  if (command.name === "/copy") {
64375
- const target = newestBlock();
64376
- if (target === undefined || !copyBlock(target.id)) {
64782
+ if (!copyNewestOrLastCode()) {
64377
64783
  io.onSystem?.(`Nothing to copy yet.
64378
64784
  `);
64379
64785
  }
@@ -64856,17 +65262,6 @@ async function mountChatShell(otui, renderer, opts) {
64856
65262
  chrome.setStatus(label());
64857
65263
  sbModel.content = otui.t`${otui.dim(label())}`;
64858
65264
  };
64859
- const summarizeSubmittedLine = (line) => {
64860
- const normalized = line.replace(/\r\n/g, `
64861
- `).replace(/\r/g, `
64862
- `);
64863
- const count = normalized.split(`
64864
- `).filter((linePart) => linePart.length > 0).length;
64865
- if (count <= 1) {
64866
- return line;
64867
- }
64868
- return `[pasted ${count} lines]`;
64869
- };
64870
65265
  const bridge = createChatBridge({
64871
65266
  onAccepted: (line) => {
64872
65267
  const displayLine = line.startsWith("/") ? line : summarizeSubmittedLine(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.64",
3
+ "version": "0.2.66",
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": {