mia-code 0.2.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 (103) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.coaia/pde/d77620fc-1cd9-47e2-ba00-c03e114e42e9.jsonl +16 -0
  3. package/.coaia/pde/de44d838-b58b-4e91-b791-dd3b0f940ed1.jsonl +60 -0
  4. package/.gemini/settings.json +8 -0
  5. package/.hch/issue_.env +4 -0
  6. package/.hch/issue_add__2601211715.json +77 -0
  7. package/.hch/issue_add__2601211715.md +4 -0
  8. package/.hch/issue_add__2602242020.json +78 -0
  9. package/.hch/issue_add__2602242020.md +7 -0
  10. package/.hch/issues.json +2312 -0
  11. package/.hch/issues.md +30 -0
  12. package/260123084839.coaia-narrative.autoRevisionOfInitial_NewStructuralTensionChart-to-initiate-HierarchicalThinking.txt +5 -0
  13. package/2602010101.issue.txt +31 -0
  14. package/BUGS.md +242 -0
  15. package/CLAUDE.md +2 -0
  16. package/ENHANCEMENTS.md +129 -0
  17. package/FEATURES_ENDING_SESSIONS.md +21 -0
  18. package/FIXES.md +114 -0
  19. package/GUILLAUME.md +77 -0
  20. package/KINSHIP.md +50 -0
  21. package/LAUNCH__session_id__MiaCodeNextWorkReviewAndCommits_2601312020.sh +7 -0
  22. package/PHASE_2.md +153 -0
  23. package/PHASE_2_IMPLEMENTATION.md +134 -0
  24. package/README.md +203 -0
  25. package/RESUME__issueMaker__540244c2-b096-40d8-8c3f-398408d3e0eb.2602041757.sh +1 -0
  26. package/RUN_COPILOT_with_related_folders__260130.sh +2 -0
  27. package/WS__mia-code__260214__IAIP_PDE.code-workspace +29 -0
  28. package/WS__mia-code__src332__260122.code-workspace +23 -0
  29. package/_env.sh +12 -0
  30. package/dist/cli.d.ts +11 -0
  31. package/dist/cli.js +679 -0
  32. package/dist/commands.d.ts +43 -0
  33. package/dist/commands.js +108 -0
  34. package/dist/config.d.ts +8 -0
  35. package/dist/config.js +57 -0
  36. package/dist/formatting.d.ts +12 -0
  37. package/dist/formatting.js +133 -0
  38. package/dist/geminiHeadless.d.ts +25 -0
  39. package/dist/geminiHeadless.js +246 -0
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +186 -0
  42. package/dist/mcp/config-generator.d.ts +23 -0
  43. package/dist/mcp/config-generator.js +116 -0
  44. package/dist/mcp/index.d.ts +18 -0
  45. package/dist/mcp/index.js +43 -0
  46. package/dist/mcp/miaco-server.d.ts +15 -0
  47. package/dist/mcp/miaco-server.js +161 -0
  48. package/dist/mcp/miatel-server.d.ts +15 -0
  49. package/dist/mcp/miatel-server.js +123 -0
  50. package/dist/mcp/miawa-server.d.ts +15 -0
  51. package/dist/mcp/miawa-server.js +125 -0
  52. package/dist/mcp/utils.d.ts +51 -0
  53. package/dist/mcp/utils.js +76 -0
  54. package/dist/multiline-input.d.ts +98 -0
  55. package/dist/multiline-input.js +630 -0
  56. package/dist/narrative/index.d.ts +9 -0
  57. package/dist/narrative/index.js +11 -0
  58. package/dist/narrative/router.d.ts +89 -0
  59. package/dist/narrative/router.js +186 -0
  60. package/dist/narrative/tracer.d.ts +75 -0
  61. package/dist/narrative/tracer.js +180 -0
  62. package/dist/sessionStore.d.ts +10 -0
  63. package/dist/sessionStore.js +93 -0
  64. package/dist/types.d.ts +44 -0
  65. package/dist/types.js +1 -0
  66. package/dist/unifier.d.ts +6 -0
  67. package/dist/unifier.js +147 -0
  68. package/issue-358--architecture/ARCHITECTURE_OVERVIEW.md +60 -0
  69. package/issue-358--architecture/CLI_INTEGRATION.md +61 -0
  70. package/issue-358--architecture/COVER_ART_BRIEF.md +68 -0
  71. package/issue-358--architecture/MEMORY_SYSTEM.md +89 -0
  72. package/issue-358--architecture/PERSONA_REGISTRY.md +97 -0
  73. package/issue-358--architecture/PODCAST_PRODUCTION_PLAN.md +61 -0
  74. package/issue-358--architecture/PODCAST_SCRIPT_FINAL.md +109 -0
  75. package/issue-358--architecture/PROTOTYPE_CHARACTER_SPEC.md +59 -0
  76. package/issue-358--architecture/RESOURCES.md +41 -0
  77. package/issue-358--architecture/TEAM_LISTENING_GUIDE.md +53 -0
  78. package/llms-gemini-cli.txt +145 -0
  79. package/package.json +39 -0
  80. package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/checkpoints/index.md +6 -0
  81. package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/events.jsonl +213 -0
  82. package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/plan.md +243 -0
  83. package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/workspace.yaml +5 -0
  84. package/src/cli.ts +742 -0
  85. package/src/commands.ts +127 -0
  86. package/src/config.ts +67 -0
  87. package/src/formatting.ts +157 -0
  88. package/src/geminiHeadless.ts +300 -0
  89. package/src/index.ts +194 -0
  90. package/src/mcp/config-generator.ts +141 -0
  91. package/src/mcp/index.ts +55 -0
  92. package/src/mcp/miaco-server.ts +199 -0
  93. package/src/mcp/miatel-server.ts +138 -0
  94. package/src/mcp/miawa-server.ts +158 -0
  95. package/src/mcp/utils.ts +121 -0
  96. package/src/multiline-input.ts +739 -0
  97. package/src/narrative/index.ts +33 -0
  98. package/src/narrative/router.ts +260 -0
  99. package/src/narrative/tracer.ts +249 -0
  100. package/src/sessionStore.ts +111 -0
  101. package/src/types.ts +49 -0
  102. package/src/unifier.ts +171 -0
  103. package/tsconfig.json +15 -0
