From b04a573402f8be6801ebb74a234ad9d107a1019e Mon Sep 17 00:00:00 2001 From: locnguyen1842 Date: Mon, 4 May 2026 14:42:18 +0700 Subject: [PATCH 01/15] feat(15-01): remove hardcoded shebang from script generation and parsing - Remove const scriptHeader '#!/bin/bash' from script.go - GenerateScript() returns body without shebang prefix, empty input returns empty string - ParseScriptBody() generically strips any #! prefix line for backward compat - Both old (#!/bin/bash in DB) and new (no shebang) scripts handled transparently --- script.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/script.go b/script.go index c3af261..f2337be 100644 --- a/script.go +++ b/script.go @@ -8,22 +8,27 @@ import ( var templateVarRe = regexp.MustCompile(`\{\{(\w+)\}\}`) -const scriptHeader = "#!/bin/bash" - -// GenerateScript wraps a body in a shebang header. +// GenerateScript returns the script body trimmed of surrounding whitespace with a trailing newline. +// No shebang is prepended — the executor adds a platform-appropriate shebang at execution time. func GenerateScript(body string) string { body = strings.TrimSpace(body) - return scriptHeader + "\n\n" + body + "\n" + if body == "" { + return "" + } + return body + "\n" } -// ParseScriptBody strips the shebang header and returns the user-editable body. +// ParseScriptBody strips any shebang line (#!...) from the beginning of stored script content. +// Handles both old format (scripts stored with #!/bin/bash in the DB) and new format +// (scripts stored without a shebang) transparently. func ParseScriptBody(scriptContent string) string { s := strings.TrimSpace(scriptContent) - if strings.HasPrefix(s, scriptHeader) { - s = strings.TrimPrefix(s, scriptHeader) - s = strings.TrimLeft(s, "\n") + if strings.HasPrefix(s, "#!") { + if idx := strings.Index(s, "\n"); idx != -1 { + s = s[idx+1:] + } } - return s + return strings.TrimSpace(s) } // ExtractTemplateVars returns unique variable names from {{var}} patterns, in order of first appearance. From f706f569c49f699e19bb5038b4e5c300f209f7c5 Mon Sep 17 00:00:00 2001 From: locnguyen1842 Date: Mon, 4 May 2026 14:44:09 +0700 Subject: [PATCH 02/15] feat(15-01): platform-aware shebang injection and fix Unix -lc flag usage - Add platform-appropriate shebang in ExecuteScript: #!/bin/sh on Unix, none on Windows - Strip existing shebangs from stored content before adding platform shebang (backward compat) - Fix Unix execution path to use -lc flag (was previously dropped, cmd ran without login shell) - Add stripShebang helper for generic #! line removal - Both platforms now uniformly use e.flag in exec.CommandContext --- executor.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/executor.go b/executor.go index c9dacd0..2631ffc 100644 --- a/executor.go +++ b/executor.go @@ -97,6 +97,14 @@ type OutputChunk struct { // ExecuteScript runs a resolved script (all {{var}} already replaced) and streams output via callback. func (e *Executor) ExecuteScript(scriptContent string, workingDir string, onChunk func(OutputChunk)) ExecutionResult { + // Strip any existing shebang from stored content (backward compat with old DB records) + scriptContent = stripShebang(scriptContent) + + // Add platform-appropriate shebang at execution time + if runtime.GOOS != "windows" { + scriptContent = "#!/bin/sh\n" + scriptContent + } + tmpPath, err := writeTempScript(scriptContent) if err != nil { return ExecutionResult{Error: err.Error(), ExitCode: -1} @@ -107,13 +115,8 @@ func (e *Executor) ExecuteScript(scriptContent string, workingDir string, onChun defer cancel() var cmd *exec.Cmd - if runtime.GOOS == "windows" { - // Windows: cmd /C tmp.bat - cmd = exec.CommandContext(ctx, e.shell, e.flag, tmpPath) - } else { - // Unix: shell can execute the temp script file directly. - cmd = exec.CommandContext(ctx, e.shell, tmpPath) - } + // Both platforms use e.flag: /C on Windows, -lc on Unix + cmd = exec.CommandContext(ctx, e.shell, e.flag, tmpPath) if workingDir != "" { cmd.Dir = workingDir } @@ -200,6 +203,18 @@ func (e *Executor) ExecuteScript(scriptContent string, workingDir string, onChun return result } +// stripShebang removes any shebang line (#!...) from the beginning of script content. +// Used for backward compatibility with old DB records that stored scripts with #!/bin/bash. +func stripShebang(content string) string { + s := strings.TrimSpace(content) + if strings.HasPrefix(s, "#!") { + if idx := strings.Index(s, "\n"); idx != -1 { + return s[idx+1:] + } + } + return s +} + // OpenInTerminal opens a terminal and runs the resolved script. // Each LaunchFn receives the raw script body and handles its own quoting. func (e *Executor) OpenInTerminal(terminalID string, scriptContent string, workingDir string) error { From eae0f743020a3d7f898fbced22bd3ebe88c5e9b0 Mon Sep 17 00:00:00 2001 From: locnguyen1842 Date: Mon, 4 May 2026 14:50:27 +0700 Subject: [PATCH 03/15] docs(15-01): complete centralize shebang handling plan - Remove hardcoded #!/bin/bash from stored scripts - Platform-aware shebang injection at execution time (#!/bin/sh on Unix, none on Windows) - Fix Unix -lc flag usage for login shell - Generic #! stripping for backward compat with old DB records - No changes needed to execution_service.go (callers transparently compatible) --- .planning/ROADMAP.md | 16 ++- .planning/STATE.md | 128 ++++++++++++++++++ .../15-01-SUMMARY.md | 79 +++++++++++ 3 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 .planning/STATE.md create mode 100644 .planning/phases/15-cross-platform-execution/15-01-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index cae8fc3..cfeacf7 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -6,7 +6,8 @@ - ✅ **v1.1 Build Settings Window** — Phases 6-7 (shipped) - ✅ **v1.2 DB Migration Refactor** — Phases 8-9 (shipped) - ✅ **v1.3 Working Directory** — Phases 10-13 (shipped 2026-04-23) -- ✅ **v1.4 Editor Multi-Mount Refactor** — Phases 14 (shipped 2026-04-23) +- ✅ **v1.4 Editor Multi-Mount Refactor** — Phase 14 (shipped 2026-04-23) +- 📋 **v1.5 Cross-Platform Execution** — Phase 15 (in progress) - 📋 **v2.0 Workspaces** — Phases (planned) ## Phases @@ -152,6 +153,16 @@ Plans: +### Phase 15: Cross-Platform Execution + +**Goal:** Centralize command execution to work cross-platform by removing the hardcoded `#!/bin/bash` shebang from stored scripts and making the executor responsible for platform-appropriate shebang injection at runtime. +**Plans:** 3 plans + +Plans: +- [x] 15-01: Centralize shebang handling (script.go + executor.go core) +- [ ] 15-02: Fix display commands and terminal execution +- [ ] 15-03: Tests and verification + ### 📋 v2.0 Workspaces (Planned) **Milestone Goal:** Named project contexts with sidebar switcher, cloud sync, and command sharing. @@ -182,4 +193,5 @@ Phases execute in numeric order: 10 → 11 → 12 → 13 → 14 | 11. Execution Engine & Directory Picker | v1.3 | 3/3 | Complete | 2026-04-23 | | 12. Settings UI | v1.3 | 3/3 | Complete | 2026-04-23 | | 13. Command Editor & List UI | v1.3 | 3/3 | Complete | 2026-04-23 | -| 14. Editor Multi-Mount Refactor | v1.4 | 3/3 | Complete | 2026-04-23 | \ No newline at end of file +| 14. Editor Multi-Mount Refactor | v1.4 | 3/3 | Complete | 2026-04-23 | +| 15. Cross-Platform Execution | v1.5 | 1/3 | In Progress | — | \ No newline at end of file diff --git a/.planning/STATE.md b/.planning/STATE.md new file mode 100644 index 0000000..fbf2463 --- /dev/null +++ b/.planning/STATE.md @@ -0,0 +1,128 @@ +--- +gsd_state_version: 1.0 +phase: 15-cross-platform-execution +plan: 2 of 3 +milestone: v1.5 +milestone_name: Cross-Platform Execution +status: executing +last_updated: "2026-05-04T07:45:22Z" +last_activity: 2026-05-04 -- Plan 15-01 completed +progress: + total_phases: 1 + completed_phases: 0 + total_plans: 3 + completed_plans: 1 + percent: 33 +--- + +# Project State + +## Project Reference + +See: .planning/PROJECT.md (updated 2026-04-23) + +**Core value:** Users can organize commands by project context, execute with variables, and share +**Current focus:** Phase 15-cross-platform-execution — 15 + +## Current Position + +Phase: 15-cross-platform-execution (15) — EXECUTING +Plan: 2 of 3 +Milestone: v1.5 Cross-Platform Execution +Status: Executing Phase 15-cross-platform-execution +Last activity: 2026-05-04 -- Plan 15-01 completed + +Progress: [███░░░░░░░] 33% + +## Performance Metrics + +**Velocity:** + +- Total plans completed: 21 +- Total execution time: ~90 minutes +- Timeline: 4 days + +## By Phase: + +| Phase | Plans | Avg/Plan | +|-------|-------|----------| +| 1. Layout Overhaul | 3/3 | ~7 min | +| 2. Editor & Interactions | 2/2 | ~7.5 min | +| 3. Theme Engine | 3/3 | ~7 min | +| 4. Theme Customization | 2/2 | ~11.5 min | +| 5. Import & Export | 2/2 | ~7.5 min | +| 6. Wails Window Migration | 2/2 | — | +| 7. Settings Window Polish | 2/2 | — | +| 8. Migration Package | 2/2 | — | +| 9. Runner Integration | 2/2 | — | +| 10. Data Foundation | 3/3 | — | +| 11. Execution Engine & Directory Picker | 3/3 | — | +| 12. Settings UI | 3/3 | — | +| 13. Command Editor & List UI | 3/3 | — | +| 14. Editor Multi-Mount Refactor | 3/3 | — | +| 15. Cross-Platform Execution | 0/3 | — | +| Phase 15-cross-platform-execution P01 | 5min | 3 tasks | 2 files | + +## Accumulated Context + +### Roadmap Evolution + +- Phase 1 added: on every command now we should store a command execution directory (working dir) — which helps users run commands in a specific directory they want (have UI) +- Milestone v1.3 scoped to Working Directory feature: per-command working dirs, global default fallback, transparent OS-keyed storage +- Milestone v1.3 archived 2026-04-23 — all 14 requirements validated, 6/6 UAT tests passed +- Phase 1 added (v2.0 bucket): Editor Multi-Mount Refactor — per-tab CommandDetail instances, hide inactive via CSS, preserve local DOM state across tab switches +- Phase 15 added (v1.5): Cross-Platform Execution — remove hardcoded #!/bin/bash shebang from stored scripts, centralize shebang generation in executor at runtime with platform detection (Windows cmd vs Unix sh) + +### Decisions + +- Milestone scoped to Premium Polish. Cloud, workspaces, sharing deferred to v2. +- Used jsdelivr CDN for Nunito fonts +- Working directory stored as JSON keyed by OS (darwin/windows/linux) for cross-OS import/export compatibility +- UI completely transparent — users only see plain path for current OS + +### Blockers/Concerns + +- No tests exist — manual verification required for all phases. +- Need to ensure Wails directory picker binding works across Mac/Linux/Windows. + +### Quick Tasks Completed + +| # | Description | Date | Commit | Directory | +|---|-------------|------|--------|-----------| +| 260414-fvh | check setting dialogs linking to wails setting window | 2026-04-14 | bebe4a4 | [260414-fvh-check-setting-dialogs-linking-to-wails-s](./quick/260414-fvh-check-setting-dialogs-linking-to-wails-s/) | +| 260414-g2s | add padding to setting window, remove old SettingsDialog | 2026-04-14 | 8df32fe | [260414-g2s-add-padding-to-setting-window-remove-old](./quick/260414-g2s-add-padding-to-setting-window-remove-old/) | +| 260414-h5v | auto-save settings on change, remove close/save buttons | 2026-04-14 | 8a3dcb4 | [260414-h5v-auto-save-settings-on-change-remove-clos](./quick/260414-h5v-auto-save-settings-on-change-remove-clos/) | +| 260414-hlh | fix settings: density/font selects not updating main window (PARTIAL - Bug 2 BringToFront not available in Wails v3) | 2026-04-14 | 7171560 | [260414-hlh-fix-settings-density-font-selects-not-up](./quick/260414-hlh-fix-settings-density-font-selects-not-up/) | +| 260414-jcp | replace magic string event names with global constants | 2026-04-14 | ffd0aa2 | [260414-jcp-replace-magic-string-event-names-with-gl](./quick/260414-jcp-replace-magic-string-event-names-with-gl/) | +| 260414-k9n | move event names to Go binding, frontend uses generated types | 2026-04-14 | c0b8bdb | [260414-k9n-replace-magic-string-event-names-with-gl](./quick/260414-k9n-move-event-names-to-go-binding-frontend-uses-generated-types/) | +| 260414-m4q | refactor Go services into separate structs (CommandService, SettingsService, etc.) | 2026-04-14 | d262b5e | [260414-m4q-refactor-go-services-into-separate](./quick/260414-m4q-refactor-go-services-into-separate/) | +| 260415-n7k | make Vite server port dynamic using VITE_PORT env var | 2026-04-15 | - | - | +| 260416-pr3 | update github actions versions in ci and release workflows | 2026-04-16 | 171bf77 | [260416-pr3-update-github-actions-versions-in-ci-and](./quick/260416-pr3-update-github-actions-versions-in-ci-and/) | +| 260424-01u | Apply code review fixes from v1.3 Working Directory milestone across db.go, executor.go, frontend components, models.go, and importexport_service.go | 2026-04-23 | 7713ce8 | [260424-01u-apply-code-review-fixes-from-v1-3-workin](./quick/260424-01u-apply-code-review-fixes-from-v1-3-workin/) | + +--- + +### Pending Todos + +| # | Title | Area | Created | +|---|-------|------|---------| +| 1 | Revamp Working Directory UI in Command Editor | ui | 2026-04-23 | + +--- + +### Documentation + +| Doc | Path | Status | +|-----|------|--------| +| README.md | ./README.md | preserved (hand-written) | +| ARCHITECTURE.md | ./docs/ARCHITECTURE.md | generated | +| GETTING-STARTED.md | ./docs/GETTING-STARTED.md | generated | +| DEVELOPMENT.md | ./docs/DEVELOPMENT.md | generated | +| TESTING.md | ./docs/TESTING.md | generated | +| CONFIGURATION.md | ./docs/CONFIGURATION.md | generated | +| CONTRIBUTING.md | ./CONTRIBUTING.md | generated | +| DEPLOYMENT.md | ./docs/DEPLOYMENT.md | generated | + +--- + +*Last updated: 2026-05-04* diff --git a/.planning/phases/15-cross-platform-execution/15-01-SUMMARY.md b/.planning/phases/15-cross-platform-execution/15-01-SUMMARY.md new file mode 100644 index 0000000..d7d2e28 --- /dev/null +++ b/.planning/phases/15-cross-platform-execution/15-01-SUMMARY.md @@ -0,0 +1,79 @@ +--- +phase: 15-cross-platform-execution +plan: 01 +subsystem: execution-engine +tags: [shebang, cross-platform, script-generation, executor, backward-compat] +requires: [] +provides: [shebang-free-script-storage, platform-aware-execution, backward-compatible-parsing] +affects: [script.go, executor.go] +tech-stack: + added: [] + patterns: [platform-aware-shebang-injection, generic-shebang-stripping, dead-code-identified] +key-files: + created: [] + modified: + - script.go (remove scriptHeader constant, simplify GenerateScript, generic ParseScriptBody) + - executor.go (platform shebang in ExecuteScript, fix Unix -lc flag, add stripShebang helper) +decisions: + - "Shebangs removed from stored scripts; platform-appropriate shebangs injected at execution time only" + - "Unix execution path now uses -lc flag (was previously dropped), enabling login shell with profile sourcing" + - "ParseScriptBody and stripShebang use generic #! prefix detection for full backward compatibility" + - "BuildFinalCommand is dead code (defined but never called); left untouched for now" +completed: 2026-05-04T07:45:22Z +duration: ~5min +--- + +# Phase 15 Plan 01: Centralize Shebang Handling (script.go + executor.go core) + +**One-liner:** Removed hardcoded `#!/bin/bash` from script storage, centralized platform-aware shebang injection in executor at runtime with generic backward-compatible parsing. + +## Tasks Completed + +| Task | Name | Commit | Key Changes | +|------|------|--------|-------------| +| 1 | Update script.go | `b04a573` | Removed `const scriptHeader`, `GenerateScript()` returns body-only, `ParseScriptBody()` strips any `#!` prefix | +| 2 | Update executor.go | `f706f56` | Platform shebang in `ExecuteScript()`, fixed Unix `-lc` flag, added `stripShebang()` helper | +| 3 | Verify execution_service.go compatibility | N/A (no changes) | All 3 call sites (`BuildDisplayCommand`, `ExecuteScript`, `OpenInTerminal`) verified compatible | + +## Execution Flow + +The new data flow for command execution: + +``` +Storage: GenerateScript(body) → "echo hi\n" (no shebang stored) +Parsing: ParseScriptBody("#!/bin/bash\n\necho hi\n") → "echo hi" (backward compat) +Execution: ExecuteScript("echo hi") → stripShebang → "#!/bin/sh\necho hi\n" → temp file → sh -lc /tmp/xxx.sh +Windows: ExecuteScript("echo hi") → stripShebang → "echo hi\n" → temp file → cmd /C tmp.bat +``` + +## Changes Summary + +### script.go +- **Removed:** `const scriptHeader = "#!/bin/bash"` +- **Changed `GenerateScript()`:** Returns trimmed body with trailing newline, no shebang prefix. Empty input returns empty string. +- **Changed `ParseScriptBody()`:** Generic `#!` detection replaces specific `#!/bin/bash` check. Strips the entire first line if it starts with `#!`. Handles both old (shebang in DB) and new (no shebang) formats transparently. + +### executor.go +- **Added shebang injection in `ExecuteScript()`:** Strips existing shebang from stored content via `stripShebang()`, then prepends `#!/bin/sh\n` on Unix. No shebang on Windows (`.bat` files don't use them). +- **Fixed Unix execution path:** Previously `exec.CommandContext(ctx, e.shell, tmpPath)` dropped the `-lc` flag. Now uses `exec.CommandContext(ctx, e.shell, e.flag, tmpPath)` uniformly across platforms — `/C` on Windows, `-lc` on Unix. +- **Added `stripShebang()` helper:** Removes any `#!...` first line from script content. Used by `ExecuteScript()` for backward compatibility with old DB records that still contain `#!/bin/bash`. + +### execution_service.go +- **No changes needed.** All three call sites (`BuildDisplayCommand`, `ExecuteScript`, `OpenInTerminal`) remain signature-compatible. The executor's internal shebang handling is transparent to callers. + +## Deviations from Plan + +None — plan executed exactly as written. All three tasks completed per specification with zero deviations. + +## Self-Check: PASSED + +All files exist and commits verified: +- ✅ `script.go` — modified, committed as `b04a573` +- ✅ `executor.go` — modified, committed as `f706f56` +- ✅ `execution_service.go` — reviewed, no changes needed +- ✅ `go build ./...` — succeeds with no errors +- ✅ `GenerateScript` returns body without shebang prefix +- ✅ `ParseScriptBody` strips any `#!` line generically +- ✅ `ExecuteScript` adds `#!/bin/sh` on Unix, no shebang on Windows +- ✅ Unix execution uses `-lc` flag (was previously unused) +- ✅ Backward compatible: existing DB scripts with `#!/bin/bash` execute correctly From 59fff59a9e207b9a18171a49b7c4a431504787de Mon Sep 17 00:00:00 2001 From: locnguyen1842 Date: Mon, 4 May 2026 14:53:52 +0700 Subject: [PATCH 04/15] feat(15-cross-platform-execution-02): make BuildFinalCommand use platform-appropriate shell name - Convert BuildFinalCommand from package-level function to method on *Executor - Use e.shell basename instead of hardcoded "bash" - Windows displays "cmd