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/calm-reviews-describe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hunkdiff": minor
---

Let extension CLI commands attach validated provider-neutral review metadata when they delegate a patch into Hunk, expose it to extension panes and live-session snapshots, and show delegated change-request identity in a concise built-in top pane.
4 changes: 4 additions & 0 deletions docs/agent-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ hunk session review --repo . --json
- `get --repo .` confirms which live session matches the current repo
- `review --json` returns the loaded file and hunk structure without dumping the full raw patch

When a CLI extension delegated the review, JSON list, context, and review outputs may also include a
bounded `review` descriptor with provider, title, URL, and kind-specific identity. It is descriptive
context only and does not add remote provider or reload capabilities.

Only add `--include-patch` when an agent truly needs raw unified diff text:

```bash
Expand Down
4 changes: 3 additions & 1 deletion docs/browser-review-rebuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ untouched (rung 4).
`reviewProtocol.ts`, broker `wire.ts` validation, broker review mirror, `reviewResourceCache`
(bounded in-flight budget). Patch reconstruction for `hunk session review --include-patch` uses
bounded-parallel loads from day one. Valuable without any web UI: agents get chunked,
digest-verified, memory-bounded resource access. The wire vocabulary is derived from
digest-verified, memory-bounded resource access. Optional extension-delegated review identity rides
in bounded registration metadata and projects into list, context, and review snapshots; it never
enters `ReviewDocumentV1` or creates a remote reload/provider capability. The wire vocabulary is derived from
`ReviewIntent` (B12) and carries `expandedLineProof` (B10) and actor identity (G2) from its
first version so the browser never needs a schema break.

Expand Down
25 changes: 17 additions & 8 deletions docs/extension-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ object and registry collection (`src/extensions/runExtension.ts`):
by the app composition root (`app/vcsCatalog.ts`) and loaded synchronously
before config resolution, so backends exist without making core import the
extension host. `default/ui/index.ts` is deliberately not part of that list:
it synchronously loads the bundled files pane through `runExtensionFactory`
only where the app resolves UI panes.
it synchronously loads the bundled files and delegated review-info panes through
`runExtensionFactory` only where the app resolves UI panes.

Git and the built-in file navigation use the public `registerVcsAdapter` and
`registerPane` paths. The external [Hunk Lens](https://github.com/modem-dev/hunk-lens)
Git, built-in file navigation, and delegated change-request identity use the public
`registerVcsAdapter` and `registerPane` paths. The external [Hunk Lens](https://github.com/modem-dev/hunk-lens)
extension exercises current-line pane paint through that same public contract.

Bundled extensions are implicitly trusted and stay loaded under
Expand Down Expand Up @@ -67,8 +67,15 @@ the raw subtree and runs through leased process I/O. An exit result retires the
registry before returning an exit plan; a one-time built-in delegation reparses
through the ordinary planner. Delegated reviews reconcile the already loaded
candidate/config prefix and hand the same registry to `AppBootstrap`, so factories
are not rerun merely for the handoff. Headless delegation retires before executing
the built-in plan. Terminal probing occurs only after the handler releases I/O.
are not rerun merely for the handoff. A delegated patch may attach a validated, bounded
provider-neutral review descriptor. Startup carries it beside the input on `AppBootstrap`; it does
not enter the changeset transform pipeline or `ReviewDocumentV1`. The host preserves it only while
the same file-backed patch identity reloads and clears it when a reload selects another input.
Session registration projects the same optional bounded descriptor into list, selected-context, and
review exports through strict app-wire validation. It remains registration metadata rather than a
`ReviewDocumentV1` field and does not imply any provider or remote-reload capability. Headless
delegation retires before executing the built-in plan. Terminal probing occurs only after
the handler releases I/O.

## Host-served runtime modules

Expand All @@ -91,8 +98,10 @@ stream coordinates. Pane registrations may opt into a body-axis `fraction`;
the planner resolves it to an integer target before applying bounds and lets a
session-local divider drag override that automatic size.

`src/ui/components/panes/ExtensionPane.tsx` mounts panes with guarded actions and
failure containment. `DiffPane` exposes optional current-line paint — the row
`src/ui/components/panes/ExtensionPane.tsx` mounts panes with guarded actions,
immutable delegated review metadata, and failure containment. The fixed three-row
`hunk:review-info` top pane uses one border row above two metadata rows and is available only for
delegated change requests, so ordinary reviews spend no geometry on it. `DiffPane` exposes optional current-line paint — the row
painter plus the public `{ side, line }` address — without publishing Pierre
rows, plans, cursor keys, or caches. Deprecated sidebar APIs
normalize into this same registry and layout path.
Expand Down
55 changes: 48 additions & 7 deletions docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,9 @@ new instances and run that shutdown/startup pair around the replacement.

### `hunk.apiVersion`

The API generation this Hunk speaks (currently `16`). Branch on it if you want
one file to support several Hunk versions. Version 16 adds pane-wide
The API generation this Hunk speaks (currently `17`). Branch on it if you want
one file to support several Hunk versions. 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 `rangeEndpoints`
to two-revision VCS diff requests; version 13 added saved-note parent identities and
Expand Down Expand Up @@ -312,7 +313,23 @@ hunk.registerCliCommand(
}

await ctx.stderr.write("Preparing review…\n");
return { kind: "delegate", argv: ["diff", "--agent-context", "notes.json"] };
return {
kind: "delegate",
argv: ["patch", "review.diff"],
review: {
kind: "change-request",
provider: "GitHub",
title: "Add structured review metadata",
url: "https://github.com/acme/project/pull/123",
id: "#123",
repository: "acme/project",
author: "octocat",
base: "main",
head: "review-metadata",
state: "open",
draft: false,
},
};
},
);
```
Expand All @@ -324,6 +341,22 @@ to access networks, processes, services, and files. Return `{ kind: "exit",
code? }` with a status from 0 through 255, or delegate exactly once to a
built-in Hunk command.

A delegated built-in `patch` command may include a provider-neutral `review` descriptor. Its
`kind` is `change-request`, `commit`, or `comparison`; each exact shape combines bounded display
strings with an optional credential-free HTTPS URL. Hunk rejects unknown fields, control
characters, invalid types, unsafe URLs, fields over their byte limits, and descriptors over 4 KiB,
then copies and freezes the accepted value. `provider` and change-request `id` allow 256 bytes;
`repository`, `author`, `base`, `head`, and `revision` allow 512; `title` and `url` allow 2 KiB.
Change requests may also carry `state` (`open`, `closed`, or `merged`) and boolean `draft`. Exit results and delegation to any built-in other than
`patch` cannot carry review metadata. An ordinary `hunk patch` has no descriptor.

The descriptor describes the review source rather than its diff contents: it stays on the app
bootstrap and does not enter changeset transforms or `ReviewDocumentV1`. Refreshing the same
file-backed patch preserves it, including watch and manual refresh; an explicit reload to a
different patch path or input kind clears it. Live-session list, context, and review JSON snapshots
project the same optional descriptor from registration metadata; it remains outside the semantic
review document and grants no remote reload or provider capability.

Delegation cannot target another extension command or change extension bootstrap
flags. Do not write stdout or read stdin before delegating; use stderr for
progress. Reading stdin is an exit-only workflow because even a pending read can
Expand Down Expand Up @@ -353,10 +386,11 @@ Both fields are collapsed to one sanitized line, so an extension cannot forge
host output with newlines or escape sequences.

