@thegitai/cli 1.0.0-preview.27 → 1.0.0-preview.29
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 +2 -2
- package/dist/bin/ai.js +0 -15
- package/dist/src/agent-mode.js +0 -5
- package/dist/src/api/browser-login.js +2 -2
- package/dist/src/api/chat.js +4 -7
- package/dist/src/help-text.js +4 -4
- package/dist/src/session-safety.js +0 -12
- package/dist/src/signin.js +5 -3
- package/dist/src/tool-executor.js +9 -44
- package/dist/src/tools/delete-file.js +1 -3
- package/dist/src/tools/index.js +0 -10
- package/dist/src/tools/patch-file.js +1 -11
- package/dist/src/tools/restore-checkpoint.js +0 -1
- package/dist/src/tools/run-command.js +2 -45
- package/dist/src/tools/run-node-script.js +1 -55
- package/dist/src/tools/str-replace.js +1 -11
- package/dist/src/tools/undo-edit.js +1 -6
- package/dist/src/tools/write-file.js +1 -11
- package/dist/src/ui/repl.js +1 -10
- package/dist/src/ui/tui/build-frame.js +1 -4
- package/package.json +6 -6
- 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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TheGitAI — AI coding agent for your terminal
|
|
2
2
|
|
|
3
|
-
TheGitAI is an AI coding agent for your terminal. It
|
|
3
|
+
TheGitAI is an AI coding agent for your terminal. It reads and searches your repository,
|
|
4
4
|
writes and edits files, runs commands, and builds features with you.
|
|
5
5
|
|
|
6
6
|
Talk to your repo in plain English. TheGitAI can keep long-running processes
|
|
@@ -37,7 +37,7 @@ also be listed while signed out or offline.
|
|
|
37
37
|
Signing in opens your browser. The same screen also prints a URL you can open on
|
|
38
38
|
any other device and a box for an authorization code, so SSH and headless
|
|
39
39
|
machines need no separate command: open the URL wherever you have a browser,
|
|
40
|
-
choose "
|
|
40
|
+
choose "On another computer" on that page, and paste the code it shows.
|
|
41
41
|
|
|
42
42
|
CLI login tokens use a rolling 48-hour inactivity timeout. If one expires during
|
|
43
43
|
any server request, the CLI removes the expired credential and signs you in
|
package/dist/bin/ai.js
CHANGED
|
@@ -6,7 +6,6 @@ import { isSignInCancelled } from '../src/api/browser-login.js';
|
|
|
6
6
|
import { runSignIn } from '../src/signin.js';
|
|
7
7
|
import { STARTUP_RETRY_BUDGET, authenticationErrorMessage, isAuthenticationError, isTransientNetworkError, } from '../src/api/http.js';
|
|
8
8
|
import { formatCliHelpText } from '../src/help-text.js';
|
|
9
|
-
import { createIndex } from '../src/project-index.js';
|
|
10
9
|
import { createSession } from '../src/session.js';
|
|
11
10
|
import { applySessionSnapshot, listSessionMetadata, loadSessionSnapshot, saveSessionState, sessionHasUserMessage, } from '../src/session-store.js';
|
|
12
11
|
import { runClientInteractive } from '../src/ui/repl.js';
|
|
@@ -238,25 +237,11 @@ export async function main() {
|
|
|
238
237
|
if (sourceSnapshot) {
|
|
239
238
|
applySessionSnapshot(session, sourceSnapshot);
|
|
240
239
|
}
|
|
241
|
-
const projectIndex = createIndex({
|
|
242
|
-
rootDir,
|
|
243
|
-
onStatus: (message) => {
|
|
244
|
-
if (message.trim()) {
|
|
245
|
-
console.log(chalk.dim(` ${message}`));
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
onContextLog: (message) => {
|
|
249
|
-
if (message.trim()) {
|
|
250
|
-
console.log(chalk.cyan(` ${message}`));
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
240
|
const initialPrompt = prompt || undefined;
|
|
255
241
|
const outcome = await runClientInteractive({
|
|
256
242
|
appendPromptHistory: (value) => appendPromptHistory(value, session.env),
|
|
257
243
|
authConfig,
|
|
258
244
|
debugUi: whoami.debugUi,
|
|
259
|
-
projectIndex,
|
|
260
245
|
serverModels,
|
|
261
246
|
serverSessionClient,
|
|
262
247
|
session,
|
package/dist/src/agent-mode.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
export const AGENT_MODES = ['default', 'auto-accept', 'plan'];
|
|
2
2
|
const PLAN_MODE_TOOL_NAMES = new Set([
|
|
3
|
-
'search_code',
|
|
4
3
|
'list_files',
|
|
5
4
|
'list_directories',
|
|
6
5
|
'read_file',
|
|
7
6
|
'grep_code',
|
|
8
|
-
'find_symbol',
|
|
9
|
-
'list_symbols',
|
|
10
|
-
'hover_symbol',
|
|
11
|
-
'signature_help',
|
|
12
7
|
'read_document',
|
|
13
8
|
'analyze_image',
|
|
14
9
|
'run_command',
|
|
@@ -6,7 +6,7 @@ import os from 'node:os';
|
|
|
6
6
|
import { openUrl } from '../core/open-url.js';
|
|
7
7
|
import { ServerApiError, createTraceContext, failureCode, failureMessage, normalizeServerUrl, readJsonResponse, } from './http.js';
|
|
8
8
|
import { DEFAULT_THEGITAI_HOST } from './default-host.js';
|
|
9
|
-
const DEFAULT_TIMEOUT_MS =
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
10
10
|
function shutDownServer(server) {
|
|
11
11
|
server.closeAllConnections?.();
|
|
12
12
|
server.close();
|
|
@@ -179,7 +179,7 @@ export async function loginViaBrowser(options) {
|
|
|
179
179
|
const codePromise = new Promise((resolve, reject) => {
|
|
180
180
|
timer = setTimeout(() => {
|
|
181
181
|
shutDownServer(server);
|
|
182
|
-
reject(new Error('
|
|
182
|
+
reject(new Error('Sign-in timed out. Authorization codes last 10 minutes — run `ai` again to start over.'));
|
|
183
183
|
}, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
184
184
|
server.on('request', (req, res) => {
|
|
185
185
|
const requestUrl = new URL(req.url ?? '/', 'http://127.0.0.1');
|
package/dist/src/api/chat.js
CHANGED
|
@@ -299,7 +299,7 @@ function exitServerTurnId(session) {
|
|
|
299
299
|
turnIdOverrides.delete(session);
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
|
-
async function executeAndPostToolResult({ config,
|
|
302
|
+
async function executeAndPostToolResult({ config, session, event, input, fetchImpl, signal, traceId, }) {
|
|
303
303
|
const turnId = String(event?.turnId ?? '').trim();
|
|
304
304
|
if (!turnId || !event?.call?.id || !event.call.name) {
|
|
305
305
|
throw new Error('Server emitted an invalid tool-call event.');
|
|
@@ -323,7 +323,7 @@ async function executeAndPostToolResult({ config, projectIndex, session, event,
|
|
|
323
323
|
suggestedFilename: event.generatedImage.suggestedFilename ||
|
|
324
324
|
String(call.args?.filename ?? call.args?.file_name ?? ''),
|
|
325
325
|
})
|
|
326
|
-
: await executeLocalToolCall(
|
|
326
|
+
: await executeLocalToolCall(session, call);
|
|
327
327
|
preserveCancelledTurnToolResult(session, input, { ...event, call }, rawResult);
|
|
328
328
|
if (signal?.aborted) {
|
|
329
329
|
throw new TurnCancelledError();
|
|
@@ -344,7 +344,7 @@ async function executeAndPostToolResult({ config, projectIndex, session, event,
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
-
async function consumeTurnStream({ response, config,
|
|
347
|
+
async function consumeTurnStream({ response, config, session, input, fetchImpl, signal, traceId, onTurnStart, onInterjectionDelivered, }) {
|
|
348
348
|
if (!response.body) {
|
|
349
349
|
throw new Error('Server returned an empty chat stream.');
|
|
350
350
|
}
|
|
@@ -417,7 +417,6 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
417
417
|
if (data?.parallelSafe === true) {
|
|
418
418
|
pendingParallelTools.push(executeAndPostToolResult({
|
|
419
419
|
config,
|
|
420
|
-
projectIndex,
|
|
421
420
|
session,
|
|
422
421
|
event: data,
|
|
423
422
|
input,
|
|
@@ -430,7 +429,6 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
430
429
|
await drainParallelTools();
|
|
431
430
|
await executeAndPostToolResult({
|
|
432
431
|
config,
|
|
433
|
-
projectIndex,
|
|
434
432
|
session,
|
|
435
433
|
event: data,
|
|
436
434
|
input,
|
|
@@ -529,7 +527,7 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
529
527
|
}
|
|
530
528
|
return finalResult.current;
|
|
531
529
|
}
|
|
532
|
-
export async function sendServerUserMessage({ config,
|
|
530
|
+
export async function sendServerUserMessage({ config, session, input, imageAttachments = [], fetchImpl = globalThis.fetch, signal, onTurnStart, onInterjectionDelivered, }) {
|
|
533
531
|
const autoAttach = autoAttachImages(input, session.rootDir, imageAttachments);
|
|
534
532
|
const requestImageAttachments = autoAttach.attachments.length > 0
|
|
535
533
|
? [...imageAttachments, ...autoAttach.attachments]
|
|
@@ -580,7 +578,6 @@ export async function sendServerUserMessage({ config, projectIndex, session, inp
|
|
|
580
578
|
const result = await consumeTurnStream({
|
|
581
579
|
response,
|
|
582
580
|
config,
|
|
583
|
-
projectIndex,
|
|
584
581
|
session,
|
|
585
582
|
input: requestInputBase,
|
|
586
583
|
fetchImpl,
|
package/dist/src/help-text.js
CHANGED
|
@@ -14,7 +14,7 @@ const PASTE_SHORTCUT = pasteShortcutForPlatform();
|
|
|
14
14
|
const HELP_MARKDOWN = [
|
|
15
15
|
'# TheGitAI',
|
|
16
16
|
'',
|
|
17
|
-
'Interactive terminal coding agent. Local repo
|
|
17
|
+
'Interactive terminal coding agent. Local repo search, file edits, and',
|
|
18
18
|
'shell commands run on your machine. Model inference and server-executed',
|
|
19
19
|
'tools run on the server.',
|
|
20
20
|
'',
|
|
@@ -132,7 +132,7 @@ const HELP_MARKDOWN = [
|
|
|
132
132
|
' ends.',
|
|
133
133
|
'- File and shell operations are confined to the target repo root.',
|
|
134
134
|
'- Sensitive directories (`.git`, `node_modules`, build output) are',
|
|
135
|
-
'
|
|
135
|
+
' excluded from search and listing.',
|
|
136
136
|
'',
|
|
137
137
|
'## Troubleshooting',
|
|
138
138
|
'',
|
|
@@ -142,8 +142,8 @@ const HELP_MARKDOWN = [
|
|
|
142
142
|
'- Signed in with the wrong account → `/logout` (or `ai logout`), then run',
|
|
143
143
|
' `ai` and sign in as the account you intended to use.',
|
|
144
144
|
'- The browser did not open, or opened on the wrong machine → open the URL',
|
|
145
|
-
|
|
146
|
-
'
|
|
145
|
+
' printed on the sign-in screen anywhere you like, choose "On another',
|
|
146
|
+
' computer" on that page, and paste the code back into the terminal.',
|
|
147
147
|
'- A local session was used with a different sign-in → sign in with the',
|
|
148
148
|
' account you used for that session or start a new session.',
|
|
149
149
|
'- For anything else, re-run the command and report the printed error',
|
|
@@ -4,7 +4,6 @@ import path from 'node:path';
|
|
|
4
4
|
import { ARTIFACT_IGNORE_DIRS, normalizeProjectRelativePath, shouldIgnoreArtifactPath, } from './artifact-policy.js';
|
|
5
5
|
import { hashBytes, readFileEditSnapshot, storedContentBuffer, } from './edit-journal.js';
|
|
6
6
|
import { deleteProjectFile, resolveProjectPath, writeProjectFile, writeProjectFileBuffer, } from './patcher.js';
|
|
7
|
-
import { removeIndexFile, upsertIndexFile } from './project-index.js';
|
|
8
7
|
const MAX_CHECKPOINTS = 20;
|
|
9
8
|
const MAX_SESSION_EDITS = 500;
|
|
10
9
|
const MAX_READ_RECORDS = 200;
|
|
@@ -672,7 +671,6 @@ export async function restoreCheckpointFiles(args) {
|
|
|
672
671
|
const restored = [];
|
|
673
672
|
const applied = [];
|
|
674
673
|
let changed = false;
|
|
675
|
-
const syncPolicy = args.projectIndex.initialized;
|
|
676
674
|
for (const snapshot of targets) {
|
|
677
675
|
const before = readFileEditSnapshot(args.rootDir, snapshot.filePath);
|
|
678
676
|
try {
|
|
@@ -680,15 +678,11 @@ export async function restoreCheckpointFiles(args) {
|
|
|
680
678
|
const result = writeStoredProjectFile(args.rootDir, snapshot.filePath, snapshot.content ?? '', snapshot.contentEncoding);
|
|
681
679
|
if (result.changed)
|
|
682
680
|
changed = true;
|
|
683
|
-
if (syncPolicy)
|
|
684
|
-
await upsertIndexFile(args.projectIndex, snapshot.filePath);
|
|
685
681
|
}
|
|
686
682
|
else {
|
|
687
683
|
const result = deleteProjectFile(args.rootDir, snapshot.filePath);
|
|
688
684
|
if (result.deleted)
|
|
689
685
|
changed = true;
|
|
690
|
-
if (syncPolicy)
|
|
691
|
-
await removeIndexFile(args.projectIndex, snapshot.filePath);
|
|
692
686
|
}
|
|
693
687
|
const after = readFileEditSnapshot(args.rootDir, snapshot.filePath);
|
|
694
688
|
applied.push({ snapshot, before, after });
|
|
@@ -710,15 +704,9 @@ export async function restoreCheckpointFiles(args) {
|
|
|
710
704
|
throw new Error('previous file content is unavailable');
|
|
711
705
|
}
|
|
712
706
|
writeStoredProjectFile(args.rootDir, item.snapshot.filePath, item.before.content, item.before.contentEncoding);
|
|
713
|
-
if (syncPolicy) {
|
|
714
|
-
await upsertIndexFile(args.projectIndex, item.snapshot.filePath);
|
|
715
|
-
}
|
|
716
707
|
}
|
|
717
708
|
else {
|
|
718
709
|
deleteProjectFile(args.rootDir, item.snapshot.filePath);
|
|
719
|
-
if (syncPolicy) {
|
|
720
|
-
await removeIndexFile(args.projectIndex, item.snapshot.filePath);
|
|
721
|
-
}
|
|
722
710
|
}
|
|
723
711
|
rolledBack.push({ filePath: item.snapshot.filePath });
|
|
724
712
|
}
|
package/dist/src/signin.js
CHANGED
|
@@ -21,8 +21,8 @@ export function formatSignInScreen({ url }) {
|
|
|
21
21
|
'',
|
|
22
22
|
` ${hyperlink(url, chalk.cyan('→ Click here to authenticate'))}`,
|
|
23
23
|
'',
|
|
24
|
-
|
|
25
|
-
chalk.
|
|
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
26
|
'',
|
|
27
27
|
].join('\n');
|
|
28
28
|
}
|
|
@@ -46,7 +46,9 @@ export async function runSignIn({ env = process.env, login = loginViaBrowser, wr
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
promptCode: promptForCode,
|
|
49
|
-
onPasteRejected: (message) => log(chalk.red(
|
|
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.'),
|
|
50
52
|
});
|
|
51
53
|
write(result, env);
|
|
52
54
|
log('');
|
|
@@ -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,7 @@ 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) {
|
|
248
216
|
session.onStatus(formatToolCallForStatus(call));
|
|
249
217
|
try {
|
|
250
218
|
const agentModeBlocked = buildAgentModeToolBlockedResult(session.agentMode, call);
|
|
@@ -275,7 +243,6 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
275
243
|
const context = {
|
|
276
244
|
rootDir: session.rootDir,
|
|
277
245
|
sessionId: session.sessionId,
|
|
278
|
-
projectIndex: toolContext.projectIndex,
|
|
279
246
|
autoYes: session.autoYes,
|
|
280
247
|
grants: session.grants,
|
|
281
248
|
requestPermission: session.requestPermission,
|
|
@@ -299,7 +266,6 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
299
266
|
if (result && typeof result === 'object' && commandTracker) {
|
|
300
267
|
await collectTrackedCommandMutations({
|
|
301
268
|
session,
|
|
302
|
-
projectIndex: toolContext.projectIndex,
|
|
303
269
|
result,
|
|
304
270
|
tracker: commandTracker,
|
|
305
271
|
toolName: call.name,
|
|
@@ -326,7 +292,6 @@ export async function executeLocalToolCall(toolContext, session, call) {
|
|
|
326
292
|
if (tracked) {
|
|
327
293
|
await collectTrackedCommandMutations({
|
|
328
294
|
session,
|
|
329
|
-
projectIndex: toolContext.projectIndex,
|
|
330
295
|
result,
|
|
331
296
|
tracker: tracked.tracker,
|
|
332
297
|
toolName: tracked.toolName,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import chalk from '../colors.js';
|
|
2
2
|
import { classifyProjectPath, deleteProjectFile } from '../patcher.js';
|
|
3
3
|
import { isTuiMode } from '../runtime-mode.js';
|
|
4
|
-
import { removeIndexFile } from '../project-index.js';
|
|
5
4
|
import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './shell-diagnostics.js';
|
|
6
5
|
import { ensurePermission } from '../permissions.js';
|
|
7
6
|
export async function deleteFile(context, args) {
|
|
8
|
-
const { rootDir
|
|
7
|
+
const { rootDir } = context;
|
|
9
8
|
const filePath = String(args.filePath ?? '').trim();
|
|
10
9
|
if (!filePath) {
|
|
11
10
|
return { ok: false, error: 'filePath is required' };
|
|
@@ -41,7 +40,6 @@ export async function deleteFile(context, args) {
|
|
|
41
40
|
const result = deleteProjectFile(rootDir, filePath);
|
|
42
41
|
if (result.deleted) {
|
|
43
42
|
if (!scratchPath) {
|
|
44
|
-
await removeIndexFile(projectIndex, filePath);
|
|
45
43
|
invalidateShellDiagnosticsCache(rootDir, filePath);
|
|
46
44
|
}
|
|
47
45
|
if (!isTuiMode())
|
package/dist/src/tools/index.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { shellJobOutput } from './shell-job-output.js';
|
|
2
2
|
import { deleteFile } from './delete-file.js';
|
|
3
|
-
import { findSymbol } from './find-symbol.js';
|
|
4
3
|
import { getDiagnostics } from './get-diagnostics.js';
|
|
5
4
|
import { grepCode } from './grep-code.js';
|
|
6
|
-
import { hoverSymbol } from './hover-symbol.js';
|
|
7
5
|
import { listDirectories } from './list-directories.js';
|
|
8
6
|
import { listCheckpoints } from './list-checkpoints.js';
|
|
9
7
|
import { listFiles } from './list-files.js';
|
|
10
8
|
import { listSessionEdits } from './list-session-edits.js';
|
|
11
|
-
import { listSymbols } from './list-symbols.js';
|
|
12
9
|
import { patchFile } from './patch-file.js';
|
|
13
10
|
import { readDocument } from './read-document.js';
|
|
14
11
|
import { readFile } from './read-file.js';
|
|
@@ -16,8 +13,6 @@ import { replaceDocumentText } from './replace-document-text.js';
|
|
|
16
13
|
import { runShellCommand } from './run-command.js';
|
|
17
14
|
import { runNodeScript } from './run-node-script.js';
|
|
18
15
|
import { restoreFilesToCheckpoint, restoreToCheckpoint, } from './restore-checkpoint.js';
|
|
19
|
-
import { searchCode } from './search-code.js';
|
|
20
|
-
import { getSignatureHelp } from './signature-help.js';
|
|
21
16
|
import { shellJobKill } from './shell-job-kill.js';
|
|
22
17
|
import { strReplace } from './str-replace.js';
|
|
23
18
|
import { undoEdit } from './undo-edit.js';
|
|
@@ -26,17 +21,12 @@ import { readImageFile } from './read-image-file.js';
|
|
|
26
21
|
import { saveGeneratedImage } from './save-generated-image.js';
|
|
27
22
|
import { writeFile } from './write-file.js';
|
|
28
23
|
export const TOOL_MAP = {
|
|
29
|
-
search_code: (context, args) => searchCode(context.projectIndex, args),
|
|
30
24
|
list_files: (context, args) => listFiles(context, args),
|
|
31
25
|
list_directories: (context, args) => listDirectories(context, args),
|
|
32
26
|
read_file: (context, args) => readFile(context, args),
|
|
33
27
|
read_document: (context, args) => readDocument(context.rootDir, args, context.env),
|
|
34
28
|
replace_document_text: replaceDocumentText,
|
|
35
29
|
grep_code: (context, args) => grepCode(context.rootDir, args),
|
|
36
|
-
find_symbol: (context, args) => findSymbol(context, args),
|
|
37
|
-
list_symbols: (context, args) => listSymbols(context, args),
|
|
38
|
-
hover_symbol: (context, args) => hoverSymbol(context, args),
|
|
39
|
-
signature_help: (context, args) => getSignatureHelp(context, args),
|
|
40
30
|
get_diagnostics: (context, args) => getDiagnostics(context, args),
|
|
41
31
|
list_checkpoints: (context) => listCheckpoints(context),
|
|
42
32
|
list_session_edits: (context) => listSessionEdits(context),
|
|
@@ -2,7 +2,6 @@ import chalk from '../colors.js';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { normalizeProjectRelativePath } from '../artifact-policy.js';
|
|
4
4
|
import { applyUnifiedPatch, classifyProjectPath, readProjectFile, renderDiffPreview, writeProjectFile, } from '../patcher.js';
|
|
5
|
-
import { upsertIndexFile } from '../project-index.js';
|
|
6
5
|
import { repairFilePath } from './path-suggest.js';
|
|
7
6
|
import { isTuiMode } from '../runtime-mode.js';
|
|
8
7
|
import { getCurrentFileHash, resolveRedactionTokens } from '../session-safety.js';
|
|
@@ -10,7 +9,7 @@ import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './shell-d
|
|
|
10
9
|
import { ensurePermission } from '../permissions.js';
|
|
11
10
|
const DOCUMENT_EXTENSIONS = new Set(['.pdf', '.xlsx', '.docx']);
|
|
12
11
|
export async function patchFile(context, args) {
|
|
13
|
-
const { rootDir
|
|
12
|
+
const { rootDir } = context;
|
|
14
13
|
const filePath = repairFilePath(rootDir, String(args.filePath ?? '').trim());
|
|
15
14
|
let patch = typeof args.patch === 'string' ? args.patch : '';
|
|
16
15
|
if (!filePath) {
|
|
@@ -90,13 +89,6 @@ export async function patchFile(context, args) {
|
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
const { changed } = writeProjectFile(rootDir, filePath, patchedContent);
|
|
93
|
-
let indexedChunks = 0;
|
|
94
|
-
let retrievalTokensUsed = 0;
|
|
95
|
-
if (changed && !scratchPath) {
|
|
96
|
-
const indexResult = await upsertIndexFile(projectIndex, filePath);
|
|
97
|
-
indexedChunks = indexResult.indexedChunks;
|
|
98
|
-
retrievalTokensUsed = indexResult.retrievalTokensUsed ?? 0;
|
|
99
|
-
}
|
|
100
92
|
let diagnostics;
|
|
101
93
|
if (!scratchPath) {
|
|
102
94
|
invalidateShellDiagnosticsCache(rootDir, filePath);
|
|
@@ -115,8 +107,6 @@ export async function patchFile(context, args) {
|
|
|
115
107
|
changed,
|
|
116
108
|
operation: 'patch',
|
|
117
109
|
...(scratchPath ? { scratch: true } : {}),
|
|
118
|
-
indexedChunks,
|
|
119
|
-
retrievalTokensUsed,
|
|
120
110
|
bytesWritten: Buffer.byteLength(patchedContent, 'utf-8'),
|
|
121
111
|
diagnostics,
|
|
122
112
|
};
|
|
@@ -54,7 +54,6 @@ export async function restoreToCheckpoint(context, args) {
|
|
|
54
54
|
const result = await restoreCheckpointFiles({
|
|
55
55
|
state: context.safety,
|
|
56
56
|
rootDir: context.rootDir,
|
|
57
|
-
projectIndex: context.projectIndex,
|
|
58
57
|
checkpointId,
|
|
59
58
|
filePaths,
|
|
60
59
|
currentTurnId: context.currentTurnId ?? null,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from '../colors.js';
|
|
2
2
|
import { startBackgroundJob } from '../background-jobs.js';
|
|
3
3
|
import { getBlockedCommandReason, runCommand, } from '../executor.js';
|
|
4
|
-
import { syncIndexFromDisk } from '../project-index.js';
|
|
5
4
|
import { ensurePermission } from '../permissions.js';
|
|
6
5
|
import { isTuiMode } from '../runtime-mode.js';
|
|
7
6
|
import { redactConnectionStringCredentials } from '../secret-preview.js';
|
|
@@ -9,7 +8,7 @@ import { buildNestedGitHint } from '../session-safety.js';
|
|
|
9
8
|
import { buildDeferredShellDiagnostics, invalidateShellDiagnosticsCache, } from './shell-diagnostics.js';
|
|
10
9
|
const MAX_OUTPUT_CHARS = 4000;
|
|
11
10
|
export async function runShellCommand(context, args) {
|
|
12
|
-
const { rootDir,
|
|
11
|
+
const { rootDir, requestSudoPassword, onStatus, } = context;
|
|
13
12
|
const command = String(args.command ?? '').trim();
|
|
14
13
|
if (!command) {
|
|
15
14
|
return { ok: false, error: 'command is required' };
|
|
@@ -52,12 +51,8 @@ export async function runShellCommand(context, args) {
|
|
|
52
51
|
requestSudoPassword,
|
|
53
52
|
timeout: typeof args.timeout_ms === 'number' && args.timeout_ms > 0 ? args.timeout_ms : undefined,
|
|
54
53
|
});
|
|
55
|
-
const repoSync = await syncRepoIndex(projectIndex, onStatus);
|
|
56
54
|
invalidateShellDiagnosticsCache(rootDir);
|
|
57
55
|
const diagnostics = buildDeferredShellDiagnostics('run_command');
|
|
58
|
-
if (repoSync.added || repoSync.modified || repoSync.removed) {
|
|
59
|
-
onStatus(`Synced repo state after command (${repoSync.added} added, ${repoSync.modified} modified, ${repoSync.removed} removed).`);
|
|
60
|
-
}
|
|
61
56
|
const output = typeof result.output === 'string'
|
|
62
57
|
? boundCommandOutput(redactConnectionStringCredentials(result.output))
|
|
63
58
|
: result.output;
|
|
@@ -67,8 +62,6 @@ export async function runShellCommand(context, args) {
|
|
|
67
62
|
exitCode: result.exitCode,
|
|
68
63
|
timedOut: result.timedOut,
|
|
69
64
|
output,
|
|
70
|
-
repoSync,
|
|
71
|
-
retrievalTokensUsed: repoSync.retrievalTokensUsed,
|
|
72
65
|
diagnostics,
|
|
73
66
|
repoHint,
|
|
74
67
|
};
|
|
@@ -82,36 +75,8 @@ export function boundCommandOutput(output) {
|
|
|
82
75
|
`\n\n... (${output.length - headSize - tailSize} chars truncated) ...\n\n` +
|
|
83
76
|
output.slice(-tailSize));
|
|
84
77
|
}
|
|
85
|
-
async function syncRepoIndex(projectIndex, onStatus) {
|
|
86
|
-
if (!projectIndex.initialized) {
|
|
87
|
-
return {
|
|
88
|
-
added: 0,
|
|
89
|
-
modified: 0,
|
|
90
|
-
removed: 0,
|
|
91
|
-
indexedChunks: 0,
|
|
92
|
-
retrievalTokensUsed: 0,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
try {
|
|
96
|
-
return await syncIndexFromDisk(projectIndex);
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
-
onStatus(`Command completed, but local index sync failed: ${message}`);
|
|
101
|
-
return {
|
|
102
|
-
added: 0,
|
|
103
|
-
modified: 0,
|
|
104
|
-
removed: 0,
|
|
105
|
-
indexedChunks: 0,
|
|
106
|
-
retrievalTokensUsed: 0,
|
|
107
|
-
skipped: true,
|
|
108
|
-
reason: 'local index sync failed after command execution',
|
|
109
|
-
error: message,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
78
|
async function runBackgroundCommand(context, command, timeoutMs, repoHint) {
|
|
114
|
-
const { rootDir,
|
|
79
|
+
const { rootDir, onStatus } = context;
|
|
115
80
|
const started = await startBackgroundJob(command, rootDir, {
|
|
116
81
|
startupWaitMs: typeof timeoutMs === 'number' && timeoutMs > 0 ? timeoutMs : undefined,
|
|
117
82
|
sessionId: context.sessionId,
|
|
@@ -128,11 +93,7 @@ async function runBackgroundCommand(context, command, timeoutMs, repoHint) {
|
|
|
128
93
|
};
|
|
129
94
|
}
|
|
130
95
|
const snapshot = started.snapshot;
|
|
131
|
-
const repoSync = await syncRepoIndex(projectIndex, onStatus);
|
|
132
96
|
invalidateShellDiagnosticsCache(rootDir);
|
|
133
|
-
if (repoSync.added || repoSync.modified || repoSync.removed) {
|
|
134
|
-
onStatus(`Synced repo state after command (${repoSync.added} added, ${repoSync.modified} modified, ${repoSync.removed} removed).`);
|
|
135
|
-
}
|
|
136
97
|
const output = boundCommandOutput(redactConnectionStringCredentials(started.startupOutput ?? '').trim());
|
|
137
98
|
if (snapshot.status === 'running') {
|
|
138
99
|
return {
|
|
@@ -144,8 +105,6 @@ async function runBackgroundCommand(context, command, timeoutMs, repoHint) {
|
|
|
144
105
|
pid: snapshot.pid,
|
|
145
106
|
output,
|
|
146
107
|
note: `Background job ${snapshot.id} is running. Use shell_job_output to poll status and new output, and shell_job_kill to stop it.`,
|
|
147
|
-
repoSync,
|
|
148
|
-
retrievalTokensUsed: repoSync.retrievalTokensUsed,
|
|
149
108
|
repoHint,
|
|
150
109
|
};
|
|
151
110
|
}
|
|
@@ -158,8 +117,6 @@ async function runBackgroundCommand(context, command, timeoutMs, repoHint) {
|
|
|
158
117
|
exitCode: snapshot.exitCode,
|
|
159
118
|
output,
|
|
160
119
|
note: `Background job ${snapshot.id} finished during the startup window.`,
|
|
161
|
-
repoSync,
|
|
162
|
-
retrievalTokensUsed: repoSync.retrievalTokensUsed,
|
|
163
120
|
repoHint,
|
|
164
121
|
};
|
|
165
122
|
}
|