install.sh's inline PATH fallback interpolated COMPOSIO_INSTALL_DIR raw
into the fish and zsh rc lines, and escaped only " in the bash arm. Since
that fallback is exactly what runs when `composio install` rejects a path
and exits non-zero, a directory containing $(...) or a backtick was
written unescaped into ~/.zshrc or config.fish and became live command
substitution at the next shell start.
Escape backslash, backtick, $ and " for every arm before the value is
embedded, skipping the backtick for fish (which neither substitutes it
nor recognizes \` inside double quotes). The $HOME prefix is re-applied
after escaping so the intended variable reference survives while a
user-supplied $ does not.
Replace the base installer's --shell flag with a COMPOSIO_INSTALL_SHELL
environment variable, matching the COMPOSIO_INSTALL_VERSION precedent
and reading more naturally in the curl-pipe form:
curl -fsSL https://composio.dev/install | COMPOSIO_INSTALL_SHELL=zsh sh
The variable is validated before any network call. Shell variants set
it explicitly when invoking the base installer, so the route stays
authoritative over any inherited value. The composio install --shell
CLI flag and the delegation/fallback behavior are unchanged.
Teach install.sh a --shell <zsh|bash|fish> flag that performs the same
shell setup the /install/<shell> variants do: delegate to
'composio install --shell' when the installed CLI supports it, fall
back to writing the # Composio CLI PATH block inline otherwise.
The shell variants become thin wrappers that fetch the base installer
and append '--shell <name>' to the forwarded arguments, so the
delegation and fallback logic now lives in exactly one script. This
also removes the double-configuration hazard of a variant blindly
forwarding a user-supplied --shell.
Shell setup no longer depends on the composio.dev/install/<shell>
redirect rules existing: post-install guidance, docs, and release
notes now print 'curl -fsSL https://composio.dev/install | sh -s --
--shell <shell>', which works through the single existing redirect.
The variant scripts and their raw-URL preview commands keep working
for when the routes land.
Coverage: direct --shell delegation, fallback on unsupported or
failing CLI, missing and invalid values failing before any network
call, and a Docker e2e leg for the idempotent --shell bash flow.
- make the documented uninstall strip legacy three-line PATH blocks
(dangling 'export PATH' left cwd on PATH) and preserve symlinked rc
files by writing back in place instead of mv
- point readers at Configure your shell before 'composio login' when
~/.local/bin is not on PATH
- restore an Update section covering 'composio upgrade', version
pinning, and --beta
- drop the completions claim from install.sh post-install help; shell
routes configure PATH only
- replace the nonexistent @composio/cli@0.3.1-beta.2 example tag with
the published 0.3.1-beta.329
- add the missing changelog description and note that the uninstall
file list tracks the current release layout
This PR:
- follows up https://github.com/ComposioHQ/composio/pull/3943, replacing
its hardcoded message with a fix for the cause it worked around
- stops `install.sh` redirecting `composio install`'s stderr to a temp
file — that redirect is what silenced the command, since the CLI gates
every log, note and outro on stderr being a terminal, so the captured
file was always empty and the replay printed nothing
- probes `"$exe" --version` before delegating, so a binary that cannot
run still falls back to inline setup without spilling loader errors —
the reason the redirect existed in the first place
- makes `composio install` fall back to plain `ui.error` lines when
there is nothing to decorate, so container and CI installs that pipe the
session to a build log still get the report; interactive output is
unchanged. `canDecorate` is constant for a run, so each channel binds to
its decorated or plain form once rather than re-testing per message
- drops the unconditional "Composio was added to your PATH" line, which
printed on three paths where nothing was written: shell-not-detected and
unsafe-install-dir (both exit 0 after reporting manual setup), and the
inline fallback's unsupported-shell and unwritable-rc branches
- adds regression tests using the production `makeTerminalUI`
implementation with captured streams, so the real decoration gate is
exercised — the shared test double reports `canDecorate: false` but
prints everything anyway, which is why this went unnoticed through two
prior attempts
## Context
Measured against the released binary, `composio install` writes 0 bytes
when its stderr is not a terminal, and exits 0 without touching any rc
file when `$SHELL` is unset. Both were invisible to the installer:
| case | exit | stderr bytes | PATH block written |
| --- | --- | --- | --- |
| `2>file`, as `install.sh` did | 0 | 0 | yes |
| `$SHELL` unset | 0 | 0 | no |
| stderr on a terminal | 0 | full report | yes |
With the redirect gone, the delegated path prints what the user was
missing — `Updated ~/.zshrc` and `Restart your shell to apply changes` /
`source ~/.zshrc` — and the manual-setup text on the paths where PATH
genuinely was not touched. The closing block no longer needs a PATH
claim of its own, and the fallback keeps printing its own
`refresh_command`.
067c67b695 already tried to surface this
output by capturing it to a temp file and replaying it on success. That
replay has been a no-op for the same reason, so this guidance has never
reached a user since the delegation was introduced.
## How Has This Been Tested?
- new tests fail with `expected '' to contain 'PATH: will add'` (empty
output — the production bug) without the `install.cmd.ts` change, and
pass with it
- full CLI suite: 957 passed, 1 skipped; `pnpm typecheck` 14/14;
`validate:boundaries`, eslint and prettier clean, no new eslint disables
- released binary in a sandboxed `$HOME`: stderr to a file yields 0
bytes; stderr on a pty yields the full report
- CLI from source with stderr captured: 155 bytes of plain guidance
where it previously wrote nothing; decorated output on a terminal
unchanged
- `install.sh` driven through a harness built from the real script under
a pty: a working binary's stderr reaches the terminal, a broken binary's
loader noise stays suppressed and the fallback writes the PATH block and
prints `source ~/.zshrc`. The same harness against `next` shows the
swallow.
No changeset: `@composio/cli` is Changesets-ignored and `install.sh` is
not a published package. The CLI note went into `CHANGELOG.md` directly.
`install.sh` is served from `next`, so the installer half of this
reaches new installs on merge and works with already-released binaries.
The `install.cmd.ts` half only affects captured-stderr installs and
rides the next CLI release.
curl install.sh is now the only CLI install channel.
- INSTALL.md: drop npm/pnpm/yarn section; Windows guidance is WSL
- install.sh: Windows error no longer advises the dead npm package
- build-cli-binaries.yml: strip npm section from generated release INSTALL.md
- cli.install-health-check.yml: drop stale npm dist-tag/Homebrew comment
- cli.test-installation.yml: delete no-op npm-fallback job and its
toolchain-versions feeder job; prune summary references
- delete cli.bump-homebrew-tap.yml + bump-homebrew-formula.py (automation
never fired: GITHUB_TOKEN-published releases suppress release-triggered
workflows and HOMEBREW_TAP_TOKEN is dead)
- ts/packages/cli/package.json: drop unused publishConfig (package is private)
- cli-release skill + CLI AGENTS.md: remove Homebrew steps from release docs
- toolchain-versions.json: drop now-unused node_install_compat matrix
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CLI's PATH-setup output is swallowed by the installer's stderr
capture, so first-time users were never told the PATH change only takes
effect in a new shell — they ran `composio --help` in the same terminal,
got "command not found", and concluded the install had failed to set up
PATH. State it explicitly in the closing message.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-ups.
- install.sh ran `login --agent` without an origin marker while its `install`
and `setup` invocations both set one, so installer-driven agent logins were
indistinguishable from manual ones — the exact ambiguity this PR removes for
the other two.
- Hoist the release channel to a module constant; both inputs are constants, so
recomputing it per event was pointless.
- The changelog overstated the gap: a Homebrew user running `composio install`
by hand still emits install events, they just carry no installer origin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every CLI event now carries journey_stage (install|setup|login|connect|
execute|other) via an exhaustive event->stage map in events.ts, and
cli_channel (stable|beta) derived from the existing
inferSkillReleaseChannel(APP_VERSION). Both are applied by a single
buildEvent factory so no builder hand-writes them.
install.sh now sets COMPOSIO_CLI_INVOCATION_ORIGIN=installer on its
"$exe" install run (matching the setup run), so script installs are
distinguishable from a user manually running `composio install`.
npm and Homebrew installs never run install.sh, so the install stage
only covers install.sh installs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-lands 4e68e652 (reverted by 333d2296) and extends it:
Part 1 (re-land):
- Dedicated CLI_INSTALL_* / CLI_SETUP_* lifecycle families; 'setup' added
to KNOWN_COMMAND_TOKENS so it is no longer misattributed to
command_path='composio'
- Per-host CLI_PLUGIN_SETUP_SUCCEEDED / CLI_PLUGIN_UNINSTALL_SUCCEEDED
fired from installSetupTargets/uninstallSetupTargets
- install.sh auto-setup runs with COMPOSIO_CLI_INVOCATION_ORIGIN=installer
Part 2 (extensions, fired from inside setup.cmd.ts / setup.ts):
- CLI_SETUP_HOST_DETECTED per host after detection, with agent_host,
available, supported, host_version and normalized unsupported_reason_code
(codex_too_old | no_json_inspection | host_command_failed | unknown)
threaded through SetupTargetDetection
- CLI_PLUGIN_SETUP_FAILED per host (phase install|uninstall) tapped before
the existing mapError rethrow
- CLI_SETUP_CANCELLED (user declined confirm) and CLI_SETUP_SKIPPED
(--if-present with no supported host)
- stdout_is_tty added to the setup command properties
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
## Summary
- Resolve the default CLI install version from GitHub Releases instead
of git tags.
- Require the selected CLI release to include the current platform
archive asset and fall back to older stable CLI releases when the newest
release is missing that asset.
- Add a mocked install.sh regression test covering missing release
assets, ignored beta/provider releases, and tag-only versions with no
release.
## Tests
- `bash -n install.sh test/install-sh-release-resolution.test.sh`
- `pnpm run test:install-sh`
- `git diff --check`
- Parsed `.github/workflows/cli.test-installation.yml` with Ruby YAML
- Parsed `package.json` with Node JSON.parse
- Manual real install smoke test with temporary
HOME/COMPOSIO_INSTALL_DIR selected `@composio/cli@0.2.28`
## Notes
- This prevents the install health check from selecting tag-only
versions like `@composio/cli@0.2.29` when no GitHub release/asset exists
yet.
## Summary
- make `install.sh` choose the latest stable `@composio/cli@x.y.z` tag
by default instead of prerelease beta tags
- add a clearer `--agent` setup failure message for users already signed
in as a regular Composio user
## Tests
- `bash -n install.sh`
- verified the release-tag selection pipeline resolves
`@composio/cli@0.2.28` instead of `@composio/cli@0.2.28-beta.223`
## Summary
- add `composio signup`, `composio login --agent`, and `composio agent
signup` flows backed by agents.composio.dev
- make signup messaging explicit that `composio signup` creates/signs
into a Composio agent and that the flow is non-interactive
- add `composio agent login <composio_agent_key>` for restoring an
existing agent when the key was saved
- add `composio agent whoami`, `claim`, and JSON-only `inbox` commands
using the stored `~/.composio/agent.json` agent identity
- guard agent flows so a regular human CLI login cannot run `composio
agent ...` or overwrite itself with agent signup without logging out
first
- show expected agent-auth issues as a softer warning with next steps
instead of the global crash-style error panel
- warn and confirm before agent logout removes `~/.composio/agent.json`
/ `composio_agent_key`; `logout --force` skips the prompt
- update `install.sh --agent` to sign up/log in as an agent after
install
## Tests
- `pnpm --filter @composio/cli build`
- `pnpm --filter @composio/cli test --
test/src/commands/agent.cmd.test.ts`
- `pnpm --filter @composio/cli test --
test/src/commands/agent.cmd.test.ts test/src/commands/logout.cmd.test.ts
test/src/commands/login.cmd.test.ts`
- `pnpm lint` (passes with existing warnings only)
## CI
- GitHub checks are rerunning on the latest push.
## Summary
- replace runtime MCP SDK path resolution with static imports in the
structured-output helper
- ensure the helper works when shipped next to the standalone CLI binary
without repo-local node_modules
## Verification
- pnpm --filter @composio/cli run typecheck:src
- pnpm --filter @composio/cli test
test/src/services/run-subagent-acp.test.ts
- built and executed the bundled helper from a temp directory outside
the repo
## Summary
- Hardened `composio run` subagent execution to preserve structured
output and recover more reliably when the primary prompt result is
malformed.
- Added a dedicated MCP helper for structured output so helper runs can
write results through a file-backed path instead of relying only on
stderr/stdout parsing.
- Propagated run log and artifact paths through helper context so
subagent logs land in the run log file consistently.
- Skipped zsh completion installation in `install.sh` and the CLI
installer to avoid broken completion wiring for zsh users.
- Refreshed the CLI lockfile and bundled the new MCP runtime dependency
into the CLI package.
## Testing
- `Not run`
## Summary
This changes `composio run` so an installed binary can recover missing
`run-subagent-*.mjs` companion modules at runtime instead of failing
immediately.
## What changed
- add run-time companion repair in `composio run`
- persist the installed release tag so `run` can fetch the matching
release asset when repair is needed
- update binary upgrade flow to keep that release metadata current
- add CLI coverage for companion install metadata and missing companion
detection
- include a patch changeset for `@composio/cli`
## Root cause
`composio run` launches a child Bun process that imports helper modules
from disk. If the installed binary was missing the companion `.mjs`
files, the command failed with `Cannot find module ...
run-subagent-shared.mjs` before any user code ran.
## Impact
- future CLI binaries with this change can self-heal missing companion
modules during `composio run`
- install and upgrade flows now record the release tag needed for
deterministic repair
- already-shipped older binaries still need one reinstall to get this
repair logic
## Validation
- `pnpm --filter @composio/cli test -- run.cmd.test.ts`
- `pnpm --filter @composio/cli run build:binary`
- verified a binary-only temp install containing just `composio` plus
`release-tag.txt`; `composio run 'console.log("healed")'` restored the
three companion modules and executed successfully
## What changed
- make `build:binary:cross` emit the `run-subagent-*.mjs` companion
modules alongside the compiled CLI binary
- update the `Build CLI Binaries` workflow to include those companion
modules in the published release archives
- make `install.sh` copy companion modules when they are present, while
remaining backward compatible with older archives that only contain
`composio`
- fix the CLI installation workflow assertions to match the actual
installer behavior and paths
## Why
`composio run` launches a separate Bun runtime that imports the
`run-subagent-*` helpers from disk. The main binary alone is not
sufficient. The previous release workflow zipped only the `composio`
binary, so prerelease assets installed successfully but failed at
runtime with missing-module errors.
The installation test workflow also had stale expectations for
`~/.composio/bin/composio` and `COMPOSIO_INSTALL`, which caused false
negatives even when installation itself succeeded.
## Impact
- newly built CLI release assets will contain the files required for
`composio run`
- `install.sh` will install sidecars for new releases and ignore them
for older releases
- the installation workflow will validate the real install layout
instead of the stale `bin/` path
## Validation
- `pnpm --filter @composio/cli run build:binary:cross --target
bun-darwin-arm64` from the existing checkout emitted:
- `dist/binaries/composio-darwin-aarch64`
- `dist/binaries/companions/run-subagent-shared.mjs`
- `dist/binaries/companions/run-subagent-acp.mjs`
- `dist/binaries/companions/run-subagent-legacy.mjs`
- simulated the GitHub Actions archive step and verified the zip
contains `composio` plus all three companion modules
- earlier local binary/package validation on the main fix branch
confirmed the extracted binary runs `composio run
'console.log("hello")'` successfully once those files are packaged
The install script accepts COMPOSIO_GITHUB_URL, COMPOSIO_GITHUB_OWNER,
and COMPOSIO_GITHUB_REPO from the environment to construct the download
URL. A malicious override could redirect the download to an attacker-
controlled server, bypassing checksum verification (since checksums.txt
is fetched from the same source).
Fix:
- Require COMPOSIO_GITHUB_URL to start with https://
- Require COMPOSIO_GITHUB_OWNER and COMPOSIO_GITHUB_REPO to match
safe identifier patterns (alphanumeric, hyphens, underscores, dots)
The install command writes COMPOSIO_INSTALL_DIR into shell rc files
(.zshrc, .bashrc, config.fish). Previously the value was unquoted,
allowing shell metacharacters (;, `, $, |, etc.) to inject arbitrary
commands that execute every time the user opens a terminal.
Fix:
- Quote the install directory value in all path blocks (bash, zsh, fish)
- Add isUnsafePath validation that rejects paths with metacharacters
- Quote the value in the manual setup note for unknown shells
- Quote the fish and zsh fallback paths in install.sh
- Add test for unsafe path rejection
- Replace 2>/dev/null with a temp file capture so the CLI's TerminalUI
output (written to stderr) is shown when the command succeeds, but
suppressed on failure to avoid noise.
- Move the "To get started" guidance block outside the if/else so it
always displays regardless of which code path ran.
grep -qF does substring matching, so the PATH marker "# Composio CLI"
would falsely match "# Composio CLI completions". Switch to grep -qxF
to require full-line matches, consistent with the TS fileContains logic.
- Add cross-compilation scripts (build-binary-cross.ts, build-all-binaries.ts)
using Bun's --target flag, enabling all platforms to build from ubuntu-latest
- Add package-binaries.ts and generate-checksums.ts for release artifact preparation
- Restructure CI workflow into build + release jobs; release job aggregates
artifacts and generates checksums.txt
- Simplify install.sh: use git ls-remote refspec filter for version detection,
add SHA-256 checksum verification with graceful fallback, use mktemp/trap
for temp file cleanup, remove dead musl detection code
- Add checksum verification to composio upgrade command via fetchChecksums
and verifyChecksum helpers in upgrade-binary.ts