Skip to content

Command execution: run Snips with a live terminal + clean history (v1.0.0)#51

Merged
StuartMeeks merged 9 commits into
mainfrom
feature/command-execution
Jun 8, 2026
Merged

Command execution: run Snips with a live terminal + clean history (v1.0.0)#51
StuartMeeks merged 9 commits into
mainfrom
feature/command-execution

Conversation

@StuartMeeks

Copy link
Copy Markdown
Owner

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

  • Run action beside Copy → reuses the parameter-fill flow, then a dry-run preview
    (resolved command + shell + working directory) before anything executes.
  • Live interactive terminal — runs under a real pseudo-terminal (ConPTY via
    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.
  • Clean history — every run records a clean plain-text transcript (no ANSI, no
    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 runs
    newest-first with search; open one to replay in colour or run again.
  • Per-CLI execution config — shell (cmd / PowerShell / pwsh / bash / custom),
    optional executable path and working directory; per-Snip overrides. Validated at Run
    time, never on save.
  • Settings — run-history retention per Snip and max captured output size.
  • Acknowledgements in Settings → About and the README.

Architecture

New Snipdeck.Execution project (net10.0, UI-free, references Core only): VT→plain-text
processor, 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
IRunCoordinator hook. The WinUI head wires it up.

Verification

  • 240 Core + 61 Execution unit tests green on Linux (incl. the VT cleanliness contract).
  • Whole solution compiles clean on the Windows build agent.
  • Manually verified on Windows: run / live fidelity / interactive prompts / clean
    history / cancel / run-again / process-exit detection / Ctrl+C interrupt.

Release

version.json is bumped to 1.0.0. After merge, tagging v1.0.0 triggers the release
workflow (Velopack build + GitHub release).

🤖 Generated with Claude Code

StuartMeeks and others added 9 commits June 8, 2026 02:15
… 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>
@StuartMeeks StuartMeeks merged commit b743d4c into main Jun 8, 2026
6 checks passed
@StuartMeeks StuartMeeks deleted the feature/command-execution branch June 8, 2026 03:55
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