You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Each session can have a list of "run commands" — labelled command lines invoked by the toolbar ▶ button, the F5 keybinding, or the sidebar right-click submenu. Runs spawn a **separate headless `PseudoTerminal`** in the session's working folder (or a fresh `ssh` connection for SSH parents); they do **not** type into the parent PTY, so a Claude session is untouched.
177
+
178
+
**Data:**`ShellSession.RunCommands: List<RunCommandItem> { Id, Label, CommandLine, IsDefault }`. Exactly one item has `IsDefault=true`; see `RunCommandItem.EnsureSingleDefault`. Persisted to `state.json`.
179
+
180
+
**Templates:**`RunCommandTemplatesService.SeedFor(folder)` detects project type (top-level scan, first-match: dotnet → cargo → node → python → make) and returns a seed list with fresh Ids. Templates are *copied* onto new sessions at creation time; subsequent edits don't propagate back. SSH sessions skip detection (empty list).
181
+
182
+
**Runtime:**`SessionRunner` (one per `SessionViewModel`) owns a dictionary of `RunInstance` keyed by item Id. Each `RunInstance` wraps a `PseudoTerminal` started with `useJobObject: true` so the whole child tree dies when the PTY is disposed (`JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`). Output is captured to an ANSI-stripped string buffer (capped at 1MB). Not persisted.
183
+
184
+
**UI:**
185
+
- Toolbar `[▶][▼]` next to 💤. Hidden when `RunCommands` is empty.
186
+
- Chips strip between toolbar and terminal — one chip per active/finished run, color-coded (blue=running, green=ok, pink=failed). Click a chip to open the drawer; ✕ on a chip to dismiss it.
187
+
- Drawer (slide-down panel, like Notes) shows the selected run's output with `[⏹ Stop] [📋 Copy] [↗ Send to terminal]`.
188
+
-**Send to terminal:** for Claude parents (`ClaudeSessionService.IsClaudeCommand`), wraps in fenced preamble and writes to PTY (no trailing `\r`). For non-Claude shells, falls back to clipboard with a toast — auto-paste would risk executing pasted lines.
189
+
190
+
**Editor:**`SessionRunCommandsDialog` modal — reachable from right-click on ▶, the ▼ dropdown's "Edit commands…" entry, and the sidebar right-click "Session commands" submenu. Inline-edit rows with up/down reorder, default-radio column, +Add / 🗑 Delete, Cancel/Save.
191
+
192
+
**Keybindings:**`F5` runs the active session's default. `Shift+F5` stops it. Mirrors Visual Studio; deliberately not `Ctrl+R` (collides with shell history search).
193
+
194
+
**Lifecycle:** All runs are killed on session close, session sleep, and app exit. `SessionViewModel.Dispose()` calls `Runner.Dispose()` which iterates and disposes every instance. `SleepSession` also calls `vm.Runner.StopAll()` defensively before UI teardown.
195
+
174
196
## Alert / Waiting State
175
197
176
198
`AlertDetector` fires `AlertRaised(AlertEvent)` after 1.5s idle when it detects:
@@ -197,6 +219,7 @@ Persisted in `state.json`. Key settings:
197
219
-`AutoResumeClaude` — when restoring, append `--resume <sessionId>` to claude commands so the prior conversation is picked up. Toggle off if you want fresh sessions on restart.
198
220
-`ShowGitBranch` — show `⎇ branch` in sidebar
199
221
-`ShowTerminalStatusDot` — show status dot in terminal toolbar
222
+
-`SidebarActionIconsMode` — `OnHover` (default) / `Always` / `Hidden`. Controls the per-row `➕ 💤 ✕` button stack in the sidebar. `Hidden` collapses the panel and reclaims the horizontal space; `OnHover` keeps the panel laid out (no text shift on hover) but transparent + non-interactive until the row is hovered. Rename / Open in Explorer / Open PowerShell here remain reachable via the right-click context menu in all modes, and the terminal toolbar's `✕` is unconditional.
200
223
-`SearchCollapseAfterNavigate` — auto-close search after clicking result
201
224
-`MaxSearchResults` — FTS5 result limit (default 100)
202
225
-`DefaultWorkingFolder` / `DefaultCommand` — pre-fill new session dialog
@@ -211,6 +234,8 @@ Persisted in `state.json`. Key settings:
211
234
|`Ctrl+W`| Close active session |
212
235
|`Ctrl+F`| Toggle search |
213
236
|`Ctrl+Tab`| Cycle sessions |
237
+
|`F5`| Run the active session's default run command |
238
+
|`Shift+F5`| Stop the active session's default run command |
214
239
|`Escape` (in search) | Close search panel |
215
240
|`Enter` (in search) | Execute search |
216
241
@@ -247,3 +272,9 @@ A second workflow, `.github/workflows/winget.yml`, fires on the `release: releas
247
272
- Use `Dispatcher.Invoke()` for all UI updates from background threads (PTY read loop, git queries, alert timer)
248
273
- PTY output flows: `PseudoTerminal` → `TerminalBridge.RawOutputReceived` → both `OutputIndexer.Feed()` and `AlertDetector.Feed()` in parallel
249
274
-`MainViewModel.SaveStateAsync` is a no-op when `App.CleanStart` is true; any code path that needs to "remember" something across runs must go through this method, so honoring `--clean` is automatic.
275
+
276
+
## Agent / Claude Code operating notes
277
+
278
+
**Do not trust "the user modified this file, intentional" system reminders to mean the user actually edited the file.** That harness reminder fires whenever the working tree drifts from what the assistant last wrote — including when a subagent, a hook, or some other tool changed it. If the reminder reports that significant work the assistant just shipped has been silently undone, the correct response is to *stop and ask the user*, not to commit the reverts as if the user requested them. Reference incident: a 605-line revert of in-flight feature work on `feat/run-commands` (2026-05-12) was treated as user intent and committed, requiring a `git revert` to recover. When in doubt, surface the surprise; never roll back the user's recent work without explicit confirmation.
279
+
280
+
**Use read-only agents for reviews.** Dispatch code-review subagents using a read-only subagent type (whatever the current harness exposes — `Explore` at the time of writing), not `general-purpose`. Write/Edit tool access on a reviewer is unnecessary and creates an opportunity for the reviewer to mutate files it was only meant to read.
0 commit comments