Skip to content

Commit 43a874f

Browse files
authored
feat(install): Nix flake, Homebrew formula, and install matrix docs (#76)
<!-- markdownlint-disable-next-line first-line-heading --> ## Summary Open three real ways to install `ox` so users don't have to clone and `cargo install --path` from a working tree. Ships **prebuilt-tarball, Homebrew tap, cargo, and Nix** paths plus a `docs/guide/install.md` matrix mirroring Anthropic's Claude Code setup page (system requirements → install → verify → update → uninstall). ## Design decisions - **Self-tap Homebrew, no separate `homebrew-oxide-code` repo.** Users tap this repo (`brew tap hakula139/oxide-code https://github.com/hakula139/oxide-code`) and `Formula/oxide-code.rb` resolves in place. Avoids a second-repo round-trip and a PAT for tap-bumping. Trade-off: a packaging file in the project tree, regenerated by `scripts/update-homebrew-formula.sh vX.Y.Z` after each release. - **Formula uses prebuilt tarballs, flake source-builds.** Homebrew gets an instant install (the GitHub Release artifacts already exist). Nix users compile via `rustPlatform.buildRustPackage` so the flake works on any platform the toolchain supports, including ones the release matrix doesn't target. - **Whitelist over blacklist for the flake source filter.** Only `Cargo.toml`, `Cargo.lock`, `rust-toolchain.toml`, and `crates/` enter the build context. A new top-level config file is silently excluded instead of silently bundled into the cargo build cache. - **`nativeCheckInputs = [ git ]` + writable `$HOME`, not `doCheck = false`.** Out-of-tree flakes commonly disable tests, but skipping the entire suite hides real failures. The 19 sandbox-failing tests cluster cleanly: a few were genuinely brittle (assumed runner cwd was inside the oxide-code repo) and the rest just needed `git` on PATH. Refactor + provide the env, and all 1864 tests run inside Nix. - **Refactor or delete brittle cwd-dependent tests.** `find_git_root_inside_repo` and two siblings used `std::env::current_dir()` to find a real git repo; refactor to a tempdir + `init_git_repo`, matching the `*_outside_repo` shape. The redundant `build_prompt_includes_user_context_with_claude_md` and `execute_forwards_process_cwd_through_execute_in` plumbing tests are subsumed by `build_user_context_with_claude_md` and the `execute_in_*` family — drop them. ## Changes | File | Description | | ---- | ----------- | | `flake.nix` | New flake — `packages.${system}.{default, oxide-code}` source-built via `rustPlatform.buildRustPackage`. Whitelist source filter, `git` in `nativeCheckInputs`, `preCheck` writable HOME. | | `flake.lock` | Pinned inputs — nixpkgs `nixos-25.11`, `numtide/flake-utils`, `oxalica/rust-overlay`. | | `Formula/oxide-code.rb` | Self-tap Homebrew formula targeting the GitHub Release artifacts (macOS aarch64 / x86_64, Linux x86_64). Tested with `brew style`; sha256 sums verified against the live `v0.1.0-alpha.1` artifacts. | | `scripts/update-homebrew-formula.sh` | Regenerates `Formula/oxide-code.rb` from a release tag's `.sha256` sidecars. Run after `upload-assets` finishes. | | `prompt.rs` | Refactor `find_git_root_inside_repo` to use a tempdir + `init_git_repo`. Drop redundant `build_prompt_includes_user_context_with_claude_md`. | | `slash/diff.rs` | Drop `execute_forwards_process_cwd_through_execute_in`; covered by `execute_in_*` against `fresh_repo()`-controlled inputs. | | `docs/guide/install.md` | New install matrix — system requirements, four install paths, verify, update, uninstall. | | `docs/guide/README.md` | Index `install.md` at the top of the user-guide table. | | `docs/guide/quickstart.md` | Replace the bare `cargo install --path` line with a pointer to the install matrix and the shortest macOS / Linux one-liner (Homebrew). | | `README.md` | New `## Installation` section with the four most common one-liners; drop the `## Building from Source` subsection (superseded by the install matrix). | | `RELEASING.md` | Add Step 8: refresh the Homebrew formula after assets land. | | `CLAUDE.md` | List `installation` first in the `docs/guide/` user-doc summary. | | `.cspell/words.txt` | Add `MSRV`, `msys`, `nixfmt`, `nixos`, `nixpkgs`, `numtide`, `oxalica`, `pkgs`, `pname`, `rustc`, `untap`. | ## Test plan - [x] `cargo fmt --all --check` - [x] `cargo clippy --all-targets -- -D warnings` — zero warnings - [x] `cargo test` — 1862 passed (down from 1864 since two redundant tests were dropped) - [x] `pnpm lint` — markdownlint clean - [x] `pnpm spellcheck` — cspell clean - [x] `nix build .#oxide-code` — sandbox build succeeds, full test suite runs - [x] `brew style ./Formula/oxide-code.rb` — clean - [x] `./scripts/update-homebrew-formula.sh v0.1.0-alpha.1` — regenerates the committed formula bytes-identically - [x] sha256 of fetched `oxide-code-aarch64-apple-darwin.tar.gz` matches the formula's pinned digest - [ ] Manual: `nix run github:hakula139/oxide-code -- --version` once the branch lands on `main` (flake-by-branch refs are slower to refresh than tag refs) - [ ] Manual: `brew tap hakula139/oxide-code https://github.com/hakula139/oxide-code && brew install oxide-code && ox --version` once the formula is on `main`
1 parent 43513d7 commit 43a874f

13 files changed

Lines changed: 458 additions & 54 deletions

File tree

.cspell/words.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,21 @@ MMIX
4747
mplan
4848
mpsc
4949
msvc
50+
msys
5051
mtimes
52+
MSRV
5153
MULT
54+
nixfmt
55+
nixos
56+
nixpkgs
5257
nonewline
5358
nucleo
59+
numtide
60+
oxalica
5461
onig
5562
pgid
63+
pkgs
64+
pname
5665
println
5766
pulldown
5867
RAII
@@ -64,6 +73,7 @@ rfind
6473
riscv
6574
rrggbb
6675
rsplit
76+
rustc
6777
rustls
6878
serde
6979
serie
@@ -86,6 +96,7 @@ tracing
8696
unmaterialized
8797
unresumable
8898
unrotated
99+
untap
89100
urandom
90101
usize
91102
venv

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ ox # Start an interactive session
162162
## Documentation
163163

164164
- [`docs/README.md`](docs/README.md) — top-level index of design specs, research notes, user guides, and the roadmap.
165-
- [`docs/guide/`](docs/guide/) — user-facing docs (quickstart, configuration, slash commands, instructions, sessions, theming).
165+
- [`docs/guide/`](docs/guide/) — user-facing docs (installation, quickstart, configuration, slash commands, instructions, sessions, theming).
166166
- [`docs/design/`](docs/design/) and [`docs/research/`](docs/research/) — internal architecture decisions and external research, both organized by topic (api, session, slash, tools, tui). Each subdirectory has its own README with per-doc summaries.
167167
- [`docs/roadmap.md`](docs/roadmap.md) — working features, current focus, and explicit non-goals.
168168

Formula/oxide-code.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Regenerated by scripts/update-homebrew-formula.sh after each release tag.
2+
# Edit that script, not this file, when the formula's structure needs to change.
3+
4+
class OxideCode < Formula
5+
desc "Terminal-based AI coding assistant written in Rust"
6+
homepage "https://github.com/hakula139/oxide-code"
7+
version "0.1.0-alpha.1"
8+
license "MIT"
9+
10+
on_macos do
11+
on_arm do
12+
url "https://github.com/hakula139/oxide-code/releases/download/v0.1.0-alpha.1/oxide-code-aarch64-apple-darwin.tar.gz"
13+
sha256 "adb399884afee3539103f7a5a2c8d5f78f1951f6c3762b7b79d92a519d891919"
14+
end
15+
on_intel do
16+
url "https://github.com/hakula139/oxide-code/releases/download/v0.1.0-alpha.1/oxide-code-x86_64-apple-darwin.tar.gz"
17+
sha256 "f0762669729fec25b7d3f2f1911f250befd56a06d577f11b0367c4d16bdfabfb"
18+
end
19+
end
20+
21+
on_linux do
22+
on_intel do
23+
url "https://github.com/hakula139/oxide-code/releases/download/v0.1.0-alpha.1/oxide-code-x86_64-unknown-linux-gnu.tar.gz"
24+
sha256 "063598d0f801d4d53628cda63e573e6af57288c116af35d04d81c3f959ad6e9b"
25+
end
26+
end
27+
28+
def install
29+
bin.install "ox"
30+
end
31+
32+
test do
33+
assert_match version.to_s, shell_output("#{bin}/ox --version")
34+
end
35+
end

README.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,36 @@ Early development. What works today:
2828

2929
See [`docs/roadmap.md`](docs/roadmap.md) for current focus and plans.
3030

31-
## Usage
31+
## Installation
32+
33+
Four install paths are supported: prebuilt tarball, Homebrew tap, `cargo install`, and Nix flake. See [Installation](docs/guide/install.md) for the full matrix. The most common one-liners:
3234

3335
```bash
34-
export ANTHROPIC_API_KEY=sk-ant-...
35-
ox
36-
```
36+
# macOS aarch64 (Apple Silicon)
37+
curl -fsSL https://github.com/hakula139/oxide-code/releases/latest/download/oxide-code-aarch64-apple-darwin.tar.gz | tar -xz
38+
sudo mv ox /usr/local/bin/
3739

38-
## Documentation
40+
# Homebrew (macOS, Linux)
41+
brew tap hakula139/oxide-code https://github.com/hakula139/oxide-code
42+
brew install oxide-code
3943

40-
See the [user guide](docs/guide/) for installation, configuration, slash commands, instruction files, sessions, and theming.
44+
# From source (any Rust-supported platform)
45+
cargo install --git https://github.com/hakula139/oxide-code --locked
4146

42-
## Building from Source
47+
# Nix
48+
nix profile install github:hakula139/oxide-code
49+
```
4350

44-
Requires [Rust](https://www.rust-lang.org/tools/install) 1.91+ (uses edition 2024).
51+
## Usage
4552

4653
```bash
47-
cargo build --release
54+
export ANTHROPIC_API_KEY=sk-ant-...
55+
ox
4856
```
4957

50-
The binary will be at `target/release/ox`.
58+
## Documentation
59+
60+
See the [user guide](docs/guide/) for installation, configuration, slash commands, instruction files, sessions, and theming.
5161

5262
## Development
5363

RELEASING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ Any prose that should land in the changelog must come from a commit message: use
4343

4444
Each archive contains the `ox` binary.
4545

46+
8. Refresh the Homebrew formula against the published artifacts and commit:
47+
48+
```bash
49+
./scripts/update-homebrew-formula.sh vX.Y.Z
50+
git commit -am "chore(release): refresh Homebrew formula for vX.Y.Z"
51+
git push
52+
```
53+
54+
The script regenerates `Formula/oxide-code.rb` by fetching each `.sha256` sidecar from the release. Run it after the workflow finishes uploading assets, otherwise the sidecar URLs return 404.
55+
4656
## Installing `git-cliff`
4757

4858
```bash

crates/oxide-code/src/prompt.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,6 @@ mod tests {
154154
assert!(parts.system_joined().contains("test-model"));
155155
}
156156

157-
#[tokio::test]
158-
async fn build_prompt_includes_user_context_with_claude_md() {
159-
// Runs inside the oxide-code repo so CLAUDE.md exists and `user_context` is populated.
160-
let parts = build_prompt("test-model").await;
161-
let ctx = parts
162-
.user_context
163-
.as_deref()
164-
.expect("expected user context from project CLAUDE.md");
165-
assert!(ctx.contains("<system-reminder>"));
166-
assert!(ctx.contains("# CLAUDE.md"));
167-
assert!(ctx.contains("# Current date"));
168-
assert!(ctx.contains("</system-reminder>"));
169-
}
170-
171157
#[tokio::test]
172158
async fn build_prompt_system_does_not_contain_user_instructions() {
173159
let parts = build_prompt("test-model").await;
@@ -308,13 +294,15 @@ mod tests {
308294

309295
#[tokio::test]
310296
async fn find_git_root_inside_repo() {
311-
let cwd = std::env::current_dir().expect("cwd should be available");
312-
let root = find_git_root(&cwd).await;
313-
assert!(root.is_some(), "test must run inside a git repo");
314-
assert!(
315-
root.as_ref().unwrap().join(".git").exists(),
316-
"root should contain .git"
317-
);
297+
let tmp = tempfile::tempdir().expect("failed to create tempdir");
298+
init_git_repo(tmp.path());
299+
300+
let root = find_git_root(tmp.path()).await.expect("expected git root");
301+
// Compare via canonicalize — macOS routes tempdirs through `/private`.
302+
let expected = tmp.path().canonicalize().unwrap();
303+
let actual = root.canonicalize().unwrap();
304+
assert_eq!(actual, expected);
305+
assert!(actual.join(".git").exists(), "root should contain .git");
318306
}
319307

320308
#[tokio::test]

crates/oxide-code/src/slash/diff.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -205,18 +205,6 @@ mod tests {
205205
assert!(!DiffCmd.description().is_empty());
206206
}
207207

208-
// ── execute ──
209-
210-
#[test]
211-
fn execute_forwards_process_cwd_through_execute_in() {
212-
let mut chat = ChatView::new(&Theme::default(), false);
213-
let info = test_session_info();
214-
let result = DiffCmd.execute("", &mut SlashContext::new(&mut chat, &info));
215-
assert_eq!(result, Ok(SlashOutcome::Done));
216-
assert_eq!(chat.entry_count(), 1);
217-
assert!(!chat.last_is_error());
218-
}
219-
220208
// ── execute_in ──
221209

222210
#[test]

docs/guide/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
Documentation for using oxide-code (`ox`).
44

5-
| Document | Description |
6-
| ------------------------------------ | ------------------------------------------------ |
7-
| [Quickstart](quickstart.md) | Install, first run, basic usage |
8-
| [Configuration](configuration.md) | API credentials, model selection, environment |
9-
| [Slash Commands](slash-commands.md) | Built-in client-side commands and autocomplete |
10-
| [Instruction Files](instructions.md) | CLAUDE.md / AGENTS.md setup and discovery rules |
11-
| [Sessions](sessions.md) | Session persistence, listing, and resume |
12-
| [Theming](theming.md) | Built-in palettes, custom themes, slot overrides |
5+
| Document | Description |
6+
| ------------------------------------ | ---------------------------------------------------------- |
7+
| [Installation](install.md) | Prebuilt tarball, Homebrew, cargo, Nix — and verify/update |
8+
| [Quickstart](quickstart.md) | First run, credentials, basic usage |
9+
| [Configuration](configuration.md) | API credentials, model selection, environment |
10+
| [Slash Commands](slash-commands.md) | Built-in client-side commands and autocomplete |
11+
| [Instruction Files](instructions.md) | CLAUDE.md / AGENTS.md setup and discovery rules |
12+
| [Sessions](sessions.md) | Session persistence, listing, and resume |
13+
| [Theming](theming.md) | Built-in palettes, custom themes, slot overrides |

docs/guide/install.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Installation
2+
3+
How to install `ox` on macOS, Linux, and Windows. For first-run setup once installed, see the [quickstart](quickstart.md).
4+
5+
## System requirements
6+
7+
- **OS**: macOS 12+, a recent glibc-based Linux, or Windows 10+ (Alpine / musl-based Linux needs the source-build path)
8+
- **Architecture**: `x86_64` everywhere, plus `aarch64` on macOS (Apple Silicon)
9+
- **Network**: outbound HTTPS to `api.anthropic.com`
10+
- **From-source builds only**: [Rust](https://www.rust-lang.org/tools/install) 1.91+
11+
12+
## Install `ox`
13+
14+
Pick whichever path matches how you usually install developer tools.
15+
16+
### Prebuilt binary
17+
18+
Download the latest release archive from [Releases](https://github.com/hakula139/oxide-code/releases/latest), extract, and move the `ox` binary onto your `PATH`:
19+
20+
```bash
21+
# macOS aarch64 (Apple Silicon)
22+
curl -fsSL https://github.com/hakula139/oxide-code/releases/latest/download/oxide-code-aarch64-apple-darwin.tar.gz | tar -xz
23+
sudo mv ox /usr/local/bin/
24+
25+
# macOS x86_64 (Intel)
26+
curl -fsSL https://github.com/hakula139/oxide-code/releases/latest/download/oxide-code-x86_64-apple-darwin.tar.gz | tar -xz
27+
sudo mv ox /usr/local/bin/
28+
29+
# Linux x86_64
30+
curl -fsSL https://github.com/hakula139/oxide-code/releases/latest/download/oxide-code-x86_64-unknown-linux-gnu.tar.gz | tar -xz
31+
sudo mv ox /usr/local/bin/
32+
33+
# Windows x86_64 (Git Bash / MSYS / WSL)
34+
curl -fsSLO https://github.com/hakula139/oxide-code/releases/latest/download/oxide-code-x86_64-pc-windows-msvc.zip
35+
unzip oxide-code-x86_64-pc-windows-msvc.zip
36+
```
37+
38+
Each archive ships a single `ox` executable. Each release also publishes a `<archive>.sha256` sidecar you can verify against.
39+
40+
### Homebrew (macOS, Linux)
41+
42+
Tap this repo, then install:
43+
44+
```bash
45+
brew tap hakula139/oxide-code https://github.com/hakula139/oxide-code
46+
brew install oxide-code
47+
```
48+
49+
The formula pulls the prebuilt tarball published by `.github/workflows/release.yml`, so the install is binary-only and needs no Rust toolchain.
50+
51+
### From source via cargo
52+
53+
If you already have a Rust toolchain set up:
54+
55+
```bash
56+
cargo install --git https://github.com/hakula139/oxide-code --locked
57+
```
58+
59+
This compiles `oxide-code` from the `main` branch and installs `ox` to `~/.cargo/bin`. Pass `--tag vX.Y.Z` to pin a specific release.
60+
61+
### Nix
62+
63+
The repo ships a flake that source-builds `ox`:
64+
65+
```bash
66+
nix run github:hakula139/oxide-code # one-shot
67+
nix profile install github:hakula139/oxide-code # install to user profile
68+
```
69+
70+
Or as a flake input from another project:
71+
72+
```nix
73+
inputs.oxide-code.url = "github:hakula139/oxide-code";
74+
# Outputs: packages.${system}.{default, oxide-code}
75+
```
76+
77+
## Verify your installation
78+
79+
```bash
80+
ox --version
81+
```
82+
83+
If `ox` is on `PATH`, this prints the version string. A `command not found` means the binary didn't land somewhere your shell searches, so re-check the move or install step for the path you used.
84+
85+
## Update `ox`
86+
87+
| Install method | Update command |
88+
| --------------- | ------------------------------------------------------------------------------------------ |
89+
| Prebuilt binary | Re-run the `curl ... \| tar -xz` + `sudo mv` block from above against `/releases/latest/`. |
90+
| Homebrew | `brew update && brew upgrade oxide-code` |
91+
| Cargo | `cargo install --git https://github.com/hakula139/oxide-code --locked --force` |
92+
| Nix profile | `nix profile upgrade oxide-code` (or refresh the flake input in your project) |
93+
94+
## Uninstall `ox`
95+
96+
| Install method | Uninstall command |
97+
| --------------- | -------------------------------------------------------------- |
98+
| Prebuilt binary | `sudo rm /usr/local/bin/ox` (or wherever you moved it) |
99+
| Homebrew | `brew uninstall oxide-code && brew untap hakula139/oxide-code` |
100+
| Cargo | `cargo uninstall oxide-code` |
101+
| Nix profile | `nix profile remove oxide-code` |
102+
103+
To also remove session data and config:
104+
105+
```bash
106+
rm -rf ${XDG_DATA_HOME:-$HOME/.local/share}/ox # session JSONLs
107+
rm -rf ${XDG_STATE_HOME:-$HOME/.local/state}/ox # log file
108+
rm -rf ${XDG_CONFIG_HOME:-$HOME/.config}/ox # user config (if you wrote one)
109+
```

docs/guide/quickstart.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
## Install
44

5-
Requires [Rust](https://www.rust-lang.org/tools/install) 1.91+ (uses edition 2024).
5+
See [Installation](install.md) for the full matrix (prebuilt tarball, Homebrew, cargo, Nix). The shortest path on macOS / Linux:
66

77
```bash
8-
cargo install --path crates/oxide-code
8+
brew tap hakula139/oxide-code https://github.com/hakula139/oxide-code
9+
brew install oxide-code
910
```
1011

1112
## Set up credentials

0 commit comments

Comments
 (0)