From 3a613b7a23de702692aa352ab30b1db8611bd20d Mon Sep 17 00:00:00 2001 From: Ben Vinegar <2153+benvinegar@users.noreply.github.com> Date: Tue, 13 Oct 2026 20:04:39 -0400 Subject: [PATCH 1/2] docs: correct stale facts and fill gaps across hunk.dev docs An audit of every docs page against the source found wrong or stale facts, a broken first-run agent flow, and shipped commands missing from the reference. - Agent notes start hidden; say so on the agent pages, quick start, README, and in the bundled hunk-review skill, which now tells agents to --focus their first note. - Rewrite skill setup around `npx skills add` and `hunk skill show`. - Add `hunk log` (drop the leftover publicDocs: false) and `hunk gh` to the generated CLI reference; gh help and the reference now share structured metadata, and a test requires every top-level command in `hunk --help` to appear in the reference. - Document the sidecar JSON schema, note kinds, replies, comment list types, daemon security and env vars, and the post-upgrade fix. - Correct the extension API version, trust state path, Windows paths, platform matrix, HUNK_INSTALL_DIR contents, pager/difftool undo, Windows text pager, jj diff semantics, vcs placement, pager config tables, watch placement, and the VCS adapter detect sample. - Move the Vercel deployment runbook out of the user docs into docs/website-deployment.md. Co-Authored-By: Claude Opus 5.5 --- .changeset/docs-accuracy-pass.md | 5 + README.md | 21 +- .../website-deployment.md | 14 +- packages/hunk-gh/src/extension.ts | 14 +- packages/hunk-gh/src/help.ts | 211 +++++++++++++++--- packages/hunk/README.md | 21 +- packages/hunk/skills/hunk-review/SKILL.md | 6 +- packages/hunk/src/app/cli.test.ts | 4 +- packages/hunk/src/app/cli.ts | 10 +- .../hunk/src/hunk-review/skillDocument.ts | 6 +- scripts/generate/config-examples.test.ts | 6 +- scripts/generate/generate-docs.test.ts | 43 +++- scripts/generate/generate-docs.ts | 78 ++++++- website/astro.config.mjs | 1 - website/public/docs/hunk-review-skill.md | 6 +- .../docs/agents/agent-context-and-stml.md | 71 +++++- .../docs/agents/comments-and-annotations.md | 70 +++++- .../docs/docs/agents/live-session-control.md | 30 ++- .../content/docs/docs/agents/review-skill.md | 37 +-- .../docs/docs/agents/review-with-an-agent.md | 52 +++-- .../docs/docs/configure/configuration.md | 6 +- .../docs/docs/configure/layout-and-display.md | 4 +- .../content/docs/docs/extend/extension-api.md | 53 +++-- .../content/docs/docs/extend/extensions.md | 2 +- .../content/docs/docs/extend/vcs-adapters.md | 12 +- .../content/docs/docs/help/compatibility.md | 18 +- .../content/docs/docs/help/troubleshooting.md | 13 +- website/src/content/docs/docs/index.mdx | 4 +- .../src/content/docs/docs/reference/cli.md | 114 ++++++++++ .../src/content/docs/docs/reference/config.md | 6 +- .../src/content/docs/docs/start/install.md | 16 +- .../docs/docs/start/keyboard-and-mouse.md | 46 ++-- .../content/docs/docs/start/quick-start.md | 13 +- .../docs/docs/workflows/files-and-patches.md | 2 +- .../docs/workflows/git-pager-and-difftool.md | 12 +- .../docs/workflows/jujutsu-and-sapling.md | 19 +- .../content/docs/docs/workflows/watch-mode.md | 13 +- .../workflows/working-trees-and-commits.md | 7 +- website/tests/docs-smoke.spec.ts | 2 +- 39 files changed, 828 insertions(+), 240 deletions(-) create mode 100644 .changeset/docs-accuracy-pass.md rename website/src/content/docs/docs/help/deployment.md => docs/website-deployment.md (76%) diff --git a/.changeset/docs-accuracy-pass.md b/.changeset/docs-accuracy-pass.md new file mode 100644 index 000000000..dfd85e3bf --- /dev/null +++ b/.changeset/docs-accuracy-pass.md @@ -0,0 +1,5 @@ +--- +"hunkdiff": patch +--- + +The bundled `hunk-review` skill now tells agents that agent notes start hidden, `hunk gh --help` lists its options and token variables, and the hunk.dev docs add `hunk log` and `hunk gh` to the CLI reference and correct stale install, platform, pager, watch, Jujutsu, and extension details. diff --git a/README.md b/README.md index 6a1d05bac..2a7004e88 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Windows requires mise 2026.8.6 or newer. Nix users can use the `default` package Requirements: -- macOS, Linux, or Windows +- macOS (arm64, x64), Linux (arm64, x64; glibc), or Windows (x64); prebuilt binaries are not published for Windows on arm64 or musl-based Linux such as Alpine - On x86-64, a CPU with SSE4.2 (Intel Nehalem 2008+, AMD Bulldozer 2011+); arm64 has no CPU feature floor - Node.js 22+ for the npm install; the install script, Homebrew, mise, and Nix ship a standalone binary that does not require Node.js - Git recommended for most workflows @@ -137,17 +137,14 @@ git diff --no-color | hunk patch - # review a patch from stdin ### Working with agents -1. Open Hunk in another terminal with `hunk diff` or `hunk show`. -2. Tell your agent to add the skill file returned by `hunk skill path`. -3. Ask your agent to use the skill against the live Hunk session. +1. Install the review skill once: `npx skills add modem-dev/hunk -g --skill hunk-review`. +2. Open Hunk in another terminal with `hunk diff` or `hunk show`. +3. Ask your agent to use the hunk-review skill against the live Hunk session. +4. Press `a` in Hunk to show agent notes; they start hidden. -A good generic prompt is: +If your agent has no skill system, ask it to run `hunk skill show` and follow the output. -```text -Load the Hunk skill and use it for this review. Run `hunk skill path` to get the skill path. -``` - -See [the agent workflow guide](docs/agent-workflows.md) for live sessions, `--agent-context`, and experimental rich notes. +See [Review with an agent](https://hunk.dev/docs/agents/review-with-an-agent/) for live sessions, `--agent-context`, and experimental rich notes. ## Feature comparison @@ -169,8 +166,8 @@ See [the agent workflow guide](docs/agent-workflows.md) for live sessions, `--ag You can persist preferences to a config file: -- `~/.config/hunk/config.toml` -- `.hunk/config.toml` +- `$XDG_CONFIG_HOME/hunk/config.toml` (default `~/.config/hunk/config.toml`) +- `.hunk/config.toml` in a repository Persistent state uses `$XDG_STATE_HOME/hunk/state.json` when the variable is absolute, or `~/.local/state/hunk/state.json` otherwise. Hunk preserves existing diff --git a/website/src/content/docs/docs/help/deployment.md b/docs/website-deployment.md similarity index 76% rename from website/src/content/docs/docs/help/deployment.md rename to docs/website-deployment.md index 64e436505..3f8be1f02 100644 --- a/website/src/content/docs/docs/help/deployment.md +++ b/docs/website-deployment.md @@ -1,7 +1,6 @@ ---- -title: Deployment integration -description: Publish the Hunk landing page and documentation together from one Astro build. ---- +# Website deployment + +Publish the Hunk landing page and documentation together from one Astro build. The `website/` Astro project owns the complete `hunk.dev` site: @@ -9,6 +8,8 @@ The `website/` Astro project owns the complete `hunk.dev` site: - `/docs/` and `/docs/*` are the Starlight documentation. - `/pagefind/` contains the documentation search index. - `/docs/hunk-review-skill.md` publishes the generated agent skill. +- `/changelog/` and `/compare/` are generated from `CHANGELOG.md` and the comparison data. +- `/install.sh` is the installer, staged into the build by `scripts/packaging/stage-install-script.ts`; `/install` rewrites to it. One static build keeps navigation, metadata, and deployment atomic. Do not copy the docs into the former `hunk-web` repository or operate a second docs origin. @@ -28,7 +29,7 @@ Archive `website/dist/` as one deployable artifact. `bun run website:build` chec ## Deploy with Vercel -The repository-level `vercel.json` defines the install command, build command, Astro framework, and `website/dist` output directory. It also rewrites `/api/release/latest` to the release-proxy Worker (`workers/release-proxy/`) so the installer and `hunk update` resolve releases from the apex domain rather than the Worker's own hostname. Configure one Vercel project with: +The repository-level `vercel.json` defines the install command, build command, Astro framework, and `website/dist` output directory. Its `ignoreCommand` skips deployments for commits that touch none of the website, installer, docs generators, or `CHANGELOG.md`. It also rewrites `/install` to `/install.sh` and `/api/release/latest` to the release-proxy Worker (`workers/release-proxy/`) so the installer and `hunk update` resolve releases from the apex domain rather than the Worker's own hostname. Configure one Vercel project with: - **Git repository:** `modem-dev/hunk` - **Root directory:** repository root @@ -45,7 +46,7 @@ Vercel should deploy pushes to `main` after the website and repository checks pa 3. Verify production before disabling the old project. 4. Archive `modem-dev/hunk-web` after the cutover so the landing page does not drift into a second implementation. -The domain move is the only production switch. No proxy, rewrite, or `/docs` path mount is required. +The domain move is the only production switch. No proxy or `/docs` path mount is required beyond the rewrites in `vercel.json`. ## Verify before switching traffic @@ -55,6 +56,7 @@ curl --fail --location https://hunk.dev/docs/ curl --fail https://hunk.dev/sitemap.xml curl --fail https://hunk.dev/pagefind/pagefind.js curl --fail https://hunk.dev/docs/hunk-review-skill.md +curl --fail --silent https://hunk.dev/install.sh | head -1 curl --fail https://hunk.dev/og.png curl --fail --location --silent --show-error --dump-header - https://hunk.dev/api/release/latest | grep --extended-regexp '^cache-control: no-store|"version"' ``` diff --git a/packages/hunk-gh/src/extension.ts b/packages/hunk-gh/src/extension.ts index 391612fa7..d024d7f58 100644 --- a/packages/hunk-gh/src/extension.ts +++ b/packages/hunk-gh/src/extension.ts @@ -13,7 +13,13 @@ import { fetchGitHubPullRequestMetadata, findOpenPullRequestForBranch, } from "./github"; -import { GITHUB_COMMIT_HELP, GITHUB_COMPARE_HELP, GITHUB_HELP, GITHUB_PR_HELP } from "./help"; +import { + GITHUB_CLI_REFERENCE, + GITHUB_COMMIT_HELP, + GITHUB_COMPARE_HELP, + GITHUB_HELP, + GITHUB_PR_HELP, +} from "./help"; import { parseGitHubCommitInvocation, parseGitHubCompareInvocation, @@ -206,9 +212,9 @@ export function createGitHubPrExtension( hunk.registerCliCommand( { - name: "gh", - summary: "Review GitHub pull requests, commits, and comparisons", - usage: " [--repo ]", + name: GITHUB_CLI_REFERENCE.name, + summary: GITHUB_CLI_REFERENCE.summary, + usage: GITHUB_CLI_REFERENCE.usage, }, handler, ); diff --git a/packages/hunk-gh/src/help.ts b/packages/hunk-gh/src/help.ts index 5c86e6259..1c7c397b1 100644 --- a/packages/hunk-gh/src/help.ts +++ b/packages/hunk-gh/src/help.ts @@ -1,49 +1,188 @@ -export const GITHUB_HELP = `Usage: hunk gh +/** + * Describes the `hunk gh` command surface as structured metadata and renders its `--help` text. + * The command registration, every `--help` screen, and the generated website CLI reference read + * this metadata, so a new form or option is documented everywhere by adding it once here. + */ -GitHub commands: - pr [pull-request] [--repo ] [-- ] - Review a GitHub pull request; infer it from the current branch when omitted. - commit [--repo ] [-- ] - Review a GitHub commit without requiring the gh CLI. - compare ... [--repo ] [-- ] - Review the changes between two GitHub refs. +/** One accepted argument spelling for a GitHub subcommand. */ +export interface GitHubCliForm { + /** Arguments after `hunk gh `; empty when the target is omitted entirely. */ + readonly args: string; + /** Omitted for long spellings such as URLs whose meaning is self-evident. */ + readonly description?: string; +} -Run \`hunk gh --help\` for command-specific help. -`; +/** One `hunk gh` subcommand. */ +export interface GitHubCliSubcommand { + readonly name: "pr" | "commit" | "compare"; + /** Positional grammar between the subcommand name and the shared options. */ + readonly arguments: string; + /** One-line summary shown in the `hunk gh --help` overview. */ + readonly summary: string; + /** Lead sentence for the subcommand's own help and reference section. */ + readonly description: string; + readonly formsHeading: string; + readonly forms: readonly GitHubCliForm[]; + readonly notes?: readonly string[]; +} -export const GITHUB_PR_HELP = `Usage: hunk gh pr [pull-request] [--repo ] [-- ] +/** One option shared by every GitHub subcommand. */ +export interface GitHubCliOption { + readonly flag: string; + readonly description: string; +} -Review a GitHub pull request without requiring the gh CLI. +/** Complete `hunk gh` command metadata. */ +export interface GitHubCliReference { + readonly name: string; + readonly summary: string; + readonly usage: string; + readonly options: readonly GitHubCliOption[]; + readonly authentication: { + /** Environment variables in lookup order. */ + readonly variables: readonly string[]; + readonly description: string; + }; + readonly subcommands: readonly GitHubCliSubcommand[]; +} -Pull request forms: - (omitted) find the open PR for the current branch - 123 infer owner/repo from the local origin - 123 --repo modem-dev/hunk use an explicit repository - 'modem-dev/hunk#123' name the repository and pull request - https://github.com/modem-dev/hunk/pull/123 +/** Describes `hunk gh` for its registration, `--help` screens, and the website CLI reference. */ +export const GITHUB_CLI_REFERENCE: GitHubCliReference = { + name: "gh", + summary: "Review GitHub pull requests, commits, and comparisons", + usage: " [--repo ]", + options: [ + { + flag: "--repo ", + description: "use an explicit repository instead of the local origin", + }, + { + flag: "-- ", + description: "pass the remaining options to the `hunk patch` review", + }, + ], + authentication: { + variables: ["GH_TOKEN", "GITHUB_TOKEN"], + description: "optional for public repositories", + }, + subcommands: [ + { + name: "pr", + arguments: "[pull-request]", + summary: "Review a GitHub pull request; infer it from the current branch when omitted.", + description: "Review a GitHub pull request without requiring the gh CLI.", + formsHeading: "Pull request forms", + forms: [ + { args: "", description: "find the open PR for the current branch" }, + { args: "123", description: "infer owner/repo from the local origin" }, + { args: "123 --repo modem-dev/hunk", description: "use an explicit repository" }, + { args: "'modem-dev/hunk#123'", description: "name the repository and pull request" }, + { args: "https://github.com/modem-dev/hunk/pull/123" }, + ], + }, + { + name: "commit", + arguments: "", + summary: "Review a GitHub commit without requiring the gh CLI.", + description: "Review a GitHub commit without requiring the gh CLI.", + formsHeading: "Commit forms", + forms: [ + { args: "abcdef1", description: "infer owner/repo from the local origin" }, + { args: "abcdef1 --repo modem-dev/hunk", description: "use an explicit repository" }, + { args: "modem-dev/hunk@abcdef1", description: "name the repository and commit" }, + { args: "https://github.com/modem-dev/hunk/commit/abcdef1" }, + ], + }, + { + name: "compare", + arguments: "...", + summary: "Review the changes between two GitHub refs.", + description: "Review the changes between two GitHub branches, tags, or commits.", + formsHeading: "Compare forms", + forms: [ + { args: "main...feature", description: "infer owner/repo from the local origin" }, + { args: "v0.20.0...v0.21.0 --repo modem-dev/hunk" }, + ], + notes: ["Use three dots exactly. Pass Hunk patch options after `--`."], + }, + ], +}; -Authentication: - GH_TOKEN, then GITHUB_TOKEN optional for public repositories -`; +const HELP_TERM_WIDTH = 37; +const HELP_OPTION_WIDTH = 24; -export const GITHUB_COMMIT_HELP = `Usage: hunk gh commit [--repo ] [-- ] +/** Render one indented help row, aligning descriptions after short terms. */ +function helpRow(term: string, description?: string, width = HELP_TERM_WIDTH) { + if (!description) return ` ${term}`; + return ` ${term.padEnd(width - 1)} ${description}`; +} -Review a GitHub commit without requiring the gh CLI. +/** Render the shared GitHub authentication row. */ +function authenticationHelp(reference: GitHubCliReference) { + return [ + "Authentication:", + helpRow( + reference.authentication.variables.join(", then "), + reference.authentication.description, + ), + ]; +} -Commit forms: - abcdef1 infer owner/repo from the local origin - abcdef1 --repo modem-dev/hunk use an explicit repository - modem-dev/hunk@abcdef1 name the repository and commit - https://github.com/modem-dev/hunk/commit/abcdef1 -`; +/** Format one subcommand's usage after `hunk gh`, including the shared options. */ +export function formatGitHubSubcommandUsage(subcommand: GitHubCliSubcommand) { + const options = GITHUB_CLI_REFERENCE.options.map((option) => `[${option.flag}]`); + return [subcommand.name, subcommand.arguments, ...options].join(" "); +} -export const GITHUB_COMPARE_HELP = `Usage: hunk gh compare ... [--repo ] [-- ] +/** Render the `hunk gh --help` overview. */ +function renderGitHubHelp(reference: GitHubCliReference) { + return [ + "Usage: hunk gh ", + "", + "GitHub commands:", + ...reference.subcommands.flatMap((subcommand) => [ + ` ${formatGitHubSubcommandUsage(subcommand)}`, + ` ${subcommand.summary}`, + ]), + "", + "Options:", + ...reference.options.map((option) => + helpRow(option.flag, option.description, HELP_OPTION_WIDTH), + ), + "", + ...authenticationHelp(reference), + "", + "Run `hunk gh --help` for command-specific help.", + "", + ].join("\n"); +} -Review the changes between two GitHub branches, tags, or commits. +/** Render `hunk gh --help` for one subcommand. */ +function renderGitHubSubcommandHelp( + subcommand: GitHubCliSubcommand, + reference: GitHubCliReference, +) { + return [ + `Usage: hunk gh ${formatGitHubSubcommandUsage(subcommand)}`, + "", + subcommand.description, + "", + `${subcommand.formsHeading}:`, + ...subcommand.forms.map((form) => helpRow(form.args || "(omitted)", form.description)), + ...(subcommand.notes ? ["", ...subcommand.notes] : []), + "", + ...authenticationHelp(reference), + "", + ].join("\n"); +} -Compare forms: - main...feature infer owner/repo from the local origin - v0.20.0...v0.21.0 --repo modem-dev/hunk +/** Render one subcommand's help by name. */ +function renderGitHubSubcommandHelpByName(name: GitHubCliSubcommand["name"]) { + const subcommand = GITHUB_CLI_REFERENCE.subcommands.find((entry) => entry.name === name)!; + return renderGitHubSubcommandHelp(subcommand, GITHUB_CLI_REFERENCE); +} -Use three dots exactly. Pass Hunk patch options after --. -`; +export const GITHUB_HELP = renderGitHubHelp(GITHUB_CLI_REFERENCE); +export const GITHUB_PR_HELP = renderGitHubSubcommandHelpByName("pr"); +export const GITHUB_COMMIT_HELP = renderGitHubSubcommandHelpByName("commit"); +export const GITHUB_COMPARE_HELP = renderGitHubSubcommandHelpByName("compare"); diff --git a/packages/hunk/README.md b/packages/hunk/README.md index 6a1d05bac..2a7004e88 100644 --- a/packages/hunk/README.md +++ b/packages/hunk/README.md @@ -53,7 +53,7 @@ Windows requires mise 2026.8.6 or newer. Nix users can use the `default` package Requirements: -- macOS, Linux, or Windows +- macOS (arm64, x64), Linux (arm64, x64; glibc), or Windows (x64); prebuilt binaries are not published for Windows on arm64 or musl-based Linux such as Alpine - On x86-64, a CPU with SSE4.2 (Intel Nehalem 2008+, AMD Bulldozer 2011+); arm64 has no CPU feature floor - Node.js 22+ for the npm install; the install script, Homebrew, mise, and Nix ship a standalone binary that does not require Node.js - Git recommended for most workflows @@ -137,17 +137,14 @@ git diff --no-color | hunk patch - # review a patch from stdin ### Working with agents -1. Open Hunk in another terminal with `hunk diff` or `hunk show`. -2. Tell your agent to add the skill file returned by `hunk skill path`. -3. Ask your agent to use the skill against the live Hunk session. +1. Install the review skill once: `npx skills add modem-dev/hunk -g --skill hunk-review`. +2. Open Hunk in another terminal with `hunk diff` or `hunk show`. +3. Ask your agent to use the hunk-review skill against the live Hunk session. +4. Press `a` in Hunk to show agent notes; they start hidden. -A good generic prompt is: +If your agent has no skill system, ask it to run `hunk skill show` and follow the output. -```text -Load the Hunk skill and use it for this review. Run `hunk skill path` to get the skill path. -``` - -See [the agent workflow guide](docs/agent-workflows.md) for live sessions, `--agent-context`, and experimental rich notes. +See [Review with an agent](https://hunk.dev/docs/agents/review-with-an-agent/) for live sessions, `--agent-context`, and experimental rich notes. ## Feature comparison @@ -169,8 +166,8 @@ See [the agent workflow guide](docs/agent-workflows.md) for live sessions, `--ag You can persist preferences to a config file: -- `~/.config/hunk/config.toml` -- `.hunk/config.toml` +- `$XDG_CONFIG_HOME/hunk/config.toml` (default `~/.config/hunk/config.toml`) +- `.hunk/config.toml` in a repository Persistent state uses `$XDG_STATE_HOME/hunk/state.json` when the variable is absolute, or `~/.local/state/hunk/state.json` otherwise. Hunk preserves existing diff --git a/packages/hunk/skills/hunk-review/SKILL.md b/packages/hunk/skills/hunk-review/SKILL.md index ff0e6ef97..748c52de5 100644 --- a/packages/hunk/skills/hunk-review/SKILL.md +++ b/packages/hunk/skills/hunk-review/SKILL.md @@ -133,12 +133,12 @@ hunk session comment add --repo . --reply-to user:123 --summary "Addressed in th printf '%s\n' '{"comments":[{"filePath":"README.md","newLine":103,"summary":"Tighten this wording"}]}' | hunk session comment apply --repo . --stdin ``` -- `comment list --type user` shows human-authored inline notes; without `--type`, `comment list` preserves the legacy live-agent-comment view +- Agent notes start hidden: the user sees them only after pressing `a` or setting `agent_notes = true`. `--focus` jumps to the new note (or the first note in a batch) and turns agent notes on, so pass it on the first note you leave in a session, or tell the user to press `a` +- `comment list` shows the comments added through `hunk session comment` by default; `--type user` lists notes the user wrote with `c`, `--type ai` lists notes loaded from an `--agent-context` sidecar, `--type agent` lists agent-authored notes, and `--type all` lists every note - `comment add` is best for one note; `comment apply` is best when an agent already has several notes ready - Root `comment add` notes require `--file`, `--summary`, and exactly one of `--old-line` or `--new-line`; replies use `--reply-to ` with `--summary` and inherit the parent's anchor - `comment apply` items require `summary` plus either `replyTo` by itself or `filePath` with exactly one target such as `hunk`, `hunkNumber`, `oldLine`, or `newLine` - `comment apply` reads a JSON batch from stdin and validates the full batch before mutating the live session -- Pass `--focus` when you want to jump to the new note or the first note in a batch - `comment list` and `comment clear` accept optional `--file` - Quote `--summary` and `--rationale` defensively in the shell @@ -202,7 +202,7 @@ Guidelines: - Navigate before commenting so the user sees the code you're discussing - Use `highlight add --focus` to steer the user's eyes to the exact expression while you explain it, and `highlight clear` before moving to the next topic - Use `comment apply` for agent-generated batches and `comment add` for one-off notes -- Use `--focus` sparingly when the note itself should actively steer the review +- Use `--focus` on your first note so agent notes become visible, then sparingly when a note should actively steer the review - Keep comments focused: intent, structure, risks, or follow-ups - Don't comment on every hunk -- highlight what the user wouldn't spot themselves diff --git a/packages/hunk/src/app/cli.test.ts b/packages/hunk/src/app/cli.test.ts index 6c021add9..9cd026bf4 100644 --- a/packages/hunk/src/app/cli.test.ts +++ b/packages/hunk/src/app/cli.test.ts @@ -60,7 +60,7 @@ describe("parseCli", () => { expect(parsed.text).toContain("hunk diff"); expect(parsed.text).toContain("hunk show"); expect(parsed.text).toContain("hunk log"); - expect(parsed.text).toContain("attractive repository history"); + expect(parsed.text).toContain("browse repository history"); expect(parsed.text).not.toContain("Git commit history"); expect(parsed.text).toContain("hunk skill path"); expect(parsed.text).toContain("Global options:"); @@ -1886,7 +1886,7 @@ describe("parseCli command help text", () => { expect(await expectHelp(["pager", "--help"])).toContain("general Git pager wrapper"); expect(await expectHelp(["difftool", "--help"])).toContain("review Git difftool file pairs"); const logHelp = await expectHelp(["log", "--help"]); - expect(logHelp).toContain("browse an attractive repository history"); + expect(logHelp).toContain("browse repository history"); expect(logHelp).toContain("--static"); expect(logHelp).not.toContain("--interactive"); }); diff --git a/packages/hunk/src/app/cli.ts b/packages/hunk/src/app/cli.ts index 7ef8c10b7..9eb4cc71d 100644 --- a/packages/hunk/src/app/cli.ts +++ b/packages/hunk/src/app/cli.ts @@ -299,10 +299,8 @@ export const CLI_REFERENCE_COMMANDS = { }, log: { path: "log", - // Release preparation enables this when an installable build contains history browsing. - publicDocs: false, - summary: "browse an attractive repository history", - synopsis: ["hunk log [revision-expression] [-- ]"], + summary: "browse repository history", + synopsis: ["hunk log [target] [-- ]"], details: [ "A terminal opens the responsive browser; pipes and redirects receive static output.", "Use --static to force static output, paging when it exceeds the terminal.", @@ -750,7 +748,7 @@ function renderCliHelp() { " hunk diff --files compare two concrete files", " hunk show [target] [-- ] review the last commit or a given target", " hunk status [--static | --json] inspect this workspace and sibling worktrees", - " hunk log [target] [-- ] browse an attractive repository history", + " hunk log [target] [-- ] browse repository history", " hunk gh review changes hosted on GitHub", " hunk stash show [ref] review a stash entry (git only)", " hunk patch [file] review a patch file or stdin", @@ -1215,7 +1213,7 @@ async function parseHistoryCommand( ): Promise { const { commandTokens, pathspecs } = splitPathspecArgs(tokens); const command = createCliReferenceCommand("log") - .argument("[revision]") + .argument("[target]") // Accept the former opt-in spelling during migration without presenting two experiences. .addOption(new Option("--interactive").hideHelp()); let revision: string | undefined; diff --git a/packages/hunk/src/hunk-review/skillDocument.ts b/packages/hunk/src/hunk-review/skillDocument.ts index 55d0b5069..987ff8b91 100644 --- a/packages/hunk/src/hunk-review/skillDocument.ts +++ b/packages/hunk/src/hunk-review/skillDocument.ts @@ -158,12 +158,12 @@ const COMMENTS_SECTION = [ ...(commands["comment-apply"].examples ?? []), ]), "", - "- `comment list --type user` shows human-authored inline notes; without `--type`, `comment list` preserves the legacy live-agent-comment view", + "- Agent notes start hidden: the user sees them only after pressing `a` or setting `agent_notes = true`. `--focus` jumps to the new note (or the first note in a batch) and turns agent notes on, so pass it on the first note you leave in a session, or tell the user to press `a`", + "- `comment list` shows the comments added through `hunk session comment` by default; `--type user` lists notes the user wrote with `c`, `--type ai` lists notes loaded from an `--agent-context` sidecar, `--type agent` lists agent-authored notes, and `--type all` lists every note", "- `comment add` is best for one note; `comment apply` is best when an agent already has several notes ready", "- Root `comment add` notes require `--file`, `--summary`, and exactly one of `--old-line` or `--new-line`; replies use `--reply-to ` with `--summary` and inherit the parent's anchor", "- `comment apply` items require `summary` plus either `replyTo` by itself or `filePath` with exactly one target such as `hunk`, `hunkNumber`, `oldLine`, or `newLine`", "- `comment apply` reads a JSON batch from stdin and validates the full batch before mutating the live session", - "- Pass `--focus` when you want to jump to the new note or the first note in a batch", "- `comment list` and `comment clear` accept optional `--file`", "- Quote `--summary` and `--rationale` defensively in the shell", ]; @@ -229,7 +229,7 @@ const GUIDING_SECTION = [ "- Navigate before commenting so the user sees the code you're discussing", "- Use `highlight add --focus` to steer the user's eyes to the exact expression while you explain it, and `highlight clear` before moving to the next topic", "- Use `comment apply` for agent-generated batches and `comment add` for one-off notes", - "- Use `--focus` sparingly when the note itself should actively steer the review", + "- Use `--focus` on your first note so agent notes become visible, then sparingly when a note should actively steer the review", "- Keep comments focused: intent, structure, risks, or follow-ups", "- Don't comment on every hunk -- highlight what the user wouldn't spot themselves", ]; diff --git a/scripts/generate/config-examples.test.ts b/scripts/generate/config-examples.test.ts index 22230e712..c96bbe3ac 100644 --- a/scripts/generate/config-examples.test.ts +++ b/scripts/generate/config-examples.test.ts @@ -127,21 +127,21 @@ for (const [name, markdown] of Object.entries(pages)) { test("pager table overrides a command only for pager sessions, and flags still win", () => { const pager = findTestTomlBlock(markdown, (value) => Boolean(value.pager)); const files = createTestConfigFiles( - `wrap_lines = false\n[patch]\nwrap_lines = false\nmenu_bar = true\n${pager}`, + `wrap_lines = false\n[patch]\nwrap_lines = false\nmode = "split"\n${pager}`, ); const interactive = resolveConfiguredCliInput( { kind: "patch", file: "-", options: {} }, files, ); expect(interactive.input.options.wrapLines).toBe(false); - expect(interactive.input.options.menuBar).toBe(true); + expect(interactive.input.options.mode).toBe("split"); const paged = resolveConfiguredCliInput( { kind: "patch", file: "-", options: { pager: true } }, files, ); expect(paged.input.options.wrapLines).toBe(true); - expect(paged.input.options.menuBar).toBe(false); + expect(paged.input.options.mode).toBe("unified"); const explicit = resolveConfiguredCliInput( { kind: "patch", file: "-", options: { pager: true, wrapLines: false } }, diff --git a/scripts/generate/generate-docs.test.ts b/scripts/generate/generate-docs.test.ts index bbc055dd9..844467f31 100644 --- a/scripts/generate/generate-docs.test.ts +++ b/scripts/generate/generate-docs.test.ts @@ -1,4 +1,5 @@ import { afterEach, describe, expect, test } from "bun:test"; +import { GITHUB_CLI_REFERENCE } from "@hunk/gh"; import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; @@ -17,6 +18,7 @@ import { UNSAFE_ALLOW_REMOTE_SESSION_BROKER_ENV, } from "../../packages/hunk/src/session/broker/brokerConfig"; import { LEGACY_THEME_ID_ALIASES } from "../../packages/hunk/src/core/theme/catalog"; +import { parseCli } from "../../packages/hunk/src/app/cli"; import { CONFIG_REFERENCE_GROUPS, CONFIG_THEME_COLOR_DESCRIPTIONS, @@ -42,7 +44,7 @@ describe("generated website references", () => { expect(reference).toContain("hunk diff --staged"); expect(reference).toContain("--no-transparent-bg"); expect(reference).toContain("hunk markup render"); - expect(reference).not.toContain("## `hunk log`"); + expect(reference).toContain("hunk log [target] [-- ]"); const statusSection = reference.split("## `hunk status`")[1]!.split("\n## ")[0]!; expect(statusSection).toContain("`--vcs `"); expect(statusSection).toContain("Q returns or quits"); @@ -67,6 +69,45 @@ describe("generated website references", () => { expect(reference).toContain("for `--file` navigation, exactly one of"); }); + test("documents every top-level command listed in `hunk --help`", async () => { + const help = await parseCli(["bun", "hunk", "--help"]); + if (help.kind !== "help") throw new Error("Expected top-level help output."); + const commandBlock = help.text.split("Commands:\n")[1]!.split("\n\n")[0]!; + const commandPaths = new Set( + commandBlock.split("\n").map((line) => { + const usage = line.trim().split(/\s{2,}/)[0]!; + const words = usage.split(" ").slice(1); + const argumentIndex = words.findIndex((word) => /^[[<(-]/.test(word)); + return words.slice(0, argumentIndex < 0 ? undefined : argumentIndex).join(" "); + }), + ); + const reference = renderCliReference(); + + expect(commandPaths).toContain("log"); + expect(commandPaths).toContain("gh"); + for (const path of commandPaths) { + expect(reference).toMatch(new RegExp(`^## \x60hunk ${path}[\x60 ]`, "m")); + } + }); + + test("renders the bundled GitHub command from its help metadata", () => { + const reference = renderCliReference(); + const githubSection = reference.split("## `hunk gh`")[1]!.split("\n## ")[0]!; + + expect(reference.indexOf("## `hunk log`")).toBeLessThan(reference.indexOf("## `hunk gh`")); + for (const option of GITHUB_CLI_REFERENCE.options) { + expect(githubSection).toContain(`\`${option.flag}\``); + } + for (const subcommand of GITHUB_CLI_REFERENCE.subcommands) { + expect(githubSection).toContain(`### \`hunk gh ${subcommand.name}\``); + for (const form of subcommand.forms) { + expect(githubSection).toContain(`hunk gh ${subcommand.name} ${form.args}`.trimEnd()); + } + } + expect(githubSection).toContain("`GH_TOKEN`, then `GITHUB_TOKEN`"); + expect(githubSection).toContain("--disable-extension hunk.gh"); + }); + test("renders every runtime-parsed config key with defaults and compatibility metadata", () => { const reference = renderConfigReference(); diff --git a/scripts/generate/generate-docs.ts b/scripts/generate/generate-docs.ts index d112baca9..93d6573d3 100644 --- a/scripts/generate/generate-docs.ts +++ b/scripts/generate/generate-docs.ts @@ -1,5 +1,6 @@ import { readFileSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; +import { formatGitHubSubcommandUsage, GITHUB_CLI_REFERENCE } from "@hunk/gh"; import { CLI_REFERENCE_COMMANDS, COMMON_REVIEW_OPTIONS, @@ -14,6 +15,7 @@ import { CONFIG_REFERENCE_EXTENSIONS, CONFIG_REFERENCE_OPTIONS, } from "../../packages/hunk/src/core/run/config"; +import { findBundledCoreExtensionByCommand } from "../../packages/hunk/src/extensions/default/core"; import { renderHunkReviewSkill } from "../../packages/hunk/src/hunk-review/skillDocument"; import { type AgentCommandOption, @@ -123,11 +125,73 @@ function renderUsage(lines: readonly string[]) { return ["```bash", ...lines, "```"].join("\n"); } +/** Render the bundled `hunk gh` extension command from its own help metadata. */ +function renderGitHubSection() { + const reference = GITHUB_CLI_REFERENCE; + const selectionId = findBundledCoreExtensionByCommand(reference.name)?.selectionId; + if (!selectionId) { + throw new Error(`No bundled extension owns \`hunk ${reference.name}\`.`); + } + + const subcommandSections = reference.subcommands.map((subcommand) => { + const formWidth = Math.max( + ...subcommand.forms + .filter((form) => form.description) + .map((form) => `hunk gh ${subcommand.name} ${form.args}`.trimEnd().length), + ); + const forms = subcommand.forms.map((form) => { + const invocation = `hunk gh ${subcommand.name} ${form.args}`.trimEnd(); + return form.description + ? `${invocation.padEnd(formWidth)} # ${form.description}` + : invocation; + }); + const pieces = [ + `### \`hunk gh ${subcommand.name}\``, + "", + subcommand.description, + "", + renderUsage([`hunk gh ${formatGitHubSubcommandUsage(subcommand)}`]), + "", + `**${subcommand.formsHeading}:**`, + "", + renderUsage(forms), + ]; + for (const note of subcommand.notes ?? []) { + pieces.push("", proseSafe(note)); + } + return pieces.join("\n"); + }); + + return [ + `## \`hunk ${reference.name}\``, + "", + `${reference.summary}. This command is provided by the bundled \`${selectionId}\` extension; disable it with \`--disable-extension ${selectionId}\` or the [\`[extensions]\` config table](/docs/reference/config/#extensions).`, + "", + "### Usage", + "", + renderUsage( + reference.subcommands.map( + (subcommand) => `hunk gh ${formatGitHubSubcommandUsage(subcommand)}`, + ), + ), + "", + "### Command-specific options", + "", + renderOptionTable(reference.options), + "", + "Patch options after `--` accept every [common review option](#common-review-options).", + "", + `**Authentication:** ${reference.authentication.variables.map((name) => `\`${name}\``).join(", then ")}; ${reference.authentication.description}.`, + "", + ...subcommandSections.flatMap((section, index) => (index > 0 ? ["", section] : [section])), + ].join("\n"); +} + /** Render the deterministic exhaustive CLI reference. */ export function renderCliReference() { const commandSections = (Object.values(CLI_REFERENCE_COMMANDS) as CliReferenceCommand[]) .filter((command) => command.publicDocs !== false) - .map((command) => { + .flatMap((command) => { const pieces = [ `## \`hunk ${command.path}\``, "", @@ -171,7 +235,11 @@ export function renderCliReference() { `| \`${UNSAFE_ALLOW_REMOTE_SESSION_BROKER_ENV}\` | Set to \`1\` to allow unsafe non-loopback binding. |`, ); } - return pieces.join("\n"); + + // Bundled extension commands follow `hunk log`, matching their place in `hunk --help`. + return command.path === "log" + ? [pieces.join("\n"), renderGitHubSection()] + : [pieces.join("\n")]; }); const sessionSections = SESSION_AGENT_COMMAND_LIST.map((command) => { @@ -430,11 +498,11 @@ ${sectionRows.length > 0 ? ["| Table | Applies to |", "| --- | --- |", ...sectio ### Pager settings -\`[pager]\` applies when a diff opens as a pager session, such as Git output displayed by \`hunk pager\`. It overrides the matching command table **within the same file**, not in ordinary interactive reviews. +\`[pager]\` applies when a diff opens as a pager session: Git output displayed by \`hunk pager\`, or any review launched with \`--pager\`. It overrides the matching command table **within the same file**, not in ordinary interactive reviews. \`hunk pager\` reviews match \`[patch]\`. \`\`\`toml [pager] -menu_bar = false +mode = "unified" wrap_lines = true \`\`\` @@ -444,7 +512,7 @@ Try it for one Git invocation: git -c core.pager="hunk pager" diff \`\`\` -Pager reviews start with the files pane closed, even if \`sidebar = true\`. Non-diff input goes to a plain-text pager instead; these view preferences do not configure that fallback. See [Git pager and difftool](/docs/workflows/git-pager-and-difftool/) for permanent setup. +Pager reviews start with the files pane closed, even if \`sidebar = true\`, and hide the menu bar unless a config layer sets \`menu_bar\`. Non-diff input goes to a plain-text pager instead; these view preferences do not configure that fallback. See [Git pager and difftool](/docs/workflows/git-pager-and-difftool/) for permanent setup. ## Resolution and scope diff --git a/website/astro.config.mjs b/website/astro.config.mjs index fde7a894f..cd4466d97 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -178,7 +178,6 @@ export default defineConfig({ items: [ { label: "Troubleshooting", slug: "docs/help/troubleshooting" }, { label: "Terminal and platform compatibility", slug: "docs/help/compatibility" }, - { label: "Deployment integration", slug: "docs/help/deployment" }, ], }, // One link rather than a group: the changelog is generated, grows every release, and diff --git a/website/public/docs/hunk-review-skill.md b/website/public/docs/hunk-review-skill.md index ff0e6ef97..748c52de5 100644 --- a/website/public/docs/hunk-review-skill.md +++ b/website/public/docs/hunk-review-skill.md @@ -133,12 +133,12 @@ hunk session comment add --repo . --reply-to user:123 --summary "Addressed in th printf '%s\n' '{"comments":[{"filePath":"README.md","newLine":103,"summary":"Tighten this wording"}]}' | hunk session comment apply --repo . --stdin ``` -- `comment list --type user` shows human-authored inline notes; without `--type`, `comment list` preserves the legacy live-agent-comment view +- Agent notes start hidden: the user sees them only after pressing `a` or setting `agent_notes = true`. `--focus` jumps to the new note (or the first note in a batch) and turns agent notes on, so pass it on the first note you leave in a session, or tell the user to press `a` +- `comment list` shows the comments added through `hunk session comment` by default; `--type user` lists notes the user wrote with `c`, `--type ai` lists notes loaded from an `--agent-context` sidecar, `--type agent` lists agent-authored notes, and `--type all` lists every note - `comment add` is best for one note; `comment apply` is best when an agent already has several notes ready - Root `comment add` notes require `--file`, `--summary`, and exactly one of `--old-line` or `--new-line`; replies use `--reply-to ` with `--summary` and inherit the parent's anchor - `comment apply` items require `summary` plus either `replyTo` by itself or `filePath` with exactly one target such as `hunk`, `hunkNumber`, `oldLine`, or `newLine` - `comment apply` reads a JSON batch from stdin and validates the full batch before mutating the live session -- Pass `--focus` when you want to jump to the new note or the first note in a batch - `comment list` and `comment clear` accept optional `--file` - Quote `--summary` and `--rationale` defensively in the shell @@ -202,7 +202,7 @@ Guidelines: - Navigate before commenting so the user sees the code you're discussing - Use `highlight add --focus` to steer the user's eyes to the exact expression while you explain it, and `highlight clear` before moving to the next topic - Use `comment apply` for agent-generated batches and `comment add` for one-off notes -- Use `--focus` sparingly when the note itself should actively steer the review +- Use `--focus` on your first note so agent notes become visible, then sparingly when a note should actively steer the review - Keep comments focused: intent, structure, risks, or follow-ups - Don't comment on every hunk -- highlight what the user wouldn't spot themselves diff --git a/website/src/content/docs/docs/agents/agent-context-and-stml.md b/website/src/content/docs/docs/agents/agent-context-and-stml.md index 2d429e55b..1a3336353 100644 --- a/website/src/content/docs/docs/agents/agent-context-and-stml.md +++ b/website/src/content/docs/docs/agents/agent-context-and-stml.md @@ -1,9 +1,9 @@ --- -title: Agent context and experimental STML notes -description: Load review rationale from a sidecar and opt into rich terminal-native note markup. +title: Agent context and STML +description: Load review notes from a JSON sidecar and opt into experimental rich note markup. --- -Live session comments are the recommended workflow. A sidecar is useful when the annotations already exist before Hunk starts or need to travel with a patch. +[Live agent comments](/docs/agents/comments-and-annotations/) are the recommended workflow. A sidecar file is useful when the notes already exist before Hunk starts, or need to travel with a patch. ## Load a JSON sidecar @@ -12,21 +12,74 @@ hunk diff --agent-context notes.json hunk patch change.patch --agent-context notes.json ``` -The sidecar can set narrative file order and attach hunk-level annotations. Keep it concise: one changeset summary, short file summaries, and only rationale that improves the review. The visible UI prioritizes hunk notes rather than generic explainer cards. +Use `--agent-context -` to read the sidecar from stdin. With `--watch`, Hunk also reloads when the sidecar file changes, so an agent can rewrite the file and the open review updates; a stdin sidecar cannot be watched. -A compact example lives at `examples/3-agent-review-demo/agent-context.json` in the repository. +Sidecar annotations are agent notes, so they start hidden. Press `a` to show them, or launch with `--agent-notes`. + +## Sidecar format + +```json +{ + "version": 1, + "files": [ + { + "path": "src/search.ts", + "summary": "Scores and sorts matches instead of returning the first substring hit.", + "annotations": [ + { + "newRange": [15, 35], + "summary": "Prefix and exact matches now outrank weaker substring hits.", + "rationale": "The old behavior made every match look equally good.", + "author": "sonnet" + } + ] + } + ] +} +``` + +Top level: + +| Field | Type | Notes | +| --------- | ------ | -------------------------------------------------------------------------------- | +| `version` | number | Optional; defaults to `1`. | +| `summary` | string | Optional changeset summary. Extensions can read it; the TUI does not display it. | +| `files` | array | One entry per file you want to describe. | + +Each `files[]` entry: + +| Field | Type | Notes | +| ------------- | ------ | ----------------------------------------------------------------------------- | +| `path` | string | Required. Matches the file's current path, or its previous path for a rename. | +| `summary` | string | Optional. Used by the file filter, not shown as a card. | +| `annotations` | array | Optional notes for this file. | + +Each `annotations[]` entry: + +| Field | Type | Notes | +| ------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `summary` | string | Required. The note's headline. | +| `newRange` / `oldRange` | `[start, end]` | Optional 1-based, inclusive line range on the new or old side. A note with no range attaches to the file's first hunk. | +| `rationale` | string | Optional longer explanation shown under the summary. | +| `author` | string | Optional label for the card, such as `sonnet note`. | +| `markup` | string | Optional [STML](#opt-into-stml) body. Ignored unless the session launched with `--experimental`. | +| `id`, `tags`, `confidence`, `source`, `createdAt` | various | Optional metadata kept with the note. `confidence` is `low`, `medium`, or `high`. | + +Files listed in the sidecar appear first in the review, in sidecar order; other changed files follow in their usual order. Hunk rejects a sidecar with a missing `summary` or `path`, or a range whose end is before its start. + +Keep it concise: short file summaries and only rationale that improves the review. A fuller example lives at [`examples/3-agent-review-demo/agent-context.json`](https://github.com/modem-dev/hunk/blob/main/examples/3-agent-review-demo/agent-context.json). ## Opt into STML -STML is experimental rich markup for terminal note bodies. It is off by default: +STML is experimental markup for richer note bodies: boxes, rows, badges, lists, and code. It is off by default and only the launch flag turns it on: ```bash hunk --experimental diff --agent-context notes.json ``` -The launch flag is the authority for that session; a reload cannot turn the capability on later. Plain `summary` text remains required as the fallback. +A reload cannot turn STML on later, and there is no config key for it. Every note still needs a plain `summary`, which is the fallback whenever markup is unavailable. Without `--experimental`, a sidecar's `markup` is ignored and a live `--markup` comment is rejected. -Before sending markup, an agent should inspect support and width: +Before sending markup, an agent should check support and width: ```bash hunk session context --repo . --json @@ -34,4 +87,4 @@ hunk markup guide hunk markup render - --width ``` -Only send `--markup` when `experimentalFeatures` includes `stml`. Keep colors symbolic and markup compact so notes retain a clear spatial relationship to their code. +Only send `--markup` when `experimentalFeatures` includes `stml`. `hunk markup guide` prints the tag reference and copy-paste patterns. Keep colors symbolic and markup compact so notes stay close to their code. diff --git a/website/src/content/docs/docs/agents/comments-and-annotations.md b/website/src/content/docs/docs/agents/comments-and-annotations.md index 62511bc60..036f4c192 100644 --- a/website/src/content/docs/docs/agents/comments-and-annotations.md +++ b/website/src/content/docs/docs/agents/comments-and-annotations.md @@ -3,7 +3,21 @@ title: Comments and annotations description: Attach human or agent review notes to the code and navigate them in context. --- -Notes are hunk-specific and render beside the rows they explain. Hunk intentionally keeps them in the review flow rather than in a separate comments screen. +Notes are hunk-specific and render beside the rows they explain. Hunk keeps them in the review flow rather than in a separate comments screen. + +## Kinds of notes + +| Term | What it is | +| ---------------------- | ---------------------------------------------------------------------------------------------------- | +| **Note** | Any card shown beside the code. | +| **Agent comment** | A note an agent adds to a live window with `hunk session comment`. This is the recommended path. | +| **Sidecar annotation** | A note loaded at launch from an [`--agent-context` JSON file](/docs/agents/agent-context-and-stml/). | +| **Your note** | A note you write in Hunk with `c`. | +| **Highlight** | A temporary mark on a character range. It draws attention but is not a note. | + +Agent comments and sidecar annotations are both **agent notes**. They start hidden: press `a` to show or hide them, or launch with `--agent-notes`. Your own notes are always visible. + +Notes live in the Hunk window. Closing the window discards them. ## Add one agent comment @@ -12,19 +26,49 @@ hunk session comment add \ --repo . \ --file README.md \ --new-line 103 \ - --summary "Tighten this wording" + --summary "Tighten this wording" \ + --focus ``` -Choose exactly one `--old-line` or `--new-line` target. Add `--focus` only when the new note should move the user's viewport. +Choose exactly one `--old-line` or `--new-line` target. `--focus` moves the user's viewport to the new note and turns agent notes on; use it for the first note in a session and when a note should actively steer the review. + +Optional fields: + +- `--rationale `: a longer explanation shown under the summary. +- `--author `: the label on the note card, such as `Claude note` instead of `Agent note`. +- `--markup `: an experimental rich body; see [Agent context and STML](/docs/agents/agent-context-and-stml/#opt-into-stml). ## Apply a batch ```bash printf '%s\n' '{"comments":[{"filePath":"README.md","newLine":103,"summary":"Tighten this wording"}]}' \ - | hunk session comment apply --repo . --stdin + | hunk session comment apply --repo . --stdin --focus ``` -Every item needs `filePath`, `summary`, and exactly one target: `hunk`, `hunkNumber`, `oldLine`, or `newLine`. Hunk validates the complete batch before changing the live session. +Each item needs a `summary` and either: + +- `filePath` with exactly one target: `hunk`, `hunkNumber`, `oldLine`, or `newLine`, or +- `replyTo` by itself, to reply to an existing note. + +Items also accept `rationale`, `author`, and `markup`. Hunk validates the complete batch before changing the live session. `--focus` jumps to the first note in the batch. + +## Reply to a note + +A reply inherits the parent note's file and line: + +```bash +hunk session comment add --repo . --reply-to user:123 --summary "Addressed in the latest revision" +``` + +## Hand your notes to the agent + +Your notes are a way to ask the agent questions in context: + +1. Press `c` on a line and write the question. +2. Ask your agent to read your notes. It runs `hunk session comment list --repo . --type user --json` or `hunk session review --repo . --include-notes --json`. +3. The agent answers with `--reply-to `, and the reply appears under your note. + +Agents can remove your notes during cleanup but cannot create or edit them. ## Inspect and clean up @@ -35,8 +79,18 @@ hunk session comment rm --repo . hunk session comment clear --repo . --file README.md --yes ``` -Use `--all --yes` to clear both live agent comments and human notes. Destructive clears require confirmation. +`comment list` shows agent comments by default. `--type` selects other notes: + +| `--type` | Lists | +| ---------------- | --------------------------------------------- | +| `live` (default) | Comments added through `hunk session comment` | +| `user` | Your notes | +| `ai` | Sidecar annotations | +| `agent` | Agent-authored notes | +| `all` | Every note | + +`comment clear` removes agent comments. Add `--all` (or its synonym `--include-user`) to clear your notes too. Clearing always requires `--yes`. -## Add a human note +## Add your own note -In the TUI, select a hunk and press `c` or click an add-note affordance. Human and agent notes are labeled by source. Use `{` and `}` to move through annotated hunks across the review stream. +In the TUI, move to a line and press `c`, or click an add-note affordance. Press `Ctrl+S` to save or Escape to cancel. Human and agent notes are labeled by source. Press `{` and `}` to move through annotated hunks across the review stream. diff --git a/website/src/content/docs/docs/agents/live-session-control.md b/website/src/content/docs/docs/agents/live-session-control.md index 2bd110037..6c02883c3 100644 --- a/website/src/content/docs/docs/agents/live-session-control.md +++ b/website/src/content/docs/docs/agents/live-session-control.md @@ -34,9 +34,10 @@ hunk session review --repo . --include-patch --json hunk session navigate --repo . --file src/App.tsx --hunk 2 hunk session navigate --repo . --file src/App.tsx --new-line 372 hunk session navigate --repo . --next-comment +hunk session navigate --repo . --comment ``` -Hunk numbers are 1-based. Absolute navigation needs a file and exactly one hunk, old-line, or new-line target. +Hunk numbers are 1-based. Absolute navigation needs a file and exactly one hunk, old-line, or new-line target. `--next-comment` and `--prev-comment` move between annotated hunks; `--comment` jumps to one note by the id that `comment list --json` returns. ## Reload the review @@ -49,10 +50,29 @@ hunk session reload --repo . -- show HEAD~1 -- README.md Advanced reloads can target the live window by `--session-path` and load from a separate `--source` directory. Prefer `--repo` until those roles genuinely need to differ. -## Diagnose local access +## Security and access -If a visible Hunk window does not appear in `session list`, an agent sandbox may block loopback access. Hunk's daemon is intentionally local-only; retry with the agent's network/sandbox permission rather than exposing it remotely. `hunk daemon serve` is available for manual startup or daemon debugging. +The daemon listens on `127.0.0.1:47657` and refuses non-loopback addresses. Session commands authenticate with an owner-private credential that Hunk discovers automatically, so only your user account can control your windows; there are no credential flags to pass. The daemon starts with the first Hunk window and shuts down about a minute after the last one closes. -## Daemon build mismatches +| Variable | Effect | +| -------------------------------- | ------------------------------------------------------------------------------------------ | +| `HUNK_MCP_HOST`, `HUNK_MCP_PORT` | Move the daemon to another loopback host or port, for example to avoid a port conflict. | +| `HUNK_MCP_DISABLE=1` | Start the TUI without registering a session. | +| `HUNK_MCP_UNSAFE_ALLOW_REMOTE=1` | Allow a non-loopback host. Anyone who can reach the port can attempt to connect; avoid it. | -After a Hunk upgrade, a daemon from the previous build keeps running while any older window holds it open. A window from the new build shows a status-bar notice naming both builds, and every `hunk session` command fails with a `daemon-build-mismatch` error (in-band under `--json`) that names both builds, counts the attached windows, and recommends `restart-daemon` or `use-newer-hunk`. Run `hunk daemon status` to see the daemon's build and attached windows, and `hunk daemon restart` to replace it with one from this build after confirming; windows that could not attach register on their own afterwards, while windows from the old build must be relaunched. Agents should report the mismatch and ask before restarting. +If a visible Hunk window does not appear in `session list`, an agent sandbox may be blocking loopback access. Grant the agent local network access and retry; don't expose the daemon remotely. `hunk daemon serve` starts the daemon in the foreground for debugging. + +## After upgrading Hunk + +A daemon from the previous build keeps running while any older Hunk window is open. Until it is replaced: + +- a window from the new build shows a status-bar notice naming both builds, and +- every `hunk session` command fails with a `daemon-build-mismatch` error (reported in-band under `--json`). + +To fix it: + +1. Run `hunk daemon status` to see the daemon's build and its attached windows. +2. Run `hunk daemon restart` and confirm. Windows from the new build reattach on their own. +3. Relaunch any windows from the old build. Their notes are lost. + +Agents should report the mismatch and ask before restarting the daemon. diff --git a/website/src/content/docs/docs/agents/review-skill.md b/website/src/content/docs/docs/agents/review-skill.md index 46968aea3..e81c4a44f 100644 --- a/website/src/content/docs/docs/agents/review-skill.md +++ b/website/src/content/docs/docs/agents/review-skill.md @@ -1,32 +1,39 @@ --- title: Hunk review skill -description: Load the versioned machine guidance that teaches coding agents Hunk's live review protocol. +description: Install the versioned skill that teaches coding agents Hunk's live review commands. --- -Hunk ships a generated `hunk-review` skill with every installation. It is the authoritative machine-facing workflow for session selection, efficient review inspection, navigation, reloads, and comments. +Every Hunk installation bundles a `hunk-review` skill. It teaches agents how to find a live session, inspect the review efficiently, navigate, reload, and leave notes, and it tells them not to launch the interactive TUI themselves. -## Locate the installed skill +## Install it + +Install the skill with the `skills` CLI: ```bash -hunk skill path +npx skills add modem-dev/hunk -g --skill hunk-review ``` -Load or symlink the returned file according to your coding agent's skill mechanism. Resolve the path again after upgrades so the guidance stays aligned with the installed CLI. +`-g` installs the skill for your user instead of one project. Add `-a ` (for example `-a claude-code`) to install it for a single agent. -For agents that need a stable web-readable URL, use the [generated Hunk review skill](/docs/hunk-review-skill.md). The published artifact and installed skill are rendered by the same function; neither is a handwritten copy. +The installed file is a pointer: it tells the agent to run `hunk skill show hunk-review` and follow the output. The instructions therefore come from the Hunk binary on your `PATH` and stay in step with it after every upgrade; you don't need to reinstall the skill. -## Why it is generated +## Load it without the skills CLI -The checked-in `packages/hunk/skills/hunk-review/SKILL.md` is rendered from typed command metadata and agent error definitions in Hunk's source. Parser help, examples, constraints, and common remedies therefore share ownership instead of drifting as separate handwritten copies. +| Agent setup | Command | +| --------------------------------------- | ----------------------------------------------------- | +| Loads instructions on demand | `hunk skill show` prints the skill to stdout. | +| Loads or symlinks skill files from disk | `hunk skill path` prints the bundled file's path. | +| Can only fetch URLs | Use the [published copy](/docs/hunk-review-skill.md). | -Do not edit the generated skill directly. Contributors change `packages/hunk/src/hunk-review/skillDocument.ts`, `packages/hunk/src/session/agent/surface.ts`, or `packages/hunk/src/session/agent/errors.ts`, then run: +The published copy tracks the newest Hunk release, so prefer `hunk skill show` when your installed Hunk might be older. -```bash -bun run generate:skill -``` +## Other bundled skills -## Use it safely +`hunk-extensions` teaches agents to build [extensions](/docs/extend/extensions/) against the `hunkdiff/extension` API: -The skill instructs agents to avoid launching interactive commands such as `hunk diff` themselves. The user owns the TUI; the agent talks to an already-live review through `hunk session *`. +```bash +npx skills add modem-dev/hunk -g --skill hunk-extensions +hunk skill show hunk-extensions +``` -For the human workflow around that surface, start with [Review with an agent](/docs/agents/review-with-an-agent/). +For the human side of the workflow, start with [Review with an agent](/docs/agents/review-with-an-agent/). diff --git a/website/src/content/docs/docs/agents/review-with-an-agent.md b/website/src/content/docs/docs/agents/review-with-an-agent.md index fffd0f23c..d62551a10 100644 --- a/website/src/content/docs/docs/agents/review-with-an-agent.md +++ b/website/src/content/docs/docs/agents/review-with-an-agent.md @@ -5,58 +5,66 @@ description: Let a coding agent inspect and guide a live Hunk review without giv The Hunk window stays with you. Your agent uses non-interactive `hunk session` commands from another terminal to inspect the same review, navigate it, and leave inline notes. -## Start the review +## Install the review skill + +The `hunk-review` skill teaches your agent the session commands. Install it once with the `skills` CLI: ```bash -hunk diff +npx skills add modem-dev/hunk -g --skill hunk-review ``` -Keep that window open. Normal Hunk sessions register with a local loopback daemon so the session CLI can find them. +The installed skill is a small pointer that runs `hunk skill show`, so the instructions your agent reads always match the Hunk version on your `PATH`. Add `-a ` (for example `-a claude-code`) to install it for a single agent. -## Give the agent the skill +If your agent has no skill system, ask it to run `hunk skill show` and follow the output. See [Hunk review skill](/docs/agents/review-skill/) for other ways to load it. -In the agent's shell, locate the skill bundled with the installed Hunk version: +## Start the review ```bash -hunk skill path +hunk diff ``` -Ask the agent to load that file and use it for the review. A portable prompt is: +Keep that window open. Normal Hunk sessions register with a local loopback daemon so the session CLI can find them. -```text -Load the Hunk skill and use it for this review. Run `hunk skill path` to get the skill path. -``` +## Ask the agent to review -The skill tells agents not to launch the interactive TUI themselves. It teaches them to use the session surface instead. +In your agent, ask for the review: -## What the agent does +```text +Use the hunk-review skill to walk me through the changes in my open Hunk session. +``` -A typical agent flow is: +The skill tells agents not to launch the interactive TUI themselves. A typical agent flow is: ```bash hunk session list hunk session get --repo . hunk session review --repo . --json hunk session navigate --repo . --file src/App.tsx --hunk 2 -hunk session comment add --repo . --file src/App.tsx --new-line 42 --summary "Check this boundary" +hunk session comment add --repo . --file src/App.tsx --new-line 42 --summary "Check this boundary" --focus ``` `review --json` exposes structure without forcing the full patch into agent context. The agent should request `--include-patch` only when it actually needs raw unified diff text. +## See the agent's notes + +Agent notes are hidden until you turn them on. Press `a` to show or hide them, or launch with `--agent-notes` (or set `agent_notes = true` in your [config](/docs/configure/configuration/)) to show them from the start. When the agent adds a note with `--focus`, Hunk jumps to it and turns agent notes on for you. + ![A Hunk review with agent rationale rendered directly beside the annotated diff hunk](/docs/images/agent-comments.webp) -Agent notes remain spatially attached to the code they explain. Use `{` and `}` to move between annotated hunks while keeping the full changeset visible. +Agent notes stay attached to the code they explain. Press `{` and `}` to move between annotated hunks while keeping the full changeset visible. + +## Keep control + +The agent can move the visible selection and add notes, but you stay in the review stream and can navigate normally. Ask it to summarize when finished, then walk the annotated hunks with `{` and `}`. + +To hand questions back to the agent, leave your own notes with `c`; see [Comments and annotations](/docs/agents/comments-and-annotations/#hand-your-notes-to-the-agent). ## Give the agent the docs These docs are published as plain Markdown so an agent can read them without scraping HTML: -- [/llms.txt](https://hunk.dev/llms.txt) — index of every page, for pulling only what is needed. -- [/llms-small.txt](https://hunk.dev/llms-small.txt) — compact corpus for tight context budgets. -- [/llms-full.txt](https://hunk.dev/llms-full.txt) — the complete docs in one file, around 130KB. +- [/llms.txt](https://hunk.dev/llms.txt): an index of every page, for pulling only what is needed. +- [/llms-small.txt](https://hunk.dev/llms-small.txt): a compact corpus for tight context budgets. +- [/llms-full.txt](https://hunk.dev/llms-full.txt): every page and the full changelog in one file. Any docs page URL also returns its Markdown source with `.md` appended, so `https://hunk.dev/docs/reference/cli.md` is the CLI reference as the agent should read it. - -## Keep control - -The agent can guide the visible selection and add agent-authored notes, but you remain in the review stream and can navigate normally. Ask it to summarize when finished, then use `{` and `}` to walk annotated hunks. diff --git a/website/src/content/docs/docs/configure/configuration.md b/website/src/content/docs/docs/configure/configuration.md index 5dff6c54c..0c80861b4 100644 --- a/website/src/content/docs/docs/configure/configuration.md +++ b/website/src/content/docs/docs/configure/configuration.md @@ -90,11 +90,11 @@ Some settings are user-only, such as keyboard bindings and `wheel_scroll_lines`. ## Set pager preferences -For diff reviews opened through `hunk pager`, add: +For diff reviews opened through `hunk pager` or with `--pager`, add: ```toml [pager] -menu_bar = false +mode = "unified" wrap_lines = true ``` @@ -104,7 +104,7 @@ Try it without changing your Git configuration: git -c core.pager="hunk pager" diff ``` -`[pager]` overrides the matching command table within each config file. It doesn't apply to ordinary interactive reviews. Pager reviews start with the files pane closed; non-diff input uses a separate plain-text pager. +`hunk pager` reviews resolve like `hunk patch` reviews: Hunk applies the `[patch]` table, then `[pager]` on top of it, within each config file. `[pager]` doesn't apply to ordinary interactive reviews. Pager reviews start with the files pane closed and, unless a config layer sets `menu_bar`, the menu bar hidden; non-diff input uses a separate plain-text pager. See [Git pager and difftool](/docs/workflows/git-pager-and-difftool/) for permanent setup and fallback behavior. diff --git a/website/src/content/docs/docs/configure/layout-and-display.md b/website/src/content/docs/docs/configure/layout-and-display.md index b2d1cd33f..a7d05c282 100644 --- a/website/src/content/docs/docs/configure/layout-and-display.md +++ b/website/src/content/docs/docs/configure/layout-and-display.md @@ -17,7 +17,7 @@ hunk diff --mode unified - `split` keeps before and after columns side by side. - `unified` shows changed rows in a single-width flow. -Explicit split and unified choices override responsive behavior. Press `0`, `1`, or `2` to switch while reviewing. The former `stack` value remains accepted as a deprecated input alias and normalizes to `unified`. +Explicit split and unified choices override responsive behavior. While reviewing, press `0` for auto, `1` for unified, or `2` for split. The former `stack` value remains accepted as a deprecated input alias and normalizes to `unified`. ## Tune code rows @@ -47,7 +47,7 @@ transparent_background = false cursor_line = "row" ``` -`transparent_background` lets the terminal paint Hunk surfaces; turn it off when exact theme surfaces matter more than matching terminal transparency. +`transparent_background` lets the terminal's own background show through Hunk surfaces. It is off by default; turn it on when matching a transparent or blurred terminal matters more than exact theme colors. Set `animations = false` to make panes open and close immediately. diff --git a/website/src/content/docs/docs/extend/extension-api.md b/website/src/content/docs/docs/extend/extension-api.md index 9eb61b944..4bd4c3f13 100644 --- a/website/src/content/docs/docs/extend/extension-api.md +++ b/website/src/content/docs/docs/extend/extension-api.md @@ -7,25 +7,38 @@ The extension factory receives one API object. Registration calls are only valid ## `hunk.apiVersion` -The API generation this Hunk speaks (currently `28`). Branch on it if you want -one file to support several Hunk versions. Version 28 adds host-owned syntax highlighting for file-view code documents; version 27 adds `ctx.selection.files`, the visible files in review order; version 26 adds the status line (`ctx.statusLine` items and `ctx.prompts.line()` inline prompts); version 25 adds Promise-returning watch signatures and watch cancellation; version 24 adds review metadata to VCS patch results and short display revisions to commit descriptors; version 23 adds canonical unified-layout fields while preserving the previous event vocabulary; version 22 adds frame-derived pane preferred sizing, non-resizable dynamic panes, and commit-history paint tokens; version 21 adds optional inclusive history-range review -planning and bounded comparison commit summaries; version 20 adds optional commit timestamps to review -metadata, pane clipboard actions, and the `theme.copyAction` paint token; version 19 adds provider-owned history enumeration and review planning; version 18 lets -lifecycle and custom-event handlers request a host-owned review reload; version 17 adds structured review metadata to delegated -patch commands and projects it into pane availability and component props; version 16 adds pane-wide -`onActivate`; version 15 added `{ side, line }` to opted-in pane `currentLine` -paint; version 14 added structured two-revision -VCS diff endpoints; version 13 added saved-note parent identities -and committed note-edit events; version 12 added responsive fractional pane -sizing; version 11 added the `dim` line-highlight tone; version 10 added -generic top-level CLI commands; version 9 -added exact-filename and glob selectors to `registerFileLanguage`; version 8 -added authoritative review snapshots to command handlers; version 7 added the -current source line to command selection snapshots. Version 6 added session -behavior, terminal-command observation, and live navigation/dialogs in lifecycle -and bus handlers; version 5 added line highlighters and line-granular navigation -(`revealLine`); version 4 added keyboard modes and docked panes, with API-v3 -sidebar names remaining as deprecated aliases. +The API generation this Hunk speaks (currently `31`). Branch on it if you want one file to support several Hunk versions, or declare `"hunk": {"apiVersion": N}` in a folder extension's `package.json` so an older Hunk refuses to install it. + +| Version | Adds | +| ------- | --------------------------------------------------------------------------------------------------------- | +| 31 | Optional read-only VCS workspace status, sibling inspection, and status review planning | +| 30 | The opaque current review generation (`reviewGeneration`) in pane props | +| 29 | Generation-scoped review presentation scopes | +| 28 | Host-owned syntax highlighting for file-view code documents | +| 27 | `ctx.selection.files`, the visible files in review order | +| 26 | The status line: `ctx.statusLine` items and `ctx.prompts.line()` inline prompts | +| 25 | Promise-returning watch signatures and watch cancellation | +| 24 | Review metadata on VCS patch results; short display revisions on commit descriptors | +| 23 | Canonical unified-layout fields, keeping the previous event vocabulary | +| 22 | Frame-derived pane preferred sizing, non-resizable dynamic panes, commit-history paint tokens | +| 21 | Optional inclusive history-range review planning and bounded comparison commit summaries | +| 20 | Optional commit timestamps in review metadata, pane clipboard actions, `theme.copyAction` | +| 19 | Provider-owned history enumeration and review planning | +| 18 | Lifecycle and custom-event handlers can request a host-owned review reload | +| 17 | Structured review metadata on delegated patch commands, projected into panes | +| 16 | Pane-wide `onActivate` | +| 15 | `{ side, line }` on opted-in pane `currentLine` paint | +| 14 | Structured two-revision VCS diff endpoints | +| 13 | Saved-note parent identities and committed note-edit events | +| 12 | Responsive fractional pane sizing | +| 11 | The `dim` line-highlight tone | +| 10 | Generic top-level CLI commands | +| 9 | Exact-filename and glob selectors in `registerFileLanguage` | +| 8 | Authoritative review snapshots in command handlers | +| 7 | The current source line in command selection snapshots | +| 6 | Session behavior, terminal-command observation, live navigation and dialogs in lifecycle and bus handlers | +| 5 | Line highlighters and line-granular navigation (`revealLine`) | +| 4 | Keyboard modes and docked panes; API-v3 sidebar names remain as deprecated aliases | ## `hunk.registerCliCommand(command, handler)` @@ -488,7 +501,7 @@ Every handler and transform receives a context with `cwd` and `notify`; event an ## `hunk.log(message)` -Record a diagnostic line. Logs are collected per extension rather than written to the terminal, because the TUI owns the screen. +Record a diagnostic line. Logs are collected per extension rather than written to the terminal, because the TUI owns the screen. Hunk does not display collected logs yet, so use [`ctx.notify`](#ctxnotifymessage-type) while debugging. ## Not contributable yet diff --git a/website/src/content/docs/docs/extend/extensions.md b/website/src/content/docs/docs/extend/extensions.md index 249aa7ef1..f1f9c819c 100644 --- a/website/src/content/docs/docs/extend/extensions.md +++ b/website/src/content/docs/docs/extend/extensions.md @@ -130,7 +130,7 @@ Run this repository's extensions? **Trust** records the decision and reloads the session; **not now** asks again next time; **never** stops the offers. The prompt is a dialog over the review stream, not a gate in front of it — dismiss it and keep reviewing. -Decisions are stored per repository root in `~/.config/hunk/state.json`, keyed by path (the VS Code workspace-trust model). A different repository later occupying a trusted path inherits the decision; clear the entry if that matters for a path you reuse. +Decisions are stored per repository root in `$XDG_STATE_HOME/hunk/state.json` (by default `~/.local/state/hunk/state.json`), keyed by path (the VS Code workspace-trust model). A different repository later occupying a trusted path inherits the decision; clear the entry if that matters for a path you reuse. ## Failure isolation diff --git a/website/src/content/docs/docs/extend/vcs-adapters.md b/website/src/content/docs/docs/extend/vcs-adapters.md index 57619d515..7b02786bc 100644 --- a/website/src/content/docs/docs/extend/vcs-adapters.md +++ b/website/src/content/docs/docs/extend/vcs-adapters.md @@ -6,10 +6,18 @@ description: Contribute a version-control backend with detection, watch support, `hunk.registerVcsAdapter(adapter)` contributes an additional VCS backend. This is the same call Hunk's own bundled Git, Jujutsu, and Sapling backends make. ```ts +/** Walk upward from the launch directory to the nearest Mercurial checkout. */ +function detect(cwd: string) { + for (let dir = resolve(cwd); ; dir = dirname(dir)) { + if (existsSync(join(dir, ".hg"))) return { id: "hg", repoRoot: dir }; + if (dirname(dir) === dir) return null; + } +} + hunk.registerVcsAdapter({ id: "hg", name: "Mercurial", - detect: (cwd) => (existsSync(join(cwd, ".hg")) ? { id: "hg", repoRoot: cwd } : null), + detect, operations: { "working-tree-diff": { async load(input, ctx) { @@ -42,7 +50,7 @@ A `load` result is patch text plus how to label it. Everything else on it is opt ## Detection order -Detection prefers the **nearest** checkout: a Git repository nested inside a jj workspace is reviewed as Git, whatever the priorities say. The same rule covers your adapter — a Mercurial checkout inside a Git repository is reviewed as Mercurial. `detectionPriority` only decides which backend wins when several recognize the _same_ directory — the colocated case, where one working copy carries two sets of markers. +Hunk calls `detect` once with the directory Hunk was launched from, so your adapter must walk up to the checkout root itself, as the example above does. Detection then prefers the **nearest** checkout: a Git repository nested inside a jj workspace is reviewed as Git, whatever the priorities say. The same rule covers your adapter — a Mercurial checkout inside a Git repository is reviewed as Mercurial. `detectionPriority` only decides which backend wins when several recognize the _same_ directory — the colocated case, where one working copy carries two sets of markers. | Adapter | Priority | | ------------------------ | -------------------------------------------- | diff --git a/website/src/content/docs/docs/help/compatibility.md b/website/src/content/docs/docs/help/compatibility.md index a2f404122..44643c00e 100644 --- a/website/src/content/docs/docs/help/compatibility.md +++ b/website/src/content/docs/docs/help/compatibility.md @@ -3,7 +3,7 @@ title: Terminal and platform compatibility description: Understand Hunk's OS requirements and the terminal capabilities behind mouse, color, copy, and layout behavior. --- -Hunk supports macOS, Linux, and Windows. npm installs require Node.js 22 or newer; standalone installs do not require Node.js. Git is recommended; Jujutsu and Sapling support requires their respective command-line tools. +Hunk publishes prebuilt binaries for macOS (arm64, x64), Linux (arm64, x64; glibc), and Windows (x64). There are no prebuilt binaries for Windows on arm64 or for musl-based Linux such as Alpine. On x86-64, Hunk needs a CPU with SSE4.2. npm installs require Node.js 22 or newer; standalone installs do not require Node.js. Git is recommended; Jujutsu and Sapling support requires their respective command-line tools. ## Terminal capabilities @@ -22,10 +22,22 @@ SSH, tmux, and similar layers can filter mouse, clipboard, keyboard, or color se ## Windows notes -Run Hunk in a modern Windows terminal. npm installs require Node.js 22 or newer on `PATH`; standalone mise installs do not require Node.js. Repository paths and config locations follow platform conventions; examples use shell-neutral Hunk arguments where possible. Git Bash, PowerShell, and other shells quote commands differently, so adapt multi-line shell examples to your environment. +Run Hunk in a modern Windows terminal. npm installs require Node.js 22 or newer on `PATH`; standalone mise installs do not require Node.js. + +Hunk uses the same directory layout on Windows as on macOS and Linux, rooted at your home directory (`HOME` if set, otherwise `%USERPROFILE%`) rather than `%APPDATA%`: + +| File | Default location | +| ------------------- | -------------------------------------------- | +| User config | `%USERPROFILE%\.config\hunk\config.toml` | +| Saved state (trust) | `%USERPROFILE%\.local\state\hunk\state.json` | +| Global extensions | `%USERPROFILE%\.config\hunk\extensions\` | + +Set `XDG_CONFIG_HOME` or `XDG_STATE_HOME` to move them. The plain-text pager falls back to `more`, and `hunk completion` supports bash and zsh only. + +Git Bash, PowerShell, and other shells quote commands differently, so adapt multi-line shell examples to your environment. ## Light and dark backgrounds -`--theme auto` queries the terminal. If no answer arrives, Hunk uses the dark default. Choose `github-light-default` or another explicit theme when terminal reporting, transparency, or remote layers make detection unreliable. +The default `terminal` theme reads your terminal's palette and falls back to a standard xterm-style palette when the terminal doesn't answer. `--theme auto` queries the terminal background and falls back to `github-dark-default`. Choose `github-light-default` or another explicit theme when terminal reporting, transparency, or remote layers make detection unreliable. For a specific failure, continue with [Troubleshooting](/docs/help/troubleshooting/). diff --git a/website/src/content/docs/docs/help/troubleshooting.md b/website/src/content/docs/docs/help/troubleshooting.md index 2b0d31942..35c3e859b 100644 --- a/website/src/content/docs/docs/help/troubleshooting.md +++ b/website/src/content/docs/docs/help/troubleshooting.md @@ -5,14 +5,13 @@ description: Diagnose missing input, session access, terminal behavior, and conf ## Hunk shows no changes -Confirm the VCS and input first: +Confirm that your VCS sees changes: ```bash git status --short -hunk diff --help ``` -A Git working-tree review includes untracked files by default. Pager input does not, because Git decides what enters the pipe. Check `vcs` when a directory contains markers for more than one backend. +If it lists files but Hunk shows nothing, check which backend Hunk picked. In a checkout with markers for more than one VCS (for example a colocated jj and Git repository), Hunk prefers Jujutsu, then Sapling; set `vcs` in the repository's `.hunk/config.toml` to choose. A Git working-tree review includes untracked files by default. Pager input does not, because Git decides what enters the pipe. ## A live session is not found @@ -25,13 +24,17 @@ hunk session get --repo . Use the repository root that the live review loaded. If an agent sandbox blocks loopback networking, grant local network access and retry. Do not expose the local daemon publicly. If the daemon itself prevents startup, set `HUNK_MCP_DISABLE=1` to run the TUI without session registration, then report the failure. +If every `hunk session` command fails with `daemon-build-mismatch` after an upgrade, an older daemon is still running; see [After upgrading Hunk](/docs/agents/live-session-control/#after-upgrading-hunk). If another program holds port 47657, set `HUNK_MCP_PORT` to a free port in the environment of both Hunk and your agent. + ## Watch mode is rejected -`--watch` needs file- or VCS-backed input Hunk can reopen. It cannot replay stdin patches or `--agent-context -`. Save the input to a file or use `hunk diff` / `hunk show` directly. +`--watch` needs an interactive terminal and file- or VCS-backed input Hunk can reopen. It cannot run when Hunk's output is piped, and it cannot replay stdin patches or `--agent-context -`. Save the input to a file or use `hunk diff` / `hunk show` directly. + +If `hunk pager` or `hunk patch -` fails with this error even though you didn't pass `--watch`, your config has a top-level `watch = true`. Move it into a command table such as `[vcs]`; see [Watch mode](/docs/workflows/watch-mode/#turn-it-on-by-default). ## Theme detection looks wrong -Some terminals do not answer color queries. The default `terminal` theme then falls back to a standard xterm-style palette and `theme = "auto"` falls back to `github-dark-default`; choose a theme explicitly if needed. Disable transparency if terminal compositing makes contrast unpredictable. +Some terminals do not answer color queries. The default `terminal` theme then falls back to a standard xterm-style palette and `theme = "auto"` falls back to `github-dark-default`; choose a theme explicitly if needed. If you turned on `transparent_background`, turn it off (`--no-transparent-bg`) when terminal compositing makes contrast unpredictable. ## Layout or text is hard to read diff --git a/website/src/content/docs/docs/index.mdx b/website/src/content/docs/docs/index.mdx index d46167b2e..40490b2db 100644 --- a/website/src/content/docs/docs/index.mdx +++ b/website/src/content/docs/docs/index.mdx @@ -49,7 +49,7 @@ Hunk is a terminal diff viewer for reviewing complete changesets and keeping age @@ -58,7 +58,7 @@ Hunk is a terminal diff viewer for reviewing complete changesets and keeping age - [Review current work or a commit](/docs/workflows/working-trees-and-commits/) - [Configure Hunk as a Git pager or difftool](/docs/workflows/git-pager-and-difftool/) - [Review a live session with a coding agent](/docs/agents/review-with-an-agent/) -- [Choose a theme and layout](/docs/configure/themes/) +- [Choose a theme](/docs/configure/themes/) - [Remap a keyboard shortcut](/docs/configure/keybindings/) - [Build a custom file preview](/docs/extend/file-previews/) - [Look up a command](/docs/reference/cli/) diff --git a/website/src/content/docs/docs/reference/cli.md b/website/src/content/docs/docs/reference/cli.md index eba95fa6d..295095538 100644 --- a/website/src/content/docs/docs/reference/cli.md +++ b/website/src/content/docs/docs/reference/cli.md @@ -174,6 +174,120 @@ Status never fetches or modifies the repository; providers without status report | `--json` | emit a version 1 JSON snapshot | | `--color ` | color output: auto, always, never | +## `hunk log` + +browse repository history + +### Usage + +```bash +hunk log [target] [-- ] +``` + +A terminal opens the responsive browser; pipes and redirects receive static output. + +Use --static to force static output, paging when it exceeds the terminal. + +The selected VCS provider defines revision, filtering, and review semantics. + +### Command-specific options + +| Option | Description | +| -------------------------- | --------------------------------------------------------------- | +| `--all` | include history from every provider-visible head | +| `--first-parent` | follow only the first parent of merge commits | +| `-n, --max-count ` | stop after this many commits | +| `--author ` | limit commits by author | +| `--grep ` | limit commits by subject or message | +| `--since ` | show commits newer than a provider date | +| `--until ` | show commits older than a provider date | +| `--color ` | color output: auto, always, never | +| `--format ` | static record format: medium or compact | +| `--oneline` | alias for static --format compact | +| `--theme ` | use the same theme as Hunk review | +| `--ascii` | use an ASCII graph | +| `--static` | print static output, paging when needed | +| `--vcs ` | select a VCS history provider | +| `--extension ` | load an extension entry file or directory (repeatable) | +| `--no-extensions` | disable user extensions for this run | +| `--disable-extension ` | disable one bundled or user extension for this run (repeatable) | +| `--enable-extension ` | re-enable one extension for this run (repeatable) | + +## `hunk gh` + +Review GitHub pull requests, commits, and comparisons. This command is provided by the bundled `hunk.gh` extension; disable it with `--disable-extension hunk.gh` or the [`[extensions]` config table](/docs/reference/config/#extensions). + +### Usage + +```bash +hunk gh pr [pull-request] [--repo ] [-- ] +hunk gh commit [--repo ] [-- ] +hunk gh compare ... [--repo ] [-- ] +``` + +### Command-specific options + +| Option | Description | +| ----------------------- | ------------------------------------------------------ | +| `--repo ` | use an explicit repository instead of the local origin | +| `-- ` | pass the remaining options to the `hunk patch` review | + +Patch options after `--` accept every [common review option](#common-review-options). + +**Authentication:** `GH_TOKEN`, then `GITHUB_TOKEN`; optional for public repositories. + +### `hunk gh pr` + +Review a GitHub pull request without requiring the gh CLI. + +```bash +hunk gh pr [pull-request] [--repo ] [-- ] +``` + +**Pull request forms:** + +```bash +hunk gh pr # find the open PR for the current branch +hunk gh pr 123 # infer owner/repo from the local origin +hunk gh pr 123 --repo modem-dev/hunk # use an explicit repository +hunk gh pr 'modem-dev/hunk#123' # name the repository and pull request +hunk gh pr https://github.com/modem-dev/hunk/pull/123 +``` + +### `hunk gh commit` + +Review a GitHub commit without requiring the gh CLI. + +```bash +hunk gh commit [--repo ] [-- ] +``` + +**Commit forms:** + +```bash +hunk gh commit abcdef1 # infer owner/repo from the local origin +hunk gh commit abcdef1 --repo modem-dev/hunk # use an explicit repository +hunk gh commit modem-dev/hunk@abcdef1 # name the repository and commit +hunk gh commit https://github.com/modem-dev/hunk/commit/abcdef1 +``` + +### `hunk gh compare` + +Review the changes between two GitHub branches, tags, or commits. + +```bash +hunk gh compare ... [--repo ] [-- ] +``` + +**Compare forms:** + +```bash +hunk gh compare main...feature # infer owner/repo from the local origin +hunk gh compare v0.20.0...v0.21.0 --repo modem-dev/hunk +``` + +Use three dots exactly. Pass Hunk patch options after `--`. + ## `hunk stash show` review a stash entry as a full Hunk changeset diff --git a/website/src/content/docs/docs/reference/config.md b/website/src/content/docs/docs/reference/config.md index 9f9b1e08b..574407fb5 100644 --- a/website/src/content/docs/docs/reference/config.md +++ b/website/src/content/docs/docs/reference/config.md @@ -73,11 +73,11 @@ With this config and no repository overrides: ### Pager settings -`[pager]` applies when a diff opens as a pager session, such as Git output displayed by `hunk pager`. It overrides the matching command table **within the same file**, not in ordinary interactive reviews. +`[pager]` applies when a diff opens as a pager session: Git output displayed by `hunk pager`, or any review launched with `--pager`. It overrides the matching command table **within the same file**, not in ordinary interactive reviews. `hunk pager` reviews match `[patch]`. ```toml [pager] -menu_bar = false +mode = "unified" wrap_lines = true ``` @@ -87,7 +87,7 @@ Try it for one Git invocation: git -c core.pager="hunk pager" diff ``` -Pager reviews start with the files pane closed, even if `sidebar = true`. Non-diff input goes to a plain-text pager instead; these view preferences do not configure that fallback. See [Git pager and difftool](/docs/workflows/git-pager-and-difftool/) for permanent setup. +Pager reviews start with the files pane closed, even if `sidebar = true`, and hide the menu bar unless a config layer sets `menu_bar`. Non-diff input goes to a plain-text pager instead; these view preferences do not configure that fallback. See [Git pager and difftool](/docs/workflows/git-pager-and-difftool/) for permanent setup. ## Resolution and scope diff --git a/website/src/content/docs/docs/start/install.md b/website/src/content/docs/docs/start/install.md index a474c2a78..6d46e5f21 100644 --- a/website/src/content/docs/docs/start/install.md +++ b/website/src/content/docs/docs/start/install.md @@ -3,7 +3,7 @@ title: Install description: Install Hunk with the install script, npm, Homebrew, mise, or Nix and verify the CLI. --- -Hunk runs on macOS, Linux, and Windows. The install script is the default method on macOS and Linux; npm or mise covers Windows. npm installs require Node.js 22 or newer, while the install script, Homebrew, mise, and Nix installs are self-contained binaries that do not require Node.js. Git is recommended for the most common review workflows. +Hunk publishes prebuilt binaries for macOS (arm64, x64), Linux (arm64, x64; glibc), and Windows (x64). On x86-64 it needs a CPU with SSE4.2 (2008 or newer). The install script is the default method on macOS and Linux; npm or mise covers Windows. npm installs require Node.js 22 or newer, while the install script, Homebrew, mise, and Nix installs are self-contained binaries that do not require Node.js. Git is recommended for the most common review workflows. ## Install script (default) @@ -18,13 +18,13 @@ When the release publishes `SHA256SUMS` and your machine has `sha256sum` or `sha The script accepts these settings: -| Setting | Effect | -| -------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| `HUNK_VERSION` | Install an exact release instead of the newest one. Also accepted as a positional argument. | -| `HUNK_INSTALL_DIR` | Install the binary into this directory instead of `~/.hunk/bin`. | -| `--no-modify-path` (or `HUNK_NO_MODIFY_PATH=1`) | Leave shell startup files alone. | -| `--force` (or `HUNK_ALLOW_CONFLICTING_INSTALLS=1`) | Install despite another Hunk on PATH or in a known version-manager directory. | -| `HUNK_DISABLE_ANALYTICS=1` or `DO_NOT_TRACK=1` | Keep release discovery direct to GitHub instead of using Hunk's aggregate release endpoint. | +| Setting | Effect | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `HUNK_VERSION` | Install an exact release instead of the newest one. Also accepted as a positional argument. | +| `HUNK_INSTALL_DIR` | Install into this directory instead of `~/.hunk`. The binary, a `skills/` folder, and `metadata.json` all go here, so use a dedicated directory rather than a shared one such as `/usr/local/bin`. | +| `--no-modify-path` (or `HUNK_NO_MODIFY_PATH=1`) | Leave shell startup files alone. | +| `--force` (or `HUNK_ALLOW_CONFLICTING_INSTALLS=1`) | Install despite another Hunk on PATH or in a known version-manager directory. | +| `HUNK_DISABLE_ANALYTICS=1` or `DO_NOT_TRACK=1` | Keep release discovery direct to GitHub instead of using Hunk's aggregate release endpoint. | By default, the installer refuses to create a second Hunk installation. It lists every competing path it finds, its version and PATH precedence, and the command that removes it. Remove those diff --git a/website/src/content/docs/docs/start/keyboard-and-mouse.md b/website/src/content/docs/docs/start/keyboard-and-mouse.md index 5b0b9b288..85d71fc5a 100644 --- a/website/src/content/docs/docs/start/keyboard-and-mouse.md +++ b/website/src/content/docs/docs/start/keyboard-and-mouse.md @@ -7,19 +7,20 @@ Press `?` at any time for Hunk's in-app shortcut reference. Menus and primary re ## Navigate the review -| Keys | Action | -| ------------------------- | ----------------------------------------------------- | -| `↑` / `↓`, `k` / `j` | Scroll one row | -| `Space` / `f`, `b` | Page down / up | -| `Shift+Space` | Page up | -| `d` / `u` | Half page down / up | -| `[` / `]` | Previous / next hunk | -| `,` / `.` | Previous / next file | -| `{` / `}` | Previous / next annotated hunk | -| `/` | Search diff content | -| `n` / `N` | Next / previous search match | -| `Home` / `End`, `g` / `G` | Start / end of review | -| `←` / `→` | Scroll unwrapped code; hold Shift for faster movement | +| Keys | Action | +| ------------------------------ | ----------------------------------------------------- | +| `↑` / `↓`, `k` / `j` | Move the current line | +| `Space` / `f`, `b` | Page down / up | +| `PageDown` / `PageUp` | Page down / up | +| `Shift+Space` | Page up | +| `d` / `u`, `Ctrl+D` / `Ctrl+U` | Half page down / up | +| `[` / `]` | Previous / next hunk | +| `,` / `.` | Previous / next file | +| `{` / `}` | Previous / next annotated hunk | +| `/` | Search diff content | +| `n` / `N` | Next / previous search match | +| `Home` / `End`, `g` / `G` | Start / end of review | +| `←` / `→` | Scroll unwrapped code; hold Shift for faster movement | Hunk navigation stays review-wide: hunk and file shortcuts move through the same multi-file stream shown in the main pane. @@ -48,19 +49,30 @@ Hunk navigation stays review-wide: hunk and file shortcuts move through the same Hunk may offer to save view changes on quit. Saving writes personal preferences globally unless the repository already has a `.hunk/config.toml`. -## Add a human note +## Select, copy, and annotate -Press `c` on the selected hunk or use a visible add-note affordance with the mouse. While editing, app shortcuts are suspended so normal text entry works. Save with the note editor's displayed action or cancel with Escape. +| Key | Action | +| --------------- | ---------------------------------------------- | +| `v` | Start a visual selection from the current line | +| `y` | Copy the selection | +| `c` | Add a note on the current line | +| `Ctrl+S` | Save the note you are editing | +| `E` / `R` / `D` | Edit, reply to, or delete the active note | +| `?` | Show every shortcut | + +Press `c` or use a visible add-note affordance with the mouse. While editing, app shortcuts are suspended so normal text entry works. Press `Ctrl+S` to save or Escape to cancel. + +Copying goes through the terminal clipboard protocol (OSC 52). If your terminal or multiplexer blocks it, Hunk reports that the clipboard is unavailable; see [terminal compatibility](/docs/help/compatibility/). ## Mouse behavior - Click a sidebar file to jump to it in the review stream. - Click menus and dialog actions instead of their key equivalents. - Use the wheel or scrollbar to move through the review; hold Shift while scrolling to move horizontally through unwrapped code. -- Select diff text for copy where the terminal supports it. +- Drag across diff rows to select them, then copy or comment from the selection's action bar. ## Remap the defaults -Every shortcut above is a named command you can move to different keys with a `[keybindings]` table in your user config. See [Keybindings](/docs/configure/keybindings/). The file filter ships without its own key (Tab and the File menu reach it); `"hunk.review.focusFilter" = "/"` puts it back on `/` and hands search the remaining keys. +Every shortcut above is a named command you can move to different keys with a `[keybindings]` table in your user config. See [Keybindings](/docs/configure/keybindings/). The file filter ships without its own key (Tab and the File menu reach it); `"hunk.review.focusFilter" = "/"` moves it to `/`, which leaves content search without a key until you bind `hunk.search.find` elsewhere. Terminal mouse protocols vary; see [terminal compatibility](/docs/help/compatibility/) if clicks or selection do not behave as expected. diff --git a/website/src/content/docs/docs/start/quick-start.md b/website/src/content/docs/docs/start/quick-start.md index 27a514334..864793437 100644 --- a/website/src/content/docs/docs/start/quick-start.md +++ b/website/src/content/docs/docs/start/quick-start.md @@ -20,7 +20,8 @@ Inside Hunk: 1. Press `]` to jump to the next hunk. 2. Press `.` to jump to the next file. 3. Press `1`, `2`, or `0` for unified, split, or responsive auto layout. -4. Press `q` to quit. +4. Press `?` to see every shortcut. +5. Press `q` to quit. ![Hunk showing a multi-file review stream with a file sidebar, split diff rows, and restrained terminal chrome](/docs/images/review-stream.webp) @@ -45,8 +46,14 @@ hunk show HEAD~1 -- src/ui README.md hunk diff --watch ``` -Hunk reloads file- and Git-backed input while preserving the review experience. Watch mode is continuous; press `q` when finished. +Hunk reloads the review as files change and keeps your place in it. Press `q` when finished. See [Watch mode](/docs/workflows/watch-mode/) for which inputs can be watched. ## Bring in an agent -Keep Hunk open, then in another terminal ask your coding agent to run `hunk skill path` and use the returned review skill. Continue with [Review with an agent](/docs/agents/review-with-an-agent/). +Install the review skill once: + +```bash +npx skills add modem-dev/hunk -g --skill hunk-review +``` + +Keep Hunk open and ask your coding agent to walk you through the changes. Agent notes start hidden; press `a` to show them. Continue with [Review with an agent](/docs/agents/review-with-an-agent/). diff --git a/website/src/content/docs/docs/workflows/files-and-patches.md b/website/src/content/docs/docs/workflows/files-and-patches.md index 9f41543b9..857352bd8 100644 --- a/website/src/content/docs/docs/workflows/files-and-patches.md +++ b/website/src/content/docs/docs/workflows/files-and-patches.md @@ -50,6 +50,6 @@ A file-backed patch can use watch mode. It remains tied to that file path. git diff --no-color | hunk patch - ``` -Use `-` to make stdin explicit. Stdin is a snapshot, so it cannot use `--watch`; write the patch to a file when you need continuous reloads. +Use `-` to make stdin explicit. Stdin is a snapshot, so it cannot use `--watch`; write the patch to a file when you need continuous reloads. A top-level `watch = true` in your config makes stdin reviews fail too; see [Watch mode](/docs/workflows/watch-mode/#turn-it-on-by-default). Patch-like input is parsed into the same file and hunk model as repository input. Non-diff text belongs in [pager mode](/docs/workflows/git-pager-and-difftool/), where Hunk can fall back to plain text. diff --git a/website/src/content/docs/docs/workflows/git-pager-and-difftool.md b/website/src/content/docs/docs/workflows/git-pager-and-difftool.md index ec9b180a2..054741f9e 100644 --- a/website/src/content/docs/docs/workflows/git-pager-and-difftool.md +++ b/website/src/content/docs/docs/workflows/git-pager-and-difftool.md @@ -22,7 +22,7 @@ git config --global alias.hshow '-c core.pager="hunk pager" show' ## Plain-text fallback -Output that is not a unified diff never opens the review UI; Hunk streams it to a plain-text pager instead. The pager command comes from `HUNK_TEXT_PAGER`, then `PAGER`, then falls back to `less -R`. A value that resolves back to `hunk` is ignored so Git can never recurse into `hunk pager` itself. +Output that is not a unified diff never opens the review UI; Hunk streams it to a plain-text pager instead. The pager command comes from `HUNK_TEXT_PAGER`, then `PAGER`, then falls back to `less -R` (`more` on Windows). A value that resolves back to `hunk` is ignored in favor of that fallback, so Git can never recurse into `hunk pager` itself. ## Configure Git difftool @@ -44,7 +44,15 @@ Difftool is pair-oriented because Git invokes the command once per file. Prefer ## Undo the integration +Remove only the settings you added: + ```bash -git config --global --unset core.pager +git config --global --unset core.pager # pager +git config --global --unset alias.hdiff # opt-in aliases +git config --global --unset alias.hshow +git config --global --unset diff.tool # difftool +git config --global --unset difftool.prompt git config --global --remove-section difftool.hunk ``` + +Leaving `diff.tool = hunk` behind after removing `difftool.hunk` makes `git difftool` warn about an unknown tool. diff --git a/website/src/content/docs/docs/workflows/jujutsu-and-sapling.md b/website/src/content/docs/docs/workflows/jujutsu-and-sapling.md index 08e6d3741..3bbf831e6 100644 --- a/website/src/content/docs/docs/workflows/jujutsu-and-sapling.md +++ b/website/src/content/docs/docs/workflows/jujutsu-and-sapling.md @@ -8,11 +8,14 @@ Hunk detects Git, Jujutsu (`jj`), and Sapling (`sl`) repositories. `hunk diff [t ## Jujutsu ```bash -hunk diff -hunk diff @- +hunk diff # the working-copy change +hunk diff @- # the changes inside @-, like `jj diff -r @-` +hunk diff @-- @ # compare two revisions hunk show @ ``` +In Jujutsu, `hunk diff ` shows the changes inside that revset. In Git and Sapling, `hunk diff ` compares your working copy against the target. Use `hunk diff ` when you want a comparison in every backend. + Configure Hunk as jj's pager and request Git-format diffs: ```toml @@ -26,8 +29,8 @@ Edit user settings with `jj config edit --user`. ## Sapling ```bash -hunk diff -hunk diff .^ +hunk diff # uncommitted changes +hunk diff .^ # working copy against the parent of `.` hunk show . ``` @@ -40,10 +43,12 @@ pager = hunk pager ## Override detection -Set the backend in Hunk config when a checkout is ambiguous: +In a colocated checkout (for example a jj repository with a `.git` directory), Hunk prefers Jujutsu, then Sapling, over Git. To choose a backend for one repository, set it in that repository's `.hunk/config.toml`: ```toml -vcs = "jj" # git, jj, or sl +vcs = "git" # git, jj, or sl ``` -Jujutsu and Sapling do not have Git's staging area, and stash review is Git-only. Their watch mode currently polls rather than observing repository files directly. +An explicit `vcs` always wins over detection, so avoid setting it in your user config: `vcs = "jj"` there would force Jujutsu in every Git repository. `vcs = "git"` in a colocated repository restores `--staged` and stash review. + +Jujutsu and Sapling do not have Git's staging area, and stash review is Git-only. Their watch mode polls about every two seconds rather than observing repository files directly. diff --git a/website/src/content/docs/docs/workflows/watch-mode.md b/website/src/content/docs/docs/workflows/watch-mode.md index 5a3225067..83c1dbbfc 100644 --- a/website/src/content/docs/docs/workflows/watch-mode.md +++ b/website/src/content/docs/docs/workflows/watch-mode.md @@ -23,7 +23,7 @@ hunk patch changes.patch --watch ## Know what can reload -Watch mode requires input Hunk can open again. Stdin-backed patches and stdin agent context cannot be watched: +Watch mode requires an interactive terminal and input Hunk can open again. Piped output, stdin-backed patches, and stdin agent context cannot be watched: ```bash # Snapshot only; --watch would fail @@ -32,6 +32,17 @@ some-command | hunk patch - Save changing output to a file or use a repository-backed command instead. +## Turn it on by default + +Put `watch = true` in the [command table](/docs/configure/configuration/#override-settings-for-a-command) for the reviews you want watched, not at the top level of your config: + +```toml +[vcs] +watch = true +``` + +A top-level `watch = true` also applies to `hunk pager` and `hunk patch -`, which read stdin and cannot be watched, so those commands fail. + ## Refresh manually Press `r` for a reloadable review when you need an immediate refresh without continuous watch mode. A live agent can also use `hunk session reload` to replace the session's entire input. diff --git a/website/src/content/docs/docs/workflows/working-trees-and-commits.md b/website/src/content/docs/docs/workflows/working-trees-and-commits.md index 55523f54e..4afc95691 100644 --- a/website/src/content/docs/docs/workflows/working-trees-and-commits.md +++ b/website/src/content/docs/docs/workflows/working-trees-and-commits.md @@ -27,11 +27,12 @@ hunk diff --cached ## Compare against a target ```bash -hunk diff main +hunk diff main # working tree against main hunk diff main...feature -- src/core +hunk diff v1.2.0 v1.3.0 # two revisions ``` -Arguments after `--` are pathspecs. Before `--`, the target is interpreted by the detected VCS. +Arguments after `--` are pathspecs. Before `--`, the target is interpreted by the detected VCS. In Jujutsu, a single target means something different; see [Jujutsu and Sapling](/docs/workflows/jujutsu-and-sapling/). ## Review a commit @@ -40,7 +41,7 @@ hunk show hunk show HEAD~2 -- README.md src/ui ``` -`show` defaults to the latest commit. The loaded files still form one review stream, so path filtering changes the input rather than changing navigation behavior. +`show` defaults to the latest commit in Git and Sapling, and to the working-copy change `@` in Jujutsu. The loaded files still form one review stream, so path filtering changes the input rather than changing navigation behavior. ## Review a stash diff --git a/website/tests/docs-smoke.spec.ts b/website/tests/docs-smoke.spec.ts index 4fe85acc7..d5227830c 100644 --- a/website/tests/docs-smoke.spec.ts +++ b/website/tests/docs-smoke.spec.ts @@ -83,7 +83,7 @@ test("key human and machine-readable routes load", async ({ page, request }) => "/docs/agents/review-with-an-agent/", "/docs/extend/file-previews/", "/docs/reference/cli/", - "/docs/help/deployment/", + "/docs/help/troubleshooting/", ]) { const response = await page.goto(route); expect(response?.ok(), route).toBe(true); From 46c6b0cfb285ca9f1dd48d56a597b82acedd7e0b Mon Sep 17 00:00:00 2001 From: Ben Vinegar <2153+benvinegar@users.noreply.github.com> Date: Wed, 14 Oct 2026 08:04:47 -0400 Subject: [PATCH 2/2] docs(extensions): say apiVersion is enforced at load time Co-Authored-By: Claude Opus 5.5 --- website/src/content/docs/docs/extend/extension-api.md | 2 +- website/src/content/docs/docs/extend/extensions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/src/content/docs/docs/extend/extension-api.md b/website/src/content/docs/docs/extend/extension-api.md index 4bd4c3f13..d05d0aee2 100644 --- a/website/src/content/docs/docs/extend/extension-api.md +++ b/website/src/content/docs/docs/extend/extension-api.md @@ -7,7 +7,7 @@ The extension factory receives one API object. Registration calls are only valid ## `hunk.apiVersion` -The API generation this Hunk speaks (currently `31`). Branch on it if you want one file to support several Hunk versions, or declare `"hunk": {"apiVersion": N}` in a folder extension's `package.json` so an older Hunk refuses to install it. +The API generation this Hunk speaks (currently `31`). Branch on it if you want one file to support several Hunk versions, or declare `"hunk": {"apiVersion": N}` in a folder extension's `package.json` so an older Hunk refuses to load it with a clear message. | Version | Adds | | ------- | --------------------------------------------------------------------------------------------------------- | diff --git a/website/src/content/docs/docs/extend/extensions.md b/website/src/content/docs/docs/extend/extensions.md index f1f9c819c..7e28fe942 100644 --- a/website/src/content/docs/docs/extend/extensions.md +++ b/website/src/content/docs/docs/extend/extensions.md @@ -98,7 +98,7 @@ Community extensions are listed at [hunk.dev/extensions](/extensions/), and the A publishable extension repository is the folder-extension layout at the repository root — `package.json` with a `hunk` field (or an `index.*` entry), code, README. To share one: -1. Fill in `package.json`'s `name`, `version`, and `description`, and declare `"hunk": {"apiVersion": N}` if you rely on recent API surface — an older Hunk then refuses the install cleanly instead of failing mid-load. +1. Fill in `package.json`'s `name`, `version`, and `description`, and declare `"hunk": {"apiVersion": N}` if you rely on recent API surface — an older Hunk then refuses to load it with a clear message instead of failing partway through. 2. Keep `dependencies` real: they are installed into the extension's own `node_modules` at install time. `react`, `@opentui/*`, and `hunkdiff/extension` come from the host at runtime and belong in `devDependencies`. 3. Tag releases so users can pin with `@v1.2.0`. 4. Push to any git host and add the **`hunk-extension`** GitHub topic, then open a pull request to list it on [hunk.dev/extensions](/extensions/).