@thegitai/cli 1.0.0-preview.28 → 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 +1 -1
- package/dist/bin/ai.js +0 -15
- package/dist/src/agent-mode.js +0 -5
- package/dist/src/api/chat.js +4 -7
- package/dist/src/help-text.js +2 -2
- package/dist/src/session-safety.js +0 -12
- 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
|
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',
|
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
|
'',
|
|
@@ -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
|
}
|
|
@@ -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
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from '../colors.js';
|
|
2
|
-
import {
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
3
|
import { ensurePermission } from '../permissions.js';
|
|
4
|
-
import { syncIndexFromDisk } from '../project-index.js';
|
|
5
4
|
import { isTuiMode } from '../runtime-mode.js';
|
|
6
5
|
import { buildDeferredShellDiagnostics, invalidateShellDiagnosticsCache, } from './shell-diagnostics.js';
|
|
7
6
|
import { ensureSessionScratchDir } from '../scratch-dir.js';
|
|
@@ -57,29 +56,6 @@ function buildOutput(stdout, stderr, exitCode) {
|
|
|
57
56
|
return trimOutput(stdout.trim());
|
|
58
57
|
return trimOutput([stdout, stderr].filter(Boolean).join('\n').trim());
|
|
59
58
|
}
|
|
60
|
-
function emptyRepoSync(reason) {
|
|
61
|
-
return {
|
|
62
|
-
added: 0,
|
|
63
|
-
modified: 0,
|
|
64
|
-
removed: 0,
|
|
65
|
-
indexedChunks: 0,
|
|
66
|
-
retrievalTokensUsed: 0,
|
|
67
|
-
skipped: true,
|
|
68
|
-
...(reason ? { reason } : {}),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function readGitStatusSignature(rootDir) {
|
|
72
|
-
try {
|
|
73
|
-
return execFileSync('git', ['-C', rootDir, 'status', '--porcelain=v1', '--untracked-files=all'], {
|
|
74
|
-
encoding: 'utf-8',
|
|
75
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
76
|
-
timeout: 10_000,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
59
|
function executeNodeScript(rootDir, script, timeout) {
|
|
84
60
|
const effectiveTimeout = typeof timeout === 'number' && timeout > 0
|
|
85
61
|
? timeout
|
|
@@ -170,38 +146,10 @@ export async function runNodeScript(context, args) {
|
|
|
170
146
|
}
|
|
171
147
|
return denied;
|
|
172
148
|
}
|
|
173
|
-
const beforeGitStatus = readGitStatusSignature(rootDir);
|
|
174
149
|
const result = await executeNodeScript(rootDir, script, typeof args.timeout_ms === 'number' && args.timeout_ms > 0
|
|
175
150
|
? args.timeout_ms
|
|
176
151
|
: undefined);
|
|
177
|
-
const afterGitStatus = readGitStatusSignature(rootDir);
|
|
178
|
-
const gitStatusCleanBeforeAndAfter = beforeGitStatus === '' && afterGitStatus === '';
|
|
179
|
-
const shouldSync = context.projectIndex.initialized && !gitStatusCleanBeforeAndAfter;
|
|
180
|
-
let repoSync;
|
|
181
|
-
if (shouldSync) {
|
|
182
|
-
try {
|
|
183
|
-
repoSync = await syncIndexFromDisk(context.projectIndex);
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
-
context.onStatus(`Node script completed, but local index sync failed: ${message}`);
|
|
188
|
-
repoSync = {
|
|
189
|
-
...emptyRepoSync('local index sync failed after Node script execution'),
|
|
190
|
-
error: message,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
repoSync = emptyRepoSync(!context.projectIndex.initialized
|
|
196
|
-
? 'project index not initialized'
|
|
197
|
-
: gitStatusCleanBeforeAndAfter
|
|
198
|
-
? 'git status clean before and after'
|
|
199
|
-
: 'sync not needed');
|
|
200
|
-
}
|
|
201
152
|
invalidateShellDiagnosticsCache(rootDir);
|
|
202
|
-
if (repoSync.added || repoSync.modified || repoSync.removed) {
|
|
203
|
-
context.onStatus(`Synced repo state after Node script (${repoSync.added} added, ${repoSync.modified} modified, ${repoSync.removed} removed).`);
|
|
204
|
-
}
|
|
205
153
|
return {
|
|
206
154
|
ok: result.exitCode === 0,
|
|
207
155
|
command: COMMAND_LABEL,
|
|
@@ -210,8 +158,6 @@ export async function runNodeScript(context, args) {
|
|
|
210
158
|
stdout: result.stdout,
|
|
211
159
|
stderr: result.stderr,
|
|
212
160
|
output: result.output,
|
|
213
|
-
repoSync,
|
|
214
|
-
retrievalTokensUsed: repoSync.retrievalTokensUsed,
|
|
215
161
|
diagnostics: buildDeferredShellDiagnostics('run_node_script'),
|
|
216
162
|
};
|
|
217
163
|
}
|
|
@@ -3,7 +3,6 @@ import path from 'node:path';
|
|
|
3
3
|
import { normalizeProjectRelativePath } from '../artifact-policy.js';
|
|
4
4
|
import { classifyProjectPath, readProjectFile, writeProjectFile, } from '../patcher.js';
|
|
5
5
|
import { repairFilePath } from './path-suggest.js';
|
|
6
|
-
import { upsertIndexFile } from '../project-index.js';
|
|
7
6
|
import { isTuiMode } from '../runtime-mode.js';
|
|
8
7
|
import { getCurrentFileHash, resolveRedactionTokens } from '../session-safety.js';
|
|
9
8
|
import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './shell-diagnostics.js';
|
|
@@ -77,7 +76,7 @@ function buildStrReplacePreview(oldString, newString) {
|
|
|
77
76
|
return `@@ str_replace @@\n${minus}\n${plus}`;
|
|
78
77
|
}
|
|
79
78
|
export async function strReplace(context, args) {
|
|
80
|
-
const { rootDir
|
|
79
|
+
const { rootDir } = context;
|
|
81
80
|
const filePath = repairFilePath(rootDir, String(args.filePath ?? args.file_path ?? '').trim());
|
|
82
81
|
let oldString = typeof args.old_string === 'string'
|
|
83
82
|
? args.old_string
|
|
@@ -193,13 +192,6 @@ export async function strReplace(context, args) {
|
|
|
193
192
|
const nextContent = originalContent.split(oldString).join(newString);
|
|
194
193
|
const { changed } = writeProjectFile(rootDir, filePath, nextContent);
|
|
195
194
|
const replacements = changed ? (replaceAll ? n : 1) : 0;
|
|
196
|
-
let indexedChunks = 0;
|
|
197
|
-
let retrievalTokensUsed = 0;
|
|
198
|
-
if (changed && !scratchPath) {
|
|
199
|
-
const indexResult = await upsertIndexFile(projectIndex, filePath);
|
|
200
|
-
indexedChunks = indexResult.indexedChunks;
|
|
201
|
-
retrievalTokensUsed = indexResult.retrievalTokensUsed ?? 0;
|
|
202
|
-
}
|
|
203
195
|
let diagnostics;
|
|
204
196
|
if (!scratchPath) {
|
|
205
197
|
invalidateShellDiagnosticsCache(rootDir, filePath);
|
|
@@ -219,8 +211,6 @@ export async function strReplace(context, args) {
|
|
|
219
211
|
operation: 'str_replace',
|
|
220
212
|
...(scratchPath ? { scratch: true } : {}),
|
|
221
213
|
replacements,
|
|
222
|
-
indexedChunks,
|
|
223
|
-
retrievalTokensUsed,
|
|
224
214
|
bytesWritten: Buffer.byteLength(nextContent, 'utf-8'),
|
|
225
215
|
diagnostics,
|
|
226
216
|
message: changed ? undefined : 'The provided replacement resulted in no changes to the file content.',
|