26 Commits

Author SHA1 Message Date
Passion 62739e65f4 fix: resolve Windows MSI ICE30 by removing duplicate helmor-cli exter… (#865)
* 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>
2026-06-21 11:45:58 +08:00
Walaff Fernandes e8dbade1bf fix(sidecar): cross-compile sidecar to release target triple (Intel ships arm64 sidecar) (#844)
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>
2026-06-14 09:15:45 +08:00
Caspian 東澔 1e29a79fc8 feat: add quick panel — global-hotkey floating window to create tasks and chat (#791)
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).
2026-06-10 22:45:13 -07:00
JRBusiness 6cea31b094 Windows support (#739)
* 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>
2026-06-08 18:41:50 +08:00
Caspian 東澔 cad2390d5e Extend platform boundary: PTY and CLI-install seams (#747)
* 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.
2026-06-07 22:50:51 -07:00
Caspian 東澔 e4ab522f35 Add build platform boundary (#742)
* Add build platform boundary

* docs: Dosu updates for PR #742

---------

Co-authored-by: dosubot[bot] <131922026+dosubot[bot]@users.noreply.github.com>
2026-06-07 14:07:00 -07:00
Caspian 東澔 d17c1879de feat: stacked PR support (#721)
* 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>
2026-06-05 00:56:01 -07:00
Nathan L 2bf1250872 fix(scripts): accept openStartPage action in release announcement verifier
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.
2026-05-19 06:36:28 +08:00
Nathan L b6018d05f8 refactor: redesign release announcement catalog and move skills to .agents (#518)
- Consolidate release announcement catalog from separate id + published binding to single release-version-bound entries
- Simplify dismissal tracking from ids to release versions with legacy id→version migration
- Remove stamp script; add consume and verify scripts for announcement validation
- Move .codex/skills/* → .agents/skills/* (helmor-cli, helmor-release)
- Remove hand-maintained release-announcement-catalog.ts; generate from .announcements/*.json fragments
- Update release script names: release:stamp → release:announcements
2026-05-13 01:39:41 +08:00
Caspian 東澔 d02a9ad672 feat: group workspaces by repository in sidebar (#462)
* 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>
2026-05-12 09:38:44 +08:00
Nathan L e427036fd4 chore(scripts): align verify-bundled-cli with native-binary CLI layout (#364) 2026-05-03 20:49:30 +08:00
Nathan L 7025e5a003 fix(bundle): stage vendor binaries for the target arch, not host
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.
2026-04-29 13:13:54 +08:00
Caspian Zhao 05460788ba fix: stage sidecar binary before building helmor-cli
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.
2026-04-22 13:11:58 +08:00
Caspian Zhao 37482bdbb9 feat(cli): bundle helmor-cli in the app and install via symlink
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.
2026-04-22 00:48:31 +08:00
Nathan L 3dc1bf3b1c fix(release): emit v<version> tags via custom publish script
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.
2026-04-20 22:24:03 +08:00
Nathan L 505b9cdd66 fix(release): sync Cargo.lock version alongside Cargo.toml 2026-04-19 18:44:25 +08:00
Nathan L 8567d355d2 fix(release): add JIT entitlements so Bun sidecar starts under hardened runtime
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>
2026-04-18 04:12:20 +08:00
Caspian Zhao 4ce7364698 chore: drop Windows and Linux support, macOS-only from now on
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.
2026-04-18 01:28:40 +08:00
Caspian Zhao 4745c10619 fix(release): extract-release-notes works when target section is last
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
2026-04-17 22:35:08 +08:00
Caspian Zhao 14d0244704 Merge remote-tracking branch 'origin/main' into dohooo/sirius
# Conflicts:
#	.github/actions/build-sidecar/action.yml
#	.github/workflows/quality.yml
#	.github/workflows/test.yml
#	src-tauri/tauri.conf.json
2026-04-17 14:54:03 +08:00
Caspian Zhao 29397632a5 chore(pm): migrate from pnpm to bun
- package.json: packageManager pnpm@10.13.1 -> bun@1.3.2, scripts use bun run, add trustedDependencies for @tauri-apps/cli/playwright/esbuild/@parcel/watcher/husky
- Replace pnpm-lock.yaml with bun.lock (~5% smaller, auto-migrated)
- tauri.conf.json: beforeDevCommand pnpm exec vite -> bun x vite; beforeBuildCommand tail -> bun run build
- .husky/pre-commit: pnpm lint-staged -> bunx lint-staged
- CI: setup-js action switched to oven-sh/setup-bun@v2 only (drop pnpm/action-setup + setup-node); build-sidecar, build, test, quality, release-plan, publish-macos workflows updated; paths filter pnpm-lock.yaml -> bun.lock; publish-macos adds tauriScript: bun run tauri for tauri-action
- scripts/release-local-macos.sh + helmor.json + vite.config.ts comment updated
- .gitignore: add .bun-debug.log*

Warm frozen install measured ~0.94s vs pnpm ~3-8s previously.
All tests still pass (395 frontend, 116 sidecar); typecheck, lint, build clean.
2026-04-17 13:49:43 +08:00
Caspian Zhao 4e9a097b36 phase 2: make Windows + Linux compile (zero macOS regression)
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.
2026-04-17 11:47:36 +08:00
Caspian Zhao 1b5ce2615c fix: harden app updater status handling 2026-04-16 23:21:22 +08:00
Caspian Zhao 9d1a5a4f6f feat: add app updater and release automation 2026-04-16 23:00:18 +08:00
Nathan L 2eb85046ba feat(backend): add Conductor import module and clean up fixture remnants
- import.rs: SQLite backup API import from Conductor DB, with optional
  repo filter and automatic token redaction. Backs up existing DB first.
- New Tauri commands: import_conductor, conductor_import_available
- Enable rusqlite "backup" feature
- Remove scripts/conductor/ (export-repo-fixture.sh, README.md)
- Update AGENTS.md: document new module structure, data directory,
  remove fixture references
2026-04-04 00:16:58 +08:00
Caspian Zhao 636fa125cd Add fixture-backed workspace tabs and Conductor detail views 2026-04-02 20:55:26 +08:00