The design agent is now drafting the implementation plan covering the…#39
Conversation
… File Writer IPC surface, incremental auto-reindex, file icons, and the bottom-gap fix. I'll review its output against the code and write the final plan when it returns.
There was a problem hiding this comment.
Summary
This PR successfully implements the File Writer component with comprehensive security controls and proper validation. The implementation demonstrates excellent defense-in-depth security practices across multiple layers.
Key Security Strengths
Multi-Layer Validation
- IPC handlers validate all inputs before reaching the File System Manager
- File Writer performs additional defense-in-depth validation
- All paths are constrained: bounded, NUL-free, relative, non-traversing
Path Traversal Protection
- Multiple checks prevent directory escape attempts
- Symlink resolution with containment re-validation prevents redirection attacks
.gitdirectory protection maintains repository integrity
Safe Operations
- Atomic writes using temp files prevent corruption
- Bounded recursive operations prevent DoS
- Prototype pollution protection in options sanitization
- File operations never follow symlinks
Architecture
The implementation follows the documented security model (CLAUDE.md §6) correctly:
- IPC boundary validates and sanitizes all renderer inputs
- File Writer enforces workspace containment
- Operations are atomic, bounded, and safe
Verification Status
Required Actions:
- ✅ All security validations implemented correctly
- ✅ Path containment enforced at multiple layers
- ✅ Atomic operations prevent file corruption
- ✅ DOS protection with bounded operations
Recommended Before Merge:
- Complete the checklist items in the PR description
- Run
npm run lintand build verification - Manual testing of file write operations
- Verify the integrated watcher triggers incremental reindexing correctly
The code quality is excellent with no blocking defects identified. Once the PR checklist is completed and tests pass, this is ready to merge.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
📝 WalkthroughWalkthroughThis PR adds a guarded File Writer feature: a new writer module with symlink/traversal protections, FileSystemManager mutation methods, IPC handlers, preload bindings, a renderer store with error toasts, and a file-tree context menu. It also adds batched watcher events with incremental search indexing, expanded language/icon detection, and restyles the app shell layout. ChangesFile Writer Feature
Estimated code review effort: 4 (Complex) | ~60 minutes Floating App Shell Restyle
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant FileTreeMenu
participant useFileSystemStore
participant PreloadFsApi
participant fsHandlers
participant FileSystemManager
participant Writer as fs/writer
FileTreeMenu->>useFileSystemStore: createFile/rename/remove(workspaceId, path)
useFileSystemStore->>PreloadFsApi: ipcRenderer.invoke(fs channel)
PreloadFsApi->>fsHandlers: fs:writeFile / fs:rename / fs:delete
fsHandlers->>FileSystemManager: writeFile/rename/deleteEntry(...)
FileSystemManager->>Writer: resolveMutationTarget + atomicWrite/rename/delete
Writer-->>FileSystemManager: FileWriteResult
FileSystemManager->>FileSystemManager: record FileHistory, afterMutation()
FileSystemManager-->>fsHandlers: result
fsHandlers-->>PreloadFsApi: result
PreloadFsApi-->>useFileSystemStore: result
useFileSystemStore-->>FileTreeMenu: success/false (toast on failure)
sequenceDiagram
participant WorkspaceWatcher
participant FileSystemManager
participant SearchManager
participant GitManager
WorkspaceWatcher->>FileSystemManager: onChange(WatchBatch: paths, structural)
FileSystemManager->>FileSystemManager: onWatchedChange -> afterMutation
FileSystemManager->>GitManager: refresh git status
FileSystemManager->>SearchManager: scheduleSearchIndex(paths, structural)
alt structural or too many paths
SearchManager->>SearchManager: indexWorkspace (full reindex)
else small path set
SearchManager->>SearchManager: indexFiles (incremental reindex)
end
SearchManager-->>FileSystemManager: notifyChanged()
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/renderer/features/activity/FileTreeMenu.tsx (3)
85-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMenu can render off-screen near viewport edges.
positionedplaces the menu at the rawclientX/clientYwith no clamping againstwindow.innerWidth/innerHeight. Right-clicking near the right or bottom edge of the window can push menu items (especially the wider 224pxw-56edit panel) partially or fully off-screen and unreachable.Consider clamping the coordinates (either with an estimated width/height, or by measuring the rendered menu via
ref+useLayoutEffectand repositioning once mounted).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 85 - 121, The context menu positioning in FileTreeMenu is using raw point.x/point.y, so the menu can render off-screen near the right or bottom viewport edges. Update the positioning logic around the positioned value in FileTreeMenu to clamp the coordinates against window.innerWidth and window.innerHeight, or measure the rendered menu through ref with useLayoutEffect and adjust after mount. Make sure both the edit panel and the normal menu stay fully visible when opened near the viewport boundaries.
116-216: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMenu lacks ARIA menu semantics.
The dropdown and its items are plain
div/buttonelements with norole="menu"/role="menuitem", and no item receives focus when the menu opens (only the rename/create input auto-focuses). Adding basic ARIA roles (and optionally auto-focusing the first item) would improve screen-reader and keyboard support for this new interactive surface.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 116 - 216, The FileTreeMenu dropdown currently renders as plain container/button markup without menu semantics, so update the root menu wrapper and MenuItem in FileTreeMenu to use proper ARIA roles (menu/menuitem) and keyboard-friendly focus behavior. Add the relevant role attributes and ensure an item is focusable or auto-focused when the menu opens, especially for the non-editing actions like Reveal in Explorer, Copy path, and Delete, so the menu is accessible without relying only on the rename/create inputs.
31-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winValidation/path logic lives directly in a presentational component.
isValidNameand the path-composition logic incommit()are domain/business logic embedded in a feature component file. As per path instructions forsrc/renderer/features/**/!(*.test).{ts,tsx}: "Keep renderer components presentational: components should not contain business logic; use feature folders plus stores for domain behavior." MovingisValidName(and ideally the target-path computation) into a shared lib (e.g. alongsidefileIcons.tsx) or intouseFileSystemStorewould keep this component focused on rendering/interaction and make the validation reusable/testable independently.Also applies to: 64-83
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 31 - 36, Move the name validation and target-path construction out of FileTreeMenu into domain logic, since this component currently mixes presentation with business rules. Extract isValidName and the commit() path-composition behavior into a shared helper or useFileSystemStore, keeping FileTreeMenu focused on rendering and user interaction. Use the existing symbols isValidName and commit() as the main entry points when relocating the logic so the component stays thin and the rules become reusable/testable.Source: Path instructions
src/renderer/stores/useFileSystemStore.ts (1)
98-121: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider consolidating the repeated api-guard boilerplate.
createFile,createDir,remove, andrenameall repeat the identicalconst api = fsApi(); if (!api) return false;guard before delegating torunMutation. A small helper (e.g.withApi(fn, label)that resolves the api once and wraps the call) would remove the duplication and reduce the chance of one method drifting from the pattern later.♻️ Example consolidation
+async function guardedMutation( + op: (api: NonNullable<ReturnType<typeof fsApi>>) => Promise<unknown>, + label: string, +): Promise<boolean> { + const api = fsApi(); + if (!api) return false; + return runMutation(() => op(api), label); +} + createFile: async (workspaceId, relPath) => { - const api = fsApi(); - if (!api) return false; - return runMutation(() => api.createFile(workspaceId, relPath), 'Could not create file'); + return guardedMutation((api) => api.createFile(workspaceId, relPath), 'Could not create file'); },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/stores/useFileSystemStore.ts` around lines 98 - 121, The methods createFile, createDir, remove, and rename in useFileSystemStore all repeat the same fsApi() null-check before calling runMutation. Extract that shared guard into a small helper such as withApi(fn, label) or similar, then have each method delegate through it so the api is resolved once and the mutation/error-label pattern stays consistent across these operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLAUDE.md`:
- Around line 200-208: The ASCII diagram in the documentation is still an
unlabeled fenced block, which will keep triggering the markdown lint warning.
Update the fenced block around the diagram in CLAUDE.md to use an explicit
language tag such as text, and keep the diagram content unchanged so the doc
linter recognizes it correctly.
In `@src/main/managers/fs/writer.ts`:
- Around line 56-102: resolveMutationTarget currently blocks literal “.git”
segments but can still allow symlinked paths that resolve into .git after
realpath checks. Update the writer flow in fs/writer.ts so the .git guard is
applied again on the resolved path or resolved ancestor chain inside
resolveMutationTarget before any mutation is returned, and ensure
write/mkdir/delete/rename/copy callers still rely on this helper for
enforcement.
In `@src/renderer/features/activity/FileTreeMenu.tsx`:
- Around line 173-186: The delete action in FileTreeMenu is using the same
MenuItem for both “Delete” and “Confirm delete,” which allows a rapid
double-click to bypass the intended confirmation. Update the delete flow in
FileTreeMenu/MenuItem handling so the first click only arms confirmation for a
short delay or via a distinct confirm affordance, and ensure the actual
remove(workspaceId, node.path, node.type === 'dir') call can only happen after
that deliberate second step; if you keep the current confirm state, add a brief
disable/debounce or separate confirm/cancel UI to prevent same-position
double-click execution.
---
Nitpick comments:
In `@src/renderer/features/activity/FileTreeMenu.tsx`:
- Around line 85-121: The context menu positioning in FileTreeMenu is using raw
point.x/point.y, so the menu can render off-screen near the right or bottom
viewport edges. Update the positioning logic around the positioned value in
FileTreeMenu to clamp the coordinates against window.innerWidth and
window.innerHeight, or measure the rendered menu through ref with
useLayoutEffect and adjust after mount. Make sure both the edit panel and the
normal menu stay fully visible when opened near the viewport boundaries.
- Around line 116-216: The FileTreeMenu dropdown currently renders as plain
container/button markup without menu semantics, so update the root menu wrapper
and MenuItem in FileTreeMenu to use proper ARIA roles (menu/menuitem) and
keyboard-friendly focus behavior. Add the relevant role attributes and ensure an
item is focusable or auto-focused when the menu opens, especially for the
non-editing actions like Reveal in Explorer, Copy path, and Delete, so the menu
is accessible without relying only on the rename/create inputs.
- Around line 31-36: Move the name validation and target-path construction out
of FileTreeMenu into domain logic, since this component currently mixes
presentation with business rules. Extract isValidName and the commit()
path-composition behavior into a shared helper or useFileSystemStore, keeping
FileTreeMenu focused on rendering and user interaction. Use the existing symbols
isValidName and commit() as the main entry points when relocating the logic so
the component stays thin and the rules become reusable/testable.
In `@src/renderer/stores/useFileSystemStore.ts`:
- Around line 98-121: The methods createFile, createDir, remove, and rename in
useFileSystemStore all repeat the same fsApi() null-check before calling
runMutation. Extract that shared guard into a small helper such as withApi(fn,
label) or similar, then have each method delegate through it so the api is
resolved once and the mutation/error-label pattern stays consistent across these
operations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 050e1c61-b8e9-4b8e-a092-8700f1c3a039
📒 Files selected for processing (29)
CLAUDE.mddocs/reference/window-limboo-api.mdsrc/main/ipc/fsHandlers.tssrc/main/managers/FileSystemManager.tssrc/main/managers/fs/watcher.tssrc/main/managers/fs/writer.tssrc/main/managers/search/SearchManager.tssrc/main/managers/search/query.tssrc/main/managers/search/symbols.tssrc/preload/index.tssrc/renderer/app/AppShell.tsxsrc/renderer/components/layout/TitleBar.tsxsrc/renderer/components/ui/ResizeHandle.tsxsrc/renderer/features/activity/ActivityDrawer.tsxsrc/renderer/features/activity/ActivityRail.tsxsrc/renderer/features/activity/FileTree.tsxsrc/renderer/features/activity/FileTreeMenu.tsxsrc/renderer/features/activity/panels.tsxsrc/renderer/features/git/GitPanel.tsxsrc/renderer/features/memory/MemoryPanel.tsxsrc/renderer/features/sessions/SessionsSidebar.tsxsrc/renderer/features/terminal/TerminalPanel.tsxsrc/renderer/features/workspace/CenterWorkspace.tsxsrc/renderer/features/workspace/Composer.tsxsrc/renderer/lib/fileIcons.tsxsrc/renderer/stores/useFileSystemStore.tssrc/shared/constants.tssrc/shared/ipc-channels.tssrc/shared/types.ts
| ``` | ||
| ┌──────────────────────────────────────────────────────────────┐ | ||
| │ TitleBar (frameless, draggable) [search][_][□][x] │ | ||
| ├────────┬──────────────────────────────────────────┬──────────┤ | ||
| │Sessions│ header / conversation / Composer │ drawer │▐ rail | ||
| └────────┴──────────────────────────────────────────┴──────────┘ | ||
| │ TitleBar (root bg, frameless, draggable) [search][_][□][x] │ | ||
| │ ╷ ┌────────────────────────────────────────┐ │ | ||
| │Sessions│ │ header / conversation / Composer │drawer│ ▐ rail │ | ||
| │(root bg)│ │ floating card (bg-surface) │ (root bg)│ | ||
| │ ╵ └────────────────────────────────────────┘ │ | ||
| └──────────────────────────────────────────────────────────────┘ | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Label the ASCII diagram fence.
This is still an unlabeled fenced block, so the markdownlint warning will keep firing. Mark it as text (or another explicit language) to satisfy the doc linter.
Fix
-```
+```text📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| ┌──────────────────────────────────────────────────────────────┐ | |
| │ TitleBar (frameless, draggable) [search][_][□][x] │ | |
| ├────────┬──────────────────────────────────────────┬──────────┤ | |
| │Sessions│ header / conversation / Composer │ drawer │▐ rail | |
| └────────┴──────────────────────────────────────────┴──────────┘ | |
| │ TitleBar (root bg, frameless, draggable) [search][_][□][x] │ | |
| │ ╷ ┌────────────────────────────────────────┐ │ | |
| │Sessions│ │ header / conversation / Composer │drawer│ ▐ rail │ | |
| │(root bg)│ │ floating card (bg-surface) │ (root bg)│ | |
| │ ╵ └────────────────────────────────────────┘ │ | |
| └──────────────────────────────────────────────────────────────┘ | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 200-200: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CLAUDE.md` around lines 200 - 208, The ASCII diagram in the documentation is
still an unlabeled fenced block, which will keep triggering the markdown lint
warning. Update the fenced block around the diagram in CLAUDE.md to use an
explicit language tag such as text, and keep the diagram content unchanged so
the doc linter recognizes it correctly.
Source: Linters/SAST tools
| function resolveMutationTarget(root: string, relPath: string): string { | ||
| const rel = (relPath ?? '').trim(); | ||
| if (!rel) throw new FileWriteError('Refusing to mutate the workspace root.'); | ||
|
|
||
| const segments = rel.split(/[\\/]+/).filter((s) => s.length > 0 && s !== '.'); | ||
| if (segments.length === 0) throw new FileWriteError('Refusing to mutate the workspace root.'); | ||
| if (segments.some((s) => s === '..')) { | ||
| throw new FileWriteError('Path escapes the workspace root.'); | ||
| } | ||
| if (segments.some((s) => s.toLowerCase() === '.git')) { | ||
| throw new FileWriteError('Refusing to touch the .git directory.'); | ||
| } | ||
|
|
||
| const target = path.resolve(root, segments.join(path.sep)); | ||
| if (!isInsideRoot(root, target) || target === path.resolve(root)) { | ||
| throw new FileWriteError('Path escapes the workspace root.'); | ||
| } | ||
|
|
||
| // The target may not exist yet, so realpath the deepest EXISTING ancestor and | ||
| // re-assert containment against the REAL root — a symlinked directory in the | ||
| // chain can never redirect the mutation outside the workspace. | ||
| let realRoot: string; | ||
| try { | ||
| realRoot = fs.realpathSync(root); | ||
| } catch { | ||
| throw new FileWriteError('Workspace root is not accessible.'); | ||
| } | ||
| let probe = target; | ||
| for (;;) { | ||
| let real: string | null = null; | ||
| try { | ||
| real = fs.realpathSync(probe); | ||
| } catch { | ||
| real = null; | ||
| } | ||
| if (real !== null) { | ||
| if (!isInsideRoot(realRoot, real)) { | ||
| throw new FileWriteError('Resolved path escapes the workspace root.'); | ||
| } | ||
| break; | ||
| } | ||
| const parent = path.dirname(probe); | ||
| if (parent === probe) break; // hit the filesystem root — nothing existed | ||
| probe = parent; | ||
| } | ||
| return target; | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the file and locate the relevant helpers.
git ls-files 'src/main/managers/fs/*' 'src/main/managers/**/*' | sed -n '1,200p'
printf '\n--- outline ---\n'
ast-grep outline src/main/managers/fs/writer.ts --view expanded || true
printf '\n--- relevant slices ---\n'
sed -n '1,220p' src/main/managers/fs/writer.ts
printf '\n--- tail ---\n'
sed -n '220,380p' src/main/managers/fs/writer.tsRepository: BotCoder254/limboo
Length of output: 13754
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- src/main/managers/workspace/validate.ts ---'
sed -n '1,220p' src/main/managers/workspace/validate.ts
printf '\n%s\n' '--- search for resolveMutationTarget callers / symlink handling ---'
rg -n "resolveMutationTarget\\(|Refusing to write through a symlink|Refusing to touch the \\.git directory|skip symlinks entirely|lstatSync\\(" src/main/managers/fs/writer.ts src/main/managers -g'*.ts'Repository: BotCoder254/limboo
Length of output: 6318
Block symlinked paths that resolve into .git in resolveMutationTarget
The literal segment check misses parent symlinks: a path can resolve into .git and still pass the current realpath containment check. Re-run the .git guard on the resolved path (or resolved ancestor chain) before any write/mkdir/delete/rename/copy through this helper.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/managers/fs/writer.ts` around lines 56 - 102, resolveMutationTarget
currently blocks literal “.git” segments but can still allow symlinked paths
that resolve into .git after realpath checks. Update the writer flow in
fs/writer.ts so the .git guard is applied again on the resolved path or resolved
ancestor chain inside resolveMutationTarget before any mutation is returned, and
ensure write/mkdir/delete/rename/copy callers still rely on this helper for
enforcement.
| <MenuItem | ||
| icon={Trash2} | ||
| label={confirmDelete ? 'Confirm delete' : 'Delete'} | ||
| danger | ||
| onClick={() => { | ||
| if (!confirmDelete) { | ||
| setConfirmDelete(true); | ||
| return; | ||
| } | ||
| void remove(workspaceId, node.path, node.type === 'dir'); | ||
| onClose(); | ||
| }} | ||
| /> | ||
| </> |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Rapid double-click bypasses the delete confirmation.
The delete button toggles its own label from "Delete" to "Confirm delete" and executes on the very next click of the same button, at the same screen position. React's re-render completes well before a human double-click registers (~200-400ms), so a normal double-click (or two quick clicks — a common habit) on "Delete" will silently execute an irreversible, potentially recursive (node.type === 'dir') deletion instead of requiring a deliberate second decision.
Guard against this with a short arm-delay, a timestamp-based debounce, or a distinct confirm affordance (e.g., a separate "Cancel"/"Confirm" pair, or briefly disabling the button after the first click).
🛡️ Example fix: debounce the confirm click
+ const armedAtRef = useRef(0);
<MenuItem
icon={Trash2}
label={confirmDelete ? 'Confirm delete' : 'Delete'}
danger
onClick={() => {
if (!confirmDelete) {
setConfirmDelete(true);
+ armedAtRef.current = Date.now();
return;
}
+ if (Date.now() - armedAtRef.current < 400) return; // ignore rapid double-click
void remove(workspaceId, node.path, node.type === 'dir');
onClose();
}}
/>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/renderer/features/activity/FileTreeMenu.tsx` around lines 173 - 186, The
delete action in FileTreeMenu is using the same MenuItem for both “Delete” and
“Confirm delete,” which allows a rapid double-click to bypass the intended
confirmation. Update the delete flow in FileTreeMenu/MenuItem handling so the
first click only arms confirmation for a short delay or via a distinct confirm
affordance, and ensure the actual remove(workspaceId, node.path, node.type ===
'dir') call can only happen after that deliberate second step; if you keep the
current confirm state, add a brief disable/debounce or separate confirm/cancel
UI to prevent same-position double-click execution.
… File Writer IPC surface, incremental auto-reindex, file icons, and the bottom-gap fix. I'll review its output against the code and write the final plan when it returns.
Summary
Type of change
Architectural reasoning
Verification
npm run lintpassesnpx vite build --config vite.renderer.config.mtspassesnpm start(for main/preload changes)Security checklist (if touching main process)
shell: true)contextIsolation/sandbox/ CSP / navigation lockdownDocumentation and changelog
docs/CHANGELOG.mdentry (if user-facing)Theme discipline (for UI changes)
dark:variants, no gradients)Note
Medium Risk
New main-process filesystem mutation surface is security-sensitive, though layered IPC and writer guards mitigate traversal/symlink risks; layout and search indexing changes are lower risk.
Overview
Adds a guarded File Writer path in main (
fs/writer.ts) and wires it through newfs:*IPC, preloadwindow.limboo.fsmethods, andFileSystemManagermutations (atomic write, create file/dir, delete, rename/move, copy). IPC validates paths, content size, and rebuilds mutation options from booleans only; the writer enforces workspace containment,realpathsymlink checks,.gitprotection, and size/entry caps.Watcher batches now emit
WatchBatch(changed paths + structural flag). Small batches triggerSearchManager.indexFilesincremental FTS updates; structural/large batches still do a full reindex. Mutations record expanded File History actions and refresh tree/git/search via the same path as external changes.Renderer: floating app shell (title bar + side rails on
bg-base, center + drawer in onebg-surfacecard with gutters and a ghost resize handle), per-language file icons, and a Files tree context menu (FileTreeMenu) backed byuseFileSystemStorecreate/rename/delete with error toasts. Search indexing gains broader language detection and symbol rules (e.g. PowerShell, Lua).Docs updated for the shell layout and
window.limboofs write API.Reviewed by Cursor Bugbot for commit 6baaea8. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation