Add CLI and deeplinks for running arbitrary user-defined scripts#253
Merged
Add CLI and deeplinks for running arbitrary user-defined scripts#253
Conversation
Introduces sub-path deeplinks that target a specific user-defined script by UUID, so CLI tooling and share URLs can run or stop scripts other than the primary .run-kind one. - Add .runScript(scriptID:) and .stopScript(scriptID:) cases to Deeplink.WorktreeAction and a parseWorktreeScript helper that matches worktree/<id>/script/<uuid>/run|stop. - Route the new actions in worktreeActionEffect through existing runNamedScript / stopScript reducer paths. Unknown script UUIDs surface an alert rather than silently dropping. - Reuse requiresInputConfirmation for URL-scheme run requests so script commands display in the confirmation sheet under the existing automatedActionPolicy gate. Stop requests are not gated, matching the bare .stop behaviour. - Extend parser and reducer test suites covering happy paths, unknown UUIDs, missing verb, and policy bypass.
Wires the earlier runScript/stopScript deeplinks to actual CLI tooling and adds the socket query needed to discover script UUIDs. - Add "scripts" socket query resource keyed on worktreeID that returns each ScriptDefinition as id / kind / name / displayName plus a running marker derived from runningScriptsByWorktreeID. - Extend `supacode worktree run` and `stop` with `--script/-s` so callers can target a specific script by UUID while the zero-argument form keeps firing the bare run / stop deeplinks for backward compatibility. - Introduce `supacode worktree script list` (via a sibling WorktreeScriptCommand to satisfy SwiftLint nesting) that prints tab-separated id / kind / displayName rows and underlines any currently running script. - Validate `--script` arguments as UUIDs at the CLI layer so a typo surfaces a helpful error instead of a generic deeplink parse failure.
Review follow-ups covering cross-worktree correctness, silent success paths, the `-s` flag collision, and in-app docs. - Resolve the target script directly from the worktree's @SharedReader RepositorySettings in runScript/stopScript deeplink handlers so cross-worktree CLI calls no longer miss a freshly loaded selection's scripts. - Validate empty command, already-running, and not-running cases inline in the deeplink effects and surface them as alerts. Previously these hit silent `.none` guards in runNamedScript / stopScript and misled the CLI into ok:true. - Normalize trailing slashes in the `scripts` socket query so `supacode worktree script list -w <path>` matches worktree IDs with or without a trailing slash, matching resolveWorktreeID. - Rename the CLI short flag from `-s` to `-c` under `worktree run`/`stop` so it stops colliding with `-s` = `--surface` across the rest of the CLI. Long form `--script` is unchanged. - Update CLIReferenceView, DeeplinkReferenceView, and the supacode-cli skill content with the new subcommand, option, and deeplink URLs. - Extend the test suite with confirm-accepted round trip, socket-source responseFD storage, empty command, already running, and not-running-stop cases, and rewrite the existing script deeplink tests to seed scripts via @shared repository settings now that the reducer reads them from there.
Phase 3 verification caught a narrow but real silent-success path in the script deeplink effects: if the target worktree is removed between the initial `handleWorktreeDeeplink` check and the confirmation-accept re-dispatch, both runScript and stopScript handlers would return `.none` without setting an alert, so the outer response flow would report `ok:true` to the CLI even though nothing ran. - Set a "Worktree not found" alert in the worktree-missing guard of both runScriptDeeplinkEffect and stopScriptDeeplinkEffect so the alert-diff check drives the socket response to ok:false. - Extract a shared worktreeNotFoundAlert helper. - Add stopScriptSocketDeeplinkSendsErrorWhenNotRunning as a regression guard exercising the socket responseFD path for stop failures, which had no coverage.
Final polish pass after Phase 3 self-review. - Collapse the inline "Worktree not found" AlertState block in handleWorktreeDeeplink into the worktreeNotFoundAlert helper introduced for the script deeplink effects. - Sanitize tab, newline, and carriage return characters in the `worktree script list` output so a script name containing those characters cannot corrupt the tab-separated columns when the output is piped into xargs/cut/awk. - Expand CLISkillContent's shared/codex sections to document the new `worktree script list` subcommand, the `-c` `--script` flag, and the tab-separated list output format so agents reading the skill pick up the new surface.
onevcat
referenced
this pull request
in onevcat/Prowl
Apr 20, 2026
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.
Summary
Recent commit #246 introduced multiple user-defined scripts per repository, but the CLI and deeplink surface still only knew how to run/stop the primary
.run-kind script. This PR extends both so any configured script can be run or stopped by UUID.supacode://worktree/<wt-id>/script/<script-uuid>/run|stopURLs, routed through new.runScript(scriptID:)/.stopScript(scriptID:)cases onDeeplink.WorktreeAction. Bare/runand/stopkeep their existing semantics.runScriptDeeplinkEffect/stopScriptDeeplinkEffectresolve the script via@SharedReader(.repositorySettings(...))so cross-worktree calls don't depend on the currently selected worktree. Unknown UUID, empty command, already-running, not-running, and missing-worktree all surface user-facing alerts so the CLI socket response reportsok:falseinstead of silent success..runScriptgoes through the existingrequiresInputConfirmationgate with.command(script.command);.stopScriptis ungated, matching bare/stop."scripts"resource keyed onworktreeID(tolerant of trailing slash), returning{id, kind, name, displayName, running}rows.--script/-c <uuid>option onworktree run/stop, plus a newworktree script listsubcommand.-cwas chosen over-sto avoid colliding with--surfaceacross the rest of the CLI. UUIDs are validated up front so typos fail fast.CLIReferenceView,DeeplinkReferenceView, and thesupacode-cliskill content all reflect the new surface.Test plan
supacode worktree run -c <uuid>— confirmation sheet appears under default policy; dispatches whenautomatedActionPolicyallows bypass.supacode worktree stop -c <uuid>— no confirmation prompt, script tab closes.supacode worktree script listprints tab-separated rows; running rows are ANSI-underlined.supacode://worktree/<id>/runandsupacode://worktree/<id>/stopstill behave like before (primary run-kind / all run-kind).-wpointing at a worktree in a different repo than the currently selected one) runs the correct script.-cexits non-zero with a helpful error before hitting the socket.make testor the targeted suitesAppFeatureDeeplinkTests/DeeplinkClientTests/AppFeatureRunScriptTests.