Compare commits

...

4 Commits

Author SHA1 Message Date
Christian Gunderman 57ef47b9ce Add simplified start_line, end_line, and file_length metadata to replace tool response 2026-02-25 17:01:30 +00:00
Christian Gunderman 55d0bf5f8e Fix build. 2026-02-25 06:27:48 +00:00
Christian Gunderman 81217cc906 fix: add devtools reference to cli tsconfig 2026-02-25 06:27:36 +00:00
Christian Gunderman 4325e434f9 Fix build. 2026-02-25 06:27:24 +00:00
7 changed files with 69 additions and 26 deletions
+2
View File
@@ -2150,6 +2150,7 @@
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz",
"integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@hono/node-server": "^1.19.9",
"ajv": "^8.17.1",
@@ -17142,6 +17143,7 @@
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
"@google/gemini-cli-core": "file:../core",
"@google/gemini-cli-devtools": "file:../devtools",
"@google/genai": "1.41.0",
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.23.0",
+1
View File
@@ -31,6 +31,7 @@
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
"@google/gemini-cli-core": "file:../core",
"@google/gemini-cli-devtools": "file:../devtools",
"@google/genai": "1.41.0",
"@iarna/toml": "^2.2.5",
"@modelcontextprotocol/sdk": "^1.23.0",
+1 -1
View File
@@ -14,5 +14,5 @@
"./package.json"
],
"exclude": ["node_modules", "dist"],
"references": [{ "path": "../core" }]
"references": [{ "path": "../core" }, { "path": "../devtools" }]
}
+52 -13
View File
@@ -137,7 +137,22 @@ async function calculateExactReplacement(
const normalizedSearch = old_string.replace(/\r\n/g, '\n');
const normalizedReplace = new_string.replace(/\r\n/g, '\n');
const exactOccurrences = normalizedCode.split(normalizedSearch).length - 1;
const matchRanges: Array<{ start: number; end: number }> = [];
let index = normalizedCode.indexOf(normalizedSearch);
while (index !== -1) {
const startLine = normalizedCode
.substring(0, index)
.split(String.fromCharCode(10)).length;
const endLine =
startLine + normalizedSearch.split(String.fromCharCode(10)).length - 1;
matchRanges.push({ start: startLine, end: endLine });
index = normalizedCode.indexOf(
normalizedSearch,
index + normalizedSearch.length,
);
}
const exactOccurrences = matchRanges.length;
if (!params.allow_multiple && exactOccurrences > 1) {
return {
@@ -145,6 +160,8 @@ async function calculateExactReplacement(
occurrences: exactOccurrences,
finalOldString: normalizedSearch,
finalNewString: normalizedReplace,
strategy: 'exact',
matchRanges,
};
}
@@ -160,6 +177,8 @@ async function calculateExactReplacement(
occurrences: exactOccurrences,
finalOldString: normalizedSearch,
finalNewString: normalizedReplace,
strategy: 'exact',
matchRanges,
};
}
@@ -178,9 +197,9 @@ async function calculateFlexibleReplacement(
const sourceLines = normalizedCode.match(/.*(?:\n|$)/g)?.slice(0, -1) ?? [];
const searchLinesStripped = normalizedSearch
.split('\n')
.split(String.fromCharCode(10))
.map((line: string) => line.trim());
const replaceLines = normalizedReplace.split('\n');
const replaceLines = normalizedReplace.split(String.fromCharCode(10));
let flexibleOccurrences = 0;
let i = 0;
@@ -238,11 +257,13 @@ async function calculateRegexReplacement(
let processedString = normalizedSearch;
for (const delim of delimiters) {
processedString = processedString.split(delim).join(` ${delim} `);
processedString = processedString
.split(String.fromCharCode(10))
.join(` ${delim} `);
}
// Split by any whitespace and remove empty strings.
const tokens = processedString.split(/\s+/).filter(Boolean);
const tokens = processedString.split(String.fromCharCode(10)).filter(Boolean);
if (tokens.length === 0) {
return null;
@@ -265,7 +286,7 @@ async function calculateRegexReplacement(
}
const occurrences = matches.length;
const newLines = normalizedReplace.split('\n');
const newLines = normalizedReplace.split(String.fromCharCode(10));
// Use the appropriate regex for replacement based on allow_multiple.
const replaceRegex = new RegExp(
@@ -765,11 +786,13 @@ class EditToolInvocation
}
const oldStringSnippet =
this.params.old_string.split('\n')[0].substring(0, 30) +
(this.params.old_string.length > 30 ? '...' : '');
this.params.old_string
.split(String.fromCharCode(10))[0]
.substring(0, 30) + (this.params.old_string.length > 30 ? '...' : '');
const newStringSnippet =
this.params.new_string.split('\n')[0].substring(0, 30) +
(this.params.new_string.length > 30 ? '...' : '');
this.params.new_string
.split(String.fromCharCode(10))[0]
.substring(0, 30) + (this.params.new_string.length > 30 ? '...' : '');
if (this.params.old_string === this.params.new_string) {
return `No file changes to ${shortenPath(relativePath)}`;
@@ -877,10 +900,26 @@ class EditToolInvocation
};
}
const totalLength = finalContent.split(String.fromCharCode(10)).length;
const metadataParts = [];
if (editData.matchRanges && editData.matchRanges.length > 0) {
if (editData.matchRanges.length === 1) {
metadataParts.push(
`start_line: ${editData.matchRanges[0].start}, end_line: ${editData.matchRanges[0].end}`,
);
} else {
const ranges = editData.matchRanges
.map((r) => `${r.start}-${r.end}`)
.join(', ');
metadataParts.push(`ranges: ${ranges}`);
}
}
metadataParts.push(`file_length: ${totalLength}`);
const llmSuccessMessageParts = [
editData.isNewFile
? `Created new file: ${this.params.file_path} with provided content.`
: `Successfully modified file: ${this.params.file_path} (${editData.occurrences} replacements).`,
? `Created new file: ${this.params.file_path} with provided content. [file_length: ${totalLength}]`
: `Successfully modified file: ${this.params.file_path}. [${metadataParts.join(', ')}]`,
];
// Return a diff of the file before and after the write so that the agent
@@ -1211,7 +1250,7 @@ async function calculateFuzzyReplacement(
// so that indices remain valid
selectedMatches.sort((a, b) => b.index - a.index);
const newLines = normalizedReplace.split('\n');
const newLines = normalizedReplace.split(String.fromCharCode(10));
for (const match of selectedMatches) {
// If we want to preserve the indentation of the first line of the match:
+10 -8
View File
@@ -5,7 +5,7 @@
*/
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { useDevToolsData, type ConsoleLog, type NetworkLog } from './hooks';
import { useDevToolsData, type ConsoleLog, type NetworkLog } from './hooks.js';
type ThemeMode = 'light' | 'dark' | null; // null means follow system
@@ -115,7 +115,6 @@ export default function App() {
if (!networkMap.has(id)) {
networkMap.set(id, {
...payload,
type,
timestamp,
id,
} as NetworkLog);
@@ -125,8 +124,7 @@ export default function App() {
networkMap.set(id, {
...existing,
...payload,
// Ensure we don't overwrite the original timestamp or type
type: existing.type,
// Ensure we don't overwrite the original timestamp
timestamp: existing.timestamp,
} as NetworkLog);
}
@@ -158,7 +156,7 @@ export default function App() {
const entries: Array<{ timestamp: number; data: object }> = [];
// Export console logs
filteredConsoleLogs.forEach((log) => {
filteredConsoleLogs.forEach((log: ConsoleLog) => {
entries.push({
timestamp: log.timestamp,
data: {
@@ -171,7 +169,7 @@ export default function App() {
});
// Export network logs
filteredNetworkLogs.forEach((log) => {
filteredNetworkLogs.forEach((log: NetworkLog) => {
entries.push({
timestamp: log.timestamp,
data: {
@@ -230,7 +228,9 @@ export default function App() {
if (selectedSessionId === importedSessionId && importedLogs) {
return importedLogs.console;
}
return consoleLogs.filter((l) => l.sessionId === selectedSessionId);
return consoleLogs.filter(
(l: ConsoleLog) => l.sessionId === selectedSessionId,
);
}, [consoleLogs, selectedSessionId, importedSessionId, importedLogs]);
const filteredNetworkLogs = useMemo(() => {
@@ -238,7 +238,9 @@ export default function App() {
if (selectedSessionId === importedSessionId && importedLogs) {
return importedLogs.network;
}
return networkLogs.filter((l) => l.sessionId === selectedSessionId);
return networkLogs.filter(
(l: NetworkLog) => l.sessionId === selectedSessionId,
);
}, [networkLogs, selectedSessionId, importedSessionId, importedLogs]);
return (
+1 -1
View File
@@ -6,7 +6,7 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import App from './App.js';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
+2 -3
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"lib": ["DOM", "DOM.Iterable", "ES2023"],
"jsx": "react-jsx",
"allowImportingTsExtensions": true,
"noEmit": true
"jsx": "react-jsx"
},
"include": ["src", "client/src"]
}