Skip to content

fix(web): destroy terminal shells on tab close with busy confirmation#1203

Merged
carlosflorencio merged 9 commits into
mainfrom
feature/stop-terminal-on-clo-o8m
May 31, 2026
Merged

fix(web): destroy terminal shells on tab close with busy confirmation#1203
carlosflorencio merged 9 commits into
mainfrom
feature/stop-terminal-on-clo-o8m

Conversation

@carlosflorencio
Copy link
Copy Markdown
Member

@carlosflorencio carlosflorencio commented May 31, 2026

Summary

  • Closing a terminal tab (dockview X, right-panel strip, or mobile picker) now destroys the shell and stops the PTY instead of parking it in the background
  • When a command appears to be running (Enter pressed, no shell prompt yet) or the terminal is a script terminal, a confirmation dialog warns before closing
  • Extracted shared CloseTerminalConfirmDialog and a xterm-based busy registry so all close paths behave consistently

Test plan

  • Open a task, add a second terminal, close it at idle prompt — tab closes immediately with no dialog
  • Run a long command (e.g. sleep 60), click tab X — confirmation dialog appears; Cancel keeps tab open
  • Confirm close on a running command — shell stops and tab is removed
  • Reload page after closing terminal feat: web app cleanup + init kanban #2 — it does not reappear in the "+" → Terminals menu
  • Mobile terminal picker: idle close is instant; busy close shows dialog

Made with Cursor

Closing a terminal tab now stops the PTY and removes the shell instead of
parking it in the background. A confirmation dialog appears when a command
looks like it is still running.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements conditional terminal close confirmation by detecting terminal activity via busy-state registry tracking, replacing unconditional park/stop behavior with destroy, and integrating confirmation prompts across task-right-panel, terminal-tab, and mobile UI layers.

Changes

Terminal Close Confirmation Refactor

Layer / File(s) Summary
Terminal Busy State Detection Registry
apps/web/lib/terminal/terminal-busy-registry.ts, apps/web/lib/terminal/terminal-busy-registry.test.ts
Module-scoped Map tracks terminal busy state. markTerminalInput marks busy on newline/return; markTerminalOutput clears busy when xterm buffer shows a shell prompt. shouldConfirmTerminalClose decides confirmation based on script type, legacy initialCommand flag, and current busy state.
Real-time Busy Tracking Hook and Tests
apps/web/components/task/use-terminal-busy-tracking.ts, apps/web/components/task/use-terminal-busy-tracking.test.ts
Implements useTerminalBusyTracking hook subscribing to xterm onData and onWriteParsed to mark input/output activity in busy registry. Handles RAF retry when terminal ref is initially null and cleans up subscriptions on unmount. Tests verify subscription attachment, disabled no-op behavior, RAF retry, and cleanup disposals.
Core Close Behavior: Unconditional Destroy
apps/web/hooks/domains/session/use-terminals.ts, apps/web/components/task/dockview-layout-setup.ts
Removes conditional park/stop logic. useCloseTab and dockview close handlers now always call destroyUserShell, eliminating the "park ordinary shells" branching. Updated import destructuring and hook call signatures.
Shared UI: Confirmation Dialog and Tab Contents
apps/web/components/task/close-terminal-confirm-dialog.tsx, apps/web/components/task/task-right-panel-tab-contents.tsx
New CloseTerminalConfirmDialog renders alert with cancel/confirm actions. CommandsTabContent and TerminalTabContents export tab panels for scripts and terminals with appropriate content wrappers and conditional terminal rendering.
Confirmation Integration: Task Right Panel
apps/web/components/task/task-right-panel.tsx
Adds useConfirmableTerminalClose hook to intercept close events, check shouldConfirmTerminalClose, and perform async destroy only after confirmation. Refactors TaskRightPanel into useTaskRightPanel hook for state/wiring centralization. Renders confirmation dialog controlled by pendingClose state.
Confirmation Integration: Terminal Tab
apps/web/components/task/terminal-tab.tsx
Adds useTerminalTabClose hook managing tab-close confirmation and async destroy flow including store removal and dockview termination-close marking. Updates TerminalTab to track closable flag, wire close handlers, and render CloseTerminalConfirmDialog. TerminalTabBody hides dockview's default close control when not closable.
Confirmation Integration: Mobile Terminals
apps/web/components/task/mobile/mobile-terminals-section.tsx
Refactors useTerminalCloseHandler to accept taskId, split close into immediate and confirmed paths, and check shouldConfirmTerminalClose to conditionally show confirmation dialog before destroying.
Busy Tracking Wired to PassthroughTerminal
apps/web/components/task/passthrough-terminal.tsx
Integrates useTerminalBusyTracking call passing terminal id, xterm ref, shell-mode flag, and terminal-ready state to enable real-time busy detection.
E2E Test Updates: Destroy Behavior Validation
apps/web/e2e/tests/terminal/terminal-dockview-ui.spec.ts
Updates "closing a terminal" test to verify destroy semantics: closing seq=2 tab removes it permanently, reload confirms it no longer appears in reopen menu (instead of expecting parked pill). Comments reflect destroy-on-close expectation.

Sequence Diagrams

Right Panel Close Request Flow

sequenceDiagram
  participant UI
  participant Confirmable
  participant Registry
  UI->>Confirmable: handleAskCloseTab
  Confirmable->>Registry: shouldConfirmTerminalClose
  Registry-->>Confirmable: needsConfirm
  Confirmable->>UI: setPendingClose
  UI->>UI: show dialog
  UI->>Confirmable: handleConfirmClose
  Confirmable->>Confirmable: destroyTerminal
Loading

Busy Tracking Lifecycle

sequenceDiagram
  participant Component as Passthrough
  participant Hook as Tracking
  participant Terminal as xterm
  participant Registry as busy-registry
  Component->>Hook: enable with ref
  Hook->>Terminal: wait for ready
  Terminal-->>Hook: ready
  Hook->>Terminal: subscribe onData
  Hook->>Terminal: subscribe onWriteParsed
  Terminal->>Hook: onData event
  Hook->>Registry: markInput
  Terminal->>Hook: onWriteParsed event
  Hook->>Registry: markOutput
  Component->>Hook: unmount
  Hook->>Registry: clearBusy
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • kdlbs/kandev#491: Modifies terminal teardown in dockview-layout-setup and session/identity handling during close.
  • kdlbs/kandev#840: Refines mobile terminal close/management flow with confirmation and task-scoped termination.

Poem

🐰 A rabbit hops through busy terminals
Busy? Confirm before you close!
No more parkings, shells now vanish—
Tracked by hooks that know the way. 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: terminal shells are now destroyed on tab close with confirmation when the terminal is busy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description covers all required sections: a clear summary explaining the behavioral change (destroy vs. park), validation steps for manual testing, and mentions related components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/stop-terminal-on-clo-o8m

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 24s —— View job


Code Review

  • Gather context and understand the PR
  • Read changed files
  • Review terminal-busy-registry.ts and its test
  • Review use-terminal-busy-tracking.ts
  • Review close-terminal-confirm-dialog.tsx
  • Review terminal-tab.tsx
  • Review task-right-panel.tsx
  • Review mobile-terminals-section.tsx
  • Review use-terminals.ts, dockview-layout-setup.ts, passthrough-terminal.tsx
  • Post inline comments and summary
  • Final update

Summary

Good PR overall. The core mechanic — xterm onData/onWriteParsed → module-level busy map → confirm before destroy — is solid, the PROMPT_TAIL regex is well-documented, and the three close paths (dockview tab, right-panel strip, mobile picker) all share the CloseTerminalConfirmDialog consistently.

The key discovery from reading the API layer: stopUserShell is literally an alias for destroyUserShell, so the naming difference across paths is cosmetic — both send user_shell.destroy (with user_shell.stop fallback).

Findings (5 inline comments):

Severity File Issue
Medium mobile-terminals-section.tsx:94 Mobile close path omits taskId — backend ownership check may reject destroy for ordinary terminals
Low terminal-tab.tsx:81 Dockview uses initialCommand for script detection; right-panel/mobile use type === "script" — can diverge
Low task-right-panel.tsx:514 pendingClose?.label || "" renders empty quotes in confirm dialog copy
Note terminal-busy-registry.ts:5 Module-level map is safe today (client-only in practice) but worth guarding against accidental server import
Note terminal-busy-registry.test.ts:26 beforeEach only clears "term-1" — fine now but will leak state if new test IDs are added

The missing taskId in mobile is the only thing worth fixing before merge if ordinary terminal close is expected to be fully authorized. The rest are polish.

Comment thread apps/web/components/task/mobile/mobile-terminals-section.tsx Outdated
Comment thread apps/web/components/task/terminal-tab.tsx Outdated
Comment thread apps/web/components/task/task-right-panel.tsx Outdated
Comment thread apps/web/lib/terminal/terminal-busy-registry.ts
Comment thread apps/web/lib/terminal/terminal-busy-registry.test.ts Outdated
Comment thread apps/web/components/task/use-terminal-busy-tracking.ts Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR replaces the old park-on-close behavior with destroy-on-close for terminal tabs, and adds a shared busy-detection layer (xterm buffer heuristic + module-level registry) so all three close paths (dockview X, right-panel strip, mobile picker) consistently show a confirmation dialog before killing a running command.

  • Destroy on close: handleTerminalPanelClosed and useCloseTab now always call destroyUserShell/stopUserShell; parkUserShell is fully removed. The dockview tab header intercepts the close button via closeActionOverride, calls destroyUserShell itself, marks the panel with markTerminalPanelTerminateClose, and then closes the panel so the fallback handler skips the duplicate stop.
  • Busy registry: terminal-busy-registry.ts tracks busy state via xterm onData (Enter key → busy) and onWriteParsed (prompt regex → idle), with fixes for blank-line false-clears and heredoc > false-positives; useTerminalBusyTracking wires this hook into PassthroughTerminal with an rAF retry for late-attach.
  • Shared confirm dialog: CloseTerminalConfirmDialog is extracted and reused by all three close paths; shouldConfirmTerminalClose centralises the "needs confirmation?" logic (script terminal or busy state).

Confidence Score: 5/5

Safe to merge — all three close paths are consistent, the terminate-mark guard prevents double-stops, and the busy registry is well-tested.

The core destroy-on-close logic is clean and the terminate-mark mechanism correctly prevents the dockview fallback from double-stopping a shell that the tab component already destroyed. The busy-tracking registry handles the previously flagged edge cases (blank lines, heredoc prompts). No functional regressions were identified; remaining comments are quality-of-life observations about silent error swallowing, a fragile internal CSS class dependency for testid attachment, and an unbounded rAF retry loop.

apps/web/components/task/terminal-tab.tsx (testid via internal dockview class), apps/web/components/task/close-terminal-confirm-dialog.tsx (silent error swallowing in confirm action), apps/web/components/task/use-terminal-busy-tracking.ts (unbounded rAF retry)

Important Files Changed

Filename Overview
apps/web/lib/terminal/terminal-busy-registry.ts New module-level busy-state registry with PROMPT_TAIL regex; well-tested edge cases (blank lines, heredoc) are handled correctly and the SSR guard is in place.
apps/web/components/task/use-terminal-busy-tracking.ts New hook wires xterm input/output to the registry; rAF retry handles late-attach correctly, though the loop is unbounded if xtermRef.current never resolves while mounted.
apps/web/components/task/terminal-tab.tsx Dockview X-button close path is correctly intercepted via closeActionOverride; destroyAndClosePanel marks the panel before calling props.api.close() to prevent the fallback double-stop. The testid attachment via querySelector(".dv-default-tab-action") is coupled to dockview's internal CSS class.
apps/web/components/task/close-terminal-confirm-dialog.tsx Shared confirm dialog extracted cleanly; void onConfirm() silently swallows async rejections, leaving the dialog open without any user feedback on failure.
apps/web/components/task/task-right-panel.tsx Logic split into useTaskRightPanel hook and useConfirmableTerminalClose; confirmation and immediate-destroy paths correctly delegate to destroyTerminal or the original handleCloseTab.
apps/web/components/task/mobile/mobile-terminals-section.tsx Migrated to shared CloseTerminalConfirmDialog and shouldConfirmTerminalClose; taskId is now threaded correctly to stopUserShell for audit purposes.
apps/web/components/task/dockview-layout-setup.ts Fallback handleTerminalPanelClosed now always calls stopUserShell and removes the shell from the store; park path removed; terminate-mark skip mechanism preserved.
apps/web/hooks/domains/session/use-terminals.ts Removed parkUserShell path from useCloseTab; ordinary terminals now go through destroyUserShell on close, consistent with the new PR policy.
apps/web/e2e/tests/terminal/terminal-dockview-ui.spec.ts E2E test correctly inverted: now asserts the closed terminal does NOT reappear in the + menu (destroy) instead of asserting it does (park).

Sequence Diagram

sequenceDiagram
    participant U as User
    participant DV as TerminalTab (dockview X)
    participant RP as Right-Panel Strip X
    participant MB as Mobile Picker X
    participant Reg as terminal-busy-registry
    participant API as user-shell-api
    participant Store as App Store
    participant DL as dockview-layout-setup

    U->>DV: click X
    DV->>Reg: "shouldConfirmTerminalClose(id, {kind})"
    alt busy or script
        Reg-->>DV: true
        DV->>U: show CloseTerminalConfirmDialog
        U->>DV: confirm
    else idle
        Reg-->>DV: false
    end
    DV->>API: destroyUserShell(env, id)
    API-->>DV: ok
    DV->>Store: removeUserShell(env, id)
    DV->>DL: markTerminalPanelTerminateClose(panelId)
    DV->>DL: props.api.close() → onDidRemovePanel
    DL->>DL: sees terminate mark → skip stopUserShell

    U->>RP: click X
    RP->>Reg: "shouldConfirmTerminalClose(id, {type,kind})"
    alt busy or script
        Reg-->>RP: true
        RP->>U: show CloseTerminalConfirmDialog
        U->>RP: confirm
        RP->>API: destroyTerminal(id)
    else idle
        Reg-->>RP: false
        RP->>API: destroyUserShell(env, id)
    end
    API-->>RP: ok
    RP->>Store: removeUserShell + removeTerminal

    U->>MB: click X
    MB->>Reg: "shouldConfirmTerminalClose(id, {type,kind})"
    alt busy or script
        Reg-->>MB: true
        MB->>U: show CloseTerminalConfirmDialog
        U->>MB: confirm
    else idle
        Reg-->>MB: false
    end
    MB->>API: stopUserShell(env, id)
    API-->>MB: ok
    MB->>Store: removeTerminal
Loading

Reviews (7): Last reviewed commit: "fix(web): tighten prompt regex bare-sigi..." | Re-trigger Greptile

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts
Comment thread apps/web/components/task/terminal-tab.tsx Outdated
Comment thread apps/web/components/task/use-terminal-busy-tracking.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 11 files

You’re at about 92% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Comment thread apps/web/components/task/mobile/mobile-terminals-section.tsx Outdated
Comment thread apps/web/components/task/terminal-tab.tsx Outdated
Thread taskId through mobile destroy, unify script/busy confirm gating,
fix blank-line busy false-positive, and harden busy-tracking attach.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 20s —— View job


Code Review

  • Gather context and understand the PR
  • Read code review skill instructions
  • Read changed files
  • Review terminal-busy-registry.ts and its test
  • Review use-terminal-busy-tracking.ts
  • Review close-terminal-confirm-dialog.tsx
  • Review terminal-tab.tsx
  • Review task-right-panel.tsx
  • Review mobile-terminals-section.tsx
  • Review use-terminals.ts, dockview-layout-setup.ts, passthrough-terminal.tsx
  • Post inline comments and summary
  • Final update

Summary

All issues from the first review round were addressed in e1a08ac. Two new findings from this pass:

Blocker: use-terminal-busy-tracking.ts has no *.test.ts — the hook's subscription setup, RAF retry, and cleanup path are untested. Add a renderHook-based test before merging.

Suggestion: initialCommand in TerminalCloseConfirmOpts is unused dead code — no caller passes it and shouldConfirmTerminalClose never reads it.

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Comment thread apps/web/components/task/use-terminal-busy-tracking.ts
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Findings

All issues flagged in the first review round have been addressed in `e1a08ac` (empty-line false-positive, script-terminal detection, mobile `taskId`, SSR guard, `beforeEach` leak, exhaustive-deps comment, blank terminal name). Two remaining items from this pass:

Blocker (must fix before merge)

Missing tests for new logic

  1. No test file for use-terminal-busy-tracking.tsapps/web/components/task/use-terminal-busy-tracking.ts:11
    • Issue: New hook has no *.test.ts. The registry functions are tested, but the hook's own behaviour (subscription setup, RAF retry when xtermRef.current is initially null, cleanup: RAF cancel + disposable dispose + clearTerminalBusy) is not.
    • Why: Convention requires *.test.ts for new hooks (apps/web/AGENTS.md). The RAF-retry path and cleanup are non-trivial and easy to regress silently.
    • Fix: Add use-terminal-busy-tracking.test.ts with renderHook covering: subscriptions register when enabled+ready, cleanup disposes + clears, RAF fires when ref is null on first run, no-op when enabled=false.

