8.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this repository is
This is not a Rust project. It is a CraftBukkit/Spigot-style patch workflow wrapped around
xai-org/grok-build (the grok CLI/TUI, a 91-crate Rust
workspace).
Upstream is a periodic one-way export of an xAI-internal monorepo — linear squashed
Synced from monorepo commits, with SOURCE_REV naming the internal commit — and its
CONTRIBUTING.md states that no external PRs or patches are accepted. Changes cannot be pushed
back, and upstream re-drops the whole tree. So local changes live as replayable patches, exactly as
CraftBukkit does against Mojang:
patches/is the source.work/is the build output.
The two-layer model — read this before editing anything
| Path | Tracked? | What it is |
|---|---|---|
patches/ |
yes | The only source of truth. One .patch file per change. |
scripts/, Makefile, upstream.rev, README.md |
yes | The workflow itself and the pinned upstream SHA. |
upstream/ |
no (gitignored) | Pristine clone of the vendor repo. Never edit. |
work/ |
no (gitignored) | upstream@upstream.rev + patches/ applied. This is where you edit Rust code. Fully disposable. |
Two rules follow, and violating either silently destroys work:
- Rust source edits go in
work/, never inpatches/. Patch files are generated output — hand-editing them is how you get a tree that no longer applies. make rebuildis the only exit fromwork/.make applydoesgit reset --hard+git clean -fdx. Anything inwork/that has not been committed and exported bymake rebuildis gone.
Spigot mapping, if it helps: upstream/ ≈ the CraftBukkit clone, work/ ≈ the patched work dir,
patches/ ≈ CraftBukkit-Patches/, apply-patches.sh ≈ applyPatches.sh,
rebuild-patches.sh ≈ rebuildPatches.sh, upstream.rev ≈ versions/*.json.
Commands
Run everything from the repo root via make (make help lists all targets). The scripts inject
~/.cargo/bin into PATH themselves, so cargo need not be on the interactive PATH.
make setup # idempotent: rustup + pinned toolchain, dotslash/protoc
make apply # upstream.rev + patches/ -> work/ (FORCE=1 to discard work/ changes)
make rebuild # work/ commits above `base` -> patches/
make status # pinned rev, patch count, commits in work/, dirty state
make update-dry # preview an upstream sync, change nothing
make update # re-pin upstream.rev and forward-port patches
Build and test — all of these operate inside work/:
make build # debug binary -> work/target/debug/xai-grok-pager
make release # optimised build
make check # cargo check
make test # PKG=<crate> to pick the crate; defaults to xai-grok-version
make clippy
make fmt
make run # build + launch the TUI
Running a single test. make test is a thin wrapper; go through build.sh directly to pass a
filter, since extra args are forwarded to cargo verbatim:
CARGO_CMD=test PKG=xai-grok-version ./scripts/build.sh test_fork_tag
CARGO_CMD=test PKG=xai-grok-pager-bin ./scripts/build.sh version_output_writer -- --nocapture
./scripts/build.sh also accepts arbitrary cargo flags (--release, --locked, -j2, …).
Daily loop
One commit in work/ == one patch file. The commit message becomes the patch title.
make apply
$EDITOR work/crates/codegen/.../foo.rs
cd work && git add -A && git commit -m 'Do the thing'
cd .. && make rebuild
git add patches/ && git commit -m 'Do the thing'
To amend, reorder, squash, or drop existing patches, use ordinary git — work/ is a real repo, and
the base tag marks the boundary between upstream and ours:
cd work && git rebase -i base
cd .. && make rebuild # patches/ is regenerated wholesale
Upstream syncs
make update fetches upstream, reports the new commits / SOURCE_REV delta / changed-file count,
re-pins upstream.rev, replays patches/ on the new base, and re-exports on success. On conflict it
stops mid-git am and prints the resolution steps — resolve it like a rebase (git status, fix
markers, git add -A, git am --continue), then make rebuild from the root.
Conflicts here are the point of the model, not a defect: they mark exactly where an upstream change collided with ours instead of letting it vanish.
Safety rails
The scripts refuse rather than silently discard. When one fires, understand it before overriding:
make applyaborts ifwork/has uncommitted changes.make applyaborts if the commit count abovebase≠ the number of files inpatches/. Usually this means a forgottenmake rebuild; it also fires when you deliberately add or delete patch files, and the check cannot tell the two apart.make rebuild/make buildabort while agit amis half-finished.FORCE=1(i.e.make apply FORCE=1) is the escape hatch for bothapplyguards — it means "discardwork/and replaypatches/as-is".
make apply's git clean -fdx deliberately excludes /target; preserving the incremental cache is
the difference between a fast replay and a cold rebuild.
Patches are generated with --zero-commit --full-index --no-signature --no-stat -N. --zero-commit
keeps the From <sha> line stable across rebases (otherwise every patch file churns on every
replay); --full-index gives git am --3way blob hashes to fall back on when context drifts.
Upstream tree architecture (inside work/)
91 workspace members: crates/codegen/ (74 — the CLI crate closure), crates/common/ (11 — shared
leaves), crates/build/ (1), prod/mc/ (1), and third_party/ (4 — a vendored Mermaid diagram
stack).
| Crate | Role |
|---|---|
xai-grok-pager-bin |
Composition root; produces the xai-grok-pager binary (shipped as grok) |
xai-grok-pager |
The TUI — scrollback, prompt, modals, rendering. Also carries docs/user-guide/ |
xai-grok-shell |
Agent runtime + leader/stdio/headless entry points |
xai-grok-tools |
Tool implementations (terminal, file edit, search, …) |
xai-grok-workspace |
Host filesystem, VCS, execution, checkpoints |
Constraints inherited from upstream:
- The root
Cargo.tomlis generated — treat it as read-only. Edit per-crateCargo.tomlfiles instead. Note it also carries the[patch.crates-io]pin for theour-forks/async-openaigit dependency. - Always target a specific crate (
-p <crate>). Full-workspace builds are slow enough to be impractical here;build.shenforces this by always passing-p. - Toolchain is pinned to Rust 1.94.0 by
rust-toolchain.toml. Lint/format config:clippy.toml,rustfmt.tomlat the tree root. - protoc is needed only by
xai-grok-tools-api(forproto/grok-tools.proto). Resolution order is$PROTOC→ walk up forbin/protoc→PATH.bin/protocis a DotSlash wrapper that fetches protoc 29.3;setup.shinstalls dotslash and falls back to the systemprotobuf-compiler, andbuild.shsetsPROTOCif the wrapper is unusable.
When touching upstream code, check the tests that already cover it — the shipped example patch (below) exists partly to demonstrate this failure mode.
Build cost
On this host (4 cores / 7 GB) a cold debug build of xai-grok-pager-bin takes ~11 min and
work/target grows to ~24 GB; the binary is ~612 MB. cargo check and cargo build keep separate
caches, so make check after a make build is not fast (~6 min cold). Avoid make clean and
make distclean unless you actually need the space — both throw away that cache. Prefer
per-crate commands while iterating.
Example patches
patches/ ships two patches, both tagged [EXAMPLE PATCH] in their commit messages, and both safe
to delete:
0001modifies upstream Rust: addsFORK_NAME/fork_tag()toxai-grok-versionand wires it into the pager's version line, so--versionprintsgrok [newgrok] 1.0.3 (…). The tag is inserted after"grok "rather than appended, becausemain.rs'sversion_output_writer_preserves_channel_aware_contractasserts the line still ends with the channel label; anddisplay_versionitself is untouched becausetest_display_version_formatting_matrixpins its output.0002adds a new file (FORK.md), demonstrating that file-creating patches work.
Deleting them requires the override, since work/ still holds their commits:
rm patches/000*.patch && make apply FORCE=1.
License
Upstream code is Apache-2.0 (work/LICENSE); changes in patches/ are distributed under the same.
This is an unofficial fork, unaffiliated with xAI.