-
Notifications
You must be signed in to change notification settings - Fork 401
feat(agent): implement fuzzy file search functionality across runtime interfaces #621
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
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds fuzzy file-search end-to-end: new runtime types and APIs, Karton server procedure, Fuse-based implementations with cached indexes for Node and Mock runtimes, client-side chat UI for file mentions (dropdown, chips, icons, hotkey behavior), and related dependency/build updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor ClientUI as Chat UI
participant KartonServer as Karton Server
participant Runtime as ClientRuntime.fileSystem
participant Provider as FileSystemProvider
participant Fuse as Fuse Index
ClientUI->>KartonServer: request fuzzyFileSearch(searchString)
KartonServer->>Runtime: fuzzySearch(searchString)
Runtime->>Provider: fuzzySearch(searchString)
activate Provider
Provider->>Provider: ensureFileIndexBuilt() (TTL, guard)
alt index build needed
Provider->>Provider: buildFileIndex (fast-glob / memfs)
Provider->>Fuse: new Fuse(files, options)
end
Provider->>Fuse: search(searchString)
Fuse-->>Provider: FuseResult<FileData>[]
deactivate Provider
Provider-->>Runtime: FuzzyFileSearchResult
Runtime-->>KartonServer: FuzzyFileSearchResult
KartonServer-->>ClientUI: FuzzyFileSearchResult
sequenceDiagram
autonumber
participant User as User
participant Chat as ChatPanelFooter
participant Dropdown as FileMentionDropdown
participant Server as Karton Server
User->>Chat: types "@" + query
Chat->>Server: fuzzyFileSearch(query) [debounced]
Server-->>Chat: results
Chat->>Dropdown: show results (open)
User->>Dropdown: navigate/select
Dropdown-->>Chat: selected FileData
Chat->>Chat: insert mention token, add selectedFiles, close dropdown
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 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. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 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 (
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
agent/runtime-interface/package.json (1)
24-31: Fix dependency placement: zod should be a runtime dependency; fuse.js can be dev-only (type-only import).
- zod is used at runtime throughout this package; it must be in dependencies (not devDependencies).
- fuse.js is only imported for types (
import type { FuseResult }), so it can be moved to devDependencies to avoid forcing downstream installs.Apply this diff:
"devDependencies": { "rimraf": "^5.0.0", - "typescript": "^5.0.0", - "zod": "4.0.17" + "typescript": "^5.0.0" }, - "dependencies": { - "fuse.js": "^7.1.0" - } + "dependencies": { + "zod": "4.0.17", + "fuse.js": "^7.1.0" + }
🧹 Nitpick comments (12)
agent/runtime-interface/src/file-operations.ts (3)
2-2: Avoid leaking fuse.js types in the public interface (decouple result type).Depending on
FuseResultin the interface locks consumers to fuse.js and makes future swaps harder. Define a small, library-agnostic result type instead.Apply this diff:
-import type { FuseResult } from 'fuse.js'; +// Keep the interface independent of the fuzzy search engine implementation. +// Providers can map their engine-specific results to this shape. + @@ -export type FuzzyFileSearchResult = FuseResult<FileData>[]; +export type FuzzyMatch<T> = { + item: T; + score: number; + // Optional highlights; providers may omit. + matches?: Array<{ + key?: string; + indices: [number, number][]; + value?: string; + arrayIndex?: number; + }>; + // Optional original index in provider’s corpus. + refIndex?: number; +}; +export type FuzzyFileSearchResult = FuzzyMatch<FileData>[];Also applies to: 14-14
4-9: Clarify path semantics in FileData (relative vs absolute).
filepathvsfullpathis ambiguous. Consider renaming for clarity (e.g.,relativePath,absolutePath) or document semantics inline.If you want to rename now, I can generate a follow-up diff across providers and consumers.
11-14: Add minimal input validation and export the inferred input type.Prevent expensive searches on empty queries and expose the inferred type for reuse.
Apply this diff:
export const FuzzyFileSearchInputSchema = z.object({ - searchString: z.string(), + searchString: z.string().min(2, 'Use at least 2 characters for fuzzy search'), }); +export type FuzzyFileSearchInput = z.infer<typeof FuzzyFileSearchInputSchema>;agent/client/src/Agent.ts (1)
295-297: Guard empty queries, cap results, and sanitize file paths in fuzzyFileSearchWe should prevent expensive searches on empty input, limit the number of returned entries, and strip absolute paths (
fullpath) before sending results to clients.• Trim and short-circuit empty queries.
• Cap the result list (e.g. 100 items) to bound payload size.
• Remove or omit thefullpathfield (defined inagent/runtime-interface/src/file-operations.ts:8, and populated inagent/runtime-node/src/index.ts:782andagent/runtime-mock/src/mock-file-system.ts:554) before returning to remote consumers.- fuzzyFileSearch: async (searchString) => { - return this.clientRuntime.fileSystem.fuzzySearch(searchString); - }, + fuzzyFileSearch: async (searchString) => { + const q = String(searchString ?? '').trim(); + if (!q) { + return []; + } + + const results = await this.clientRuntime.fileSystem.fuzzySearch(q); + // Strip absolute paths before exposing to clients + const sanitized = results.map(({ filename, filepath, dirname }) => ({ + filename, + filepath, + dirname, + })); + // Cap to a reasonable size for transport/UI (tune as needed) + return sanitized.slice(0, 100); + },agent/runtime-node/src/index.ts (5)
38-43: Add cache invalidation on file mutationsTTL helps, but writes/moves/deletes can keep the index stale for up to 30s. Invalidate on mutations for fresher results.
// Add near other privates private invalidateFileIndexCache(): void { this.fileSearchCache = null; // allow concurrent callers to rebuild }Apply calls after successful operations (examples):
@@ async writeFile(path: string, content: string) { - await fs.writeFile(fullPath, content, 'utf-8'); + await fs.writeFile(fullPath, content, 'utf-8'); + this.invalidateFileIndexCache(); @@ async deleteFile(path: string) { - await fs.unlink(fullPath); + await fs.unlink(fullPath); + this.invalidateFileIndexCache(); @@ async moveFile(source: string, destination: string) { - await fs.rename(sourcePath, destPath); + await fs.rename(sourcePath, destPath); + this.invalidateFileIndexCache(); @@ async copyFile(source: string, destination: string) { - await fs.copyFile(sourcePath, destPath); + await fs.copyFile(sourcePath, destPath); + this.invalidateFileIndexCache();Optionally make CACHE_TTL configurable via provider config for large repos.
Also applies to: 49-53
712-724: Short-circuit empty query and bound result sizeAvoid expensive broad matches and large payloads.
- async fuzzySearch(searchString: string): Promise<FuzzyFileSearchResult> { + async fuzzySearch(searchString: string): Promise<FuzzyFileSearchResult> { // Build or refresh the file index if needed await this.ensureFileIndexBuilt(); if (!this.fileSearchCache) { // This shouldn't happen after ensureFileIndexBuilt, but handle it gracefully return []; } - // Perform the fuzzy search - const results = this.fileSearchCache.fuse.search(searchString); - return results; + const q = String(searchString ?? '').trim(); + if (!q) return []; + const results = this.fileSearchCache.fuse.search(q); + return results.slice(0, 200); // cap for transport/UI }
781-789: Consider not exposing absolute fullpath (privacy)Indexing with fullpath is fine, but returning it may leak local paths. Prefer returning relative paths only and compute absolute paths inside the provider when needed.
792-810: Fuse options: consider enabling extended search for power usersOptional: useExtendedSearch: true allows ^=, $=, etc., which often helps file search. Threshold 0.4 is reasonable.
821-829: Replace console.error with a shared loggerKeeps logs consistent and testable; also consider debouncing rebuild failures.
agent/runtime-mock/src/mock-file-system.ts (3)
48-52: Add cache invalidation on mutations (mirror node runtime)Same stale-index concern applies here; invalidate on write/move/delete.
private invalidateFileIndexCache(): void { this.fileSearchCache = null; }Call after successful writeFile/deleteFile/moveFile/copyFile operations.
484-496: Short-circuit empty query and cap resultsPrevents broad matches and large payloads in tests/mocks too.
async fuzzySearch(searchString: string): Promise<FuzzyFileSearchResult> { // Build or refresh the file index if needed await this.ensureFileIndexBuilt(); if (!this.fileSearchCache) { // This shouldn't happen after ensureFileIndexBuilt, but handle it gracefully return []; } // Perform the fuzzy search - const results = this.fileSearchCache.fuse.search(searchString); - return results; + const q = String(searchString ?? '').trim(); + if (!q) return []; + const results = this.fileSearchCache.fuse.search(q); + return results.slice(0, 200); }
524-611: Align ignore semantics with node runtime for parityMock uses a simple wildcard matcher; node uses gitignore semantics. Consider reusing the same ignore patterns/logic to keep test behavior aligned.
📜 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 ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
agent/client/src/Agent.ts(1 hunks)agent/runtime-interface/package.json(1 hunks)agent/runtime-interface/src/file-operations.ts(3 hunks)agent/runtime-interface/src/index.ts(1 hunks)agent/runtime-mock/package.json(1 hunks)agent/runtime-mock/src/mock-file-system.ts(5 hunks)agent/runtime-node/package.json(1 hunks)agent/runtime-node/src/index.ts(4 hunks)packages/karton-contract/package.json(1 hunks)packages/karton-contract/src/index.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use any. NEVER use unknown unless absolutely necessary.
Files:
agent/runtime-interface/src/index.tsagent/client/src/Agent.tsagent/runtime-node/src/index.tspackages/karton-contract/src/index.tsagent/runtime-interface/src/file-operations.tsagent/runtime-mock/src/mock-file-system.ts
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/monorepo-guide.mdc)
Local packages can be linked using workspace protocol workspace:* in package.json files.
Use the scope "deps" for dependency updates in package.json or similar files.
Files:
agent/runtime-node/package.jsonagent/runtime-mock/package.jsonpackages/karton-contract/package.jsonagent/runtime-interface/package.json
packages/*/**
📄 CodeRabbit inference engine (.cursor/rules/precommit.mdc)
For changes in packages/*, use the package name as the scope (e.g., changes in packages/core use scope "core").
Files:
packages/karton-contract/package.jsonpackages/karton-contract/src/index.ts
🧠 Learnings (3)
📚 Learning: 2025-08-26T00:53:53.537Z
Learnt from: juliangoetze
PR: stagewise-io/stagewise#608
File: .changeset/public-chairs-bet.md:1-10
Timestamp: 2025-08-26T00:53:53.537Z
Learning: The stagewise/karton-contract package is not versioned and therefore doesn't need entries in changesets, even when its exports are modified.
Applied to files:
packages/karton-contract/package.json
📚 Learning: 2025-08-17T19:03:32.418Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: toolbar/core/CLAUDE.md:0-0
Timestamp: 2025-08-17T19:03:32.418Z
Learning: Build the `stagewise/agent-interface` package before building this package during development
Applied to files:
packages/karton-contract/package.json
📚 Learning: 2025-08-03T19:30:01.868Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: apps/cli/CLAUDE.md:0-0
Timestamp: 2025-08-03T19:30:01.868Z
Learning: Keep workspace packages (`stagewise/*`) external in the ESBuild bundle
Applied to files:
packages/karton-contract/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (8)
agent/runtime-node/package.json (1)
26-27: Deps for fuzzy search look correct (runtime deps).Adding fast-glob and fuse.js under dependencies matches their runtime use in Node provider. No further action from my side.
agent/runtime-mock/package.json (1)
26-26: Mock runtime: fuse.js as a runtime dep is appropriate.This aligns with the mock provider’s in-process fuzzy index.
packages/karton-contract/package.json (1)
22-22: Contract now depends on runtime-interface — confirm type-only usage to avoid runtime coupling.If imports from @stagewise/agent-runtime-interface are type-only, consider keeping them strictly as type imports so the built JS doesn’t require this package at runtime.
Would you like me to scan src/index.ts to verify all imports from agent-runtime-interface are
import type?agent/runtime-interface/src/file-operations.ts (2)
139-145: Specify contract semantics for fuzzySearch in JSDoc.Document whether:
- Search scope is the workspace root.
- .gitignore is respected by default.
- Results are limited (default limit).
- Returned paths are relative or absolute in FileData.
This avoids divergent behavior across providers.
439-439: All BaseFileSystemProvider subclasses already implementfuzzySearchBoth
NodeFileSystemProviderandMockFileSystemProviderdefine anasync fuzzySearch(searchString: string): Promise<FuzzyFileSearchResult>matching the new abstract method inBaseFileSystemProvider:
- agent/runtime-node/src/index.ts:712–714 —
NodeFileSystemProvider.fuzzySearch- agent/runtime-mock/src/mock-file-system.ts:484–486 —
MockFileSystemProvider.fuzzySearchNo further action needed.
packages/karton-contract/src/index.ts (1)
3-3: Dependency & RPC wiring verified
@stagewise/agent-runtime-interfaceis declared under dependencies in packages/karton-contract/package.json (asworkspace:*).- The
fuzzyFileSearchprocedure is exported inpackages/karton-contract/src/index.ts(line 72) and implemented inagent/client/src/Agent.ts(lines 295–297).No further action needed—changes look correctly wired and typed.
agent/runtime-interface/src/index.ts (1)
15-17: Types-only re-exports are correctExporting FileData and FuzzyFileSearchResult as type-only keeps runtime clean. LGTM.
agent/runtime-node/src/index.ts (1)
1-7: ESM imports for fast-glob and fuse look fineNo issues spotted with default/type imports. LGTM.
| import { basename, dirname } from 'node:path'; | ||
| import Fuse, { type IFuseOptions } from 'fuse.js'; | ||
| import { |
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.
🛠️ Refactor suggestion
Use POSIX path helpers with memfs to avoid Windows separator issues
Memfs is POSIX-like; node:path may emit backslashes on Windows. Use node:path/posix for consistency across platforms.
-import { basename, dirname } from 'node:path';
+import { basename, dirname } from 'node:path/posix';📝 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.
| import { basename, dirname } from 'node:path'; | |
| import Fuse, { type IFuseOptions } from 'fuse.js'; | |
| import { | |
| import { basename, dirname } from 'node:path/posix'; | |
| import Fuse, { type IFuseOptions } from 'fuse.js'; | |
| import { |
🤖 Prompt for AI Agents
In agent/runtime-mock/src/mock-file-system.ts around lines 3 to 5, the file
imports basename and dirname from 'node:path', which can produce backslashes on
Windows; replace that import with the POSIX path helpers by importing basename
and dirname from 'node:path/posix' (or import * as posix from 'node:path/posix'
and use posix.basename/posix.dirname) so memfs receives consistent POSIX-style
paths across platforms.
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: 7
🧹 Nitpick comments (11)
agent/runtime-node/build.ts (1)
45-45: Make keepNames dev-friendly (tie to watch).Preserving names helps stack traces during watch; obfuscation belongs to release builds.
- keepNames: false, // Don't preserve function/class names + keepNames: isWatch, // Preserve names in dev; strip in releaseOptional (strongly recommended) to align other flags with watch vs release:
- sourcemap: false, // Disable source maps for security - minify: true, // Always minify for published packages + sourcemap: isWatch ? 'inline' : false, + minify: !isWatch,toolbar/core/src/layouts/default.tsx (1)
218-218: Propagating position into ChatPanel is correct; consider a shared type alias.Define a shared
PanelPositiontype (e.g., insrc/panels/types.ts) and reuse here and in ChatPanel/Footer to avoid inline duplicates.toolbar/core/src/panels/chat/index.tsx (1)
14-21: Avoid repeating inline position type; extract a reusable alias.Create a
PanelPositiontype and import it here and in callers/children to keep signatures consistent.-export function ChatPanel({ - position, -}: { - position?: { - isTopHalf: boolean; - isLeftHalf: boolean; - }; -}) { +import type { PanelPosition } from '@/panels/types'; +export function ChatPanel({ position }: { position?: PanelPosition }) {toolbar/core/src/components/file-mention-chip.tsx (1)
21-49: Minor a11y polish for chip container.Add an accessible label to the chip and a more descriptive title on the remove button.
- <div + <div className={cn( 'flex min-w-fit shrink-0 items-center gap-1 rounded-lg border border-border/20 bg-white/30 px-2 py-1 shadow-sm backdrop-blur-lg transition-all hover:border-border/40 hover:bg-white/80', className, )} title={filepath} + aria-label={`Selected file: ${filename}`} > @@ - <button + <button type="button" onClick={(e) => { e.stopPropagation(); onRemove(); }} className="text-muted-foreground transition-colors hover:text-red-500" - aria-label="Remove file mention" + aria-label={`Remove ${filename}`} + title={`Remove ${filename}`} >toolbar/core/src/components/hotkey-listener.tsx (2)
38-55: ESC prioritization to close the mention dropdown is correct.This matches expected UX and prevents unintended chat-close when the dropdown is open. Consider centralizing a “closeMentionDropdown” action in chat state to also reset related ephemeral state (query, selection) instead of only toggling the boolean, so both hotkey and input handlers use the same cleanup.
66-68: Nit: trim memo dependencies.setIsMentionDropdownOpen is stable and doesn’t need to be in the deps array.
- isMentionDropdownOpen, - setIsMentionDropdownOpen, + isMentionDropdownOpen,toolbar/core/src/components/file-mention-dropdown.tsx (2)
109-121: Expose or remove no-op ref methods.navigateUp/navigateDown are stubs. Either wire them to callbacks (so parents can delegate), or drop them from the ref until needed to avoid a misleading API surface.
export interface FileMentionDropdownRef { - navigateUp: () => void; - navigateDown: () => void; + // Add when needed; currently unused + // navigateUp: () => void; + // navigateDown: () => void; selectCurrent: () => void; } @@ - useImperativeHandle(ref, () => ({ - navigateUp: () => { - // Navigate up logic handled by parent - }, - navigateDown: () => { - // Navigate down logic handled by parent - }, + useImperativeHandle(ref, () => ({ selectCurrent: () => { if (searchResults[selectedIndex]) { onSelect(searchResults[selectedIndex]); } }, }));
180-205: Improve a11y semantics for list and options.Use listbox/option roles with aria-selected and keyboard focusability.
- <div - role="button" + <div + role="option" + aria-selected={displaySelectedIndex === displayIndex} + tabIndex={-1}Optionally set role="listbox" on the scroll container and manage aria-activedescendant.
toolbar/core/src/hooks/use-chat-state.tsx (2)
167-173: Cleanup mention UI on stop.Also close the dropdown when stopping prompt creation to avoid stale UI state on re-entry.
const stopPromptCreation = useCallback(() => { setIsPromptCreationMode(false); // Always stop context selector when stopping prompt creation setIsContextSelectorMode(false); setDomContextElements([]); setSelectedFiles([]); + setIsMentionDropdownOpen(false); plugins.forEach((plugin) => { plugin.onPromptingAbort?.(); }); }, [plugins]);
330-335: Redundant reset.setSelectedFiles([]) is immediately followed by stopPromptCreation(), which also clears selected files. Remove one.
- clearFileAttachments(); - setSelectedFiles([]); - stopPromptCreation(); // This also stops context selector + clearFileAttachments(); + stopPromptCreation(); // This also clears selected files and stops context selectortoolbar/core/src/panels/chat/panel-footer.tsx (1)
223-271: Deduplication should key on filepath only.Filename is redundant; filepath is unique and sufficient.
- const alreadySelected = selectedFiles.some( - (f) => - f.filepath === file.item.filepath && - f.filename === file.item.filename, - ); + const alreadySelected = selectedFiles.some( + (f) => f.filepath === file.item.filepath, + );
📜 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 ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
agent/runtime-interface/package.json(1 hunks)agent/runtime-node/build.ts(1 hunks)packages/karton-contract/src/metadata.ts(1 hunks)toolbar/core/package.json(1 hunks)toolbar/core/src/components/file-mention-chip.tsx(1 hunks)toolbar/core/src/components/file-mention-dropdown.tsx(1 hunks)toolbar/core/src/components/hotkey-listener.tsx(3 hunks)toolbar/core/src/hooks/use-chat-state.tsx(9 hunks)toolbar/core/src/layouts/default.tsx(1 hunks)toolbar/core/src/panels/chat/index.tsx(2 hunks)toolbar/core/src/panels/chat/panel-footer.tsx(8 hunks)toolbar/core/src/utils/file-icons.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- agent/runtime-interface/package.json
🧰 Additional context used
📓 Path-based instructions (8)
packages/*/**
📄 CodeRabbit inference engine (.cursor/rules/precommit.mdc)
For changes in packages/*, use the package name as the scope (e.g., changes in packages/core use scope "core").
Files:
packages/karton-contract/src/metadata.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use any. NEVER use unknown unless absolutely necessary.
Files:
packages/karton-contract/src/metadata.tstoolbar/core/src/layouts/default.tsxagent/runtime-node/build.tstoolbar/core/src/utils/file-icons.tsxtoolbar/core/src/components/file-mention-chip.tsxtoolbar/core/src/panels/chat/index.tsxtoolbar/core/src/components/file-mention-dropdown.tsxtoolbar/core/src/components/hotkey-listener.tsxtoolbar/core/src/panels/chat/panel-footer.tsxtoolbar/core/src/hooks/use-chat-state.tsx
toolbar/*/**
📄 CodeRabbit inference engine (.cursor/rules/precommit.mdc)
For changes in toolbar/*, use the package name as the scope (e.g., changes in toolbar/extension use scope "extension").
Files:
toolbar/core/src/layouts/default.tsxtoolbar/core/package.jsontoolbar/core/src/utils/file-icons.tsxtoolbar/core/src/components/file-mention-chip.tsxtoolbar/core/src/panels/chat/index.tsxtoolbar/core/src/components/file-mention-dropdown.tsxtoolbar/core/src/components/hotkey-listener.tsxtoolbar/core/src/panels/chat/panel-footer.tsxtoolbar/core/src/hooks/use-chat-state.tsx
toolbar/core/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (toolbar/core/CLAUDE.md)
All source code is written in TypeScript
Files:
toolbar/core/src/layouts/default.tsxtoolbar/core/src/utils/file-icons.tsxtoolbar/core/src/components/file-mention-chip.tsxtoolbar/core/src/panels/chat/index.tsxtoolbar/core/src/components/file-mention-dropdown.tsxtoolbar/core/src/components/hotkey-listener.tsxtoolbar/core/src/panels/chat/panel-footer.tsxtoolbar/core/src/hooks/use-chat-state.tsx
toolbar/core/src/layouts/**/*.{ts,tsx}
📄 CodeRabbit inference engine (toolbar/core/CLAUDE.md)
Place main UI layout definitions under
src/layouts
Files:
toolbar/core/src/layouts/default.tsx
**/package.json
📄 CodeRabbit inference engine (.cursor/rules/monorepo-guide.mdc)
Local packages can be linked using workspace protocol workspace:* in package.json files.
Use the scope "deps" for dependency updates in package.json or similar files.
Files:
toolbar/core/package.json
toolbar/core/src/panels/**/*.{ts,tsx}
📄 CodeRabbit inference engine (toolbar/core/CLAUDE.md)
Place panel content (chat, settings, agent connection settings, etc.) under
src/panels
Files:
toolbar/core/src/panels/chat/index.tsxtoolbar/core/src/panels/chat/panel-footer.tsx
toolbar/core/src/hooks/**/*.{ts,tsx}
📄 CodeRabbit inference engine (toolbar/core/CLAUDE.md)
Place application hooks under
src/hooks
Files:
toolbar/core/src/hooks/use-chat-state.tsx
🧠 Learnings (5)
📚 Learning: 2025-08-17T19:03:32.418Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: toolbar/core/CLAUDE.md:0-0
Timestamp: 2025-08-17T19:03:32.418Z
Learning: Applies to toolbar/core/src/panels/**/*.{ts,tsx} : Place panel content (chat, settings, agent connection settings, etc.) under `src/panels`
Applied to files:
toolbar/core/src/layouts/default.tsxtoolbar/core/src/panels/chat/index.tsxtoolbar/core/src/panels/chat/panel-footer.tsx
📚 Learning: 2025-08-03T19:30:01.868Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: apps/cli/CLAUDE.md:0-0
Timestamp: 2025-08-03T19:30:01.868Z
Learning: Keep workspace packages (`stagewise/*`) external in the ESBuild bundle
Applied to files:
agent/runtime-node/build.ts
📚 Learning: 2025-08-03T19:30:01.868Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: apps/cli/CLAUDE.md:0-0
Timestamp: 2025-08-03T19:30:01.868Z
Learning: Bundle most dependencies (e.g., axios, chalk, commander, zod, express, winston) into the output file using ESBuild
Applied to files:
agent/runtime-node/build.ts
📚 Learning: 2025-08-17T19:03:32.418Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: toolbar/core/CLAUDE.md:0-0
Timestamp: 2025-08-17T19:03:32.418Z
Learning: Applies to toolbar/core/src/**/*.{ts,tsx} : All source code is written in TypeScript
Applied to files:
toolbar/core/src/utils/file-icons.tsx
📚 Learning: 2025-07-18T16:48:09.404Z
Learnt from: CR
PR: stagewise-io/stagewise#0
File: .cursor/rules/plugin-creation-guide.mdc:0-0
Timestamp: 2025-07-18T16:48:09.404Z
Learning: Applies to plugins/**/*.ts : Use the `onPromptingAbort` method to perform cleanup or state reset if the user cancels prompting.
Applied to files:
toolbar/core/src/hooks/use-chat-state.tsx
🧬 Code graph analysis (5)
toolbar/core/src/layouts/default.tsx (1)
toolbar/core/src/panels/chat/index.tsx (1)
ChatPanel(14-263)
toolbar/core/src/components/file-mention-chip.tsx (1)
toolbar/core/src/utils/file-icons.tsx (1)
getFileIcon(67-231)
toolbar/core/src/panels/chat/index.tsx (1)
toolbar/core/src/panels/chat/panel-footer.tsx (1)
ChatPanelFooter(33-598)
toolbar/core/src/components/file-mention-dropdown.tsx (1)
toolbar/core/src/utils/file-icons.tsx (1)
getFileIcon(67-231)
toolbar/core/src/panels/chat/panel-footer.tsx (3)
toolbar/core/src/hooks/use-chat-state.tsx (1)
useChatState(384-390)toolbar/core/src/components/file-mention-dropdown.tsx (4)
FuseResult(24-27)FileData(17-22)FileMentionDropdownRef(39-43)FileMentionDropdown(45-225)toolbar/core/src/components/file-mention-chip.tsx (1)
FileMentionChip(12-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (11)
packages/karton-contract/src/metadata.ts (1)
168-178: selectedFiles is propagated into ChatMessage metadata
Verified in packages/toolbar/core/src/hooks/use-chat-state.tsx wheremetadata.selectedFiles = selectedFiles;(lines 259–261) and theChatMessageobject includesselectedFiles.toolbar/core/src/panels/chat/index.tsx (1)
252-256: LGTM: Footer receives position for dropdown orientation.Prop threading looks correct and matches Footer’s expectations.
toolbar/core/src/components/file-mention-chip.tsx (1)
12-20: LGTM: Clear API and correct icon/color usage.Props are minimal, title shows full path, and remove handler stops propagation.
toolbar/core/src/components/hotkey-listener.tsx (1)
18-20: Dropdown state wiring looks correct.No issues with adding isMentionDropdownOpen/setIsMentionDropdownOpen to the chat state consumption.
toolbar/core/src/components/file-mention-dropdown.tsx (1)
64-101: Floating UI config is solid.Good use of offset/flip/shift/size with autoUpdate and dynamic placement based on toolbar position.
toolbar/core/src/hooks/use-chat-state.tsx (3)
53-58: Selected files state: OK.Shape and exposure via context look good.
116-121: State initialization: OK.No issues with selectedFiles/isMentionDropdownOpen defaults.
258-262: No action needed:selectedFilesis defined in the contract.
TheUserMessageMetadataschema (packages/karton-contract/src/metadata.ts) includes an optionalselectedFiles: z.array(…).optional(), andChatMessage['metadata']uses this type.toolbar/core/src/panels/chat/panel-footer.tsx (3)
36-44: Position prop integration looks good.Deriving isToolbarAtBottom from position.isTopHalf is straightforward and keeps layout concerns local.
115-156: Keyboard nav behavior: OK.Arrow and Ctrl+N/P handling matches visual order (top/bottom toolbar). Guarded by isMentionDropdownOpen.
552-562: Dropdown wiring/render: OK.Anchor, isOpen, loading and selection props are correctly forwarded.
| external: [ | ||
| 'vscode', // VSCode extension API should always be external | ||
| // Node.js built-in modules | ||
| 'os', | ||
| 'path', | ||
| 'fs', | ||
| 'util', | ||
| 'stream', | ||
| 'events', | ||
| 'buffer', | ||
| 'crypto', | ||
| 'child_process', | ||
| 'http', | ||
| 'https', | ||
| 'url', | ||
| 'querystring', | ||
| 'assert', | ||
| 'zlib', | ||
| 'tty', | ||
| 'net', | ||
| 'dns', | ||
| // Dependencies to prevent bundling issues | ||
| 'fast-glob', | ||
| 'fuse.js', | ||
| 'ignore', | ||
| 'minimatch', | ||
| '@stagewise/agent-runtime-interface', | ||
| ], |
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.
💡 Verification agent
🧩 Analysis chain
External list: compute Node built-ins programmatically and bundle app deps (avoid over-externalizing).
- Hardcoding built-ins is redundant and misses
node:-prefixed specifiers (e.g.,node:path) used by the new code; esbuild treats Node built-ins as external by default, but if you want explicitness, generate both forms frombuiltinModules. - Per our learning, keep workspace packages external, but bundle most third-party deps; externalizing
fast-glob,fuse.js,ignore,minimatchshifts breakage to runtime and can cause “module not found” unless pinned in deps. The summary only mentionsfast-glob/fuse.jsadded—please verifyignore/minimatchare declared if they’re truly needed externally.
Apply the following changes:
- Add this near the imports (outside the selected range):
import { builtinModules } from 'node:module';
const nodeBuiltins = Array.from(
new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)])
);- Replace the externals block to rely on the generated list and stop externalizing app deps:
- external: [
- 'vscode', // VSCode extension API should always be external
- // Node.js built-in modules
- 'os',
- 'path',
- 'fs',
- 'util',
- 'stream',
- 'events',
- 'buffer',
- 'crypto',
- 'child_process',
- 'http',
- 'https',
- 'url',
- 'querystring',
- 'assert',
- 'zlib',
- 'tty',
- 'net',
- 'dns',
- // Dependencies to prevent bundling issues
- 'fast-glob',
- 'fuse.js',
- 'ignore',
- 'minimatch',
- '@stagewise/agent-runtime-interface',
- ],
+ external: [
+ 'vscode',
+ ...nodeBuiltins,
+ '@stagewise/agent-runtime-interface',
+ ],If you must keep any third-party lib external due to a demonstrated bundling issue, document the reason in a code comment and ensure it’s listed in dependencies.
Run to verify imports and deps:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Do we use node: built-ins?
rg -nP --type=ts 'from\s+["'\'']node:' | sed 's/^/node-prefix import: /'
# 2) Are externals declared as runtime-node deps?
RNPKG="$(fd -p package.json -t f agent/runtime-node | head -n1)"
echo "Using package.json: $RNPKG"
jq -r '.dependencies // {} | keys[]' "$RNPKG" | sort > /tmp/deps.txt
printf "%s\n" fast-glob fuse.js ignore minimatch | sort > /tmp/need.txt
echo "Missing externals in dependencies (if you keep them external):"
comm -13 /tmp/deps.txt /tmp/need.txt || trueLength of output: 11152
Generate Node built-ins dynamically and bundle third-party modules
Replace the hardcoded built-in list in agent/runtime-node/build.ts with a programmatic builtinModules import (including node: prefixes) and only externalize VSCode API and the workspace package; bundle everything else (removing fast-glob, fuse.js, ignore, minimatch).
--- a/agent/runtime-node/build.ts
+++ b/agent/runtime-node/build.ts
@@ –1,4 +1,9 @@
-import { build } from 'esbuild';
+import { build } from 'esbuild';
+import { builtinModules } from 'node:module';
+
+const nodeBuiltins = Array.from(
+ new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)])
+);
export async function createBundle() {
await build({
@@ –13,18 +18,7 @@ export async function createBundle() {
// keep other options...
- external: [
- 'vscode',
- 'os','path','fs','util','stream','events','buffer','crypto',
- 'child_process','http','https','url','querystring','assert',
- 'zlib','tty','net','dns',
- 'fast-glob','fuse.js','ignore','minimatch',
- '@stagewise/agent-runtime-interface',
- ],
+ external: ['vscode', ...nodeBuiltins, '@stagewise/agent-runtime-interface'],
});
}📝 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.
| external: [ | |
| 'vscode', // VSCode extension API should always be external | |
| // Node.js built-in modules | |
| 'os', | |
| 'path', | |
| 'fs', | |
| 'util', | |
| 'stream', | |
| 'events', | |
| 'buffer', | |
| 'crypto', | |
| 'child_process', | |
| 'http', | |
| 'https', | |
| 'url', | |
| 'querystring', | |
| 'assert', | |
| 'zlib', | |
| 'tty', | |
| 'net', | |
| 'dns', | |
| // Dependencies to prevent bundling issues | |
| 'fast-glob', | |
| 'fuse.js', | |
| 'ignore', | |
| 'minimatch', | |
| '@stagewise/agent-runtime-interface', | |
| ], | |
| import { build } from 'esbuild'; | |
| import { builtinModules } from 'node:module'; | |
| const nodeBuiltins = Array.from( | |
| new Set([...builtinModules, ...builtinModules.map((m) => `node:${m}`)]) | |
| ); | |
| export async function createBundle() { | |
| await build({ | |
| // keep other options... | |
| external: ['vscode', ...nodeBuiltins, '@stagewise/agent-runtime-interface'], | |
| }); | |
| } |
🤖 Prompt for AI Agents
In agent/runtime-node/build.ts around lines 13 to 40, replace the hardcoded list
of Node built-ins and third-party externals with a programmatic approach: import
builtinModules from 'module' (i.e., const { builtinModules } = require('module')
or ES import) and construct the external array to include only 'vscode' and the
workspace package '@stagewise/agent-runtime-interface' plus all Node built-ins
both with and without the 'node:' prefix (deduplicated); remove 'fast-glob',
'fuse.js', 'ignore', and 'minimatch' from externals so they are bundled, and set
the rollup/webpack external option to that generated array.
| }, | ||
| "dependencies": { | ||
| "@floating-ui/react": "^0.27.16", | ||
| "react-icons": "^5.5.0" | ||
| } |
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.
💡 Verification agent
🧩 Analysis chain
Add deps scope and verify React 19 compatibility before merging.
- Follow guideline: use "deps" scope for dependency updates. Suggest commit message like:
deps(core): add @floating-ui/react and react-icons. - Potential bundle impact: we now ship both lucide-react and react-icons. If file-icons can use a single icon source, consider consolidating to reduce size.
To confirm compatibility and avoid surprises with React 19.1.0, please run these web checks:
Optionally, inspect how many icons we import to estimate bundle cost:
🏁 Script executed:
#!/bin/bash
# Count react-icons imports and sites
rg -nP "from 'react-icons/" -C2 toolbar/core/src | wc -l
rg -nP "from 'react-icons/" -C2 toolbar/core/srcLength of output: 406
🌐 Web query:
Do @floating-ui/react v0.27.x and react-icons v5.5.0 officially support React 19? What are their peerDependencies?
💡 Result:
Short answer: Yes — both are compatible with React 19 based on their published package metadata.
Details & peerDependencies:
- @floating-ui/react v0.27.x — peerDependencies: "react": ">=17.0.0", "react-dom": ">=17.0.0" (so React 19 satisfies this). [1].
- react-icons v5.5.0 — the package states it "Requires React 16.3 or higher" and does not restrict to a specific major (effectively compatible with React 19); community/package snapshots show [email protected] listing react as peerDependency "*" in some registries. [2][3].
Sources:
- @floating-ui/react package.json (v0.27.5) — peerDependencies: react >=17.0.0, react-dom >=17.0.0. [https://unpkg.com/@floating-ui/[email protected]/files/package.json] [1]
- react-icons v5.5.0 npm page — "Requires React 16.3 or higher." (package metadata). [https://www.npmjs.com/package/react-icons/v/5.5.0] [2]
- Example registry/package-lock snapshot showing [email protected] peerDependencies: { "react": "*" }. [3]
If you want, I can fetch the exact package.json file for [email protected] (registry/unpkg) and paste the peerDependencies line.
Use deps scope for this commit
Commit message should be
deps(core): add @floating-ui/react and react-icons
Both packages officially support React 19 (peerDependencies ≥17.0.0 and ≥16.3.0).
Optional: consolidate icon sources—toolbar/core/src/utils/file-icons.tsx currently imports 5 icons from react-icons—to reduce bundle size.
🤖 Prompt for AI Agents
In toolbar/core/package.json around lines 87-91, the commit message for adding
the two deps needs to use the deps scope; amend the change so the commit message
is exactly "deps(core): add @floating-ui/react and react-icons" (use git commit
--amend -m "...", or create a new commit with that message), ensure the two
packages remain in dependencies as shown, and optionally reduce bundle size by
consolidating react-icons imports in toolbar/core/src/utils/file-icons.tsx
(import only the used icons or replace multiple imports with a single
specific-subpackage import) to minimize footprint.
| const mentionDropdownRef = useRef<FileMentionDropdownRef>(null); | ||
| const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null); | ||
| const fuzzyFileSearch = useKartonProcedure((p) => p.fuzzyFileSearch); | ||
|
|
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.
🛠️ Refactor suggestion
Fix timer ref type for browser/TS compatibility.
Prefer ReturnType to avoid NodeJS.Timeout mismatches in DOM builds.
- const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
+ const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);📝 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.
| const mentionDropdownRef = useRef<FileMentionDropdownRef>(null); | |
| const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null); | |
| const fuzzyFileSearch = useKartonProcedure((p) => p.fuzzyFileSearch); | |
| const mentionDropdownRef = useRef<FileMentionDropdownRef>(null); | |
| const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); | |
| const fuzzyFileSearch = useKartonProcedure((p) => p.fuzzyFileSearch); |
🤖 Prompt for AI Agents
In toolbar/core/src/panels/chat/panel-footer.tsx around lines 81 to 84, the
searchTimeoutRef is typed as NodeJS.Timeout | null which can cause type
mismatches in browser/DOM builds; change its type to ReturnType<typeof
setTimeout> | null (i.e. const searchTimeoutRef = useRef<ReturnType<typeof
setTimeout> | null>(null)) so the timeout works across Node and browser
environments without NodeJS types.
| const currentMentions = extractFileMentions(value); | ||
| const updatedSelectedFiles = selectedFiles.filter((file) => | ||
| currentMentions.some((mention) => mention.startsWith(file.filepath)), | ||
| ); | ||
|
|
||
| // Only update if there's a difference | ||
| if (updatedSelectedFiles.length !== selectedFiles.length) { | ||
| setSelectedFiles(updatedSelectedFiles); | ||
| } |
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.
🛠️ Refactor suggestion
Tighten syncing logic: use exact match over startsWith.
Using startsWith can incorrectly retain chips when a different, longer path is mentioned. Exact equality is safer.
- const updatedSelectedFiles = selectedFiles.filter((file) =>
- currentMentions.some((mention) => mention.startsWith(file.filepath)),
- );
+ const updatedSelectedFiles = selectedFiles.filter((file) =>
+ currentMentions.some((mention) => mention === file.filepath),
+ );📝 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.
| const currentMentions = extractFileMentions(value); | |
| const updatedSelectedFiles = selectedFiles.filter((file) => | |
| currentMentions.some((mention) => mention.startsWith(file.filepath)), | |
| ); | |
| // Only update if there's a difference | |
| if (updatedSelectedFiles.length !== selectedFiles.length) { | |
| setSelectedFiles(updatedSelectedFiles); | |
| } | |
| const currentMentions = extractFileMentions(value); | |
| - const updatedSelectedFiles = selectedFiles.filter((file) => | |
| - currentMentions.some((mention) => mention.startsWith(file.filepath)), | |
| const updatedSelectedFiles = selectedFiles.filter((file) => | |
| currentMentions.some((mention) => mention === file.filepath), | |
| ); | |
| // Only update if there's a difference | |
| if (updatedSelectedFiles.length !== selectedFiles.length) { | |
| setSelectedFiles(updatedSelectedFiles); | |
| } |
🤖 Prompt for AI Agents
In toolbar/core/src/panels/chat/panel-footer.tsx around lines 296 to 304, the
sync logic currently uses startsWith to match mentions to selectedFiles which
can falsely preserve files when a longer path is mentioned; change the predicate
to use exact equality (compare mention === file.filepath) when building
updatedSelectedFiles so only exact path matches are kept, then keep the existing
conditional that calls setSelectedFiles only if the length differs.
| const performFuzzySearch = useCallback( | ||
| async (query: string) => { | ||
| if (!query || query.length === 0) { | ||
| setMentionSearchResults([]); | ||
| return; | ||
| } | ||
|
|
||
| setIsSearchingFiles(true); | ||
| try { | ||
| const results = await fuzzyFileSearch(query); | ||
| // Limit to top 10 results | ||
| setMentionSearchResults(results.slice(0, 10)); | ||
| } catch (error) { | ||
| console.error('Failed to search files:', error); | ||
| setMentionSearchResults([]); | ||
| } finally { | ||
| setIsSearchingFiles(false); | ||
| } | ||
| }, | ||
| [fuzzyFileSearch], | ||
| ); |
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.
🛠️ Refactor suggestion
Prevent stale async results from overwriting newer searches.
Race-condition: multiple in-flight fuzzy searches can resolve out of order. Gate state updates with a monotonically increasing request id.
- const performFuzzySearch = useCallback(
+ const latestSearchIdRef = useRef(0);
+
+ const performFuzzySearch = useCallback(
async (query: string) => {
if (!query || query.length === 0) {
setMentionSearchResults([]);
return;
}
setIsSearchingFiles(true);
try {
- const results = await fuzzyFileSearch(query);
+ const reqId = ++latestSearchIdRef.current;
+ const results = await fuzzyFileSearch(query);
+ if (reqId !== latestSearchIdRef.current) {
+ // A newer search completed; ignore these stale results
+ return;
+ }
// Limit to top 10 results
setMentionSearchResults(results.slice(0, 10));
} catch (error) {
console.error('Failed to search files:', error);
setMentionSearchResults([]);
} finally {
setIsSearchingFiles(false);
}
},
[fuzzyFileSearch],
);📝 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.
| const performFuzzySearch = useCallback( | |
| async (query: string) => { | |
| if (!query || query.length === 0) { | |
| setMentionSearchResults([]); | |
| return; | |
| } | |
| setIsSearchingFiles(true); | |
| try { | |
| const results = await fuzzyFileSearch(query); | |
| // Limit to top 10 results | |
| setMentionSearchResults(results.slice(0, 10)); | |
| } catch (error) { | |
| console.error('Failed to search files:', error); | |
| setMentionSearchResults([]); | |
| } finally { | |
| setIsSearchingFiles(false); | |
| } | |
| }, | |
| [fuzzyFileSearch], | |
| ); | |
| // Prevent stale async results from overwriting newer searches | |
| const latestSearchIdRef = useRef(0); | |
| const performFuzzySearch = useCallback( | |
| async (query: string) => { | |
| if (!query || query.length === 0) { | |
| setMentionSearchResults([]); | |
| return; | |
| } | |
| setIsSearchingFiles(true); | |
| try { | |
| const reqId = ++latestSearchIdRef.current; | |
| const results = await fuzzyFileSearch(query); | |
| if (reqId !== latestSearchIdRef.current) { | |
| // A newer search completed; ignore these stale results | |
| return; | |
| } | |
| // Limit to top 10 results | |
| setMentionSearchResults(results.slice(0, 10)); | |
| } catch (error) { | |
| console.error('Failed to search files:', error); | |
| setMentionSearchResults([]); | |
| } finally { | |
| setIsSearchingFiles(false); | |
| } | |
| }, | |
| [fuzzyFileSearch], | |
| ); |
🤖 Prompt for AI Agents
In toolbar/core/src/panels/chat/panel-footer.tsx around lines 378 to 398,
asynchronous fuzzyFileSearch calls can resolve out of order and overwrite newer
results; add a monotonically increasing request id (useRef counter) that you
increment for each call, capture the current id in the closure before awaiting,
and only call setMentionSearchResults and setIsSearchingFiles if the captured id
matches the latest ref value (thus ignoring stale responses); ensure the ref is
not included in the hook dependency array and that the guard is applied in both
success, catch and finally blocks so stale errors or loading flags cannot
overwrite newer state.
| SiSharp, | ||
| SiPhp, |
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.
Invalid icon import: SiSharp doesn’t exist in react-icons/si. Use SiCsharp.
This will fail type-check and at runtime. Swap the import and the usage.
- SiSharp,
+ SiCsharp,
@@
- cs: { Icon: SiSharp, color: '#239120' },
+ cs: { Icon: SiCsharp, color: '#239120' },Also applies to: 176-177
🤖 Prompt for AI Agents
In toolbar/core/src/utils/file-icons.tsx around lines 21-22 (and again around
176-177) the icon import and usage reference SiSharp which does not exist in
react-icons/si; replace the import name with SiCsharp and update any
corresponding JSX usages to use SiCsharp instead of SiSharp, ensuring the import
statement and all occurrences across the file are renamed consistently so the
type-check and runtime import resolve correctly.
| export interface FileIconData { | ||
| Icon: ComponentType<{ className?: string; style?: React.CSSProperties }>; | ||
| color?: string; | ||
| } |
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.
Type error: React.CSSProperties is referenced without importing React types.
Import CSSProperties from react and use it directly to avoid “Cannot find name 'React'” in TS.
-import type { ComponentType } from 'react';
+import type { ComponentType, CSSProperties } from 'react';
@@
export interface FileIconData {
- Icon: ComponentType<{ className?: string; style?: React.CSSProperties }>;
+ Icon: ComponentType<{ className?: string; style?: CSSProperties }>;
color?: string;
}📝 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.
| export interface FileIconData { | |
| Icon: ComponentType<{ className?: string; style?: React.CSSProperties }>; | |
| color?: string; | |
| } | |
| import type { ComponentType, CSSProperties } from 'react'; | |
| export interface FileIconData { | |
| Icon: ComponentType<{ className?: string; style?: CSSProperties }>; | |
| color?: string; | |
| } |
🤖 Prompt for AI Agents
In toolbar/core/src/utils/file-icons.tsx around lines 57 to 60, the interface
references React.CSSProperties but React types aren't imported; import the
needed types from 'react' (e.g. ComponentType and CSSProperties) at the top of
the file and change the interface to use CSSProperties directly so the type
resolves (ensure the import line includes ComponentType and CSSProperties).
Summary by CodeRabbit
New Features
UX
Performance
Chores