@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.
@@ -1,7 +1,6 @@
1
1
  import chalk from '../colors.js';
2
- import { execFileSync, spawn } from 'node:child_process';
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, projectIndex } = context;
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.',
@@ -1,7 +1,6 @@
1
1
  import chalk from '../colors.js';
2
2
  import { hashStoredContent, readFileEditSnapshot, storedContentBuffer, } from '../edit-journal.js';
3
3
  import { deleteProjectFile, writeProjectFile, writeProjectFileBuffer, } from '../patcher.js';
4
- import { removeIndexFile, upsertIndexFile, } from '../project-index.js';
5
4
  import { isTuiMode } from '../runtime-mode.js';
6
5
  import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './shell-diagnostics.js';
7
6
  function normalizeTarget(value) {
@@ -101,10 +100,9 @@ function validateUndoPlan(rootDir, records) {
101
100
  return { ok: true };
102
101
  }
103
102
  async function applyUndo(context, record) {
104
- const { rootDir, projectIndex } = context;
103
+ const { rootDir } = context;
105
104
  if (record.operation === 'create') {
106
105
  deleteProjectFile(rootDir, record.filePath);
107
- await removeIndexFile(projectIndex, record.filePath);
108
106
  return { changed: true };
109
107
  }
110
108
  if (record.beforeContent === null) {
@@ -113,9 +111,6 @@ async function applyUndo(context, record) {
113
111
  const { changed } = record.beforeContentEncoding === 'base64'
114
112
  ? writeProjectFileBuffer(rootDir, record.filePath, storedContentBuffer(record.beforeContent, record.beforeContentEncoding))
115
113
  : writeProjectFile(rootDir, record.filePath, record.beforeContent);
116
- if (changed) {
117
- await upsertIndexFile(projectIndex, record.filePath);
118
- }
119
114
  return { changed };
120
115
  }
121
116
  function summarizeUndoRecord(record) {
@@ -3,7 +3,6 @@ import path from 'node:path';
3
3
  import { normalizeProjectRelativePath } from '../artifact-policy.js';
4
4
  import { classifyProjectPath, writeProjectFile } from '../patcher.js';
5
5
  import { readFileEditSnapshot } from '../edit-journal.js';
6
- import { upsertIndexFile } from '../project-index.js';
7
6
  import { isTuiMode } from '../runtime-mode.js';
8
7
  import { getCurrentFileHash, hasFreshFullReadCoverage, resolveRedactionTokens, } from '../session-safety.js';
9
8
  import { invalidateShellDiagnosticsCache, runShellDiagnostics, } from './shell-diagnostics.js';
@@ -44,7 +43,7 @@ function buildWritePreview(previous, next) {
44
43
  return rows.join('\n');
45
44
  }
46
45
  export async function writeFile(context, args) {
47
- const { rootDir, projectIndex } = context;
46
+ const { rootDir } = context;
48
47
  const filePath = String(args.filePath ?? '').trim();
49
48
  let content = typeof args.content === 'string' ? args.content : '';
50
49
  if (!filePath) {
@@ -127,13 +126,6 @@ export async function writeFile(context, args) {
127
126
  }
128
127
  }
129
128
  const { changed } = writeProjectFile(rootDir, filePath, content);
130
- let indexedChunks = 0;
131
- let retrievalTokensUsed = 0;
132
- if (changed && !scratchPath) {
133
- const indexResult = await upsertIndexFile(projectIndex, filePath);
134
- indexedChunks = indexResult.indexedChunks;
135
- retrievalTokensUsed = indexResult.retrievalTokensUsed ?? 0;
136
- }
137
129
  let diagnostics;
138
130
  if (!scratchPath) {
139
131
  invalidateShellDiagnosticsCache(rootDir, filePath);
@@ -150,8 +142,6 @@ export async function writeFile(context, args) {
150
142
  changed,
151
143
  operation: 'write',
152
144
  ...(scratchPath ? { scratch: true } : {}),
153
- indexedChunks,
154
- retrievalTokensUsed,
155
145
  bytesWritten: Buffer.byteLength(content, 'utf-8'),
156
146
  diagnostics,
157
147
  };
@@ -1049,7 +1049,6 @@ export function formatClientTokenUsage(_responseTimeMs, usageSummary = null) {
1049
1049
  const reasoningTokens = usageSummary?.reasoningTokens ?? 0;
1050
1050
  const cacheTokens = usageSummary?.cacheTokens ?? 0;
1051
1051
  const cacheWriteTokens = usageSummary?.cacheWriteTokens ?? 0;
1052
- const indexTokens = usageSummary?.indexTokens ?? 0;
1053
1052
  return [
1054
1053
  'Session tokens',
1055
1054
  `in ${formatTokenCount(inputTokens)}`,
@@ -1057,7 +1056,6 @@ export function formatClientTokenUsage(_responseTimeMs, usageSummary = null) {
1057
1056
  ...(reasoningTokens > 0 ? [`think ${formatTokenCount(reasoningTokens)}`] : []),
1058
1057
  `cache ${formatTokenCount(cacheTokens)}`,
1059
1058
  `write ${formatTokenCount(cacheWriteTokens)}`,
1060
- `index ${formatTokenCount(indexTokens)}`,
1061
1059
  ].join(' • ');
1062
1060
  }
1063
1061
  export function formatPromptDirectoryLabel(projectRoot, homeDir = process.env.HOME ?? '') {
@@ -1344,7 +1342,7 @@ async function saveSessionBoth({ serverSessionClient, session, }) {
1344
1342
  saveSessionState(session);
1345
1343
  await serverSessionClient.save(session);
1346
1344
  }
1347
- export async function runClientInteractive({ appendPromptHistory, authConfig, debugUi, projectIndex, serverModels, serverSessionClient, session, usageText, initialPrompt, }) {
1345
+ export async function runClientInteractive({ appendPromptHistory, authConfig, debugUi, serverModels, serverSessionClient, session, usageText, initialPrompt, }) {
1348
1346
  if (process.stdin.isTTY !== true) {
1349
1347
  throw new Error('stdin is not a terminal');
1350
1348
  }
@@ -2018,7 +2016,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2018
2016
  const id = jobId.trim();
2019
2017
  await collectBackgroundJobUiOutputMutations({
2020
2018
  session,
2021
- projectIndex,
2022
2019
  jobId: id,
2023
2020
  });
2024
2021
  const job = listBackgroundJobs().find((candidate) => candidate.id === id);
@@ -2050,7 +2047,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2050
2047
  return;
2051
2048
  await collectBackgroundJobUiOutputMutations({
2052
2049
  session,
2053
- projectIndex,
2054
2050
  jobId,
2055
2051
  });
2056
2052
  syncBackgroundJobsState();
@@ -2066,7 +2062,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2066
2062
  const killed = await killBackgroundJob(jobId);
2067
2063
  await collectBackgroundJobUiKillMutations({
2068
2064
  session,
2069
- projectIndex,
2070
2065
  jobId,
2071
2066
  result: killed,
2072
2067
  });
@@ -2454,7 +2449,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2454
2449
  const killed = await killBackgroundJob(jobId);
2455
2450
  await collectBackgroundJobUiKillMutations({
2456
2451
  session,
2457
- projectIndex,
2458
2452
  jobId,
2459
2453
  result: killed,
2460
2454
  });
@@ -2662,7 +2656,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2662
2656
  try {
2663
2657
  const result = await chat.sendServerUserMessage({
2664
2658
  config: authConfig,
2665
- projectIndex,
2666
2659
  session,
2667
2660
  input,
2668
2661
  imageAttachments,
@@ -2892,8 +2885,6 @@ export async function runClientInteractive({ appendPromptHistory, authConfig, de
2892
2885
  appendTurnAwareEntry(buildBackgroundJobNoticeEntry(snapshot));
2893
2886
  }
2894
2887
  });
2895
- projectIndex.onStatus = session.onStatus;
2896
- projectIndex.onContextLog = session.onContextLog;
2897
2888
  session.requestSudoPassword = async ({ command, prompt, signal }) => openSudoPasswordPrompt(command, prompt, signal);
2898
2889
  session.requestUserInput = async (request, signal) => openUserInputPrompt(request.questions, signal);
2899
2890
  session.requestPermission = async (request) => {
@@ -413,7 +413,7 @@ function renderDiffPreviewLines(preview, width, maxDiffLines = TRANSCRIPT_DIFF_P
413
413
  ];
414
414
  }
415
415
  function tokenUsageLines(usage) {
416
- const match = usage.match(/^Session tokens • in ([^•]+) • out ([^•]+)(?: • think ([^•]+))? • cache ([^•]+)(?: • write ([^•]+))?(?: • index ([^•]+))?$/);
416
+ const match = usage.match(/^Session tokens • in ([^•]+) • out ([^•]+)(?: • think ([^•]+))? • cache ([^•]+)(?: • write ([^•]+))?$/);
417
417
  if (!match) {
418
418
  return [plainLine(usage, { color: 'cyan' })];
419
419
  }
@@ -431,9 +431,6 @@ function tokenUsageLines(usage) {
431
431
  if (match[5]) {
432
432
  spans.push(span(' Write ', { color: 'gray' }), span(match[5].trim(), { color: 'yellow' }));
433
433
  }
434
- if (match[6]) {
435
- spans.push(span(' Index ', { color: 'gray' }), span(match[6].trim(), { color: 'blue' }));
436
- }
437
434
  return [line(...spans)];
438
435
  }
439
436
  function footerTransientStatus(status) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thegitai/cli",
3
- "version": "1.0.0-preview.27",
4
- "description": "TheGitAI is an AI coding agent for your terminal. It indexes your repository, writes and edits files, runs commands, and builds features with you.",
3
+ "version": "1.0.0-preview.29",
4
+ "description": "TheGitAI is an AI coding agent for your terminal. It reads and searches your repository, writes and edits files, runs commands, and builds features with you.",
5
5
  "keywords": [
6
6
  "ai",
7
7
  "ai-coding-agent",
@@ -37,10 +37,10 @@
37
37
  "@lydell/node-pty-linux-x64": "1.1.0",
38
38
  "@lydell/node-pty-win32-arm64": "1.1.0",
39
39
  "@lydell/node-pty-win32-x64": "1.1.0",
40
- "@thegitai/tui-darwin-arm64": "1.0.0-preview.27",
41
- "@thegitai/tui-darwin-x64": "1.0.0-preview.27",
42
- "@thegitai/tui-linux-x64": "1.0.0-preview.27",
43
- "@thegitai/tui-win32-x64": "1.0.0-preview.27",
40
+ "@thegitai/tui-darwin-arm64": "1.0.0-preview.29",
41
+ "@thegitai/tui-darwin-x64": "1.0.0-preview.29",
42
+ "@thegitai/tui-linux-x64": "1.0.0-preview.29",
43
+ "@thegitai/tui-win32-x64": "1.0.0-preview.29",
44
44
  "@vscode/ripgrep": "1.18.0"
45
45
  },
46
46
  "publishConfig": {
@@ -1,233 +0,0 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import path from 'node:path';
3
- import { listProjectFiles, scanFiles, shouldIgnorePath, } from './scanner.js';
4
- import { truncate } from './utils.js';
5
- function normalizeProjectFilePath(rootDir, filePath) {
6
- const resolvedRoot = path.resolve(rootDir);
7
- const resolvedFile = path.resolve(resolvedRoot, filePath);
8
- const relative = path.relative(resolvedRoot, resolvedFile);
9
- if (relative.startsWith('..') || path.isAbsolute(relative)) {
10
- return null;
11
- }
12
- return relative.replace(/\\/g, '/');
13
- }
14
- function getFileSignature(rootDir, relPath) {
15
- try {
16
- const stat = statSync(path.join(rootDir, relPath));
17
- if (!stat.isFile()) {
18
- return null;
19
- }
20
- return `${stat.size}:${stat.mtimeMs}`;
21
- }
22
- catch {
23
- return null;
24
- }
25
- }
26
- function setChunksForFile(index, relPath, chunks) {
27
- if (chunks.length === 0) {
28
- index.chunksByFile.delete(relPath);
29
- index.fileSignatures.delete(relPath);
30
- return;
31
- }
32
- index.chunksByFile.set(relPath, chunks);
33
- const signature = getFileSignature(index.rootDir, relPath);
34
- if (signature) {
35
- index.fileSignatures.set(relPath, signature);
36
- }
37
- }
38
- function removeFile(index, relPath) {
39
- index.chunksByFile.delete(relPath);
40
- index.fileSignatures.delete(relPath);
41
- }
42
- function countIndexedChunks(index) {
43
- return Array.from(index.chunksByFile.values()).reduce((sum, chunks) => sum + chunks.length, 0);
44
- }
45
- async function initializeIndex(index) {
46
- if (index.initialized) {
47
- return countIndexedChunks(index);
48
- }
49
- if (!index._initializing) {
50
- index._initializing = scanProjectIntoIndex(index).finally(() => {
51
- index._initializing = null;
52
- });
53
- }
54
- return index._initializing;
55
- }
56
- async function scanProjectIntoIndex(index) {
57
- const files = listProjectFiles(index.rootDir);
58
- const chunks = await scanFiles(index.rootDir, files);
59
- index.fileSignatures.clear();
60
- index.chunksByFile.clear();
61
- for (const filePath of files) {
62
- const signature = getFileSignature(index.rootDir, filePath);
63
- if (signature) {
64
- index.fileSignatures.set(filePath, signature);
65
- }
66
- }
67
- for (const chunk of chunks) {
68
- const current = index.chunksByFile.get(chunk.filePath) ?? [];
69
- current.push(chunk);
70
- index.chunksByFile.set(chunk.filePath, current);
71
- }
72
- index.initialized = true;
73
- index.onStatus?.(`Local code index ready: ${index.fileSignatures.size.toLocaleString()} files.`);
74
- return chunks.length;
75
- }
76
- function queryTerms(query) {
77
- const parts = query
78
- .toLowerCase()
79
- .split(/[^a-z0-9_./-]+/i)
80
- .map((part) => part.trim())
81
- .filter(Boolean);
82
- return [...new Set(parts)];
83
- }
84
- function countOccurrences(haystack, needle) {
85
- if (!needle)
86
- return 0;
87
- let count = 0;
88
- let cursor = 0;
89
- while (cursor < haystack.length) {
90
- const index = haystack.indexOf(needle, cursor);
91
- if (index === -1)
92
- break;
93
- count += 1;
94
- cursor = index + needle.length;
95
- }
96
- return count;
97
- }
98
- function scoreChunk(terms, chunk) {
99
- const filePath = chunk.filePath.toLowerCase();
100
- const content = chunk.content.toLowerCase();
101
- let score = 0;
102
- for (const term of terms) {
103
- if (term.length < 2)
104
- continue;
105
- score += countOccurrences(filePath, term) * 3;
106
- score += countOccurrences(content, term);
107
- if (chunk.label?.toLowerCase().includes(term)) {
108
- score += 2;
109
- }
110
- }
111
- return score;
112
- }
113
- function flattenChunks(index) {
114
- return Array.from(index.chunksByFile.values()).flat();
115
- }
116
- export function createIndex({ rootDir, onStatus = null, onContextLog = null, }) {
117
- return {
118
- rootDir: path.resolve(rootDir),
119
- initialized: false,
120
- _initializing: null,
121
- fileSignatures: new Map(),
122
- chunksByFile: new Map(),
123
- onStatus,
124
- onContextLog,
125
- };
126
- }
127
- export async function searchIndex(index, query, limit = 10) {
128
- await initializeIndex(index);
129
- const terms = queryTerms(query);
130
- if (terms.length === 0) {
131
- return { results: [], retrievalTokensUsed: 0 };
132
- }
133
- const scored = flattenChunks(index)
134
- .map((chunk) => ({
135
- ...chunk,
136
- score: scoreChunk(terms, chunk),
137
- }))
138
- .filter((chunk) => chunk.score > 0)
139
- .sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath))
140
- .slice(0, Math.max(1, Math.min(limit, 20)));
141
- if (scored.length) {
142
- const visible = scored
143
- .slice(0, 6)
144
- .map((chunk) => `${chunk.filePath}:${chunk.startLine}-${chunk.endLine}`)
145
- .join(', ');
146
- index.onContextLog?.(`Local search hit: ${visible}`);
147
- }
148
- return {
149
- results: scored,
150
- retrievalTokensUsed: 0,
151
- };
152
- }
153
- export function formatIndexResults(results) {
154
- return results.map((chunk) => ({
155
- ...chunk,
156
- content: truncate(chunk.content, 1200),
157
- }));
158
- }
159
- export function listIndexFiles(index) {
160
- return [...index.fileSignatures.keys()].sort();
161
- }
162
- export async function upsertIndexFile(index, filePath) {
163
- if (!index.initialized) {
164
- return { indexedChunks: 0, retrievalTokensUsed: 0 };
165
- }
166
- const relPath = normalizeProjectFilePath(index.rootDir, filePath);
167
- if (!relPath || shouldIgnorePath(relPath)) {
168
- return { indexedChunks: 0, retrievalTokensUsed: 0 };
169
- }
170
- const chunks = existsSync(path.join(index.rootDir, relPath))
171
- ? await scanFiles(index.rootDir, [relPath])
172
- : [];
173
- setChunksForFile(index, relPath, chunks);
174
- return {
175
- indexedChunks: chunks.length,
176
- retrievalTokensUsed: 0,
177
- };
178
- }
179
- export async function removeIndexFile(index, filePath) {
180
- const relPath = normalizeProjectFilePath(index.rootDir, filePath);
181
- if (!relPath)
182
- return;
183
- removeFile(index, relPath);
184
- }
185
- export async function syncIndexFromDisk(index) {
186
- if (!index.initialized) {
187
- const indexedChunks = await initializeIndex(index);
188
- return {
189
- added: index.fileSignatures.size,
190
- modified: 0,
191
- removed: 0,
192
- indexedChunks,
193
- retrievalTokensUsed: 0,
194
- };
195
- }
196
- const currentFiles = new Set(listProjectFiles(index.rootDir));
197
- let added = 0;
198
- let modified = 0;
199
- let removed = 0;
200
- let indexedChunks = 0;
201
- for (const existing of [...index.fileSignatures.keys()]) {
202
- if (!currentFiles.has(existing)) {
203
- removeFile(index, existing);
204
- removed += 1;
205
- }
206
- }
207
- for (const relPath of currentFiles) {
208
- const nextSignature = getFileSignature(index.rootDir, relPath);
209
- if (!nextSignature)
210
- continue;
211
- const previousSignature = index.fileSignatures.get(relPath);
212
- if (!previousSignature) {
213
- const chunks = await scanFiles(index.rootDir, [relPath]);
214
- setChunksForFile(index, relPath, chunks);
215
- added += 1;
216
- indexedChunks += chunks.length;
217
- continue;
218
- }
219
- if (previousSignature !== nextSignature) {
220
- const chunks = await scanFiles(index.rootDir, [relPath]);
221
- setChunksForFile(index, relPath, chunks);
222
- modified += 1;
223
- indexedChunks += chunks.length;
224
- }
225
- }
226
- return {
227
- added,
228
- modified,
229
- removed,
230
- indexedChunks,
231
- retrievalTokensUsed: 0,
232
- };
233
- }