@thegitai/cli 1.0.0-preview.5 → 1.0.0-preview.50
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 +93 -4
- package/dist/bin/ai.js +142 -112
- package/dist/bin/browser-host.js +265 -0
- package/dist/src/agent-mode.js +32 -7
- package/dist/src/api/auth.js +10 -4
- package/dist/src/api/browser-login.js +149 -37
- package/dist/src/api/chat.js +680 -166
- package/dist/src/api/contracts.js +55 -1
- package/dist/src/api/default-host.js +1 -0
- package/dist/src/api/http.js +151 -7
- package/dist/src/api/models.js +29 -10
- package/dist/src/api/startup-handshake.js +36 -0
- package/dist/src/api/turn-recovery.js +189 -0
- package/dist/src/background-jobs.js +4 -2
- package/dist/src/browser/bridge.js +262 -0
- package/dist/src/browser/framing.js +42 -0
- package/dist/src/browser/native-host.js +219 -0
- package/dist/src/browser/protocol.js +46 -0
- package/dist/src/browser/session-bridge.js +130 -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 +112 -21
- package/dist/src/patcher.js +43 -4
- package/dist/src/permissions.js +288 -0
- package/dist/src/preferences.js +39 -0
- package/dist/src/quota-notice.js +35 -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 +13 -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 +949 -279
- package/dist/src/ui/slash-commands.js +121 -0
- package/dist/src/ui/tui/attention.js +25 -0
- package/dist/src/ui/tui/bridge.js +38 -5
- package/dist/src/ui/tui/build-frame.js +686 -223
- package/dist/src/ui/tui/composer-layout.js +76 -0
- package/dist/src/ui/tui/desktop-notification.js +118 -0
- package/dist/src/ui/tui/markdown-render.js +11 -2
- package/dist/src/ui/tui/shell-input.js +256 -73
- package/dist/src/ui/tui/shortcuts.js +61 -0
- package/dist/src/ui/tui/terminal-theme.js +28 -0
- package/dist/src/ui/tui/user-input.js +593 -0
- package/dist/src/update-check.js +205 -0
- package/dist/src/usage.js +5 -2
- package/dist/src/utils.js +13 -0
- package/dist/src/version.js +52 -2
- package/package.json +22 -9
- package/dist/src/project-index.js +0 -233
- 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
package/dist/src/session.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { normalizeAgentMode, } from './agent-mode.js';
|
|
3
|
+
import { createSessionGrants, } from './permissions.js';
|
|
3
4
|
import { createSessionSafetyState, } from './session-safety.js';
|
|
5
|
+
import { closeBrowserBridge } from './browser/session-bridge.js';
|
|
4
6
|
import { clampInteger } from './utils.js';
|
|
5
7
|
const DEFAULT_MAX_TOOL_STEPS = 32;
|
|
6
8
|
function defaultStatus(message) {
|
|
@@ -31,7 +33,7 @@ function preserveProviderSelection(serverState) {
|
|
|
31
33
|
: null;
|
|
32
34
|
return providerSelection ? { providerSelection } : {};
|
|
33
35
|
}
|
|
34
|
-
export function createSession({ rootDir, autoYes = false, agentMode, modelId, maxToolSteps = DEFAULT_MAX_TOOL_STEPS,
|
|
36
|
+
export function createSession({ rootDir, autoYes = false, agentMode, modelId, maxToolSteps = DEFAULT_MAX_TOOL_STEPS, maxToolStepsWasExplicit = false, requestPermission = null, requestSudoPassword = null, requestUserInput = null, onStatus = null, onContextLog = null, onToolEvent = null, env = process.env, sessionId = createSessionId(), sessionName = null, history = [], serverState = null, editJournal = [], stickyFilePaths = [], editCounter = 0, safety = createSessionSafetyState(), }) {
|
|
35
37
|
const createdAt = new Date().toISOString();
|
|
36
38
|
const initialAgentMode = normalizeAgentMode(agentMode ?? (autoYes ? 'auto-accept' : 'default'));
|
|
37
39
|
return {
|
|
@@ -40,12 +42,14 @@ export function createSession({ rootDir, autoYes = false, agentMode, modelId, ma
|
|
|
40
42
|
autoYes: initialAgentMode === 'auto-accept',
|
|
41
43
|
agentMode: initialAgentMode,
|
|
42
44
|
maxToolSteps: clampInteger(maxToolSteps, DEFAULT_MAX_TOOL_STEPS, 128),
|
|
45
|
+
maxToolStepsWasExplicit: maxToolStepsWasExplicit === true,
|
|
43
46
|
onStatus: onStatus ?? defaultStatus,
|
|
44
47
|
onContextLog: onContextLog ?? defaultContextLog,
|
|
45
48
|
onToolEvent,
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
grants: createSessionGrants(),
|
|
50
|
+
requestPermission,
|
|
48
51
|
requestSudoPassword,
|
|
52
|
+
requestUserInput,
|
|
49
53
|
history: JSON.parse(JSON.stringify(history)),
|
|
50
54
|
initialized: true,
|
|
51
55
|
sessionId,
|
|
@@ -69,8 +73,17 @@ export function createSession({ rootDir, autoYes = false, agentMode, modelId, ma
|
|
|
69
73
|
serverState: cloneOpaqueState(serverState),
|
|
70
74
|
};
|
|
71
75
|
}
|
|
76
|
+
export function startNewConversation(session) {
|
|
77
|
+
clearConversation(session);
|
|
78
|
+
const createdAt = new Date().toISOString();
|
|
79
|
+
session.sessionId = createSessionId();
|
|
80
|
+
session.sessionName = null;
|
|
81
|
+
session.sessionCreatedAt = createdAt;
|
|
82
|
+
session.sessionUpdatedAt = createdAt;
|
|
83
|
+
}
|
|
72
84
|
export function clearConversation(session) {
|
|
73
85
|
session.history = [];
|
|
86
|
+
session.grants = createSessionGrants();
|
|
74
87
|
session.serverState = preserveProviderSelection(session.serverState);
|
|
75
88
|
session.turnState = {
|
|
76
89
|
id: null,
|
|
@@ -86,7 +99,9 @@ export function clearConversation(session) {
|
|
|
86
99
|
safety: createSessionSafetyState(),
|
|
87
100
|
};
|
|
88
101
|
}
|
|
89
|
-
export async function disposeSession(_session) {
|
|
102
|
+
export async function disposeSession(_session) {
|
|
103
|
+
closeBrowserBridge();
|
|
104
|
+
}
|
|
90
105
|
export function switchModel(session, modelId) {
|
|
91
106
|
session.modelId = Number(modelId);
|
|
92
107
|
return { id: session.modelId };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
2
|
+
import readline from 'node:readline/promises';
|
|
3
|
+
import chalk from './colors.js';
|
|
4
|
+
import { auth } from './api/index.js';
|
|
5
|
+
import { DEFAULT_THEGITAI_HOST } from './api/default-host.js';
|
|
6
|
+
import { loginViaBrowser } from './api/browser-login.js';
|
|
7
|
+
function hyperlink(url, label) {
|
|
8
|
+
if (process.stdout.isTTY !== true)
|
|
9
|
+
return label;
|
|
10
|
+
return `\x1b]8;;${url}\x07${label}\x1b]8;;\x07`;
|
|
11
|
+
}
|
|
12
|
+
export function formatSignInScreen({ url }) {
|
|
13
|
+
return [
|
|
14
|
+
'',
|
|
15
|
+
` ${chalk.bold.cyan('TheGitAI')}`,
|
|
16
|
+
'',
|
|
17
|
+
` ${chalk.bold('Sign in to continue.')}`,
|
|
18
|
+
' Your browser should open automatically. If not, copy this URL:',
|
|
19
|
+
'',
|
|
20
|
+
` ${chalk.cyan(url)}`,
|
|
21
|
+
'',
|
|
22
|
+
` ${hyperlink(url, chalk.cyan('→ Click here to authenticate'))}`,
|
|
23
|
+
'',
|
|
24
|
+
' If this terminal is not on the computer where that page opened, choose',
|
|
25
|
+
` ${chalk.bold('"On another computer"')} there and paste the code it gives you below.`,
|
|
26
|
+
'',
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
async function promptForCode(signal) {
|
|
30
|
+
const rl = readline.createInterface({ input, output });
|
|
31
|
+
try {
|
|
32
|
+
return await rl.question(` ${chalk.bold('Authorization code:')} `, { signal });
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
rl.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function runSignIn({ env = process.env, login = loginViaBrowser, write = auth.writeCliAuthConfig, log = (line) => console.log(line), } = {}) {
|
|
39
|
+
const result = await login({
|
|
40
|
+
serverUrl: DEFAULT_THEGITAI_HOST,
|
|
41
|
+
deviceName: env.THEGITAI_DEVICE_NAME?.trim() || undefined,
|
|
42
|
+
onUrl: (url) => log(formatSignInScreen({ url })),
|
|
43
|
+
onBrowserOpen: (opened) => {
|
|
44
|
+
if (!opened) {
|
|
45
|
+
log(chalk.dim(' (no browser opened — use the URL or the code box above)'));
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
promptCode: promptForCode,
|
|
49
|
+
onPasteRejected: (message) => log(` ${chalk.red('✖')} ${message}\n` +
|
|
50
|
+
' Codes last 10 minutes. If that page has been open a while, run `ai`\n' +
|
|
51
|
+
' again for a fresh one.'),
|
|
52
|
+
});
|
|
53
|
+
write(result, env);
|
|
54
|
+
log('');
|
|
55
|
+
log(chalk.green(` ✓ Signed in as ${result.customer.email}`));
|
|
56
|
+
log('');
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
@@ -4,7 +4,6 @@ import { clearEditFailure, collectCommandMutations, captureMutationBaseline, ens
|
|
|
4
4
|
import { buildAgentModeToolBlockedResult, } from './agent-mode.js';
|
|
5
5
|
import { classifyProjectPath } from './patcher.js';
|
|
6
6
|
import { dispatchTool } from './tools/index.js';
|
|
7
|
-
import { syncIndexFromDisk } from './project-index.js';
|
|
8
7
|
import { PATH_REPAIRING_EDIT_TOOLS, repairFilePath } from './tools/path-suggest.js';
|
|
9
8
|
import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './tools/shell-diagnostics.js';
|
|
10
9
|
import { extractTodosArg } from './todo-list.js';
|
|
@@ -69,7 +68,7 @@ function editToolWritesSeparateOutput(call) {
|
|
|
69
68
|
const output = args.outputPath ?? args.output_path;
|
|
70
69
|
return typeof output === 'string' && output.trim().length > 0;
|
|
71
70
|
}
|
|
72
|
-
async function collectTrackedCommandMutations({ session,
|
|
71
|
+
async function collectTrackedCommandMutations({ session, result, tracker, toolName, toolCallId, turnId, }) {
|
|
73
72
|
const checkpoint = ensureActiveCheckpoint(session.clientState.safety, turnId);
|
|
74
73
|
const records = collectCommandMutations({
|
|
75
74
|
state: session.clientState.safety,
|
|
@@ -84,40 +83,11 @@ async function collectTrackedCommandMutations({ session, projectIndex, result, t
|
|
|
84
83
|
return;
|
|
85
84
|
invalidateShellDiagnosticsCache(session.rootDir);
|
|
86
85
|
rememberCheckpointFiles(session.clientState.safety, session.rootDir, records.map((record) => record.filePath), turnId);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
removed: records.filter((record) => record.operation === 'delete').length,
|
|
93
|
-
indexedChunks: 0,
|
|
94
|
-
retrievalTokensUsed: 0,
|
|
95
|
-
};
|
|
96
|
-
if (priorSync?.error) {
|
|
97
|
-
result.repoSync = {
|
|
98
|
-
...mutationCounts,
|
|
99
|
-
indexSyncError: priorSync.error,
|
|
100
|
-
skipped: true,
|
|
101
|
-
reason: 'local index sync failed after command execution',
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
try {
|
|
106
|
-
const repoSync = await syncIndexFromDisk(projectIndex);
|
|
107
|
-
result.repoSync = {
|
|
108
|
-
...repoSync,
|
|
109
|
-
added: Math.max(repoSync.added, mutationCounts.added),
|
|
110
|
-
modified: Math.max(repoSync.modified, mutationCounts.modified),
|
|
111
|
-
removed: Math.max(repoSync.removed, mutationCounts.removed),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
const indexSyncError = error instanceof Error ? error.message : String(error);
|
|
116
|
-
result.repoSync = { ...mutationCounts, indexSyncError };
|
|
117
|
-
session.onStatus(`Repository changes were recorded, but local index sync failed: ${indexSyncError}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
86
|
+
result.repoSync = {
|
|
87
|
+
added: records.filter((record) => record.operation === 'create').length,
|
|
88
|
+
modified: records.filter((record) => record.operation === 'update').length,
|
|
89
|
+
removed: records.filter((record) => record.operation === 'delete').length,
|
|
90
|
+
};
|
|
121
91
|
result.sessionEdits = records.map((record) => ({
|
|
122
92
|
id: record.id,
|
|
123
93
|
filePath: record.filePath,
|
|
@@ -127,7 +97,7 @@ async function collectTrackedCommandMutations({ session, projectIndex, result, t
|
|
|
127
97
|
}));
|
|
128
98
|
result.diagnostics = runShellDiagnostics(session.rootDir);
|
|
129
99
|
}
|
|
130
|
-
export async function collectBackgroundJobUiKillMutations({ session,
|
|
100
|
+
export async function collectBackgroundJobUiKillMutations({ session, jobId, result, }) {
|
|
131
101
|
const normalizedJobId = String(jobId ?? result.snapshot?.id ?? '').trim();
|
|
132
102
|
if (!normalizedJobId || !result.ok)
|
|
133
103
|
return;
|
|
@@ -137,7 +107,6 @@ export async function collectBackgroundJobUiKillMutations({ session, projectInde
|
|
|
137
107
|
const mutationResult = result;
|
|
138
108
|
await collectTrackedCommandMutations({
|
|
139
109
|
session,
|
|
140
|
-
projectIndex,
|
|
141
110
|
result: mutationResult,
|
|
142
111
|
tracker: tracked.tracker,
|
|
143
112
|
toolName: tracked.toolName,
|
|
@@ -151,7 +120,7 @@ export async function collectBackgroundJobUiKillMutations({ session, projectInde
|
|
|
151
120
|
backgroundCommandTrackers.delete(normalizedJobId);
|
|
152
121
|
}
|
|
153
122
|
}
|
|
154
|
-
export async function collectBackgroundJobUiOutputMutations({ session,
|
|
123
|
+
export async function collectBackgroundJobUiOutputMutations({ session, jobId, }) {
|
|
155
124
|
const normalizedJobId = String(jobId ?? '').trim();
|
|
156
125
|
if (!normalizedJobId)
|
|
157
126
|
return;
|
|
@@ -165,7 +134,6 @@ export async function collectBackgroundJobUiOutputMutations({ session, projectIn
|
|
|
165
134
|
return;
|
|
166
135
|
await collectTrackedCommandMutations({
|
|
167
136
|
session,
|
|
168
|
-
projectIndex,
|
|
169
137
|
result: {},
|
|
170
138
|
tracker: tracked.tracker,
|
|
171
139
|
toolName: tracked.toolName,
|
|
@@ -244,7 +212,8 @@ function recordAssistantEdit(session, call, result, before) {
|
|
|
244
212
|
});
|
|
245
213
|
clearEditFailure(session.clientState.safety, filePath);
|
|
246
214
|
}
|
|
247
|
-
export async function executeLocalToolCall(
|
|
215
|
+
export async function executeLocalToolCall(session, call) {
|
|
216
|
+
session.turnAbortSignal?.throwIfAborted();
|
|
248
217
|
session.onStatus(formatToolCallForStatus(call));
|
|
249
218
|
try {
|
|
250
219
|
const agentModeBlocked = buildAgentModeToolBlockedResult(session.agentMode, call);
|
|
@@ -275,10 +244,10 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
275
244
|
const context = {
|
|
276
245
|
rootDir: session.rootDir,
|
|
277
246
|
sessionId: session.sessionId,
|
|
278
|
-
projectIndex: toolContext.projectIndex,
|
|
279
247
|
autoYes: session.autoYes,
|
|
280
|
-
|
|
281
|
-
|
|
248
|
+
agentMode: session.agentMode,
|
|
249
|
+
grants: session.grants,
|
|
250
|
+
requestPermission: session.requestPermission,
|
|
282
251
|
requestSudoPassword: session.requestSudoPassword,
|
|
283
252
|
onStatus: session.onStatus,
|
|
284
253
|
editJournal: session.clientState.editJournal,
|
|
@@ -299,7 +268,6 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
299
268
|
if (result && typeof result === 'object' && commandTracker) {
|
|
300
269
|
await collectTrackedCommandMutations({
|
|
301
270
|
session,
|
|
302
|
-
projectIndex: toolContext.projectIndex,
|
|
303
271
|
result,
|
|
304
272
|
tracker: commandTracker,
|
|
305
273
|
toolName: call.name,
|
|
@@ -326,7 +294,6 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
326
294
|
if (tracked) {
|
|
327
295
|
await collectTrackedCommandMutations({
|
|
328
296
|
session,
|
|
329
|
-
projectIndex: toolContext.projectIndex,
|
|
330
297
|
result,
|
|
331
298
|
tracker: tracked.tracker,
|
|
332
299
|
toolName: tracked.toolName,
|