Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/docs-accuracy-pass.md
Original file line number Diff line number Diff line change
@@ -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.
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
---
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:

- `/` is the marketing landing page.
- `/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.

Expand All @@ -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
Expand All @@ -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

Expand All @@ -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"'
```
Expand Down
14 changes: 10 additions & 4 deletions packages/hunk-gh/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -206,9 +212,9 @@ export function createGitHubPrExtension(

hunk.registerCliCommand(
{
name: "gh",
summary: "Review GitHub pull requests, commits, and comparisons",
usage: "<pr|commit|compare> <target> [--repo <owner/repo>]",
name: GITHUB_CLI_REFERENCE.name,
summary: GITHUB_CLI_REFERENCE.summary,
usage: GITHUB_CLI_REFERENCE.usage,
},
handler,
);
Expand Down
211 changes: 175 additions & 36 deletions packages/hunk-gh/src/help.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,188 @@
export const GITHUB_HELP = `Usage: hunk gh <command>
/**
* 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 <owner/repo>] [-- <patch-options...>]
Review a GitHub pull request; infer it from the current branch when omitted.
commit <commit> [--repo <owner/repo>] [-- <patch-options...>]
Review a GitHub commit without requiring the gh CLI.
compare <base>...<head> [--repo <owner/repo>] [-- <patch-options...>]
Review the changes between two GitHub refs.
/** One accepted argument spelling for a GitHub subcommand. */
export interface GitHubCliForm {
/** Arguments after `hunk gh <command>`; 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 <command> --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 <owner/repo>] [-- <patch-options...>]
/** 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: "<pr|commit|compare> <target> [--repo <owner/repo>]",
options: [
{
flag: "--repo <owner/repo>",
description: "use an explicit repository instead of the local origin",
},
{
flag: "-- <patch-options...>",
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: "<commit>",
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: "<base>...<head>",
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 <commit> [--repo <owner/repo>] [-- <patch-options...>]
/** 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 <base>...<head> [--repo <owner/repo>] [-- <patch-options...>]
/** Render the `hunk gh --help` overview. */
function renderGitHubHelp(reference: GitHubCliReference) {
return [
"Usage: hunk gh <command>",
"",
"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 <command> --help` for command-specific help.",
"",
].join("\n");
}

Review the changes between two GitHub branches, tags, or commits.
/** Render `hunk gh <command> --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");
21 changes: 9 additions & 12 deletions packages/hunk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
Loading
Loading