From 656f081f90f422c21c346dab272d5f4ecd708e68 Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 10:53:26 +0530 Subject: [PATCH 1/6] improved interleaved thinking --- src/api/providers/openrouter.ts | 24 +++++++++-- src/api/transform/openai-format.ts | 54 ++++++++++++++++++++---- src/core/task-persistence/apiMessages.ts | 7 ++- src/core/task/Task.ts | 37 ++++++++-------- src/package.json | 2 +- 5 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 4e4c57b242..fcbe641219 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -219,6 +219,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH addNativeToolCallsToParams(requestOptions, this.options, metadata) + // kilocode_change: logs removed + let stream try { stream = await this.client.chat.completions.create( @@ -241,10 +243,13 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH try { let fullContent = "" + let fullReasoning = "" // kilocode_change: variable kept for structural integrity if needed, but unused logs removed let isThinking = false + const _allRawChunks: any[] = [] // DEBUG: collect all chunks for await (const chunk of stream) { + _allRawChunks.push(chunk) // DEBUG: collect // OpenRouter returns an error object instead of the OpenAI SDK throwing an error. if ("error" in chunk) { const error = chunk.error as { message?: string; code?: number } @@ -318,12 +323,24 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH } } + // kilocode_change start: Handle reasoning from API (both 'reasoning' and 'reasoning_content' keys) + // Some models send 'reasoning', others send 'reasoning_content' + if ("reasoning" in delta && delta.reasoning) { + const reasoningText = (delta.reasoning as string | undefined) || "" + yield { + type: "reasoning", + text: reasoningText, + } + } + if ("reasoning_content" in delta && delta.reasoning_content) { + const reasoningText = (delta.reasoning_content as string | undefined) || "" yield { type: "reasoning", - text: (delta.reasoning_content as string | undefined) || "", + text: reasoningText, } } + // kilocode_change end // Handle native tool calls when toolStyle is "json" yield* processNativeToolCallsFromDelta(delta, getActiveToolUseStyle(this.options)) @@ -333,6 +350,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH // yield { type: "text", text: delta.content } // } } + + // kilocode_change: logs removed } catch (error) { console.error("OpenRouter API Error:", error) let errorMessage = makeOpenRouterErrorReadable(error) @@ -556,9 +575,6 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH // kilocode_change start function makeOpenRouterErrorReadable(error: any) { try { - // Add logging to help debug the issue - console.debug("makeOpenRouterErrorReadable called with error:", JSON.stringify(error, null, 2)) - const metadata = error?.error?.metadata as { raw?: string; provider_name?: string } | undefined const parsedJson = safeJsonParse(metadata?.raw) const rawError = parsedJson as { error?: string & { message?: string }; detail?: string } | undefined diff --git a/src/api/transform/openai-format.ts b/src/api/transform/openai-format.ts index a4d91f58e8..f093736d65 100644 --- a/src/api/transform/openai-format.ts +++ b/src/api/transform/openai-format.ts @@ -8,16 +8,36 @@ export function convertToOpenAiMessages( for (const anthropicMessage of anthropicMessages) { if (typeof anthropicMessage.content === "string") { - openAiMessages.push({ role: anthropicMessage.role, content: anthropicMessage.content }) + // kilocode_change start: Preserve reasoning fields for assistant messages with string content + if (anthropicMessage.role === "assistant") { + const messageWithReasoning = anthropicMessage as typeof anthropicMessage & { + reasoning?: string + reasoning_content?: string + } + const assistantMsg: OpenAI.Chat.ChatCompletionAssistantMessageParam = { + role: "assistant", + content: anthropicMessage.content, + } + if (messageWithReasoning.reasoning) { + ;(assistantMsg as any).reasoning = messageWithReasoning.reasoning + } + if (messageWithReasoning.reasoning_content) { + ;(assistantMsg as any).reasoning_content = messageWithReasoning.reasoning_content + } + openAiMessages.push(assistantMsg) + } else { + openAiMessages.push({ role: anthropicMessage.role, content: anthropicMessage.content }) + } + // kilocode_change end } else { // image_url.url is base64 encoded image data // ensure it contains the content-type of the image: data:image/png;base64, /* - { role: "user", content: "" | { type: "text", text: string } | { type: "image_url", image_url: { url: string } } }, - // content required unless tool_calls is present - { role: "assistant", content?: "" | null, tool_calls?: [{ id: "", function: { name: "", arguments: "" }, type: "function" }] }, - { role: "tool", tool_call_id: "", content: ""} - */ + { role: "user", content: "" | { type: "text", text: string } | { type: "image_url", image_url: { url: string } } }, + // content required unless tool_calls is present + { role: "assistant", content?: "" | null, tool_calls?: [{ id: "", function: { name: "", arguments: "" }, type: "function" }] }, + { role: "tool", tool_call_id: "", content: ""} + */ if (anthropicMessage.role === "user") { const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{ nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] @@ -139,12 +159,30 @@ export function convertToOpenAiMessages( }, })) - openAiMessages.push({ + // kilocode_change start: Preserve reasoning fields from the original message + // Some models (DeepSeek, OpenRouter, etc.) return reasoning/reasoning_content + // in their responses, and these should be passed through in subsequent API calls + const assistantMsg: OpenAI.Chat.ChatCompletionAssistantMessageParam = { role: "assistant", content, // Cannot be an empty array. API expects an array with minimum length 1, and will respond with an error if it's empty tool_calls: tool_calls.length > 0 ? tool_calls : undefined, - }) + } + + // Pass through reasoning fields if present on the source message + const messageWithReasoning = anthropicMessage as typeof anthropicMessage & { + reasoning?: string + reasoning_content?: string + } + if (messageWithReasoning.reasoning) { + ;(assistantMsg as any).reasoning = messageWithReasoning.reasoning + } + if (messageWithReasoning.reasoning_content) { + ;(assistantMsg as any).reasoning_content = messageWithReasoning.reasoning_content + } + + openAiMessages.push(assistantMsg) + // kilocode_change end } } } diff --git a/src/core/task-persistence/apiMessages.ts b/src/core/task-persistence/apiMessages.ts index f846aaf13f..35f9d6a5e9 100644 --- a/src/core/task-persistence/apiMessages.ts +++ b/src/core/task-persistence/apiMessages.ts @@ -9,7 +9,12 @@ import { fileExistsAtPath } from "../../utils/fs" import { GlobalFileNames } from "../../shared/globalFileNames" import { getTaskDirectoryPath } from "../../utils/storage" -export type ApiMessage = Anthropic.MessageParam & { ts?: number; isSummary?: boolean } +export type ApiMessage = Anthropic.MessageParam & { + ts?: number + isSummary?: boolean + reasoning?: string + reasoning_content?: string +} export async function readApiMessages({ taskId, diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 136564df5e..e97640cc16 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2239,18 +2239,7 @@ export class Task extends EventEmitter implements TaskLike { switch (chunk.type) { case "reasoning": { reasoningMessage += chunk.text - // Only apply formatting if the message contains sentence-ending punctuation followed by ** - let formattedReasoning = reasoningMessage - if (reasoningMessage.includes("**")) { - // Add line breaks before **Title** patterns that appear after sentence endings - // This targets section headers like "...end of sentence.**Title Here**" - // Handles periods, exclamation marks, and question marks - formattedReasoning = reasoningMessage.replace( - /([.!?])\*\*([^*\n]+)\*\*/g, - "$1\n\n**$2**", - ) - } - await this.say("reasoning", formattedReasoning, undefined, true) + // kilocode_change: removed UI updates for reasoning as requested break } case "usage": @@ -2685,10 +2674,21 @@ export class Task extends EventEmitter implements TaskLike { assistantMessageContent.push({ type: "text", text: assistantMessage }) } assistantMessageContent.push(...assistantToolUses) - await this.addToApiConversationHistory({ + + const assistantHistoryMessage: Anthropic.MessageParam & { + reasoning?: string + reasoning_content?: string + } = { role: "assistant", content: assistantMessageContent, - }) + } + + // Add reasoning content if present + if (reasoningMessage) { + assistantHistoryMessage.reasoning = reasoningMessage + } + + await this.addToApiConversationHistory(assistantHistoryMessage) // kilocode_change end TelemetryService.instance.captureConversationMessage(this.taskId, "assistant") @@ -3138,9 +3138,12 @@ export class Task extends EventEmitter implements TaskLike { } const messagesSinceLastSummary = getMessagesSinceLastSummary(this.apiConversationHistory) - let cleanConversationHistory = maybeRemoveImageBlocks(messagesSinceLastSummary, this.api).map( - ({ role, content }) => ({ role, content }), - ) + let cleanConversationHistory = maybeRemoveImageBlocks(messagesSinceLastSummary, this.api).map((msg) => ({ + role: msg.role, + content: msg.content, + // kilocode_change: preserve reasoning + ...("reasoning" in msg ? { reasoning: (msg as any).reasoning } : {}), + })) // kilocode_change start // Fetch project properties for KiloCode provider tracking diff --git a/src/package.json b/src/package.json index 98b4b25ee0..6a1e1bac28 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "5.3.4", + "version": "5.3.5", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", From cd98ebc2c1ea5fcf3ad2de033b25cac285bca317 Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 11:42:36 +0530 Subject: [PATCH 2/6] show read tool lines + show main reasoning text --- src/api/providers/openrouter.ts | 4 ++-- src/core/task/Task.ts | 13 ++++++++++++- src/core/tools/readFileTool.ts | 8 +++++++- src/package.json | 2 +- src/shared/ExtensionMessage.ts | 4 ++++ webview-ui/src/components/chat/ChatRow.tsx | 12 +++++++----- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index fcbe641219..e168c93f14 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -307,8 +307,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH isThinking = false } - newText = newText.replace(/<\/?think>/g, "") - newText = newText.replace(//g, "") + // newText = newText.replace(/<\/?think>/g, "") + // newText = newText.replace(//g, "") yield { type: "reasoning", diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index e97640cc16..295f3d3734 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2239,7 +2239,18 @@ export class Task extends EventEmitter implements TaskLike { switch (chunk.type) { case "reasoning": { reasoningMessage += chunk.text - // kilocode_change: removed UI updates for reasoning as requested + let formattedReasoning = reasoningMessage + if (reasoningMessage.includes("**")) { + formattedReasoning = reasoningMessage.replace( + /([.!?])\*\*([^*\n]+)\*\*/g, + "$1\n\n**$2**", + ) + } + if (formattedReasoning.includes("")) { + formattedReasoning = formattedReasoning.replace(/<\/?think>/g, "") + formattedReasoning = formattedReasoning.replace(//g, "") + await this.say("reasoning", formattedReasoning, undefined, true) + } break } case "usage": diff --git a/src/core/tools/readFileTool.ts b/src/core/tools/readFileTool.ts index 059f24cbd6..4511001b37 100644 --- a/src/core/tools/readFileTool.ts +++ b/src/core/tools/readFileTool.ts @@ -303,12 +303,16 @@ export async function readFileTool( const batchFiles = filesToApprove.map((fileResult) => { const relPath = fileResult.path const fullPath = path.isAbsolute(relPath) ? relPath : path.resolve(cline.cwd, relPath) + const offset = fileResult.offset + const limit = fileResult.limit return { path: getReadablePath(cline.cwd, relPath), - lineSnippet: "", + lineSnippet: offset !== undefined && limit !== undefined ? `#L${offset}-${offset + limit - 1}` : "", isOutsideWorkspace: isPathOutsideWorkspace(fullPath), key: relPath, content: fullPath, + offset, + limit, } }) @@ -342,6 +346,8 @@ export async function readFileTool( path: getReadablePath(cline.cwd, relPath), isOutsideWorkspace, content: fullPath, + offset: fileResult.offset || 0, + limit: fileResult.limit || MAX_READ_FILE_LINES, } satisfies ClineSayTool) // kilocode_change: Auto-approve - show in UI and immediately approve diff --git a/src/package.json b/src/package.json index 6a1e1bac28..a9b0022e9c 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "5.3.5", + "version": "5.3.5-internal", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 6a4213bfc8..51d674dca9 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -516,12 +516,16 @@ export interface ClineSayTool { endLine?: number lineNumber?: number query?: string + offset?: number + limit?: number batchFiles?: Array<{ path: string lineSnippet: string isOutsideWorkspace?: boolean key: string content?: string + offset?: number + limit?: number }> batchDiffs?: Array<{ path: string diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 23de89eaae..2dd1c75894 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -814,11 +814,13 @@ export const ChatRowContent = ({ {tool.path?.startsWith(".") && .} {fileName} - {tool.reason - ?.replace("lines", "#L") - ?.replaceAll(" ", "") - .replaceAll("(", "") - .replaceAll(")", "")} + {tool.offset !== undefined && tool.limit !== undefined + ? `#L${tool.offset}-${tool.offset + tool.limit - 1}` + : tool.reason + ?.replace("lines", "#L") + ?.replaceAll(" ", "") + .replaceAll("(", "") + .replaceAll(")", "")} From f301fdb9fb0deb384e24f0f256e41cb7645449e7 Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 15:31:33 +0530 Subject: [PATCH 3/6] fix: remove debug code causing memory leak in openrouter stream handler --- src/api/providers/openrouter.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index e168c93f14..d72eaf3f30 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -246,10 +246,8 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH let fullReasoning = "" // kilocode_change: variable kept for structural integrity if needed, but unused logs removed let isThinking = false - const _allRawChunks: any[] = [] // DEBUG: collect all chunks for await (const chunk of stream) { - _allRawChunks.push(chunk) // DEBUG: collect // OpenRouter returns an error object instead of the OpenAI SDK throwing an error. if ("error" in chunk) { const error = chunk.error as { message?: string; code?: number } From 9406a5487436bb2c022f212727f21f0ee9bbdc86 Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 16:08:02 +0530 Subject: [PATCH 4/6] update: improve UI components and webview message handling --- src/core/webview/webviewMessageHandler.ts | 4 ++ .../src/components/chat/BatchDiffApproval.tsx | 11 +++ webview-ui/src/components/chat/ChatRow.tsx | 67 ++++++++++++++++--- .../chat/kilocode/AcceptRejectButtons.tsx | 9 ++- .../src/components/common/CodeAccordian.tsx | 23 ++++++- 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 1b3f400130..1a6e26924b 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1281,6 +1281,9 @@ export const webviewMessageHandler = async ( } } + // Get the first changed line number from the diff anchor (1-indexed for VS Code) + const firstLineNumber = edit.diffAnchor ? edit.diffAnchor.start.line + 1 : undefined + return { relPath: edit.relPath, absolutePath: edit.absolutePath, @@ -1288,6 +1291,7 @@ export const webviewMessageHandler = async ( additions: totalAdditions, deletions: totalDeletions, }, + firstLineNumber, } }) diff --git a/webview-ui/src/components/chat/BatchDiffApproval.tsx b/webview-ui/src/components/chat/BatchDiffApproval.tsx index 24ad8d489d..e9a106b05f 100644 --- a/webview-ui/src/components/chat/BatchDiffApproval.tsx +++ b/webview-ui/src/components/chat/BatchDiffApproval.tsx @@ -1,3 +1,4 @@ +import { vscode } from "@/utils/vscode" import React, { memo, useState } from "react" import CodeAccordian from "../common/CodeAccordian" @@ -37,6 +38,8 @@ export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProp {files.map((file) => { // Combine all diffs into a single diff string for this file const combinedDiff = file.diffs?.map((diff) => diff.content).join("\n\n") || file.content + // Get the first changed line from the diffs, or fallback to extracting from combined diff + const firstChangedLine = file.diffs?.find((d) => d.startLine)?.startLine return (
@@ -46,6 +49,14 @@ export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProp language="diff" isExpanded={expandedFiles[file.path] || false} onToggleExpand={() => handleToggleExpand(file.path)} + onJumpToFile={(line) => + vscode.postMessage({ + type: "openFile", + text: "./" + file.path, + values: + (line ?? firstChangedLine) ? { line: line ?? firstChangedLine } : undefined, + }) + } />
) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 2dd1c75894..b0a3341a0c 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -17,7 +17,7 @@ import { findMatchingResourceOrTemplate } from "@src/utils/mcp" import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" import { vscode } from "@src/utils/vscode" -import CodeAccordian from "../common/CodeAccordian" +import CodeAccordian, { extractFirstLineNumberFromDiff } from "../common/CodeAccordian" import ImageBlock from "../common/ImageBlock" import MarkdownBlock from "../common/MarkdownBlock" import Thumbnails from "../common/Thumbnails" @@ -465,6 +465,7 @@ export const ChatRowContent = ({ } // Regular single file diff + const diffCode = tool.content ?? tool.diff return ( <>
@@ -487,12 +488,19 @@ export const ChatRowContent = ({
+ vscode.postMessage({ + type: "openFile", + text: "./" + tool.path, + values: line ? { line } : undefined, + }) + } /> { // kilocode_change start @@ -502,9 +510,18 @@ export const ChatRowContent = ({
) - case "fileEdit": + case "fileEdit": { const fileEditDiff = tool.diff ?? buildFileEditDiff(tool) const diffStats = computeDiffStats(fileEditDiff) + // Extract first line number from diff for navigation + const firstLineNumber = extractFirstLineNumberFromDiff(fileEditDiff) + const openFileWithLine = () => { + vscode.postMessage({ + type: "openFile", + text: "./" + tool.path, + values: firstLineNumber ? { line: firstLineNumber } : undefined, + }) + } return (
@@ -544,18 +561,18 @@ export const ChatRowContent = ({ className="cursor-pointer" role="button" tabIndex={0} - title={tool.path} + title={tool.path + (firstLineNumber ? `:${firstLineNumber}` : "")} aria-label={tool.path} data-mention={tool.path} onClick={(e) => { e.stopPropagation() - vscode.postMessage({ type: "openFile", text: "./" + tool.path }) + openFileWithLine() }} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault() e.stopPropagation() - vscode.postMessage({ type: "openFile", text: "./" + tool.path }) + openFileWithLine() } }}> {tool.path.split("/").pop() || tool.path} @@ -585,6 +602,7 @@ export const ChatRowContent = ({
) + } case "planFileEdit": return (
@@ -620,7 +638,9 @@ export const ChatRowContent = ({
) - case "insertContent": + case "insertContent": { + // Use the explicit lineNumber from the tool, or extract from diff + const insertLineNumber = tool.lineNumber && tool.lineNumber > 0 ? tool.lineNumber : undefined return ( <>
@@ -653,11 +673,23 @@ export const ChatRowContent = ({ isLoading={message.partial} isExpanded={isExpanded} onToggleExpand={handleToggleExpand} + onJumpToFile={(line) => + vscode.postMessage({ + type: "openFile", + text: "./" + tool.path, + values: + (line ?? insertLineNumber) ? { line: line ?? insertLineNumber } : undefined, + }) + } />
) - case "searchAndReplace": + } + case "searchAndReplace": { + const searchDiffCode = tool.diff + const searchFirstLineMatch = searchDiffCode?.match(/@@\s*-\d+(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s*@@/) + const searchFirstLineNumber = searchFirstLineMatch ? parseInt(searchFirstLineMatch[1], 10) : undefined return ( <>
@@ -686,10 +718,21 @@ export const ChatRowContent = ({ isLoading={message.partial} isExpanded={isExpanded} onToggleExpand={handleToggleExpand} + onJumpToFile={(line) => + vscode.postMessage({ + type: "openFile", + text: "./" + tool.path, + values: + (line ?? searchFirstLineNumber) + ? { line: line ?? searchFirstLineNumber } + : undefined, + }) + } />
) + } case "codebaseSearch": { return (
@@ -753,7 +796,13 @@ export const ChatRowContent = ({ isLoading={message.partial} isExpanded={isExpanded} onToggleExpand={handleToggleExpand} - onJumpToFile={() => vscode.postMessage({ type: "openFile", text: "./" + tool.path })} + onJumpToFile={(line) => + vscode.postMessage({ + type: "openFile", + text: "./" + tool.path, + values: line ? { line } : undefined, + }) + } /> { // kilocode_change start diff --git a/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx b/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx index 31dec0a4a5..6480130bc7 100644 --- a/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx +++ b/webview-ui/src/components/chat/kilocode/AcceptRejectButtons.tsx @@ -10,6 +10,7 @@ type FileChange = { additions: number deletions: number } + firstLineNumber?: number } export const AcceptRejectButtons = ({ onDismiss }: { onDismiss?: () => void }) => { @@ -127,7 +128,13 @@ export const AcceptRejectButtons = ({ onDismiss }: { onDismiss?: () => void }) =
vscode.postMessage({ type: "openFile", text: file.relPath })} + onClick={() => + vscode.postMessage({ + type: "openFile", + text: file.relPath, + values: file.firstLineNumber ? { line: file.firstLineNumber } : undefined, + }) + } title={file.absolutePath}> {/* File Icon - Use actual file icon */} {fileIconUrl ? ( diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx index f10de657fa..86990df1c0 100644 --- a/webview-ui/src/components/common/CodeAccordian.tsx +++ b/webview-ui/src/components/common/CodeAccordian.tsx @@ -7,6 +7,21 @@ import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanu import { ToolUseBlock, ToolUseBlockHeader } from "./ToolUseBlock" import CodeBlock from "../kilocode/common/CodeBlock" // kilocode_change +/** + * Extract the first line number from a unified diff string. + * Looks for @@ -oldStart,oldCount +newStart,newCount @@ patterns. + * Returns the newStart line number (where changes appear in the new file). + */ +export function extractFirstLineNumberFromDiff(diff?: string): number | undefined { + if (!diff) return undefined + // Match unified diff hunk headers: @@ -start,count +start,count @@ + const match = diff.match(/@@\s*-\d+(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s*@@/) + if (match) { + return parseInt(match[1], 10) + } + return undefined +} + interface CodeAccordianProps { path?: string code?: string @@ -18,7 +33,7 @@ interface CodeAccordianProps { onToggleExpand: () => void header?: string headerContent?: ReactNode - onJumpToFile?: () => void + onJumpToFile?: (line?: number) => void } const CodeAccordian = ({ @@ -37,6 +52,8 @@ const CodeAccordian = ({ const inferredLanguage = useMemo(() => language ?? (path ? getLanguageFromPath(path) : "txt"), [path, language]) const source = useMemo(() => String(code).trim() /*kilocode_change: coerce to string*/, [code]) const hasHeader = Boolean(path || isFeedback || header || headerContent) + // Extract line number from diff if this is a diff view + const firstLineNumber = useMemo(() => extractFirstLineNumberFromDiff(code), [code]) return ( @@ -80,9 +97,9 @@ const CodeAccordian = ({ style={{ fontSize: 13.5 }} onClick={(e) => { e.stopPropagation() - onJumpToFile() + onJumpToFile(firstLineNumber) }} - aria-label={`Open file: ${path}`} + aria-label={`Open file: ${path}${firstLineNumber ? `:${firstLineNumber}` : ""}`} /> )} {!onJumpToFile && ( From ecfa115914727720e9af30c9587c21d5d3cbc27e Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 16:13:30 +0530 Subject: [PATCH 5/6] fix: TypeScript type error in ChatRow.tsx --- webview-ui/src/components/chat/ChatRow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index b0a3341a0c..38d3a44c0a 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -94,12 +94,12 @@ const headerStyle: React.CSSProperties = { } // Build a minimal unified diff for fileEdit when backend doesn't supply one -const buildFileEditDiff = (tool: ClineSayTool): string | null => { +const buildFileEditDiff = (tool: ClineSayTool): string | undefined => { const path = tool.path || "file" const oldText = (tool.search ?? "").trimEnd() const newText = (tool.replace ?? tool.content ?? "").trimEnd() - if (!oldText && !newText) return null + if (!oldText && !newText) return undefined const lines: string[] = [] lines.push(`--- a/${path}`) From d7cabfbcb9eab0aa301d3b0470da09722151aa47 Mon Sep 17 00:00:00 2001 From: code-crusher Date: Sat, 7 Feb 2026 16:21:55 +0530 Subject: [PATCH 6/6] update package version --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index a9b0022e9c..6a1e1bac28 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "5.3.5-internal", + "version": "5.3.5", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF",