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.
- package/.claude/settings.local.json +9 -0
- package/.coaia/pde/d77620fc-1cd9-47e2-ba00-c03e114e42e9.jsonl +16 -0
- package/.coaia/pde/de44d838-b58b-4e91-b791-dd3b0f940ed1.jsonl +60 -0
- package/.gemini/settings.json +8 -0
- package/.hch/issue_.env +4 -0
- package/.hch/issue_add__2601211715.json +77 -0
- package/.hch/issue_add__2601211715.md +4 -0
- package/.hch/issue_add__2602242020.json +78 -0
- package/.hch/issue_add__2602242020.md +7 -0
- package/.hch/issues.json +2312 -0
- package/.hch/issues.md +30 -0
- package/260123084839.coaia-narrative.autoRevisionOfInitial_NewStructuralTensionChart-to-initiate-HierarchicalThinking.txt +5 -0
- package/2602010101.issue.txt +31 -0
- package/BUGS.md +242 -0
- package/CLAUDE.md +2 -0
- package/ENHANCEMENTS.md +129 -0
- package/FEATURES_ENDING_SESSIONS.md +21 -0
- package/FIXES.md +114 -0
- package/GUILLAUME.md +77 -0
- package/KINSHIP.md +50 -0
- package/LAUNCH__session_id__MiaCodeNextWorkReviewAndCommits_2601312020.sh +7 -0
- package/PHASE_2.md +153 -0
- package/PHASE_2_IMPLEMENTATION.md +134 -0
- package/README.md +203 -0
- package/RESUME__issueMaker__540244c2-b096-40d8-8c3f-398408d3e0eb.2602041757.sh +1 -0
- package/RUN_COPILOT_with_related_folders__260130.sh +2 -0
- package/WS__mia-code__260214__IAIP_PDE.code-workspace +29 -0
- package/WS__mia-code__src332__260122.code-workspace +23 -0
- package/_env.sh +12 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +679 -0
- package/dist/commands.d.ts +43 -0
- package/dist/commands.js +108 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +57 -0
- package/dist/formatting.d.ts +12 -0
- package/dist/formatting.js +133 -0
- package/dist/geminiHeadless.d.ts +25 -0
- package/dist/geminiHeadless.js +246 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -0
- package/dist/mcp/config-generator.d.ts +23 -0
- package/dist/mcp/config-generator.js +116 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.js +43 -0
- package/dist/mcp/miaco-server.d.ts +15 -0
- package/dist/mcp/miaco-server.js +161 -0
- package/dist/mcp/miatel-server.d.ts +15 -0
- package/dist/mcp/miatel-server.js +123 -0
- package/dist/mcp/miawa-server.d.ts +15 -0
- package/dist/mcp/miawa-server.js +125 -0
- package/dist/mcp/utils.d.ts +51 -0
- package/dist/mcp/utils.js +76 -0
- package/dist/multiline-input.d.ts +98 -0
- package/dist/multiline-input.js +630 -0
- package/dist/narrative/index.d.ts +9 -0
- package/dist/narrative/index.js +11 -0
- package/dist/narrative/router.d.ts +89 -0
- package/dist/narrative/router.js +186 -0
- package/dist/narrative/tracer.d.ts +75 -0
- package/dist/narrative/tracer.js +180 -0
- package/dist/sessionStore.d.ts +10 -0
- package/dist/sessionStore.js +93 -0
- package/dist/types.d.ts +44 -0
- package/dist/types.js +1 -0
- package/dist/unifier.d.ts +6 -0
- package/dist/unifier.js +147 -0
- package/issue-358--architecture/ARCHITECTURE_OVERVIEW.md +60 -0
- package/issue-358--architecture/CLI_INTEGRATION.md +61 -0
- package/issue-358--architecture/COVER_ART_BRIEF.md +68 -0
- package/issue-358--architecture/MEMORY_SYSTEM.md +89 -0
- package/issue-358--architecture/PERSONA_REGISTRY.md +97 -0
- package/issue-358--architecture/PODCAST_PRODUCTION_PLAN.md +61 -0
- package/issue-358--architecture/PODCAST_SCRIPT_FINAL.md +109 -0
- package/issue-358--architecture/PROTOTYPE_CHARACTER_SPEC.md +59 -0
- package/issue-358--architecture/RESOURCES.md +41 -0
- package/issue-358--architecture/TEAM_LISTENING_GUIDE.md +53 -0
- package/llms-gemini-cli.txt +145 -0
- package/package.json +39 -0
- package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/checkpoints/index.md +6 -0
- package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/events.jsonl +213 -0
- package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/plan.md +243 -0
- package/samples/copilot/session-state/be76abaa-a27f-4725-b2a9-22fb45f7e0f7/workspace.yaml +5 -0
- package/src/cli.ts +742 -0
- package/src/commands.ts +127 -0
- package/src/config.ts +67 -0
- package/src/formatting.ts +157 -0
- package/src/geminiHeadless.ts +300 -0
- package/src/index.ts +194 -0
- package/src/mcp/config-generator.ts +141 -0
- package/src/mcp/index.ts +55 -0
- package/src/mcp/miaco-server.ts +199 -0
- package/src/mcp/miatel-server.ts +138 -0
- package/src/mcp/miawa-server.ts +158 -0
- package/src/mcp/utils.ts +121 -0
- package/src/multiline-input.ts +739 -0
- package/src/narrative/index.ts +33 -0
- package/src/narrative/router.ts +260 -0
- package/src/narrative/tracer.ts +249 -0
- package/src/sessionStore.ts +111 -0
- package/src/types.ts +49 -0
- package/src/unifier.ts +171 -0
- 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";
|