chesspal 0.3.5 → 0.4.0
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/README.md +1 -0
- package/dist/cli/cli.js +17 -4
- package/dist/cli/command-line.js +22 -12
- package/dist/cli/icon.d.ts +2 -0
- package/dist/cli/icon.js +11 -1
- package/dist/cli/prompt-hook.d.ts +17 -0
- package/dist/cli/prompt-hook.js +100 -0
- package/dist/commands/analysis/analyse.d.ts +2 -2
- package/dist/commands/analysis/analyse.js +10 -16
- package/dist/commands/analysis/annotate.d.ts +8 -0
- package/dist/commands/analysis/annotate.js +19 -3
- package/dist/commands/answers/agents.d.ts +17 -7
- package/dist/commands/answers/agents.js +46 -16
- package/dist/commands/answers/clock.js +2 -1
- package/dist/commands/answers/drawings.js +6 -2
- package/dist/commands/answers/index.d.ts +4 -3
- package/dist/commands/answers/index.js +4 -3
- package/dist/commands/answers/lines.d.ts +5 -3
- package/dist/commands/answers/lines.js +17 -9
- package/dist/commands/answers/opening-book.d.ts +13 -7
- package/dist/commands/answers/opening-book.js +34 -38
- package/dist/commands/answers/opening-tags.d.ts +4 -0
- package/dist/commands/answers/opening-tags.js +11 -0
- package/dist/commands/answers/play-move.d.ts +3 -1
- package/dist/commands/answers/play-move.js +9 -6
- package/dist/commands/answers/say.js +2 -1
- package/dist/commands/answers/summary.d.ts +7 -3
- package/dist/commands/answers/summary.js +16 -5
- package/dist/commands/answers/view.d.ts +1 -1
- package/dist/commands/answers/view.js +2 -2
- package/dist/commands/game/ai-move.js +4 -4
- package/dist/commands/game/coach.js +7 -6
- package/dist/commands/game/end.js +2 -2
- package/dist/commands/game/export.d.ts +1 -1
- package/dist/commands/game/index.d.ts +1 -1
- package/dist/commands/game/index.js +1 -1
- package/dist/commands/game/look.d.ts +7 -3
- package/dist/commands/game/look.js +9 -4
- package/dist/commands/game/new.js +42 -12
- package/dist/commands/game/player-move.d.ts +16 -0
- package/dist/commands/game/player-move.js +69 -19
- package/dist/commands/game/ponder.js +7 -4
- package/dist/commands/game/position.d.ts +2 -1
- package/dist/commands/game/position.js +9 -4
- package/dist/commands/game/status.d.ts +4 -2
- package/dist/commands/game/status.js +24 -14
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/index.js +3 -3
- package/dist/commands/profiles/past-games.d.ts +7 -2
- package/dist/commands/profiles/past-games.js +45 -18
- package/dist/commands/registry/context.d.ts +2 -0
- package/dist/commands/registry/context.js +5 -0
- package/dist/commands/registry/index.d.ts +1 -1
- package/dist/commands/registry/index.js +1 -1
- package/dist/commands/talk/ai-say.d.ts +1 -1
- package/dist/commands/talk/ai-say.js +7 -6
- package/dist/core/game.d.ts +13 -0
- package/dist/core/game.js +29 -3
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +3 -3
- package/dist/core/openings.d.ts +20 -6
- package/dist/core/openings.js +45 -22
- package/dist/core/pgn.d.ts +4 -0
- package/dist/core/pgn.js +35 -6
- package/dist/games/analysis.d.ts +7 -4
- package/dist/games/analysis.js +5 -3
- package/dist/games/archive.d.ts +25 -5
- package/dist/games/archive.js +52 -19
- package/dist/games/chat-start.d.ts +2 -2
- package/dist/games/chat-start.js +6 -1
- package/dist/games/current-game.d.ts +2 -2
- package/dist/games/current-game.js +3 -2
- package/dist/games/index.d.ts +3 -2
- package/dist/games/index.js +3 -2
- package/dist/games/position-shown.d.ts +24 -0
- package/dist/games/position-shown.js +51 -0
- package/dist/games/thoughts.d.ts +12 -4
- package/dist/games/thoughts.js +27 -10
- package/dist/home/index.d.ts +1 -0
- package/dist/home/index.js +1 -0
- package/dist/home/same-path.d.ts +7 -0
- package/dist/home/same-path.js +24 -0
- package/dist/host-files/host-files.d.ts +5 -0
- package/dist/host-files/host-files.js +21 -7
- package/dist/host-files/index.d.ts +1 -1
- package/dist/host-files/index.js +1 -1
- package/dist/page/app/anchor.js +15 -11
- package/dist/page/app/app.js +49 -14
- package/dist/page/app/served.js +30 -38
- package/dist/page/app/watch.js +97 -0
- package/dist/page/avatars/torretta.css +108 -0
- package/dist/page/base.css +1 -1
- package/dist/page/board/board.css +11 -8
- package/dist/page/board/marks.js +2 -2
- package/dist/page/board/pointer.js +2 -2
- package/dist/page/dialogs/analyses.js +5 -3
- package/dist/page/dialogs/dialogs.css +3 -1
- package/dist/page/dialogs/rows.js +72 -0
- package/dist/page/dialogs/site-games.js +3 -1
- package/dist/page/game/column.js +10 -8
- package/dist/page/game/game.css +137 -16
- package/dist/page/game/move-list.js +7 -5
- package/dist/page/game/plies.js +4 -0
- package/dist/page/game/review-progress.js +11 -0
- package/dist/page/guide/drawings/drawings.css +0 -1
- package/dist/page/guide/en.md +2 -2
- package/dist/page/guide/it.md +2 -2
- package/dist/page/helps/helps.css +5 -5
- package/dist/page/helps/helps.js +3 -181
- package/dist/page/helps/rows.js +16 -0
- package/dist/page/helps/strategy.js +105 -0
- package/dist/page/helps/tactics.js +69 -0
- package/dist/page/layout/header.js +49 -6
- package/dist/page/layout/icon.ico +0 -0
- package/dist/page/layout/layout.css +49 -17
- package/dist/page/layout/window.js +45 -6
- package/dist/page/lead/lead.js +41 -4
- package/dist/page/legend/legend.css +4 -16
- package/dist/page/legend/legend.js +28 -15
- package/dist/page/profile/settings.js +1 -0
- package/dist/page/sounds/sounds.js +1 -1
- package/dist/page/style.css +1 -0
- package/dist/page/table/action.js +42 -0
- package/dist/page/table/table.css +57 -39
- package/dist/page/table/window.js +67 -67
- package/dist/page/talk/talk.css +52 -118
- package/dist/page/talk/talk.js +65 -20
- package/dist/page/talk/voice.js +92 -21
- package/dist/page/text.js +34 -8
- package/dist/page/theme.css +13 -0
- package/dist/profiles/profiles.js +5 -1
- package/dist/server/bell/bell.d.ts +24 -29
- package/dist/server/bell/bell.js +79 -133
- package/dist/server/bell/index.d.ts +1 -1
- package/dist/server/bell/index.js +2 -2
- package/dist/server/mcp/mcp.js +49 -7
- package/dist/server/page/analyses.js +15 -4
- package/dist/server/page/chat.d.ts +5 -1
- package/dist/server/page/chat.js +5 -1
- package/dist/server/page/files.js +3 -1
- package/dist/server/page/game.d.ts +5 -0
- package/dist/server/page/game.js +95 -15
- package/dist/server/page/http.d.ts +1 -1
- package/dist/server/page/page.d.ts +14 -9
- package/dist/server/page/page.js +111 -26
- package/dist/server/page/table.d.ts +40 -5
- package/dist/server/page/table.js +65 -25
- package/dist/server/page/update.d.ts +1 -1
- package/dist/server/page/update.js +4 -4
- package/dist/server/page/windows.d.ts +22 -0
- package/dist/server/page/windows.js +45 -0
- package/dist/server/ports/index.d.ts +1 -0
- package/dist/server/ports/index.js +2 -0
- package/dist/server/ports/ports.d.ts +78 -0
- package/dist/server/ports/ports.js +222 -0
- package/dist/server/table/browser.d.ts +22 -6
- package/dist/server/table/browser.js +98 -20
- package/dist/server/table/hosts/chats.d.ts +5 -0
- package/dist/server/table/hosts/chats.js +11 -4
- package/dist/server/table/hosts/hooks.d.ts +6 -0
- package/dist/server/table/hosts/hooks.js +38 -0
- package/dist/server/table/hosts/hosts.d.ts +13 -5
- package/dist/server/table/hosts/hosts.js +16 -10
- package/dist/server/table/hosts/index.d.ts +1 -0
- package/dist/server/table/hosts/index.js +1 -0
- package/dist/server/table/hosts/play.d.ts +7 -3
- package/dist/server/table/hosts/play.js +63 -9
- package/dist/server/table/icon/index.d.ts +2 -0
- package/dist/server/table/icon/index.js +16 -2
- package/dist/server/table/index.d.ts +2 -2
- package/dist/server/table/index.js +1 -1
- package/dist/server/table/launch.d.ts +5 -4
- package/dist/server/table/launch.js +18 -5
- package/dist/server/table/link.js +4 -1
- package/dist/server/table/table.d.ts +7 -0
- package/dist/server/table/table.js +40 -12
- package/dist/sites/index.d.ts +1 -1
- package/dist/sites/index.js +1 -1
- package/dist/sites/{chesscom.d.ts → ratings/chesscom.d.ts} +2 -2
- package/dist/sites/{chesscom.js → ratings/chesscom.js} +1 -1
- package/dist/sites/{choice.d.ts → ratings/choice.d.ts} +1 -1
- package/dist/sites/{choice.js → ratings/choice.js} +1 -1
- package/dist/sites/ratings/index.d.ts +1 -0
- package/dist/sites/ratings/index.js +2 -0
- package/dist/sites/{lichess.d.ts → ratings/lichess.d.ts} +2 -2
- package/dist/sites/{lichess.js → ratings/lichess.js} +1 -1
- package/dist/sites/{sites.d.ts → ratings/ratings.d.ts} +2 -2
- package/dist/sites/{sites.js → ratings/ratings.js} +1 -1
- package/instructions/opponent.md +1 -1
- package/instructions/start.md +1 -1
- package/package.json +1 -1
package/dist/games/analysis.js
CHANGED
|
@@ -8,7 +8,7 @@ import { writeAtomically } from "./files.js";
|
|
|
8
8
|
* unless it starts afresh, in one write, so that the page never sees it leave the analysis; the analyst stays with the game it took, whichever the page
|
|
9
9
|
* opens meanwhile (Federico), and goes when that game starts afresh.
|
|
10
10
|
*/
|
|
11
|
-
export function startAnalysis(folder, game, now, { brought = false, corrected = [], analyst, afresh = false } = {}) {
|
|
11
|
+
export function startAnalysis(folder, game, now, { brought = false, redo = false, corrected = [], analyst, afresh = false } = {}) {
|
|
12
12
|
const before = gameInAnalysis(folder);
|
|
13
13
|
const same = !afresh && before?.game === game;
|
|
14
14
|
const kept = [...new Set([...(same ? before.corrected ?? [] : []), ...corrected])];
|
|
@@ -17,6 +17,7 @@ export function startAnalysis(folder, game, now, { brought = false, corrected =
|
|
|
17
17
|
game,
|
|
18
18
|
at: now.toISOString(),
|
|
19
19
|
...(brought ? { brought: true } : {}),
|
|
20
|
+
...(brought && redo ? { redo: true } : {}),
|
|
20
21
|
...(kept.length === 0 ? {} : { corrected: kept }),
|
|
21
22
|
...withAnalyst(analyst ?? held),
|
|
22
23
|
});
|
|
@@ -25,8 +26,8 @@ export function startAnalysis(folder, game, now, { brought = false, corrected =
|
|
|
25
26
|
export function forgetAnalyst(folder) {
|
|
26
27
|
const analysis = gameInAnalysis(folder);
|
|
27
28
|
if (analysis?.analyst !== undefined) {
|
|
28
|
-
const { game, at, brought, corrected } = analysis;
|
|
29
|
-
writeAnalysis(folder, { game, at, ...(brought === undefined ? {} : { brought }), ...(corrected === undefined ? {} : { corrected }) });
|
|
29
|
+
const { game, at, brought, redo, corrected } = analysis;
|
|
30
|
+
writeAnalysis(folder, { game, at, ...(brought === undefined ? {} : { brought }), ...(redo === undefined ? {} : { redo }), ...(corrected === undefined ? {} : { corrected }) });
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
/** The game in analysis, or none when there is none, or analysis.json cannot be read. */
|
|
@@ -39,6 +40,7 @@ export function gameInAnalysis(folder) {
|
|
|
39
40
|
game: analysis.game,
|
|
40
41
|
at: analysis.at,
|
|
41
42
|
...(analysis.brought === true ? { brought: true } : {}),
|
|
43
|
+
...(analysis.brought === true && analysis.redo === true ? { redo: true } : {}),
|
|
42
44
|
...(corrected.length === 0 ? {} : { corrected }),
|
|
43
45
|
...withAnalyst(isAnalystRun(analysis.analyst) ? analysis.analyst : undefined),
|
|
44
46
|
}
|
package/dist/games/archive.d.ts
CHANGED
|
@@ -10,15 +10,15 @@ export declare function isOverOnTable(folder: string, name: string): boolean;
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function startOfNewGame(folder: string, now: Date, before?: CurrentGame): Date;
|
|
12
12
|
/**
|
|
13
|
-
* Writes the game to the archive, with the plans, how it ended
|
|
14
|
-
* same file. A game closed before any move is not kept, as Lichess aborts it: none.
|
|
13
|
+
* Writes the game to the archive, with the plans, how it ended, the AI's model and host last noted (D-82, D-97) and the opening's tags, and gives its name;
|
|
14
|
+
* the same game again rewrites the same file. A game closed before any move is not kept, as Lichess aborts it: none.
|
|
15
15
|
*/
|
|
16
|
-
export declare function archiveGame(folder: string, current: CurrentGame): string | undefined;
|
|
16
|
+
export declare function archiveGame(folder: string, current: CurrentGame, opening: OpeningTags): string | undefined;
|
|
17
17
|
/**
|
|
18
18
|
* Archives a game that has ended: its name in the archive, none for a game closed before any move, or, when that fails, a warning, since the game is saved in
|
|
19
19
|
* game.json all the same.
|
|
20
20
|
*/
|
|
21
|
-
export declare function archiveEnded(folder: string, current: CurrentGame): {
|
|
21
|
+
export declare function archiveEnded(folder: string, current: CurrentGame, opening: OpeningTags): {
|
|
22
22
|
archive?: string;
|
|
23
23
|
warning?: string;
|
|
24
24
|
};
|
|
@@ -26,16 +26,36 @@ export declare function archiveEnded(folder: string, current: CurrentGame): {
|
|
|
26
26
|
* Writes the coach's judgments into a game over already in the archive, beside what the analyst may have written there since (Federico);
|
|
27
27
|
* archives the game when it is not there.
|
|
28
28
|
*/
|
|
29
|
-
export declare function archiveCoached(folder: string, current: CurrentGame): {
|
|
29
|
+
export declare function archiveCoached(folder: string, current: CurrentGame, opening: OpeningTags): {
|
|
30
30
|
archive?: string;
|
|
31
31
|
warning?: string;
|
|
32
32
|
};
|
|
33
33
|
/** The name of a game over in the archive, when it is there. */
|
|
34
34
|
export declare function archivedName(folder: string, current: CurrentGame): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The opening a game reached, in its ECO and Opening tags, as the commands name it: none from another position or outside the book. With the PlyCount the
|
|
37
|
+
* archive adds, a game is listed from its tags alone.
|
|
38
|
+
*/
|
|
39
|
+
export interface OpeningTags {
|
|
40
|
+
readonly ECO?: string;
|
|
41
|
+
readonly Opening?: string;
|
|
42
|
+
}
|
|
35
43
|
/** The names of the games in the archive, the latest first: games/2026-11-02-153012.pgn. */
|
|
36
44
|
export declare function archivedGames(folder: string): string[];
|
|
37
45
|
/** A game of the archive by its name, as listed; refused when it is not there, or cannot be read. */
|
|
38
46
|
export declare function archivedGame(folder: string, name: string): GameRecord;
|
|
47
|
+
/** A game of the archive by its name, as listed, with its moves as written, neither checked nor played: for its tags and comments, quickly. */
|
|
48
|
+
export declare function archivedGameUnplayed(folder: string, name: string): GameRecord;
|
|
49
|
+
/** The tags of a game of the archive by its name, as listed, read without its moves; refused as archivedGame refuses. */
|
|
50
|
+
export declare function archivedTags(folder: string, name: string): Readonly<Record<string, string>>;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the archive wrote a game's list tags, its PlyCount with its ECO and Opening, or none for a game that has no opening: a numeric PlyCount says so.
|
|
53
|
+
* A game an older Chesspal archived has none, and is read whole.
|
|
54
|
+
*/
|
|
55
|
+
export declare function hasListTags(tags: Readonly<Record<string, string>>): boolean;
|
|
56
|
+
/** The refusal of a game asked by a name no game has, apart from a game that is there and cannot be read. */
|
|
57
|
+
export declare class GameMissing extends Error {
|
|
58
|
+
}
|
|
39
59
|
/** The names of the games kept to study in a profile's folder, the latest kept first: study/2026-11-04-153012.pgn. */
|
|
40
60
|
export declare function keptGames(root: string): string[];
|
|
41
61
|
/** A game kept to study by its name, as listed; refused when it is not there, or cannot be read. */
|
package/dist/games/archive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The finished games, as PGN in games/ (D-9, D-41), with the AI's plans for the analyst (D-21); and the games played elsewhere, kept to study in study/ (D-68).
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { movesOnly, pgnDate, readPgn, unsigned, withAiHost, withAiModel, writePgn } from "../core/index.js";
|
|
4
|
+
import { movesOnly, pgnDate, readPgn, readPgnTags, readPgnUnplayed, unsigned, withAiHost, withAiModel, writePgn } from "../core/index.js";
|
|
5
5
|
import { aiHostOf, aiModelOf } from "./ai-model.js";
|
|
6
6
|
import { coachWords, withCoach } from "./coach.js";
|
|
7
7
|
import { endOf, loadGame } from "./current-game.js";
|
|
@@ -34,10 +34,10 @@ export function startOfNewGame(folder, now, before) {
|
|
|
34
34
|
return start;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* Writes the game to the archive, with the plans, how it ended
|
|
38
|
-
* same file. A game closed before any move is not kept, as Lichess aborts it: none.
|
|
37
|
+
* Writes the game to the archive, with the plans, how it ended, the AI's model and host last noted (D-82, D-97) and the opening's tags, and gives its name;
|
|
38
|
+
* the same game again rewrites the same file. A game closed before any move is not kept, as Lichess aborts it: none.
|
|
39
39
|
*/
|
|
40
|
-
export function archiveGame(folder, current) {
|
|
40
|
+
export function archiveGame(folder, current, opening) {
|
|
41
41
|
if (current.game.record.moves.length === 0) {
|
|
42
42
|
return undefined;
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ export function archiveGame(folder, current) {
|
|
|
45
45
|
const path = join(folder, name);
|
|
46
46
|
try {
|
|
47
47
|
makeFolder(dirname(path));
|
|
48
|
-
writeAtomically(path, writePgn(withAiHost(withAiModel(endedRecord(folder, current), aiModelOf(folder)), aiHostOf(folder))));
|
|
48
|
+
writeAtomically(path, writePgn(withListTags(withAiHost(withAiModel(endedRecord(folder, current), aiModelOf(folder)), aiHostOf(folder)), opening)));
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
51
|
throw new Error(`Chesspal could not archive the game: ${error.message}`, { cause: error });
|
|
@@ -56,9 +56,9 @@ export function archiveGame(folder, current) {
|
|
|
56
56
|
* Archives a game that has ended: its name in the archive, none for a game closed before any move, or, when that fails, a warning, since the game is saved in
|
|
57
57
|
* game.json all the same.
|
|
58
58
|
*/
|
|
59
|
-
export function archiveEnded(folder, current) {
|
|
59
|
+
export function archiveEnded(folder, current, opening) {
|
|
60
60
|
try {
|
|
61
|
-
const archive = archiveGame(folder, current);
|
|
61
|
+
const archive = archiveGame(folder, current, opening);
|
|
62
62
|
return archive === undefined ? {} : { archive };
|
|
63
63
|
}
|
|
64
64
|
catch (error) {
|
|
@@ -69,10 +69,10 @@ export function archiveEnded(folder, current) {
|
|
|
69
69
|
* Writes the coach's judgments into a game over already in the archive, beside what the analyst may have written there since (Federico);
|
|
70
70
|
* archives the game when it is not there.
|
|
71
71
|
*/
|
|
72
|
-
export function archiveCoached(folder, current) {
|
|
72
|
+
export function archiveCoached(folder, current, opening) {
|
|
73
73
|
const name = archivedName(folder, current);
|
|
74
74
|
if (name === undefined) {
|
|
75
|
-
return archiveEnded(folder, current);
|
|
75
|
+
return archiveEnded(folder, current, opening);
|
|
76
76
|
}
|
|
77
77
|
try {
|
|
78
78
|
writeAtomically(join(folder, name), writePgn(withCoach(archivedGame(folder, name), coachWords(folder, current))));
|
|
@@ -98,6 +98,28 @@ export function archivedGames(folder) {
|
|
|
98
98
|
export function archivedGame(folder, name) {
|
|
99
99
|
return gameNamed(folder, "games", name);
|
|
100
100
|
}
|
|
101
|
+
/** A game of the archive by its name, as listed, with its moves as written, neither checked nor played: for its tags and comments, quickly. */
|
|
102
|
+
export function archivedGameUnplayed(folder, name) {
|
|
103
|
+
const [record] = readPgnUnplayed(gameText(folder, "games", name));
|
|
104
|
+
if (record === undefined) {
|
|
105
|
+
throw new Error(`${name} holds no game Chesspal can read.`);
|
|
106
|
+
}
|
|
107
|
+
return record;
|
|
108
|
+
}
|
|
109
|
+
/** The tags of a game of the archive by its name, as listed, read without its moves; refused as archivedGame refuses. */
|
|
110
|
+
export function archivedTags(folder, name) {
|
|
111
|
+
return readPgnTags(gameText(folder, "games", name));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Whether the archive wrote a game's list tags, its PlyCount with its ECO and Opening, or none for a game that has no opening: a numeric PlyCount says so.
|
|
115
|
+
* A game an older Chesspal archived has none, and is read whole.
|
|
116
|
+
*/
|
|
117
|
+
export function hasListTags(tags) {
|
|
118
|
+
return /^\d+$/.test(tags.PlyCount ?? "");
|
|
119
|
+
}
|
|
120
|
+
/** The refusal of a game asked by a name no game has, apart from a game that is there and cannot be read. */
|
|
121
|
+
export class GameMissing extends Error {
|
|
122
|
+
}
|
|
101
123
|
/** The names of the games kept to study in a profile's folder, the latest kept first: study/2026-11-04-153012.pgn. */
|
|
102
124
|
export function keptGames(root) {
|
|
103
125
|
return gameNames(root, "study");
|
|
@@ -211,24 +233,30 @@ function gameNames(folder, shelf) {
|
|
|
211
233
|
}
|
|
212
234
|
}
|
|
213
235
|
function gameNamed(folder, shelf, name) {
|
|
236
|
+
const [record] = readPgn(gameText(folder, shelf, name));
|
|
237
|
+
if (record === undefined) {
|
|
238
|
+
throw new Error(`${name} holds no game Chesspal can read.`);
|
|
239
|
+
}
|
|
240
|
+
return record;
|
|
241
|
+
}
|
|
242
|
+
function gameText(folder, shelf, name) {
|
|
214
243
|
// The name as listed, or without its folder or its .pgn.
|
|
215
244
|
const file = `${(name.startsWith(`${shelf}/`) ? name.slice(shelf.length + 1) : name).trim().replace(/\.pgn$/, "")}.pgn`;
|
|
216
245
|
const { what, list } = shelves[shelf];
|
|
217
246
|
if (!namePattern.test(file)) {
|
|
218
|
-
throw new
|
|
247
|
+
throw new GameMissing(`${name} is no game ${what}: give a name as ${list} writes it, such as ${shelf}/2026-11-02-153012.pgn.`);
|
|
219
248
|
}
|
|
220
|
-
let text;
|
|
221
249
|
try {
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
catch {
|
|
225
|
-
throw new Error(`There is no ${name} among the games ${what}: give a name as ${list} writes it.`);
|
|
250
|
+
return readFileSync(join(folder, shelf, file), "utf8");
|
|
226
251
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
252
|
+
catch (error) {
|
|
253
|
+
const code = error.code;
|
|
254
|
+
// Only a file that is not there is a missing game: one busy or locked by another process is there, and may be read in a moment.
|
|
255
|
+
if (code === "ENOENT") {
|
|
256
|
+
throw new GameMissing(`There is no ${name} among the games ${what}: give a name as ${list} writes it.`);
|
|
257
|
+
}
|
|
258
|
+
throw new Error(`${name} could not be read (${code ?? error.message}): try again in a moment.`, { cause: error });
|
|
230
259
|
}
|
|
231
|
-
return record;
|
|
232
260
|
}
|
|
233
261
|
/** Whether two records are the same game: the same tags and the same moves, whatever their comments say. */
|
|
234
262
|
function sameGame(one, other) {
|
|
@@ -245,6 +273,11 @@ function endedRecord(folder, current) {
|
|
|
245
273
|
const { record, outcome } = current.game;
|
|
246
274
|
return withTermination(withCoach(record, coachWords(folder, current)), outcome);
|
|
247
275
|
}
|
|
276
|
+
/** The record with the tags past_games lists it from: the opening's, the commands' alone whatever the game brought, and how many plies it has. */
|
|
277
|
+
function withListTags(record, opening) {
|
|
278
|
+
const tags = Object.fromEntries(Object.entries(record.tags).filter(([name]) => name !== "ECO" && name !== "Opening" && name !== "PlyCount"));
|
|
279
|
+
return { ...record, tags: { ...tags, ...opening, PlyCount: String(record.moves.length) } };
|
|
280
|
+
}
|
|
248
281
|
function withTermination(record, outcome) {
|
|
249
282
|
return outcome.over && record.tags.Termination === undefined ? { ...record, tags: { ...record.tags, Termination: outcome.ending } } : record;
|
|
250
283
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* How a chat started, as the host's SessionStart hook says it: new, cleared, resumed or compacted; when, as an ISO date; its session,
|
|
3
|
-
* file where the host writes the chat, where Claude Code tells which model made each tool call (D-81).
|
|
2
|
+
* How a chat started, as the host's SessionStart hook says it: new, cleared, resumed or compacted; when, as an ISO date; its session, as the hooks have it;
|
|
3
|
+
* and the file where the host writes the chat, where Claude Code tells which model made each tool call (D-81).
|
|
4
4
|
*/
|
|
5
5
|
export interface ChatStart {
|
|
6
6
|
readonly source: string;
|
package/dist/games/chat-start.js
CHANGED
|
@@ -13,7 +13,12 @@ export function lastChatStart(folder) {
|
|
|
13
13
|
if (typeof start.source !== "string" || typeof start.at !== "string") {
|
|
14
14
|
return undefined;
|
|
15
15
|
}
|
|
16
|
-
return {
|
|
16
|
+
return {
|
|
17
|
+
source: start.source,
|
|
18
|
+
at: start.at,
|
|
19
|
+
...(typeof start.session === "string" ? { session: start.session } : {}),
|
|
20
|
+
...(typeof start.transcript === "string" ? { transcript: start.transcript } : {}),
|
|
21
|
+
};
|
|
17
22
|
}
|
|
18
23
|
catch {
|
|
19
24
|
return undefined;
|
|
@@ -43,9 +43,9 @@ export interface GameEnd {
|
|
|
43
43
|
export declare function loadGame(folder: string): CurrentGame | undefined;
|
|
44
44
|
/**
|
|
45
45
|
* Writes the current game to game.json, whole or not at all; from then on it is the one kept in memory. Refused with GameChanged when another process wrote
|
|
46
|
-
* the file since this one read it: the change made on the game read before would lose theirs.
|
|
46
|
+
* the file since this one read it: the change made on the game read before would lose theirs. `first` runs just before the write, on the game read.
|
|
47
47
|
*/
|
|
48
|
-
export declare function saveGame(folder: string, current: CurrentGame): void;
|
|
48
|
+
export declare function saveGame(folder: string, current: CurrentGame, first?: () => void): void;
|
|
49
49
|
/** The refusal of a save on a game another process changed since this one read it: what wrote it reads it again and starts over, once (rerunIfChanged). */
|
|
50
50
|
export declare class GameChanged extends Error {
|
|
51
51
|
constructor();
|
|
@@ -23,15 +23,16 @@ export function loadGame(folder) {
|
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Writes the current game to game.json, whole or not at all; from then on it is the one kept in memory. Refused with GameChanged when another process wrote
|
|
26
|
-
* the file since this one read it: the change made on the game read before would lose theirs.
|
|
26
|
+
* the file since this one read it: the change made on the game read before would lose theirs. `first` runs just before the write, on the game read.
|
|
27
27
|
*/
|
|
28
|
-
export function saveGame(folder, current) {
|
|
28
|
+
export function saveGame(folder, current, first) {
|
|
29
29
|
const path = join(folder, fileName);
|
|
30
30
|
whileLocked(path, () => {
|
|
31
31
|
if (stampOf(path) !== kept.get(path)?.stamp) {
|
|
32
32
|
kept.delete(path);
|
|
33
33
|
throw new GameChanged();
|
|
34
34
|
}
|
|
35
|
+
first?.();
|
|
35
36
|
write(path, current);
|
|
36
37
|
});
|
|
37
38
|
}
|
package/dist/games/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { aiHostOf, aiModelOf, forgetTheAi, noteAiHost, noteAiModel } from "./ai-model.ts";
|
|
2
2
|
export { type Analysis, type AnalysisStart, endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis } from "./analysis.ts";
|
|
3
|
-
export { archiveCoached, archivedGame, archivedGames, archivedName, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, gameStemOf, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, playerPgn, rewriteGame, startOfNewGame, } from "./archive.ts";
|
|
3
|
+
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, type OpeningTags, playerPgn, rewriteGame, startOfNewGame, } from "./archive.ts";
|
|
4
4
|
export { type ChatStart, cleanSince, lastChatStart, saveChatStart } from "./chat-start.ts";
|
|
5
5
|
export { type CoachHint, type CoachJudgment, type CoachLead, coachShown, coachStarted, coachWaitingSince, type CoachWords, coachWords, forgetCoach, keepHint, keepJudgment, keepLead, startCoach, withCoach } from "./coach.ts";
|
|
6
6
|
export { bothSidesMoved, type CurrentGame, drawOfferOf, endOf, GameChanged, type GameEnd, gameStamp, loadGame, removeGame, rerunIfChanged, saveGame, type Shown, type Speaker, type TalkLine } from "./current-game.ts";
|
|
7
7
|
export { makeFolder, renameOver, writeAtomically } from "./files.ts";
|
|
8
8
|
export { commentWithPlan, currentPlan, planOf, withoutPlans } from "./plans.ts";
|
|
9
|
+
export { forgetPositionShown, hostProcess, notePositionShown, type PositionShown, positionShownNote } from "./position-shown.ts";
|
|
9
10
|
export { endTableTalk, loadTableTalk, saveTableTalk, type TableTalk, tableTalkStamp } from "./table-talk.ts";
|
|
10
11
|
export { type Heard, heardLines, type Sayer, type Talk, talkLimits, talkLine, unheardWords, wakesTheChat, type Words, WordsRefused } from "./talk.ts";
|
|
11
|
-
export { keepThought, notePondering, ponderingOn, type Thought, thoughtForLastMove, thoughtKey, waitingSince } from "./thoughts.ts";
|
|
12
|
+
export { keepThought, notePondering, ponderingOn, ponderingUntil, type Thought, thoughtForLastMove, thoughtKey, waitingSince } from "./thoughts.ts";
|
package/dist/games/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// The player's games on disk: the current one in game.json, the finished ones in games/ (D-27, D-41), the games kept to study in study/ (D-68). Below the commands, above the core.
|
|
2
2
|
export { aiHostOf, aiModelOf, forgetTheAi, noteAiHost, noteAiModel } from "./ai-model.js";
|
|
3
3
|
export { endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis } from "./analysis.js";
|
|
4
|
-
export { archiveCoached, archivedGame, archivedGames, archivedName, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, gameStemOf, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, playerPgn, rewriteGame, startOfNewGame, } from "./archive.js";
|
|
4
|
+
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, playerPgn, rewriteGame, startOfNewGame, } from "./archive.js";
|
|
5
5
|
export { cleanSince, lastChatStart, saveChatStart } from "./chat-start.js";
|
|
6
6
|
export { coachShown, coachStarted, coachWaitingSince, coachWords, forgetCoach, keepHint, keepJudgment, keepLead, startCoach, withCoach } from "./coach.js";
|
|
7
7
|
export { bothSidesMoved, drawOfferOf, endOf, GameChanged, gameStamp, loadGame, removeGame, rerunIfChanged, saveGame } from "./current-game.js";
|
|
8
8
|
export { makeFolder, renameOver, writeAtomically } from "./files.js";
|
|
9
9
|
export { commentWithPlan, currentPlan, planOf, withoutPlans } from "./plans.js";
|
|
10
|
+
export { forgetPositionShown, hostProcess, notePositionShown, positionShownNote } from "./position-shown.js";
|
|
10
11
|
export { endTableTalk, loadTableTalk, saveTableTalk, tableTalkStamp } from "./table-talk.js";
|
|
11
12
|
export { heardLines, talkLimits, talkLine, unheardWords, wakesTheChat, WordsRefused } from "./talk.js";
|
|
12
|
-
export { keepThought, notePondering, ponderingOn, thoughtForLastMove, thoughtKey, waitingSince } from "./thoughts.js";
|
|
13
|
+
export { keepThought, notePondering, ponderingOn, ponderingUntil, thoughtForLastMove, thoughtKey, waitingSince } from "./thoughts.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The position shown to a chat by its session, as the host names it to its hooks, and by the host's process, when it names one: the game by its start, none
|
|
3
|
+
* when there is none, its plies, the stamp of game.json after the move, when, and the keys of the player's words given with it.
|
|
4
|
+
*/
|
|
5
|
+
export interface PositionShown {
|
|
6
|
+
readonly session: string;
|
|
7
|
+
readonly process?: string;
|
|
8
|
+
readonly stamp?: string;
|
|
9
|
+
readonly started?: string;
|
|
10
|
+
readonly plies: number;
|
|
11
|
+
readonly at: string;
|
|
12
|
+
readonly given: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The Claude Code process this one runs in, as it names it to its hooks and to the MCP server alike, the same through /clear and /resume: none when it
|
|
16
|
+
* names none. Not documented: Priya measured it in Claude Code 2.1.323.
|
|
17
|
+
*/
|
|
18
|
+
export declare function hostProcess(): string | undefined;
|
|
19
|
+
/** Notes the position shown to a chat, over the last one. */
|
|
20
|
+
export declare function notePositionShown(folder: string, shown: PositionShown): void;
|
|
21
|
+
/** The position a hook showed a chat of the folder, or undefined when there is no note, or it cannot be read. */
|
|
22
|
+
export declare function positionShownNote(folder: string): PositionShown | undefined;
|
|
23
|
+
/** Forgets the note; one that cannot go now is taken again, which changes nothing, or dropped once the game has moved on. */
|
|
24
|
+
export declare function forgetPositionShown(folder: string): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The position a hook showed a chat outside its server (D-71), and the player's words on the board page it gave with it: `chesspal prompt` notes them
|
|
2
|
+
// after playing the move the player wrote alone, and the chat's server takes them before its next tool, so that the AI moves without asking for status
|
|
3
|
+
// and no tool gives those words again; the chat's doorbell does not ring for that move, which the chat hears from the prompt itself.
|
|
4
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { writeAtomically } from "./files.js";
|
|
7
|
+
/**
|
|
8
|
+
* The Claude Code process this one runs in, as it names it to its hooks and to the MCP server alike, the same through /clear and /resume: none when it
|
|
9
|
+
* names none. Not documented: Priya measured it in Claude Code 2.1.323.
|
|
10
|
+
*/
|
|
11
|
+
export function hostProcess() {
|
|
12
|
+
const socket = process.env.CLAUDE_CODE_MESSAGING_SOCKET?.trim();
|
|
13
|
+
return socket === undefined || socket === "" ? undefined : socket;
|
|
14
|
+
}
|
|
15
|
+
/** Notes the position shown to a chat, over the last one. */
|
|
16
|
+
export function notePositionShown(folder, shown) {
|
|
17
|
+
writeAtomically(join(folder, fileName), `${JSON.stringify(shown)}\n`);
|
|
18
|
+
}
|
|
19
|
+
/** The position a hook showed a chat of the folder, or undefined when there is no note, or it cannot be read. */
|
|
20
|
+
export function positionShownNote(folder) {
|
|
21
|
+
try {
|
|
22
|
+
const shown = JSON.parse(readFileSync(join(folder, fileName), "utf8"));
|
|
23
|
+
if (typeof shown.session !== "string" || typeof shown.at !== "string" || !Number.isInteger(shown.plies)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
// A note of an older Chesspal has no words.
|
|
27
|
+
const given = Array.isArray(shown.given) ? shown.given.filter((key) => typeof key === "string") : [];
|
|
28
|
+
return {
|
|
29
|
+
session: shown.session,
|
|
30
|
+
...(typeof shown.process === "string" ? { process: shown.process } : {}),
|
|
31
|
+
...(typeof shown.stamp === "string" ? { stamp: shown.stamp } : {}),
|
|
32
|
+
...(typeof shown.started === "string" ? { started: shown.started } : {}),
|
|
33
|
+
plies: shown.plies,
|
|
34
|
+
at: shown.at,
|
|
35
|
+
given,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Forgets the note; one that cannot go now is taken again, which changes nothing, or dropped once the game has moved on. */
|
|
43
|
+
export function forgetPositionShown(folder) {
|
|
44
|
+
try {
|
|
45
|
+
rmSync(join(folder, fileName), { force: true });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Another process holds it for a moment.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const fileName = "position-shown.json";
|
package/dist/games/thoughts.d.ts
CHANGED
|
@@ -7,8 +7,11 @@ export interface Thought {
|
|
|
7
7
|
}
|
|
8
8
|
/** The key of the position the player is to move in: the game, by its start, and how many moves it has. */
|
|
9
9
|
export declare function thoughtKey(current: CurrentGame, ply?: number): string;
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Keeps a thought for a position, in place of one for the same reply; thoughts of another position go. The thoughts kept now. A thought kept while the AI
|
|
12
|
+
* ponders shows it still at work.
|
|
13
|
+
*/
|
|
14
|
+
export declare function keepThought(folder: string, key: string, thought: Thought, now: Date): Thought[];
|
|
12
15
|
/** The thought for the player's last move, when the AI is to move and thought of it on their time. */
|
|
13
16
|
export declare function thoughtForLastMove(folder: string, current: CurrentGame): Thought | undefined;
|
|
14
17
|
/** Since when the AI has waited for the player's move in a position: the first time it asked, `now` then, kept with its thoughts. */
|
|
@@ -17,6 +20,11 @@ export declare function waitingSince(folder: string, key: string, now: Date): Da
|
|
|
17
20
|
* Notes whether the AI thinks on the player's time in a position now, from its first look until it waits for their move, for the page's sign: in the file,
|
|
18
21
|
* since in Codex the agent that thinks has a process of its own (D-98).
|
|
19
22
|
*/
|
|
20
|
-
export declare function notePondering(folder: string, key: string, pondering: boolean): void;
|
|
23
|
+
export declare function notePondering(folder: string, key: string, pondering: boolean, now: Date): void;
|
|
24
|
+
/**
|
|
25
|
+
* Until when the AI is seen thinking on the player's time in a position, as noted: a while after its last look or thought, so that an agent stopped before
|
|
26
|
+
* it waits (Esc, its host closed) does not leave the sign on until the player moves.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ponderingUntil(folder: string, key: string): Date | undefined;
|
|
21
29
|
/** Whether the AI thinks on the player's time in a position now, as noted. */
|
|
22
|
-
export declare function ponderingOn(folder: string, key: string): boolean;
|
|
30
|
+
export declare function ponderingOn(folder: string, key: string, now: Date): boolean;
|
package/dist/games/thoughts.js
CHANGED
|
@@ -8,14 +8,17 @@ import { writeAtomically } from "./files.js";
|
|
|
8
8
|
export function thoughtKey(current, ply = current.game.record.moves.length) {
|
|
9
9
|
return `${current.started}#${ply}`;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Keeps a thought for a position, in place of one for the same reply; thoughts of another position go. The thoughts kept now. A thought kept while the AI
|
|
13
|
+
* ponders shows it still at work.
|
|
14
|
+
*/
|
|
15
|
+
export function keepThought(folder, key, thought, now) {
|
|
13
16
|
const kept = keptAt(folder, key);
|
|
14
17
|
const thoughts = [...kept.thoughts.filter((one) => one.reply !== thought.reply), thought];
|
|
15
18
|
if (thoughts.length > mostThoughts) {
|
|
16
19
|
throw new Error(`You have ${mostThoughts} thoughts for this position already: they are enough. Nothing changed.`);
|
|
17
20
|
}
|
|
18
|
-
write(folder, { ...kept, thoughts });
|
|
21
|
+
write(folder, { ...kept, thoughts, ...(kept.pondering === undefined ? {} : { pondering: now.toISOString() }) });
|
|
19
22
|
return thoughts;
|
|
20
23
|
}
|
|
21
24
|
/** The thought for the player's last move, when the AI is to move and thought of it on their time. */
|
|
@@ -37,17 +40,30 @@ export function waitingSince(folder, key, now) {
|
|
|
37
40
|
* Notes whether the AI thinks on the player's time in a position now, from its first look until it waits for their move, for the page's sign: in the file,
|
|
38
41
|
* since in Codex the agent that thinks has a process of its own (D-98).
|
|
39
42
|
*/
|
|
40
|
-
export function notePondering(folder, key, pondering) {
|
|
43
|
+
export function notePondering(folder, key, pondering, now) {
|
|
41
44
|
const { pondering: before, ...kept } = keptAt(folder, key);
|
|
42
|
-
if (
|
|
43
|
-
write(folder,
|
|
45
|
+
if (pondering) {
|
|
46
|
+
write(folder, { ...kept, pondering: now.toISOString() });
|
|
47
|
+
}
|
|
48
|
+
else if (before !== undefined) {
|
|
49
|
+
write(folder, kept);
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
|
-
/**
|
|
47
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Until when the AI is seen thinking on the player's time in a position, as noted: a while after its last look or thought, so that an agent stopped before
|
|
54
|
+
* it waits (Esc, its host closed) does not leave the sign on until the player moves.
|
|
55
|
+
*/
|
|
56
|
+
export function ponderingUntil(folder, key) {
|
|
48
57
|
const kept = read(folder);
|
|
49
|
-
return kept?.key === key && kept.pondering ===
|
|
58
|
+
return kept?.key === key && typeof kept.pondering === "string" ? new Date(new Date(kept.pondering).getTime() + ponderSignFor) : undefined;
|
|
59
|
+
}
|
|
60
|
+
/** Whether the AI thinks on the player's time in a position now, as noted. */
|
|
61
|
+
export function ponderingOn(folder, key, now) {
|
|
62
|
+
const until = ponderingUntil(folder, key);
|
|
63
|
+
return until !== undefined && now < until;
|
|
50
64
|
}
|
|
65
|
+
// An agent thinks on one reply in well under this; one silent for longer has stopped.
|
|
66
|
+
const ponderSignFor = 5 * 60_000;
|
|
51
67
|
/** The most thoughts kept for a position: a few likely replies, not every legal one. */
|
|
52
68
|
const mostThoughts = 8;
|
|
53
69
|
const fileName = "thoughts.json";
|
|
@@ -55,7 +71,8 @@ const thoughtsFile = z.object({
|
|
|
55
71
|
key: z.string(),
|
|
56
72
|
thoughts: z.array(z.object({ reply: z.string(), move: z.string(), why: z.string() })),
|
|
57
73
|
since: z.iso.datetime().optional(),
|
|
58
|
-
|
|
74
|
+
// The time of the AI's last look or thought while it ponders; true, from an older Chesspal, is a sign without its time, taken as gone.
|
|
75
|
+
pondering: z.union([z.iso.datetime(), z.boolean()]).optional(),
|
|
59
76
|
});
|
|
60
77
|
/** What is kept for a position: nothing yet when the file is for another, or missing. */
|
|
61
78
|
function keptAt(folder, key) {
|
package/dist/home/index.d.ts
CHANGED
package/dist/home/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
// Where the player's data is (D-71): one folder for every chat, apart from the host's folder. Below the commands, beside the games and the profiles.
|
|
2
2
|
export { dataFolder, tableFolder } from "./data-folder.js";
|
|
3
3
|
export { openFolder } from "./open-folder.js";
|
|
4
|
+
export { realPath, samePath } from "./same-path.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Whether two paths name the same folder: as written, or through the links and junctions on their way; without regard to case on Windows. */
|
|
2
|
+
export declare function samePath(one: string, other: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* A path with the links and junctions on its way followed, as a page's ports are counted from it: two names of one folder give one path. Case, drive
|
|
5
|
+
* letters and short names stay as written, so a path with no link stays as it was. A folder not made yet keeps the rest of its path as written.
|
|
6
|
+
*/
|
|
7
|
+
export declare function realPath(path: string): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
/** Whether two paths name the same folder: as written, or through the links and junctions on their way; without regard to case on Windows. */
|
|
4
|
+
export function samePath(one, other) {
|
|
5
|
+
// The same path as written needs no disk: only two different names may be one folder.
|
|
6
|
+
return sameText(resolve(one), resolve(other)) || sameText(realPath(one), realPath(other));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A path with the links and junctions on its way followed, as a page's ports are counted from it: two names of one folder give one path. Case, drive
|
|
10
|
+
* letters and short names stay as written, so a path with no link stays as it was. A folder not made yet keeps the rest of its path as written.
|
|
11
|
+
*/
|
|
12
|
+
export function realPath(path) {
|
|
13
|
+
const written = resolve(path);
|
|
14
|
+
try {
|
|
15
|
+
return realpathSync(written);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
const parent = dirname(written);
|
|
19
|
+
return parent === written ? written : join(realPath(parent), basename(written));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function sameText(one, other) {
|
|
23
|
+
return process.platform === "win32" ? one.toLowerCase() === other.toLowerCase() : one === other;
|
|
24
|
+
}
|
|
@@ -11,6 +11,11 @@ export declare function setUpHosts(folder: string, platform: string): string[];
|
|
|
11
11
|
export declare function outdatedHostFiles(folder: string, platform: string): string[];
|
|
12
12
|
/** Where Codex reads a folder's hooks, which it runs only once the player trusts them. */
|
|
13
13
|
export declare function codexHooksPath(folder: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the host runs the doorbell's hook init writes, Claude Code and Codex by the names they give the server, so the player's act on the page wakes
|
|
16
|
+
* the chat; the command line, which names no host, is taken as one. Elsewhere the player tells the chat in its window.
|
|
17
|
+
*/
|
|
18
|
+
export declare function hasDoorbell(host: string | undefined): boolean;
|
|
14
19
|
/** The instructions of the AI opponent, read from the package wherever it is installed: the MCP server's `instructions` tool gives them (D-102). */
|
|
15
20
|
export declare function opponentInstructions(): string;
|
|
16
21
|
/** The lines that start a chat, in the MCP server's instructions and in AGENTS.md: call `instructions`, then `status` (D-102). */
|