Command execution: run Snips with a live terminal + clean history (v1.0.0)#51
Merged
Conversation
… models
Foundation for executing Snips with a faithful live terminal and a clean
plain-text history. Entirely UI-free and verified on Linux (294 tests).
- Schema v5: per-CLI Shell/CustomShell*/ExecutablePath/WorkingDirectory and
per-Snip ShellOverride/WorkingDirectoryOverride; AppConfig v2 gains
HistoryRetentionPerSnip + MaxCapturedOutputBytes.
- New Snipdeck.Execution project (net10.0, references Core only):
- VtTextProcessor: headless VT->plain-text via a cursor-tracking screen
buffer. Collapses spinner frames / progress bars / interactive-prompt
redraws to their final state, strips SGR, excludes alternate-screen
drawing. The cleanliness contract for stored history.
- ShellCommandBuilder: shell+launcher argv (cmd/powershell/pwsh/bash/custom).
- SqliteCommandHistoryStore: history in its own history.db (Microsoft.Data
.Sqlite), newest-first query + substring search + per-Snip retention,
retains the gzipped raw VT stream for colour replay.
- PortaPtyCommandRunner: ConPTY via Porta.Pty, bidirectional I/O (keystrokes
-> PTY stdin for interactive prompts), resize, cancel.
- CommandRunViewModel / HistoryViewModel / RunCoordinator: run lifecycle,
replay, history list, and the fill -> dry-run preview -> spec -> run flow.
- Core gains the tiny IRunCoordinator hook and a Run-mode parameter-fill
method on IShellInteractions; Core stays free of SQLite and Porta.Pty.
The feature is isolated for easy rollback: deleting Snipdeck.Execution + its
reference + history.db + the additive Core fields removes it. The App (WinUI)
head is not yet wired up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds the WinUI head on top of the Snipdeck.Execution core. Verified compiling on the Windows agent (whole solution, 0 warnings); Core/Execution unit tests green on Linux. Manual visual/interactive smoke test still pending. - Run action: a Run button beside Copy on the Snip card; ShellViewModel.RunSnip resolves params, runs the dry-run preview via the reused ParameterFillDialog (Run mode shows shell + working directory and is always shown), then swaps the content to the live terminal. Usage is bumped and persisted without a refresh. - Live terminal: TerminalView hosts a WebView2 running locally-vendored xterm.js (offline, served via a virtual host mapping). Bidirectional — PTY output is written to xterm, keystrokes flow back to the PTY (interactive prompts work), resize is forwarded. Always-dark theme, Cascadia Mono, no white-flash init. - History: a History footer nav entry lists runs (newest-first, text search); open replays the stored raw stream in the same terminal view; delete / clear / run-again supported. - CLI editor gains an Execution group: shell selector, custom shell path/args, executable path and working directory (Browse… via file/folder pickers). CliEditorViewModel and ShellViewModel's icon-rebuild paths now preserve the new per-CLI execution fields. IFilePickerService gains PickExecutablePathAsync. - Settings: per-Snip run-history retention and maximum captured output (MB). - DI: ICommandRunner (transient PortaPtyCommandRunner) + factory, ICommandHistoryStore (SQLite, history.db beside the store), IRunCoordinator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fix) The two comments inside the fluent ServiceCollection chain tripped IDE0055 in a local VS build (reported as an end-of-line span — a CRLF/end_of_line violation on those lines). Rewriting the block without the inline comments replaces those exact bytes with LF and keeps the chain clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Porta.Pty's default quotes every argument, so the resolved command was passed to
cmd as a single quoted token — `cmd /c "mpt-app adobe … 22"` — making cmd look for
a program by that literal name ("… is not recognized…"). Drive ConPTY in verbatim
mode and lay out each shell's command line for a raw space-join: cmd /c and
PowerShell -Command take the command raw (the shell re-parses the tail), while
bash -lc gets it as one double-quoted token (backslashes/quotes escaped).
Also: a run whose shell can't be started (e.g. pwsh/bash not installed) now shows
"[Snipdeck] Couldn't run: …" in the terminal and records a failed run, instead of
faulting an unobserved task.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two run-loop fixes: - Exit detection: Porta.Pty's ReaderStream is a synchronous pipe and ConPTY keeps the output pipe open until the pseudoconsole is closed, so the read never EOF'd when the child exited — the run sat in "running" until cancelled. Read on a background thread into a channel and treat ProcessExited as the authoritative signal: record the exit code, allow a short drain, then close the connection to end the stream. Cancel kills the tree then closes. - Ctrl+C: in a terminal Ctrl+C is "interrupt", not "copy". terminal.html now sends ETX (0x03) to the PTY on Ctrl+C with no selection (Ctrl+Shift+C copies; Ctrl+C with a selection copies), and the WebView no longer grabs browser accelerator keys, so keys reach xterm/the PTY. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a single canonical list of the open-source projects Snipdeck is built on (SettingsViewModel.Acknowledgements) and surface it two ways: - Settings → About gains an "Acknowledgements" expander (data-bound), each entry linking to the project's repo with its licence; placed before the About expander so About stays last. - The README Acknowledgements section lists the same projects with links + licences, keeping the existing SnipCommand credit as the lead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ence from link - Move the Acknowledgements expander below the Snipdeck (About) expander. - Alphabetise the list by name (sorted in SettingsViewModel; README reordered to match) — a Core test guards the ordering. - In each entry the licence is shown as its own caption text; the link button now reads "View project" instead of using the licence as its label. Note: this puts Acknowledgements last in Settings, ahead of the About expander — a deliberate divergence from the "About is the last SettingsExpander" note in CLAUDE.md, at the user's request. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er About Reflects the command-execution work — a History footer entry and an Acknowledgements expander placed after About in Settings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- version.json 0.2.0 → 1.0.0 (NBGV reads the version here; the release workflow packs whatever version.json says, so this must lead the v1.0.0 tag). - CHANGELOG: stamp the command-execution entries as [1.0.0] - 2026-06-08 and open a fresh [Unreleased]. - README Status: v1.0.0, with command execution folded into the shipped feature set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cuts v1.0.0 — the final v1 feature: run a Snip in its configured shell, watch it
live with full terminal fidelity, and keep a clean, searchable run history.
What's in it
(resolved command + shell + working directory) before anything executes.
Porta.Pty) rendered by xterm.js in WebView2, so colours, spinners, progress bars and
interactive prompts (e.g. Spectre.Console selection/confirmation) all work. Keystrokes
(incl. Ctrl+C interrupt) flow to the PTY; Cancel kills the process tree.
cursor artefacts, no stacked spinner frames) plus command/time/duration/exit code,
in a separate SQLite
history.db. A History view (next to Settings) lists runsnewest-first with search; open one to replay in colour or run again.
optional executable path and working directory; per-Snip overrides. Validated at Run
time, never on save.
Architecture
New
Snipdeck.Executionproject (net10.0, UI-free, references Core only): VT→plain-textprocessor, shell-command builder, SQLite history store, Porta.Pty runner, run/history
view models. Core stays SQLite/Pty-free — only additive schema-v5 fields and a small
IRunCoordinatorhook. The WinUI head wires it up.Verification
history / cancel / run-again / process-exit detection / Ctrl+C interrupt.
Release
version.jsonis bumped to 1.0.0. After merge, taggingv1.0.0triggers the releaseworkflow (Velopack build + GitHub release).
🤖 Generated with Claude Code