Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 0 additions & 37 deletions .github/homebrew/agent.rb.tmpl

This file was deleted.

21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,29 @@ jobs:
- run: bun run typecheck
- run: bun run test
- run: bun run build
- run: shellcheck install.sh
# Compile-smoke: the release Bun-compiles a single binary, which bundles
# everything (unlike tsup) and can fail where `build` passes. Catch that
# here so a release tag never breaks on it.
- run: bun build src/cli.ts --compile --outfile /tmp/agent
- run: /tmp/agent --version
# Install-smoke: run install.sh for real against the binary above, laid
# out the way GitHub Releases serves it, then let the binary remove
# itself. GITHUB_PATH is unset so the startup-file branch is exercised.
- name: install.sh and agent uninstall round trip
run: |
set -euo pipefail
site="$RUNNER_TEMP/releases/latest/download"
mkdir -p "$site" "$RUNNER_TEMP/home"
tar -czf "$site/agent-linux-x64.tar.gz" -C /tmp agent
(cd "$site" && sha256sum agent-linux-x64.tar.gz > checksums.txt)
python3 -m http.server 8123 --directory "$RUNNER_TEMP/releases" >/dev/null 2>&1 &
sleep 1
env -u GITHUB_PATH HOME="$RUNNER_TEMP/home" SHELL=/bin/bash \
ELLIPSIS_DOWNLOAD_BASE=http://127.0.0.1:8123 \
sh install.sh --dir "$RUNNER_TEMP/bin"
"$RUNNER_TEMP/bin/agent" --version
grep -q "Ellipsis agent installer" "$RUNNER_TEMP/home/.bashrc"
HOME="$RUNNER_TEMP/home" "$RUNNER_TEMP/bin/agent" uninstall
test ! -e "$RUNNER_TEMP/bin/agent"
! grep -q "Ellipsis agent installer" "$RUNNER_TEMP/home/.bashrc"
47 changes: 6 additions & 41 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,28 @@ jobs:
bun run typecheck
bun run test

# One tarball per target. install.sh and `agent update` pick the one for
# the machine they run on, so the list here must match RELEASE_TARGETS in
# src/lib/install.ts (test/install.test.ts checks).
- name: Build platform binaries
run: |
set -euo pipefail
mkdir -p dist
for t in darwin-arm64 darwin-x64 linux-x64 linux-arm64; do
for t in darwin-arm64 darwin-x64 linux-x64 linux-arm64 linux-x64-musl linux-arm64-musl; do
echo "::group::build $t"
bun build src/cli.ts --compile --target=bun-"$t" --outfile agent
tar -czf "dist/agent-$t.tar.gz" agent
rm -f agent
echo "::endgroup::"
done

# checksums.txt is what install.sh and `agent update` verify against.
- name: Compute checksums
id: sha
run: |
set -euo pipefail
cd dist
for t in darwin-arm64 darwin-x64 linux-x64 linux-arm64; do
sum=$(sha256sum "agent-$t.tar.gz" | cut -d' ' -f1)
echo "${t//-/_}=$sum" >> "$GITHUB_OUTPUT"
done
sha256sum *.tar.gz > checksums.txt
cat checksums.txt

- name: Create GitHub release
uses: softprops/action-gh-release@v3
Expand All @@ -77,38 +77,3 @@ jobs:
files: |
dist/*.tar.gz
dist/checksums.txt

- name: Check out the Homebrew tap
uses: actions/checkout@v7
with:
repository: ellipsis-dev/homebrew-cli
# Write-scoped deploy key for the tap repo only (the workflow's own
# GITHUB_TOKEN can't reach a second repo). checkout configures the SSH
# remote + key, so the push step below authenticates over SSH.
ssh-key: ${{ secrets.HOMEBREW_TAP_DEPLOY_KEY }}
path: tap

- name: Regenerate the formula
run: |
set -euo pipefail
sed \
-e "s|__VERSION__|${{ steps.version.outputs.version }}|g" \
-e "s|__SHA_DARWIN_ARM64__|${{ steps.sha.outputs.darwin_arm64 }}|g" \
-e "s|__SHA_DARWIN_X64__|${{ steps.sha.outputs.darwin_x64 }}|g" \
-e "s|__SHA_LINUX_ARM64__|${{ steps.sha.outputs.linux_arm64 }}|g" \
-e "s|__SHA_LINUX_X64__|${{ steps.sha.outputs.linux_x64 }}|g" \
.github/homebrew/agent.rb.tmpl > tap/Formula/agent.rb

- name: Commit and push the formula
run: |
set -euo pipefail
cd tap
git config user.name "ellipsis-bot"
git config user.email "bot@ellipsis.dev"
git add Formula/agent.rb
if git diff --cached --quiet; then
echo "Formula unchanged; nothing to push."
else
git commit -m "agent ${{ steps.version.outputs.version }}"
git push
fi
53 changes: 33 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,28 @@ authenticates, opens a WebSocket, and streams results. It is open source
## Install

```sh
brew install ellipsis-dev/cli/agent
curl -fsSL https://raw.githubusercontent.com/ellipsis-dev/cli/main/install.sh | sh
```

The script downloads the binary for your OS and CPU from GitHub Releases,
checks its SHA-256, and puts it at `~/.local/bin/agent`. If that directory is
not on your PATH, it appends one line to your shell's startup file
(`--no-modify-path` to skip that). Pin a version with `ELLIPSIS_VERSION=2.30.0`
or `sh -s -- --version 2.30.0`; choose the directory with `--dir`.

In CI the same line works: inside GitHub Actions the directory is added to
`GITHUB_PATH`, and in a container `--dir /usr/local/bin` skips PATH setup
entirely. Alpine images get the musl build automatically.

```sh
agent update # replace the binary with the latest release (--check to only look)
agent uninstall # remove the binary and the PATH line (--purge to delete ~/.ellipsis too)
```

An installed binary checks for a newer release once a day, in the background,
and prints one line on stderr when it finds one. `ELLIPSIS_NO_UPDATE_CHECK=1`
turns that off; it is already off when `CI` is set or stderr is not a terminal.

## Teach your coding agent about Ellipsis

[`skills/ellipsis`](skills/ellipsis/SKILL.md) is an
Expand All @@ -39,10 +58,9 @@ skills:
## Usage

```sh
agent install # open the dashboard sign-in page, where you install Ellipsis
agent login # device-code auth against the active host
agent logout # remove stored credentials (--all for every host)
agent me # show the current credential's identity
agent auth login # device-code auth against the active host
agent auth logout # remove stored credentials (--all for every host)
agent auth status # active host, where the token came from, and who you are

agent host list # list configured hosts (the active one is marked *)
agent host add beta https://beta-api.ellipsis.dev # add a host and switch to it
Expand Down Expand Up @@ -104,7 +122,8 @@ agent usage # usage dashboard for the period
agent analytics reviewer --account-type bot # which apps review the most PRs
agent analytics pr --days 30 # PR volume/trend with human vs bot splits
agent analytics review --repo my-service # review totals + top reviewers
agent ping # check authenticated API connectivity
agent update # update the CLI to the latest release (--to <x.y.z> for a specific one)
agent uninstall # remove the CLI from this machine (--purge to delete ~/.ellipsis too)
```

Every command shown is singular. The plural spelling of each (`agent files`,
Expand All @@ -126,7 +145,7 @@ prints a clickable dashboard link. How the stream works is described in

### Auth

`agent login` uses the device-code flow: it requests a code pair, prints a
`agent auth login` uses the device-code flow: it requests a code pair, prints a
verification URL (and opens it unless `--no-browser`), and polls until you
approve the request in the dashboard. The issued user token is stored under
`~/.ellipsis/config.json` (mode 0600) and attributes sessions to you.
Expand All @@ -136,8 +155,7 @@ environment (`ELLIPSIS_API_TOKEN` / `ELLIPSIS_API_BASE_URL`, with the legacy
`ELLIPSIS_API_BASE` accepted as a fallback) → the **active host** in the config
file → default (prod). This lets the CLI run headlessly — e.g. inside an
Ellipsis cloud sandbox where a per-sandbox token and base URL are injected into
the environment — with no `agent login` and no config file on disk. `agent
logout` only clears the on-disk token (`--all` for every host); a token supplied
the environment — with no `agent auth login` and no config file on disk. `agent auth logout` only clears the on-disk token (`--all` for every host); a token supplied
via `ELLIPSIS_API_TOKEN` lives in the environment and keeps working until you
unset it.

Expand All @@ -151,8 +169,7 @@ them all (the active one marked `*`). Each host keeps its own token (so
switching doesn't re-authenticate) and its own dashboard/app URL. The app URL
is derived from the API URL by default (`api.` → `app.`); a self-hosted instance
whose dashboard host isn't a mechanical swap sets it explicitly with `agent host
add … --app-base <url>` (or `agent host set <name> --app-base <url>`). `agent
login` then authenticates the active host, and every link the CLI prints points
add … --app-base <url>` (or `agent host set <name> --app-base <url>`). `agent auth login` then authenticates the active host, and every link the CLI prints points
at that host's dashboard.

Hosts and tokens live in `~/.ellipsis/config.json` (mode 0600); set
Expand Down Expand Up @@ -228,20 +245,16 @@ npm run compile # single-binary build (bun)
### Releasing

Pushing a `v*` tag triggers `.github/workflows/release.yml`, which Bun-compiles
binaries for macOS and Linux (arm64 + x64), publishes a GitHub release with the
tarballs, and regenerates the formula in
[`ellipsis-dev/homebrew-cli`](https://github.com/ellipsis-dev/homebrew-cli).
one binary per target (macOS arm64 and x64, Linux arm64 and x64, both glibc
and musl), and publishes a GitHub release with the tarballs and a
`checksums.txt`. `install.sh` and `agent update` download from that release,
so publishing it is the whole distribution step. See
[`docs/RELEASING.md`](docs/RELEASING.md).

```sh
git tag v2.30.0 && git push origin v2.30.0
```

The cross-repo push to the tap uses a write-scoped **deploy key**: the public
half is registered on `ellipsis-dev/homebrew-cli` (Settings → Deploy keys, write
access), and the private half is stored as the `HOMEBREW_TAP_DEPLOY_KEY` secret
on this repo. The workflow checks out the tap over SSH with it. A deploy key is
scoped to that one repo only — no account-wide PAT involved.

### Status

The full public REST surface (auth, sessions, session steps, configs,
Expand Down
32 changes: 23 additions & 9 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Releasing the CLI (maintainers)

The CLI ships only as a Homebrew formula from the `ellipsis-dev/homebrew-cli`
tap. It is never published to npm: `package.json` is `private`, has no `bin`,
and there is no `publishConfig`.
The CLI ships only as Bun-compiled binaries on GitHub Releases. Users install
with `install.sh` (`curl -fsSL https://raw.githubusercontent.com/ellipsis-dev/cli/main/install.sh | sh`)
and stay current with `agent update`. Both download from the release assets
and verify them against `checksums.txt`. It is never published to npm:
`package.json` is `private`, has no `bin`, and there is no `publishConfig`.

CLI **2.X.Y** always uses SDK **0.X.Y**. For example, CLI **2.30.0** uses
`@ellipsis-dev/sdk` **0.30.0**. Keep the CLI version and exact SDK dependency
Expand All @@ -17,12 +19,16 @@ takes a version input in the Actions UI). On a tag push it:
1. Installs dependencies with the frozen lockfile, checks that the release
version matches `package.json` and the installed SDK follows the version
rule, then runs typechecking and tests. Mismatches stop the release.
2. Cross-compiles four binaries (`darwin-arm64`, `darwin-x64`, `linux-x64`,
`linux-arm64`) with `bun build --compile`, tars each, and computes SHA-256
checksums.
3. Creates the GitHub release with the tarballs and `checksums.txt`.
4. Regenerates `Formula/agent.rb` in the tap repo from the template and pushes
it, so `brew install ellipsis-dev/cli/agent` picks up the new version.
2. Cross-compiles six binaries (`darwin-arm64`, `darwin-x64`, `linux-x64`,
`linux-arm64`, `linux-x64-musl`, `linux-arm64-musl`) with
`bun build --compile` and tars each. The list lives in the workflow and in
`RELEASE_TARGETS` in `src/lib/install.ts`; a test keeps them equal.
3. Writes `checksums.txt` (`sha256sum` output) and creates the GitHub release
with the tarballs and that file.

Nothing else needs to happen: `install.sh` resolves the newest release through
GitHub's `releases/latest/download/` redirect, and installed binaries learn
about it from their daily background check.

The manual steps (Hunter cuts releases) are: commit the version updates and
SDK migration, ensure CI is green, then create and push the matching `v2.X.Y`
Expand All @@ -34,3 +40,11 @@ and releases. `bun run compile` checks the CLI/SDK pair before building;
`./agent --version` reports `2.30.0` for this version, including local builds.
Run `bun run check:versions` to check the pair without building, or
`bun run check:versions 2.30.0` to also validate an intended release version.

## Trying the installer without a release

CI runs `install.sh` against a locally built binary served from a temporary
directory laid out like GitHub Releases (`latest/download/<tarball>` plus
`checksums.txt`), by pointing `ELLIPSIS_DOWNLOAD_BASE` at it. The same trick
works on a laptop with `python3 -m http.server`; see the install-smoke step in
`.github/workflows/ci.yml`.
Loading
Loading