@mrciphersmith/keryx 0.2.58 → 0.2.60
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 +169 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -53454,7 +53454,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
53454
53454
|
// package.json
|
|
53455
53455
|
var package_default = {
|
|
53456
53456
|
name: "@mrciphersmith/keryx",
|
|
53457
|
-
version: "0.2.
|
|
53457
|
+
version: "0.2.60",
|
|
53458
53458
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
53459
53459
|
private: false,
|
|
53460
53460
|
publishConfig: {
|
|
@@ -57441,6 +57441,31 @@ function checkWinner(board) {
|
|
|
57441
57441
|
}
|
|
57442
57442
|
return null;
|
|
57443
57443
|
}
|
|
57444
|
+
function freeCells(board) {
|
|
57445
|
+
return board.flatMap((cell, index) => cell === null ? [index] : []);
|
|
57446
|
+
}
|
|
57447
|
+
function bestLocalMove(board, mark) {
|
|
57448
|
+
const free = freeCells(board);
|
|
57449
|
+
if (free.length === 0) {
|
|
57450
|
+
return;
|
|
57451
|
+
}
|
|
57452
|
+
const opponent = mark === "X" ? "O" : "X";
|
|
57453
|
+
for (const candidate of [mark, opponent]) {
|
|
57454
|
+
for (const index of free) {
|
|
57455
|
+
const next = board.slice();
|
|
57456
|
+
next[index] = candidate;
|
|
57457
|
+
if (checkWinner(next)?.winner === candidate) {
|
|
57458
|
+
return index;
|
|
57459
|
+
}
|
|
57460
|
+
}
|
|
57461
|
+
}
|
|
57462
|
+
for (const index of [4, 0, 2, 6, 8]) {
|
|
57463
|
+
if (board[index] === null) {
|
|
57464
|
+
return index;
|
|
57465
|
+
}
|
|
57466
|
+
}
|
|
57467
|
+
return free[0];
|
|
57468
|
+
}
|
|
57444
57469
|
function placeMark(board, index, mark) {
|
|
57445
57470
|
if (index < 0 || index > 8 || board[index] !== null) {
|
|
57446
57471
|
return;
|
|
@@ -57482,7 +57507,8 @@ function gameSystemPrompt() {
|
|
|
57482
57507
|
"3 4 5",
|
|
57483
57508
|
"6 7 8",
|
|
57484
57509
|
"Reply with ONLY the index of the cell you choose, as a single digit 0-8.",
|
|
57485
|
-
"Choose an empty cell. Prefer winning, then blocking, then center/corner."
|
|
57510
|
+
"Choose an empty cell. Prefer winning, then blocking, then center/corner.",
|
|
57511
|
+
"Answer immediately. No explanation, no working out \u2014 one character."
|
|
57486
57512
|
].join(`
|
|
57487
57513
|
`);
|
|
57488
57514
|
}
|
|
@@ -57500,7 +57526,7 @@ async function modelMove(board, opts = {}) {
|
|
|
57500
57526
|
user: gameUserPrompt(board),
|
|
57501
57527
|
...opts.provider !== undefined ? { provider: opts.provider } : {},
|
|
57502
57528
|
...opts.model !== undefined ? { model: opts.model } : {},
|
|
57503
|
-
maxOutputTokens:
|
|
57529
|
+
maxOutputTokens: 256,
|
|
57504
57530
|
requestId: "keryx-game",
|
|
57505
57531
|
...opts.providerFactory !== undefined ? { providerFactory: opts.providerFactory } : {},
|
|
57506
57532
|
...opts.env !== undefined ? { env: opts.env } : {}
|
|
@@ -57519,6 +57545,20 @@ var GAME_FOOTER = [
|
|
|
57519
57545
|
{ key: "r", label: "new game" },
|
|
57520
57546
|
{ key: "esc", label: "minimize" }
|
|
57521
57547
|
];
|
|
57548
|
+
var GAME_CELL_GAP = 1;
|
|
57549
|
+
var GAME_BOARD_CHROME_X = 4;
|
|
57550
|
+
var GAME_CELL_SIZES = {
|
|
57551
|
+
large: { width: 9, height: 5 },
|
|
57552
|
+
small: { width: 5, height: 3 }
|
|
57553
|
+
};
|
|
57554
|
+
function gameBoardWidth(cellWidth) {
|
|
57555
|
+
return cellWidth * 3 + GAME_CELL_GAP * 2 + GAME_BOARD_CHROME_X;
|
|
57556
|
+
}
|
|
57557
|
+
function resolveCellSize(availableWidth) {
|
|
57558
|
+
const large = GAME_CELL_SIZES.large;
|
|
57559
|
+
return gameBoardWidth(large.width) <= availableWidth ? large : GAME_CELL_SIZES.small;
|
|
57560
|
+
}
|
|
57561
|
+
var GAME_MODEL_TIMEOUT_MS = 12000;
|
|
57522
57562
|
function asOtui2(otui) {
|
|
57523
57563
|
if (otui === undefined || otui === null) {
|
|
57524
57564
|
return;
|
|
@@ -57545,37 +57585,40 @@ function statusText(state) {
|
|
|
57545
57585
|
if (state.draw) {
|
|
57546
57586
|
return "Draw! (r \u2014 new game)";
|
|
57547
57587
|
}
|
|
57548
|
-
return
|
|
57588
|
+
return state.turn === "X" ? "Your turn \u2014 X" : "Agent's turn \u2014 O";
|
|
57549
57589
|
}
|
|
57550
57590
|
function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
57551
57591
|
const renderer = options.renderer;
|
|
57552
57592
|
const core = asOtui2(otui);
|
|
57553
57593
|
let handle;
|
|
57554
|
-
let
|
|
57594
|
+
let cells = [];
|
|
57555
57595
|
let statusBox;
|
|
57556
|
-
let
|
|
57596
|
+
let noticeBox;
|
|
57557
57597
|
let cursor = 4;
|
|
57598
|
+
let notice;
|
|
57558
57599
|
let unsubscribeKey;
|
|
57559
57600
|
const paint = () => {
|
|
57560
|
-
if (
|
|
57601
|
+
if (statusBox === undefined || noticeBox === undefined || cells.length !== 9) {
|
|
57561
57602
|
return;
|
|
57562
57603
|
}
|
|
57563
57604
|
const theme = getTheme();
|
|
57564
|
-
clearTranscriptChildren(boardBox);
|
|
57565
57605
|
statusBox.content = statusText(currentGame);
|
|
57566
|
-
|
|
57567
|
-
|
|
57568
|
-
|
|
57569
|
-
|
|
57570
|
-
|
|
57571
|
-
|
|
57572
|
-
|
|
57573
|
-
|
|
57574
|
-
|
|
57575
|
-
|
|
57576
|
-
|
|
57577
|
-
|
|
57578
|
-
|
|
57606
|
+
statusBox.fg = theme.text;
|
|
57607
|
+
if (modelBusy) {
|
|
57608
|
+
noticeBox.content = "agent is thinking\u2026";
|
|
57609
|
+
noticeBox.fg = theme.focus;
|
|
57610
|
+
} else {
|
|
57611
|
+
noticeBox.content = notice ?? "";
|
|
57612
|
+
noticeBox.fg = theme.error;
|
|
57613
|
+
}
|
|
57614
|
+
for (const [index, view] of cells.entries()) {
|
|
57615
|
+
const cell = currentGame.board[index] ?? null;
|
|
57616
|
+
const isCursor = index === cursor && !isGameOver(currentGame) && !modelBusy;
|
|
57617
|
+
const won = currentGame.winLine?.includes(index) === true;
|
|
57618
|
+
view.text.content = cell ?? "\xB7";
|
|
57619
|
+
view.text.fg = cell === null ? theme.muted : markColor(cell);
|
|
57620
|
+
view.box.borderColor = won ? markColor(currentGame.winner ?? "X") : isCursor ? theme.focus : theme.border;
|
|
57621
|
+
view.box.backgroundColor = isCursor || won ? theme.highlight : undefined;
|
|
57579
57622
|
}
|
|
57580
57623
|
};
|
|
57581
57624
|
const applyModelMove = async () => {
|
|
@@ -57583,26 +57626,44 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57583
57626
|
return;
|
|
57584
57627
|
}
|
|
57585
57628
|
modelBusy = true;
|
|
57629
|
+
notice = undefined;
|
|
57586
57630
|
paint();
|
|
57587
|
-
const
|
|
57588
|
-
|
|
57589
|
-
|
|
57590
|
-
|
|
57591
|
-
...options.env !== undefined ? { env: options.env } : {}
|
|
57631
|
+
const timeoutMs = options.timeoutMs ?? GAME_MODEL_TIMEOUT_MS;
|
|
57632
|
+
let timer;
|
|
57633
|
+
const deadline = new Promise((resolve3) => {
|
|
57634
|
+
timer = setTimeout(() => resolve3("timeout"), timeoutMs);
|
|
57592
57635
|
});
|
|
57636
|
+
const outcome = await Promise.race([
|
|
57637
|
+
modelMove(currentGame.board, {
|
|
57638
|
+
...options.provider !== undefined ? { provider: options.provider } : {},
|
|
57639
|
+
...options.model !== undefined ? { model: options.model } : {},
|
|
57640
|
+
...options.providerFactory !== undefined ? { providerFactory: options.providerFactory } : {},
|
|
57641
|
+
...options.env !== undefined ? { env: options.env } : {}
|
|
57642
|
+
}),
|
|
57643
|
+
deadline
|
|
57644
|
+
]);
|
|
57645
|
+
if (timer !== undefined) {
|
|
57646
|
+
clearTimeout(timer);
|
|
57647
|
+
}
|
|
57593
57648
|
modelBusy = false;
|
|
57594
|
-
|
|
57595
|
-
|
|
57596
|
-
|
|
57597
|
-
|
|
57598
|
-
|
|
57599
|
-
|
|
57600
|
-
|
|
57649
|
+
const timedOut = outcome === "timeout";
|
|
57650
|
+
const result = timedOut ? { move: undefined, error: undefined } : outcome;
|
|
57651
|
+
let move = result.move;
|
|
57652
|
+
let playedLocally = false;
|
|
57653
|
+
if (move === undefined && result.error === undefined) {
|
|
57654
|
+
move = bestLocalMove(currentGame.board, "O");
|
|
57655
|
+
playedLocally = move !== undefined;
|
|
57656
|
+
}
|
|
57657
|
+
const placed = move === undefined ? undefined : placeMark(currentGame.board, move, "O");
|
|
57658
|
+
currentGame = placed ?? { ...currentGame, turn: "X" };
|
|
57659
|
+
if (result.error !== undefined) {
|
|
57660
|
+
notice = `agent: ${result.error}`;
|
|
57661
|
+
} else if (timedOut && playedLocally) {
|
|
57662
|
+
notice = `agent timed out after ${Math.round(timeoutMs / 1000)}s \u2014 played a local move`;
|
|
57663
|
+
} else if (playedLocally) {
|
|
57664
|
+
notice = "agent gave no usable answer \u2014 played a local move";
|
|
57601
57665
|
} else {
|
|
57602
|
-
|
|
57603
|
-
if (result.error !== undefined) {
|
|
57604
|
-
statusBox !== undefined && (statusBox.content = `agent: ${result.error}`);
|
|
57605
|
-
}
|
|
57666
|
+
notice = undefined;
|
|
57606
57667
|
}
|
|
57607
57668
|
paint();
|
|
57608
57669
|
};
|
|
@@ -57615,6 +57676,7 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57615
57676
|
return;
|
|
57616
57677
|
}
|
|
57617
57678
|
currentGame = placed;
|
|
57679
|
+
notice = undefined;
|
|
57618
57680
|
paint();
|
|
57619
57681
|
if (!isGameOver(currentGame) && currentGame.turn === "O") {
|
|
57620
57682
|
applyModelMove();
|
|
@@ -57632,13 +57694,18 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57632
57694
|
const restart = () => {
|
|
57633
57695
|
resetGame();
|
|
57634
57696
|
cursor = 4;
|
|
57697
|
+
notice = undefined;
|
|
57635
57698
|
paint();
|
|
57636
57699
|
};
|
|
57637
57700
|
handle = openModalFn(otui, chrome, {
|
|
57638
57701
|
title: "/game",
|
|
57639
57702
|
tabs: [{ id: "game", label: "Tic-tac-toe" }],
|
|
57640
57703
|
footer: GAME_FOOTER,
|
|
57641
|
-
|
|
57704
|
+
onArrowKeys: (_key, direction) => {
|
|
57705
|
+
moveCursor(0, direction === "left" ? -1 : 1);
|
|
57706
|
+
return true;
|
|
57707
|
+
},
|
|
57708
|
+
renderTab: (_tabId, body, ctx) => {
|
|
57642
57709
|
if (body === undefined || body === null) {
|
|
57643
57710
|
return;
|
|
57644
57711
|
}
|
|
@@ -57647,10 +57714,27 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57647
57714
|
return;
|
|
57648
57715
|
}
|
|
57649
57716
|
const theme = getTheme();
|
|
57650
|
-
const
|
|
57717
|
+
const r = renderer;
|
|
57718
|
+
const cellSize = resolveCellSize(ctx.width);
|
|
57719
|
+
const wrap2 = new core.BoxRenderable(r, {
|
|
57720
|
+
id: "game-wrap",
|
|
57721
|
+
width: "100%",
|
|
57722
|
+
flexDirection: "column",
|
|
57723
|
+
alignItems: "center"
|
|
57724
|
+
});
|
|
57725
|
+
const legend = new core.BoxRenderable(r, {
|
|
57726
|
+
id: "game-legend",
|
|
57727
|
+
flexDirection: "row",
|
|
57728
|
+
marginBottom: 1
|
|
57729
|
+
});
|
|
57730
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-x", content: "X you", fg: theme.ok }));
|
|
57731
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-sep", content: " \xB7 ", fg: theme.muted }));
|
|
57732
|
+
legend.add(new core.TextRenderable(r, { id: "game-legend-o", content: "O model", fg: theme.error }));
|
|
57733
|
+
wrap2.add(legend);
|
|
57734
|
+
const board = new core.BoxRenderable(r, {
|
|
57651
57735
|
id: "game-board",
|
|
57652
|
-
width: 15,
|
|
57653
57736
|
flexDirection: "column",
|
|
57737
|
+
flexShrink: 0,
|
|
57654
57738
|
border: true,
|
|
57655
57739
|
borderStyle: "rounded",
|
|
57656
57740
|
borderColor: theme.border,
|
|
@@ -57658,22 +57742,55 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57658
57742
|
paddingLeft: 1,
|
|
57659
57743
|
paddingRight: 1
|
|
57660
57744
|
});
|
|
57661
|
-
|
|
57662
|
-
|
|
57663
|
-
|
|
57745
|
+
cells = [];
|
|
57746
|
+
for (let row = 0;row < 3; row++) {
|
|
57747
|
+
const rowBox = new core.BoxRenderable(r, {
|
|
57748
|
+
id: `game-row-${row}`,
|
|
57749
|
+
flexDirection: "row",
|
|
57750
|
+
flexShrink: 0,
|
|
57751
|
+
gap: GAME_CELL_GAP
|
|
57752
|
+
});
|
|
57753
|
+
for (let col = 0;col < 3; col++) {
|
|
57754
|
+
const index = row * 3 + col;
|
|
57755
|
+
const cellBox = new core.BoxRenderable(r, {
|
|
57756
|
+
id: `game-cell-${index}`,
|
|
57757
|
+
width: cellSize.width,
|
|
57758
|
+
height: cellSize.height,
|
|
57759
|
+
flexShrink: 0,
|
|
57760
|
+
flexGrow: 0,
|
|
57761
|
+
flexDirection: "row",
|
|
57762
|
+
alignItems: "center",
|
|
57763
|
+
justifyContent: "center",
|
|
57764
|
+
border: true,
|
|
57765
|
+
borderStyle: "rounded",
|
|
57766
|
+
borderColor: theme.border
|
|
57767
|
+
});
|
|
57768
|
+
const cellText = new core.TextRenderable(r, {
|
|
57769
|
+
id: `game-cell-text-${index}`,
|
|
57770
|
+
content: "\xB7",
|
|
57771
|
+
fg: theme.muted
|
|
57772
|
+
});
|
|
57773
|
+
cellBox.add(cellText);
|
|
57774
|
+
rowBox.add(cellBox);
|
|
57775
|
+
cells.push({ box: cellBox, text: cellText });
|
|
57776
|
+
}
|
|
57777
|
+
board.add(rowBox);
|
|
57778
|
+
}
|
|
57779
|
+
wrap2.add(board);
|
|
57780
|
+
const status = new core.TextRenderable(r, {
|
|
57664
57781
|
id: "game-status",
|
|
57665
57782
|
content: "",
|
|
57666
57783
|
marginTop: 1
|
|
57667
57784
|
});
|
|
57668
57785
|
statusBox = status;
|
|
57669
|
-
|
|
57670
|
-
const
|
|
57671
|
-
id: "game-
|
|
57672
|
-
content: ""
|
|
57673
|
-
marginTop: 1
|
|
57786
|
+
wrap2.add(status);
|
|
57787
|
+
const noticeText = new core.TextRenderable(r, {
|
|
57788
|
+
id: "game-notice",
|
|
57789
|
+
content: ""
|
|
57674
57790
|
});
|
|
57675
|
-
|
|
57676
|
-
|
|
57791
|
+
noticeBox = noticeText;
|
|
57792
|
+
wrap2.add(noticeText);
|
|
57793
|
+
parent.add(wrap2);
|
|
57677
57794
|
paint();
|
|
57678
57795
|
},
|
|
57679
57796
|
onClose: () => {
|
|
@@ -57694,11 +57811,11 @@ function presentGame(openModalFn, otui, chrome, options = {}) {
|
|
|
57694
57811
|
moveCursor(1, 0);
|
|
57695
57812
|
return;
|
|
57696
57813
|
}
|
|
57697
|
-
if (token === "
|
|
57814
|
+
if (token === "h") {
|
|
57698
57815
|
moveCursor(0, -1);
|
|
57699
57816
|
return;
|
|
57700
57817
|
}
|
|
57701
|
-
if (token === "
|
|
57818
|
+
if (token === "l") {
|
|
57702
57819
|
moveCursor(0, 1);
|
|
57703
57820
|
return;
|
|
57704
57821
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.60",
|
|
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": {
|