Skip to content

Add CLI and deeplinks for running arbitrary user-defined scripts#253

Merged
sbertix merged 5 commits intomainfrom
sbertix/scripts-cli
Apr 18, 2026
Merged

Add CLI and deeplinks for running arbitrary user-defined scripts#253
sbertix merged 5 commits intomainfrom
sbertix/scripts-cli

Conversation

@sbertix
Copy link
Copy Markdown
Collaborator

@sbertix sbertix commented Apr 18, 2026

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.

  • Deeplink: new supacode://worktree/<wt-id>/script/<script-uuid>/run|stop URLs, routed through new .runScript(scriptID:) / .stopScript(scriptID:) cases on Deeplink.WorktreeAction. Bare /run and /stop keep their existing semantics.
  • Reducer: runScriptDeeplinkEffect / stopScriptDeeplinkEffect resolve 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 reports ok:false instead of silent success.
  • Confirmation: URL-scheme .runScript goes through the existing requiresInputConfirmation gate with .command(script.command); .stopScript is ungated, matching bare /stop.
  • Socket query: new "scripts" resource keyed on worktreeID (tolerant of trailing slash), returning {id, kind, name, displayName, running} rows.
  • CLI: --script/-c <uuid> option on worktree run / stop, plus a new worktree script list subcommand. -c was chosen over -s to avoid colliding with --surface across the rest of the CLI. UUIDs are validated up front so typos fail fast.
  • Docs: CLIReferenceView, DeeplinkReferenceView, and the supacode-cli skill content all reflect the new surface.
  • Tests: parser (6 new), reducer (11 new covering confirmation, policy bypass, socket responseFD, alerts for each failure mode).

Test plan

  • Run an arbitrary test/lint/deploy script from the CLI against the current worktree: supacode worktree run -c <uuid> — confirmation sheet appears under default policy; dispatches when automatedActionPolicy allows bypass.
  • Stop a specific running script: supacode worktree stop -c <uuid> — no confirmation prompt, script tab closes.
  • supacode worktree script list prints tab-separated rows; running rows are ANSI-underlined.
  • Bare supacode://worktree/<id>/run and supacode://worktree/<id>/stop still behave like before (primary run-kind / all run-kind).
  • Cross-worktree deeplink from CLI (-w pointing at a worktree in a different repo than the currently selected one) runs the correct script.
  • Invalid UUID via -c exits non-zero with a helpful error before hitting the socket.
  • CLI exits non-zero with a surfaced error when: script UUID unknown, command empty, script already running, script not running for stop, worktree vanishes.
  • Unit tests: make test or the targeted suites AppFeatureDeeplinkTests / DeeplinkClientTests / AppFeatureRunScriptTests.

sbertix added 5 commits April 18, 2026 20:15
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.
@sbertix sbertix enabled auto-merge (squash) April 18, 2026 18:56
@tuist
Copy link
Copy Markdown

tuist Bot commented Apr 18, 2026

🛠️ Tuist Run Report 🛠️

Builds 🔨

Scheme Status Duration Commit
supacode 39.6s 0a08b128a

@sbertix sbertix merged commit 788dcff into main Apr 18, 2026
2 checks passed
@sbertix sbertix deleted the sbertix/scripts-cli branch April 18, 2026 19:00
onevcat referenced this pull request in onevcat/Prowl Apr 20, 2026
Reviewed 47 commits between v0.8.0 and v0.8.1. Recorded skip decisions
for Tuist migration, upstream CLI (#227/#253/#246), and Ghostty opacity
toggle (#225) so the next sync has explicit precedent.
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