Add multiple user-defined scripts per repository (#246)
* Add ScriptKind, ScriptDefinition, and refactor BlockingScriptKind
Introduce the foundational data model for multiple running scripts:
- ScriptKind enum with predefined types (run, debug, test, deploy,
custom), each carrying default icon, color, and name.
- ScriptDefinition struct with id, kind, name, systemImage, tintColor,
and command fields.
- Move TerminalTabTintColor to SupacodeSettingsShared and extend with
blue, purple, yellow, and teal cases for new script types.
- Refactor BlockingScriptKind: replace .run with .script(ScriptDefinition)
to support multiple concurrent user-defined scripts while keeping
.archive and .delete as lifecycle-bound cases.
- Add scripts and selectedScriptID fields to RepositorySettings with
backward-compatible migration from the legacy runScript field.
- Update all downstream switch sites, terminal state tracking, and tests.
* Add keyboard shortcuts for test, debug, and deploy scripts
Register testScript (Cmd+Shift+U), debugScript (Cmd+Shift+Y), and
deployScript (Cmd+Shift+D) in AppShortcutID with stable keys, display
names, and Ghostty unbind support.
Add corresponding menu items in WorktreeCommands and wire focused
value actions through WorktreeDetailView. Actions are currently nil
placeholders — they will be connected to AppFeature in a later commit.
* Add scripts settings table and expandable repo sidebar
Split repository settings into General and Scripts sub-sections via
expandable DisclosureGroup in the sidebar. The Scripts section shows a
list-based editor where each row has a ScriptKind picker, name field,
and monospaced command field with add/remove/reorder support.
Remove the old single "Run Script" ScriptSection from the General
settings — lifecycle scripts (setup, archive, delete) stay as-is.
Add reducer tests for addScript, removeScripts, and moveScripts
actions covering edge cases (remove middle, move to end, move to
beginning).
* Add split-button toolbar and command palette for scripts
Replace RunScriptToolbarButton with ScriptSplitButton: primary button
runs the selected script, chevron dropdown lists all scripts with
per-type icons and run/stop per-script. Cmd+. only stops .run kind.
Rewrite AppFeature script state: replace selectedRunScript/runScriptDraft
with scripts array and selectedScriptID. Wire testScript/debugScript/
deployScript actions to run the first matching script kind.
Add command palette entries: "Run: <name>" and "Stop: <name>" generated
dynamically from repository scripts. Selected script shows Cmd+R badge.
Replace runScriptWorktreeIDs with runningScriptsByWorktreeID dictionary
for per-definition-ID tracking. Remove RunScriptPromptView — scripts
are now configured via Settings.
* Add color-cycling sidebar indicator for running scripts
Replace the single-color boolean PingDot with MultiColorPingDot that
cycles through tint colors of all running script types per worktree.
Uses TimelineView for smooth color transitions and falls back to a
static dot when accessibilityReduceMotion is enabled.
Add scriptTintColorByID cache to RepositoriesFeature.State for
efficient color lookup without passing full ScriptDefinition arrays
through the view hierarchy. Cache is maintained alongside
runningScriptsByWorktreeID on script start, completion, and pruning.
* Fix review findings: encode compat, decode resilience, and cleanup
- Add custom encode(to:) deriving runScript from first .run script
for backward compatibility with older app versions.
- Guard against running the same script definition twice concurrently.
- Navigate to repositoryScripts settings (not General) when no scripts
are configured and user presses Cmd+R.
- Use try? for scripts decode so unknown ScriptKind values from future
versions fall back gracefully instead of crashing.
- Remove selectedScriptID entirely — primary toolbar button always
runs the first .run-kind script, matching the "Open In" pattern.
- Enforce at most one script per predefined kind in settings (only
.custom allows duplicates).
- Add Codable migration tests: legacy decode, dual-key decode,
encode round-trip, and unknown-kind resilience.
* Add lint/format script types, lock kind at creation, displayName
Add .lint and .format cases to ScriptKind with dedicated SF Symbols
and colors. Add displayName computed property to ScriptDefinition:
predefined types use their kind name, custom types use user name.
Lock script kind at creation: the + button opens a Menu showing only
unused predefined kinds plus always-available custom. Kind picker
replaced with static icon in script rows. Uniqueness enforced at
insertion in addScript(ScriptKind). Move TerminalTabTintColor.color
to shared module so SupacodeSettingsFeature can access it.
Add lintScript/formatScript keyboard shortcuts (Cmd+Shift+L/F),
menu items, and focused value wiring.
* Restructure scripts settings, toolbar labels, and cleanup
Remove debug script kind. Add resolvedSystemImage/resolvedTintColor
to ScriptDefinition so non-custom scripts always derive visuals from
ScriptKind defaults rather than persisted values.
Add Image.tintedSymbol helper for colored SF Symbols in macOS menus
(auto-resolves .fill variants via AppKit palette colors). Use tinted
icons in toolbar script dropdown, plain icons in menu bar.
Hide toolbar dropdown chevron when only a .run script is configured.
Fix DisclosureGroup: auto-expands on selection, allows manual collapse.
Navigate to Scripts settings tab (not General) from toolbar and Cmd+R.
Move lifecycle scripts to Scripts tab. Remove non-run/stop keyboard
shortcuts entirely (deferred to future iteration). Extend Makefile
and swiftlint to cover SupacodeSettingsShared/SupacodeSettingsFeature.
* Fix decode resilience, dedup, tests, and cleanup
* Fix opening brace lint violations in persistence extensions
* Fix identity, kind defaults, and dead code cleanup
* Remove tint color cache, fix displayName, encode fallback, DRY
- Remove `scriptTintColorByID` side-cache from RepositoriesFeature;
resolve colors from current script definitions via environment.
- Use `displayName` in BlockingScriptKind.tabTitle and settings header.
- Encode `runScript` as empty string when no .run scripts exist.
- Distinguish absent vs corrupted `scripts` key in lossy decoder.
- Document `runScript` as legacy backward-compat field.
- Extract duplicated `shortcutDisplay` into shared free function.
- Add test for encode fallback with no .run-kind script.
- Update RepositorySettingsKeyTests to use setupScript for round-trips.
* Fix cross-repo indicator bug, DRY violations, and runScript safety
- Fix sidebar indicator disappearing for scripts running in non-selected
repositories by falling back to .green when script ID lookup fails
- Refactor .binding case to reuse persistAndNotify, removing duplication
- Extract scriptButton helper in ScriptSplitButton to DRY three branches
- Extract LifecycleScriptSection in RepositoryScriptsSettingsView
- Make runScript property private(set) to prevent accidental direct mutation
- Clarify stopRunScripts intent with expanded doc comments
* Optimize script color lookup, add duplicate-run test, fix help text dots
- Pre-compute scriptsByID dictionary once in environment instead of
rebuilding per sidebar row in runningScriptColors
- Add test verifying duplicate runNamedScript is silently rejected
- Add trailing dots to help text strings for convention consistency
* Add confirmation dialog for script deletion
Show an alert before removing a user-defined script in repository
settings. Also extract SettingsView subviews to fix type-checker
timeout caused by the new @Presents alert state.
* Toggle disclosure on tap when repository is already selected
* Replace split buttons with Menu primaryAction for open and script toolbars
* Show menu indicator on open and script toolbar menus
* Populate supacode repo-specific scripts
* Address review findings from argue-review debate
- Extract duplicate isExpanded Binding to local let in SettingsView
- Update toolbar placeholder to match live ScriptMenu (play instead of play.fill, Label instead of HStack)
- Guard against empty shortcut display in resolveShortcutDisplay
- Use fallback label in ScriptMenu when shortcut resolves to empty
- Use stroke-only stop icon consistently (stop instead of stop.fill)
- Make Divider conditional on non-empty scripts in ScriptMenu dropdown
- Fix doc comments for ScriptMenu and primaryScript
authored by