The dependency-free [`github-pr` example](../examples/extensions/github-pr/)
is a complete network workflow built on this contract. It fetches a GitHub PR
diff without the `gh` CLI, writes a temporary patch with restrictive POSIX
modes (and inherited temporary-directory ACLs on Windows), delegates to the
built-in `patch` command, and removes the patch on extension shutdown. Run it
is a complete network workflow built on this contract. It fetches bounded GitHub PR metadata and
the diff without the `gh` CLI, attaches a `change-request` descriptor so the bundled review-info
pane shows the provider facts above the diff, writes a temporary patch with restrictive POSIX modes
(and inherited temporary-directory ACLs on Windows), delegates to the built-in `patch` command, and
removes the patch on extension shutdown. Run it
from this checkout with:

```bash
Expand Down Expand Up @@ -746,6 +780,12 @@ hide it conditionally. One pane may replace each named target; the first
registration owns that slot and later claims are skipped with a warning.
`replaces` may also name another pane by its fully qualified
`"<extensionId>:<paneId>"` key, and Hunk follows those replacement chains.
Both `available(context)` and the mounted component receive `review`: immutable
metadata supplied by a delegated patch command, or `null` for ordinary reviews.
The bundled `hunk:review-info` top pane uses this to show change-request identity
without taking any rows when no change-request descriptor exists. Pane extensions that read
`review` should declare `"hunk": { "apiVersion": 17 }` in their manifest so older Hunk versions
refuse them cleanly instead of mounting with an incomplete prop contract.

`onActivate()` observes a primary mouse press anywhere in the pane's content,
including content nested in a `<scrollbox>`. Use it to focus an extension-owned
Expand Down Expand Up @@ -781,6 +821,7 @@ The component receives fresh props as the app changes:

| Prop | What it is |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `review` | immutable delegated review metadata (`change-request`, `commit`, or `comparison`), or `null` for ordinary reviews |
| `files` | the visible reviewed files, review-stream order, filtered, frozen views (each carries `changeType`, `statsTruncated`, and `hunks` summaries beside the usual file fields) |
| `selectedFileId` | the selected file, or `null` |
| `selectedHunkIndex` | the selected hunk within that file, or `null` |
Expand Down
6 changes: 4 additions & 2 deletions examples/extensions/github-pr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Review a GitHub pull request in Hunk with a generic extension-provided CLI comma
hunk gh 123
```

The extension fetches the PR diff directly from GitHub's API, writes it to a temporary patch, and delegates once to Hunk's built-in `patch` command. It has no npm dependencies and does not require the `gh` CLI.
The extension fetches the PR metadata and diff directly from GitHub's API, writes the diff to a temporary patch, and delegates once to Hunk's built-in `patch` command. Hunk shows the provided title, author, state, repository, and base/head refs in its bundled review-info pane above the diff. The extension has no npm dependencies and does not require the `gh` CLI.

## Try it from this checkout

Expand Down Expand Up @@ -45,7 +45,9 @@ Public repositories work anonymously within GitHub's API rate limits. For privat
1. `GH_TOKEN`
2. `GITHUB_TOKEN` when `GH_TOKEN` is absent

The token needs access to the target repository and may require organization SSO authorization. The extension only accepts `github.com` PR URLs and only sends credentials to the fixed `https://api.github.com` endpoint. Redirects are refused, response bodies are not copied into errors, and fetched diffs are bounded to 64 MiB.
The token needs access to the target repository and may require organization SSO authorization. The extension only accepts `github.com` PR URLs and only sends credentials to the fixed `https://api.github.com` endpoint. Redirects are refused, response bodies are not copied into errors, fetched metadata is bounded to 256 KiB, and fetched diffs are bounded to 64 MiB.

Metadata and diff are separate GitHub requests. Hunk intentionally shows only stable provider facts and ref names; it does not claim that the metadata attests the exact bytes returned by the following diff request.

PR patches can contain private source. On POSIX systems, the extension creates a mode-`0700` temporary directory and a mode-`0600` patch. Windows does not enforce those POSIX mode bits; the directory and patch inherit the ACL of the user's system temporary directory. The extension retains the patch while the delegated review can reload, then removes it during extension shutdown. Abrupt process termination may leave cleanup to the operating system's temporary-file policy.

Expand Down
Loading
Loading