@@ -0,0 +1,630 @@
1
+ /**
2
+ * MultilineInput - Multiline input handler for terminal CLIs
3
+ *
4
+ * Supports:
5
+ * - Ctrl+J to add new lines without submitting
6
+ * - Enter to submit the complete input
7
+ * - Pasting multiline content
8
+ * - Arrow key navigation
9
+ * - Interactive dropdown completion for / commands and @ file paths
10
+ * - TTY detection with readline fallback
11
+ */
12
+ import { EventEmitter } from "events";
13
+ /**
14
+ * Check if environment supports raw mode multiline input
15
+ */
16
+ export function supportsMultilineInput() {
17
+ return process.stdin.isTTY === true;
18
+ }
19
+ export class MultilineInput extends EventEmitter {
20
+ prompt;
21
+ continuationPrompt;
22
+ maxLines;
23
+ onSubmit;
24
+ onClose;
25
+ onTabComplete;
26
+ lines = [];
27
+ currentLine = "";
28
+ cursorPos = 0;
29
+ isRunning = false;
30
+ isPaused = false;
31
+ isProcessing = false;
32
+ // Dropdown
33
+ dropdown = {
34
+ visible: false,
35
+ items: [],
36
+ labels: [],
37
+ selectedIndex: 0,
38
+ completion: { matches: [], prefix: "", tokenStart: 0, triggerChar: "" },
39
+ renderedRows: 0,
40
+ };
41
+ static MAX_DROPDOWN_ITEMS = 10;
42
+ // ── Helpers ─────────────────────────────────────────────────────
43
+ static stripAnsi(str) {
44
+ // eslint-disable-next-line no-control-regex
45
+ return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
46
+ }
47
+ visibleLength(str) {
48
+ return MultilineInput.stripAnsi(str).length;
49
+ }
50
+ getColumns() {
51
+ return process.stdout.columns || 80;
52
+ }
53
+ getRows() {
54
+ return process.stdout.rows || 24;
55
+ }
56
+ wrappedRows(promptStr, text) {
57
+ const totalVisible = this.visibleLength(promptStr) + text.length;
58
+ const cols = this.getColumns();
59
+ if (cols <= 0)
60
+ return 1;
61
+ return Math.max(1, Math.ceil(totalVisible / cols));
62
+ }
63
+ constructor(options) {
64
+ super();
65
+ this.prompt = options.prompt;
66
+ this.continuationPrompt = options.continuationPrompt || "... ";
67
+ this.maxLines = options.maxLines || 100;
68
+ this.onSubmit = options.onSubmit;
69
+ this.onClose = options.onClose;
70
+ this.onTabComplete = options.onTabComplete;
71
+ }
72
+ start() {
73
+ if (!supportsMultilineInput()) {
74
+ throw new Error("MultilineInput requires TTY mode");
75
+ }
76
+ this.isRunning = true;
77
+ process.stdin.setRawMode(true);
78
+ process.stdin.resume();
79
+ process.stdin.on("data", this.handleData);
80
+ this.showPrompt();
81
+ }
82
+ stop() {
83
+ this.isRunning = false;
84
+ this.clearDropdown();
85
+ process.stdin.setRawMode(false);
86
+ process.stdin.removeListener("data", this.handleData);
87
+ }
88
+ pause() {
89
+ if (!this.isRunning)
90
+ return;
91
+ this.isPaused = true;
92
+ this.clearDropdown();
93
+ process.stdin.setRawMode(false);
94
+ process.stdin.removeListener("data", this.handleData);
95
+ }
96
+ resume() {
97
+ if (!this.isRunning || !this.isPaused)
98
+ return;
99
+ this.isPaused = false;
100
+ process.stdin.setRawMode(true);
101
+ process.stdin.on("data", this.handleData);
102
+ this.redraw();
103
+ }
104
+ reset() {
105
+ this.lines = [];
106
+ this.currentLine = "";
107
+ this.cursorPos = 0;
108
+ this.isProcessing = false;
109
+ this.clearDropdown();
110
+ this.showPrompt();
111
+ }
112
+ // ── Prompt / Redraw ─────────────────────────────────────────────
113
+ showPrompt() {
114
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
115
+ process.stdout.write(promptText);
116
+ }
117
+ redraw() {
118
+ // First erase any visible dropdown so it doesn't leave ghosts
119
+ this.eraseDropdownRows();
120
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
121
+ const cols = this.getColumns();
122
+ const promptVisible = this.visibleLength(promptText);
123
+ const totalVisible = promptVisible + this.currentLine.length;
124
+ const totalRows = Math.max(1, Math.ceil(totalVisible / cols));
125
+ const cursorAbsPos = promptVisible + this.cursorPos;
126
+ const cursorRow = Math.floor(cursorAbsPos / cols);
127
+ if (cursorRow > 0) {
128
+ process.stdout.write(`\x1B[${cursorRow}A`);
129
+ }
130
+ for (let i = 0; i < totalRows; i++) {
131
+ process.stdout.write("\r\x1B[K");
132
+ if (i < totalRows - 1) {
133
+ process.stdout.write("\x1B[1B");
134
+ }
135
+ }
136
+ if (totalRows > 1) {
137
+ process.stdout.write(`\x1B[${totalRows - 1}A`);
138
+ }
139
+ process.stdout.write("\r");
140
+ process.stdout.write(promptText);
141
+ process.stdout.write(this.currentLine);
142
+ if (this.cursorPos < this.currentLine.length) {
143
+ const newTotalRows = Math.max(1, Math.ceil((promptVisible + this.currentLine.length) / cols));
144
+ const endRow = newTotalRows - 1;
145
+ const targetAbsPos = promptVisible + this.cursorPos;
146
+ const targetRow = Math.floor(targetAbsPos / cols);
147
+ const targetCol = targetAbsPos % cols;
148
+ if (endRow > targetRow) {
149
+ process.stdout.write(`\x1B[${endRow - targetRow}A`);
150
+ }
151
+ process.stdout.write(`\r\x1B[${targetCol}C`);
152
+ }
153
+ // Re-render dropdown if still active
154
+ if (this.dropdown.visible) {
155
+ this.renderDropdown();
156
+ }
157
+ }
158
+ // ── Dropdown rendering ──────────────────────────────────────────
159
+ //
160
+ // IMPORTANT: We avoid \x1B[s / \x1B[u (save/restore cursor) because
161
+ // terminal scrolling invalidates the saved position, causing ghost rows.
162
+ // Instead we use explicit relative movement and always return the cursor
163
+ // to the input position by counting rows moved.
164
+ /**
165
+ * Calculate how many rows down from the cursor to the start of the dropdown
166
+ */
167
+ rowsFromCursorToDropdown() {
168
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
169
+ const promptVisible = this.visibleLength(promptText);
170
+ const cols = this.getColumns();
171
+ const inputRows = Math.max(1, Math.ceil((promptVisible + this.currentLine.length) / cols));
172
+ const cursorAbsPos = promptVisible + this.cursorPos;
173
+ const cursorRow = Math.floor(cursorAbsPos / cols);
174
+ return (inputRows - 1 - cursorRow) + 1;
175
+ }
176
+ /**
177
+ * Reposition cursor back to the input editing position (absolute)
178
+ */
179
+ repositionCursor() {
180
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
181
+ const promptVisible = this.visibleLength(promptText);
182
+ const cols = this.getColumns();
183
+ const cursorAbsPos = promptVisible + this.cursorPos;
184
+ const cursorCol = cursorAbsPos % cols;
185
+ process.stdout.write("\r");
186
+ if (cursorCol > 0) {
187
+ process.stdout.write(`\x1B[${cursorCol}C`);
188
+ }
189
+ }
190
+ eraseDropdownRows() {
191
+ if (this.dropdown.renderedRows <= 0)
192
+ return;
193
+ const down = this.rowsFromCursorToDropdown();
194
+ // Move down from cursor to dropdown start
195
+ if (down > 0) {
196
+ process.stdout.write(`\x1B[${down}B`);
197
+ }
198
+ // Clear from here to end of screen (wipes all dropdown rows at once)
199
+ process.stdout.write("\r\x1B[J");
200
+ // Move back up to cursor row
201
+ if (down > 0) {
202
+ process.stdout.write(`\x1B[${down}A`);
203
+ }
204
+ // Restore column position
205
+ this.repositionCursor();
206
+ this.dropdown.renderedRows = 0;
207
+ }
208
+ renderDropdown() {
209
+ if (!this.dropdown.visible || this.dropdown.items.length === 0)
210
+ return;
211
+ const cols = this.getColumns();
212
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
213
+ const promptVisible = this.visibleLength(promptText);
214
+ const inputRows = Math.max(1, Math.ceil((promptVisible + this.currentLine.length) / cols));
215
+ const down = this.rowsFromCursorToDropdown();
216
+ // Move down from cursor to dropdown area
217
+ if (down > 0) {
218
+ process.stdout.write(`\x1B[${down}B`);
219
+ }
220
+ // Clear everything below (any stale dropdown content)
221
+ process.stdout.write("\r\x1B[J");
222
+ // Determine how many items to show
223
+ const maxVisible = Math.min(this.dropdown.items.length, MultilineInput.MAX_DROPDOWN_ITEMS, Math.max(1, this.getRows() - inputRows - 1));
224
+ // Scroll window around selected index
225
+ let startIdx = 0;
226
+ if (this.dropdown.selectedIndex >= maxVisible) {
227
+ startIdx = this.dropdown.selectedIndex - maxVisible + 1;
228
+ }
229
+ const { triggerChar } = this.dropdown.completion;
230
+ let rowsDrawn = 0;
231
+ for (let i = 0; i < maxVisible; i++) {
232
+ const idx = startIdx + i;
233
+ if (idx >= this.dropdown.items.length)
234
+ break;
235
+ const label = this.dropdown.labels[idx] || (triggerChar + this.dropdown.items[idx]);
236
+ const isSelected = idx === this.dropdown.selectedIndex;
237
+ const truncated = label.length > cols - 4
238
+ ? label.slice(0, cols - 7) + "..."
239
+ : label;
240
+ if (isSelected) {
241
+ process.stdout.write(`\x1B[7m ${truncated} \x1B[0m`);
242
+ }
243
+ else {
244
+ process.stdout.write(`\x1B[90m ${truncated}\x1B[0m`);
245
+ }
246
+ rowsDrawn++;
247
+ if (i < maxVisible - 1) {
248
+ process.stdout.write("\n\r");
249
+ }
250
+ }
251
+ // Scroll indicator
252
+ if (this.dropdown.items.length > maxVisible) {
253
+ const showing = `${startIdx + 1}-${Math.min(startIdx + maxVisible, this.dropdown.items.length)}`;
254
+ process.stdout.write(`\n\r\x1B[90m ↕ ${showing} of ${this.dropdown.items.length}\x1B[0m`);
255
+ rowsDrawn++;
256
+ }
257
+ this.dropdown.renderedRows = rowsDrawn;
258
+ // Move back up to cursor row: rowsDrawn lines back to dropdown start, then `down` back to cursor
259
+ const totalUp = rowsDrawn + down - 1;
260
+ if (totalUp > 0) {
261
+ process.stdout.write(`\x1B[${totalUp}A`);
262
+ }
263
+ // Restore column position
264
+ this.repositionCursor();
265
+ }
266
+ clearDropdown() {
267
+ this.eraseDropdownRows();
268
+ this.dropdown.visible = false;
269
+ this.dropdown.items = [];
270
+ this.dropdown.labels = [];
271
+ this.dropdown.selectedIndex = 0;
272
+ this.dropdown.renderedRows = 0;
273
+ }
274
+ /**
275
+ * Query the completion handler and update dropdown state
276
+ */
277
+ updateDropdown() {
278
+ if (!this.onTabComplete) {
279
+ this.clearDropdown();
280
+ return;
281
+ }
282
+ const result = this.onTabComplete(this.currentLine);
283
+ if (!result || result.matches.length === 0) {
284
+ this.clearDropdown();
285
+ return;
286
+ }
287
+ this.dropdown.visible = true;
288
+ this.dropdown.items = result.matches;
289
+ this.dropdown.labels = result.labels || result.matches.map(m => result.triggerChar + m);
290
+ this.dropdown.selectedIndex = 0;
291
+ this.dropdown.completion = result;
292
+ }
293
+ /**
294
+ * Accept the currently selected dropdown item
295
+ */
296
+ acceptDropdownSelection() {
297
+ if (!this.dropdown.visible || this.dropdown.items.length === 0)
298
+ return;
299
+ const { tokenStart, triggerChar, isFilePath } = this.dropdown.completion;
300
+ const match = this.dropdown.items[this.dropdown.selectedIndex];
301
+ const before = this.currentLine.slice(0, tokenStart);
302
+ const suffix = (isFilePath && match.endsWith("/")) ? "" : " ";
303
+ const completed = before + triggerChar + match + suffix;
304
+ this.currentLine = completed;
305
+ this.cursorPos = completed.length;
306
+ // If it's a directory, keep dropdown open for deeper navigation
307
+ if (isFilePath && match.endsWith("/")) {
308
+ this.redraw();
309
+ this.updateDropdown();
310
+ this.renderDropdown();
311
+ }
312
+ else {
313
+ this.clearDropdown();
314
+ this.redraw();
315
+ }
316
+ }
317
+ // ── Input handling ──────────────────────────────────────────────
318
+ handleData = async (data) => {
319
+ if (this.isProcessing)
320
+ return;
321
+ const str = data.toString("utf8");
322
+ // Paste detection: multiple chars without escape sequence
323
+ if (str.length > 1 && !str.startsWith("\x1B")) {
324
+ this.clearDropdown();
325
+ this.handlePaste(str);
326
+ return;
327
+ }
328
+ const char = str;
329
+ const code = char.charCodeAt(0);
330
+ // ── Dropdown-aware key handling ──
331
+ // Escape sequences while dropdown is open
332
+ if (char.startsWith("\x1B") && this.dropdown.visible) {
333
+ if (char === "\x1B[A") {
334
+ // Arrow Up — move selection up
335
+ if (this.dropdown.selectedIndex > 0) {
336
+ this.dropdown.selectedIndex--;
337
+ this.eraseDropdownRows();
338
+ this.renderDropdown();
339
+ }
340
+ return;
341
+ }
342
+ else if (char === "\x1B[B") {
343
+ // Arrow Down — move selection down
344
+ if (this.dropdown.selectedIndex < this.dropdown.items.length - 1) {
345
+ this.dropdown.selectedIndex++;
346
+ this.eraseDropdownRows();
347
+ this.renderDropdown();
348
+ }
349
+ return;
350
+ }
351
+ else if (char === "\x1B" || char === "\x1B[D" || char === "\x1B[C") {
352
+ // Esc or horizontal arrows — close dropdown
353
+ this.clearDropdown();
354
+ if (char !== "\x1B") {
355
+ this.handleEscapeSequence(char);
356
+ }
357
+ return;
358
+ }
359
+ }
360
+ // Escape (standalone) to close dropdown
361
+ if (code === 27 && char.length === 1 && this.dropdown.visible) {
362
+ this.clearDropdown();
363
+ return;
364
+ }
365
+ switch (code) {
366
+ case 3: // Ctrl+C
367
+ this.clearDropdown();
368
+ if (this.currentLine || this.lines.length > 0) {
369
+ process.stdout.write("\n");
370
+ this.lines = [];
371
+ this.currentLine = "";
372
+ this.cursorPos = 0;
373
+ this.showPrompt();
374
+ }
375
+ else {
376
+ process.stdout.write("\n");
377
+ if (this.onClose)
378
+ this.onClose();
379
+ }
380
+ break;
381
+ case 4: // Ctrl+D
382
+ if (!this.currentLine && this.lines.length === 0) {
383
+ this.clearDropdown();
384
+ process.stdout.write("\n");
385
+ if (this.onClose)
386
+ this.onClose();
387
+ }
388
+ break;
389
+ case 10: // Ctrl+J (newline without submit)
390
+ this.clearDropdown();
391
+ if (this.lines.length < this.maxLines) {
392
+ this.lines.push(this.currentLine);
393
+ this.currentLine = "";
394
+ this.cursorPos = 0;
395
+ process.stdout.write("\n");
396
+ this.showPrompt();
397
+ }
398
+ break;
399
+ case 9: // Tab — accept dropdown selection or trigger completion
400
+ if (this.dropdown.visible) {
401
+ this.acceptDropdownSelection();
402
+ }
403
+ else if (this.onTabComplete) {
404
+ // Open dropdown on Tab if there's a completable context
405
+ this.updateDropdown();
406
+ if (this.dropdown.visible) {
407
+ this.redraw();
408
+ }
409
+ }
410
+ break;
411
+ case 13: // Enter
412
+ if (this.dropdown.visible) {
413
+ // Accept selection instead of submitting
414
+ this.acceptDropdownSelection();
415
+ }
416
+ else {
417
+ this.clearDropdown();
418
+ this.lines.push(this.currentLine);
419
+ const fullInput = this.lines.join("\n");
420
+ this.lines = [];
421
+ this.currentLine = "";
422
+ this.cursorPos = 0;
423
+ process.stdout.write("\n");
424
+ this.isProcessing = true;
425
+ try {
426
+ await this.onSubmit(fullInput);
427
+ }
428
+ finally {
429
+ this.isProcessing = false;
430
+ }
431
+ }
432
+ break;
433
+ case 127: // Backspace (macOS/Linux)
434
+ case 8: // Backspace (Windows)
435
+ if (this.cursorPos > 0) {
436
+ this.currentLine =
437
+ this.currentLine.slice(0, this.cursorPos - 1) +
438
+ this.currentLine.slice(this.cursorPos);
439
+ this.cursorPos--;
440
+ this.redraw();
441
+ this.maybeShowDropdown();
442
+ }
443
+ else if (this.lines.length > 0) {
444
+ const prevLine = this.lines.pop();
445
+ this.cursorPos = prevLine.length;
446
+ this.currentLine = prevLine + this.currentLine;
447
+ this.clearDropdown();
448
+ process.stdout.write("\x1B[A");
449
+ this.redraw();
450
+ }
451
+ break;
452
+ case 21: // Ctrl+U (clear line)
453
+ this.currentLine = this.currentLine.slice(this.cursorPos);
454
+ this.cursorPos = 0;
455
+ this.clearDropdown();
456
+ this.redraw();
457
+ break;
458
+ case 23: // Ctrl+W (delete word)
459
+ if (this.cursorPos > 0) {
460
+ const before = this.currentLine.slice(0, this.cursorPos);
461
+ const after = this.currentLine.slice(this.cursorPos);
462
+ const lastSpace = before.trimEnd().lastIndexOf(" ");
463
+ const newBefore = lastSpace >= 0 ? before.slice(0, lastSpace + 1) : "";
464
+ this.currentLine = newBefore + after;
465
+ this.cursorPos = newBefore.length;
466
+ this.redraw();
467
+ this.maybeShowDropdown();
468
+ }
469
+ break;
470
+ case 12: // Ctrl+L (clear screen)
471
+ this.clearDropdown();
472
+ process.stdout.write("\x1B[2J\x1B[H");
473
+ this.redrawAll();
474
+ break;
475
+ default:
476
+ if (char.startsWith("\x1B")) {
477
+ this.clearDropdown();
478
+ this.handleEscapeSequence(char);
479
+ }
480
+ else if (code >= 32) {
481
+ // Regular character
482
+ this.currentLine =
483
+ this.currentLine.slice(0, this.cursorPos) +
484
+ char +
485
+ this.currentLine.slice(this.cursorPos);
486
+ this.cursorPos++;
487
+ this.redraw();
488
+ this.maybeShowDropdown();
489
+ }
490
+ }
491
+ };
492
+ /**
493
+ * Check if the current input context should trigger a live dropdown.
494
+ * "/" commands: always live (few items, instant feedback).
495
+ * "@" files: only update if dropdown is already open (Tab to initiate).
496
+ */
497
+ maybeShowDropdown() {
498
+ if (!this.onTabComplete)
499
+ return;
500
+ const textUpToCursor = this.currentLine.slice(0, this.cursorPos);
501
+ // "/" commands: live dropdown as you type
502
+ if (textUpToCursor.startsWith("/") && !textUpToCursor.includes(" ")) {
503
+ this.updateDropdown();
504
+ if (this.dropdown.visible) {
505
+ this.renderDropdown();
506
+ }
507
+ return;
508
+ }
509
+ // "@" files: if dropdown already open, update it as you type deeper
510
+ if (this.dropdown.visible && this.isInAtContext(textUpToCursor)) {
511
+ this.updateDropdown();
512
+ if (this.dropdown.visible) {
513
+ this.renderDropdown();
514
+ }
515
+ return;
516
+ }
517
+ // No trigger context — close dropdown
518
+ if (this.dropdown.visible) {
519
+ this.clearDropdown();
520
+ }
521
+ }
522
+ /**
523
+ * Check if text ends with an active @path context (no space after @)
524
+ */
525
+ isInAtContext(text) {
526
+ const atIdx = text.lastIndexOf("@");
527
+ if (atIdx < 0)
528
+ return false;
529
+ const afterAt = text.slice(atIdx + 1);
530
+ return !afterAt.includes(" ");
531
+ }
532
+ handlePaste(str) {
533
+ const pastedLines = str.split(/\r?\n/);
534
+ this.currentLine =
535
+ this.currentLine.slice(0, this.cursorPos) +
536
+ pastedLines[0] +
537
+ this.currentLine.slice(this.cursorPos);
538
+ this.cursorPos += pastedLines[0].length;
539
+ for (let i = 1; i < pastedLines.length; i++) {
540
+ if (this.lines.length >= this.maxLines)
541
+ break;
542
+ this.lines.push(this.currentLine);
543
+ this.currentLine = pastedLines[i];
544
+ this.cursorPos = this.currentLine.length;
545
+ }
546
+ this.redrawAll();
547
+ }
548
+ handleEscapeSequence(seq) {
549
+ if (seq === "\x1B[D") {
550
+ if (this.cursorPos > 0) {
551
+ this.cursorPos--;
552
+ this.redraw();
553
+ }
554
+ }
555
+ else if (seq === "\x1B[C") {
556
+ if (this.cursorPos < this.currentLine.length) {
557
+ this.cursorPos++;
558
+ this.redraw();
559
+ }
560
+ }
561
+ else if (seq === "\x1B[A") {
562
+ // Arrow Up — could implement history
563
+ }
564
+ else if (seq === "\x1B[B") {
565
+ // Arrow Down — could implement history
566
+ }
567
+ else if (seq === "\x1B[H" || seq === "\x1B[1~") {
568
+ this.cursorPos = 0;
569
+ this.redraw();
570
+ }
571
+ else if (seq === "\x1B[F" || seq === "\x1B[4~") {
572
+ this.cursorPos = this.currentLine.length;
573
+ this.redraw();
574
+ }
575
+ else if (seq === "\x1B[3~") {
576
+ if (this.cursorPos < this.currentLine.length) {
577
+ this.currentLine =
578
+ this.currentLine.slice(0, this.cursorPos) +
579
+ this.currentLine.slice(this.cursorPos + 1);
580
+ this.redraw();
581
+ }
582
+ }
583
+ }
584
+ redrawAll() {
585
+ const cols = this.getColumns();
586
+ let totalTermRows = 0;
587
+ for (let i = 0; i < this.lines.length; i++) {
588
+ const p = i === 0 ? this.prompt : this.continuationPrompt;
589
+ totalTermRows += this.wrappedRows(p, this.lines[i]);
590
+ }
591
+ const currentPrompt = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
592
+ totalTermRows += this.wrappedRows(currentPrompt, this.currentLine);
593
+ const currentLineRows = this.wrappedRows(currentPrompt, this.currentLine);
594
+ const rowsAboveCurrent = totalTermRows - currentLineRows;
595
+ const cursorRowInCurrent = currentLineRows - 1;
596
+ const moveUp = rowsAboveCurrent + cursorRowInCurrent;
597
+ if (moveUp > 0) {
598
+ process.stdout.write(`\x1B[${moveUp}A`);
599
+ }
600
+ for (let i = 0; i < totalTermRows; i++) {
601
+ process.stdout.write("\r\x1B[K");
602
+ if (i < totalTermRows - 1) {
603
+ process.stdout.write("\x1B[1B");
604
+ }
605
+ }
606
+ if (totalTermRows > 1) {
607
+ process.stdout.write(`\x1B[${totalTermRows - 1}A`);
608
+ }
609
+ process.stdout.write("\r");
610
+ for (let i = 0; i < this.lines.length; i++) {
611
+ const p = i === 0 ? this.prompt : this.continuationPrompt;
612
+ process.stdout.write(p + this.lines[i] + "\n");
613
+ }
614
+ const promptText = this.lines.length === 0 ? this.prompt : this.continuationPrompt;
615
+ const promptVisible = this.visibleLength(promptText);
616
+ process.stdout.write(promptText);
617
+ process.stdout.write(this.currentLine);
618
+ if (this.cursorPos < this.currentLine.length) {
619
+ const newRows = this.wrappedRows(promptText, this.currentLine);
620
+ const endRow = newRows - 1;
621
+ const targetAbsPos = promptVisible + this.cursorPos;
622
+ const targetRow = Math.floor(targetAbsPos / cols);
623
+ const targetCol = targetAbsPos % cols;
624
+ if (endRow > targetRow) {
625
+ process.stdout.write(`\x1B[${endRow - targetRow}A`);
626
+ }
627
+ process.stdout.write(`\r\x1B[${targetCol}C`);
628
+ }
629
+ }
630
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Narrative Intelligence Integration for mia-code
3
+ *
4
+ * Exports the core narrative intelligence components:
5
+ * - NarrativeRouter: Routes events to miaco/miatel/miawa based on three-universe analysis
6
+ * - MiaCodeTracer: Observability for mia-code operations
7
+ */
8
+ export { NarrativeRouter, getNarrativeRouter, MiaCodeEvent, RoutingResult, UNIVERSE_TO_CLI, CLI_TO_UNIVERSE, Universe, ThreeUniverseAnalysis, StoryBeat, NarrativeFunction, EmotionalTone, CoherenceResult, } from "./router.js";
9
+ export { MiaCodeTracer, getTracer, removeTracer, MiaCodeOperation, NarrativeEventType, NarrativeMetrics, } from "./tracer.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Narrative Intelligence Integration for mia-code
3
+ *
4
+ * Exports the core narrative intelligence components:
5
+ * - NarrativeRouter: Routes events to miaco/miatel/miawa based on three-universe analysis
6
+ * - MiaCodeTracer: Observability for mia-code operations
7
+ */
8
+ // Router exports
9
+ export { NarrativeRouter, getNarrativeRouter, UNIVERSE_TO_CLI, CLI_TO_UNIVERSE, Universe, NarrativeFunction, EmotionalTone, } from "./router.js";
10
+ // Tracer exports
11
+ export { MiaCodeTracer, getTracer, removeTracer, NarrativeEventType, } from "./tracer.js";