From 2a8cfce9521aa7eb8d885aa9c3ef5d65f8f907a9 Mon Sep 17 00:00:00 2001 From: Stuart Meeks Date: Sat, 30 May 2026 00:32:45 +0000 Subject: [PATCH 1/3] Add TODO entry for executing Snips with per-CLI shell and run history MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Snipdeck today only constructs commands and copies them. The user wants to also execute them: each CLI declaring its shell (cmd / PowerShell / pwsh / bash), live output streaming into the Snipdeck chrome, and a searchable per-Snip execution history. Captures the sketch, the storage trade-off (the existing "one JSON document" rule was scoped to definitions, not arbitrary observation data — so SQLite specifically for execution history is worth a real conversation), and the open questions (working dir, env vars, safety confirmation, ANSI handling, large-output capping). Co-Authored-By: Claude Opus 4.7 --- TODO.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/TODO.md b/TODO.md index 22a171c..b7681e0 100644 --- a/TODO.md +++ b/TODO.md @@ -53,6 +53,108 @@ This is the single biggest content-quality-of-life feature after v1 ships. --- +## Execute Snips, not just construct them (with per-CLI shell + execution history) + +**Problem.** Today Snipdeck is a sophisticated clipboard — it builds the command +and you paste it into your own terminal. That round-trip discards the most +valuable artefact of running a CLI: the *output*. There's no way to ask "what +did this Snip return last time I ran it against staging?", to diff two runs, +or to even tell whether the previous invocation succeeded. + +**Idea.** Let Snipdeck *execute* a Snip in its configured shell, stream the +output into a Snipdeck panel as it runs, and persist each execution against +the Snip so the user accumulates a searchable run history. + +**Sketch.** + +- **Per-CLI shell declaration.** `Cli` gains a `Shell` field (enum: + `Cmd` / `PowerShell` / `PwshCore` / `Bash` / `Custom`) and an optional + `CustomShellPath` + `CustomShellArgsTemplate` for the escape hatch. + Default for new CLIs is whatever the platform considers canonical + (`PowerShell` on Windows). A Snip can override its CLI's shell when needed. +- **A Run action alongside Copy.** Card overflow gains **Run**. Clicking it + walks the same parameter-fill flow as Copy, but on submit we spawn the + configured shell with the resolved command instead of copying to the + clipboard. The parameter-fill dialog is replaced or extended with a + **dry-run preview** of the exact command line plus a final "Run" button — + arbitrary execution is a footgun and the user must see the resolved + string once before it runs. +- **Live output panel.** Output streams into a dedicated content pane — + probably a new content state alongside `HomeViewModel` / `CliViewModel` — + with stdout and stderr distinguished (colour, or two lanes), an exit-code + badge once the process completes, an elapsed timer, and a **Cancel** button + that kills the process group. Lines arrive via `IDispatcher` so we don't + mutate UI state off-thread (this fix's lesson, applied early). +- **Execution history per Snip.** Each run captures: `SnipId`, + `ResolvedCommand`, `StartedAt`, `FinishedAt`, `ExitCode`, + `Cancelled`, `Stdout`, `Stderr` (or an interleaved stream with timestamps), + and the parameter values that were used. New entries append to a per-Snip + history log. +- **Searchable history.** A new "History" view (probably a pane footer entry, + next to Settings) lists executions across all Snips, newest-first, with + full-text search over the captured output and command line. Clicking an + entry opens the run in the same output panel as a live run — just with the + Cancel button disabled and a "Run again" button enabled. + +**Storage — the awkward bit.** + +The current JSON-store rule (`CLAUDE.md`: "not SQLite, not LiteDB — the data +is small") was made on the assumption of snippet definitions. **Execution +output is not small.** A single `kubectl describe pod` can be tens of KB, and +a power user might rack up thousands of runs. + +Options to weigh when this is scheduled: + +- **Per-Snip history file** (e.g. `/history/.jsonl`, + append-only JSONL, one line per run). Keeps the main snip store small and + fast to load. Pruning is per-file. Cross-Snip search needs to walk all + files — feasible up to maybe ~10k files; degrades after. +- **Per-CLI history file**. Compromise; smaller fan-out, still naturally + partitioned, but a single hot CLI's history can grow unbounded. +- **Bring in SQLite specifically for executions** (NOT for the snip store). + Best query/search story, opens the door to `LIKE` / `MATCH` over output. + Departs from the "one JSON document" principle — but the principle was + scoped to *definitions*, not arbitrary observation data. Worth a real + conversation when this gets picked up; I (Claude) lean toward this option + for any non-trivial history feature. + +Retention defaults probably want to be "last N runs per Snip" (configurable), +matching the existing backup-retention shape. + +**Open questions** to settle when scheduled: + +- **Working directory.** Per-CLI? Per-Snip override? Inherit from Snipdeck's + cwd? Likely per-CLI with per-Snip override, defaulting to the user's home. +- **Environment variables.** Some CLIs need env (auth tokens, region pins). + Probably a per-CLI key/value list, masked in the UI (touches the parked + "secret parameters" decision). +- **Safety / confirmation.** First-run confirmation per Snip? An allow-list? + A "this Snip has been edited since you last ran it" warning? Worth getting + right — Snipdeck running an unreviewed `rm -rf` is a category of incident + we don't want to ship. +- **Cancellation semantics.** `Process.Kill(entireProcessTree: true)` covers + most shells. PowerShell can be sticky; document and test. +- **Cross-platform shells.** `cmd` and `PowerShell` are Windows-only; + `pwsh` is cross-platform; `bash` on Windows means WSL. The enum needs to + encode both the shell *and* its launcher. +- **Large outputs.** Cap captured-per-run size (configurable; default ~5 MB?) + and truncate with a marker. The UI panel can spool to disk for the live + view if needed. +- **ANSI / colour.** `IShell` should pass output through verbatim; the panel + needs an ANSI escape parser to render colours and clear-line sequences. + Avalonia / WinUI both lack a built-in terminal control — either pull a + community one or render to a `RichTextBlock` with the ANSI stripped / + interpreted. +- **Streaming-to-history vs. capture-then-write.** Stream-to-history (write + each chunk as it arrives) means a crashed Snipdeck still leaves a useful + partial log. Capture-then-write is simpler. Probably stream. + +This is a *significant* expansion of Snipdeck's surface area — it crosses +from "snippet manager" into "lightweight runbook executor". Worth doing, +worth doing carefully, and worth a design conversation before the first PR. + +--- + ## Carried over from the phase stack These were trimmed out of Phase 4–6 to keep the PRs reviewable. None are From dc13153211166ab7d5d606ec3c28f3ed489a9d29 Mon Sep 17 00:00:00 2001 From: Stuart Meeks Date: Sat, 30 May 2026 00:33:57 +0000 Subject: [PATCH 2/3] Capture optional executable path and working directory on Cli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A CLI's executable location is useful execution context but must not be mandatory — Snipdeck has to remain a sensible place to author Snips for a CLI that isn't installed yet on this machine. Document that the field is optional, validated only at Run time, and that the working directory defaults sensibly off the executable when set. Drops the working-directory open question since it's now part of the sketch. Co-Authored-By: Claude Opus 4.7 --- TODO.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index b7681e0..28c8743 100644 --- a/TODO.md +++ b/TODO.md @@ -72,6 +72,24 @@ the Snip so the user accumulates a searchable run history. `CustomShellPath` + `CustomShellArgsTemplate` for the escape hatch. Default for new CLIs is whatever the platform considers canonical (`PowerShell` on Windows). A Snip can override its CLI's shell when needed. +- **Per-CLI executable path and working directory.** `Cli` gains optional + `ExecutablePath` and `WorkingDirectory` fields. The CLI editor dialog + gains a **Browse…** for the executable (re-using `IFilePickerService`) + and another for the working directory. Both are **optional** — Snipdeck + must remain useful as a place to author Snips for a CLI that isn't + installed yet on this machine, or one the user will install later. So: + - No validation at save time. An empty `ExecutablePath` is the default + and is fine. + - When the field *is* set, validation happens **only at Run time**: if + the path is missing, the Run action shows a friendly "couldn't find + `` — has it been installed? edit the CLI to fix the path" rather + than throwing. Copy still works unconditionally. + - `WorkingDirectory` defaults at runtime to the executable's parent + directory when `ExecutablePath` is set, otherwise the user's home + directory. A Snip can override the CLI's working directory. + - The executable path is not currently used by Copy — but it's worth + storing now because the user might reasonably expect "show me where + `pl-app` lives" as a small affordance even before Run lands. - **A Run action alongside Copy.** Card overflow gains **Run**. Clicking it walks the same parameter-fill flow as Copy, but on submit we spawn the configured shell with the resolved command instead of copying to the @@ -123,8 +141,6 @@ matching the existing backup-retention shape. **Open questions** to settle when scheduled: -- **Working directory.** Per-CLI? Per-Snip override? Inherit from Snipdeck's - cwd? Likely per-CLI with per-Snip override, defaulting to the user's home. - **Environment variables.** Some CLIs need env (auth tokens, region pins). Probably a per-CLI key/value list, masked in the UI (touches the parked "secret parameters" decision). From c492037cdb734464209b60a22cb2d8e35eb89cb4 Mon Sep 17 00:00:00 2001 From: Stuart Meeks Date: Sat, 30 May 2026 00:36:54 +0000 Subject: [PATCH 3/3] Capture per-CLI env vars and labelled executions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The user explicitly wants: - Custom environment variables per CLI (with Snip-level override). Non-secret values stored in the JSON document verbatim; secret values stored via Windows DPAPI (current-user scope). This flips the parked 'secret / masked parameters' item to 'needed' and accepts the cross-machine-sync trade-off (you re-enter secrets per machine — the alternative is plaintext in a synced document, which is wrong). - Free-form labels on each execution, e.g. support ticket IDs. Labels are searchable from the history view, settable in the Run dialog, editable post-hoc on past runs, and — the productivity unlock — pinnable from the shell so every Run during an incident is auto-tagged. Updates the execution-history record to include Labels and drops the environment-variables open question since it's now part of the sketch. Co-Authored-By: Claude Opus 4.7 --- TODO.md | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 28c8743..69261c7 100644 --- a/TODO.md +++ b/TODO.md @@ -90,6 +90,37 @@ the Snip so the user accumulates a searchable run history. - The executable path is not currently used by Copy — but it's worth storing now because the user might reasonably expect "show me where `pl-app` lives" as a small affordance even before Run lands. +- **Per-CLI environment variables.** `Cli` gains an `EnvironmentVariables` + collection of `(Name, Value, IsSecret)` entries; a Snip can add or + override entries on top of its CLI's set. The child process inherits the + OS environment and gets these merged on top — they're additions / + overrides, not a replacement. + - Non-secret values are stored verbatim in the JSON store and shown in + the editor in cleartext. + - Secret values flip the parked "secret / masked parameters" item from + `CLAUDE.md` from "park" to "needed". Store them via Windows DPAPI + (`ProtectedData.Protect`, current-user scope) so the ciphertext is + bound to the local Windows account. The editor masks the value behind + a "Show" toggle and never logs it. + - Trade-off: DPAPI-protected values won't survive a cross-machine sync + of the data folder — the user would need to re-enter secrets on each + machine. That's the right shape; the alternative (plaintext secrets + in a synced JSON document) is the wrong one. +- **Labelled executions.** Each run can carry a free-form list of `Labels` + — short strings like `INC-4567`, `staging-rollback`, `pr-1234-debug`. + Labels are surfaced as chips on the history list and are fully searchable + (`label:INC-4567` filter, or just free-text). The Run dialog has a small + "labels" input next to the resolved-command preview; the history view + has an inline editor so the user can label a past run after the fact. + - **Sticky labels.** A small affordance in the shell (probably a chip in + the title bar) lets the user *pin* one or more labels for the current + investigation. While pinned, every Run pre-fills with those labels. + Clearing the pin reverts to the empty default. This is the + productivity multiplier — during an incident, the user pins + `INC-4567` once and every subsequent Run is automatically tagged. + - Labels are *not* tags on the Snip itself — they belong to the + execution record. A single Snip will accumulate runs labelled with + many different incident / ticket / deployment identifiers over time. - **A Run action alongside Copy.** Card overflow gains **Run**. Clicking it walks the same parameter-fill flow as Copy, but on submit we spawn the configured shell with the resolved command instead of copying to the @@ -106,8 +137,8 @@ the Snip so the user accumulates a searchable run history. - **Execution history per Snip.** Each run captures: `SnipId`, `ResolvedCommand`, `StartedAt`, `FinishedAt`, `ExitCode`, `Cancelled`, `Stdout`, `Stderr` (or an interleaved stream with timestamps), - and the parameter values that were used. New entries append to a per-Snip - history log. + the parameter values that were used, and the `Labels` the run was + tagged with. New entries append to a per-Snip history log. - **Searchable history.** A new "History" view (probably a pane footer entry, next to Settings) lists executions across all Snips, newest-first, with full-text search over the captured output and command line. Clicking an @@ -141,9 +172,6 @@ matching the existing backup-retention shape. **Open questions** to settle when scheduled: -- **Environment variables.** Some CLIs need env (auth tokens, region pins). - Probably a per-CLI key/value list, masked in the UI (touches the parked - "secret parameters" decision). - **Safety / confirmation.** First-run confirmation per Snip? An allow-list? A "this Snip has been edited since you last ran it" warning? Worth getting right — Snipdeck running an unreviewed `rm -rf` is a category of incident