From 617db2e4beb318718810d921b32dee50c5eabe7c Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 13 Jul 2026 13:19:14 -0300 Subject: [PATCH] fix: derive executable name from plugin ID for raw binary installs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code releases are raw binaries, not archives. When proto installs a non-archive download, it renames the file on disk to the plugin's configured ID (tool.get_file_name() in proto's flow/install.rs), e.g. `claude-code.exe` for `proto plugin add claude-code ...`. v0.2.0/v0.3.0 hardcoded the executable as `claude`/`claude.exe` in locate_executables, which never exists on disk under the default plugin ID. On Windows this surfaced as: - "Unable to symlink binary, source file does not exist" warnings during `proto install claude-code` - `proto::commands::run::missing_alternate_binary` when running `claude` Fix by deriving the exe name from get_plugin_id() at runtime and marking it as the primary executable — the same pattern moonrepo's own `moon` plugin uses for its raw-binary releases. Verified end-to-end in an isolated PROTO_HOME on Windows with an arbitrary plugin ID: install produces no symlink warnings, and both the `claude` shim and bin link run successfully. --- .claude/agent-memory/archgate-developer/MEMORY.md | 3 +++ .../project_proto-raw-binary-rename.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/proto.rs | 15 +++++++++------ 5 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 .claude/agent-memory/archgate-developer/MEMORY.md create mode 100644 .claude/agent-memory/archgate-developer/project_proto-raw-binary-rename.md diff --git a/.claude/agent-memory/archgate-developer/MEMORY.md b/.claude/agent-memory/archgate-developer/MEMORY.md new file mode 100644 index 0000000..c4ec9ed --- /dev/null +++ b/.claude/agent-memory/archgate-developer/MEMORY.md @@ -0,0 +1,3 @@ +# Memory Index + +- [proto raw-binary rename](project_proto-raw-binary-rename.md) — proto renames non-archive downloads to the plugin ID; derive exe name via get_plugin_id(), never hardcode diff --git a/.claude/agent-memory/archgate-developer/project_proto-raw-binary-rename.md b/.claude/agent-memory/archgate-developer/project_proto-raw-binary-rename.md new file mode 100644 index 0000000..b8d819a --- /dev/null +++ b/.claude/agent-memory/archgate-developer/project_proto-raw-binary-rename.md @@ -0,0 +1,12 @@ +--- +name: proto-raw-binary-rename +description: proto renames non-archive (raw binary) downloads to the plugin ID on disk — locate_executables must derive the exe name from get_plugin_id() +metadata: + type: project +--- + +proto (moonrepo) renames a raw, non-archive prebuilt download to the **plugin ID** when moving it into the install dir (`tool.get_file_name()` in `crates/core/src/flow/install.rs`). The user-chosen ID from `proto plugin add ...` decides the on-disk filename, e.g. `claude-code.exe`. + +**Why:** Claude Code ships as a raw binary (no archive), so hardcoding any exe name in `locate_executables` breaks whenever the plugin ID differs. This caused the Windows "Unable to symlink binary" / `missing_alternate_binary` regression shipped in v0.2.0–v0.3.0 (PR #1 renamed the exe to `claude` on a wrong premise). + +**How to apply:** In `locate_executables`, always use `get_plugin_id()` + `env.os.get_exe_name(&id)` and `ExecutableConfig::new_primary` — same pattern as moonrepo's own `moon` plugin (tools/moon in moonrepo/plugins), which documents this exact behavior in a comment. Never hardcode the exe filename for raw-binary tools. diff --git a/Cargo.lock b/Cargo.lock index 47fe6e5..90303ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "claude_plugin" -version = "0.1.0" +version = "0.3.1" dependencies = [ "extism-pdk", "proto_pdk", diff --git a/Cargo.toml b/Cargo.toml index 77a6b1b..215fe79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "claude_plugin" -version = "0.1.0" +version = "0.3.1" edition = "2021" license = "MIT" publish = false diff --git a/src/proto.rs b/src/proto.rs index 613fbf3..3aa4dcd 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -77,14 +77,17 @@ pub fn locate_executables( ) -> FnResult> { let env = get_host_environment()?; - let exe_name = if env.os == HostOS::Windows { - "claude.exe" - } else { - "claude" - }; + // Claude Code releases are raw binaries, not archives. When proto installs + // a non-archive download, it renames the file on disk to the plugin's + // configured ID (e.g. `claude-code.exe` for `proto plugin add claude-code`), + // so the executable name must be derived from the ID at runtime. + let id = get_plugin_id()?; let mut exes = HashMap::default(); - exes.insert("claude".to_string(), ExecutableConfig::new(exe_name)); + exes.insert( + "claude".to_string(), + ExecutableConfig::new_primary(env.os.get_exe_name(&id)), + ); Ok(Json(LocateExecutablesOutput { exes,