Suggestion (recommended, doesn't block)

  1. Dead initialCommand field in TerminalCloseConfirmOptsapps/web/lib/terminal/terminal-busy-registry.ts:21
    • Issue: The field is exported in the type but shouldConfirmTerminalClose never reads it, and no caller passes it.
    • Fix: Remove initialCommand?: string from the type (see inline suggestion).

Summary

Severity Count
Blocker 1
Suggestion 1

Verdict: Blocked — add the missing hook test, then ready to merge.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 7 files (changes from recent commits).

You’re at about 93% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/web/components/task/terminal-tab.tsx Outdated
Comment thread apps/web/components/task/mobile/mobile-terminals-section.tsx
Use initialCommand in confirm gating, pass taskId to useUserShells on
mobile, and add use-terminal-busy-tracking hook tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 5m 57s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Read changed files
  • Review terminal-busy-registry.ts and tests
  • Review use-terminal-busy-tracking.ts and tests
  • Review close-terminal-confirm-dialog.tsx
  • Review terminal-tab.tsx
  • Review task-right-panel.tsx
  • Review mobile-terminals-section.tsx
  • Review use-terminals.ts, dockview-layout-setup.ts, passthrough-terminal.tsx
  • Post inline comments and summary

Findings

All issues from the prior two review rounds are resolved. Three new findings from this pass:

Suggestion (recommended, doesn't block)

  1. Dialog dismissed before async destroy settlesapps/web/components/task/task-right-panel.tsx:225

    • handleConfirmClose calls void destroyTerminal(...) then setPendingClose(null) synchronously. If the API call fails, the dialog is already gone and the user sees no feedback — the terminal tab correctly stays on-screen, but there's no visible error.
    • Fix: await destroyTerminal(...) before clearing pendingClose (inline comment has a suggestion block).
  2. Stale comment references removed parkUserShellapps/web/hooks/domains/session/use-terminals.ts:189

    • The RemoveTerminalOpts JSDoc still says parkUserShell(...).then(removeTerminal). Since park was replaced by destroy in this PR, it should read destroyUserShell(...).
    • Fix: Update the one-liner example in the JSDoc.
  3. File length at 599/600 linesapps/web/components/task/task-right-panel.tsx:524

    • ESLint enforces a hard 600-line cap. The file is one line from the limit; the next PR touching this file will fail CI if it adds even one line.
    • Fix: Extract TerminalTabContents or CommandsTabContent to a sibling file before the next change.

Summary

Severity Count
Suggestion 3

Verdict: Ready to merge. No blockers — all prior review findings were addressed. The three suggestions above are polish; the file-length one (#3) is worth acting on soon to avoid a future CI surprise.

Comment thread apps/web/components/task/task-right-panel.tsx Outdated
Comment thread apps/web/hooks/domains/session/use-terminals.ts
Comment thread apps/web/components/task/task-right-panel.tsx
Keep confirm dialog open until destroy settles, update stale park doc,
and extract right-panel tab contents to stay under file line cap.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 10s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Read changed files (latest commit: 1682bb1)
  • Review terminal-busy-registry.ts and tests
  • Review use-terminal-busy-tracking.ts and its new test file
  • Review close-terminal-confirm-dialog.tsx
  • Review terminal-tab.tsx
  • Review task-right-panel.tsx and task-right-panel-tab-contents.tsx
  • Review mobile-terminals-section.tsx
  • Review use-terminals.ts, dockview-layout-setup.ts, passthrough-terminal.tsx
  • Post inline comments and summary

All prior findings from rounds 1–3 were addressed in e1a08ac, 138aba5, and 1682bb1. One new suggestion this pass: the right-panel strip and mobile close paths don't pass initialCommand to shouldConfirmTerminalClose, so legacy passthrough shells identified only by initialCommand skip the busy dialog on those two paths (dockview X button correctly handles it). In practice the gap is narrow — shellToTerminal already maps type: "script" via the terminalId.startsWith("script-") check. Ready to merge.

Comment thread apps/web/components/task/task-right-panel.tsx Outdated
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Findings

All findings from the prior three review rounds have been addressed in `1682bb1`. One new suggestion from this pass:

Suggestion (recommended, doesn't block)

  1. initialCommand not threaded into the right-panel and mobile close paths — `task-right-panel.tsx:211` / `mobile-terminals-section.tsx:195`
    • Issue: `shouldConfirmTerminalClose` checks `opts.initialCommand` as a fallback for legacy passthrough shells (those with an `initialCommand` but no `kind === "script"` and no `"script-"` terminalId prefix). The dockview X button passes this from the store's `UserShellInfo` directly. The right-panel strip and mobile paths use the `Terminal` type, which drops `initialCommand` in `shellToTerminal`.
    • Impact: During the legacy-shell transition window, a shell identified only by `initialCommand` gets a confirmation on dockview close but not on right-panel strip or mobile close.
    • Why it's narrow: `shellToTerminal` already maps `type: "script"` when `terminalId.startsWith("script-")`, which covers the bulk of real-world script shells. The gap only affects the corner case where the backend returned `initialCommand` without the `"script-"` prefix.
    • Fix: Add `initialCommand?: string` to the `Terminal` type in `use-terminals-types.ts`, populate it from `shell.initialCommand` in `shellToTerminal`, then pass it in both `useConfirmableTerminalClose` and `useTerminalCloseHandler`.

Summary

Severity Count
Blocker 0
Suggestion 1

Verdict: Ready to merge. The suggestion above is a narrow edge case affecting legacy shells during the transition period; it doesn't block shipping.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/components/task/dockview-layout-setup.ts (1)

350-369: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix tab-close JSDoc to match actual API behavior
File: apps/web/components/task/dockview-layout-setup.ts (lines 350-369)

  • stopUserShell is an alias of destroyUserShell (both call user_shell.destroy, with a fallback to user_shell.stop on unknown action), so the close-path semantic mismatch claim doesn’t hold.
  • The JSDoc “DB row removed” is not unconditionally true when the backend falls back to user_shell.stop; adjust the wording to reflect the fallback (e.g., “destroy terminal; backend may fall back to stop”).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/task/dockview-layout-setup.ts` around lines 350 - 369,
Update the JSDoc above handleTerminalPanelClosed to accurately reflect actual
API behavior: note that stopUserShell is an alias of destroyUserShell (both
delegate to user_shell.destroy and may fall back to user_shell.stop), so change
the comment from “DB row removed” to something like “destroys terminal; backend
may fall back to stop (DB row removal not guaranteed)”, and clarify that the
handler skips already-terminated panels via terminalTerminateClosePanelIds; keep
references to stopUserShell/destroyUserShell/user_shell.destroy/user_shell.stop
so readers can see the exact behavior.
🧹 Nitpick comments (3)
apps/web/lib/terminal/terminal-busy-registry.ts (1)

42-47: ⚡ Quick win

Short-circuit markTerminalOutput when the terminal is already idle.

onWriteParsed fires on every parsed write, so this runs bufferLooksAtPrompt (buffer read + stripAnsi + regex) on every output frame — including high-throughput streams like build logs — even when the terminal is not busy. The only meaningful transition here is busy→idle, so skipping the prompt check while idle avoids work on the I/O hot path with no behavior change.

⚡ Proposed optimization
 export function markTerminalOutput(terminalId: string, terminal: Terminal): void {
   if (typeof window === "undefined") return;
+  if (!busyByTerminalId.get(terminalId)) return;
   if (bufferLooksAtPrompt(terminal)) {
     busyByTerminalId.set(terminalId, false);
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/lib/terminal/terminal-busy-registry.ts` around lines 42 - 47,
markTerminalOutput runs bufferLooksAtPrompt on every onWriteParsed event causing
unnecessary work; add an early-return when the terminal is already marked idle
to avoid re-checking prompts. Specifically, in markTerminalOutput check
busyByTerminalId for terminalId (e.g., busyByTerminalId.get(terminalId) ===
false or !busyByTerminalId.has/true) and return immediately if not busy,
otherwise proceed to call bufferLooksAtPrompt and set the map to false on
transition; this ensures only busy→idle transitions invoke the expensive
buffer/regex work.
apps/web/hooks/domains/session/use-terminals.ts (1)

286-306: ⚡ Quick win

useCloseTab duplicates useManagedTerminalActions.destroyTerminal.

Both now perform the exact same sequence — destroyUserShell(env, id, taskID) → removeUserShellStore(env, id) → removeTerminal(id) — differing only in promise-chain vs async/await style. Consider extracting a shared destroyAndRemove(environmentId, terminalId, taskID) helper so the close-tab and destroy paths can't drift apart.

♻️ Sketch
function destroyAndRemove(
  environmentId: string,
  terminalId: string,
  taskID: string | null,
  removeUserShellStore: (e: string, t: string) => void,
  removeTerminal: (id: string) => void,
): Promise<void> {
  return destroyUserShell(environmentId, terminalId, taskID ?? undefined)
    .then(() => {
      removeUserShellStore(environmentId, terminalId);
      removeTerminal(terminalId);
    })
    .catch((error) => console.error("Failed to destroy terminal:", error));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/hooks/domains/session/use-terminals.ts` around lines 286 - 306, Both
useCloseTab and useManagedTerminalActions.destroyTerminal duplicate the same
destroy/remove sequence; extract a shared async helper like
destroyAndRemove(environmentId, terminalId, taskID, removeUserShellStore,
removeTerminal) that calls destroyUserShell(environmentId, terminalId, taskID ??
undefined) and then invokes removeUserShellStore(environmentId, terminalId) and
removeTerminal(terminalId) and logs errors, then replace the inline promise
chain in useCloseTab and the async/await path in
useManagedTerminalActions.destroyTerminal to call this helper to keep logic
centralized and avoid drift.
apps/web/components/task/close-terminal-confirm-dialog.tsx (1)

42-50: 💤 Low value

Dialog dismisses before onConfirm resolves.

onOpenChange(false) runs synchronously and the dialog closes immediately, while onConfirm() (an async destroy in every call site) is fire-and-forget. The PR notes a final adjustment to "keep the confirm dialog open until destroy completes," but this component dismisses first. If that behavior is intended, consider awaiting/loading state here; otherwise the note and code diverge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/task/close-terminal-confirm-dialog.tsx` around lines 42 -
50, The confirm action currently calls onOpenChange(false) before onConfirm(),
causing the dialog to close immediately; change the click handler on
AlertDialogAction to await the async onConfirm() and only call
onOpenChange(false) after it resolves (or handle errors), and add a temporary
disabled/loading state on the AlertDialogAction while onConfirm is in progress
so users can't re-submit; update the handler referenced (the AlertDialogAction
onClick that calls onOpenChange and onConfirm) to perform async await, set a
local loading flag, disable the button during the await, and then close the
dialog when finished.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/web/components/task/dockview-layout-setup.ts`:
- Around line 350-369: Update the JSDoc above handleTerminalPanelClosed to
accurately reflect actual API behavior: note that stopUserShell is an alias of
destroyUserShell (both delegate to user_shell.destroy and may fall back to
user_shell.stop), so change the comment from “DB row removed” to something like
“destroys terminal; backend may fall back to stop (DB row removal not
guaranteed)”, and clarify that the handler skips already-terminated panels via
terminalTerminateClosePanelIds; keep references to
stopUserShell/destroyUserShell/user_shell.destroy/user_shell.stop so readers can
see the exact behavior.

---

Nitpick comments:
In `@apps/web/components/task/close-terminal-confirm-dialog.tsx`:
- Around line 42-50: The confirm action currently calls onOpenChange(false)
before onConfirm(), causing the dialog to close immediately; change the click
handler on AlertDialogAction to await the async onConfirm() and only call
onOpenChange(false) after it resolves (or handle errors), and add a temporary
disabled/loading state on the AlertDialogAction while onConfirm is in progress
so users can't re-submit; update the handler referenced (the AlertDialogAction
onClick that calls onOpenChange and onConfirm) to perform async await, set a
local loading flag, disable the button during the await, and then close the
dialog when finished.

In `@apps/web/hooks/domains/session/use-terminals.ts`:
- Around line 286-306: Both useCloseTab and
useManagedTerminalActions.destroyTerminal duplicate the same destroy/remove
sequence; extract a shared async helper like destroyAndRemove(environmentId,
terminalId, taskID, removeUserShellStore, removeTerminal) that calls
destroyUserShell(environmentId, terminalId, taskID ?? undefined) and then
invokes removeUserShellStore(environmentId, terminalId) and
removeTerminal(terminalId) and logs errors, then replace the inline promise
chain in useCloseTab and the async/await path in
useManagedTerminalActions.destroyTerminal to call this helper to keep logic
centralized and avoid drift.

In `@apps/web/lib/terminal/terminal-busy-registry.ts`:
- Around line 42-47: markTerminalOutput runs bufferLooksAtPrompt on every
onWriteParsed event causing unnecessary work; add an early-return when the
terminal is already marked idle to avoid re-checking prompts. Specifically, in
markTerminalOutput check busyByTerminalId for terminalId (e.g.,
busyByTerminalId.get(terminalId) === false or !busyByTerminalId.has/true) and
return immediately if not busy, otherwise proceed to call bufferLooksAtPrompt
and set the map to false on transition; this ensures only busy→idle transitions
invoke the expensive buffer/regex work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5026b40e-2585-435c-b129-53b27b7978c1

📥 Commits

Reviewing files that changed from the base of the PR and between 1931619 and 1682bb1.

📒 Files selected for processing (13)
  • apps/web/components/task/close-terminal-confirm-dialog.tsx
  • apps/web/components/task/dockview-layout-setup.ts
  • apps/web/components/task/mobile/mobile-terminals-section.tsx
  • apps/web/components/task/passthrough-terminal.tsx
  • apps/web/components/task/task-right-panel-tab-contents.tsx
  • apps/web/components/task/task-right-panel.tsx
  • apps/web/components/task/terminal-tab.tsx
  • apps/web/components/task/use-terminal-busy-tracking.test.ts
  • apps/web/components/task/use-terminal-busy-tracking.ts
  • apps/web/e2e/tests/terminal/terminal-dockview-ui.spec.ts
  • apps/web/hooks/domains/session/use-terminals.ts
  • apps/web/lib/terminal/terminal-busy-registry.test.ts
  • apps/web/lib/terminal/terminal-busy-registry.ts

Extend Terminal with initialCommand from UserShellInfo so right-panel
and mobile close paths confirm before destroying legacy script shells.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 2m 57s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Review changed files (latest commit: ee84b72)
  • Post inline comments and summary

Findings

All prior blockers from rounds 1–4 are fully resolved. The ee84b72 commit correctly threads initialCommand from UserShellInfoTerminal type → shellToTerminal builder → both the right-panel and mobile shouldConfirmTerminalClose call sites, closing the legacy-shell gap identified in round 4.

Suggestion (recommended, doesn't block)

  1. markTerminalOutput runs bufferLooksAtPrompt on every write even when idleterminal-busy-registry.ts:44
    • Issue: onWriteParsed fires for every parsed terminal frame. The function re-reads the xterm buffer, strips ANSI, and runs a regex even when the terminal has never received Enter (busy=false, i.e., the default). The only meaningful transition is busy→idle.
    • Why: On high-throughput output (build logs, npm install, make) this is called hundreds of times per second doing work that cannot produce a state change.
    • Fix: add an early-return guard (if (!busyByTerminalId.get(terminalId)) return;) — inline suggestion posted.

Summary

Severity Count
Suggestion 1

Verdict: Ready to merge. No blockers remain. The single suggestion is a minor perf optimization on the output hot path; it doesn't affect correctness.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files (changes from recent commits).

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/web/components/task/task-right-panel.tsx Outdated
Comment thread apps/web/hooks/domains/session/use-terminals-types.ts Outdated
Comment thread apps/web/lib/terminal/terminal-busy-registry.ts
Drop initialCommand from confirm gating so ordinary shells with cd
metadata close idle without a dialog; short-circuit busy output checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 23s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Review changed files (latest commit: 2f82c70)
  • Post inline comments and summary

Findings

All issues from the prior five review rounds have been addressed in ee84b72 and 2f82c70. One lingering suggestion this pass:

Suggestion (recommended, doesn't block)

  1. Dialog dismisses before async destroy settlesclose-terminal-confirm-dialog.tsx:43
    • onOpenChange(false) fires synchronously before onConfirm() in the confirm click handler; React 18 batches and flushes setPendingClose(null) at end of the event frame, closing the dialog while destroy is still in-flight. The handleConfirmClose fix (await before clearing pendingClose) was the right intent but is raced by this call.
    • Impact: no visible feedback on a failed destroy (tab stays visible so retry works, but nothing signals the failure).
    • Fix: remove onOpenChange(false) from the confirm onClick and let the parent's onConfirm be the sole close gate. Inline suggestion posted.

Summary

Severity Count
Blocker 0
Suggestion 1

Verdict: Ready to merge. 2f82c70 correctly removes initialCommand from all confirm-gating paths (ordinary shells with cd metadata no longer show spurious dialogs) and adds the idle short-circuit in markTerminalOutput. Architecture, logic, tests, and complexity limits all look good.

Comment thread apps/web/components/task/close-terminal-confirm-dialog.tsx Outdated
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Findings

All issues from the prior five review rounds have been addressed. One lingering suggestion from this pass:

Suggestion (recommended, doesn't block)

  1. Dialog dismisses before async destroy settlesapps/web/components/task/close-terminal-confirm-dialog.tsx:43
    • Issue: onOpenChange(false) is called synchronously before onConfirm() in the confirm button's click handler. React 18 batches the resulting setPendingClose(null) dispatch and flushes it at the end of the synchronous event-handler frame — before destroyTerminal resolves. The dialog closes while the destroy is still in-flight. The parent-level fix (handleConfirmClose awaiting before clearing pendingClose) was the right intent, but the onOpenChange(false) call here races it.
    • Impact: On a failed destroy, the user sees no feedback — the dialog is gone, the terminal tab correctly stays visible (so retry is possible), but nothing indicates the close failed. Not a correctness bug.
    • Fix: Remove onOpenChange(false) from the confirm button's onClick and let each call site's onConfirm handler (which awaits and then clears pendingClose) be the sole gate on closing. The onOpenChange prop continues to handle Cancel and backdrop clicks. Inline suggestion posted.

Summary

Severity Count
Blocker 0
Suggestion 1

Verdict: Ready to merge. The 2f82c70 commit correctly removes initialCommand from all confirm-gating paths (preventing ordinary shells with cd metadata from showing spurious dialogs) and adds the idle short-circuit in markTerminalOutput. All architecture, logic, tests, and complexity limits look good.

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Prevent AlertDialogAction from closing early; exclude bash heredoc PS2
prompt from idle detection so busy shells still warn on close.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 22s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Review latest commit changes (97e5998)
  • Post inline comments and summary

Findings

All issues from the prior six review rounds have been addressed. Two observations from this pass:

Suggestion (recommended, doesn't block)

  1. Anchored regex regresses on prompts with special prefix charactersapps/web/lib/terminal/terminal-busy-registry.ts:15

    The new ^(?:...)$ anchor fixes the heredoc > false-positive but introduces a false-negative for prompts that start with characters outside [\w.@~:/-]. Common examples:

    • (venv) $ (Python virtualenv)
    • (py3.10) user@host:~/proj$
    • [user@host ~/proj]$ (fish/custom PS1)

    For these, bufferLooksAtPrompt returns false even when the cursor is sitting at a shell prompt, so markTerminalOutput never clears the busy flag. The consequence is that the close confirmation dialog appears even for idle terminals in these prompt styles — an annoyance but not a data loss issue.

    The trade-off is correct (heredoc false-positives are worse), but a wider bare-prefix branch could handle both:

    const PROMPT_TAIL = /^(?:.*\s)?(?:[$#%]|[\w.@~:/-]*[@~/][\w.@~:/-]*[$#>%])\s*$/;
    

    This allows any preceding text separated by whitespace, while still requiring the sigil to be preceded by at least a space (preventing logger.go:42 or Building... 50% from matching), and > still requires a path-token anchor.

    Note: the regex above still requires a test for the (venv) $ case to be added before landing.

Note (no change needed)

  1. Right-panel handleConfirmClose always closes dialog even on API failureapps/web/components/task/task-right-panel.tsx:226

    destroyTerminal in useManagedTerminalActions catches errors internally and never re-throws, so handleConfirmClose's await destroyTerminal(...) always resolves — the subsequent setPendingClose(null) clears the dialog regardless of success or failure. On failure the tab stays visible (since removeTerminal isn't called), but the user receives no feedback that the close failed. This is pre-existing behaviour and inconsistent with the dockview and mobile paths which keep the dialog open on failure. Not introduced by this commit.


Summary

Severity Count
Blocker 0
Suggestion 1

Verdict: Ready to merge. The 97e5998 commit correctly fixes both issues from round 6 — the event.preventDefault() approach reliably keeps the dialog open during destroy (Radix's composeEventHandlers skips auto-close when defaultPrevented is set), and the tightened PROMPT_TAIL regex resolves the heredoc false-positive with regression tests. The only new finding is a niche prompt-style edge case that doesn't block shipping.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Allow optional leading context before prompt sigils so virtualenv and
fish-style PS1 strings clear busy state without reopening heredoc gaps.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 4m 50s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Review latest commit changes (b180179)
  • Review terminal-busy-registry.ts (PROMPT_TAIL regex fix)
  • Review all other changed files
  • Post inline comments and summary

Findings

All issues from the prior seven review rounds are resolved. b180179 correctly fixes the prefixed-prompt regression introduced in 97e5998: the anchored regex that broke (venv) $ and [user@host ~/proj]$ is replaced with ^(?:.*\s)?(?:[$#%]|...), and three new regression tests confirm the fix. Heredoc > still correctly excluded.

Note (no change needed)

  1. Relaxed prefix can false-positive on output ending with bare sigilsterminal-busy-registry.ts:14

    The new ^(?:.*\s)?(?:[$#%]|...) structure matches any line where at least one space precedes $, #, or %. The old fully-anchored regex only matched bare sigils at line-start. Concretely, "foo $" or "result #" on the cursor line during a running command would now incorrectly clear the busy flag and suppress the close confirmation. The trade-off (fixing prefixed-prompt false-negatives) is the right call — this edge case is far less likely than a virtualenv prompt. Inline comment posted for awareness; no fix required.


Summary

Severity Count
Blocker 0
Note 1

Verdict: Ready to merge. The b180179 commit closes the last open finding (prefixed prompt false-negatives) with correct tests. All architecture, logic, complexity limits, and test coverage are sound.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

You’re at about 94% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Comment thread apps/web/lib/terminal/terminal-busy-registry.ts Outdated
Only treat $/#/% as prompts at line start or after env markers, not
when arbitrary output ends with a sigil after whitespace.

Co-authored-by: Cursor <cursoragent@cursor.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 31, 2026

Claude finished @carlosflorencio's task in 2m 19s —— View job


Code Review

  • Gather context and understand the PR
  • Read code-review skill instructions
  • Review latest commit changes (d25aae0)
  • Review terminal-busy-registry.ts (PROMPT_TAIL regex final fix)
  • Post inline comments and summary

Findings

All issues from the prior eight review rounds are resolved. d25aae0 correctly addresses the final suggestion from round 8 — the relaxed (?:.*\s)? prefix in the previous regex could false-positive on output lines like "foo $" (where a space precedes a bare sigil mid-line). The new regex splits the two use cases into separate branches:

  • Alt 1 (bare sigils): (?:\([^)\n]*\)\s+|\s*)[$#%] — matches $, #, % only at line start or after a parenthesized env marker like (venv) . No arbitrary prefix text.
  • Alt 2 (path-attached): (?:.*\s)?[\w.@~:/-]*[@~/][\w.@~:/-]*\]?[$#>%] — allows arbitrary prefix text but requires @, ~, or / in the path token. > only matches here, so bare heredoc > is correctly excluded.

Verified all documented cases:

Line Matches? Expected
$ idle
$ idle
(venv) $ idle
user@host:~/proj$ idle
[user@host ~/proj]$ idle
> busy (heredoc)
foo $ busy (output, not prompt)
result (5 items) $ busy (parenthesized output fragment)

The regression test for "foo $" is included and correctly verifies the fix.

Summary

Severity Count
Blocker 0
Suggestion 0

Verdict: Ready to merge. All eight prior review rounds have been fully addressed. Architecture, logic, tests, complexity limits, and regex correctness are all sound.

@carlosflorencio carlosflorencio merged commit d8e0507 into main May 31, 2026
46 checks passed
@carlosflorencio carlosflorencio deleted the feature/stop-terminal-on-clo-o8m branch May 31, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant