Skip to main content
@obversa/surface-diff opens a Git diff for review in a browser and returns a decision with comments anchored to lines. Its obversa-review command writes the result as framed JSON on stdout, with diagnostics on stderr, so a script or an agent reads a person’s review without a screen. The guide is Review a diff in a host pane; this page is the package.

Install

Included in @obversa/obversa. You need Git.

Quickstart

Parse a unified diff and read the changed lines with their numbers:
examples/surface-diff.ts
parseUnifiedDiff returns the files, each with its hunks and lines, each line typed add, del or context with its old and new numbers. Run it with npx tsx surface-diff.ts; it opens no browser:
Output

Review a diff

Run the command from the repository you want to review:
Terminal
The default reviews working-tree changes, --staged the index, --range the named Git range. --no-open prints the review URL instead of placing the page. The result frame starts with <<<REVIEW_RESULT_V1>>> and ends with <<<END_REVIEW_RESULT_V1>>>; --app <name> changes the app name and so the markers. Exit 0 means the reviewer returned, a decision that requests changes included; 1 means the session didn’t complete or the command failed; 2 means the arguments were invalid.

Call it from code

reviewDiff(options) is the command’s function: it computes the diff, builds the page, launches a surface and returns the framed result. diffArgs builds the Git arguments for a mode and computeDiff runs them and returns the text, buildIndexHtml builds the page, and buildSurfaceRequest the request the session opens with, copying surfaceId and gateId so a consumer routes the result. validateAnnotation and normalizeResult check a submission against the contract, and appendToThread(annotation, entry) adds a reply to an annotation’s thread without changing the one you pass in. The bounds are constants: MAX_ANNOTATIONS (500), MAX_BODY (4000 characters), MAX_THREAD (100), with DECISIONS, SIDES, AUTHOR_KINDS, FAMILIES and TRANSPORTS naming the allowed values.

Options

mode and range

mode is worktree (default), staged or range; range needs range: 'A..B'. Naming two modes is a usage error.

The rest

Errors

  • TypeError from reviewDiff when launchSurface or clientKitSource is missing.
  • A conflicted diff is refused whole with a message naming the path.
  • A repository that changes during capture stops after three attempts with The repository changed while the review was being captured; retry when it is quiet.
  • A submission whose decision disagrees with its annotations is refused and the page stays open.

API

  • reviewDiff(options): the review as a function. Call it from code.
  • parseUnifiedDiff, computeDiff, diffArgs: the diff. Quickstart.
  • buildIndexHtml, buildSurfaceRequest, isSurfaceRequest, isGateBinding, isTransport, ASSETS_DIR: the page and its request.
  • validateAnnotation, normalizeResult, appendToThread, anchorKey, buildAnchorSet, outputAnchors: the result contract.
  • Constants: MAX_ANNOTATIONS, MAX_BODY, MAX_THREAD, DECISIONS, SIDES, AUTHOR_KINDS, FAMILIES, TRANSPORTS.
  • @obversa/surface-diff/bin: the command file, for a host to resolve and run under Node. @obversa/surface-diff/testing: highlightModel, contextModel, navModel, createHighlightRegistry, registryToCss, listTrackedFiles.

Next steps