Skip to content

diffr: install script, config init, upgrade and uninstall - #1066

Merged
sidkmenon merged 11 commits into
mainfrom
sm/diffr-onboarding
Oct 9, 2026
Merged

sidkmenon merged 11 commits into
mainfrom
sm/diffr-onboarding

Conversation

@sidkmenon

@sidkmenon sidkmenon commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

One command installs diffr and its Claude Code plugin:

curl -fsSL https://install.dev.fast/diffr | sh

Commit by commit:

  • DIFFR_CONFIG_DIR: a host such as Whiteboard can keep its own diffr config file. Without the variable, diffr reads the same file as before.
  • --help guides: diffr --help ends with a guide for agents (docs/agents.md), and diffr config --help with the plugins guide (docs/plugin.md). -h stays short. An agent runs diffr --help in its shell, so what it reads always matches the installed diffr.
  • diffr config init: in a terminal, asks which agents get the diffr plugin and which provider writes summaries, and asks for a key only when neither the config nor the environment has one. Without a terminal, config init --json prints the questions and a schema, and config init --json ANSWERS applies them; the key never goes through --json. Claude Code is the only agent: claude plugin install diffr@devfast after adding or updating the devfast marketplace, the same marketplace Whiteboard's plugin uses. Installed agents are recorded in $XDG_STATE_HOME/diffr/install.json, outside the synced config directory.
  • install.sh: finds the newest diffr/X.Y.Z release (or DIFFR_VERSION), checks the archive against the release's SHA256SUMS, installs diffr and diffr-tui in DIFFR_INSTALL_DIR (~/.local/bin by default), then runs diffr config init on a first install.
  • Install module: what config init installs moves to install.rs, so upgrade and uninstall use it without the prompts. No behavior change.
  • diffr upgrade and diffr uninstall: work the way diffr was installed, read from where the binary is (Homebrew, Cargo, inside node_modules: refuse, otherwise install.sh). After an upgrade, each recorded agent updates its plugin. uninstall keeps ~/.config/diffr, which Whiteboard shares.
  • Docs: the plugins guide for agents, the man page's commands, files and environment, and a README that leads with the install script.
  • Summaries: config init asks yes or no for pseudocode summaries, then the provider, then its key.
  • PATH and failures: install.sh offers to put diffr on PATH and points install failures at the issue tracker.
  • Steps: setup shows each step as it runs.

Before the install route goes live

  • Release diffr/0.1.18 after this and feat: implement /diffr, a Claude Code mod #1019 merge. install.sh installs the newest diffr/* release; 0.1.17 has no config init or upgrade, and the plugin needs 0.1.18.
  • Then merge Fix-Fast/dev#1119, which points install.dev.fast/diffr at diffr/install.sh on main.

Testing

  • cargo fmt --check and cargo test, including the CLI tests for DIFFR_CONFIG_DIR, the --help guides, config init --json, upgrade and uninstall.

Replaces #1039, #1040, #1054, #1056, #1057 and #1063.

Stack: #1065 → #1066 → #1019 → #1023 → #1024 → #1025 → #1026

https://claude.ai/code/session_01HNbERuYJjvvAjBN19zEDbo

@sidkmenon
sidkmenon force-pushed the sm/diffr-onboarding branch 2 times, most recently from fdc937b to a2dfa18 Compare October 9, 2026 08:07
Base automatically changed from sm/diffr-tui to main October 9, 2026 08:35
A host such as Whiteboard can now keep its own diffr config file. Before,
the only way was to change XDG_CONFIG_HOME, and plugins inherit diffr's
environment, so they saw the change too. Without the variable, diffr
reads the same file as before.

Tests: CLI tests, with DIFFR_CONFIG_DIR and XDG_CONFIG_HOME both set.

Agent-Session: 01a11aca-5007-7b80-ad35-c0a0f746a05d
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: 01a11cf1-8ad4-7910-b120-b0d622692a95
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 01a11e03-81df-7d63-bfa5-953f535444d2
Agent-Session: 01a11e0a-d042-71d2-9bc9-ac7b652487d1
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
`diffr --help` ends with a guide for agents, docs/agents.md, and
`diffr config --help` with the plugins guide, docs/plugin.md. `-h`
stays short. Both files are now part of the crate's package.

An agent runs `diffr --help` in its shell, so the text it reads always
matches the installed diffr, with no help command or tool to learn.

Tests: `--help` has each guide and `-h` doesn't.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 7ba69deb-2039-47e6-900f-0f30a79e167a
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
In a terminal, `config init` asks which agents get the diffr plugin and
which provider writes summaries, and asks for a key only when neither
the config nor the environment has one. Without a terminal it errors and
points to `--json`: `config init --json` prints the questions, a schema
for the answers and the key facts, and `config init --json ANSWERS`
(a file, or - for stdin) applies them. The key never goes through
`--json`. Key instructions are a TODO.

