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
1 change: 0 additions & 1 deletion .gitattributes

This file was deleted.

13 changes: 11 additions & 2 deletions .github/workflows/formula.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ name: Homebrew Formula
on:
pull_request:
paths:
- .github/actions/setup-nix/**
- .github/workflows/formula.yml
- .github/workflows/release.yml
- Formula/oxide-code.rb
- scripts/update-homebrew-formula.sh
- flake.lock
- flake.nix
- scripts/oxide-code-formula.rb
- scripts/update-homebrew-formula.nu
workflow_call:
inputs:
tag:
Expand Down Expand Up @@ -42,6 +46,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Nix
uses: ./.github/actions/setup-nix
with:
cachix_auth_token: ${{ secrets.CACHIX_AUTH_TOKEN }}

- name: Render formula
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -54,7 +63,7 @@ jobs:
fi
mkdir sidecars
gh release download "$tag" --pattern '*.sha256' --dir sidecars
./scripts/update-homebrew-formula.sh "$tag" sidecars
nix develop .#lint -c nu scripts/update-homebrew-formula.nu "$tag" sidecars

- name: Put Homebrew on PATH
if: runner.os == 'Linux'
Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ on:
tags: ['v[0-9]+.*']
pull_request:
paths:
- .gitattributes
- .github/workflows/ci.yml
- .github/workflows/release.yml
- about.hbs
- about.toml
- LICENSE
- licenses/**
- scripts/generate-notices.sh
- Cargo.lock
- Cargo.toml
- crates/*/Cargo.toml
Expand Down Expand Up @@ -119,7 +116,12 @@ jobs:
- name: Generate third-party notices
shell: bash
run: |
./scripts/generate-notices.sh "${{ matrix.target }}" THIRD-PARTY-NOTICES
cargo about generate \
--locked \
--manifest-path crates/ox/Cargo.toml \
--target "${{ matrix.target }}" \
--name about licenses \
-o THIRD-PARTY-NOTICES

- name: Build archive
id: binary
Expand Down Expand Up @@ -148,9 +150,6 @@ jobs:
tar -xzf "$ARCHIVE" -C smoke
fi

cmp LICENSE smoke/LICENSE
cmp THIRD-PARTY-NOTICES smoke/THIRD-PARTY-NOTICES

