hunk

repository·main·Indexed 27 days ago

https://github.com/modem-dev/hunk

A review-first, desktop-inspired terminal diff viewer designed for agent-authored changesets. Hunk features an interactive TUI with multi-file navigation, AI annotations, and support for version control systems including Git, Jujutsu, and Sapling. The project includes the hunkdiff package (v0.17.0) and a suite of session broker libraries (@hunk/session-broker, @hunk/session-broker-bun, @hunk/session-broker-node, and @hunk/session-broker-core) for managing live sessions and snapshots.

Tokens
47.8K
Snippets
120
Records
274
Agent score
94%

What's inside hunk

  1. Understand the watch-mode performance improvements

    main

    The watch-mode implementation in Hunk has been updated (via PR #531) to replace the legacy 250 ms Git-backed polling loop with a more efficient system using event hints, an authoritative Git signature, and a 10-second safety check.

    Key performance benefits observed in benchmarks:

    • Reduced Idle CPU: Cumulative idle main-process CPU usage was reduced by 1.8–6.4×.
    • Reduced Git Invocations: Git calls were reduced by 35–36×.
    • Startup Latency: Mean launch-to-File View latency remains comparable to the base, with a slight increase of approximately 21 to 61 ms.

    Platform-specific behaviors:

    • macOS: Uses native recursive observation, reaching readiness quickly without per-directory registration.
    • Linux: Uses Git-pruned Chokidar, limiting traversal to relevant roots to avoid hitting inotify watch limits (e.g., avoiding the ~24,000 registrations that approach the default 61,504 limit).
    • Windows: Intended policy is native recursion to avoid Chokidar's per-directory resource model.
  2. Understand watch-mode performance metrics

    main

    The watch-mode benchmark report evaluates the performance of the candidate revision against the base revision across different platforms (macOS, Linux, Windows ARM64) and repository sizes (little repo vs. big repo). Key metrics include:

    • Startup Time: Measured from launch to File View. The candidate typically adds 24–61 ms to the mean startup time.
    • Observer-Ready Metric: Measured from probe-process launch. It includes plan (Git-aware plan derivation) and construction (backend construction to ready).
    • Idle CPU and RSS: Measures mean cumulative CPU delta and Resident Set Size (RSS) at 60s and 120s intervals.
    • Git Invocations: Total count of Git command families (e.g., diff, rev-parse, status, ls-files) used during a 120-second window.
    • Refresh Latency: Median and p95 latency for detecting Tracked write, Atomic rename, and Relevant untracked creation. The candidate uses a 200 ms quiet debounce to reduce continuous work, which may increase latency compared to the base polling method.
    • Break-even Point: The approximate time required for the CPU savings of the candidate to offset its initial startup cost.
  3. Understand backend strategies for watch-mode

    main

    The hunk watch-mode implementation uses different backend strategies depending on the operating system to balance performance and system resource limits:

    • macOS: Uses native-recursive. It registers the worktree as a single recursive native target and filters event paths against Git-derived ignored roots. This is highly efficient for large directory counts.
    • Linux: Uses chokidar-portable (Git-pruned Chokidar). Instead of watching all directories, it traverses only Git-relevant roots. This is preferred over native fs.watch recursion on Linux because native recursion can exhaust the max_user_watches system limit (e.g., inotify registrations) in multi-session environments.
    • Windows: Uses native-recursive to match Windows' recursive fs.watch capability and avoid per-directory Chokidar registrations. Note that full performance measurements for Windows are currently pending.
  4. Compare polling vs evented watch mode performance

    main

    The hunk watch implementation has transitioned from a 250ms polling mechanism to an evented Chokidar-based hybrid observer.

    Key Performance Differences:

    • Idle Overhead: The evented mode significantly reduces Git subprocess invocations and CPU consumption during idle periods. While the old polling version averaged ~13 Git calls per second, the evented version averages ~0.35 calls per second (primarily via a 10s safety poll).
    • Startup Time: Startup performance remains identical (~1.7s) between both versions, as both are dominated by the initial git diff load cost.
    • Refresh Latency: The evented mode provides sub-second passive refresh latency for various file operations:
      • Simple tracked-file write: ~340ms
      • Atomic temp-file + rename: ~418ms
      • Large 241-line diff update: ~406ms
      • New untracked file creation: ~682ms
  5. Understand the Hunk extension system design

    main

    Hunk is exploring a JavaScript/TypeScript-based extension system modeled after the pi terminal agent. The design goal is to allow users to adapt Hunk to their specific workflows (editors, forges, VCS, or agents) without forking the core.

    Key characteristics of the proposed system include:

    • Single-file extensions: An extension is a single TS file or folder exporting a default factory function.
    • No manifest/build step: The runtime executes TypeScript directly.
    • Capability-granting API: Extensions interact with Hunk through a single ExtensionAPI or ExtensionContext object rather than global hooks.
    • Interception-based events: Events like tool_call or input allow extensions to block, rewrite, or consume actions, enabling behavioral changes rather than just UI decoration.
    • Auto-discovery: Extensions can be loaded from global (~/.pi/agent/extensions/*.ts) or project-local (.pi/extensions/*.ts) locations, or via settings using npm: or git: protocols.
  6. Build HunkDiffFile inputs

    main

    Components accept HunkDiffFileInput objects. You can create these using several methods depending on your source data:

    From before/after contents

    Use parseDiffFromFile to generate metadata from two versions of a file, then wrap it in createHunkDiffFile.

    import { createHunkDiffFile, parseDiffFromFile } from "hunkdiff/opentui";
    
    const file = createHunkDiffFile({
      id: "example",
      metadata: parseDiffFromFile(beforeFile, afterFile, { context: 3 }, true),
      path: "example.ts",
      language: "typescript",
    });

    From unified diff text

    Use createHunkDiffFilesFromPatch for a quick way to generate multiple files from a single patch string.

    import { createHunkDiffFilesFromPatch } from "hunkdiff/opentui";
    
    const files = createHunkDiffFilesFromPatch(patchText, "example:patch");
  7. Review current work with hunk diff

    main

    To review the current state of your repository, including both tracked changes and untracked files, run hunk diff from within the repository.

    If you only want to see tracked changes, use the --exclude-untracked flag.

    While inside the Hunk interface, use these keybindings for navigation and layout:

    • ] : Jump to the next hunk.
    • . : Jump to the next file.
    • 1, 2, or 0 : Switch between split, stack, or responsive auto layout.
    • q : Quit Hunk.
  8. Use Experimental Rich Markup (STML)

    main

    STML (Small Terminal Markup Language) allows rendering HTML-like markup (boxes, gauges, lists, etc.) in the terminal.

    Requirements:

    1. The session must have been launched with the --experimental flag.
    2. hunk session context --json must list stml in experimentalFeatures.

    Usage:

    • Use the --markup <stml> flag in comment add or the markup field in comment apply payloads.
    • Use hunk markup guide to learn syntax and width rules.
    • Use hunk markup render - --width <width> to preview markup, where <width> is provided by noteMarkupWidth in the session context.
  9. Integrate Hunk as a Git pager

    main

    To automatically open Hunk when running git diff or git show, set Hunk as your Git pager.

    Option 1: Global configuration

    git config --global core.pager "hunk pager"

    Option 2: Git config file

    [core]
        pager = hunk pager

    Option 3: Opt-in aliases (keeps default pager for standard commands)

    git config --global alias.hdiff "-c core.pager=\"hunk pager\" diff"
    git config --global alias.hshow "-c core.pager=\"hunk pager\" show"

    Note: Untracked files are only auto-included in Hunk's own hunk diff sessions. When using the Git pager, Git determines the patch contents, so untracked files will not appear.

  10. Understand Hunk Extension Tiers and Loading

    main

    Hunk distinguishes between two types of extensions, both using the same per-extension API and registry:

    • User extensions: Loaded at interactive-app startup. These are subject to discovery, trust gating, and can be disabled using the --no-extensions flag.
    • Bundled extensions: Compiled into the binary and located in src/extensions/default/. These are implicitly trusted and remain loaded even with --no-extensions.

    Extension ID Rules: An extension ID is the namespace for its commands (<extensionId>.<commandId>), sidebar views (<extensionId>:<viewId>), and configuration ([extension.<id>]).

    • IDs must match the regex: /^[A-Za-z0-9][A-Za-z0-9_-]*$/.
    • Reserved IDs (like hunk or bundled VCS backends) cannot be used.
    • Dots or colons are not allowed in the ID itself to prevent ambiguity in composed IDs.