diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ce2d19..03767f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to the Toolpath workspace are documented here. +## Resume: cursor picker requires the `cursor` CLI — 2026-07-02 + +- **`path-cli`**: `path resume`'s harness picker no longer lists **cursor** + unless the `cursor` CLI is actually on `PATH`. `harness_available` previously + counted the always-present macOS `open` (the `open -a Cursor` fallback), so + cursor showed up even when Cursor wasn't installed. Now every harness gates + purely on its own binary being on `PATH`. + ## Derive: resolve duplicate step ids — 2026-07-01 - **`toolpath-convo`** (0.11.1): `derive_path` now guarantees the derived diff --git a/crates/path-cli/src/cmd_resume.rs b/crates/path-cli/src/cmd_resume.rs index 675dbd8..0754a31 100644 --- a/crates/path-cli/src/cmd_resume.rs +++ b/crates/path-cli/src/cmd_resume.rs @@ -305,21 +305,7 @@ pub(crate) fn harness_available( harness: crate::cmd_share::Harness, path_override: Option<&std::path::Path>, ) -> bool { - use crate::cmd_share::Harness; - if binary_on_path(harness.name(), path_override) { - return true; - } - if harness == Harness::Cursor { - #[cfg(target_os = "macos")] - { - return binary_on_path("open", path_override); - } - #[cfg(all(unix, not(target_os = "macos")))] - { - return binary_on_path("xdg-open", path_override); - } - } - false + binary_on_path(harness.name(), path_override) } const ALL_HARNESSES: &[crate::cmd_share::Harness] = &[ @@ -926,13 +912,28 @@ mod tests { assert!(err.to_string().contains("`gemini` isn't on PATH")); } - #[cfg(target_os = "macos")] #[test] - fn cursor_available_via_open_fallback_on_macos() { - let td = fake_path_with(&["open"]); - assert!(harness_available(Harness::Cursor, Some(td.path()))); - let picked = pick_harness(Some(HarnessArg::Cursor), None, Some(td.path())); - assert_eq!(picked.unwrap(), Harness::Cursor); + fn cursor_requires_its_cli_not_just_open() { + // Regression: the always-present macOS `open` must NOT make cursor + // look installed — it needs the real `cursor` CLI. + let only_open = fake_path_with(&["open"]); + assert!( + !harness_available(Harness::Cursor, Some(only_open.path())), + "cursor must not appear just because `open` exists" + ); + assert!( + pick_harness(Some(HarnessArg::Cursor), None, Some(only_open.path())) + .unwrap_err() + .to_string() + .contains("isn't on PATH") + ); + + let with_cursor = fake_path_with(&["cursor"]); + assert!(harness_available(Harness::Cursor, Some(with_cursor.path()))); + assert_eq!( + pick_harness(Some(HarnessArg::Cursor), None, Some(with_cursor.path())).unwrap(), + Harness::Cursor + ); } #[test]