Claude Code is the only agent: its plugin is installed with
`claude plugin install diffr@devfast`, after adding or updating the
devfast marketplace. Plugins install before the config is saved, so a
failure stops the run with the config unchanged. Installed agents are
recorded in $XDG_STATE_HOME/diffr/install.json, outside the synced
config directory, so a rerun offers them as defaults and removes the
plugin from an agent that is no longer chosen.

Prompts use cliclack.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: 01a11aca-5007-7b80-ad35-c0a0f746a05d
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: 01a11cf1-8ad4-7910-b120-b0d622692a95
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 01a11e03-81df-7d63-bfa5-953f535444d2
Agent-Session: 01a11e0a-d042-71d2-9bc9-ac7b652487d1
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 7ba69deb-2039-47e6-900f-0f30a79e167a
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
`curl -fsSL https://install.dev.fast/diffr | sh` finds the newest
`diffr/X.Y.Z` release of devdotfast/whiteboard (or DIFFR_VERSION), checks
its archive against the release's SHA256SUMS, and puts diffr and
diffr-tui in DIFFR_INSTALL_DIR, ~/.local/bin by default. Then it runs
`diffr config init` on /dev/tty when there is a terminal, and otherwise
prints the `config init --json` command for an agent.

The release already publishes the archives and SHA256SUMS, so the
release workflow does not change.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: 01a11aca-5007-7b80-ad35-c0a0f746a05d
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: 01a11cf1-8ad4-7910-b120-b0d622692a95
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 01a11e03-81df-7d63-bfa5-953f535444d2
Agent-Session: 01a11e0a-d042-71d2-9bc9-ac7b652487d1
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 7ba69deb-2039-47e6-900f-0f30a79e167a
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
The agents, their plugin commands and the install record move from
init.rs to install.rs, so `upgrade` and `uninstall` can use them
without going through the prompts. No change in behavior.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: 01a11aca-5007-7b80-ad35-c0a0f746a05d
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 01a11e03-81df-7d63-bfa5-953f535444d2
Agent-Session: 01a11e0a-d042-71d2-9bc9-ac7b652487d1
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 7ba69deb-2039-47e6-900f-0f30a79e167a
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
…nstall`

Both commands work the way diffr was installed, which they read from
where the binary is, as `opencode upgrade` does:

- Homebrew (a Cellar/diffr path): `brew upgrade` or `brew uninstall`
  devdotfast/tap/diffr.
- Cargo (the Cargo bin directory): `cargo install` or `cargo uninstall`
  diffr-cli.
- Inside node_modules: refuse. That diffr came with an app, such as
  Whiteboard, which updates it.
- Anywhere else: install.sh from install.dev.fast into the binary's
  directory, or remove diffr and diffr-tui from it.

install.sh asks the setup questions only when no diffr was in its
directory before, so an upgrade through it doesn't ask them again.

After the upgrade, each agent in the install record updates its plugin.
`diffr uninstall` removes those plugins first, then the record, then
diffr; a failed plugin removal stops it with diffr in place.
~/.config/diffr stays, because Whiteboard reads it too.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ded-d896-7251-bf8f-c71bf07fd283
Agent-Session: 7ba69deb-2039-47e6-900f-0f30a79e167a
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
- docs/agents.md, at the end of `diffr --help`, is a plugins guide for
  agents: set a field on a bundled plugin first, otherwise write, build,
  configure and test a plugin.
- The man page gains the commands, files and environment, and the same
  agent section last.
- The README leads with the install script; plugin.md is reworded.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
`diffr config init` asks yes or no for pseudocode summaries, then, on
yes, the provider, then that provider's key unless one is saved or in
the environment. The key can't be left blank.

With `--json`, the answers' `summaries` is "off" or {"provider": ...},
so no other combination can be written; left out, summaries stay as
they are. Each question's text is written once and shared by both
modes. KEY_INSTRUCTIONS tells the person how to provide a key.

Tests: the new answer shapes, and leaving `summaries` out keeps the
saved provider.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
On a first install in a terminal, install.sh offers to add its
directory to PATH in the shell's startup file (zsh, bash or fish).
Declined, or without a terminal, it prints the directory to add.

Every install.sh failure ends by asking the person to file an issue.
install.sh, `diffr upgrade` and `diffr uninstall` are reworded.

Tests: a diffr bundled in an app refuses to upgrade or uninstall and
touches nothing, whatever its message says.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
`config init` named one step, "Saving", for everything it did, so adding
the plugin marketplace (a clone) looked like a hang. Each step now gets
its own spinner, ticked off when the next starts: the marketplace, the
plugin, then the settings. A failed step shows its error in place,
once, instead of again after the prompts. `diffr upgrade` and
`diffr uninstall` print the same steps.

Claude-Session: https://claude.ai/code/session_01LYrgBPQFAiQVjNV6D7UkQv
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11ecf-aa86-7a81-a5e6-c5dfe72d48eb
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
Claude-Session: https://claude.ai/code/session_01HTHD79i4QdthzVCkDZ26gd
Agent-Session: ec5d2c35-3045-44f3-9c41-ba4378797c8e
Agent-Session: b5985b3c-e5d2-4bb4-b6ac-7fec168d8328
Agent-Session: 01a11f51-383b-7f63-a323-39195d82ab2e
Agent-Session: cac10624-9b5a-46ba-b261-1e0359c62796
Agent-Session: 8881e3f3-a916-4405-9706-bb6e3a2f193d
sidkmenon added a commit that referenced this pull request Oct 9, 2026
)

The TUI gains review features, and its code that does not depend on
OpenTUI moves into a new package, `@diffr/viewer`, so other frontends
(the Claude Code mod in #1019) can draw the same view.

Toolchain: every package in `diffr/packages` is a pnpm workspace member.
There is no `bun.lock`. Bun only runs and compiles the OpenTUI packages.
Bun and Node both run `@diffr/viewer` from source, so one package
manager must own its dependencies. `cargo xtask install` first runs
`pnpm --filter @diffr/tui... install`, so it now needs Node and pnpm as
well as Rust and Bun. The release build job installs them. The README
install line from a checkout is marked TODO.

Commit by commit:
- 1–4: Prepare the TUI. Split out the renderer-free code, fix two fold
hover issues, move the TUI under `diffr/packages` (no code changes), and
parse themes with smol-toml.
- 5–8: Move the renderer-free code, cell planning and viewer state into
`@diffr/viewer`. Commit 5 also makes the pnpm change. Commit 7 fixes a
rail click.
- 9–13: Clean up theme and span layering, and delete Hunk code that
diffr does not use.
- 14–23: Review features: copy a selection for an agent (Y), mark files
viewed, search (/ n N), go to a file (Ctrl-P), ]c and [c, close open
views on blur, lay out very large files, the selection bar (selected
lines are blue in every theme), cross-side selections as Git patches,
and wider fold hit areas.
- 24: Test `@diffr/viewer` and the TUI in CI.

## Testing
- `pnpm typecheck` and `pnpm test` in `diffr/packages/viewer` (70 pass),
and `bun test` in `diffr/packages/tui` (44 pass), from a fresh `pnpm
install --frozen-lockfile`.
- `cargo fmt --check` and `cargo test`.

Stack: #1065 → #1066 → #1019 → #1023 → #1024 → #1025 → #1026

https://claude.ai/code/session_01HNbERuYJjvvAjBN19zEDbo
@sidkmenon
sidkmenon force-pushed the sm/diffr-onboarding branch from a2dfa18 to 8c081ce Compare October 9, 2026 08:35
@sidkmenon
sidkmenon merged commit 7976033 into main Oct 9, 2026
10 checks passed
@sidkmenon
sidkmenon deleted the sm/diffr-onboarding branch October 9, 2026 08:36
sidkmenon added a commit that referenced this pull request Oct 9, 2026
Implement a [claude code
mod](https://code.claude.com/docs/en/plugins/mods/overview) for diffr.
You or your claude can launch a beautiful, interactive diff view right
from claude code!

No switching tabs!

<img width="1511" height="832" alt="Screenshot 2026-10-08 at 12 51
37 AM"
src="https://github.com/user-attachments/assets/4b9b41b0-11d1-43d0-919b-213362f043af"
/>

Note from clanker:

```text
/diffr opens diffr's diff viewer in a Claude Code pane, drawn with @diffr/viewer from the PR below. The mod is in diffr/packages/claude-code, and the devfast marketplace (the one Whiteboard's plugin is in) lists it as diffr@devfast.

