arbiter-ai 1.0.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.
Files changed (51) hide show
  1. package/README.md +41 -0
  2. package/assets/jerom_16x16.png +0 -0
  3. package/dist/arbiter.d.ts +43 -0
  4. package/dist/arbiter.js +486 -0
  5. package/dist/context-analyzer.d.ts +15 -0
  6. package/dist/context-analyzer.js +603 -0
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +165 -0
  9. package/dist/orchestrator.d.ts +31 -0
  10. package/dist/orchestrator.js +227 -0
  11. package/dist/router.d.ts +187 -0
  12. package/dist/router.js +1135 -0
  13. package/dist/router.test.d.ts +15 -0
  14. package/dist/router.test.js +95 -0
  15. package/dist/session-persistence.d.ts +9 -0
  16. package/dist/session-persistence.js +63 -0
  17. package/dist/session-persistence.test.d.ts +1 -0
  18. package/dist/session-persistence.test.js +165 -0
  19. package/dist/sound.d.ts +31 -0
  20. package/dist/sound.js +50 -0
  21. package/dist/state.d.ts +72 -0
  22. package/dist/state.js +107 -0
  23. package/dist/state.test.d.ts +1 -0
  24. package/dist/state.test.js +194 -0
  25. package/dist/test-headless.d.ts +1 -0
  26. package/dist/test-headless.js +155 -0
  27. package/dist/tui/index.d.ts +14 -0
  28. package/dist/tui/index.js +17 -0
  29. package/dist/tui/layout.d.ts +30 -0
  30. package/dist/tui/layout.js +200 -0
  31. package/dist/tui/render.d.ts +57 -0
  32. package/dist/tui/render.js +266 -0
  33. package/dist/tui/scene.d.ts +64 -0
  34. package/dist/tui/scene.js +366 -0
  35. package/dist/tui/screens/CharacterSelect-termkit.d.ts +18 -0
  36. package/dist/tui/screens/CharacterSelect-termkit.js +216 -0
  37. package/dist/tui/screens/ForestIntro-termkit.d.ts +15 -0
  38. package/dist/tui/screens/ForestIntro-termkit.js +856 -0
  39. package/dist/tui/screens/GitignoreCheck-termkit.d.ts +14 -0
  40. package/dist/tui/screens/GitignoreCheck-termkit.js +185 -0
  41. package/dist/tui/screens/TitleScreen-termkit.d.ts +14 -0
  42. package/dist/tui/screens/TitleScreen-termkit.js +132 -0
  43. package/dist/tui/screens/index.d.ts +9 -0
  44. package/dist/tui/screens/index.js +10 -0
  45. package/dist/tui/tileset.d.ts +97 -0
  46. package/dist/tui/tileset.js +237 -0
  47. package/dist/tui/tui-termkit.d.ts +34 -0
  48. package/dist/tui/tui-termkit.js +2602 -0
  49. package/dist/tui/types.d.ts +41 -0
  50. package/dist/tui/types.js +4 -0
  51. package/package.json +71 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Character Selection Screen (terminal-kit version)
3
+ *
4
+ * Displays 8 human character tiles for the user to choose from.
5
+ * Uses arrow keys for selection and Enter to confirm.
6
+ * Uses terminal-kit with Strategy 5 (minimal redraws) for flicker-free rendering.
7
+ */
8
+ /**
9
+ * Shows the character selection screen using terminal-kit with Strategy 5 (minimal redraws).
10
+ *
11
+ * @returns Promise<number> - The selected tile index (190-197)
12
+ */
13
+ export interface CharacterSelectResult {
14
+ character: number;
15
+ skipIntro: boolean;
16
+ }
17
+ export declare function showCharacterSelect(): Promise<CharacterSelectResult>;
18
+ export default showCharacterSelect;
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Character Selection Screen (terminal-kit version)
3
+ *
4
+ * Displays 8 human character tiles for the user to choose from.
5
+ * Uses arrow keys for selection and Enter to confirm.
6
+ * Uses terminal-kit with Strategy 5 (minimal redraws) for flicker-free rendering.
7
+ */
8
+ import termKit from 'terminal-kit';
9
+ import { playSfx } from '../../sound.js';
10
+ import { CHAR_HEIGHT, compositeTiles, compositeWithFocus, extractTile, loadTileset, RESET, renderTile, TILE, TILE_SIZE, } from '../tileset.js';
11
+ const term = termKit.terminal;
12
+ // ============================================================================
13
+ // Constants
14
+ // ============================================================================
15
+ // Human character tile indices (190-197)
16
+ const CHARACTER_TILES = [
17
+ TILE.HUMAN_1,
18
+ TILE.HUMAN_2,
19
+ TILE.HUMAN_3,
20
+ TILE.HUMAN_4,
21
+ TILE.HUMAN_5,
22
+ TILE.HUMAN_6,
23
+ TILE.HUMAN_7,
24
+ TILE.HUMAN_8,
25
+ ];
26
+ // Character names for each sprite
27
+ const CHARACTER_NAMES = [
28
+ 'Adventurer',
29
+ 'Rogue',
30
+ 'Ranger',
31
+ 'Swordsman',
32
+ 'Dwarf',
33
+ 'Knight',
34
+ 'Shadow',
35
+ 'Wizard',
36
+ ];
37
+ // Layout constants
38
+ const TILE_SPACING = 2; // Space between tiles (characters)
39
+ const TILE_DISPLAY_WIDTH = TILE_SIZE; // 16 chars per tile
40
+ // ANSI codes
41
+ const BOLD = '\x1b[1m';
42
+ const DIM = '\x1b[2m';
43
+ const YELLOW = '\x1b[33m';
44
+ const CYAN = '\x1b[36m';
45
+ // ============================================================================
46
+ // Rendering Functions
47
+ // ============================================================================
48
+ /**
49
+ * Render all character tiles as an array of ANSI strings (one per row).
50
+ * Characters are displayed in a horizontal row with the selected one highlighted.
51
+ * Character tiles are rendered with transparency preserved (no background compositing).
52
+ */
53
+ function renderCharacterRow(tileset, selectedIndex) {
54
+ // Extract focus tile
55
+ const focusTile = extractTile(tileset, TILE.FOCUS);
56
+ // Extract grass tile for background
57
+ const grassTile = extractTile(tileset, TILE.GRASS);
58
+ // Extract and render each character tile
59
+ const renderedTiles = [];
60
+ for (let i = 0; i < CHARACTER_TILES.length; i++) {
61
+ let charPixels = extractTile(tileset, CHARACTER_TILES[i]);
62
+ // Composite character on grass background
63
+ charPixels = compositeTiles(charPixels, grassTile, 1);
64
+ // Apply focus overlay to selected character
65
+ if (i === selectedIndex) {
66
+ charPixels = compositeWithFocus(charPixels, focusTile);
67
+ }
68
+ renderedTiles.push(renderTile(charPixels));
69
+ }
70
+ // Build output: combine all tiles horizontally for each row
71
+ const spacing = ' '.repeat(TILE_SPACING);
72
+ const lines = [];
73
+ for (let row = 0; row < CHAR_HEIGHT; row++) {
74
+ let line = '';
75
+ for (let charIdx = 0; charIdx < renderedTiles.length; charIdx++) {
76
+ line += renderedTiles[charIdx][row];
77
+ if (charIdx < renderedTiles.length - 1) {
78
+ line += spacing;
79
+ }
80
+ }
81
+ lines.push(line);
82
+ }
83
+ return lines;
84
+ }
85
+ /**
86
+ * Calculate the total width of the character row in characters.
87
+ */
88
+ function getRowWidth() {
89
+ return CHARACTER_TILES.length * TILE_DISPLAY_WIDTH + (CHARACTER_TILES.length - 1) * TILE_SPACING;
90
+ }
91
+ export async function showCharacterSelect() {
92
+ // Load tileset before entering the Promise
93
+ const tileset = await loadTileset();
94
+ return new Promise((resolve) => {
95
+ // Initialize terminal
96
+ term.fullscreen(true);
97
+ term.hideCursor();
98
+ term.grabInput({ mouse: 'button' });
99
+ // State
100
+ let selectedIndex = 0;
101
+ let lastSelectedIndex = -1; // For change detection
102
+ // Get terminal dimensions
103
+ let width = 180;
104
+ let height = 50;
105
+ if (typeof term.width === 'number' && Number.isFinite(term.width) && term.width > 0) {
106
+ width = term.width;
107
+ }
108
+ if (typeof term.height === 'number' && Number.isFinite(term.height) && term.height > 0) {
109
+ height = term.height;
110
+ }
111
+ // Calculate centering offsets
112
+ const rowWidth = getRowWidth();
113
+ const contentHeight = CHAR_HEIGHT + 8; // tiles + title + indicator + name + instructions
114
+ const startX = Math.max(1, Math.floor((width - rowWidth) / 2));
115
+ const startY = Math.max(1, Math.floor((height - contentHeight) / 2));
116
+ /**
117
+ * Draw the screen (only if selection changed)
118
+ */
119
+ function drawScreen() {
120
+ if (selectedIndex === lastSelectedIndex)
121
+ return;
122
+ lastSelectedIndex = selectedIndex;
123
+ // Clear screen on first draw
124
+ if (lastSelectedIndex === -1) {
125
+ term.clear();
126
+ }
127
+ // Title lines
128
+ const title1 = 'Your journey to the Arbiter begins.';
129
+ const title2 = 'Choose wisely. The forest does not forgive the undiscerning.';
130
+ // Title 1 (yellow)
131
+ term.moveTo(Math.max(1, Math.floor((width - title1.length) / 2)), startY);
132
+ process.stdout.write(`${BOLD}${YELLOW}${title1}${RESET}`);
133
+ // Title 2 (yellow)
134
+ term.moveTo(Math.max(1, Math.floor((width - title2.length) / 2)), startY + 1);
135
+ process.stdout.write(`${BOLD}${YELLOW}${title2}${RESET}`);
136
+ // Render character tiles
137
+ const characterLines = renderCharacterRow(tileset, selectedIndex);
138
+ const tilesStartY = startY + 3;
139
+ for (let i = 0; i < characterLines.length; i++) {
140
+ term.moveTo(startX, tilesStartY + i);
141
+ process.stdout.write(characterLines[i] + RESET);
142
+ }
143
+ // Character name (centered below tiles, with padding)
144
+ const nameY = tilesStartY + CHAR_HEIGHT + 2;
145
+ const characterName = CHARACTER_NAMES[selectedIndex];
146
+ // Clear the line first to remove previous name
147
+ term.moveTo(1, nameY);
148
+ process.stdout.write(' '.repeat(width));
149
+ term.moveTo(Math.max(1, Math.floor((width - characterName.length) / 2)), nameY);
150
+ process.stdout.write(`${BOLD}${CYAN}${characterName}${RESET}`);
151
+ // Instructions at bottom
152
+ const instructionY = nameY + 2;
153
+ const instructions = '[LEFT/RIGHT or H/L] Navigate [ENTER] Select [SPACE] Skip intro [Q] Exit';
154
+ term.moveTo(Math.max(1, Math.floor((width - instructions.length) / 2)), instructionY);
155
+ process.stdout.write(`${DIM}${instructions}${RESET}`);
156
+ }
157
+ /**
158
+ * Cleanup and restore terminal
159
+ */
160
+ function cleanup() {
161
+ term.removeAllListeners('key');
162
+ term.grabInput(false);
163
+ term.fullscreen(false);
164
+ term.hideCursor(false);
165
+ }
166
+ // Initial draw
167
+ term.clear();
168
+ drawScreen();
169
+ // Handle keyboard input
170
+ term.on('key', (key) => {
171
+ // Navigation
172
+ if (key === 'LEFT' || key === 'h') {
173
+ playSfx('menuLeft');
174
+ // Move selection left (wrap around)
175
+ selectedIndex = (selectedIndex - 1 + CHARACTER_TILES.length) % CHARACTER_TILES.length;
176
+ drawScreen();
177
+ }
178
+ else if (key === 'RIGHT' || key === 'l') {
179
+ playSfx('menuRight');
180
+ // Move selection right (wrap around)
181
+ selectedIndex = (selectedIndex + 1) % CHARACTER_TILES.length;
182
+ drawScreen();
183
+ }
184
+ else if (key === 'UP' || key === 'k') {
185
+ playSfx('menuLeft');
186
+ // Up moves to previous row (4 characters per row for grid layout)
187
+ selectedIndex = (selectedIndex - 4 + CHARACTER_TILES.length) % CHARACTER_TILES.length;
188
+ drawScreen();
189
+ }
190
+ else if (key === 'DOWN' || key === 'j') {
191
+ playSfx('menuRight');
192
+ // Down moves to next row (4 characters per row for grid layout)
193
+ selectedIndex = (selectedIndex + 4) % CHARACTER_TILES.length;
194
+ drawScreen();
195
+ }
196
+ else if (key === 'ENTER') {
197
+ playSfx('menuSelect');
198
+ // Confirm selection, go to path intro
199
+ cleanup();
200
+ resolve({ character: CHARACTER_TILES[selectedIndex], skipIntro: false });
201
+ }
202
+ else if (key === ' ') {
203
+ playSfx('menuSelect');
204
+ // Skip intro, go straight to arbiter
205
+ cleanup();
206
+ resolve({ character: CHARACTER_TILES[selectedIndex], skipIntro: true });
207
+ }
208
+ else if (key === 'q' || key === 'CTRL_C' || key === 'CTRL_Z') {
209
+ // Exit application
210
+ cleanup();
211
+ process.exit(0);
212
+ }
213
+ });
214
+ });
215
+ }
216
+ export default showCharacterSelect;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Forest Intro Screen (terminal-kit version)
3
+ *
4
+ * A narrative intro screen between character select and main TUI.
5
+ * Player controls their character with arrow keys to walk through the forest.
6
+ * Uses terminal-kit with Strategy 5 (minimal redraws) for flicker-free rendering.
7
+ */
8
+ /**
9
+ * Shows the forest intro screen using terminal-kit with Strategy 5 (minimal redraws)
10
+ *
11
+ * @param selectedCharacter - The tile index of the selected character (190-197)
12
+ * @returns Promise<'success' | 'death'> - 'success' when player exits right after seeing sign, 'death' if they die
13
+ */
14
+ export declare function showForestIntro(selectedCharacter: number): Promise<'success' | 'death'>;
15
+ export default showForestIntro;