refactor(cue): full SRP decomposition — engine modules, renderer components, shared types#599
refactor(cue): full SRP decomposition — engine modules, renderer components, shared types#599
Conversation
… factory, useCue error state, engine start hardening - Consolidate CUE_TEAL/#06b6d4 to shared CUE_COLOR constant in cue-pipeline-types.ts; remove 3 independent declarations, replace inline hex in NodeConfigPanel - Extract createCueEvent(type, triggerName, payload) factory in cue-types.ts; convert 12 of 14 event creation sites across 8 files, remove redundant crypto imports - Add error state to useCue hook with proper catch block; surface errors in CueModal dashboard via dismissible banner with Retry button - Harden cue-engine.start(): move enabled=true after successful DB init, return early on failure instead of continuing in broken state - 18 new tests: 8 createCueEvent factory, 5 useCue error state, 5 engine start hardening - Fix cue-concurrency and cue-sleep-wake test isolation (add cue-db mocks, mockReset)
*** NodeConfigPanel (850→249 lines, −71%) - Extract TriggerConfig switch to panels/triggers/TriggerConfig.tsx (247 lines) - Extract AgentConfigPanel.tsx (291 lines) — prompt editing, multi-trigger modes - Extract EdgePromptRow.tsx (78 lines) — per-edge prompt with debounce - Shared triggerConfigStyles.ts for input/label/select styles - Shell retains: header chrome, type routing, expand/collapse, delete *** CueModal (1,029→490 lines, −52%) - Create CueModal/ directory with barrel index.ts - Extract SessionsTable.tsx (167 lines) — sessions table with pipeline dots - Extract ActiveRunsList.tsx (81 lines) — running tasks with stop controls - Extract ActivityLog.tsx (128 lines) — expandable history with load-more - Extract ActivityLogDetail.tsx (137 lines) — execution detail view - Extract StatusDot.tsx (17 lines) — StatusDot + PipelineDot micro-components - Extract cueModalUtils.ts (70 lines) — formatting + pipeline mapping helpers *** CueYamlEditor (691→284 lines, −59%) - Create CueYamlEditor/ directory with barrel index.ts - Extract PatternPicker.tsx (42 lines) — pattern preset grid - Extract PatternPreviewModal.tsx (81 lines) — pattern detail with copy - Extract CueAiChat.tsx (114 lines) — chat history + input + streaming - Extract YamlTextEditor.tsx (107 lines) — textarea with line numbers + validation - Extract useCueAiChat hook (197 lines) — agent spawning, streaming, cleanup - Extract CUE_YAML_TEMPLATE to constants/cueYamlDefaults.ts Cross-cutting consolidation (CC-5 through CC-9): - CC-5: Consolidate IncomingTriggerEdgeInfo to shared/cue-pipeline-types.ts - CC-6: Consolidate SessionInfo → CuePipelineSessionInfo in shared types - CC-7: Extract cueEventConstants.ts — EVENT_ICONS/LABELS/COLORS single source - CC-8: Replace 5 hardcoded #06b6d4 with CUE_COLOR in Cue-specific contexts - CC-9: Extract CUE_YAML_TEMPLATE to constants/cueYamlDefaults.ts All barrel exports preserved for backwards compatibility. Type check: 0 errors. Tests: 593 files, 23,649 passed, 0 failures.
📝 WalkthroughWalkthroughCentralizes Cue event creation via a new createCueEvent factory, replaces inline event construction across Cue modules, refactors large Cue UI components into focused submodules, introduces shared cue-pipeline types/constants, improves DB initialization error handling in the engine, and adds error state handling and AI-chat support in renderer hooks/components. Changes
Sequence Diagram(s)sequenceDiagram
participant Module as Cue Module
participant Factory as createCueEvent
participant Crypto as crypto
participant Time as Date
participant Consumer as Event Consumer / Dispatcher
Module->>Factory: request createCueEvent(type, triggerName, payload)
activate Factory
Factory->>Crypto: crypto.randomUUID()
Crypto-->>Factory: uuid
Factory->>Time: new Date().toISOString()
Time-->>Factory: isoTimestamp
Factory-->>Module: CueEvent { id, timestamp, type, triggerName, payload }
deactivate Factory
Module->>Consumer: onEvent(event) / dispatch(event)
Consumer-->>Consumer: handle/store/process event
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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 |
Greptile SummaryThis PR decomposes three large monolithic Cue files ( Key changes:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
subgraph MainProcess["Main Process"]
CT["cue-types.ts\n(createCueEvent factory)"]
CE["cue-engine.ts\n(start hardening)"]
CF["cue-file-watcher.ts"]
CG["cue-github-poller.ts"]
CS["cue-subscription-setup.ts"]
CR["cue-reconciler.ts"]
CTS["cue-task-scanner.ts"]
CFI["cue-fan-in-tracker.ts"]
CT --> CF
CT --> CG
CT --> CS
CT --> CR
CT --> CTS
CT --> CFI
CT --> CE
end
subgraph Shared["shared/cue-pipeline-types.ts"]
CUE_COLOR["CUE_COLOR constant"]
CPSI["CuePipelineSessionInfo"]
ITEI["IncomingTriggerEdgeInfo"]
end
subgraph RendererHooks["Renderer Hooks"]
UC["useCue.ts\n(+ error state)"]
UPST["usePipelineState.ts"]
UPSL["usePipelineSelection.ts"]
UPLA["usePipelineLayout.ts"]
UCAI["useCueAiChat.ts"]
CPSI --> UPST
CPSI --> UPLA
CUE_COLOR --> UPSL
ITEI --> UPSL
end
subgraph CueModalDir["CueModal/"]
CM["CueModal.tsx"]
AT["ActivityLog.tsx"]
AD["ActivityLogDetail.tsx"]
ARL["ActiveRunsList.tsx"]
ST["SessionsTable.tsx"]
CMU["cueModalUtils.ts"]
UC --> CM
CM --> AT
CM --> ARL
CM --> ST
AT --> AD
end
subgraph CueYamlDir["CueYamlEditor/"]
CYE["CueYamlEditor.tsx"]
CAC["CueAiChat.tsx"]
YTE["YamlTextEditor.tsx"]
PP["PatternPicker.tsx"]
PPM["PatternPreviewModal.tsx"]
UCAI --> CYE
CYE --> CAC
CYE --> YTE
CYE --> PP
CYE --> PPM
end
Shared --> CueModalDir
Shared --> CueYamlDir
|
| export type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; | ||
| import type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; |
There was a problem hiding this comment.
Duplicate export-then-import of the same identifier
Both lines reference the same alias SessionInfo from the same source module. The export type re-exports the type for downstream consumers, but the immediately following import type is required for internal use within this file. Having export type appear before its import type is non-standard and confusing — TypeScript resolves both, but some bundlers and tooling can misread the order. The conventional pattern is to import first, then re-export:
| export type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; | |
| import type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; | |
| import type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; | |
| export type { CuePipelineSessionInfo as SessionInfo } from '../../../shared/cue-pipeline-types'; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
|
||
| interface AgentConfigPanelProps { | ||
| node: PipelineNode; | ||
| pipelines: CuePipeline[]; |
There was a problem hiding this comment.
Indirect import of
IncomingTriggerEdgeInfo through a re-exporting sibling
AgentConfigPanel imports IncomingTriggerEdgeInfo from ./NodeConfigPanel, which in turn merely re-exports it from ../../../../shared/cue-pipeline-types. This creates an unnecessary indirection: AgentConfigPanel → NodeConfigPanel → shared/cue-pipeline-types. The same pattern exists in EdgePromptRow.tsx (line 12). Both files should import directly from the shared package:
| pipelines: CuePipeline[]; | |
| import type { IncomingTriggerEdgeInfo } from '../../../../shared/cue-pipeline-types'; |
EdgePromptRow.tsx (line 12) has the same issue and should apply the same fix.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| : text; | ||
|
|
||
| try { | ||
| const spawnConfig = await buildSpawnConfigForAgent({ |
There was a problem hiding this comment.
AI prompt hardcodes
.maestro/cue.yaml — may not match the actual file path
The path given to the AI agent is assembled as ${projectRoot}/.maestro/cue.yaml, while the main-process constant is CUE_YAML_FILENAME = 'maestro-cue.yaml' (a file placed directly in the project root, not in a .maestro/ subdirectory). If these are the same config file, the AI is being told the wrong path and will fail to read or edit the YAML it is supposed to modify. If they are different files, this is still a hardcoded string that could drift. It should reference the same path helper that readYaml/writeYaml IPC calls resolve to, or at minimum be derived from a shared constant.
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (11)
src/renderer/components/CueYamlEditor/PatternPreviewModal.tsx (1)
22-26: Potential state update after unmount.The
setTimeoutinhandleCopycould trigger a state update after the component unmounts if the user closes the modal within 2 seconds of copying. Consider cleaning up the timeout.🔧 Proposed fix using useRef for cleanup
-import { useState, useCallback } from 'react'; +import { useState, useCallback, useRef, useEffect } from 'react'; import { Copy, Check } from 'lucide-react'; // ... other imports ... export function PatternPreviewModal({ pattern, theme, onClose }: PatternPreviewModalProps) { const [copied, setCopied] = useState(false); + const timeoutRef = useRef<ReturnType<typeof setTimeout>>(); + + useEffect(() => { + return () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }; + }, []); const handleCopy = useCallback(async () => { await navigator.clipboard.writeText(pattern.yaml); setCopied(true); - setTimeout(() => setCopied(false), 2000); + timeoutRef.current = setTimeout(() => setCopied(false), 2000); }, [pattern.yaml]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueYamlEditor/PatternPreviewModal.tsx` around lines 22 - 26, The setTimeout in handleCopy (which writes pattern.yaml to clipboard and calls setCopied) can call setCopied after the modal unmounts; store the timer id in a ref (e.g., copyTimeoutRef), assign the timeout return value to it inside handleCopy, and add a useEffect cleanup that clears the timeout (clearTimeout(copyTimeoutRef.current)) on unmount to prevent state updates after unmount; ensure handleCopy and the cleanup reference the same ref and that the ref is cleared/reset after the timeout completes.src/renderer/components/CuePipelineEditor/panels/triggers/TriggerConfig.tsx (1)
31-39: Type-unsafe callback pattern withunknown[]args.The
useDebouncedCallbackusage castsargs[0]which bypasses type safety. If the hook's API allows typed parameters, consider using them. However, this pattern is functional and isolated to this component.💡 Alternative: Inline debounced functions
If
useDebouncedCallbacksupports typed callbacks, consider:- const { debouncedCallback: debouncedUpdate } = useDebouncedCallback((...args: unknown[]) => { - const config = args[0] as TriggerNodeData['config']; - onUpdateNode(node.id, { config } as Partial<TriggerNodeData>); - }, 300); + const { debouncedCallback: debouncedUpdate } = useDebouncedCallback( + (config: TriggerNodeData['config']) => { + onUpdateNode(node.id, { config }); + }, + 300 + );This would require verifying
useDebouncedCallback's type signature supports this pattern.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CuePipelineEditor/panels/triggers/TriggerConfig.tsx` around lines 31 - 39, The callbacks passed to useDebouncedCallback are using an unsafe unknown[] signature and then casting args[0]; update debouncedUpdate and debouncedUpdateLabel to use a typed callback parameter (e.g., provide a typed function signature or generic to useDebouncedCallback) so you directly accept (config: TriggerNodeData['config']) and (customLabel?: string) respectively, then call onUpdateNode(node.id, { config }) and onUpdateNode(node.id, { customLabel }) without casts; reference the debouncedUpdate, debouncedUpdateLabel, useDebouncedCallback, onUpdateNode and TriggerNodeData symbols when making the change.src/renderer/components/CueYamlEditor/YamlTextEditor.tsx (1)
25-25: Unused ref declaration.
yamlTextareaRefis declared but never used. The Tab key handler accesses the textarea viae.currentTargetinstead. Consider removing this unused ref.🔧 Proposed fix
-import { useCallback, useRef } from 'react'; +import { useCallback } from 'react'; import type { Theme } from '../../types'; // ... props interface ... export function YamlTextEditor({ // ... destructured props ... }: YamlTextEditorProps) { - const yamlTextareaRef = useRef<HTMLTextAreaElement>(null); // Handle Tab key in textarea for indentationAnd remove the
refattribute from the textarea at line 79:<textarea - ref={yamlTextareaRef} value={yamlContent}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueYamlEditor/YamlTextEditor.tsx` at line 25, Remove the unused ref declaration yamlTextareaRef from YamlTextEditor and delete the ref attribute on the textarea element; the Tab key handler already uses e.currentTarget to access the textarea, so delete the const yamlTextareaRef = useRef<HTMLTextAreaElement>(null) and the corresponding ref usage to avoid dead code and unused imports.src/renderer/components/CuePipelineEditor/panels/EdgePromptRow.tsx (1)
26-28: Consider stronger typing for the debounced callback.The
...args: unknown[]pattern works but loses type safety. IfuseDebouncedCallbacksupports generics, a typed version would be cleaner.💡 Potential typed callback alternative
- const { debouncedCallback: debouncedUpdate } = useDebouncedCallback((...args: unknown[]) => { - onUpdateEdgePrompt(edgeInfo.edgeId, args[0] as string); - }, 300); + const { debouncedCallback: debouncedUpdate } = useDebouncedCallback((prompt: string) => { + onUpdateEdgePrompt(edgeInfo.edgeId, prompt); + }, 300);This assumes
useDebouncedCallbackaccepts typed callbacks. If not, the current approach is acceptable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CuePipelineEditor/panels/EdgePromptRow.tsx` around lines 26 - 28, The debounced callback currently uses an untyped rest param; change it to a strongly typed callback so TypeScript knows the argument is a string. Update the useDebouncedCallback invocation to supply an explicit generic or typed function signature (e.g. a callback taking a single string) instead of (...args: unknown[]), and call onUpdateEdgePrompt(edgeInfo.edgeId, value) inside that typed callback; target the useDebouncedCallback and debouncedUpdate symbols and keep onUpdateEdgePrompt/edgeInfo.edgeId usage the same.src/renderer/components/CuePipelineEditor/cueEventConstants.ts (1)
18-18: Consider a more semantically appropriate icon forgithub.issue.
GitBranchis typically associated with Git branches rather than issues. Consider using a more issue-specific icon likeCircleDotorAlertCirclefrom lucide-react to better match user expectations.💡 Suggested icon alternatives
-import { Clock, FileText, Zap, GitPullRequest, GitBranch, CheckSquare } from 'lucide-react'; +import { Clock, FileText, Zap, GitPullRequest, CircleDot, CheckSquare } from 'lucide-react'; ... - 'github.issue': GitBranch, + 'github.issue': CircleDot,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CuePipelineEditor/cueEventConstants.ts` at line 18, The mapping for 'github.issue' currently uses the GitBranch icon which is semantically inaccurate; update the mapping in cueEventConstants.ts to use a more issue-specific icon such as CircleDot or AlertCircle from lucide-react (replace GitBranch with the chosen symbol in the mapping for 'github.issue'), and update the import list to include the new icon symbol (remove GitBranch if no longer used). Ensure the changed symbol name (CircleDot or AlertCircle) is referenced in the same object where 'github.issue' is defined so the renderer picks up the correct icon.src/renderer/hooks/cue/useCueAiChat.ts (1)
146-159: Listener cleanup could race with error handler.Both
onExitandonAgentErrorhandlers clean up all listeners. If both events fire (e.g., agent errors then exits), the cleanup runs twice. While calling cleanup functions multiple times is likely safe, consider guarding against this.Optional: Guard against double cleanup
const cleanupExit = window.maestro.process.onExit((sid: string) => { if (sid === spawnSessionIdRef.current) { + if (aiCleanupRef.current.length === 0) return; // Already cleaned up aiCleanupRef.current.forEach((fn) => fn()); aiCleanupRef.current = []; // ... rest of handler } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/hooks/cue/useCueAiChat.ts` around lines 146 - 159, The onExit handler (registered via window.maestro.process.onExit) and the onAgentError handler both iterate aiCleanupRef.current and may run the same cleanup functions twice; add a guard to ensure cleanup runs only once per session by using a per-session flag or by clearing aiCleanupRef.current before invoking cleanup functions (e.g., check spawnSessionIdRef.current or a local cleaned boolean, remove/replace aiCleanupRef.current with [] prior to calling functions). Update the cleanupExit registration and the onAgentError path to respect this single-run guard so cleanup functions (cleanupExit) are not executed multiple times.src/renderer/components/CueYamlEditor/CueAiChat.tsx (1)
52-64: Consider adding stable keys for chat messages.Using array index as key (
key={i}) works here since messages are append-only, but if message deletion or editing is added later, this could cause rendering issues. A stable ID per message would be more robust.Optional: Add message IDs in the hook
In
useCueAiChat.ts, add an ID when creating messages:{ id: crypto.randomUUID(), role: 'user', text }Then use
key={msg.id}here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueYamlEditor/CueAiChat.tsx` around lines 52 - 64, The chat message list uses array index as the React key in chatMessages.map (key={i}), which is fragile; update the message model to include a stable id and use it as the key. Add an id property when creating messages in useCueAiChat (e.g., generate with crypto.randomUUID() or another stable ID) so each message object has msg.id, then change the mapping to use key={msg.id} in CueAiChat.tsx (and update any types/interfaces for the message shape).src/renderer/components/CuePipelineEditor/panels/AgentConfigPanel.tsx (1)
59-70: Debounced callback typing could be improved.The
...args: unknown[]pattern with type assertions works but loses type safety. Consider using a typed callback signature.Optional: Type-safe debounced callback
-const { debouncedCallback: debouncedUpdateInput } = useDebouncedCallback((...args: unknown[]) => { - const inputPrompt = args[0] as string; - onUpdateNode(node.id, { inputPrompt } as Partial<AgentNodeData>); -}, 300); +const { debouncedCallback: debouncedUpdateInput } = useDebouncedCallback((inputPrompt: string) => { + onUpdateNode(node.id, { inputPrompt }); +}, 300);This requires
useDebouncedCallbackto support typed signatures.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CuePipelineEditor/panels/AgentConfigPanel.tsx` around lines 59 - 70, Replace the untyped "...args: unknown[]" pattern in the two debounced callbacks with an explicitly typed callback signature so you don't need type assertions: update the calls to useDebouncedCallback to accept a (inputPrompt: string) => void / (outputPrompt: string) => void handler (or use the hook's generic type if it supports one) and call onUpdateNode(node.id, { inputPrompt } as Partial<AgentNodeData>) / onUpdateNode(node.id, { outputPrompt } as Partial<AgentNodeData>) directly; target the debouncedUpdateInput and debouncedUpdateOutput definitions and the useDebouncedCallback symbol to ensure full type safety for the string parameter rather than using unknown[].src/renderer/components/CueModal/CueModal.tsx (1)
92-98: Consider awaiting enable/disable to prevent rapid toggle race conditions.
enable()anddisable()returnPromise<void>but are called fire-and-forget. Rapid toggling could cause race conditions with the underlying state refresh.Proposed fix: Disable button during toggle
+const [toggling, setToggling] = useState(false); + -const handleToggle = useCallback(() => { +const handleToggle = useCallback(async () => { + if (toggling) return; + setToggling(true); + try { if (isEnabled) { - disable(); + await disable(); } else { - enable(); + await enable(); } -}, [isEnabled, enable, disable]); + } finally { + setToggling(false); + } +}, [isEnabled, enable, disable, toggling]);Then disable the toggle button while
togglingis true.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueModal/CueModal.tsx` around lines 92 - 98, The handleToggle currently calls enable()/disable() fire-and-forget which can cause race conditions; update handleToggle to await the Promise returned by enable or disable and guard re-entrancy by introducing a local toggling state (e.g., const [toggling, setToggling] = useState(false)), set toggling=true before awaiting and set it back to false in finally, and pass toggling to the toggle button's disabled prop so the UI is disabled while the async toggle is in progress; reference handleToggle, enable, disable, and the toggle button within CueModal.tsx when making these changes.src/renderer/components/CueModal/SessionsTable.tsx (2)
81-96: Consider extracting the IIFE to a local variable for readability.The inline IIFE works but adds visual complexity. A minor readability improvement would be to compute
colorsandpipelineNamesbefore the return statement.♻️ Optional refactor
<tbody> {sessions.map((s) => { const status = !s.enabled ? 'paused' : s.subscriptionCount > 0 ? 'active' : 'none'; + const colors = getPipelineColorForAgent(s.sessionId, pipelines); + const pipelineNames = pipelines + .filter((p) => colors.includes(p.color)) + .map((p) => p.name); return ( <tr key={s.sessionId} ... <td className="py-2"> - {(() => { - const colors = getPipelineColorForAgent(s.sessionId, pipelines); - if (colors.length === 0) { - return <span style={{ color: theme.colors.textDim }}>—</span>; - } - const pipelineNames = pipelines - .filter((p) => colors.includes(p.color)) - .map((p) => p.name); - return ( - <span className="flex items-center gap-1"> - {colors.map((color, i) => ( - <PipelineDot key={color} color={color} name={pipelineNames[i] ?? ''} /> - ))} - </span> - ); - })()} + {colors.length === 0 ? ( + <span style={{ color: theme.colors.textDim }}>—</span> + ) : ( + <span className="flex items-center gap-1"> + {colors.map((color, i) => ( + <PipelineDot key={color} color={color} name={pipelineNames[i] ?? ''} /> + ))} + </span> + )} </td>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueModal/SessionsTable.tsx` around lines 81 - 96, Extract the inline IIFE in SessionsTable to local variables for clarity: call getPipelineColorForAgent(s.sessionId, pipelines) into a const colors, compute pipelineNames by filtering pipelines for colors and mapping to p.name, then inside the JSX return use a conditional that renders a dimmed dash when colors.length === 0 or a <span className="flex items-center gap-1"> mapping colors to <PipelineDot key={color} color={color} name={pipelineNames[i] ?? ''} />; keep the existing theme.colors.textDim usage for the empty state and preserve the same props passed to PipelineDot.
122-127: Consider coordinating rapid-fire subscription triggers to avoid overlapping API calls and refresh requests.The loop fires
onTriggerSubscription(an async function) without awaiting, so multiple subscriptions trigger simultaneously. With N subscriptions, this fires N concurrent IPC calls plus N concurrentrefresh()calls, causing:
- Overlapping state refreshes (potential race conditions)
- Burst of simultaneous API requests (rate-limiting risk)
- No user feedback on progress or failures
Suggested fix: Await subscriptions sequentially, or batch them with
Promise.all()+ single final refresh. Also consider showing a loading state during the operation. If triggering many subscriptions becomes frequent, propose a batch trigger API to the backend.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueModal/SessionsTable.tsx` around lines 122 - 127, The button handler currently loops over subs and calls the async onTriggerSubscription(sub.name) without awaiting, causing many concurrent IPC calls and refresh() races; change the handler to coordinate these calls — either await each onTriggerSubscription sequentially (for (const sub of subs) await onTriggerSubscription(sub.name)) or run them in parallel with Promise.all(subs.map(s => onTriggerSubscription(s.name))) and then call refresh() once after all complete; also set a local loading state during the operation and handle/report failures from onTriggerSubscription before calling refresh().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/cue/cue-engine.ts`:
- Around line 145-154: The catch block after initCueDb/pruneCueEvents currently
logs the error and returns, which swallows exceptions; update this path to
explicitly report the exception to Sentry before returning by invoking your
Sentry reporting utility (e.g., Sentry.captureException(error) or the project's
reportError/reportException helper) and pass the caught error (the `error`
variable) along with any contextual info (e.g., a message/tag indicating "[CUE]
DB init failure"); place the call immediately after this.deps.onLog(...) and
before the return so the failure is sent to Sentry while keeping the existing
log and early exit behavior.
In `@src/renderer/components/CueModal/CueModal.tsx`:
- Around line 135-146: The effect that calls window.maestro.cue.getGraphData
silently swallows errors in its .catch(() => {}) block; update the CueModal
useEffect to handle failures by either logging the error (e.g., console.error or
a logger) and/or setting a component error state (e.g., introduce [graphError,
setGraphError]) so the UI can show a failure message, and ensure you still
respect the cancelled flag before calling setGraphSessions or setGraphError;
replace the empty .catch with logic that logs the error and sets the error
state.
In `@src/renderer/components/CueModal/cueModalUtils.ts`:
- Around line 9-21: The formatRelativeTime function currently computes diff
using new Date(dateStr).getTime() which yields NaN for invalid date strings and
produces "NaNd ago"; fix this by parsing the date into a Date object first
(const d = new Date(dateStr)), check isNaN(d.getTime()) and if invalid return
the placeholder '—' (same as the empty input branch), otherwise proceed to
compute diff and format seconds/minutes/hours/days; update references in the
function name formatRelativeTime and any local variables you add to ensure the
invalid-date check short-circuits before doing arithmetic.
- Around line 31-34: formatElapsed can pass NaN to formatDuration because new
Date(startedAt).getTime() may be NaN; update formatElapsed to validate the
parsed time (use Date.parse or new Date(startedAt).getTime()), check
isNaN(parsed) and if invalid use 0 (or an explicit fallback) before calling
formatDuration, so call formatDuration(Math.max(0, parsedDifference)) where
parsedDifference is only computed when startedAt is a valid date; reference
function formatElapsed and formatDuration to locate the change.
In `@src/renderer/components/CueYamlEditor/CueYamlEditor.tsx`:
- Around line 119-128: The catch block inside the handleSave async callback
(used to write YAML and refresh session) is empty and swallows errors instead of
letting them bubble to Sentry; update the catch to accept the error (e.g., catch
(err)) and rethrow it after any optional user-facing handling so exceptions
propagate (or remove the try/catch entirely) — target the handleSave function
where window.maestro.cue.writeYaml and refreshSession are called and ensure
errors are rethrown rather than silently ignored.
- Around line 130-137: handleClose currently only checks for unsaved changes
(yamlContent vs originalContent) but doesn't prevent closing when an async AI
chat operation is in progress; update handleClose to also check the chatBusy
boolean (or prop/state) before proceeding, and if chatBusy is true either block
the close and show an informative alert/confirmation or include chatBusy in the
confirmation flow so the user cannot discard in-progress AI work; specifically
modify the useCallback for handleClose to reference chatBusy alongside
yamlContent, originalContent, and onClose and conditionally return early or
prompt when chatBusy is true.
In `@src/renderer/components/CueYamlEditor/PatternPreviewModal.tsx`:
- Around line 22-26: The handleCopy callback lacks error handling around
navigator.clipboard.writeText which can reject; wrap the await call in a
try/catch inside handleCopy, call setCopied(true) only on success, and
setCopied(false) in a finally or via the existing timeout; on error log or
surface the error (e.g., process via a toast or console.error) so the UI can
fail gracefully; reference the handleCopy function,
navigator.clipboard.writeText, and setCopied when making the changes.
In `@src/renderer/hooks/cue/useCueAiChat.ts`:
- Around line 64-81: When isOpen becomes true you need to cleanup any stale AI
listeners before resetting state: inside the useEffect that handles "if
(isOpen)" call and clear aiCleanupRef.current (e.g.,
aiCleanupRef.current.forEach(fn => fn()); aiCleanupRef.current = []) before
calling setChatMessages, setChatInput, setChatBusy and resetting
agentSessionIdRef/spawnSessionIdRef; this ensures existing listeners from
previous sessions are removed prior to creating a new spawnSessionIdRef and
starting a new agent session.
In `@src/renderer/hooks/useCue.ts`:
- Around line 99-101: The catch block in the refresh flow (around the code that
updates Cue status in useCue.ts) currently converts any thrown value into UI
error state via setError and hides unexpected defects; change it to treat
expected/recoverable errors (e.g., known network or API error types returned by
your fetch function) by setting setError when mountedRef.current is true, but
for unexpected exceptions call Sentry.captureException(err) (or the project’s
reporting utility) and rethrow the error so it surfaces to Sentry instead of
being swallowed; update the catch to inspect the thrown value ( instanceof Error
or custom error classes / known error markers from your fetchCueStatus or
refreshCue function) and only setError for the expected cases, otherwise
report+rethrow.
---
Nitpick comments:
In `@src/renderer/components/CueModal/CueModal.tsx`:
- Around line 92-98: The handleToggle currently calls enable()/disable()
fire-and-forget which can cause race conditions; update handleToggle to await
the Promise returned by enable or disable and guard re-entrancy by introducing a
local toggling state (e.g., const [toggling, setToggling] = useState(false)),
set toggling=true before awaiting and set it back to false in finally, and pass
toggling to the toggle button's disabled prop so the UI is disabled while the
async toggle is in progress; reference handleToggle, enable, disable, and the
toggle button within CueModal.tsx when making these changes.
In `@src/renderer/components/CueModal/SessionsTable.tsx`:
- Around line 81-96: Extract the inline IIFE in SessionsTable to local variables
for clarity: call getPipelineColorForAgent(s.sessionId, pipelines) into a const
colors, compute pipelineNames by filtering pipelines for colors and mapping to
p.name, then inside the JSX return use a conditional that renders a dimmed dash
when colors.length === 0 or a <span className="flex items-center gap-1"> mapping
colors to <PipelineDot key={color} color={color} name={pipelineNames[i] ?? ''}
/>; keep the existing theme.colors.textDim usage for the empty state and
preserve the same props passed to PipelineDot.
- Around line 122-127: The button handler currently loops over subs and calls
the async onTriggerSubscription(sub.name) without awaiting, causing many
concurrent IPC calls and refresh() races; change the handler to coordinate these
calls — either await each onTriggerSubscription sequentially (for (const sub of
subs) await onTriggerSubscription(sub.name)) or run them in parallel with
Promise.all(subs.map(s => onTriggerSubscription(s.name))) and then call
refresh() once after all complete; also set a local loading state during the
operation and handle/report failures from onTriggerSubscription before calling
refresh().
In `@src/renderer/components/CuePipelineEditor/cueEventConstants.ts`:
- Line 18: The mapping for 'github.issue' currently uses the GitBranch icon
which is semantically inaccurate; update the mapping in cueEventConstants.ts to
use a more issue-specific icon such as CircleDot or AlertCircle from
lucide-react (replace GitBranch with the chosen symbol in the mapping for
'github.issue'), and update the import list to include the new icon symbol
(remove GitBranch if no longer used). Ensure the changed symbol name (CircleDot
or AlertCircle) is referenced in the same object where 'github.issue' is defined
so the renderer picks up the correct icon.
In `@src/renderer/components/CuePipelineEditor/panels/AgentConfigPanel.tsx`:
- Around line 59-70: Replace the untyped "...args: unknown[]" pattern in the two
debounced callbacks with an explicitly typed callback signature so you don't
need type assertions: update the calls to useDebouncedCallback to accept a
(inputPrompt: string) => void / (outputPrompt: string) => void handler (or use
the hook's generic type if it supports one) and call onUpdateNode(node.id, {
inputPrompt } as Partial<AgentNodeData>) / onUpdateNode(node.id, { outputPrompt
} as Partial<AgentNodeData>) directly; target the debouncedUpdateInput and
debouncedUpdateOutput definitions and the useDebouncedCallback symbol to ensure
full type safety for the string parameter rather than using unknown[].
In `@src/renderer/components/CuePipelineEditor/panels/EdgePromptRow.tsx`:
- Around line 26-28: The debounced callback currently uses an untyped rest
param; change it to a strongly typed callback so TypeScript knows the argument
is a string. Update the useDebouncedCallback invocation to supply an explicit
generic or typed function signature (e.g. a callback taking a single string)
instead of (...args: unknown[]), and call onUpdateEdgePrompt(edgeInfo.edgeId,
value) inside that typed callback; target the useDebouncedCallback and
debouncedUpdate symbols and keep onUpdateEdgePrompt/edgeInfo.edgeId usage the
same.
In `@src/renderer/components/CuePipelineEditor/panels/triggers/TriggerConfig.tsx`:
- Around line 31-39: The callbacks passed to useDebouncedCallback are using an
unsafe unknown[] signature and then casting args[0]; update debouncedUpdate and
debouncedUpdateLabel to use a typed callback parameter (e.g., provide a typed
function signature or generic to useDebouncedCallback) so you directly accept
(config: TriggerNodeData['config']) and (customLabel?: string) respectively,
then call onUpdateNode(node.id, { config }) and onUpdateNode(node.id, {
customLabel }) without casts; reference the debouncedUpdate,
debouncedUpdateLabel, useDebouncedCallback, onUpdateNode and TriggerNodeData
symbols when making the change.
In `@src/renderer/components/CueYamlEditor/CueAiChat.tsx`:
- Around line 52-64: The chat message list uses array index as the React key in
chatMessages.map (key={i}), which is fragile; update the message model to
include a stable id and use it as the key. Add an id property when creating
messages in useCueAiChat (e.g., generate with crypto.randomUUID() or another
stable ID) so each message object has msg.id, then change the mapping to use
key={msg.id} in CueAiChat.tsx (and update any types/interfaces for the message
shape).
In `@src/renderer/components/CueYamlEditor/PatternPreviewModal.tsx`:
- Around line 22-26: The setTimeout in handleCopy (which writes pattern.yaml to
clipboard and calls setCopied) can call setCopied after the modal unmounts;
store the timer id in a ref (e.g., copyTimeoutRef), assign the timeout return
value to it inside handleCopy, and add a useEffect cleanup that clears the
timeout (clearTimeout(copyTimeoutRef.current)) on unmount to prevent state
updates after unmount; ensure handleCopy and the cleanup reference the same ref
and that the ref is cleared/reset after the timeout completes.
In `@src/renderer/components/CueYamlEditor/YamlTextEditor.tsx`:
- Line 25: Remove the unused ref declaration yamlTextareaRef from YamlTextEditor
and delete the ref attribute on the textarea element; the Tab key handler
already uses e.currentTarget to access the textarea, so delete the const
yamlTextareaRef = useRef<HTMLTextAreaElement>(null) and the corresponding ref
usage to avoid dead code and unused imports.
In `@src/renderer/hooks/cue/useCueAiChat.ts`:
- Around line 146-159: The onExit handler (registered via
window.maestro.process.onExit) and the onAgentError handler both iterate
aiCleanupRef.current and may run the same cleanup functions twice; add a guard
to ensure cleanup runs only once per session by using a per-session flag or by
clearing aiCleanupRef.current before invoking cleanup functions (e.g., check
spawnSessionIdRef.current or a local cleaned boolean, remove/replace
aiCleanupRef.current with [] prior to calling functions). Update the cleanupExit
registration and the onAgentError path to respect this single-run guard so
cleanup functions (cleanupExit) are not executed multiple times.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 702dde9e-6d71-4999-8dba-402c24bd7160
📒 Files selected for processing (48)
src/__tests__/main/cue/cue-concurrency.test.tssrc/__tests__/main/cue/cue-engine.test.tssrc/__tests__/main/cue/cue-event-factory.test.tssrc/__tests__/main/cue/cue-sleep-wake.test.tssrc/__tests__/renderer/hooks/useCue.test.tssrc/main/cue/cue-engine.tssrc/main/cue/cue-fan-in-tracker.tssrc/main/cue/cue-file-watcher.tssrc/main/cue/cue-github-poller.tssrc/main/cue/cue-reconciler.tssrc/main/cue/cue-subscription-setup.tssrc/main/cue/cue-task-scanner.tssrc/main/cue/cue-types.tssrc/renderer/components/CueModal.tsxsrc/renderer/components/CueModal/ActiveRunsList.tsxsrc/renderer/components/CueModal/ActivityLog.tsxsrc/renderer/components/CueModal/ActivityLogDetail.tsxsrc/renderer/components/CueModal/CueModal.tsxsrc/renderer/components/CueModal/SessionsTable.tsxsrc/renderer/components/CueModal/StatusDot.tsxsrc/renderer/components/CueModal/cueModalUtils.tssrc/renderer/components/CueModal/index.tssrc/renderer/components/CuePipelineEditor/PipelineCanvas.tsxsrc/renderer/components/CuePipelineEditor/cueEventConstants.tssrc/renderer/components/CuePipelineEditor/drawers/TriggerDrawer.tsxsrc/renderer/components/CuePipelineEditor/edges/PipelineEdge.tsxsrc/renderer/components/CuePipelineEditor/nodes/TriggerNode.tsxsrc/renderer/components/CuePipelineEditor/panels/AgentConfigPanel.tsxsrc/renderer/components/CuePipelineEditor/panels/EdgePromptRow.tsxsrc/renderer/components/CuePipelineEditor/panels/NodeConfigPanel.tsxsrc/renderer/components/CuePipelineEditor/panels/triggers/TriggerConfig.tsxsrc/renderer/components/CuePipelineEditor/panels/triggers/index.tssrc/renderer/components/CuePipelineEditor/panels/triggers/triggerConfigStyles.tssrc/renderer/components/CueYamlEditor.tsxsrc/renderer/components/CueYamlEditor/CueAiChat.tsxsrc/renderer/components/CueYamlEditor/CueYamlEditor.tsxsrc/renderer/components/CueYamlEditor/PatternPicker.tsxsrc/renderer/components/CueYamlEditor/PatternPreviewModal.tsxsrc/renderer/components/CueYamlEditor/YamlTextEditor.tsxsrc/renderer/components/CueYamlEditor/index.tssrc/renderer/components/History/historyConstants.tsxsrc/renderer/constants/cueYamlDefaults.tssrc/renderer/hooks/cue/useCueAiChat.tssrc/renderer/hooks/cue/usePipelineLayout.tssrc/renderer/hooks/cue/usePipelineSelection.tssrc/renderer/hooks/cue/usePipelineState.tssrc/renderer/hooks/useCue.tssrc/shared/cue-pipeline-types.ts
💤 Files with no reviewable changes (2)
- src/renderer/components/CueModal.tsx
- src/renderer/components/CueYamlEditor.tsx
| } catch (err) { | ||
| if (!mountedRef.current) return; | ||
| setError(err instanceof Error ? err.message : 'Failed to fetch Cue status'); |
There was a problem hiding this comment.
Don’t treat all refresh failures as recoverable UI errors.
This catch path currently swallows unexpected exceptions into error state, which can hide real defects from Sentry. Split expected/recoverable failures from unexpected ones, and report unexpected exceptions explicitly.
Proposed direction
import { useState, useEffect, useCallback, useRef } from 'react';
+import { captureException } from '../utils/sentry';
@@
} catch (err) {
if (!mountedRef.current) return;
- setError(err instanceof Error ? err.message : 'Failed to fetch Cue status');
+ if (err instanceof Error) {
+ // Expected/recoverable IPC/network failures can be shown to users
+ setError(err.message);
+ return;
+ }
+ // Unexpected failure: report explicitly
+ captureException(err, { extra: { operation: 'useCue.refresh' } });
+ setError('Failed to fetch Cue status');
} finally {As per coding guidelines: “For expected/recoverable errors, catch them explicitly…” and “For unexpected errors, rethrow to let Sentry capture them,” plus “Use Sentry utilities for explicit reporting.”
📝 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.
| } catch (err) { | |
| if (!mountedRef.current) return; | |
| setError(err instanceof Error ? err.message : 'Failed to fetch Cue status'); | |
| import { useState, useEffect, useCallback, useRef } from 'react'; | |
| import { captureException } from '../utils/sentry'; | |
| } catch (err) { | |
| if (!mountedRef.current) return; | |
| if (err instanceof Error) { | |
| // Expected/recoverable IPC/network failures can be shown to users | |
| setError(err.message); | |
| return; | |
| } | |
| // Unexpected failure: report explicitly | |
| captureException(err, { extra: { operation: 'useCue.refresh' } }); | |
| setError('Failed to fetch Cue status'); | |
| } finally { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/hooks/useCue.ts` around lines 99 - 101, The catch block in the
refresh flow (around the code that updates Cue status in useCue.ts) currently
converts any thrown value into UI error state via setError and hides unexpected
defects; change it to treat expected/recoverable errors (e.g., known network or
API error types returned by your fetch function) by setting setError when
mountedRef.current is true, but for unexpected exceptions call
Sentry.captureException(err) (or the project’s reporting utility) and rethrow
the error so it surfaces to Sentry instead of being swallowed; update the catch
to inspect the thrown value ( instanceof Error or custom error classes / known
error markers from your fetchCueStatus or refreshCue function) and only setError
for the expected cases, otherwise report+rethrow.
- cue-engine.ts: report DB init failure to Sentry before early return
- CueModal: surface getGraphData errors in dashboard error banner,
add toggling guard to prevent enable/disable race conditions
- cueModalUtils: guard formatRelativeTime/formatElapsed against NaN dates
- CueYamlEditor: remove swallowed catch in handleSave (let errors bubble
to Sentry), block close while AI chat is in progress
- PatternPreviewModal: wrap clipboard.writeText in try/catch, clear
setTimeout on unmount via ref
- useCueAiChat: clean up stale listeners on modal reopen, snapshot-and-
clear pattern prevents double cleanup when both onExit and onAgentError
fire for the same session
- cueEventConstants: use CircleDot instead of GitBranch for github.issue
- YamlTextEditor: remove unused yamlTextareaRef
Skipped (verified, no fix needed):
- useCue.ts catch: IPC failures are expected/recoverable, main process
reports unexpected errors to Sentry — double-reporting not desirable
- useDebouncedCallback typing: hook generic constraint requires unknown[],
changing requires modifying the shared utility (out of scope)
- Chat message key={i}: append-only list, never reorders — index key is safe
- SessionsTable IIFE: works correctly, pure style preference
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/renderer/components/CueModal/CueModal.tsx (1)
206-214: Minor duplication withonEscapehandler.The confirmation logic here duplicates lines 120-126 in the
onEscapecallback. Consider extracting a shared helper, though this is optional given the small scope.💡 Possible extraction
+const confirmCloseIfDirty = useCallback(() => { + if (pipelineDirtyRef.current) { + const confirmed = window.confirm( + 'You have unsaved changes in the pipeline editor. Discard and close?' + ); + if (!confirmed) return false; + } + return true; +}, []); // In registerLayer onEscape: onEscape: () => { if (showHelpRef.current) { setShowHelp(false); return; } - if (pipelineDirtyRef.current) { - const confirmed = window.confirm( - 'You have unsaved changes in the pipeline editor. Discard and close?' - ); - if (!confirmed) return; - } - onCloseRef.current(); + if (confirmCloseIfDirty()) onCloseRef.current(); }, // handleCloseWithConfirm: const handleCloseWithConfirm = useCallback(() => { - if (pipelineDirtyRef.current) { - const confirmed = window.confirm( - 'You have unsaved changes in the pipeline editor. Discard and close?' - ); - if (!confirmed) return; - } - onClose(); + if (confirmCloseIfDirty()) onClose(); }, [onClose, confirmCloseIfDirty]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueModal/CueModal.tsx` around lines 206 - 214, Extract the duplicated confirmation logic used in handleCloseWithConfirm and the onEscape callback into a small shared helper (e.g., confirmDiscardIfDirty) that accepts pipelineDirtyRef and onClose (or returns a boolean) and performs the window.confirm and conditional onClose invocation; then replace the bodies of handleCloseWithConfirm and onEscape to call that helper to avoid duplication while keeping existing behavior for pipelineDirtyRef and onClose.src/renderer/components/CueYamlEditor/CueYamlEditor.tsx (1)
129-146: Silent failures inrefreshYamlFromDiskmay confuse users.Both catch blocks are marked "non-fatal" but provide no user feedback. If the AI agent modifies the file but the refresh fails, the editor will show stale content without indication. Consider setting a brief error state or toast.
💡 Possible improvement
const refreshYamlFromDisk = useCallback(async () => { try { const content = await window.maestro.cue.readYaml(projectRoot); if (content) { setYamlContent(content); setOriginalContent(content); try { const result = await window.maestro.cue.validateYaml(content); setIsValid(result.valid); setValidationErrors(result.errors); } catch { - // non-fatal + // Validation failed but content was read successfully } } } catch { - // non-fatal + // Could optionally show a brief warning that refresh failed + console.warn('Failed to refresh YAML from disk'); } }, [projectRoot]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/CueYamlEditor/CueYamlEditor.tsx` around lines 129 - 146, refreshYamlFromDisk currently swallows both readYaml and validateYaml errors leaving the editor showing stale content with no feedback; update refreshYamlFromDisk to surface errors to the user by setting a short-lived error state or invoking the app toast/notification helper when either window.maestro.cue.readYaml(projectRoot) or window.maestro.cue.validateYaml(content) throws — specifically update the catch blocks in refreshYamlFromDisk to call a UI notifier or set a new state (e.g., setYamlLoadError / showToast) and ensure you still treat validation failures as non-fatal but communicate the problem (while keeping existing setters: setYamlContent, setOriginalContent, setIsValid, setValidationErrors).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/renderer/components/CueModal/CueModal.tsx`:
- Around line 206-214: Extract the duplicated confirmation logic used in
handleCloseWithConfirm and the onEscape callback into a small shared helper
(e.g., confirmDiscardIfDirty) that accepts pipelineDirtyRef and onClose (or
returns a boolean) and performs the window.confirm and conditional onClose
invocation; then replace the bodies of handleCloseWithConfirm and onEscape to
call that helper to avoid duplication while keeping existing behavior for
pipelineDirtyRef and onClose.
In `@src/renderer/components/CueYamlEditor/CueYamlEditor.tsx`:
- Around line 129-146: refreshYamlFromDisk currently swallows both readYaml and
validateYaml errors leaving the editor showing stale content with no feedback;
update refreshYamlFromDisk to surface errors to the user by setting a
short-lived error state or invoking the app toast/notification helper when
either window.maestro.cue.readYaml(projectRoot) or
window.maestro.cue.validateYaml(content) throws — specifically update the catch
blocks in refreshYamlFromDisk to call a UI notifier or set a new state (e.g.,
setYamlLoadError / showToast) and ensure you still treat validation failures as
non-fatal but communicate the problem (while keeping existing setters:
setYamlContent, setOriginalContent, setIsValid, setValidationErrors).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 11dc4005-d6f0-4460-8650-f8b9d7cdb49f
📒 Files selected for processing (8)
src/main/cue/cue-engine.tssrc/renderer/components/CueModal/CueModal.tsxsrc/renderer/components/CueModal/cueModalUtils.tssrc/renderer/components/CuePipelineEditor/cueEventConstants.tssrc/renderer/components/CueYamlEditor/CueYamlEditor.tsxsrc/renderer/components/CueYamlEditor/PatternPreviewModal.tsxsrc/renderer/components/CueYamlEditor/YamlTextEditor.tsxsrc/renderer/hooks/cue/useCueAiChat.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/renderer/components/CueYamlEditor/PatternPreviewModal.tsx
- src/renderer/components/CueYamlEditor/YamlTextEditor.tsx
- src/main/cue/cue-engine.ts
- src/renderer/components/CueModal/cueModalUtils.ts
Summary
cue-engine.ts1,583→719 lines (−55%) via 5 extracted modules (activity log, heartbeat, subscription setup, fan-in tracker, run manager)NodeConfigPanel850→249 lines (−71%) — TriggerConfig, AgentConfigPanel, EdgePromptRowCueModal1,029→490 lines (−52%) — SessionsTable, ActiveRunsList, ActivityLog, ActivityLogDetail, StatusDot, cueModalUtilsCueYamlEditor691→284 lines (−59%) — PatternPicker, PatternPreviewModal, CueAiChat, YamlTextEditor, useCueAiChat hookCuePipelineEditor1,608→607 lines (−62%) — usePipelineState, usePipelineSelection, usePipelineLayout hooks + PipelineToolbar, PipelineCanvas, PipelineContextMenu componentsCUE_COLORconstant,createCueEventfactory (12 call sites),useCueerror state, engine start hardening,IncomingTriggerEdgeInfo/CuePipelineSessionInfotype consolidation,cueEventConstants.tssingle source,CUE_YAML_TEMPLATEextractionTest plan
npm run lint- cleannpm run lint:eslint- cleannpm run test- 23,649 tests passedSummary by CodeRabbit
New Features
Bug Fixes
Tests