-
Notifications
You must be signed in to change notification settings - Fork 51
Feat/local terminal execution #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add TERMINAL_EXECUTION_MODE environment variable (local/sandbox) - Create local terminal execution utilities using Node.js child_process - Update terminal command tool to support dual execution modes - Set local mode as default for better user experience - Maintain backward compatibility with existing sandbox mode
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. WalkthroughIntroduces an ExecutionMode (“local” | “sandbox”) across backend tools and system prompt; adds new file-edit tools; implements local filesystem and terminal execution utilities; updates chat route to pass executionMode; enhances UI with sidebar auto-open, background terminal indicator, link styling, and desktop breakpoint; reorganizes environment config and README accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Client
participant API as POST /api/chat
participant Tools as createTools
participant Prompt as systemPrompt
participant Exec as Tool (read/write/delete/searchReplace/multiEdit/run)
User->>Client: Submit message
Client->>API: Request with model
API->>Prompt: systemPrompt(model, executionMode)
API->>Tools: createTools(userID, writer, mode, executionMode)
API->>Exec: Invoke tool(s)
alt executionMode == local
Exec->>Exec: Use local-file-operations / local-terminal
else executionMode == sandbox
Exec->>Exec: Use sandbox manager/files/commands
end
Exec-->>API: Stream outputs
API-->>Client: Stream assistant + tool parts
sequenceDiagram
participant Client
participant Messages
participant Hook as useSidebarAutoOpen
participant Sidebar as GlobalState.openFileInSidebar
Client->>Messages: Render(messages, status, lastIdx, resetRef)
Messages->>Hook: useSidebarAutoOpen(messages, lastIdx, status)
Hook->>Hook: Detect read/write tool output
alt width >= 950px and streaming
Hook->>Sidebar: openFileInSidebar(file, content, range)
end
Hook-->>Messages: resetSidebarFlag via ref
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (23)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🔭 Outside diff range comments (3)
lib/ai/tools/write-file.ts (1)
10-17: Align tool reference naming in write-file.tsWe confirmed via
lib/ai/tools/index.tsthat the read‐file tool is registered under the keyreadFile(camelCase), notread_file. To avoid confusion for the model and end users, update the description inlib/ai/tools/write-file.tsto either reference the actual ID or use generic wording.• File: lib/ai/tools/write-file.ts
• Line: 14Suggested diff:
- - If this is an existing file, you MUST use the read_file tool first to read the file's contents. + - If this is an existing file, you MUST use the read file tool first to read the file's contents.lib/ai/tools/read-file.ts (2)
24-36: Guard against invalid offset/limit (offset <= 0 or negative limit yields incorrect slices)Currently,
offsetcan be 0 or negative, which causeslines.slice(offset - 1)to slice from the end. Similarly, a negativelimitwill slice from the end. Add Zod constraints to prevent invalid values.Apply this diff:
- offset: z - .number() - .optional() + offset: z + .number() + .int() + .min(1) + .optional() .describe( "The line number to start reading from. Only provide if the file is too large to read at once.", ), - limit: z - .number() - .optional() + limit: z + .number() + .int() + .positive() + .optional() .describe( "The number of lines to read. Only provide if the file is too large to read at once.", ),
22-23: Clarify sandbox vs. local absolute‐path semanticsThe current description (“you can use either a relative path in the workspace or an absolute path … preserved as is”) is misleading:
sandbox.files.read()only accepts paths rooted inside the sandbox (i.e./…refers to the sandbox’s root, not your host filesystem). Host‐absolute paths (e.g./Users/me/file.txton your machine) will fail unless that exact path was already written into the sandbox. In local mode you can use host‐absolute paths viareadLocalFile(), but for sandbox reads you must:
- Provide workspace‐relative paths (e.g.
./foo.txt), or- Provide sandbox‐rooted paths starting with
/(e.g./data/foo.txt), and- If you need to read a host file into the sandbox, first read it locally then
sandbox.files.write('/desired/sandbox/path', content).Please update the descriptions at lines 22–23 (and similarly at 47–80) to reflect this distinction.
♻️ Duplicate comments (1)
lib/ai/tools/utils/local-terminal.ts (1)
113-113: Duplicate code: Same shell injection vulnerabilityThis line has the same security issue as Line 65 - using
sh -cwith unsanitized command input.
🧹 Nitpick comments (25)
components/ui/tool-block.tsx (1)
41-41: Consistent icon sizing: nice touch; consider minor a11y + button semantics tweaksThe SVG sizing variant ensures consistent icon dimensions in tool blocks. Two small follow-ups:
- Decorative icons: hide from AT if they don’t convey meaning.
- Buttons: set non-submit intent explicitly and disable when not clickable to avoid accidental submissions and improve keyboard behavior.
Apply this diff for the icon container (decorative case):
- <div className="w-[21px] inline-flex items-center flex-shrink-0 text-foreground [&>svg]:h-4 [&>svg]:w-4"> + <div + className="w-[21px] inline-flex items-center flex-shrink-0 text-foreground [&>svg]:h-4 [&>svg]:w-4" + aria-hidden="true" + >Outside the selected lines, consider:
<button type="button" className={`${baseClasses} ${clickableClasses}`} onClick={isClickable ? onClick : undefined} onKeyDown={isClickable ? onKeyDown : undefined} tabIndex={isClickable ? 0 : -1} disabled={!isClickable} aria-label={isClickable && target ? `Open ${target} in sidebar` : undefined} />app/globals.css (1)
5-5: Custom desktop variant added — aligns with the PR’s responsive updatesDefining
@custom-variant desktop (@media (min-width: 950px));is a clean way to standardize desktop-specific styles across components. Consider documenting “desktop” in a brief README/style guide note so contributors know when to usedesktop:vs legacy breakpoints..env.local.example (1)
41-41: Fix dotenv linter warning: ensure trailing newline at EOFThe dotenv linter flags a missing blank line at the end of file. Add a trailing newline to keep CI linting happy.
Apply this minimal diff at EOF:
-# NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback" +# NEXT_PUBLIC_WORKOS_REDIRECT_URI="http://localhost:3000/callback" +lib/system-prompt.ts (2)
22-22: Compute current date at call time to avoid stale prompts
currentDateTimeis computed once at module load. In a long-lived server, this can become stale. Prefer computing withinsystemPromptor pass the date/time as a param.Example adjustment:
export const systemPrompt = (model: string, executionMode?: ExecutionMode) => { const currentDateTime = new Date().toLocaleDateString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", }); return `... The current date is ${currentDateTime}. ...`; }
65-86: Sandbox environment block: verify accuracy and make it resilient to driftTwo concerns:
- Potential mismatch: “User:
root” with home “/home/user” is unlikely; root’s home is typically/root.- Hard-coded versions and details are prone to drift; the prompt may become misleading over time.
You can either verify and correct the specifics or generalize the language to avoid brittleness. Suggested edit:
<sandbox_environment> System Environment: -- OS: Debian GNU/Linux 12 linux/amd64 (with internet access) -- User: `root` (with sudo privileges) -- Home directory: /home/user +- OS: Debian GNU/Linux (linux/amd64), with internet access +- A default user with sudo privileges is available +- Home directory: the user's home (e.g., /home/user) - VPN connectivity is not available due to missing TUN/TAP device support in the sandbox environment Development Environment: -- Python 3.12.10 (commands: python3, pip3) -- Node.js 20.19.2 (commands: node, npm) -- Golang 1.24.2 (commands: go) +- Python (commands: python3, pip3) +- Node.js (commands: node, npm) +- Golang (commands: go) Pre-installed Tools: - curl, wget, nmap, iputils-ping, whois, traceroute, dnsutils, whatweb, wafw00f, subfinder, gobuster -- SecLists is pre-installed in /home/user and should be used by default for any fuzzing or wordlist needs +- SecLists is available under the user's home; use it by default for fuzzing/wordlists (verify exact path with \`ls\`) </sandbox_environment>Additionally, consider adding a brief “local environment” block (for
localmode) with guardrails: confirm before destructive actions (delete, chmod, mass search-replace), avoid touching dotfiles and OS directories unless explicitly requested, and prefer operating within the workspace root.lib/ai/tools/utils/local-terminal.ts (1)
10-10: Unused parameter in interfaceThe
userparameter inLocalTerminalOptionsis defined but never used in the implementation.Either implement user switching functionality or remove the unused parameter:
export interface LocalTerminalOptions { cwd?: string; - user?: string; onStdout?: (output: string) => void; onStderr?: (output: string) => void; background?: boolean; }lib/ai/tools/utils/local-file-operations.ts (1)
70-71: Line count calculation may be incorrect for empty filesAn empty string will return a line count of 1 when split by newline, which may not be the intended behavior.
Consider handling empty content specially:
- const lineCount = contents.split("\n").length; + const lineCount = contents ? contents.split("\n").length : 0;README.md (3)
30-34: Add language to fenced code block for env snippetMarkdown lint flagged missing language. Use a language hint for better readability and to appease MD040.
Apply this diff:
- ``` + ```bash # OpenRouter API key (Required) # Get your API key at: https://openrouter.ai/ OPENROUTER_API_KEY=your_openrouter_api_key_here--- `38-45`: **Add language to fenced code block for sandbox snippet** Same MD040 issue here. Add a language hint. Apply this diff: ```diff - ``` + ```bash # Switch to sandbox mode (optional) TERMINAL_EXECUTION_MODE=sandbox # E2B API key for sandbox execution # Get your API key at: https://e2b.dev/ E2B_API_KEY=your_e2b_api_key_here--- `36-36`: **Minor grammar: “in a sandbox”** Improve readability with an article. Apply this diff: ```diff - HackerAI can execute terminal commands locally (default) or in sandbox. For sandbox mode, you'll need an E2B API key: + HackerAI can execute terminal commands locally (default) or in a sandbox. For sandbox mode, you'll need an E2B API key:lib/ai/tools/delete-file.ts (2)
14-25: Input field “explanation” is not usedYou accept an
explanationbut never use it. Either log it (e.g., via thewriter) for traceability or drop it from the schema to reduce noise.
18-19: Path semantics: align description with other tools (absolute vs relative)This tool says “relative to the workspace root,” while read/write allow absolute paths. For consistency, either:
- Allow absolute paths here as well (and enforce/normalize in local/sandbox modes), or
- Update read/write descriptions to state relative-only if the underlying APIs don’t accept absolute paths.
lib/ai/tools/index.ts (1)
40-42: Expose new tools in ask-mode only if read-only (currently OK)
askmode remains read-only (only readFile exposed). That’s consistent with principle of least privilege. If you later want to surface non-destructive utilities (e.g., search-only), ensure you don’t leak write/delete in this mode.If you want the UI to auto-open after search-replace edits, consider emitting a follow-up readFile (or enhancing the Messages auto-open hook) so users immediately see the updated file.
lib/ai/tools/search-replace.ts (1)
32-33: Typo in schema description: “occurrences”Minor typo in the input schema description.
Apply:
- .describe("Replace all occurences of old_string (default false)"), + .describe("Replace all occurrences of old_string (default false)"),lib/ai/tools/run-terminal-cmd.ts (2)
27-37: Make is_background optional with a defaultSchema currently requires is_background. Defaulting to false simplifies most calls.
- is_background: z - .boolean() - .describe("Whether the command should be run in the background."), + is_background: z + .boolean() + .optional() + .default(false) + .describe("Whether the command should be run in the background."),
15-27: Minor copy edit in guidance text“Dont” -> “Don't”
-7. Dont include any newlines in the command.`, +7. Don't include any newlines in the command.`,app/hooks/useSidebarAutoOpen.ts (1)
53-65: Optional: support auto-open after search/replaceAfter a successful search-replace, consider auto-reading and opening the file to show updated content. You could detect
tool-searchReplacewith a success message and then queue a readFile tool call.I can draft an enhancement to detect
tool-searchReplaceand trigger a follow-up readFile for the same path if you want to include it in this PR.app/components/MessagePartHandler.tsx (8)
6-6: Icon import changes look good; watch for default sizing changes.Switching to lucide-react defaults (without explicit size classes) can slightly change layout. Please spot-check common breakpoints for any visual regressions.
39-47: Make range computation resilient to falsy values and support offset-only/limit-only cases.Truthiness checks will fail when offset=0 or limit=0, and offset-only is not represented. Recommend explicit number checks and handling offset-only and limit-only consistently.
Apply this diff:
- const getFileRange = () => { - if (readInput.offset && readInput.limit) { - return ` L${readInput.offset}-${readInput.offset + readInput.limit - 1}`; - } - if (!readInput.offset && readInput.limit) { - return ` L1-${readInput.limit}`; - } - return ""; - }; + const getFileRange = () => { + const hasOffset = typeof readInput.offset === "number"; + const hasLimit = typeof readInput.limit === "number"; + if (hasLimit) { + const start = hasOffset ? (readInput.offset as number) : 1; + const end = start + (readInput.limit as number) - 1; + return ` L${start}-${end}`; + } + if (hasOffset) { + return ` L${readInput.offset}-`; + } + return ""; + };
72-88: Align sidebar range with the displayed “Lx-y” label when only limit is provided.Currently, the sidebar range is only set if both offset and limit are present. If only limit is provided, the label shows L1-limit but the sidebar gets no range. Also harden against falsy 0/undefined values.
Apply this diff:
- const handleOpenInSidebar = () => { - const cleanContent = readOutput.result.replace(/^\s*\d+\|/gm, ""); - const range = - readInput.offset && readInput.limit - ? { - start: readInput.offset, - end: readInput.offset + readInput.limit - 1, - } - : undefined; + const handleOpenInSidebar = () => { + const cleanContent = readOutput.result.replace(/^\s*\d+\|/gm, ""); + const hasOffset = typeof readInput.offset === "number"; + const hasLimit = typeof readInput.limit === "number"; + const range = hasLimit + ? { + start: hasOffset ? (readInput.offset as number) : 1, + end: + (hasOffset ? (readInput.offset as number) : 1) + + (readInput.limit as number) - + 1, + } + : undefined;
97-107: Add explicit button semantics for accessibility (if ToolBlock doesn’t already).If ToolBlock doesn’t set role="button" and tabIndex={0} when isClickable, consider adding them to ensure screen-reader and keyboard accessibility.
141-167: Differentiate success vs failure on write and gate clickability accordingly.Right now, the UI always claims success and allows opening the file. Consider deriving isSuccess from output and adjusting label/clickability. This also avoids opening a non-existent/unchanged file on failure.
Apply this diff:
- case "output-available": - return ( + case "output-available": { + const writeOutput = output as { result?: string; error?: string }; + const isSuccess = + !writeOutput?.error && + /success/i.test(writeOutput?.result ?? ""); // fallback heuristic + return ( <ToolBlock key={toolCallId} - icon={<FilePlus />} - action="Successfully wrote" + icon={<FilePlus />} + action={isSuccess ? "Successfully wrote" : "Failed to write"} target={writeInput.file_path} - isClickable={true} + isClickable={isSuccess} onClick={() => { - openFileInSidebar({ - path: writeInput.file_path, - content: writeInput.contents, - action: "writing", - }); + if (isSuccess) { + openFileInSidebar({ + path: writeInput.file_path, + content: writeInput.contents, + action: "writing", + }); + } }} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); - openFileInSidebar({ - path: writeInput.file_path, - content: writeInput.contents, - action: "writing", - }); + if (isSuccess) { + openFileInSidebar({ + path: writeInput.file_path, + content: writeInput.contents, + action: "writing", + }); + } } }} /> ); + }
175-178: Unused “explanation” input field.You parse explanation but don’t display or otherwise use it. Either surface it (tooltip/expanded details) or drop it from the local input type to avoid confusion.
201-209: Make success detection resilient to casing and minor phrasing changes.String includes with exact casing is brittle. Prefer a case-insensitive regex.
Apply this diff:
- const isSuccess = deleteOutput.result.includes("Successfully deleted"); + const isSuccess = /successfully deleted/i.test(deleteOutput.result ?? "");
10-15: Type the “part” parameter to avoid pervasive casts.part: any forces many unsafe casts. Consider a discriminated union keyed by part.type aligned with lib/ai/tools/types.ts for compile-time safety across input/output shapes and states.
Also applies to: 12-12
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (21)
.env.local.example(1 hunks)README.md(1 hunks)app/api/chat/route.ts(3 hunks)app/components/ComputerSidebar.tsx(1 hunks)app/components/MessagePartHandler.tsx(8 hunks)app/components/Messages.tsx(4 hunks)app/globals.css(1 hunks)app/hooks/useSidebarAutoOpen.ts(1 hunks)app/page.tsx(7 hunks)components/ui/tool-block.tsx(1 hunks)lib/ai/tools/delete-file.ts(1 hunks)lib/ai/tools/execution-types.ts(1 hunks)lib/ai/tools/index.ts(2 hunks)lib/ai/tools/read-file.ts(2 hunks)lib/ai/tools/run-terminal-cmd.ts(2 hunks)lib/ai/tools/search-replace.ts(1 hunks)lib/ai/tools/types.ts(2 hunks)lib/ai/tools/utils/local-file-operations.ts(1 hunks)lib/ai/tools/utils/local-terminal.ts(1 hunks)lib/ai/tools/write-file.ts(2 hunks)lib/system-prompt.ts(4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (11)
lib/ai/tools/types.ts (1)
lib/ai/tools/execution-types.ts (1)
ExecutionMode(1-1)
lib/ai/tools/delete-file.ts (2)
lib/ai/tools/types.ts (1)
ToolContext(15-19)lib/ai/tools/utils/local-file-operations.ts (1)
deleteLocalFile(95-121)
app/hooks/useSidebarAutoOpen.ts (2)
types/chat.ts (1)
SidebarFile(3-12)app/contexts/GlobalState.tsx (1)
useGlobalState(92-98)
lib/ai/tools/search-replace.ts (2)
lib/ai/tools/types.ts (1)
ToolContext(15-19)lib/ai/tools/utils/local-file-operations.ts (1)
searchReplaceLocalFile(123-184)
app/api/chat/route.ts (4)
lib/ai/tools/execution-types.ts (1)
ExecutionMode(1-1)lib/token-utils.ts (1)
truncateMessagesToTokenLimit(24-44)lib/ai/tools/index.ts (1)
createTools(13-50)lib/system-prompt.ts (1)
systemPrompt(9-86)
lib/ai/tools/run-terminal-cmd.ts (2)
lib/ai/tools/types.ts (1)
ToolContext(15-19)lib/ai/tools/utils/local-terminal.ts (2)
createLocalTerminalHandlers(147-170)executeLocalCommand(23-54)
lib/ai/tools/read-file.ts (2)
lib/ai/tools/types.ts (1)
ToolContext(15-19)lib/ai/tools/utils/local-file-operations.ts (1)
readLocalFile(10-57)
app/components/Messages.tsx (1)
app/hooks/useSidebarAutoOpen.ts (1)
useSidebarAutoOpen(72-103)
lib/ai/tools/index.ts (6)
lib/ai/tools/execution-types.ts (1)
ExecutionMode(1-1)lib/ai/tools/run-terminal-cmd.ts (1)
createRunTerminalCmd(11-106)lib/ai/tools/read-file.ts (1)
createReadFile(6-88)lib/ai/tools/write-file.ts (1)
createWriteFile(6-54)lib/ai/tools/delete-file.ts (1)
createDeleteFile(6-70)lib/ai/tools/search-replace.ts (1)
createSearchReplace(6-117)
lib/ai/tools/write-file.ts (2)
lib/ai/tools/types.ts (1)
ToolContext(15-19)lib/ai/tools/utils/local-file-operations.ts (1)
writeLocalFile(59-81)
app/page.tsx (1)
app/components/ComputerSidebar.tsx (1)
ComputerSidebar(13-196)
🪛 LanguageTool
README.md
[grammar] ~36-~36: There might be a mistake here.
Context: ... terminal commands locally (default) or in sandbox. For sandbox mode, you'll need ...
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
README.md
30-30: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
38-38: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 dotenv-linter (3.3.0)
.env.local.example
[warning] 41-41: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🔇 Additional comments (25)
lib/ai/tools/execution-types.ts (1)
1-1: Single source of truth for execution mode looks goodThe union type is clear and concise. This is a good central type to thread through tools and prompts.
.env.local.example (1)
10-18: Good addition of terminal execution settings with sane defaultsClear separation of sections and defaulting to
localmakes onboarding easier. The commentedE2B_API_KEYfor sandbox mode is a nice touch.lib/ai/tools/utils/local-file-operations.ts (3)
157-157: Regex special character escaping is correctThe regex escaping implementation properly handles all special regex characters to prevent unintended pattern matching.
163-165: Good user experience designExcellent error message that provides clear guidance when multiple occurrences are found, helping users understand how to resolve the ambiguity.
33-34: Ignore the off-by-one error suggestion in offset calculationThe implementation correctly treats the caller’s
offsetas a 1-based line number:
lines.slice(offset - 1)converts that 1-based value to a 0-based array index.startLineNumber = offset || 1ensures numbering also starts at the provided offset (or 1 when undefined).No changes are needed here.
Likely an incorrect or invalid review comment.
lib/ai/tools/types.ts (2)
18-18: Good design: ExecutionMode integrationThe addition of
executionModetoToolContextproperly enables tools to adapt their behavior based on the execution environment (local vs sandbox).
7-7: VerifiedsetSandboxImplementation
TheDefaultSandboxManagerinlib/ai/tools/utils/sandbox-manager.ts(lines 39–42) correctly implements the newsetSandbox(sandbox: Sandbox): voidmethod. No further action needed.app/components/ComputerSidebar.tsx (1)
89-89: Responsive design improvement with custom breakpointThe change from
md:todesktop:breakpoints aligns with the new custom responsive variant defined in the PR. This provides more precise control over the layout transition point.app/api/chat/route.ts (1)
56-61: Proper propagation of execution modeThe execution mode is correctly passed through to both the tools creation and system prompt, ensuring consistent behavior across the application.
lib/ai/tools/write-file.ts (1)
21-22: No changes needed: Absolute and relative paths are both supported
Verified via the E2B TypeScript SDK docs thatsandbox.files.read/write/removeaccept both absolute paths (e.g.'/path/to/file') and relative (workspace-/cwd-relative) paths. The current description (“You can use either a relative path in the workspace or an absolute path.”) is accurate.lib/ai/tools/index.ts (1)
32-33: Context wiring looks goodPassing executionMode into ToolContext cleanly threads mode to all tools. No concerns.
lib/ai/tools/search-replace.ts (3)
46-55: Local path and error handling are solidLocal execution correctly delegates to the shared helper and returns a uniform result. Good parity with the sandbox path, consistent error shape.
80-85: Escape of old_string and counting matches are correctThe escape before building the global regex prevents special-char pitfalls. Counting via
match(regex)ensures accurate replacement stats.
85-95: Single-replacement guard is user-friendlyReturning a guidance message when multiple occurrences exist (without replace_all) is a good UX choice to avoid unintended mass edits.
app/page.tsx (3)
43-57: Clean reset mechanism for sidebar auto-openUsing a ref the child fills to reset the auto-open guard is a neat, low-friction pattern. The reset on new submissions is well-timed.
188-191: Mobile overlay gating aligns with desktop breakpointSwitch from md:hidden to desktop:hidden matches the new breakpoint semantics. LGTM.
118-125: Messages correctly wiresresetSidebarAutoOpen
Verified that inapp/components/Messages.tsx, auseEffectruns on mount and wheneverresetSidebarFlag(or the ref itself) changes, assigningresetSidebarOpen.current = resetSidebarFlag. No further changes are needed here.app/hooks/useSidebarAutoOpen.ts (3)
26-51: Good detection of actionable tool outputs (read/write)
- Stripping
^\s*\d+\|handles line-numbered content from sandbox reads without affecting local mode.- Returning an explicit action (“reading”/“writing”) provides nice context for the sidebar.
91-95: Desktop gating matches page/layout behaviorUsing matchMedia for min-width 950px aligns with the new “desktop” breakpoint. Good consistency with the page styles.
37-41: Range semantics are correctThe
read-filetool uses 1-based indexing (it slices atoffset – 1and numbers lines starting from the provided offset), so computingrangeas{ start: offset, end: offset + limit - 1 }accurately reflects the first and last line returned. No changes required.
app/components/MessagePartHandler.tsx (5)
54-57: LGTM on read-file tool icon and interaction updates.Using FileText, shimmer states during streaming, and click/keyboard open-in-sidebar affordances read clearly and are consistent.
Also applies to: 62-67, 100-106
126-129: LGTM on write-file tool visual states.Switch to FilePlus and shimmer during “Creating/Writing to” reads well.
Also applies to: 135-139
218-247: LGTM on search/replace tool states.Shimmering “Editing file”, clear “Editing” vs “Replacing all in” labels, and the FilePen icon all make sense.
332-337: LGTM on wiring new tool types into the main switch.New cases are correctly routed to their renderers.
39-47: All read-file offset semantics and search-replace messaging verified—no changes requiredOffset is confirmed to be 1-based and limit-only reads start at line 1, matching both the tool implementation and the
getFileRangelabels. The search-replace tool indeed emits “Successfully made …”, so the existing logic is correct.
Summary by CodeRabbit
New Features
Improvements
Documentation