* fix: resolve Windows MSI ICE30 by removing duplicate helmor-cli externalBin
helmor-cli.exe was packaged into the MSI twice — once via the externalBin
entry (./target/bundled/helmor-cli) and once via Tauri auto-bundling the
crate's [[bin]] — both landing at the same install path. WiX's light.exe
rejected this with ICE30 (two components installing the same file). The
.app bundle on macOS tolerates the duplicate, so it went unnoticed.
Drop the externalBin CLI entry; Tauri's [[bin]] copy alone satisfies the
runtime lookup in agent_invocation_path(). Verified: MSI bundles cleanly
(ICE30 gone), wxs has a single helmor_cli component, dev flow unaffected.
* test: drop helmor-cli from externalBin assertion
The fix removed ./target/bundled/helmor-cli from externalBin, but
build-platform.test.js still pinned the 2-entry array, breaking
test:frontend. Align the assertion with the single sidecar entry.
---------
Co-authored-by: Nathan L <42262146+natllian@users.noreply.github.com>
Co-authored-by: Nathan L <liangeqiang@gmail.com>
fix(sidecar): cross-compile sidecar to release target triple
The x86_64 macOS build shipped an arm64 `helmor-sidecar`. Both macOS
matrix targets build on an arm64 runner (macos-26); cargo cross-compiles
the Rust/CLI via `--target`, but `bun build --compile` in build.ts had no
target and emitted a host-arch (arm64) binary. prepare-sidecar.mjs then
copied it to the `-x86_64-apple-darwin` suffix and Tauri bundled it, so
Intel users hit "Failed to start sidecar binary" / "bad CPU type in
executable". Same class as #293, on the one binary the arch guard skipped.
- build.ts: map the target triple (TAURI_TARGET_TRIPLE / CARGO_BUILD_TARGET)
to a Bun compile target; omit in dev so host builds are unchanged; throw
on an unmapped-but-set triple instead of silently shipping host arch.
- verify-bundled-cli.sh: add Contents/MacOS/helmor-sidecar to the arch
check so a wrong-arch sidecar fails CI.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summoned with Shift+Alt+Space (configurable in Settings → Shortcuts): a
small always-on-top frameless window that reuses the start surface +
composer + conversation stack in its own webview (QuickShell), creates a
workspace, streams the agent reply, and supports full multi-turn chat.
Hidden (never destroyed) on close so its session survives across summons.
Main-window flows are untouched when the feature is unused: every
per-app singleton (updater, dock badge, location persistence, OS-hotkey
sync, settings self-repair) is gated by window role, and the composer's
narrow-surface compaction is pure CSS container queries.
Also fixes a latent start-surface bug: the post-create view switch sat in
a bare requestAnimationFrame, which WKWebView pauses entirely while the
webview is hidden — raced with a 120ms timer fallback (#770 pattern).
* feat(win): platform layer + cross-platform process/PTY/fs/limits
- Add platform/ module (process group via taskkill, fs symlink-or-copy)
- Migrate sidecar, tunnel, git/ops, shell_env, runtime_registry to platform::process
- Rewrite workspace/scripts.rs PTY terminal on portable-pty (Unix+ConPTY); PowerShell shell on Windows
- cfg-gate libc to unix; add windows + uds_windows + portable-pty deps
- system_limits: _setmaxstdio on Windows; helpers: cross-platform symlink
- boring-sys2 Release CRT override (fixes MSVC debug-CRT link mismatch)
- Windows icon.ico + Store/Square logos for tauri-build resource
* feat(win): Phase 4 parity + clippy-clean
- UI-sync over AF_UNIX (uds_windows); GlobalMemoryStatusEx RAM; Claude creds from ~/.claude/.credentials.json
- gate macOS-only menu IDs / applescript / editor bundle_ids; module-level allow for macOS-only Slack scraper
- drop unneeded portable_pty trait imports; restore too_many_arguments allows
* fix(win): resolve home dir via USERPROFILE when HOME is unset
The Tauri setup hook panicked with 'Could not determine home directory' on
Windows because data_dir only consulted $HOME. Fall back to USERPROFILE then
HOMEDRIVE+HOMEPATH on Windows. Verified: the app window now launches.
* fix(win): skip login-shell PATH capture on Windows (no-op)
Windows GUI processes inherit the full user/system PATH from the registry, so
the macOS/Linux login-shell env capture (which tried to spawn /bin/zsh) is
unnecessary and was erroring. Gate the unix capture module + call to cfg(unix);
gate the now-Windows-unused codex_config::declared_env_keys lint accordingly.
* feat(win): Windows vendor-CLI staging + codex config home
- stage-vendor.ts: detect win32, stage claude-code/codex/opencode from the
installed win32-x64 sub-packages, download gh/glab/cloudflared/llama win
artifacts; cross-platform sha256 (node crypto), tar -xf for zip+tgz, .exe
naming, codex codex-resources copied adjacent, soft-verify unpinned hashes,
optional stagers non-fatal on Windows. Verified: all 7 components staged.
- codex_config: resolve ~/.codex via USERPROFILE on Windows (match codex)
- stage-dev-cli.mjs: append .exe to staged helmor-cli triple name on Windows
* fix(win): helmor-cli .exe, npx via cmd /c, agent CLI PATHEXT lookup
- cli_source_binary_name + agent_invocation_path use helmor-cli.exe on Windows
- install_helmor_skills / install_skills_silent spawn npx through cmd /c
(npm ships npx.cmd/.ps1, not npx.exe — Command::new("npx") fails)
- resolve_agent_binary falls back to a PATH+PATHEXT lookup on Windows so a
bare 'codex'/'claude' resolves to codex.cmd/.exe instead of program-not-found
Fixes the 'Helmor CLI' and 'Helmor Skills (Beta)' error cards on Windows.
* fix(win): PowerShell-correct agent/forge login boot commands
The onboarding login terminals run PowerShell on Windows, where a quoted exe
path is a string literal — executing it needs the call operator '&'. Add
scripts::format_boot_command (& <cmd>; exit on Windows) and use it for the
claude/codex/opencode and gh/glab auth terminals. Also make which_on_path skip
extensionless npm shims so 'claude' resolves to claude.exe/.cmd, not the Unix
bash shim.
Fixes 'Unexpected token auth' ParserError on Windows login.
* fix(win): centralize home-dir resolution (platform::home_dir)
Many features read $HOME directly and broke on Windows GUI processes (no HOME):
opencode config ('HOME is not set' error spam), claude project files, codex
rate-limits, glab config, cloudflared dir, and the agent/forge login working
dir. Add platform::home_dir (HOME -> USERPROFILE -> HOMEDRIVE+HOMEPATH) and
route these through it.
* fix(win): resolve bun + agent CLIs via PATHEXT (platform::which)
The dev sidecar spawn used Command::new("bun"), which fails on Windows because
bun on PATH is a .cmd/.ps1 shim and CreateProcess ignores PATHEXT ('Failed to
start sidecar — is Bun installed?'). Add platform::which (PATHEXT-aware) and use
it for the bun spawn; dedupe resolve_agent_binary onto it too.
* build(win): NSIS installer bundle config
- tauri.conf bundle.windows with NSIS current-user install + icon.ico
- disable createUpdaterArtifacts (local installer doesn't sign update artifacts)
- prepare-sidecar.mjs stages helmor-sidecar/helmor-cli with the .exe + triple
suffix Tauri's externalBin expects on Windows
Produces Helmor_<ver>_x64-setup.exe bundling the sidecar + claude/codex/gh/glab/
cloudflared/llama binaries.
* fix(windows): hide child-process consoles, resolve bundled CLIs, install CLI shim
Three Windows-port fixes driven by first real-device testing:
- Spawned CLIs (git, gh/glab polls, claude probes, taskkill, tunnels...)
flashed a console window on every invocation. Worse, each console stole
focus from the main window, so `refetchOnWindowFocus: "always"` auth
probes re-fired the moment it closed - a self-sustaining ~1/s terminal
strobe loop. New `platform::process::hide_console` (+`_tokio`) helpers
apply CREATE_NO_WINDOW at every one-shot spawn site; long-lived children
were already covered by `configure_new_group`.
- Bundled gh/glab resolution assumed the macOS Contents/Resources bundle
layout, so the installed Windows app fell back to PATH (and errored once
per probe on missing glab). `forge::bundled` now probes both the
exe-adjacent layout (NSIS/MSI) and Contents/Resources (macOS).
- `install_cli` bailed with "symlink only supported on Unix". Windows now
installs a `.cmd` shim in %LOCALAPPDATA%\Helmor\bin forwarding to the
bundled helmor-cli.exe, registers that dir on the user PATH
(HKCU\Environment, REG_EXPAND_SZ-preserving, WM_SETTINGCHANGE broadcast),
and classifies Managed/Stale by parsing the shim target.
Also makes the Rust test suite buildable and runnable on Windows for the
first time: cfg-gates unix/mac-only tests, and embeds a common-controls v6
app manifest into every linked target via build.rs (tauri-build switched to
new_without_app_manifest) - without it each test exe died at startup with
STATUS_ENTRYPOINT_NOT_FOUND because TaskDialogIndirect needs comctl32 v6.
Verified: clippy --all-targets -D warnings clean; all 11 integration test
targets pass; changed-module unit tests pass (23 pre-existing lib-test
failures remain in unrelated areas, first time the suite has run on
Windows).
* fix(windows): hide consoles for agent probes and npx skills installer
Follow-up to e27a9323: the spawn sites in commands/system_commands.rs were
left out of the hide_console sweep. They are the most visible offenders -
agent version probes (claude/codex/opencode --version), claude/codex login
status checks, and the `cmd /c npx` skills installer that the silent
startup update check runs - each flashed a console window from the GUI
process on launch and whenever Settings opened.
* fix(windows): resolve bundled agent binaries via exe-adjacent layout
The sidecar boot path resolved bundled claude/codex/opencode binaries only
under the macOS Contents/Resources layout, so on Windows (NSIS/MSI, vendor/
next to the exe) claude_bin came back None and HELMOR_CLAUDE_CODE_BIN_PATH
was never set. The sidecar's own resolveClaudeBinPath then threw at module
load (require.resolve always fails inside a bun --compile binary), killing
the process with "Invalid sidecar ready signal".
- sidecar.rs: probe both exe-adjacent (Windows) and Contents/Resources
(macOS) roots, mirroring forge::bundled.
- claude-session-manager.ts: resolveClaudeBinPath returns undefined instead
of throwing when nothing resolves, scoping any failure to a single Claude
session and letting the SDK attempt its own resolution.
* fix(windows): green the Windows CI jobs
- build.rs: append `.exe` to the externalBin placeholders on Windows so a
clean checkout doesn't panic in tauri-build ("resource path …-msvc.exe
doesn't exist") before prepare-sidecar.mjs produces the real artifacts.
Unblocks both the Build and Rust Test jobs.
- stage-vendor.ts: extract via the Windows in-box bsdtar (System32\tar.exe)
instead of a bare `tar`, which under the CI bash shell resolves to Git's
GNU tar and reads the `D:` drive in an archive path as an rsh host
("Cannot connect to D: resolve failed"), silently skipping gh/glab/llama.
- codex-app-server-manager.test.ts: the global `mock.module` for git-access
leaked into git-access.test.ts (file order is filesystem-dependent, so it
only bit on Windows); the stub now delegates to the real module unless a
codex test is actively driving it.
* fix(windows): resolve remaining Windows CI test + setup failures
- repos: normalize repo paths with dunce::canonicalize (strips the Windows
`\\?\` verbatim prefix) and route git --show-toplevel through the same
normalizer + OS-native separators, so repo dedup and clone-target detection
compare equal for the same directory. Fixes the duplicate-repo and
clone_from_url failures on Windows. Also split repo-name inference on `\`.
- cli/image_store/workspaces tests: assert against the platform path shape
(`helmor-cli.exe`, separator-agnostic) instead of hardcoded `/` forms.
- rate_limits::claude::process tests: gate to unix (they spawn /bin/echo etc.).
- setup-js: add an install-sccache input (default true); the Windows jobs opt
out since they don't use sccache and its installer is flaky on Windows.
* fix(windows): keep updater artifacts on macOS, disable only on Windows
createUpdaterArtifacts was flipped to false on the branch so the keyless NSIS
build wouldn't try to sign an updater artifact — but that's a global flag, so
it also disabled macOS auto-update and broke the build-platform boundary test
(which asserts the base config is unchanged). Restore the base to true and move
the Windows-only opt-out into tauri.windows.conf.json, which Tauri merges over
the base for Windows builds.
* fix(windows): use platform Pid type in runtime registry liveness probe
probe_pid_alive cast the pid to u32, which only matches platform::process::Pid
on Windows; on macOS Pid is i32, so the lib failed to compile there (E0308),
taking down the macOS Rust Test and Lint jobs. Cast to the platform Pid alias
so it builds on both.
* test(windows): gate macOS-only CLI permission-denied test
try_install_cli_silent_at_bails_with_friendly_message_on_permission_denied
probes /usr/local/bin and asserts the macOS sudo elevation message. On the
Windows CI runner the /usr/local/bin write fails differently (so the test's
writable-skip guard doesn't trigger) and the Windows shim install produces a
different error, failing the assertion. Windows installs a .cmd shim with no
elevation flow, so gate the test to macOS.
* fix(windows): re-hide console for git and cloudflared spawns
The #747 merge took theirs for git/ops.rs and companion/tunnel.rs and dropped
the configure_background_cli wraps windows-port had at those spawn sites,
which re-introduced the terminal-flashing loop: git status / git diff /
git stash pop polls and cloudflared tunnel create/delete each popped a
console window. Re-wrap them via configure_background_cli (CREATE_NO_WINDOW
on Windows, no-op on Unix).
* ci(windows): override checked-in sccache rustc-wrapper for Windows jobs
src-tauri/.cargo/config.toml sets rustc-wrapper = "sccache" so the macOS
jobs (setup-rust-tauri installs sccache) pick it up. The Windows jobs opt
out of installing sccache, so cargo fails immediately:
error: could not execute process `sccache rustc.exe -vV` (never executed)
Set RUSTC_WRAPPER="" at the job level for Windows Rust Test + Build to
override the file config. The macOS Typecheck flake (curl 504 fetching
sccache) is unrelated install-action infra and will recover on rerun.
* test(windows): normalize repo paths in fixture-db inserts so dedup matches
The test harness was storing the raw temp-dir path in repos.root_path while
production goes through normalize_filesystem_path (dunce::canonicalize). My
machine and the CI Windows runner disagree on what dunce returns for the
runner's temp paths, so the lookup misses and a duplicate repo gets created,
failing add_repository_from_local_path_focuses_existing_workspace_for_duplicate_repo
and add_repository_from_local_path_re_add_with_only_archived_workspaces_lands_on_start.
Route the harness inserts through the same normalizer so stored == lookup on
both platforms.
* test: assert against canonicalized repo root in workspace_creation tests
Sibling fix to e1bd54b5 (harness now stores repos.root_path via
normalize_filesystem_path so dedup matches on Windows CI). Two prepare/finalize
assertions still compared response.working_directory against the raw
harness.source_repo_root, which on macOS broke because canonicalize resolves
/var → /private/var. Compare against the canonicalized form on both sides so
the test passes uniformly.
---------
Co-authored-by: Qui Luong <jerry.luong.94@gmail.com>
Co-authored-by: Caspian 東澔 <caspian.zhao@outlook.com>
* Extend platform boundary: PTY and CLI-install seams
Behavior-preserving extraction of two OS-divergent surfaces behind the
platform/ seam. The macOS/Unix reference path is unchanged; Windows arms
are stubbed (Unsupported) so this is fully verifiable on macOS.
- platform::pty — PTY session lifecycle: openpty, session-leader
(setsid + TIOCSCTTY), non-blocking poll(2) read, TIOCSWINSZ resize.
workspace::scripts now drives the PTY through the seam instead of raw
libc; the run/terminal orchestration and macOS behavior are unchanged.
- platform::cli_install — managed CLI launcher target path, symlink
classification, and link creation. commands::system_commands routes
through the seam instead of inline cfg(unix).
Also harden release:verify with a createUpdaterArtifacts===true assertion
so a platform bundle cannot silently disable the macOS updater pipeline.
* fix(pty): drop fragile live-PTY resize test that hung CI
resize_succeeds_on_a_live_pty called child.wait() on a sleeping child
attached to an undrained PTY — a classic hang (the test never returned,
stalling nextest until the job timeout). resize is already covered
end-to-end by the passing workspace::scripts::resize_updates_pty_winsize
integration test, so the standalone seam test was redundant as well.
* refactor(pty): expose the PTY seam as OS-agnostic trait objects
Per review feedback (#747) on the seam shape: PtySession now returns
Box<dyn PtyChild> / Box<dyn PtyReader> / Box<dyn PtyWriter> instead of
std::process::Child + File, so a Windows ConPTY (or portable_pty) adapter
can supply its own handle types without the shared workspace::scripts
orchestration touching File / libc / poll(2). PtyChild exposes id()+wait();
PtyWriter is Write + resize(); PtyReader is Read + poll_readable().
macOS/Unix behavior is unchanged. The reference impl keeps the non-blocking
openpty master, poll(2)-driven reads, and the reader's stop-flag exit. It is
deliberately NOT a pure blocking-read model: a held slave dup keeps the
master from EOF, so a blocking read could never be interrupted and would
hang a run-action whose script backgrounds a process and then exits. poll /
TIOCSWINSZ / EIO now live behind the PtyReader/PtyWriter impls.
* feat: stacked PR support
Stack one workspace on another and Helmor treats the chain as a first-class
stack — in the data model, the sidebar, the CLI, the agent's context, and a
unified helmor-cli skill.
- Data: `parent_workspace_id` column + idempotent migration; stacked creation
via `helmor workspace new --parent` (dual-writes the parent link and the
child's target branch); finalize forks the child off the parent's branch tip
(local-ref fallback, so you can stack before pushing).
- Sidebar: `nestStacks` groups a stack's members contiguously under their tip
(hoisting across status lanes) with a light connector; drag ghost shows a
stack-size badge.
- Stack intelligence: `helmor workspace stack` CLI (human table + JSON spec for
the renderer), stack-aware system-prompt priming so an agent knows its layer
and base, and a consolidated helmor-cli skill that routes `stack` / `restack`
and bundles a deterministic render_stack.py.
- Composer: Restack quick-action (stack tips only) -> `/helmor-cli restack`.
- Deletion guard: tip deletes freely, deleting a root pops the bottom (children
re-target the repo default), middle layers are blocked.
* feat(stacked-pr): /helmor-cli break — split an existing change into a stack
Add a `break` route to the helmor-cli skill: analyze the current workspace's
diff, propose dependency-ordered file slices, confirm slicing granularity with
the user, then materialize the slices as a stack (one workspace + PR per slice)
and verify the split is lossless. Skill-only; reuses `helmor workspace new
--parent`, `helmor send`, and raw git.
* fix(dev): self-heal the dev CLI on restart
The managed /usr/local/bin/helmor-dev symlink and the target/debug/helmor-cli it points at could both go stale: the per-version cache gate skipped the symlink heal, and `bun run dev` never staged a real CLI (build.rs's no-op placeholder shipped instead). Un-gate the symlink self-heal so every launch re-points it, and add a dev:prepare step that builds + stages the real helmor-cli, so a plain restart fixes it instead of a manual ln -sfn.
* feat(stacked-pr): header targets the parent workspace; keep target branch fresh
For a stacked child, the panel header now shows a live "parent title" chip (click to open the parent) instead of the frozen, codename-ish target branch: WorkspaceDetail exposes parentWorkspaceId and the chip reads the parent's live title. Underneath, branch renames (manual + post-first-message regeneration) cascade to children's intended_target_branch via one write helper, with an idempotent startup backfill for stacks that drifted before the cascade existed.
* feat(stacked-pr): the start workspace becomes the stack root
/helmor-cli stack and break no longer leave the invoking workspace behind as a throwaway launchpad: stack builds the bottom layer in the current workspace, and break rewrites it into the root (capturing a helmor/break-backup recovery ref first). Skill-only change.
* chore(release): changeset + in-app announcement for stacked PR support
* feat(stacked-pr): polish the sidebar stack connectors
Connector line segments now stop at each row's node dot instead of crossing it, the stack's base layer gets a prominent solid dot, and hovering a node shows a position tooltip (via a new stackSize on the stack row meta).
* docs: Dosu updates for PR #721
* perf(stacked-pr): scope composer stack-tip check with a query select
The composer subscribed to the whole workspace-groups list to decide whether to show the Restack quick-action, so it re-rendered on any workspace-list change (adds, status flips, reorders) — including for workspaces that aren't in a stack. Narrow it to the single boolean via React Query's select so the observer only re-renders when stack-tip status actually flips; non-stack composers no longer reconcile on workspace churn.
---------
Co-authored-by: dosubot[bot] <131922026+dosubot[bot]@users.noreply.github.com>
The new action type added in #571 wasn't mirrored in the CI
verifier's allowlist, so `bun run scripts/verify-release-announcements.ts`
rejected the merged announcement file.
* Add release announcements and repository sidebar grouping
* feat(settings): drop version-pinning on release markers and refresh announcements
Decouple "New feature" badges from package.json version so markers stay
visible until explicitly removed. Refresh the release announcement catalog
entry for the upcoming launch and clean up the research scratch folder.
* feat(announcements): merge skipped releases into a single toast
Replays every published entry in (lastSeen, current] instead of matching
the current version exactly. Items are ordered newest-version first,
stable within a version. Dismissing the toast dismisses every rolled-in
id, and the list scrolls inside the card to handle long backlogs.
* fix: sidebar repo grouping order, restore freeze, and announcement replay
- Project workspace groups through `regroupByRepo` for keyboard nav and
warmup so up/down keys follow on-screen order; drop the hardcoded
status-tone ranking in `flattenWorkspaceRows` that silently dropped
repo buckets.
- Defer prefetch + selection in workspace restore until the worktree
exists, eliminating the multi-second post-restore UI freeze.
- Stop fetcher restarts on every workspace churn and remove the upfront
`git fetch` in `start_auto_fetch`, so unreachable remotes can no
longer block UI actions on the SSH connect timeout.
- Distinguish a genuine first install from an existing user's first
boot with the announcement system via `isFirstHelmorBoot`, replaying
the catalog backlog for the latter; refresh the launch entry copy.
- Suppress per-row repo avatars only inside real repo buckets and let
the branch icon carry status / running indicators in that mode.
- Add tests pinning that `prepare_local_workspace` /
`prepare_workspace_from_repo` honor `WorkspaceStatus::Backlog`.
---------
Co-authored-by: Nathan L <liangeqiang@gmail.com>
Cross-arch CI runs on Apple Silicon, so the x64 .dmg shipped arm64
gh/glab/codex/bun and Intel users hit "bad CPU type in executable"
(#293). stage-vendor.ts now honors TAURI_TARGET_TRIPLE; bun comes
from oven-sh/bun releases and cross-arch codex from npm registry
tarballs, both SHA256-verified. verify-bundled-cli.sh lipo-checks
every vendored binary against helmor-cli's arch and fails CI on
mismatch.
Tauri validates every `externalBin` during `cargo build`, so the CLI
build was failing on clean checkouts when the target-suffixed sidecar
wasn't yet present. Copy the compiled sidecar into place before the
cargo build, and have build.rs write placeholder executables for both
helmor-cli and helmor-sidecar so first-time builds succeed.
Ship helmor-cli as a Tauri externalBin so the desktop bundle carries its
own CLI companion. Settings install now symlinks /usr/local/bin/helmor
(release) or /usr/local/bin/helmor-dev (debug) to the bundled binary, so
terminal usage tracks desktop updates automatically. Adds missing/managed/
stale install-state classification, remediation hints, a CI smoke check
(scripts/verify-bundled-cli.sh), and tests for the new paths.
The marketing monorepo conversion flipped @changesets/cli into monorepo mode, so `changeset tag` produced `<name>@<version>` tags (e.g. `helmor@0.2.0`) that didn't match publish.yml's `tags: v*` trigger — 0.2.0 built no DMGs and leaked a `@helmor/marketing@0.0.0` tag. Swap the publish step for a script that tags `v<version>` from the root package.json, and ignore `@helmor/marketing` in `.changeset/config.json` so it's never bumped or tagged by changesets.
Signed + notarized macOS builds shipped without the JIT entitlements that
Bun's JSC needs to allocate executable memory. The sidecar crashed at
spawn with "Ran out of executable memory while allocating N bytes",
which surfaced in the UI as an empty model list because every listModels
call timed out before any model could be returned.
Changes:
- New src-tauri/Entitlements.plist with the 3-key minimum used by
electron-builder by default (allow-jit, allow-unsigned-executable-memory,
disable-library-validation).
- tauri.conf.json now references the plist via bundle.macOS.entitlements,
which Tauri applies to the main binary and the externalBin sidecar.
- stage-vendor.ts re-signs the bundled bun runtime with the same plist
(codex stays without entitlements, no JIT needed).
- prepare-sidecar.mjs pre-signs the target-suffixed helmor-sidecar binary
with entitlements before Tauri ingests it, as a belt-and-braces guard
against tauri#11992.
- Move bundled-resource path resolution from lib.rs setup hook into
sidecar.rs::SidecarProcess::start so the env vars are set directly on
the child Command instead of leaking into the parent process, and add
an info log dumping the resolved paths to make future regressions
visible in the rust.jsonl logs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Helmor will ship macOS-only for the foreseeable future. This commit
removes every Windows/Linux code path, build target, CI gate, asset,
and docs reference so there is a single canonical platform to reason
about.
CI / workflows
- Delete publish-linux.yml, publish-windows.yml, publish-macos.yml
(single-platform fallbacks). publish.yml is the sole release entry
point and is trimmed to mac arm64 + mac x64 matrix; preflight moved
to macos-latest; all AppImage / linuxdeploy workarounds and the
`-vv` / RUST_LOG debug flags are gone.
- quality.yml: drop the rust-check-windows and rust-check-linux
compile gates.
- updater-dry-run.yml: default `expected_platforms` now
`darwin-aarch64,darwin-x86_64`.
- setup-rust-tauri/action.yml: strip the Linux apt-install step and
the Linux + Windows sccache/cargo caches.
Rust source (src-tauri/src)
- commands/system_commands.rs: remove Windows and Linux variants of
`detect_installed_editors_blocking` and `launch_editor_with_dir`.
`cli_install_target` / `cli_source_binary_name` collapse to the
macOS implementation; `install_cli` loses its Windows fs::copy /
permissions branches.
- data_dir.rs: `dirs_home` loses the %USERPROFILE% fallback.
- import.rs: home-directory lookup stays on `$HOME` only.
- workspace/mod.rs: drop the `cfg(not(unix))` scripts_windows stub
and delete src-tauri/src/workspace/scripts_windows.rs.
- workspace/helpers.rs: `copy_symlink` loses its Windows
dereference-and-copy fallback.
- workspace/lifecycle.rs: `archive_shell` loses the cmd.exe /C branch
and the stale `#[cfg(windows)]` test-plan comment.
- sidecar.rs: remove `cfg(unix)` guards on process-group signalling
and the no-op Windows `send_sigterm`.
- git/ops.rs: remove `cfg(unix)` / `cfg(not(unix))` guards on
SIGKILL + process_group setup.
- commands/tests/support.rs: `make_executable_if_script` always
applies Unix permissions.
- shell_env.rs: collapse the `cfg(unix)` wrapper module.
- updater/service.rs: remove `cfg(not(target_os = "windows"))` guard
on `app.request_restart()`.
Cargo / Tauri config
- Cargo.toml: `[target.'cfg(unix)'.dependencies] libc` becomes an
unconditional dependency.
- tauri.conf.json: drop `icons/icon.ico` from the bundle list.
Sidecar (TypeScript)
- src/image-resize.ts: only the macOS `sips` path remains; drop the
Windows PowerShell + Linux `magick`/`convert` branches.
- src/codex-app-server.ts: drop `shell: process.platform === "win32"`
and the `taskkill` Windows kill fallback (plus the now-unused
`spawnSync` import).
- scripts/stage-vendor.ts: `detectTarget` collapses to darwin arm64 +
darwin x64; Linux seccomp bundling, Windows `.exe` handling, and
the `which bun | where bun` fork all go.
- test/image-resize.test.ts: revert the Windows-specific 15s timeout
and update the suite header comment.
Frontend (TypeScript)
- src/lib/platform.ts: shrink to a single `isMac() = true` export —
kept as a one-liner so future re-introduction of platform detection
only has to change this file.
- src/lib/keyboard-modifier.ts: `isPrimaryModifier` is now just
`event.metaKey`, `hasSecondaryModifier` is `event.ctrlKey`.
- src/App.shortcuts.test.tsx: drop the `isWindows`/`isLinux` mocks.
Scripts
- scripts/prepare-sidecar.mjs: drop the `.exe` extension logic and
the Windows-specific fallback that tolerates bun-compile stripping
the extension.
Assets / docs
- Delete the Windows + Microsoft Store tile icons
(icon.ico + Square*Logo.png + StoreLogo.png). macOS icon set
(icon.icns + 32/64/128 PNGs) and the mobile android/ios dirs are
preserved.
- README.md: download link is now "Download for macOS"; the
"Windows & Linux (alpha), vibe-coded, never tested" line is gone.
Not touched
- src-tauri/icons/android/ and src-tauri/icons/ios/ stay in the tree
(user preference — leave room for a future mobile port).
- CHANGELOG.md, CLAUDE.md, docs/ — no standing Windows/Linux support
claims to remove.
The regex used `\Z` for end-of-string, which JavaScript does not support
(it is treated as the literal character `Z`). When the requested version
had no subsequent section in CHANGELOG.md — i.e., the newest release,
which is the common case — the lookahead could never match, the regex
returned null, and publish.yml fell back to
"See CHANGELOG.md for release details." instead of the real body.
Rewritten as two separate matches: find the heading line, then read to
the next `^## ` heading or EOF. Verified locally:
- `0.1.0` → "Hello Helmor." (was "See CHANGELOG.md for release details.")
- non-existent version → correct fallback
- multi-section CHANGELOG → extracts the requested section in isolation
Closes every compile-blocking platform-coupled call site. Every macOS
branch is preserved byte-for-byte behind #[cfg(target_os = "macos")] /
#[cfg(unix)] / #[cfg(windows)] guards; new branches land Windows/Linux
behavior without replacing the existing macOS logic.
Changes:
- data_dir.rs, import.rs: dirs_home() / conductor-import home resolver
now prefers %USERPROFILE% on Windows, falls back to %HOME% for
MSYS/Cygwin setups. Unix path unchanged.
- sidecar.rs resolve_sidecar_path(): appends .exe on Windows — Tauri
places externalBin as helmor-sidecar.exe next to helmor.exe. Latent
Windows bug.
- workspace/scripts.rs → cfg(unix)-only. New scripts_windows_stub.rs
exposes the same public API surface (ScriptEvent, ScriptContext,
ScriptProcessManager, run_script) but run_script emits a structured
ScriptEvent::Error ("not yet supported on Windows") + Exited{None}
so the frontend's state machine settles. workspace/mod.rs cfg-picks
the right module. Phase 6 replaces the stub with a real
cmd.exe /C + piped-stdio impl + Windows Job Object for descendant
kill semantics.
- workspace/lifecycle.rs archive hook: archive_shell() helper picks
$SHELL + "-c" on Unix, %COMSPEC% + "/C" on Windows. Env vars
unchanged. Phase 0's run_archive_hook_inner refactor already landed
the outcome type; this commit only swaps the shell selection.
- workspace/helpers.rs copy_symlink (Windows fallback): adds a
tracing::warn! flagging that symlink semantics are silently dropped
when copying into a non-Unix target. Behavior unchanged; visibility
added.
- commands/system_commands.rs: per-OS split.
* cli_install_target(): /usr/local/bin/helmor on Unix,
%LOCALAPPDATA%\Helmor\bin\helmor.exe on Windows (user-level,
no UAC prompt).
* cli_source_binary_name() appends .exe on Windows.
* detect_installed_editors_blocking: cfg-split into macOS (.app
scan), Windows (LOCALAPPDATA/ProgramFiles probe for Cursor,
VS Code, Windsurf, Zed, WebStorm, Sublime, Warp), Linux (XDG
.desktop file scan with minimal Exec= parser).
* open_workspace_in_editor + launch_editor_with_dir: macOS keeps
`open -a "App" dir`; Windows/Linux look up the absolute editor
path via detection and spawn directly, falling back to
`cmd /C start ""` / `xdg-open` if detection missed the editor.
- workspace/files/changes.rs: replace `hash-object -t tree /dev/null`
with the canonical SHA1 empty-tree constant
4b825dc642. Git constant across every
platform + version; removes /dev/null dependency that was brittle
on git-for-Windows.
- scripts/prepare-sidecar.mjs (new) + tauri.conf.json
beforeBuildCommand: replace the POSIX-only pipe chain
(cp + grep + cut + $(rustc -vV)) with a Node script using
fs.copyFileSync + execSync('rustc --print host-tuple'). Works on
cmd.exe / PowerShell / bash identically. Verified locally on
macOS — produces helmor-sidecar-aarch64-apple-darwin as before.
- .github/workflows/quality.yml rust-check-windows: removed
continue-on-error (Phase 1 soft-gate → Phase 2 strict gate).
Uses the new prepare-sidecar.mjs instead of bespoke bash.
Verified: cargo test --lib (411 pass), cargo test --tests (all
integration suites green), cargo clippy --all-targets -- -D warnings
clean, pnpm run typecheck clean, node scripts/prepare-sidecar.mjs
succeeds on macOS and stages helmor-sidecar-aarch64-apple-darwin.