Commit by commit:
1: The /diffr mod: the command, the model's open tool, the pane, and selections added to the chat as chips whose code goes with the prompt. After Enter adds a selection, the pane closes and opens again without the keyboard, so the person types in the prompt; Claude Code has no call that moves focus there.
2: The built plugin, and its marketplace entry.
3: CI tests the mod and fails when the committed bundle differs from a fresh build.
4: /diffr and the open tool check that diffr is at least 0.1.18, the first release with `config init` and `upgrade`, and name the command that fixes it.
5: The open tool points the model at `diffr --help`, and the README lists /diffr.
```

## Testing
- `vitest run src` in `diffr/packages/claude-code` (9 pass) and `claude
plugin test plugin` (8 pass), from a fresh `pnpm install
--frozen-lockfile`. `pnpm build` reproduces the committed bundle.
- Installed in a live Claude Code session: /diffr opens the pane, Enter
adds a selection to the chat and hands the keyboard back.

Replaces #1061.

Stack: #1065 → #1066 → #1019 → #1023 → #1024 → #1025 → #1026

https://claude.ai/code/session_01HNbERuYJjvvAjBN19zEDbo
sidkmenon added a commit that referenced this pull request Oct 9, 2026
Bumps diffr's crates and `@dev.fast/diffr` to 0.1.18, for the
`diffr/0.1.18` release.

0.1.18 is the first release with `config init`, `upgrade` and
`uninstall` (#1066), which the /diffr Claude Code plugin (#1019) and
`install.dev.fast/diffr` need. It also reads revisions with gix's specs
(#1068).

The npm platform packages and Whiteboard's pin move to 0.1.18 in a
follow-up, once the release publishes them.

https://claude.ai/code/session_01HTHD79i4QdthzVCkDZ26gd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants