@thegitai/cli 1.0.0-preview.4 → 1.0.0-preview.40
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 +49 -4
- package/dist/bin/ai.js +119 -111
- package/dist/bin/browser-host.js +265 -0
- package/dist/src/agent-mode.js +11 -6
- package/dist/src/api/auth.js +6 -4
- package/dist/src/api/browser-login.js +152 -37
- package/dist/src/api/chat.js +286 -44
- package/dist/src/api/contracts.js +55 -1
- package/dist/src/api/default-host.js +1 -0
- package/dist/src/api/http.js +127 -7
- package/dist/src/api/models.js +29 -10
- package/dist/src/background-jobs.js +4 -2
- package/dist/src/browser/bridge.js +232 -0
- package/dist/src/browser/framing.js +42 -0
- package/dist/src/browser/native-host.js +209 -0
- package/dist/src/browser/protocol.js +46 -0
- package/dist/src/browser/session-bridge.js +104 -0
- package/dist/src/cli-args.js +19 -5
- package/dist/src/client-environment.js +2 -0
- package/dist/src/core/clipboard.js +7 -13
- package/dist/src/core/image-limits.js +56 -0
- package/dist/src/core/image-path-extractor.js +70 -3
- package/dist/src/core/session-image-store.js +246 -0
- package/dist/src/edit-journal.js +33 -2
- package/dist/src/executor.js +25 -3
- package/dist/src/help-text.js +58 -17
- package/dist/src/patcher.js +43 -4
- package/dist/src/permissions.js +288 -0
- package/dist/src/session-safety.js +0 -12
- package/dist/src/session-store.js +121 -20
- package/dist/src/session.js +19 -4
- package/dist/src/signin.js +58 -0
- package/dist/src/tool-executor.js +12 -46
- package/dist/src/tools/browser.js +628 -0
- package/dist/src/tools/delete-file.js +20 -4
- package/dist/src/tools/index.js +32 -10
- package/dist/src/tools/patch-file.js +12 -26
- package/dist/src/tools/read-image-file.js +85 -0
- package/dist/src/tools/replace-document-text.js +28 -18
- package/dist/src/tools/restore-checkpoint.js +0 -1
- package/dist/src/tools/run-command.js +14 -71
- package/dist/src/tools/run-node-script.js +12 -81
- package/dist/src/tools/save-generated-image.js +120 -0
- package/dist/src/tools/str-replace.js +12 -26
- package/dist/src/tools/undo-edit.js +1 -6
- package/dist/src/tools/write-file.js +67 -11
- package/dist/src/ui/prompt-history-store.js +1 -1
- package/dist/src/ui/repl.js +802 -195
- package/dist/src/ui/tui/bridge.js +10 -0
- package/dist/src/ui/tui/build-frame.js +517 -165
- package/dist/src/ui/tui/composer-layout.js +76 -0
- package/dist/src/ui/tui/markdown-render.js +81 -73
- package/dist/src/ui/tui/shell-input.js +227 -67
- package/dist/src/ui/tui/terminal-theme.js +28 -0
- package/dist/src/ui/tui/terminal-title.js +3 -0
- package/dist/src/ui/tui/terminal-writes.js +48 -0
- package/dist/src/ui/tui/text.js +158 -4
- package/dist/src/ui/tui/user-input.js +568 -0
- package/dist/src/utils.js +9 -0
- package/package.json +20 -8
- package/dist/src/markdown-renderer.js +0 -112
- package/dist/src/project-index.js +0 -221
- package/dist/src/tools/code-intel.js +0 -472
- package/dist/src/tools/find-symbol.js +0 -70
- package/dist/src/tools/hover-symbol.js +0 -95
- package/dist/src/tools/list-symbols.js +0 -55
- package/dist/src/tools/search-code.js +0 -37
- package/dist/src/tools/signature-help.js +0 -118
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const COMPOSER_PROMPT_LABEL_IDLE = '❯ ';
|
|
2
|
+
export const COMPOSER_PROMPT_LABEL_BUSY = 'queue> ';
|
|
3
|
+
export function composerPromptLabel(state) {
|
|
4
|
+
return state.busy ? COMPOSER_PROMPT_LABEL_BUSY : COMPOSER_PROMPT_LABEL_IDLE;
|
|
5
|
+
}
|
|
6
|
+
export function frameContentWidth(cols) {
|
|
7
|
+
return Math.max(20, Math.floor(cols * 0.95) - 2);
|
|
8
|
+
}
|
|
9
|
+
export function composerInputWidth(cols, state) {
|
|
10
|
+
return Math.max(1, frameContentWidth(cols) - composerPromptLabel(state).length);
|
|
11
|
+
}
|
|
12
|
+
function buildComposerLayout(input, cursor, width) {
|
|
13
|
+
const safeWidth = Math.max(1, width);
|
|
14
|
+
const normalizedCursor = Math.min(Math.max(cursor, 0), input.length);
|
|
15
|
+
const rows = [{ cursorOffsets: [0], text: '' }];
|
|
16
|
+
let cursorRow = 0;
|
|
17
|
+
let cursorCol = 0;
|
|
18
|
+
let capturedCursor = false;
|
|
19
|
+
const currentRow = () => rows[rows.length - 1];
|
|
20
|
+
const addRow = (offset) => {
|
|
21
|
+
rows.push({ cursorOffsets: [offset], text: '' });
|
|
22
|
+
};
|
|
23
|
+
const captureCursor = () => {
|
|
24
|
+
if (capturedCursor)
|
|
25
|
+
return;
|
|
26
|
+
if (currentRow().text.length >= safeWidth) {
|
|
27
|
+
addRow(normalizedCursor);
|
|
28
|
+
}
|
|
29
|
+
cursorRow = rows.length - 1;
|
|
30
|
+
cursorCol = currentRow().text.length;
|
|
31
|
+
capturedCursor = true;
|
|
32
|
+
};
|
|
33
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
34
|
+
const char = input[index] ?? '';
|
|
35
|
+
if (char !== '\n' &&
|
|
36
|
+
char !== '\r' &&
|
|
37
|
+
currentRow().text.length >= safeWidth) {
|
|
38
|
+
addRow(index);
|
|
39
|
+
}
|
|
40
|
+
if (index === normalizedCursor) {
|
|
41
|
+
captureCursor();
|
|
42
|
+
}
|
|
43
|
+
if (char === '\r') {
|
|
44
|
+
currentRow().cursorOffsets[currentRow().text.length] = index + 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (char === '\n') {
|
|
48
|
+
currentRow().cursorOffsets[currentRow().text.length] =
|
|
49
|
+
input[index - 1] === '\r' ? index - 1 : index;
|
|
50
|
+
addRow(index + 1);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
currentRow().text += char;
|
|
54
|
+
currentRow().cursorOffsets.push(index + 1);
|
|
55
|
+
}
|
|
56
|
+
if (!capturedCursor) {
|
|
57
|
+
captureCursor();
|
|
58
|
+
}
|
|
59
|
+
return { cursorCol, cursorRow, rows };
|
|
60
|
+
}
|
|
61
|
+
export function splitComposerInput(input, cursor, width) {
|
|
62
|
+
const layout = buildComposerLayout(input, cursor, width);
|
|
63
|
+
return {
|
|
64
|
+
cursorCol: layout.cursorCol,
|
|
65
|
+
cursorRow: layout.cursorRow,
|
|
66
|
+
rows: layout.rows.map((row) => row.text),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function moveComposerCursorVertically(input, cursor, width, direction) {
|
|
70
|
+
const layout = buildComposerLayout(input, cursor, width);
|
|
71
|
+
const targetRow = layout.rows[layout.cursorRow + direction];
|
|
72
|
+
if (!targetRow)
|
|
73
|
+
return null;
|
|
74
|
+
const targetCol = Math.min(layout.cursorCol, targetRow.text.length);
|
|
75
|
+
return targetRow.cursorOffsets[targetCol] ?? null;
|
|
76
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { line, plainLine, span, wrapText } from './text.js';
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
import { displayWidth, line, padToWidth, plainLine, sliceToWidth, span, wrapText, wrapToWidth, } from './text.js';
|
|
2
|
+
const MIN_TABLE_COLUMN_WIDTH = 3;
|
|
3
|
+
function tableRowOverhead(columnCount) {
|
|
4
|
+
return 3 * columnCount + 1;
|
|
5
|
+
}
|
|
4
6
|
function parseInlineSegments(text) {
|
|
5
7
|
const source = String(text ?? '');
|
|
6
8
|
const segments = [];
|
|
@@ -110,45 +112,37 @@ function stripInlineFormattingForWidth(text) {
|
|
|
110
112
|
.replace(/\*\*([^*]+)\*\*/g, '$1');
|
|
111
113
|
}
|
|
112
114
|
function fitTableColumnWidths(columnWidths, maxWidth) {
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
115
|
-
return
|
|
116
|
-
const
|
|
117
|
-
const budget = Math.
|
|
118
|
-
const total =
|
|
119
|
-
if (
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (let index = 1; index < widths.length; index++) {
|
|
130
|
-
if (widths[index] - scaled[index] > widths[targetIndex] - scaled[targetIndex]) {
|
|
131
|
-
targetIndex = index;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
scaled[targetIndex]++;
|
|
135
|
-
remaining--;
|
|
115
|
+
const columnCount = columnWidths.length;
|
|
116
|
+
if (columnCount === 0)
|
|
117
|
+
return [];
|
|
118
|
+
const natural = columnWidths.map((width) => Math.max(1, Math.floor(width)));
|
|
119
|
+
const budget = Math.floor(maxWidth) - tableRowOverhead(columnCount);
|
|
120
|
+
const total = natural.reduce((sum, width) => sum + width, 0);
|
|
121
|
+
if (budget >= total)
|
|
122
|
+
return natural;
|
|
123
|
+
const floorWidth = Math.max(1, Math.min(MIN_TABLE_COLUMN_WIDTH, Math.floor(budget / columnCount)));
|
|
124
|
+
const widths = natural.map((width) => Math.min(width, floorWidth));
|
|
125
|
+
let used = widths.reduce((sum, width) => sum + width, 0);
|
|
126
|
+
if (used > budget) {
|
|
127
|
+
const share = Math.max(1, Math.floor(budget / columnCount));
|
|
128
|
+
for (let index = 0; index < columnCount; index++)
|
|
129
|
+
widths[index] = share;
|
|
130
|
+
used = share * columnCount;
|
|
136
131
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
132
|
+
let remaining = budget - used;
|
|
133
|
+
while (remaining > 0) {
|
|
134
|
+
let grew = false;
|
|
135
|
+
for (let index = 0; index < columnCount && remaining > 0; index++) {
|
|
136
|
+
if (widths[index] >= natural[index])
|
|
141
137
|
continue;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
138
|
+
widths[index]++;
|
|
139
|
+
remaining--;
|
|
140
|
+
grew = true;
|
|
145
141
|
}
|
|
146
|
-
if (
|
|
142
|
+
if (!grew)
|
|
147
143
|
break;
|
|
148
|
-
scaled[targetIndex]--;
|
|
149
|
-
remaining++;
|
|
150
144
|
}
|
|
151
|
-
return
|
|
145
|
+
return widths;
|
|
152
146
|
}
|
|
153
147
|
function normalizeMarkdownTableCells(cells, columnCount) {
|
|
154
148
|
return Array.from({ length: columnCount }, (_, index) => cells[index] ?? '');
|
|
@@ -176,7 +170,7 @@ function parseMarkdownTableBlock(lines, startIndex) {
|
|
|
176
170
|
const normalizedHeaders = normalizeMarkdownTableCells(headers, columnCount);
|
|
177
171
|
const columnWidths = normalizedHeaders.map((header, columnIndex) => {
|
|
178
172
|
const values = [header, ...rows.map((row) => row[columnIndex] ?? '')];
|
|
179
|
-
return Math.max(
|
|
173
|
+
return Math.max(MIN_TABLE_COLUMN_WIDTH, ...values.map((value) => displayWidth(stripInlineFormattingForWidth(value))));
|
|
180
174
|
});
|
|
181
175
|
return {
|
|
182
176
|
nextIndex,
|
|
@@ -280,24 +274,42 @@ function wrapInlineToLines(text, width, bodyColor, prefix = '') {
|
|
|
280
274
|
rowWidth = 0;
|
|
281
275
|
};
|
|
282
276
|
const appendSpan = (part) => {
|
|
283
|
-
|
|
284
|
-
const limit = safeWidth - (indent ? prefix.length : 0);
|
|
277
|
+
let current = rows[rows.length - 1];
|
|
285
278
|
let remaining = part.text;
|
|
286
279
|
while (remaining.length > 0) {
|
|
280
|
+
const limit = safeWidth - (rows.length === 1 && indent ? displayWidth(prefix) : 0);
|
|
287
281
|
const room = limit - rowWidth;
|
|
288
282
|
if (room <= 0) {
|
|
289
283
|
startRow();
|
|
284
|
+
current = rows[rows.length - 1];
|
|
290
285
|
continue;
|
|
291
286
|
}
|
|
292
|
-
|
|
287
|
+
const width = displayWidth(remaining);
|
|
288
|
+
if (width <= room) {
|
|
293
289
|
current.push(span(remaining, styleFromSpan(part)));
|
|
294
|
-
rowWidth +=
|
|
290
|
+
rowWidth += width;
|
|
295
291
|
remaining = '';
|
|
296
292
|
break;
|
|
297
293
|
}
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
if (rowWidth > 0) {
|
|
295
|
+
if (/^\s+$/.test(remaining)) {
|
|
296
|
+
remaining = '';
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
startRow();
|
|
300
|
+
current = rows[rows.length - 1];
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const head = sliceToWidth(remaining, room);
|
|
304
|
+
if (!head || (displayWidth(head) > room && current.length > 0)) {
|
|
305
|
+
startRow();
|
|
306
|
+
current = rows[rows.length - 1];
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
current.push(span(head, styleFromSpan(part)));
|
|
310
|
+
remaining = remaining.slice(head.length);
|
|
300
311
|
startRow();
|
|
312
|
+
current = rows[rows.length - 1];
|
|
301
313
|
}
|
|
302
314
|
};
|
|
303
315
|
for (const segment of segments) {
|
|
@@ -316,36 +328,35 @@ function wrapInlineToLines(text, width, bodyColor, prefix = '') {
|
|
|
316
328
|
return line(...bodySpans);
|
|
317
329
|
});
|
|
318
330
|
}
|
|
319
|
-
function padCell(text, width) {
|
|
320
|
-
const plain = stripInlineFormattingForWidth(text);
|
|
321
|
-
if (plain.length >= width)
|
|
322
|
-
return plain.slice(0, width);
|
|
323
|
-
return plain + ' '.repeat(width - plain.length);
|
|
324
|
-
}
|
|
325
331
|
function renderTableLines(table, width) {
|
|
326
332
|
const columnWidths = fitTableColumnWidths(table.columnWidths, width);
|
|
333
|
+
const border = (left, joint, right) => line(span(left +
|
|
334
|
+
columnWidths.map((colWidth) => '─'.repeat(colWidth + 2)).join(joint) +
|
|
335
|
+
right, { color: 'cyan', dim: true }));
|
|
327
336
|
const renderRow = (cells, bold) => {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
337
|
+
const wrapped = columnWidths.map((colWidth, index) => wrapToWidth(stripInlineFormattingForWidth(cells[index] ?? ''), colWidth));
|
|
338
|
+
const height = Math.max(1, ...wrapped.map((cellLines) => cellLines.length));
|
|
339
|
+
const rows = [];
|
|
340
|
+
for (let row = 0; row < height; row++) {
|
|
341
|
+
const spans = [span('│', { color: 'cyan' })];
|
|
342
|
+
for (let column = 0; column < columnWidths.length; column++) {
|
|
343
|
+
const text = wrapped[column]?.[row] ?? '';
|
|
344
|
+
spans.push(span(` ${padToWidth(text, columnWidths[column])} `, {
|
|
345
|
+
color: 'cyan',
|
|
346
|
+
bold,
|
|
347
|
+
}));
|
|
348
|
+
spans.push(span('│', { color: 'cyan' }));
|
|
349
|
+
}
|
|
350
|
+
rows.push(line(...spans));
|
|
351
|
+
}
|
|
352
|
+
return rows;
|
|
333
353
|
};
|
|
334
|
-
const separator = line(span('├' +
|
|
335
|
-
columnWidths.map((colWidth) => '─'.repeat(colWidth + 2)).join('┼') +
|
|
336
|
-
'┤', { color: 'cyan', dim: true }));
|
|
337
|
-
const top = line(span('┌' +
|
|
338
|
-
columnWidths.map((colWidth) => '─'.repeat(colWidth + 2)).join('┬') +
|
|
339
|
-
'┐', { color: 'cyan', dim: true }));
|
|
340
|
-
const bottom = line(span('└' +
|
|
341
|
-
columnWidths.map((colWidth) => '─'.repeat(colWidth + 2)).join('┴') +
|
|
342
|
-
'┘', { color: 'cyan', dim: true }));
|
|
343
354
|
return [
|
|
344
|
-
|
|
345
|
-
renderRow(table.headers, true),
|
|
346
|
-
|
|
347
|
-
...table.rows.
|
|
348
|
-
|
|
355
|
+
border('┌', '┬', '┐'),
|
|
356
|
+
...renderRow(table.headers, true),
|
|
357
|
+
border('├', '┼', '┤'),
|
|
358
|
+
...table.rows.flatMap((row) => renderRow(row, false)),
|
|
359
|
+
border('└', '┴', '┘'),
|
|
349
360
|
];
|
|
350
361
|
}
|
|
351
362
|
function getEntryColor(kind) {
|
|
@@ -410,10 +421,7 @@ export function renderFormattedBodyLines(body, width, kind) {
|
|
|
410
421
|
}
|
|
411
422
|
if (formattedLine.kind === 'table' && formattedLine.table) {
|
|
412
423
|
output.push(...renderTableLines(formattedLine.table, bodyWidth).map((tableLine) => {
|
|
413
|
-
const spans = tableLine.spans.map((part) =>
|
|
414
|
-
...part,
|
|
415
|
-
text: ` ${part.text}`,
|
|
416
|
-
}));
|
|
424
|
+
const spans = tableLine.spans.map((part, index) => index === 0 ? { ...part, text: ` ${part.text}` } : part);
|
|
417
425
|
return { spans };
|
|
418
426
|
}));
|
|
419
427
|
continue;
|