Skip to content

Commit d5bbeb8

Browse files
authored
Merge pull request #34 from umage-ai/feat/run-commands
feat: per-session run commands + sidebar action-icon cleanup
2 parents 4ed7d36 + ec08f91 commit d5bbeb8

19 files changed

Lines changed: 4047 additions & 27 deletions

CLAUDE.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,28 @@ Sessions can be put to sleep instead of closed — the PTY is torn down but the
171171
- `OnLoaded` partitions saved sessions: dormant ones go through `AddDormantSidebarItem`; live ones through `LaunchSessionAsync`.
172172
- The empty-state placeholder hides whenever `_vm.Sessions.Count > 0` **or** `_dormantSidebarItems.Count > 0`.
173173

174+
## Per-Session Run Commands
175+
176+
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+
174196
## Alert / Waiting State
175197

176198
`AlertDetector` fires `AlertRaised(AlertEvent)` after 1.5s idle when it detects:
@@ -197,6 +219,7 @@ Persisted in `state.json`. Key settings:
197219
- `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.
198220
- `ShowGitBranch` — show `⎇ branch` in sidebar
199221
- `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.
200223
- `SearchCollapseAfterNavigate` — auto-close search after clicking result
201224
- `MaxSearchResults` — FTS5 result limit (default 100)
202225
- `DefaultWorkingFolder` / `DefaultCommand` — pre-fill new session dialog
@@ -211,6 +234,8 @@ Persisted in `state.json`. Key settings:
211234
| `Ctrl+W` | Close active session |
212235
| `Ctrl+F` | Toggle search |
213236
| `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 |
214239
| `Escape` (in search) | Close search panel |
215240
| `Enter` (in search) | Execute search |
216241

@@ -247,3 +272,9 @@ A second workflow, `.github/workflows/winget.yml`, fires on the `release: releas
247272
- Use `Dispatcher.Invoke()` for all UI updates from background threads (PTY read loop, git queries, alert timer)
248273
- PTY output flows: `PseudoTerminal``TerminalBridge.RawOutputReceived` → both `OutputIndexer.Feed()` and `AlertDetector.Feed()` in parallel
249274
- `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

Comments
 (0)