if [[ "$GITHUB_REF_TYPE" == 'tag' ]]; then
expected=${GITHUB_REF_NAME#v}
else
Expand Down
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
about.hbs
licenses/*.hbs
pnpm-lock.yaml
5 changes: 2 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ The tree lists only the top-level entries whose names leave their purpose unclea
.
├── .agents/ # Agent skills for the release procedure
├── .claude/ # Link to the agent skills for Claude Code
├── about.hbs # Plain-text third-party notice template
├── about.toml # Release dependency license selection
├── crates/ox/ # The `ox` binary crate with the CLI and the stdio frontends, mapped in docs/design/architecture.md
├── crates/ox-core/ # The `ox-core` library crate with the agent core, mapped in docs/design/architecture.md
├── crates/ox-tui/ # The `ox-tui` library crate with the terminal UI, mapped in docs/design/architecture.md
├── docs/ # User guides, conventions, design specs, and research notes
├── Formula/ # Homebrew formula, served from this repo as a tap
├── licenses/ # Licenses for bundled syntax definitions and themes
├── licenses/ # Third-party notice templates and bundled asset licenses
├── nix/ # Nix package and Home Manager module with its tests
└── scripts/ # Distribution notices, Homebrew formula updater, credential leak check, and comment width check
└── scripts/ # Homebrew formula renderer and Ruby template
```

## Read Before You Edit
Expand Down
3 changes: 1 addition & 2 deletions Formula/oxide-code.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Regenerated by scripts/update-homebrew-formula.sh for each release the formula follows.
# Edit that script, not this file, when the formula's structure needs to change.
# Generated by scripts/update-homebrew-formula.nu from scripts/oxide-code-formula.rb.

class OxideCode < Formula
desc "Terminal-based AI coding assistant written in Rust"
Expand Down
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,5 @@ The `v0.1.0` release PR also switches the install snippets to `releases/latest/d

The build matrix in `release.yml` lists the targets. Each release attaches one archive per target with a `.sha256` sidecar and a build provenance attestation, and release asset names belong to the [stability surface](#stability-surface).

- The platform floors in `docs/guide/install.md` bind the build. The Linux job fails when the binary needs a newer glibc than the guide promises, reading the floor from the phrase `Linux with glibc <version> or later`, so keep that wording when editing it. If a runner image update trips the check, either pin the Linux build to the previous image or raise the floor in the guide. The macOS job fails when the binary's minimum macOS version differs from the guide's `**macOS**: <version> or later` floor, so keep that wording too. Raising that floor also means changing `MACOSX_DEPLOYMENT_TARGET` in `release.yml` and the formula's `depends_on macos:` in `scripts/update-homebrew-formula.sh`.
- The platform floors in `docs/guide/install.md` bind the build. The Linux job fails when the binary needs a newer glibc than the guide promises, reading the floor from the phrase `Linux with glibc <version> or later`, so keep that wording when editing it. If a runner image update trips the check, either pin the Linux build to the previous image or raise the floor in the guide. The macOS job fails when the binary's minimum macOS version differs from the guide's `**macOS**: <version> or later` floor, so keep that wording too. Raising that floor also means changing `MACOSX_DEPLOYMENT_TARGET` in `release.yml` and the formula's `depends_on macos:` in `scripts/oxide-code-formula.rb`.
- Add a target by extending the matrix. Each target needs a runner that executes its binary, since every build job checks its archive there.
6 changes: 3 additions & 3 deletions docs/conventions/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you use [direnv](https://direnv.net), run `direnv allow` once. After that, `.

Entering the dev shell in this repository installs a [pre-commit](https://pre-commit.com) hook generated by [`git-hooks.nix`](https://github.com/cachix/git-hooks.nix). The shell generates its config, `.pre-commit-config.yaml`, as a gitignored symlink in each checkout, which is why [`AGENTS.md`](../../AGENTS.md#rules-for-every-change) asks for `nix develop -c true` in every new worktree.

The hook runs the compile-free subset of the [verification](../../AGENTS.md#verification) checks on staged files: file hygiene, Nix formatting and lints, ShellCheck, `rustfmt`, Prettier and Taplo formatting, `markdownlint`, and `cspell`, plus a `comment-width` check. Hooks that fix files in place fail the commit if they make changes, so stage the fixes and commit again. Checks that compile the workspace are excluded because their build cost would gate every commit.
The hook runs the compile-free subset of the [verification](../../AGENTS.md#verification) checks on staged files: file hygiene, Nix formatting and lints, ShellCheck, `rustfmt`, Prettier and Taplo formatting, `markdownlint`, and `cspell`. Hooks that fix files in place fail the commit if they make changes, so stage the fixes and commit again. Checks that compile the workspace are excluded because their build cost would gate every commit.

The Node-based hooks run tools pinned in `node_modules`, which pnpm installs before running them whenever it is missing or out of date with the lockfile. `nix flake check` leaves them out because its sandbox cannot install `node_modules`, and it leaves out `rustfmt` so that it never fetches the pinned toolchain. CI's Lint job runs the Node tools, and its Rust (Linux) job runs `cargo fmt --check`. Run `pnpm format`, `pnpm lint`, and `pnpm spellcheck` directly when changing documentation, comments, or config files.

Expand Down Expand Up @@ -63,7 +63,7 @@ The Node-based hooks run tools pinned in `node_modules`, which pnpm installs bef

Prettier and Taplo leave long strings alone, and no formatter covers shell code, so wrap YAML, TOML, and shell files by hand:

- Wrap comments as [Writing](writing.md#comments) describes, and keep other lines within about 100 columns, counting the indent. Leave a line long when it holds an unbreakable token, such as a URL or path, or sits in a heredoc body or literal block whose line breaks are content, unless it starts with `#`, which the `comment-width` hook checks as a comment.
- Wrap comments as [Writing](writing.md#comments) describes, and keep other lines within about 100 columns, counting the indent. Leave a line long when it holds an unbreakable token, such as a URL or path, or sits in a heredoc body or literal block whose line breaks are content.
- Wrap a long string only where its value stays the same, and break it where [Rust](rust.md#strings) breaks a continued literal.
- Split a shell command that runs past the limit at its pipelines and `&&` or `||` lists, and split one in a Markdown code block the same way where that helps readability. If a part is still too long, keep at most what names the operation on its first line, such as the command, its subcommands, and a leading script, pattern, or message, and put each further flag with its value, or each group of related arguments, on a line of its own. Indent continuation lines one level past the first line, and end each line but the last with `\`, unless it ends with `|`, `&&`, or `||`, which already continue the command. In a workflow, write the command in a `run: |` block.
- For a multiline command substitution, put the command on the next line and the closing `)` on its own line, aligned with the assignment.
Expand All @@ -81,7 +81,7 @@ CI runs on pushes to `main`, pull requests, and manual dispatch. The release wor
| Lint | `pnpm format`, `pnpm lint`, `pnpm spellcheck`, and `cargo deny check` in the flake's `lint` shell, which leaves out the pinned toolchain |
| Rust (Linux, macOS, Windows) | `cargo fmt --check` on Linux, then `cargo clippy --all-targets`, `cargo clippy`, `cargo doc --no-deps`, and the test suite under `cargo llvm-cov`, uploaded to Codecov, then on Linux a check that every snapshot baseline has a test |
| MSRV | `cargo hack check --rust-version` against `rust-version` |
| Nix (Linux, macOS) | `nix flake check`, which also runs the test suite in the build sandbox, then the package build with its `ox --version` and installed license notice checks |
| Nix (Linux, macOS) | `nix flake check`, which also runs the test suite in the build sandbox, then the package build with its `ox --version` check |

Branch protection requires only the CI Result job and Codecov's `codecov/patch` status. CI Result reads every job of the current run attempt from the Actions API and fails when any job of the CI workflow, including each matrix leg, concludes anything other than success or skipped. That catches a leg cancelled before a runner picks it up, which the `needs` results of a matrix job omit. CI Result runs once the jobs in its `needs` finish and counts any unfinished job as a failure, so list each new job there.

Expand Down
1 change: 0 additions & 1 deletion docs/conventions/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,3 @@ The tests drive headless and REPL runs. Check the TUI-only paths by hand in a sc
2. **`/model` swap**: Send a prompt on Opus, switch to Haiku with `/model`, and send another. Then switch back to Opus and send a third. Every reply succeeds and remembers the earlier exchanges.
3. **Cancellation**: Press Esc while a long reply streams, and check that the stream stops within a second. Then send a new prompt and verify that the reply answers only that prompt.
4. **Scrolling and the mouse**: In a bare terminal, inside tmux with `mouse on`, and over SSH, wheel-scroll the chat and check that tmux stays out of copy mode. Press PageUp until the jump-to-bottom pill appears, then press Ctrl+End and check that the chat follows the tail again. Drag-select a region and paste it elsewhere, double-click a word, triple-click a row, and hold a drag past the top edge until the chat scrolls. Press Ctrl+C on a selection and check that it copies without interrupting, then hold the bypass modifier and check that native selection still works. Click a `... +N lines` footer and a `Thought` header, and check that only that block expands. Click each pull request segment, then Ctrl+click it (Cmd+click in iTerm2 and Terminal.app), and check that each opens its page once. Quit, and check that the terminal screen is restored.
5. **Leak check**: Run `scripts/check-leaks.sh` in the environment the sessions used. It searches the logs, a SQL dump of the session store, and the saved tool output for each credential the live tests check and prints only the names of the variables whose values it finds.
2 changes: 1 addition & 1 deletion docs/conventions/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Conventions for comments, docstrings, documentation, commit and PR text, and str
- When editing, delete comments that repeat the code and correct any that have become misleading. Before deleting a comment that records a constraint, check whether the constraint still applies.
- Delete commented-out code because version control preserves it.
- Write `//` comments as prose. A docstring may use a Markdown list for parallel options or a sequence.
- Wrap comment and docstring prose at 100 columns, including indentation and comment markers. Fill each paragraph naturally, allowing sentences to share a line, and separate paragraphs with a blank comment line. Break early when it improves readability, such as to keep a code span or link together. Reword a paragraph whose last line would contain one short word. Preserve intentional layout in code examples, lists, tables, and diagrams. Apply the same rule to shell, Nix, YAML, and TOML comments, and to prose literals continued by hand. The `comment-width` pre-commit hook checks this limit on every line that starts with `//` in Rust or `#` elsewhere, which includes string, heredoc, and literal-block lines that start that way, and exempts a comment holding only a URL, an `<url>`, or a link reference definition. It skips trailing comments after code.
- Wrap comment and docstring prose at 100 columns, including indentation and comment markers. Fill each paragraph naturally, allowing sentences to share a line, and separate paragraphs with a blank comment line. Break early when it improves readability, such as to keep a code span or link together. Reword a paragraph whose last line would contain one short word. Preserve intentional layout in code examples, lists, tables, and diagrams. Apply the same rule to shell, Nix, YAML, and TOML comments, and to prose literals continued by hand.

## Docstrings

Expand Down
26 changes: 3 additions & 23 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -118,24 +118,6 @@

shellcheck.enable = true;

comment-width = {
enable = true;
entry = pkgs.lib.getExe (
pkgs.writeShellApplication {
name = "check-comment-width";
runtimeInputs = [ pkgs.gawk ];
text = builtins.readFile ./scripts/check-comment-width.sh;
}
);
types_or = [
"nix"
"rust"
"shell"
"toml"
"yaml"
];
};

rustfmt = {
enable = true;
packageOverrides = {
Expand Down Expand Up @@ -228,11 +210,8 @@
sqlite
]);

# git-hooks.nix sets a hooks path relative to the main checkout, which linked worktrees
# cannot resolve (cachix/git-hooks.nix#688). Unset it to use the shared hooks directory.
# The lock prevents concurrent loads from setting the path during `pre-commit install`,
# which rejects it. Install only when the current repository is this project, since
# `nix develop` can borrow this toolchain from a sibling repository.
# Use shared hooks to avoid the relative path bug in linked worktrees (git-hooks.nix#688).
# Serialize installation, and leave sibling repositories that borrow this shell untouched.
shellHook = ''
if common=$(${git} rev-parse --path-format=absolute --git-common-dir 2>/dev/null) &&
top=$(${git} rev-parse --show-toplevel 2>/dev/null) &&
Expand All @@ -256,6 +235,7 @@
cargo
cargo-deny
nodejs
nushell
pnpm
];
};
Expand Down
1 change: 1 addition & 0 deletions about.hbs → licenses/about.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Used by:
{{{text}}}

{{/each}}
{{> syntect-assets}}
File renamed without changes.
24 changes: 7 additions & 17 deletions nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ rustPlatform.buildRustPackage {
../Cargo.toml
../Cargo.lock
../LICENSE
../about.hbs
../about.toml
../crates
# The config tests check that the guide's reference lists every key and variable.
../docs/guide/configuration.md
../licenses
../scripts/generate-notices.sh
];
};

Expand All @@ -32,8 +30,13 @@ rustPlatform.buildRustPackage {
nativeBuildInputs = [ cargo-about ];

preBuild = ''
bash scripts/generate-notices.sh \
${stdenv.hostPlatform.rust.rustcTarget} THIRD-PARTY-NOTICES --offline
cargo about generate \
--locked \
--manifest-path crates/ox/Cargo.toml \
--target ${stdenv.hostPlatform.rust.rustcTarget} \
--name about licenses \
-o THIRD-PARTY-NOTICES \
--offline
'';

# The flake's `tests` check runs the suite, so builds of the package skip it.
Expand All @@ -54,19 +57,6 @@ rustPlatform.buildRustPackage {
doInstallCheck = true;
nativeInstallCheckInputs = [ versionCheckHook ];

postInstallCheck = ''
doc="$out/share/doc/oxide-code"
cmp LICENSE "$doc/LICENSE"
cmp THIRD-PARTY-NOTICES "$doc/THIRD-PARTY-NOTICES"
grep -q '^- syntect ' "$doc/THIRD-PARTY-NOTICES"
tail -c "$(wc -c < licenses/syntect-assets.txt)" "$doc/THIRD-PARTY-NOTICES" |
cmp licenses/syntect-assets.txt -
if grep -Eq '^- (insta|temp-env|wiremock) ' "$doc/THIRD-PARTY-NOTICES"; then
echo "Third-party notices include test dependencies" >&2
exit 1
fi
'';

meta = {
description = "Terminal-based AI coding assistant written in Rust";
homepage = "https://github.com/hakula139/oxide-code";
Expand Down
32 changes: 0 additions & 32 deletions scripts/check-comment-width.sh

This file was deleted.

Loading
Loading