diff --git a/apps/kilocode-docs/docs/advanced-usage/custom-rules.md b/apps/kilocode-docs/docs/advanced-usage/custom-rules.md
index 5e463cdd71..5b5cd6a5b4 100644
--- a/apps/kilocode-docs/docs/advanced-usage/custom-rules.md
+++ b/apps/kilocode-docs/docs/advanced-usage/custom-rules.md
@@ -4,7 +4,7 @@
url="https://youtu.be/GF0vjB8NxYg"
/>
-Custom rules provide a powerful way to define project-specific and global behaviors and constraints for the Axon Code AI agent. With custom rules, you can ensure consistent formatting, restrict access to sensitive files, enforce coding standards, and customize the AI's behavior for your specific project needs or across all projects.
+Custom rules provide a powerful way to define project-specific and global behaviors and constraints for the Axon AI Coding Agent | Code Reviews. With custom rules, you can ensure consistent formatting, restrict access to sensitive files, enforce coding standards, and customize the AI's behavior for your specific project needs or across all projects.
## Overview
diff --git a/apps/storybook/.storybook/theme-variables.css b/apps/storybook/.storybook/theme-variables.css
index caac3148c9..48e538f04f 100644
--- a/apps/storybook/.storybook/theme-variables.css
+++ b/apps/storybook/.storybook/theme-variables.css
@@ -46,7 +46,7 @@ classes at build time based on the @theme */
--sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--charts-green: var(--vscode-charts-green);
---charts-yellow: var(--vscode-charts-yellow);
+--charts-yellow: var(--color-matterai-yellow);
--charts-red: var(--vscode-charts-red);
--charts-blue: var(--vscode-charts-blue);
--charts-orange: var(--vscode-charts-orange);
@@ -85,7 +85,7 @@ classes at build time based on the @theme */
--ring: var(--vscode-input-border);
--chart-1: var(--vscode-charts-red);
--chart-2: var(--vscode-charts-blue);
---chart-3: var(--vscode-charts-yellow);
+--chart-3: var(--color-matterai-yellow);
--chart-4: var(--vscode-charts-orange);
--chart-5: var(--vscode-charts-green);
@@ -133,7 +133,6 @@ classes at build time based on the @theme */
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
-
/* Tailwind color delegations for semantic colors */
--color-background: var(--background);
--color-foreground: var(--foreground);
@@ -202,7 +201,7 @@ classes at build time based on the @theme */
--color-vscode-sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--color-vscode-sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--color-vscode-charts-green: var(--vscode-charts-green);
---color-vscode-charts-yellow: var(--vscode-charts-yellow);
+--color-vscode-charts-yellow: var(--color-matterai-yellow);
--color-vscode-charts-red: var(--vscode-charts-red);
--color-vscode-charts-blue: var(--vscode-charts-blue);
--color-vscode-charts-orange: var(--vscode-charts-orange);
diff --git a/jetbrains/plugin/src/main/resources/META-INF/plugin.xml.template b/jetbrains/plugin/src/main/resources/META-INF/plugin.xml.template
index ac6d4f3708..8369c4eda2 100644
--- a/jetbrains/plugin/src/main/resources/META-INF/plugin.xml.template
+++ b/jetbrains/plugin/src/main/resources/META-INF/plugin.xml.template
@@ -24,7 +24,7 @@ SPDX-License-Identifier: Apache-2.0
Simple HTML elements (text formatting, paragraphs, and lists) can be added inside of tag.
Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description -->
Axon Code AI Agent
+
Axon AI Coding Agent | Code Reviews
Open Source AI coding assistant for planning, building, and fixing code.
]]>
diff --git a/jetbrains/plugin/src/main/resources/themes/theme-variables.css b/jetbrains/plugin/src/main/resources/themes/theme-variables.css
index 2bddfb81cd..48e538f04f 100644
--- a/jetbrains/plugin/src/main/resources/themes/theme-variables.css
+++ b/jetbrains/plugin/src/main/resources/themes/theme-variables.css
@@ -46,7 +46,7 @@ classes at build time based on the @theme */
--sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--charts-green: var(--vscode-charts-green);
---charts-yellow: var(--vscode-charts-yellow);
+--charts-yellow: var(--color-matterai-yellow);
--charts-red: var(--vscode-charts-red);
--charts-blue: var(--vscode-charts-blue);
--charts-orange: var(--vscode-charts-orange);
@@ -85,7 +85,7 @@ classes at build time based on the @theme */
--ring: var(--vscode-input-border);
--chart-1: var(--vscode-charts-red);
--chart-2: var(--vscode-charts-blue);
---chart-3: var(--vscode-charts-yellow);
+--chart-3: var(--color-matterai-yellow);
--chart-4: var(--vscode-charts-orange);
--chart-5: var(--vscode-charts-green);
@@ -201,7 +201,7 @@ classes at build time based on the @theme */
--color-vscode-sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--color-vscode-sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--color-vscode-charts-green: var(--vscode-charts-green);
---color-vscode-charts-yellow: var(--vscode-charts-yellow);
+--color-vscode-charts-yellow: var(--color-matterai-yellow);
--color-vscode-charts-red: var(--vscode-charts-red);
--color-vscode-charts-blue: var(--vscode-charts-blue);
--color-vscode-charts-orange: var(--vscode-charts-orange);
diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts
index c82ca4f47e..68481cc694 100644
--- a/src/core/assistant-message/presentAssistantMessage.ts
+++ b/src/core/assistant-message/presentAssistantMessage.ts
@@ -204,12 +204,12 @@ export async function presentAssistantMessage(cline: Task) {
case "insert_content":
return `[${block.name} for '${block.params.path}']`
case "file_edit":
- return `[${block.name} for '${block.params.target_file}']`
+ return `[${block.name} for '${(block.params as any).file_path || block.params.target_file}']`
case "search_and_replace":
return `[${block.name} for '${block.params.path}']`
// kilocode_change start: Morph fast apply
case "edit_file":
- return `[${block.name} for '${block.params.target_file}']`
+ return `[${block.name} for '${(block.params as any).file_path || block.params.target_file}']`
// kilocode_change end
case "list_files":
return `[${block.name} for '${block.params.path}']`
@@ -257,7 +257,8 @@ export async function presentAssistantMessage(cline: Task) {
const pushToolResult_withToolUseId_kilocode = (
...items: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
) => {
- if (block.toolUseId) {
+ // Check for non-empty toolUseId - empty string should be treated as missing
+ if (block.toolUseId && block.toolUseId.length > 0) {
cline.userMessageContent.push({ type: "tool_result", tool_use_id: block.toolUseId, content: items })
} else {
cline.userMessageContent.push(...items)
@@ -295,8 +296,8 @@ export async function presentAssistantMessage(cline: Task) {
const pushToolResult = (content: ToolResponse) => {
// kilocode_change start
const items = new Array()
- items.push({ type: "text", text: `${toolDescription()} Result:` })
+ // No prefix - just return raw tool output
if (typeof content === "string") {
items.push({ type: "text", text: content || "(tool did not return anything)" })
} else {
diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts
index bea2aed9b3..83460cd976 100644
--- a/src/core/prompts/system.ts
+++ b/src/core/prompts/system.ts
@@ -45,7 +45,7 @@ Common tool calls and explanations
- You need to delete or rewrite a block of code but don't want to craft search/replace diff markers manually.
**Parameters**:
-1. \`target_file\` — Relative path to the file you want to modify.
+1. \`file_path\` — Absolute path to the file you want to modify (e.g., /Users/username/project/src/file.ts).
2. \`old_string\` — The current text you expect to replace. Provide enough context for a unique match; this can be empty to replace the entire file.
3. \`new_string\` — The text that should replace the match. Use an empty string to delete the matched content.
4. \`replace_all\` (optional, default false) — Set to true to replace every occurrence of the matched text. Leave false to replace only a single uniquely identified match.
@@ -57,118 +57,81 @@ Common tool calls and explanations
## read_file Tool Usage
-The \`read_file\` tool reads specific line ranges from one or more files. This is used to examine code before making changes or to discuss specific sections.
+The \`read_file\` tool reads file contents with optional offset and limit. Use it to examine code before making changes or to discuss specific sections.
-### CRITICAL: Line Ranges Are Always Required
-
-**You MUST always specify line ranges. You cannot read entire files.**
+### Parameters
-- \`line_ranges\` is a required array of strings
-- Each string must follow the format: \`"start-end"\` (e.g., \`"1-50"\`, \`"25-75"\`)
-- Maximum 100 lines per request across all files
-- Use search_files first if you don't know which lines to read
+- \`file_path\` (required): Absolute path to the file (e.g., /Users/username/project/src/file.ts)
+- \`offset\` (optional): Starting line number (1-indexed). Defaults to 1.
+- \`limit\` (optional): Maximum number of lines to read. If not specified, reads the complete file.
### Parameters Schema
\`\`\`typescript
{
files: [
{
- path: string, // File path (always quoted)
- line_ranges: string[] // Array of "start-end" strings (always quoted)
+ file_path: string, // Absolute path to file
+ offset?: number, // Starting line (1-indexed), defaults to 1
+ limit?: number // Max lines to read, omit to read entire file
}
]
}
\`\`\`
-### JSON String Rules for line_ranges
-
-**Each line range MUST be a quoted string in "number-number" format:**
-
-✅ **CORRECT** - Valid JSON:
-\`\`\`json
-{"path": "src/App.js", "line_ranges": ["1-50"]}
-{"path": "src/App.js", "line_ranges": ["1-30", "45-60"]}
-\`\`\`
+### Examples
-❌ **INCORRECT** - Invalid JSON:
-\`\`\`json
-{"path": "src/App.js", "line_ranges": [1-50]}
-{"path": "src/App.js", "line_ranges": ["1"-"50"]}
-{"path": "src/App.js", "line_ranges": [1, 50]}
-\`\`\`
-
-### Complete Examples
-
-**Read first 50 lines of a single file:**
+**Read entire file:**
\`\`\`json
{
"files": [
{
- "path": "src/components/Header.jsx",
- "line_ranges": ["1-50"]
+ "file_path": "/Users/username/project/src/App.tsx"
}
]
}
\`\`\`
-**Read multiple ranges from one file:**
+**Read first 50 lines:**
\`\`\`json
{
"files": [
{
- "path": "src/App.js",
- "line_ranges": ["1-20", "50-75", "100-120"]
+ "file_path": "/Users/username/project/src/App.tsx",
+ "limit": 50
}
]
}
\`\`\`
-**Read from multiple files (batch related files):**
+**Read lines 100-150 (50 lines starting at line 100):**
\`\`\`json
{
"files": [
{
- "path": "src/services/api.js",
- "line_ranges": ["1-40"]
- },
- {
- "path": "src/services/auth.js",
- "line_ranges": ["1-30"]
+ "file_path": "/Users/username/project/src/App.tsx",
+ "offset": 100,
+ "limit": 50
}
]
}
\`\`\`
-**Read specific function after searching:**
+**Read multiple files:**
\`\`\`json
{
"files": [
{
- "path": "src/utils/helpers.js",
- "line_ranges": ["45-68"]
+ "file_path": "/Users/username/project/src/api.ts"
+ },
+ {
+ "file_path": "/Users/username/project/src/auth.ts",
+ "offset": 50,
+ "limit": 30
}
]
}
\`\`\`
-### Line Range Format Rules
-
-1. **Must be a string**: \`"10-20"\` not \`10-20\`
-2. **Use hyphen separator**: \`"1-50"\` not \`"1:50"\` or \`"1,50"\`
-3. **Start before end**: \`"1-50"\` not \`"50-1"\`
-4. **Both numbers required**: \`"1-50"\` not \`"1-"\` or \`"-50"\`
-5. **No spaces**: \`"1-50"\` not \`"1 - 50"\`
-
-### Common Line Range Patterns
-
-| Use Case | line_ranges Example |
-|----------|-------------------|
-| Read from start | \`["1-50"]\` |
-| Read middle section | \`["100-150"]\` |
-| Read end of file | \`["450-500"]\` |
-| Multiple sections | \`["1-30", "60-90"]\` |
-| Single function | \`["45-68"]\` |
-
### Workflow: When You Don't Know Line Numbers
**Step 1:** Use \`search_files\` to find the code:
@@ -176,7 +139,7 @@ The \`read_file\` tool reads specific line ranges from one or more files. This i
{
"path": "src",
"regex": "function handleSubmit",
- "file_pattern": "*.js"
+ "file_pattern": "*.ts"
}
\`\`\`
@@ -187,134 +150,30 @@ The \`read_file\` tool reads specific line ranges from one or more files. This i
{
"files": [
{
- "path": "src/components/Form.js",
- "line_ranges": ["40-80"]
+ "file_path": "/Users/username/project/src/Form.tsx",
+ "offset": 40,
+ "limit": 50
}
]
}
\`\`\`
-### 100 Line Limit
+### Parameter Rules
-**You can read up to 100 lines total per request.**
-
-Valid (90 lines total):
-\`\`\`json
-{
- "files": [
- {
- "path": "file1.js",
- "line_ranges": ["1-50"]
- },
- {
- "path": "file2.js",
- "line_ranges": ["1-40"]
- }
- ]
-}
-\`\`\`
+1. \`file_path\` must be an absolute path
+2. \`offset\` must be >= 1 if specified
+3. \`limit\` must be >= 1 if specified
+4. If \`limit\` is omitted, the entire file is read from \`offset\`
-Invalid (150 lines total):
-\`\`\`json
-{
- "files": [
- {
- "path": "file1.js",
- "line_ranges": ["1-100"]
- },
- {
- "path": "file2.js",
- "line_ranges": ["1-50"]
- }
- ]
-}
-\`\`\`
+### Common Patterns
-### Batch Related Files
+| Use Case | Parameters |
+|----------|-----------|
+| Read entire file | \`file_path\` only |
+| Read from start | \`limit: 50\` |
+| Read middle section | \`offset: 100, limit: 50\` |
+| Read from a specific line to end | \`offset: 200\` |
-When examining related code, read multiple files in one request:
-\`\`\`json
-{
- "files": [
- {
- "path": "src/components/Button.jsx",
- "line_ranges": ["1-40"]
- },
- {
- "path": "src/styles/Button.css",
- "line_ranges": ["1-30"]
- },
- {
- "path": "src/components/Button.test.js",
- "line_ranges": ["1-30"]
- }
- ]
-}
-\`\`\`
-
-### Error Prevention Checklist
-
-Before generating the tool call, verify:
-- ✅ \`line_ranges\` is an array: \`["1-50"]\` not \`"1-50"\`
-- ✅ Each range is a quoted string: \`"1-50"\` not \`1-50\`
-- ✅ Format is \`"number-number"\`: \`"1-50"\` not \`"1:50"\`
-- ✅ Total lines ≤ 100 across all files
-- ✅ \`line_ranges\` array is not empty
-- ✅ Start line ≤ end line in each range
-
-### Common Mistakes to Avoid
-
-❌ **Unquoted ranges** (Invalid JSON):
-\`\`\`json
-{"path": "file.js", "line_ranges": [1-50]}
-\`\`\`
-
-❌ **Wrong format**:
-\`\`\`json
-{"path": "file.js", "line_ranges": ["1:50"]}
-{"path": "file.js", "line_ranges": ["1,50"]}
-\`\`\`
-
-❌ **Array of numbers instead of strings**:
-\`\`\`json
-{"path": "file.js", "line_ranges": [1, 50]}
-\`\`\`
-
-❌ **Missing line_ranges**:
-\`\`\`json
-{"path": "file.js"}
-\`\`\`
-
-❌ **Empty line_ranges**:
-\`\`\`json
-{"path": "file.js", "line_ranges": []}
-\`\`\`
-
-### Remember
-
-**Line ranges are strings in "start-end" format. Always quote them: \`"1-50"\`, never \`1-50\`**
-
-### Examples of Common Errors
-
-❌ **WRONG** (unquoted range):
-\`\`\`json
-{"files": [{"path": "src/App.js", "line_ranges": [1-50]}]}
-\`\`\`
-
-✅ **CORRECT** (quoted range):
-\`\`\`json
-{"files": [{"path": "src/App.js", "line_ranges": ["1-50"]}]}
-\`\`\`
-
-❌ **WRONG** (wrong separator):
-\`\`\`json
-{"files": [{"path": "src/App.js", "line_ranges": ["1:50"]}]}
-\`\`\`
-
-✅ **CORRECT** (hyphen separator):
-\`\`\`json
-{"files": [{"path": "src/App.js", "line_ranges": ["1-50"]}]}
-\`\`\`
# execute_command
diff --git a/src/core/prompts/tools/edit-file.ts b/src/core/prompts/tools/edit-file.ts
index a2039d26ed..18bba4d639 100644
--- a/src/core/prompts/tools/edit-file.ts
+++ b/src/core/prompts/tools/edit-file.ts
@@ -5,7 +5,7 @@ export function getFastApplyEditingInstructions(modelType: "Morph" | "Relace"):
- **ONLY use the edit_file tool for file modifications.** Traditional editing tools (apply_diff, write_to_file, insert_content, search_and_replace) are disabled in ${modelType} mode.
- **Focus on clear instructions and precise code edits** using the edit_file format with \`// ... existing code ...\` placeholders to represent unchanged sections.
- **The edit_file tool requires three parameters:**
- - \`target_file\`: Full path to the file to modify
+ - \`file_path\`: Absolute path to the file to modify (e.g., /Users/username/project/src/file.ts)
- \`instructions\`: Single sentence describing what you're doing (use first person)
- \`code_edit\`: Only the lines you want to change, using \`// ... existing code ...\` for unchanged sections
- **Always make all edits to a file in a single edit_file call** rather than multiple calls to the same file.`
@@ -40,11 +40,11 @@ ALWAYS make all edits to a file in a single edit_file instead of multiple edit_f
**REQUIRED Parameters**:
-1. **target_file** (string): The target file to modify. Always specify the full path to the file you want to edit.
+1. **file_path** (string): Absolute path to the file to modify (e.g., /Users/username/project/src/file.ts).
2. **instructions** (string): A single sentence instruction describing what you are going to do for the sketched edit. This is used to assist the less intelligent model in applying the edit. Use the first person to describe what you are going to do. Use it to disambiguate uncertainty in the edit.
3. **code_edit** (string): Specify ONLY the precise lines of code that you wish to edit. NEVER specify or write out unchanged code. Instead, represent all unchanged code using the comment of the language you're editing in - example: \`// ... existing code ...\`
-**ALL THREE PARAMETERS (target_file, instructions, code_edit) ARE MANDATORY**`
+**ALL THREE PARAMETERS (file_path, instructions, code_edit) ARE MANDATORY**`
}
diff --git a/src/core/prompts/tools/file-edit.ts b/src/core/prompts/tools/file-edit.ts
index 42cd07e5d8..8191d85115 100644
--- a/src/core/prompts/tools/file-edit.ts
+++ b/src/core/prompts/tools/file-edit.ts
@@ -9,7 +9,7 @@ export function getFileEditDescription(): string {
- You need to delete or rewrite a block of code but don't want to craft search/replace diff markers manually.
**Parameters**:
-1. \`target_file\` — Relative path to the file you want to modify.
+1. \`file_path\` — Absolute path to the file you want to modify (e.g., /Users/username/project/src/file.ts).
2. \`old_string\` — The current text you expect to replace. Provide enough context for a unique match; this can be empty to replace the entire file.
3. \`new_string\` — The text that should replace the match. Use an empty string to delete the matched content.
4. \`replace_all\` (optional, default false) — Set to true to replace every occurrence of the matched text. Leave false to replace only a single uniquely identified match.
diff --git a/src/core/prompts/tools/native-tools/edit_file.ts b/src/core/prompts/tools/native-tools/edit_file.ts
index 565ca7cc1c..0a5e5a6145 100644
--- a/src/core/prompts/tools/native-tools/edit_file.ts
+++ b/src/core/prompts/tools/native-tools/edit_file.ts
@@ -10,9 +10,9 @@ export default {
parameters: {
type: "object",
properties: {
- target_file: {
+ file_path: {
type: "string",
- description: "Full path of the file to modify",
+ description: "Absolute path to the file to modify (e.g., /Users/username/project/src/file.ts)",
},
instructions: {
type: "string",
@@ -24,7 +24,7 @@ export default {
"Only the edited lines using // ... existing code ... wherever unchanged content is omitted",
},
},
- required: ["target_file", "instructions", "code_edit"],
+ required: ["file_path", "instructions", "code_edit"],
additionalProperties: false,
},
},
diff --git a/src/core/prompts/tools/native-tools/file_edit.ts b/src/core/prompts/tools/native-tools/file_edit.ts
index 56d6274042..39379e34d6 100644
--- a/src/core/prompts/tools/native-tools/file_edit.ts
+++ b/src/core/prompts/tools/native-tools/file_edit.ts
@@ -10,9 +10,9 @@ export default {
parameters: {
type: "object",
properties: {
- target_file: {
+ file_path: {
type: "string",
- description: "Path to the file to modify, relative to the workspace root.",
+ description: "Absolute path to the file to modify (e.g., /Users/username/project/src/file.ts)",
},
old_string: {
type: "string",
@@ -30,7 +30,7 @@ export default {
"Set to true to replace every occurrence of the matched text. Defaults to false (replace a single uniquely identified occurrence).",
},
},
- required: ["target_file", "old_string", "new_string"],
+ required: ["file_path", "old_string", "new_string"],
additionalProperties: false,
},
},
diff --git a/src/core/prompts/tools/native-tools/index.ts b/src/core/prompts/tools/native-tools/index.ts
index 76969c859b..6d8c75964d 100644
--- a/src/core/prompts/tools/native-tools/index.ts
+++ b/src/core/prompts/tools/native-tools/index.ts
@@ -5,8 +5,8 @@ import executeCommand from "./execute_command"
import fetchInstructions from "./fetch_instructions"
import listCodeDefinitionNames from "./list_code_definition_names"
import listFiles from "./list_files"
-import newTask from "./new_task"
-import { read_file_multi, read_file_single } from "./read_file"
+// import newTask from "./new_task"
+import { read_file_single } from "./read_file"
import runSlashCommand from "./run_slash_command"
// import searchAndReplace from "./search_and_replace"
import searchFiles from "./search_files"
@@ -33,7 +33,7 @@ export const nativeTools = [
listFiles,
// newTask,
planFileEdit,
- read_file_multi,
+ read_file_single,
runSlashCommand,
// searchAndReplace,
searchFiles,
diff --git a/src/core/prompts/tools/native-tools/read_file.ts b/src/core/prompts/tools/native-tools/read_file.ts
index 14c2764424..f0019f82bc 100644
--- a/src/core/prompts/tools/native-tools/read_file.ts
+++ b/src/core/prompts/tools/native-tools/read_file.ts
@@ -5,7 +5,7 @@ export const read_file_multi = {
function: {
name: "read_file",
description:
- "Read one or more files and return their contents with line numbers for diffing or discussion. Use line ranges always to keep reads efficient and combine related files when possible. You will always read less thatn 100 lines at a time",
+ "Read one or more files and return their contents with line numbers. Use offset and limit to read specific portions of files efficiently. By default reads from the beginning with a reasonable limit.",
strict: true,
parameters: {
type: "object",
@@ -16,22 +16,22 @@ export const read_file_multi = {
items: {
type: "object",
properties: {
- path: {
+ file_path: {
type: "string",
- description: "Path to the file to read, relative to the workspace",
+ description:
+ "Absolute path to the file to read (e.g., /Users/username/project/src/file.ts)",
+ },
+ offset: {
+ type: ["number", "null"],
+ description: "Starting line number (1-indexed). Defaults to 1 if not specified.",
},
- line_ranges: {
- type: ["array"],
+ limit: {
+ type: ["number", "null"],
description:
- "Always required line ranges to read (format: start-end). If you are unsure about about the what line numbers to query, you can perform a search on the file to determine line numbers. You will never read more than 100 lines at a time!",
- items: {
- type: "string",
- pattern: "^\\d+-\\d+$",
- },
- minItems: 1,
+ "Maximum number of lines to read from offset. If not specified, reads the complete file from offset. Use smaller values for targeted reads.",
},
},
- required: ["path", "line_ranges"],
+ required: ["file_path"],
additionalProperties: false,
},
minItems: 1,
@@ -48,17 +48,26 @@ export const read_file_single = {
function: {
name: "read_file",
description:
- 'Request to read the contents of a file. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when discussing code.',
+ "Read a file and return its contents with line numbers. Use offset and limit to read specific portions efficiently.",
strict: true,
parameters: {
type: "object",
properties: {
- path: {
+ file_path: {
type: "string",
- description: "Path to the file to read, relative to the workspace",
+ description: "Absolute path to the file to read (e.g., /Users/username/project/src/file.ts)",
+ },
+ offset: {
+ type: ["number", "null"],
+ description: "Starting line number (1-indexed). Defaults to 1 if not specified.",
+ },
+ limit: {
+ type: ["number", "null"],
+ description:
+ "Maximum number of lines to read from offset. If not specified, reads the complete file from offset.",
},
},
- required: ["path"],
+ required: ["file_path"],
additionalProperties: false,
},
},
diff --git a/src/core/tools/editFileTool.ts b/src/core/tools/editFileTool.ts
index 93abd2041e..2ae7a888f4 100644
--- a/src/core/tools/editFileTool.ts
+++ b/src/core/tools/editFileTool.ts
@@ -49,15 +49,15 @@ function calculateFastApplyCost(inputTokens: number, outputTokens: number, model
async function validateParams(
cline: Task,
- targetFile: string | undefined,
+ filePath: string | undefined,
instructions: string | undefined,
codeEdit: string | undefined,
pushToolResult: PushToolResult,
): Promise {
- if (!targetFile) {
+ if (!filePath) {
cline.consecutiveMistakeCount++
cline.recordToolError("edit_file")
- pushToolResult(await cline.sayAndCreateMissingParamError("edit_file", "target_file"))
+ pushToolResult(await cline.sayAndCreateMissingParamError("edit_file", "file_path"))
return false
}
@@ -86,23 +86,26 @@ export async function editFileTool(
pushToolResult: PushToolResult,
removeClosingTag: RemoveClosingTag,
): Promise {
- const target_file: string | undefined = block.params.target_file
+ // Support both file_path (new) and target_file (legacy)
+ const file_path: string | undefined = (block.params as any).file_path || block.params.target_file
const instructions: string | undefined = block.params.instructions
const code_edit: string | undefined = block.params.code_edit
let fileExists = true
try {
- if (block.partial && (!target_file || instructions === undefined)) {
+ if (block.partial && (!file_path || instructions === undefined)) {
// wait so we can determine if it's a new file or editing an existing file
return
}
- fileExists = await fileExistsAtPath(path.resolve(cline.cwd, target_file ?? ""))
+ // Support absolute paths using cross-platform check
+ const resolvedPath = path.isAbsolute(file_path ?? "") ? file_path! : path.resolve(cline.cwd, file_path ?? "")
+ fileExists = await fileExistsAtPath(resolvedPath)
// Handle partial tool use
if (block.partial) {
const partialMessageProps = {
tool: fileExists ? "editedExistingFile" : "newFileCreated",
- path: getReadablePath(cline.cwd, removeClosingTag("target_file", target_file)),
+ path: getReadablePath(cline.cwd, removeClosingTag("file_path", file_path)),
content: removeClosingTag("code_edit", code_edit),
} satisfies ClineSayTool
await cline.ask("tool", JSON.stringify(partialMessageProps), block.partial).catch(() => {
@@ -112,17 +115,17 @@ export async function editFileTool(
}
// Validate required parameters
- if (!(await validateParams(cline, target_file, instructions, code_edit, pushToolResult))) {
+ if (!(await validateParams(cline, file_path, instructions, code_edit, pushToolResult))) {
return
}
// At this point we know all parameters are defined, so we can safely cast them
- const targetFile = target_file as string
+ const targetFile = file_path as string
const editInstructions = instructions as string
const editCode = code_edit as string
- // Validate and resolve the file path
- const absolutePath = path.resolve(cline.cwd, targetFile)
+ // Validate and resolve the file path - support absolute paths
+ const absolutePath = path.isAbsolute(targetFile) ? targetFile : path.resolve(cline.cwd, targetFile)
const relPath = getReadablePath(cline.cwd, absolutePath)
// Check if file access is allowed
diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts
index b32233a086..056d1cdda2 100644
--- a/src/core/tools/executeCommandTool.ts
+++ b/src/core/tools/executeCommandTool.ts
@@ -335,22 +335,20 @@ export async function executeCommand(
}
} else if (exitDetails.exitCode === undefined) {
result += ""
- exitStatus = `Exit code: `
+ exitStatus = `Exit code → `
} else {
if (exitDetails.exitCode !== 0) {
exitStatus += "Command execution was not successful, inspect the cause and adjust as needed.\n"
}
- exitStatus += `Exit code: ${exitDetails.exitCode}`
+ exitStatus += `Exit code → ${exitDetails.exitCode}`
}
} else {
result += ""
- exitStatus = `Exit code: `
+ exitStatus = `Exit code → `
}
- let workingDirInfo = ` within working directory '${terminal.getCurrentWorkingDirectory().toPosix()}'`
-
- return [false, `Command executed in terminal ${workingDirInfo}. ${exitStatus}\nOutput:\n${result}`]
+ return [false, `${exitStatus}\nOutput →\n${result}`]
} else {
return [
false,
diff --git a/src/core/tools/fileEditTool.ts b/src/core/tools/fileEditTool.ts
index ccdb59e021..994bd3c59d 100644
--- a/src/core/tools/fileEditTool.ts
+++ b/src/core/tools/fileEditTool.ts
@@ -28,7 +28,8 @@ export async function fileEditTool(
pushToolResult: PushToolResult,
removeClosingTag: RemoveClosingTag,
): Promise {
- const targetFile = block.params.target_file
+ // Support both file_path (new) and target_file (legacy)
+ const filePath = (block.params as any).file_path || block.params.target_file
const oldString = block.params.old_string
const newString = block.params.new_string
const replaceAllFlag = block.params.replace_all
@@ -38,7 +39,7 @@ export async function fileEditTool(
if (block.partial) {
const partialMessageProps: ClineSayTool = {
tool: "fileEdit",
- path: getReadablePath(cline.cwd, removeClosingTag("target_file", targetFile)),
+ path: getReadablePath(cline.cwd, removeClosingTag("file_path", filePath)),
search: removeClosingTag("old_string", oldString),
replace: removeClosingTag("new_string", newString),
useRegex: false,
@@ -52,13 +53,14 @@ export async function fileEditTool(
return
}
- if (!(await validateParams(cline, targetFile, oldString, newString, pushToolResult))) {
+ if (!(await validateParams(cline, filePath, oldString, newString, pushToolResult))) {
return
}
- const relPath = targetFile as string
+ const relPath = filePath as string
const readablePath = getReadablePath(cline.cwd, relPath)
- const absolutePath = path.resolve(cline.cwd, relPath)
+ // Support absolute paths using cross-platform check
+ const absolutePath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath)
const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
@@ -171,7 +173,7 @@ async function validateParams(
if (!targetFile) {
cline.consecutiveMistakeCount++
cline.recordToolError("file_edit")
- pushToolResult(await cline.sayAndCreateMissingParamError("file_edit", "target_file"))
+ pushToolResult(await cline.sayAndCreateMissingParamError("file_edit", "file_path"))
return false
}
diff --git a/src/core/tools/kilocode.ts b/src/core/tools/kilocode.ts
index 5b5fddccd4..cc37673e01 100644
--- a/src/core/tools/kilocode.ts
+++ b/src/core/tools/kilocode.ts
@@ -39,47 +39,51 @@ export async function blockFileReadWhenTooLarge(task: Task, relPath: string, con
if (tokenEstimate < tokenLimit) {
return undefined
}
- const linesRangesAreAllowed = ((await task.providerRef.deref()?.getState())?.maxReadFileLine ?? 0) >= 0
const errorMsg =
`File content exceeds token limit (${tokenEstimate} estimated tokens, limit is ${tokenLimit} tokens).` +
- (linesRangesAreAllowed ? ` Please use line_range to read smaller sections.` : ``)
+ ` Please use offset and limit to read smaller sections.`
return {
status: "blocked" as const,
error: errorMsg,
- xmlContent: `${relPath}${errorMsg}`,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
}
}
type FileEntry = {
path?: string
- lineRanges?: {
- start: number
- end: number
- }[]
+ offset?: number
+ limit?: number
}
-export function parseNativeFiles(nativeFiles: { path?: string; line_ranges?: string[] }[]) {
+export function parseNativeFiles(
+ nativeFiles: { file_path?: string; path?: string; offset?: number; limit?: number; line_ranges?: string[] }[],
+) {
const fileEntries = new Array()
for (const file of nativeFiles) {
- if (!file.path) continue
+ // Support both file_path (new) and path (legacy)
+ const filePath = file.file_path || file.path
+ if (!filePath) continue
const fileEntry: FileEntry = {
- path: file.path,
- lineRanges: [],
+ path: filePath,
+ offset: file.offset ?? 1,
+ limit: file.limit, // undefined means read complete file
}
- // Handle line_ranges array from native format
- if (file.line_ranges && Array.isArray(file.line_ranges)) {
- for (const range of file.line_ranges) {
- const match = String(range).match(/(\d+)-(\d+)/)
- if (match) {
- const [, start, end] = match.map(Number)
- if (!isNaN(start) && !isNaN(end)) {
- fileEntry.lineRanges?.push({ start, end })
- }
+ // Legacy support: convert line_ranges to offset+limit if provided
+ // Note: Only the first range is used since the new format only supports single offset+limit per file
+ if (file.line_ranges && Array.isArray(file.line_ranges) && file.line_ranges.length > 0) {
+ const range = file.line_ranges[0]
+ const match = String(range).match(/(\d+)-(\d+)/)
+ if (match) {
+ const [, start, end] = match.map(Number)
+ if (!isNaN(start) && !isNaN(end)) {
+ fileEntry.offset = start
+ fileEntry.limit = end - start + 1
}
}
}
+
fileEntries.push(fileEntry)
}
return fileEntries
@@ -90,12 +94,11 @@ export function getNativeReadFileToolDescription(blockName: string, files: FileE
if (paths.length === 0) {
return `[${blockName} with no valid paths]`
} else if (paths.length === 1) {
- // Modified part for single file
- return `[${blockName} for '${paths[0]}'. Reading multiple files at once is more efficient for the LLM. If other files are relevant to your current task, please read them simultaneously.]`
+ return `[${blockName} '${paths[0]}']`
} else if (paths.length <= 3) {
const pathList = paths.map((p) => `'${p}'`).join(", ")
- return `[${blockName} for ${pathList}]`
+ return `[${blockName} ${pathList}]`
} else {
- return `[${blockName} for ${paths.length} files]`
+ return `[${blockName} ${paths.length} files]`
}
}
diff --git a/src/core/tools/readFileTool.ts b/src/core/tools/readFileTool.ts
index 3e389c5497..ebd2833b4e 100644
--- a/src/core/tools/readFileTool.ts
+++ b/src/core/tools/readFileTool.ts
@@ -25,27 +25,29 @@ import {
} from "./helpers/imageHelpers"
export function getReadFileToolDescription(blockName: string, blockParams: any): string {
- // Handle both single path and multiple files via args
+ // Handle both single file_path and multiple files via args
// kilocode_change start
if (blockParams.files && Array.isArray(blockParams.files)) {
return getNativeReadFileToolDescription(blockName, parseNativeFiles(blockParams.files))
// kilocode_change end
+ } else if (blockParams.file_path) {
+ // New single file format with file_path
+ return `[${blockName} '${blockParams.file_path}']`
} else if (blockParams.args) {
try {
const parsed = parseXml(blockParams.args) as any
const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
- const paths = files.map((f: any) => f?.path).filter(Boolean) as string[]
+ const paths = files.map((f: any) => f?.path || f?.file_path).filter(Boolean) as string[]
if (paths.length === 0) {
return `[${blockName} with no valid paths]`
} else if (paths.length === 1) {
- // Modified part for single file
- return `[${blockName} for '${paths[0]}'. Reading multiple files at once is more efficient for the LLM. If other files are relevant to your current task, please read them simultaneously.]`
+ return `[${blockName} '${paths[0]}']`
} else if (paths.length <= 3) {
const pathList = paths.map((p) => `'${p}'`).join(", ")
- return `[${blockName} for ${pathList}]`
+ return `[${blockName} ${pathList}]`
} else {
- return `[${blockName} for ${paths.length} files]`
+ return `[${blockName} ${paths.length} files]`
}
} catch (error) {
console.error("Failed to parse read_file args XML for description:", error)
@@ -53,32 +55,28 @@ export function getReadFileToolDescription(blockName: string, blockParams: any):
}
} else if (blockParams.path) {
// Fallback for legacy single-path usage
- // Modified part for single file (legacy)
- return `[${blockName} for '${blockParams.path}'. Reading multiple files at once is more efficient for the LLM. If other files are relevant to your current task, please read them simultaneously.]`
+ return `[${blockName} '${blockParams.path}']`
} else {
return `[${blockName} with missing path/args]`
}
}
// Types
-interface LineRange {
- start: number
- end: number
-}
-
interface FileEntry {
path?: string
- lineRanges?: LineRange[]
+ offset?: number
+ limit?: number
}
-// New interface to track file processing state
+// Interface to track file processing state
interface FileResult {
path: string
status: "approved" | "denied" | "blocked" | "error" | "pending"
content?: string
error?: string
notice?: string
- lineRanges?: LineRange[]
- xmlContent?: string // Final XML content for this file
+ offset?: number
+ limit?: number
+ xmlContent?: string // Final content for this file
imageDataUrl?: string // Image data URL for image files
feedbackText?: string // User feedback text from approval/denial
feedbackImages?: any[] // User feedback images from approval/denial
@@ -94,8 +92,11 @@ export async function readFileTool(
) {
const argsXmlTag: string | undefined = block.params.args
const legacyPath: string | undefined = block.params.path
+ const newFilePath: string | undefined = (block.params as any).file_path // New: support file_path directly
const legacyStartLineStr: string | undefined = block.params.start_line
const legacyEndLineStr: string | undefined = block.params.end_line
+ const offsetParam: number | undefined = (block.params as any).offset
+ const limitParam: number | undefined = (block.params as any).limit
const nativeFiles: any[] | undefined = (block.params as any).files // kilocode_change: Native JSON format from OpenAI-style tool calls
@@ -106,9 +107,11 @@ export async function readFileTool(
// Handle partial message first
if (block.partial) {
let filePath = ""
- // Prioritize args for partial, then legacy path
- if (argsXmlTag) {
- const match = argsXmlTag.match(/.*?([^<]+)<\/path>/s)
+ // Prioritize file_path, then args, then legacy path
+ if (newFilePath) {
+ filePath = newFilePath
+ } else if (argsXmlTag) {
+ const match = argsXmlTag.match(/.*?<(?:file_)?path>([^<]+)<\/(?:file_)?path>/s)
if (match) filePath = match[1]
}
if (!filePath && legacyPath) {
@@ -116,7 +119,8 @@ export async function readFileTool(
filePath = legacyPath
}
- const fullPath = filePath ? path.resolve(cline.cwd, filePath) : ""
+ // Support absolute paths using cross-platform check
+ const fullPath = path.isAbsolute(filePath) ? filePath : filePath ? path.resolve(cline.cwd, filePath) : ""
const sharedMessageProps: ClineSayTool = {
tool: "readFile",
path: getReadablePath(cline.cwd, filePath),
@@ -137,28 +141,40 @@ export async function readFileTool(
if (nativeFiles && Array.isArray(nativeFiles)) {
fileEntries.push(...parseNativeFiles(nativeFiles))
// kilocode_change end
+ } else if (newFilePath) {
+ // Handle new single file_path format with optional offset/limit
+ const fileEntry: FileEntry = {
+ path: newFilePath,
+ offset: offsetParam ?? 1,
+ limit: limitParam, // undefined means read complete file
+ }
+ fileEntries.push(fileEntry)
} else if (argsXmlTag) {
- // Parse file entries from XML (new multi-file format)
+ // Parse file entries from XML (legacy multi-file format)
try {
const parsed = parseXml(argsXmlTag) as any
const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
for (const file of files) {
- if (!file.path) continue // Skip if no path in a file entry
+ const filePath = file.file_path || file.path
+ if (!filePath) continue // Skip if no path in a file entry
const fileEntry: FileEntry = {
- path: file.path,
- lineRanges: [],
+ path: filePath,
+ offset: file.offset ?? 1,
+ limit: file.limit, // undefined means read complete file
}
+ // Legacy support: convert line_range to offset+limit
if (file.line_range) {
const ranges = Array.isArray(file.line_range) ? file.line_range : [file.line_range]
- for (const range of ranges) {
- const match = String(range).match(/(\d+)-(\d+)/) // Ensure range is treated as string
+ if (ranges.length > 0) {
+ const match = String(ranges[0]).match(/(\d+)-(\d+)/)
if (match) {
const [, start, end] = match.map(Number)
if (!isNaN(start) && !isNaN(end)) {
- fileEntry.lineRanges?.push({ start, end })
+ fileEntry.offset = start
+ fileEntry.limit = end - start + 1
}
}
}
@@ -168,23 +184,26 @@ export async function readFileTool(
} catch (error) {
const errorMessage = `Failed to parse read_file XML args: ${error instanceof Error ? error.message : String(error)}`
await handleError("parsing read_file args", new Error(errorMessage))
- pushToolResult(`${errorMessage}`)
+ pushToolResult(`[error] ${errorMessage}`)
return
}
} else if (legacyPath) {
// Handle legacy single file path as a fallback
- console.warn("[readFileTool] Received legacy 'path' parameter. Consider updating to use 'args' structure.")
+ console.warn("[readFileTool] Received legacy 'path' parameter. Consider updating to use 'file_path'.")
const fileEntry: FileEntry = {
path: legacyPath,
- lineRanges: [],
+ offset: 1,
+ limit: undefined, // Read complete file by default
}
+ // Legacy support: convert start_line/end_line to offset+limit
if (legacyStartLineStr && legacyEndLineStr) {
const start = parseInt(legacyStartLineStr, 10)
const end = parseInt(legacyEndLineStr, 10)
if (!isNaN(start) && !isNaN(end) && start > 0 && end > 0) {
- fileEntry.lineRanges?.push({ start, end })
+ fileEntry.offset = start
+ fileEntry.limit = end - start + 1
} else {
console.warn(
`[readFileTool] Invalid legacy line range for ${legacyPath}: start='${legacyStartLineStr}', end='${legacyEndLineStr}'`,
@@ -194,12 +213,12 @@ export async function readFileTool(
fileEntries.push(fileEntry)
}
- // If, after trying both new and legacy, no valid file entries are found.
+ // If, after trying all formats, no valid file entries are found.
if (fileEntries.length === 0) {
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
- const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "args (containing valid file paths)")
- pushToolResult(`${errorMsg}`)
+ const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "file_path (absolute path to file)")
+ pushToolResult(`[error] ${errorMsg}`)
return
}
@@ -207,7 +226,8 @@ export async function readFileTool(
const fileResults: FileResult[] = fileEntries.map((entry) => ({
path: entry.path || "",
status: "pending",
- lineRanges: entry.lineRanges,
+ offset: entry.offset,
+ limit: entry.limit,
}))
// Function to update file result status
@@ -218,11 +238,6 @@ export async function readFileTool(
}
}
- // kilocode_change start: yolo mode
- const state = await cline.providerRef.deref()?.getState()
- const isYoloMode = state?.yoloMode ?? false
- // kilocode_change end
-
try {
// First validate all files and prepare for batch approval
const filesToApprove: FileResult[] = []
@@ -230,36 +245,27 @@ export async function readFileTool(
for (let i = 0; i < fileResults.length; i++) {
const fileResult = fileResults[i]
const relPath = fileResult.path
- const fullPath = path.resolve(cline.cwd, relPath)
-
- // Validate line ranges first
- if (fileResult.lineRanges) {
- let hasRangeError = false
- for (const range of fileResult.lineRanges) {
- if (range.start > range.end) {
- const errorMsg = "Invalid line range: end line cannot be less than start line"
- updateFileResult(relPath, {
- status: "blocked",
- error: errorMsg,
- xmlContent: `${relPath}Error reading file: ${errorMsg}`,
- })
- await handleError(`reading file ${relPath}`, new Error(errorMsg))
- hasRangeError = true
- break
- }
- if (isNaN(range.start) || isNaN(range.end)) {
- const errorMsg = "Invalid line range values"
- updateFileResult(relPath, {
- status: "blocked",
- error: errorMsg,
- xmlContent: `${relPath}Error reading file: ${errorMsg}`,
- })
- await handleError(`reading file ${relPath}`, new Error(errorMsg))
- hasRangeError = true
- break
- }
- }
- if (hasRangeError) continue
+
+ // Validate offset/limit if provided
+ if (fileResult.offset !== undefined && fileResult.offset < 1) {
+ const errorMsg = "Invalid offset: must be >= 1"
+ updateFileResult(relPath, {
+ status: "blocked",
+ error: errorMsg,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
+ })
+ await handleError(`reading file ${relPath}`, new Error(errorMsg))
+ continue
+ }
+ if (fileResult.limit !== undefined && fileResult.limit < 1) {
+ const errorMsg = "Invalid limit: must be >= 1"
+ updateFileResult(relPath, {
+ status: "blocked",
+ error: errorMsg,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
+ })
+ await handleError(`reading file ${relPath}`, new Error(errorMsg))
+ continue
}
// Then check RooIgnore validation
@@ -271,7 +277,7 @@ export async function readFileTool(
updateFileResult(relPath, {
status: "blocked",
error: errorMsg,
- xmlContent: `${relPath}${errorMsg}`,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
})
continue
}
@@ -281,38 +287,18 @@ export async function readFileTool(
}
}
- // Handle batch approval if there are multiple files to approve
+ // Handle batch files - auto-approve all
if (filesToApprove.length > 1) {
- const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
-
- // Prepare batch file data
+ // Create batch message to show in UI
const batchFiles = filesToApprove.map((fileResult) => {
const relPath = fileResult.path
- const fullPath = path.resolve(cline.cwd, relPath)
- const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
-
- // Create line snippet for this file
- let lineSnippet = ""
- if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
- const ranges = fileResult.lineRanges.map((range) =>
- t("tools:readFile.linesRange", { start: range.start, end: range.end }),
- )
- lineSnippet = ranges.join(", ")
- } else if (maxReadFileLine === 0) {
- lineSnippet = t("tools:readFile.definitionsOnly")
- } else if (maxReadFileLine > 0) {
- lineSnippet = t("tools:readFile.maxLines", { max: maxReadFileLine })
- }
-
- const readablePath = getReadablePath(cline.cwd, relPath)
- const key = `${readablePath}${lineSnippet ? ` (${lineSnippet})` : ""}`
-
+ const fullPath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath)
return {
- path: readablePath,
- lineSnippet,
- isOutsideWorkspace,
- key,
- content: fullPath, // Include full path for content
+ path: getReadablePath(cline.cwd, relPath),
+ lineSnippet: "",
+ isOutsideWorkspace: isPathOutsideWorkspace(fullPath),
+ key: relPath,
+ content: fullPath,
}
})
@@ -321,141 +307,43 @@ export async function readFileTool(
batchFiles,
} satisfies ClineSayTool)
- // kilocode_change start: yolo mode
- const { response, text, images } = isYoloMode
- ? { response: "yesButtonClicked" }
- : await cline.ask("tool", completeMessage, false)
- // kilocode_change end
-
- // Process batch response
- if (response === "yesButtonClicked") {
- // Approve all files
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- filesToApprove.forEach((fileResult) => {
- updateFileResult(fileResult.path, {
- status: "approved",
- feedbackText: text,
- feedbackImages: images,
- })
- })
- } else if (response === "noButtonClicked") {
- // Deny all files
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- cline.didRejectTool = true
- filesToApprove.forEach((fileResult) => {
- updateFileResult(fileResult.path, {
- status: "denied",
- xmlContent: `${fileResult.path}Denied by user`,
- feedbackText: text,
- feedbackImages: images,
- })
- })
- } else {
- // Handle individual permissions from objectResponse
- // if (text) {
- // await cline.say("user_feedback", text, images)
- // }
-
- try {
- const individualPermissions = JSON.parse(text || "{}")
- let hasAnyDenial = false
-
- batchFiles.forEach((batchFile, index) => {
- const fileResult = filesToApprove[index]
- const approved = individualPermissions[batchFile.key] === true
-
- if (approved) {
- updateFileResult(fileResult.path, {
- status: "approved",
- })
- } else {
- hasAnyDenial = true
- updateFileResult(fileResult.path, {
- status: "denied",
- xmlContent: `${fileResult.path}Denied by user`,
- })
- }
- })
+ // kilocode_change: Auto-approve - show in UI and immediately approve
+ // Use setImmediate to trigger approval AFTER ask starts waiting for response
+ setImmediate(() => {
+ cline.handleWebviewAskResponse("yesButtonClicked", undefined, undefined)
+ })
+ await cline.ask("tool", completeMessage, false)
- if (hasAnyDenial) {
- cline.didRejectTool = true
- }
- } catch (error) {
- // Fallback: if JSON parsing fails, deny all files
- console.error("Failed to parse individual permissions:", error)
- cline.didRejectTool = true
- filesToApprove.forEach((fileResult) => {
- updateFileResult(fileResult.path, {
- status: "denied",
- xmlContent: `${fileResult.path}Denied by user`,
- })
- })
- }
- }
+ // Auto-approve all files
+ filesToApprove.forEach((fileResult) => {
+ updateFileResult(fileResult.path, {
+ status: "approved",
+ })
+ })
} else if (filesToApprove.length === 1) {
- // Handle single file approval (existing logic)
+ // Single file - show in UI and auto-approve
const fileResult = filesToApprove[0]
const relPath = fileResult.path
- const fullPath = path.resolve(cline.cwd, relPath)
+ const fullPath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath)
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
- const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
-
- // Create line snippet for approval message
- let lineSnippet = ""
- if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
- const ranges = fileResult.lineRanges.map((range) =>
- t("tools:readFile.linesRange", { start: range.start, end: range.end }),
- )
- lineSnippet = ranges.join(", ")
- } else if (maxReadFileLine === 0) {
- lineSnippet = t("tools:readFile.definitionsOnly")
- } else if (maxReadFileLine > 0) {
- lineSnippet = t("tools:readFile.maxLines", { max: maxReadFileLine })
- }
const completeMessage = JSON.stringify({
tool: "readFile",
path: getReadablePath(cline.cwd, relPath),
isOutsideWorkspace,
content: fullPath,
- reason: lineSnippet,
} satisfies ClineSayTool)
- // kilocode_change start: yolo mode
- const { response, text, images } = isYoloMode
- ? { response: "yesButtonClicked" }
- : await cline.ask("tool", completeMessage, false)
- // kilocode_change end
-
- if (response !== "yesButtonClicked") {
- // Handle both messageResponse and noButtonClicked with text
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- cline.didRejectTool = true
-
- updateFileResult(relPath, {
- status: "denied",
- xmlContent: `${relPath}Denied by user`,
- feedbackText: text,
- feedbackImages: images,
- })
- } else {
- // Handle yesButtonClicked with text
- if (text) {
- await cline.say("user_feedback", text, images)
- }
+ // kilocode_change: Auto-approve - show in UI and immediately approve
+ // Use setImmediate to trigger approval AFTER ask starts waiting for response
+ setImmediate(() => {
+ cline.handleWebviewAskResponse("yesButtonClicked", undefined, undefined)
+ })
+ await cline.ask("tool", completeMessage, false)
- updateFileResult(relPath, {
- status: "approved",
- feedbackText: text,
- feedbackImages: images,
- })
- }
+ updateFileResult(fileResult.path, {
+ status: "approved",
+ })
}
// Track total image memory usage across all files
@@ -475,7 +363,7 @@ export async function readFileTool(
}
const relPath = fileResult.path
- const fullPath = path.resolve(cline.cwd, relPath)
+ const fullPath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath)
// Process approved files
try {
@@ -503,7 +391,7 @@ export async function readFileTool(
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${validationResult.notice}\n`,
+ xmlContent: `--- ${relPath} ---\n[notice] ${validationResult.notice}`,
})
continue
}
@@ -517,9 +405,9 @@ export async function readFileTool(
// Track file read
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
- // Store image data URL separately - NOT in XML
+ // Store image data URL separately
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${imageResult.notice}\n`,
+ xmlContent: `--- ${relPath} ---\n[image] ${imageResult.notice}`,
imageDataUrl: imageResult.dataUrl,
})
continue
@@ -528,7 +416,7 @@ export async function readFileTool(
updateFileResult(relPath, {
status: "error",
error: `Error reading image file: ${errorMsg}`,
- xmlContent: `${relPath}Error reading image file: ${errorMsg}`,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
})
await handleError(
`reading image file ${relPath}`,
@@ -547,25 +435,19 @@ export async function readFileTool(
const fileFormat = fileExtension.slice(1) || "bin" // Remove the dot, fallback to "bin"
updateFileResult(relPath, {
notice: `Binary file format: ${fileFormat}`,
- xmlContent: `${relPath}\nBinary file - content not displayed\n`,
+ xmlContent: `--- ${relPath} ---\n[binary ${fileFormat}] content not displayed`,
})
continue
}
}
- // Handle range reads (bypass maxReadFileLine)
- if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
- const rangeResults: string[] = []
- for (const range of fileResult.lineRanges) {
- const content = addLineNumbers(
- await readLines(fullPath, range.end - 1, range.start - 1),
- range.start,
- )
- const lineRangeAttr = ` lines="${range.start}-${range.end}"`
- rangeResults.push(`\n${content}`)
- }
+ // Handle offset/limit reads (if limit is specified)
+ if (fileResult.limit !== undefined) {
+ const startLine = fileResult.offset ?? 1
+ const endLine = startLine + fileResult.limit - 1
+ const content = addLineNumbers(await readLines(fullPath, endLine - 1, startLine - 1), startLine)
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${rangeResults.join("\n")}\n`,
+ xmlContent: content,
})
continue
}
@@ -575,9 +457,9 @@ export async function readFileTool(
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
- let xmlInfo = `Showing only ${maxReadFileLine} of ${totalLines} total lines. Use line_range if you need to read more lines\n`
+ // kilocode_change: Return raw definitions without path header
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${defResult}\n${xmlInfo}`,
+ xmlContent: `[definitions only, ${totalLines} total lines]\n${defResult}`,
})
}
} catch (error) {
@@ -595,17 +477,16 @@ export async function readFileTool(
// Handle files exceeding line threshold
if (maxReadFileLine > 0 && totalLines > maxReadFileLine) {
const content = addLineNumbers(await readLines(fullPath, maxReadFileLine - 1, 0))
- const lineRangeAttr = ` lines="1-${maxReadFileLine}"`
- let xmlInfo = `\n${content}\n`
+ // kilocode_change: Return content without path header, just note truncation
+ let fileOutput = `[showing ${maxReadFileLine} of ${totalLines} lines]\n${content}`
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
- xmlInfo += `${defResult}\n`
+ fileOutput += `\n[definitions]\n${defResult}`
}
- xmlInfo += `Showing only ${maxReadFileLine} of ${totalLines} total lines. Use line_range if you need to read more lines\n`
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${xmlInfo}`,
+ xmlContent: fileOutput,
})
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
@@ -630,33 +511,29 @@ export async function readFileTool(
}
// kilocode_change end
- const lineRangeAttr = ` lines="1-${totalLines}"`
- let xmlInfo = totalLines > 0 ? `\n${content}\n` : ``
-
- if (totalLines === 0) {
- xmlInfo += `File is empty\n`
- }
+ // kilocode_change: Return raw content without path header
+ let fileOutput = totalLines > 0 ? content : `(empty file: ${relPath})`
// Track file read
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
updateFileResult(relPath, {
- xmlContent: `${relPath}\n${xmlInfo}`,
+ xmlContent: fileOutput,
})
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
updateFileResult(relPath, {
status: "error",
error: `Error reading file: ${errorMsg}`,
- xmlContent: `${relPath}Error reading file: ${errorMsg}`,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
})
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
}
}
- // Generate final XML result from all file results
- const xmlResults = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
- const filesXml = `\n${xmlResults.join("\n")}\n`
+ // Generate final result from all file results
+ const fileContents = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
+ const filesOutput = fileContents.join("\n\n")
// Collect all image data URLs from file results
const fileImageUrls = fileResults
@@ -701,29 +578,29 @@ export async function readFileTool(
if (statusMessage || imagesToInclude.length > 0) {
// Always use formatResponse.toolResult when we have a status message or images
const result = formatResponse.toolResult(
- statusMessage || filesXml,
+ statusMessage || filesOutput,
imagesToInclude.length > 0 ? imagesToInclude : undefined,
)
// Handle different return types from toolResult
if (typeof result === "string") {
if (statusMessage) {
- pushToolResult(`${result}\n${filesXml}`)
+ pushToolResult(`${result}\n${filesOutput}`)
} else {
pushToolResult(result)
}
} else {
- // For block-based results, append the files XML as a text block if not already included
+ // For block-based results, append the files content as a text block if not already included
if (statusMessage) {
- const textBlock = { type: "text" as const, text: filesXml }
+ const textBlock = { type: "text" as const, text: filesOutput }
pushToolResult([...result, textBlock])
} else {
pushToolResult(result)
}
}
} else {
- // No images or status message, just push the files XML
- pushToolResult(filesXml)
+ // No images or status message, just push the file contents
+ pushToolResult(filesOutput)
}
} catch (error) {
// Handle all errors using per-file format for consistency
@@ -735,15 +612,15 @@ export async function readFileTool(
updateFileResult(relPath, {
status: "error",
error: `Error reading file: ${errorMsg}`,
- xmlContent: `${relPath}Error reading file: ${errorMsg}`,
+ xmlContent: `--- ${relPath} ---\n[error] ${errorMsg}`,
})
}
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
- // Generate final XML result from all file results
- const xmlResults = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
+ // Generate final result from all file results
+ const fileContents = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
- pushToolResult(`\n${xmlResults.join("\n")}\n`)
+ pushToolResult(fileContents.join("\n\n"))
}
}
diff --git a/src/core/tools/simpleReadFileTool.ts b/src/core/tools/simpleReadFileTool.ts
index cbe1bb785e..680615da9b 100644
--- a/src/core/tools/simpleReadFileTool.ts
+++ b/src/core/tools/simpleReadFileTool.ts
@@ -39,7 +39,8 @@ export async function simpleReadFileTool(
pushToolResult: PushToolResult,
_removeClosingTag: RemoveClosingTag,
) {
- const filePath: string | undefined = block.params.path
+ // Support both file_path (new) and path (legacy)
+ const filePath: string | undefined = (block.params as any).file_path || block.params.path
// Check if the current model supports images
const modelInfo = cline.api.getModel().info
@@ -47,7 +48,8 @@ export async function simpleReadFileTool(
// Handle partial message
if (block.partial) {
- const fullPath = filePath ? path.resolve(cline.cwd, filePath) : ""
+ // Support absolute paths using cross-platform check
+ const fullPath = filePath ? (path.isAbsolute(filePath) ? filePath : path.resolve(cline.cwd, filePath)) : ""
const sharedMessageProps: ClineSayTool = {
tool: "readFile",
path: getReadablePath(cline.cwd, filePath || ""),
@@ -65,18 +67,14 @@ export async function simpleReadFileTool(
if (!filePath) {
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
- const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "path")
- pushToolResult(`${errorMsg}`)
+ const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "file_path")
+ pushToolResult(`--- read_file ---\n[error] ${errorMsg}`)
return
}
const relPath = filePath
- const fullPath = path.resolve(cline.cwd, relPath)
-
- // kilocode_change start: yolo mode
- const state = await cline.providerRef.deref()?.getState()
- const isYoloMode = state?.yoloMode ?? false
- // kilocode_change end
+ // Support absolute paths using cross-platform check
+ const fullPath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath)
try {
// Check RooIgnore validation
@@ -108,29 +106,12 @@ export async function simpleReadFileTool(
reason: lineSnippet,
} satisfies ClineSayTool)
- // kilocode_change start: yolo mode
- const { response, text, images } = isYoloMode
- ? { response: "yesButtonClicked" }
- : await cline.ask("tool", completeMessage, false)
- // kilocode_change end
-
- if (response !== "yesButtonClicked") {
- // Handle denial
- if (text) {
- await cline.say("user_feedback", text, images)
- }
- cline.didRejectTool = true
-
- const statusMessage = text ? formatResponse.toolDeniedWithFeedback(text) : formatResponse.toolDenied()
-
- pushToolResult(`${statusMessage}\n${relPath}Denied by user`)
- return
- }
-
- // Handle approval with feedback
- if (text) {
- await cline.say("user_feedback", text, images)
- }
+ // kilocode_change: Auto-approve read_file - show in UI and immediately approve
+ // Use setImmediate to trigger approval AFTER ask starts waiting for response
+ setImmediate(() => {
+ cline.handleWebviewAskResponse("yesButtonClicked", undefined, undefined)
+ })
+ await cline.ask("tool", completeMessage, false)
// Process the file
const [totalLines, isBinary] = await Promise.all([countFileLines(fullPath), isBinaryFile(fullPath)])
@@ -213,10 +194,8 @@ export async function simpleReadFileTool(
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
- let xmlInfo = `Showing only definitions. Use standard read_file if you need to read actual content\n`
- pushToolResult(
- `${relPath}\n${defResult}\n${xmlInfo}`,
- )
+ // kilocode_change: Return raw definitions without XML
+ pushToolResult(`[definitions only]\n${defResult}`)
}
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
@@ -233,16 +212,15 @@ export async function simpleReadFileTool(
// Handle files exceeding line threshold
if (maxReadFileLine > 0 && totalLines > maxReadFileLine) {
const content = addLineNumbers(await readLines(fullPath, maxReadFileLine - 1, 0))
- const lineRangeAttr = ` lines="1-${maxReadFileLine}"`
- let xmlInfo = `\n${content}\n`
+ // kilocode_change: Return raw content without XML
+ let output = `[showing ${maxReadFileLine} of ${totalLines} lines]\n${content}`
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
- xmlInfo += `${defResult}\n`
+ output += `\n[definitions]\n${defResult}`
}
- xmlInfo += `Showing only ${maxReadFileLine} of ${totalLines} total lines. File is too large for complete display\n`
- pushToolResult(`${relPath}\n${xmlInfo}`)
+ pushToolResult(output)
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
console.warn(`[simple_read_file] Warning: ${error.message}`)
@@ -257,26 +235,19 @@ export async function simpleReadFileTool(
// Handle normal file read
const content = await extractTextFromFile(fullPath)
- const lineRangeAttr = ` lines="1-${totalLines}"`
- let xmlInfo = totalLines > 0 ? `\n${content}\n` : ``
-
- if (totalLines === 0) {
- xmlInfo += `File is empty\n`
- }
// Track file read
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
- // Return the result
- if (text) {
- const statusMessage = formatResponse.toolApprovedWithFeedback(text)
- pushToolResult(`${statusMessage}\n${relPath}\n${xmlInfo}`)
+ // kilocode_change: Return raw content without XML wrapping
+ if (totalLines === 0) {
+ pushToolResult(`(empty file: ${relPath})`)
} else {
- pushToolResult(`${relPath}\n${xmlInfo}`)
+ pushToolResult(content)
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
- pushToolResult(`${relPath}Error reading file: ${errorMsg}`)
+ pushToolResult(`[error] Error reading file ${relPath}: ${errorMsg}`)
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
}
}
diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts
index 88d86a4ec3..bd9f07bb43 100644
--- a/src/integrations/editor/DiffViewProvider.ts
+++ b/src/integrations/editor/DiffViewProvider.ts
@@ -3,7 +3,6 @@ import * as path from "path"
import * as fs from "fs/promises"
import * as diff from "diff"
import stripBom from "strip-bom"
-import { XMLBuilder } from "fast-xml-parser"
import delay from "delay"
import { createDirectoriesForFile } from "../../utils/fs"
@@ -300,17 +299,20 @@ export class DiffViewProvider {
}
/**
- * Formats a standardized XML response for file write operations
+ * Formats a standardized response for file write operations
+ * Returns a human-readable message with a snippet showing the changed content with surrounding context
*
* @param cwd Current working directory for path resolution
* @param isNewFile Whether this is a new file or an existing file being modified
- * @returns Formatted message and say object for UI feedback
+ * @returns Formatted message for the tool result
*/
async pushToolWriteResult(task: Task, cwd: string, isNewFile: boolean): Promise {
if (!this.relPath) {
throw new Error("No file path available in DiffViewProvider")
}
+ const absolutePath = path.resolve(cwd, this.relPath)
+
// Only send user_feedback_diff if userEdits exists
if (this.userEdits) {
// Create say object for UI feedback
@@ -324,49 +326,73 @@ export class DiffViewProvider {
await task.say("user_feedback_diff", JSON.stringify(say))
}
- // Build XML response
- const xmlObj = {
- file_write_result: {
- path: this.relPath,
- operation: isNewFile ? "created" : "modified",
- user_edits: this.userEdits ? this.userEdits : undefined,
- problems: this.newProblemsMessage || undefined,
- notice: {
- i: [
- "You do not need to re-read the file, as you have seen all changes",
- "Proceed with the task using these changes as the new baseline.",
- ...(this.userEdits
- ? [
- "If the user's edits have addressed part of the task or changed the requirements, adjust your approach accordingly.",
- ]
- : []),
- ],
- },
- },
- }
-
- const builder = new XMLBuilder({
- format: true,
- indentBy: "",
- suppressEmptyNode: true,
- processEntities: false,
- tagValueProcessor: (name, value) => {
- if (typeof value === "string") {
- // Only escape <, >, and & characters
- return value.replace(/&/g, "&").replace(//g, ">")
+ // Read the current file content to show the changed content with surrounding context
+ let snippet = ""
+ try {
+ const fileContent = await fs.readFile(absolutePath, "utf-8")
+ const lines = fileContent.split("\n")
+ const totalLines = lines.length
+ const contextLines = 10 // Lines of context before and after changes
+
+ let startLine = 0
+ let endLine = totalLines
+
+ if (this.originalContent && !isNewFile) {
+ // Use diff to find the range of changed lines
+ const diffs = diff.diffLines(this.originalContent, fileContent)
+ let currentLine = 0
+ let firstChangeLine = -1
+ let lastChangeLine = -1
+
+ for (const part of diffs) {
+ if (part.added || part.removed) {
+ if (firstChangeLine === -1) {
+ firstChangeLine = currentLine
+ }
+ lastChangeLine = currentLine + (part.count || 1)
+ }
+ if (!part.removed) {
+ currentLine += part.count || 0
+ }
}
- return value
- },
- attributeValueProcessor: (name, value) => {
- if (typeof value === "string") {
- // Only escape <, >, and & characters
- return value.replace(/&/g, "&").replace(//g, ">")
+
+ if (firstChangeLine !== -1) {
+ // Show the changed lines with context before and after
+ startLine = Math.max(0, firstChangeLine - contextLines)
+ endLine = Math.min(totalLines, lastChangeLine + contextLines)
}
- return value
- },
- })
+ }
+
+ // Get the snippet showing the change area with context
+ const snippetLines = lines.slice(startLine, endLine)
+ const maxLineNumberWidth = Math.max(6, String(endLine).length)
+
+ snippet = snippetLines
+ .map((line, index) => {
+ const lineNumber = String(startLine + index + 1).padStart(maxLineNumberWidth, " ")
+ return `${lineNumber}→${line}`
+ })
+ .join("\n")
+ } catch (error) {
+ // If we can't read the file, just skip the snippet
+ snippet = "(unable to read file content)"
+ }
+
+ // Build the response message
+ const operation = isNewFile ? "created" : "updated"
+ let message = `The file ${absolutePath} has been ${operation}. Here's the result of running \`cat -n\` on a snippet of the edited file:\n${snippet}`
+
+ // Add user edits notice if applicable
+ if (this.userEdits) {
+ message += `\n\nThe user made additional edits to the file:\n${this.userEdits}`
+ }
+
+ // Add any new problems detected
+ if (this.newProblemsMessage) {
+ message += this.newProblemsMessage
+ }
- return builder.build(xmlObj)
+ return message
}
async revertChanges(): Promise {
diff --git a/src/integrations/misc/__tests__/extract-text.spec.ts b/src/integrations/misc/__tests__/extract-text.spec.ts
index bb4b52fe93..604c571647 100644
--- a/src/integrations/misc/__tests__/extract-text.spec.ts
+++ b/src/integrations/misc/__tests__/extract-text.spec.ts
@@ -11,73 +11,73 @@ import {
describe("addLineNumbers", () => {
it("should add line numbers starting from 1 by default", () => {
const input = "line 1\nline 2\nline 3"
- const expected = "1 | line 1\n2 | line 2\n3 | line 3\n"
+ const expected = " 1→line 1\n 2→line 2\n 3→line 3\n"
expect(addLineNumbers(input)).toBe(expected)
})
it("should add line numbers starting from specified line number", () => {
const input = "line 1\nline 2\nline 3"
- const expected = "10 | line 1\n11 | line 2\n12 | line 3\n"
+ const expected = " 10→line 1\n 11→line 2\n 12→line 3\n"
expect(addLineNumbers(input, 10)).toBe(expected)
})
it("should handle empty content", () => {
expect(addLineNumbers("")).toBe("")
- expect(addLineNumbers("", 5)).toBe("5 | \n")
+ expect(addLineNumbers("", 5)).toBe(" 5→\n")
})
it("should handle single line content", () => {
- expect(addLineNumbers("single line")).toBe("1 | single line\n")
- expect(addLineNumbers("single line", 42)).toBe("42 | single line\n")
+ expect(addLineNumbers("single line")).toBe(" 1→single line\n")
+ expect(addLineNumbers("single line", 42)).toBe(" 42→single line\n")
})
it("should pad line numbers based on the highest line number", () => {
const input = "line 1\nline 2"
- // When starting from 99, highest line will be 100, so needs 3 spaces padding
- const expected = " 99 | line 1\n100 | line 2\n"
+ // When starting from 99, highest line will be 100, so needs 6 spaces padding (minimum width)
+ const expected = " 99→line 1\n 100→line 2\n"
expect(addLineNumbers(input, 99)).toBe(expected)
})
it("should preserve trailing newline without adding extra line numbers", () => {
const input = "line 1\nline 2\n"
- const expected = "1 | line 1\n2 | line 2\n"
+ const expected = " 1→line 1\n 2→line 2\n"
expect(addLineNumbers(input)).toBe(expected)
})
it("should handle multiple blank lines correctly", () => {
const input = "line 1\n\n\n\nline 2"
- const expected = "1 | line 1\n2 | \n3 | \n4 | \n5 | line 2\n"
+ const expected = " 1→line 1\n 2→\n 3→\n 4→\n 5→line 2\n"
expect(addLineNumbers(input)).toBe(expected)
})
it("should handle multiple trailing newlines correctly", () => {
const input = "line 1\nline 2\n\n\n"
- const expected = "1 | line 1\n2 | line 2\n3 | \n4 | \n"
+ const expected = " 1→line 1\n 2→line 2\n 3→\n 4→\n"
expect(addLineNumbers(input)).toBe(expected)
})
it("should handle numbered trailing newline correctly", () => {
const input = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10\n\n"
const expected =
- " 1 | Line 1\n 2 | Line 2\n 3 | Line 3\n 4 | Line 4\n 5 | Line 5\n 6 | Line 6\n 7 | Line 7\n 8 | Line 8\n 9 | Line 9\n10 | Line 10\n11 | \n"
+ " 1→Line 1\n 2→Line 2\n 3→Line 3\n 4→Line 4\n 5→Line 5\n 6→Line 6\n 7→Line 7\n 8→Line 8\n 9→Line 9\n 10→Line 10\n 11→\n"
expect(addLineNumbers(input)).toBe(expected)
})
it("should handle only blank lines with offset correctly", () => {
const input = "\n\n\n"
- const expected = "10 | \n11 | \n12 | \n"
+ const expected = " 10→\n 11→\n 12→\n"
expect(addLineNumbers(input, 10)).toBe(expected)
})
})
describe("everyLineHasLineNumbers", () => {
it("should return true for content with line numbers", () => {
- const input = "1 | line one\n2 | line two\n3 | line three"
+ const input = " 1→line one\n 2→line two\n 3→line three"
expect(everyLineHasLineNumbers(input)).toBe(true)
})
it("should return true for content with padded line numbers", () => {
- const input = " 1 | line one\n 2 | line two\n 3 | line three"
+ const input = " 1→line one\n 2→line two\n 3→line three"
expect(everyLineHasLineNumbers(input)).toBe(true)
})
@@ -87,7 +87,7 @@ describe("everyLineHasLineNumbers", () => {
})
it("should return false for mixed content", () => {
- const input = "1 | line one\nline two\n3 | line three"
+ const input = " 1→line one\nline two\n 3→line three"
expect(everyLineHasLineNumbers(input)).toBe(false)
})
@@ -95,21 +95,21 @@ describe("everyLineHasLineNumbers", () => {
expect(everyLineHasLineNumbers("")).toBe(false)
})
- it("should return false for content with pipe but no line numbers", () => {
- const input = "a | b\nc | d"
+ it("should return false for content with arrow but no line numbers", () => {
+ const input = "a → b\nc → d"
expect(everyLineHasLineNumbers(input)).toBe(false)
})
})
describe("stripLineNumbers", () => {
it("should strip line numbers from content", () => {
- const input = "1 | line one\n2 | line two\n3 | line three"
+ const input = " 1→line one\n 2→line two\n 3→line three"
const expected = "line one\nline two\nline three"
expect(stripLineNumbers(input)).toBe(expected)
})
it("should strip padded line numbers", () => {
- const input = " 1 | line one\n 2 | line two\n 3 | line three"
+ const input = " 1→line one\n 2→line two\n 3→line three"
const expected = "line one\nline two\nline three"
expect(stripLineNumbers(input)).toBe(expected)
})
@@ -123,38 +123,38 @@ describe("stripLineNumbers", () => {
expect(stripLineNumbers("")).toBe("")
})
- it("should preserve content with pipe but no line numbers", () => {
- const input = "a | b\nc | d"
+ it("should preserve content with arrow but no line numbers", () => {
+ const input = "a → b\nc → d"
expect(stripLineNumbers(input)).toBe(input)
})
it("should handle windows-style line endings", () => {
- const input = "1 | line one\r\n2 | line two\r\n3 | line three"
+ const input = " 1→line one\r\n 2→line two\r\n 3→line three"
const expected = "line one\r\nline two\r\nline three"
expect(stripLineNumbers(input)).toBe(expected)
})
it("should handle content with varying line number widths", () => {
- const input = " 1 | line one\n 10 | line two\n100 | line three"
+ const input = " 1→line one\n 10→line two\n 100→line three"
const expected = "line one\nline two\nline three"
expect(stripLineNumbers(input)).toBe(expected)
})
describe("aggressive mode", () => {
- it("should strip content with just a pipe character", () => {
- const input = "| line one\n| line two\n| line three"
+ it("should strip content with just an arrow character", () => {
+ const input = "→line one\n→line two\n→line three"
const expected = "line one\nline two\nline three"
expect(stripLineNumbers(input, true)).toBe(expected)
})
it("should strip content with mixed formats in aggressive mode", () => {
- const input = "1 | line one\n| line two\n123 | line three"
+ const input = "1 →line one\n→line two\n123 →line three"
const expected = "line one\nline two\nline three"
expect(stripLineNumbers(input, true)).toBe(expected)
})
- it("should not strip content with pipe characters not at start in aggressive mode", () => {
- const input = "text | more text\nx | y"
+ it("should not strip content with arrow characters not at start in aggressive mode", () => {
+ const input = "text → more text\nx → y"
expect(stripLineNumbers(input, true)).toBe(input)
})
@@ -162,20 +162,20 @@ describe("stripLineNumbers", () => {
expect(stripLineNumbers("", true)).toBe("")
})
- it("should preserve padding after pipe in aggressive mode", () => {
- const input = "| line with extra spaces\n1 | indented content"
+ it("should preserve content after arrow in aggressive mode", () => {
+ const input = "→ line with extra spaces\n1 → indented content"
const expected = " line with extra spaces\n indented content"
expect(stripLineNumbers(input, true)).toBe(expected)
})
it("should preserve windows-style line endings in aggressive mode", () => {
- const input = "| line one\r\n| line two\r\n| line three"
+ const input = "→line one\r\n→line two\r\n→line three"
const expected = "line one\r\nline two\r\nline three"
expect(stripLineNumbers(input, true)).toBe(expected)
})
it("should not affect regular content when using aggressive mode", () => {
- const input = "regular line\nanother line\nno pipes here"
+ const input = "regular line\nanother line\nno arrows here"
expect(stripLineNumbers(input, true)).toBe(input)
})
})
diff --git a/src/integrations/misc/extract-text.ts b/src/integrations/misc/extract-text.ts
index 8231c609be..15a7a741ee 100644
--- a/src/integrations/misc/extract-text.ts
+++ b/src/integrations/misc/extract-text.ts
@@ -111,10 +111,10 @@ export async function extractTextFromFile(filePath: string, maxReadFileLine?: nu
export function addLineNumbers(content: string, startLine: number = 1): string {
// If content is empty, return empty string - empty files should not have line numbers
- // If content is empty but startLine > 1, return "startLine | " because we know the file is not empty
+ // If content is empty but startLine > 1, return "startLine→" because we know the file is not empty
// but the content is empty at that line offset
if (content === "") {
- return startLine === 1 ? "" : `${startLine} | \n`
+ return startLine === 1 ? "" : `${String(startLine).padStart(6, " ")}→\n`
}
// Split into lines and handle trailing line feeds (\n)
@@ -124,29 +124,29 @@ export function addLineNumbers(content: string, startLine: number = 1): string {
lines.pop()
}
- const maxLineNumberWidth = String(startLine + lines.length - 1).length
+ const maxLineNumberWidth = Math.max(6, String(startLine + lines.length - 1).length)
const numberedContent = lines
.map((line, index) => {
const lineNumber = String(startLine + index).padStart(maxLineNumberWidth, " ")
- return `${lineNumber} | ${line}`
+ return `${lineNumber}→${line}`
})
.join("\n")
return numberedContent + "\n"
}
-// Checks if every line in the content has line numbers prefixed (e.g., "1 | content" or "123 | content")
-// Line numbers must be followed by a single pipe character (not double pipes)
+// Checks if every line in the content has line numbers prefixed (e.g., " 1→content" or " 123→content")
+// Line numbers must be followed by an arrow character (→)
export function everyLineHasLineNumbers(content: string): boolean {
const lines = content.split(/\r?\n/) // Handles both CRLF (carriage return (\r) + line feed (\n)) and LF (line feed (\n)) line endings
- return lines.length > 0 && lines.every((line) => /^\s*\d+\s+\|(?!\|)/.test(line))
+ return lines.length > 0 && lines.every((line) => /^\s*\d+→/.test(line))
}
/**
* Strips line numbers from content while preserving the actual content.
*
* @param content The content to process
- * @param aggressive When false (default): Only strips lines with clear number patterns like "123 | content"
- * When true: Uses a more lenient pattern that also matches lines with just a pipe character,
+ * @param aggressive When false (default): Only strips lines with clear number patterns like " 123→content"
+ * When true: Uses a more lenient pattern that also matches lines with just an arrow character,
* which can be useful when LLMs don't perfectly format the line numbers in diffs
* @returns The content with line numbers removed
*/
@@ -156,8 +156,8 @@ export function stripLineNumbers(content: string, aggressive: boolean = false):
// Process each line
const processedLines = lines.map((line) => {
- // Match line number pattern and capture everything after the pipe
- const match = aggressive ? line.match(/^\s*(?:\d+\s)?\|\s(.*)$/) : line.match(/^\s*\d+\s+\|(?!\|)\s?(.*)$/)
+ // Match line number pattern and capture everything after the arrow
+ const match = aggressive ? line.match(/^\s*(?:\d+\s)?→(.*)$/) : line.match(/^\s*\d+→(.*)$/)
return match ? match[1] : line
})
diff --git a/src/integrations/theme/default-themes/theme-variables.css b/src/integrations/theme/default-themes/theme-variables.css
index 2bddfb81cd..48e538f04f 100644
--- a/src/integrations/theme/default-themes/theme-variables.css
+++ b/src/integrations/theme/default-themes/theme-variables.css
@@ -46,7 +46,7 @@ classes at build time based on the @theme */
--sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--charts-green: var(--vscode-charts-green);
---charts-yellow: var(--vscode-charts-yellow);
+--charts-yellow: var(--color-matterai-yellow);
--charts-red: var(--vscode-charts-red);
--charts-blue: var(--vscode-charts-blue);
--charts-orange: var(--vscode-charts-orange);
@@ -85,7 +85,7 @@ classes at build time based on the @theme */
--ring: var(--vscode-input-border);
--chart-1: var(--vscode-charts-red);
--chart-2: var(--vscode-charts-blue);
---chart-3: var(--vscode-charts-yellow);
+--chart-3: var(--color-matterai-yellow);
--chart-4: var(--vscode-charts-orange);
--chart-5: var(--vscode-charts-green);
@@ -201,7 +201,7 @@ classes at build time based on the @theme */
--color-vscode-sideBarSectionHeader-background: var(--vscode-sideBarSectionHeader-background);
--color-vscode-sideBarSectionHeader-border: var(--vscode-sideBarSectionHeader-border);
--color-vscode-charts-green: var(--vscode-charts-green);
---color-vscode-charts-yellow: var(--vscode-charts-yellow);
+--color-vscode-charts-yellow: var(--color-matterai-yellow);
--color-vscode-charts-red: var(--vscode-charts-red);
--color-vscode-charts-blue: var(--vscode-charts-blue);
--color-vscode-charts-orange: var(--vscode-charts-orange);
diff --git a/src/package.json b/src/package.json
index 2d8ba6f2f4..77e05b650e 100644
--- a/src/package.json
+++ b/src/package.json
@@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "matterai",
- "version": "4.210.2",
+ "version": "5.0.0",
"icon": "assets/icons/matterai-ic.png",
"galleryBanner": {
"color": "#FFFFFF",
diff --git a/src/package.nls.cs.json b/src/package.nls.cs.json
index 0b8735098f..a05267d047 100644
--- a/src/package.nls.cs.json
+++ b/src/package.nls.cs.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Open Source AI asistent pro kódování pro plánování, vytváření a opravy kódu.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.es.json b/src/package.nls.es.json
index 93cb9fde29..5fb7eb9121 100644
--- a/src/package.nls.es.json
+++ b/src/package.nls.es.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Asistente de codificación de IA de código abierto para planificar, crear y corregir código.",
"command.newTask.title": "Nueva Tarea",
"command.explainCode.title": "Explicar Código",
diff --git a/src/package.nls.hi.json b/src/package.nls.hi.json
index 76f4e30687..f92ecb021a 100644
--- a/src/package.nls.hi.json
+++ b/src/package.nls.hi.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "कोड की योजना बनाने, निर्माण करने और उसे ठीक करने के लिए ओपन सोर्स एआई कोडिंग सहायक।",
"command.newTask.title": "नया कार्य",
"command.explainCode.title": "कोड समझाएं",
diff --git a/src/package.nls.id.json b/src/package.nls.id.json
index a542ed6d39..3cdcde7741 100644
--- a/src/package.nls.id.json
+++ b/src/package.nls.id.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Asisten coding AI Open Source untuk merencanakan, membangun, dan memperbaiki kode.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.json b/src/package.nls.json
index 6ce7793321..71b2e246a3 100644
--- a/src/package.nls.json
+++ b/src/package.nls.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Open Source AI coding assistant for planning, building, and fixing code.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
@@ -53,7 +53,6 @@
"ghost.commands.cancelSuggestions": "Cancel Suggested Edits",
"ghost.commands.applyCurrentSuggestion": "Apply Current Suggested Edit",
"ghost.commands.applyAllSuggestions": "Apply All Suggested Edits",
-
"ghost.commands.goToNextSuggestion": "Go To Next Suggestion",
"ghost.commands.goToPreviousSuggestion": "Go To Previous Suggestion"
}
diff --git a/src/package.nls.nl.json b/src/package.nls.nl.json
index 1127a623f3..e51afb5a84 100644
--- a/src/package.nls.nl.json
+++ b/src/package.nls.nl.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Een compleet ontwikkelteam van AI-agents in je editor.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.pt-BR.json b/src/package.nls.pt-BR.json
index 993c62c8c4..fc58072bb1 100644
--- a/src/package.nls.pt-BR.json
+++ b/src/package.nls.pt-BR.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Assistente de codificação de IA de código aberto para planejamento, construção e correção de código.",
"command.newTask.title": "Nova Tarefa",
"command.explainCode.title": "Explicar Código",
diff --git a/src/package.nls.ru.json b/src/package.nls.ru.json
index bf4e60c8d2..68d28c2ef8 100644
--- a/src/package.nls.ru.json
+++ b/src/package.nls.ru.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Целая команда ИИ-разработчиков в вашем редакторе.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.th.json b/src/package.nls.th.json
index 9dc8bd20f8..678c9c8d9b 100644
--- a/src/package.nls.th.json
+++ b/src/package.nls.th.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "ผู้ช่วยเขียนโค้ด AI แบบ Open Source สำหรับการวางแผน สร้าง และแก้ไขโค้ด",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.tr.json b/src/package.nls.tr.json
index 71bdaa107d..230edf0f56 100644
--- a/src/package.nls.tr.json
+++ b/src/package.nls.tr.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Kod planlama, oluşturma ve düzeltme için açık kaynaklı yapay zeka kodlama asistanı.",
"command.newTask.title": "Yeni Görev",
"command.explainCode.title": "Kodu Açıkla",
diff --git a/src/package.nls.uk.json b/src/package.nls.uk.json
index 7da6975d24..08b75b1656 100644
--- a/src/package.nls.uk.json
+++ b/src/package.nls.uk.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Помічник з кодування AI з відкритим кодом для планування, створення та виправлення коду.",
"views.contextMenu.label": "Axon Code",
"views.terminalMenu.label": "Axon Code",
diff --git a/src/package.nls.vi.json b/src/package.nls.vi.json
index 526f7611c7..babb263735 100644
--- a/src/package.nls.vi.json
+++ b/src/package.nls.vi.json
@@ -1,5 +1,5 @@
{
- "extension.displayName": "Axon Code AI Agent",
+ "extension.displayName": "Axon AI Coding Agent | Code Reviews",
"extension.description": "Trợ lý mã hóa AI nguồn mở để lập kế hoạch, xây dựng và sửa mã.",
"command.newTask.title": "Tác Vụ Mới",
"command.explainCode.title": "Giải Thích Mã",
diff --git a/src/shared/tools.ts b/src/shared/tools.ts
index d98ca5ce15..7b82cd09f0 100644
--- a/src/shared/tools.ts
+++ b/src/shared/tools.ts
@@ -63,6 +63,7 @@ export const toolParamNames = [
"title",
"description",
"target_file",
+ "file_path",
"old_string",
"new_string",
"replace_all",
@@ -191,14 +192,15 @@ export interface SearchAndReplaceToolUse extends ToolUse {
export interface FileEditToolUse extends ToolUse {
name: "file_edit"
- params: Required, "target_file" | "old_string" | "new_string">> &
- Partial, "replace_all">>
+ params: Required, "file_path" | "old_string" | "new_string">> &
+ Partial, "replace_all" | "target_file">>
}
// kilocode_change start: Morph fast apply
export interface EditFileToolUse extends ToolUse {
name: "edit_file"
- params: Required, "target_file" | "instructions" | "code_edit">>
+ params: Required, "file_path" | "instructions" | "code_edit">> &
+ Partial, "target_file">>
}
// kilocode_change end
diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx
index 578849cd39..5719f2273e 100644
--- a/webview-ui/src/components/chat/ChatView.tsx
+++ b/webview-ui/src/components/chat/ChatView.tsx
@@ -2216,7 +2216,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction
+
{/* Top section: Title/Subtitle left, Icons right */}