# Changelog

## [0.3.2] — 2026-08-04

### Fixed

- **pi-goal detection now tool-based** — added `hasGoalTools(pi)` that checks `getActiveTools()` for pi-goal's registered tools (`goal_complete`, `goal_blocked`). Timing-independent (tools register at load time), unlike the previous session-entry-based detection which could false-negative when pi-goal hadn't written entries yet. The `on_goal` trigger gate now checks `hasGoalTools(pi) || ensureGoalsExtension(ctx)` — if tools are detected, the entry check (and its warning) is skipped entirely. Resolves the persistent "requires pi-goal extension" false warning when @narumitw/pi-goal is installed.

## [0.3.1] — 2026-08-04

### Fixed

- **pi-goal compatibility** — goal detection (`hasGoalsExtension`, `hasActiveGoal`, `checkGoalEvents`) now recognizes customTypes from different pi-goal packages: `"pi-goal-state"` (@capyup/pi-goal), `"goal-state"` (@narumitw/pi-goal), and `"goals-state"` (legacy). Previously only `"pi-goal-state"` was detected, causing a false "requires pi-goal extension" warning when @narumitw/pi-goal was installed.

## [0.3.0] — 2026-08-04

### Changed

- **`/commit-init` now handles existing configs** — if `.pi-committer.toml` already exists, it asks whether to overwrite (via `ctx.ui.select`). Choose "Overwrite" → runs the interactive selectors (commit_mode, trigger_mode) and replaces the config with your new selections. Choose "Cancel" → keeps the existing config. Previously it refused and pointed to `/commit-config`. The `initConfig` helper now returns `{ wrote, existed, configPath }` and accepts an `{ overwrite }` option.

### Removed

- **`/commit-config` command** — config is reloaded automatically by `/commit-init` after creating/updating. For manual edits to `.pi-committer.toml`, restart pi (or re-run `/commit-init` to regenerate). Simplifies the command surface: one config command (`/commit-init`) instead of two.

## [0.2.1] — 2026-08-04

### Changed

- **`/commit-init` is now interactive** — prompts you to choose `commit_mode` (bypass / review) and `trigger_mode` (on_goal / manual / agent_sensible / after_tool) via `ctx.ui.select`, then writes `.pi-committer.toml` with your selections. Previously wrote a fixed bypass default without asking. Still idempotent (won't overwrite an existing config).

### Added

- Exported `configTemplate({ commitMode, triggerMode })` in `config.ts` — generates the TOML template with the chosen modes (powers the interactive `/commit-init`).

## [0.2.0] — 2026-08-04

### Added

- **`/commit-init` command** — scaffolds a default `.pi-committer.toml` in the current project if one does not exist. Sensible defaults (`enabled`, `commit_mode = "bypass"`, `trigger_mode = "on_goal"`, `staged_commits`) with inline comments explaining each key. Idempotent: never overwrites an existing config; if present, points you to `/commit-config` to reload. After creating, reloads the config immediately so it takes effect without a restart. Removes the need to hand-write the TOML.
- Exported `initConfig(dir)` helper in `config.ts` (powers `/commit-init`; unit-tested).

## [0.1.1] — 2026-08-03

Patch release: post-0.1.0 fixes and decoupling. No API or config changes (`.pi-committer.toml` contract and the `commit_mode` / per-call `mode` API are unchanged).

### Fixed

- **notify level**: `ctx.ui.notify(..., "success")` → `"info"` (4 sites). The pi-coding-agent `notify` type only declares `info` / `warning` / `error`; "success" was an unsupported level inherited from upstream pi-committer. Notifications now use a valid level.
- **`Model` SDK version drift**: the SDK (0.74.0) no longer exports `Model`. Replaced `import type { Model }` with a local `type AgentModel = ExtensionContext["model"]` alias (the type of `ctx.model`), clearing the type error with no runtime change.
- **`performCommit` error handling**: both branches (bypass `execSync`, review `writeFileSync`) now wrap their operation in try/catch and throw a clear context message (`Failed to commit (bypass mode): …` / `Failed to write review-mode message file: …`). The bypass `execSync` call is byte-identical (T18/T33 unaffected). Removed the dead `error?` field from `CommitEmissionResult`.

### Changed

- **Decoupled from upstream pi-committer** — gentle-committer is now an independent project on its own version line (no longer tracks tmonk/pi-committer):
  - Removed the `upstream` git remote.
  - Log/notification prefix `[pi-committer]` → `[gentle-committer]` (47 sites).
  - README/package.json reframed from "fork of" to "independent project, originally derived from".

### Removed

- Dead code: unused `findJitiRegister` wrapper (index.ts); unused `sendResult` helper and unused `ctx` / `subagentThinkingLevel` params (async-commit-worker.ts); dead `stagedFiles` binding and unused `runtimeSignal` param (index.ts).

## [0.1.0] — 2026-08-03

First release of **gentle-committer**, a fork of [pi-committer](https://github.com/tmonk/pi-committer) v0.12.8 adapted for the gentle-pi ecosystem. Adds a commit-mode switch and work-unit commit discipline while keeping the `bypass` path byte-identical to upstream.

### Added

- **`commit_mode` switch** (`bypass` default | `review`). `bypass` commits directly (byte-identical to pi-committer); `review` writes `.git/gentle_committer_pending_msg` and returns a `git commit -F` directive so gentle-ai's gate reviews the commit before it lands — zero coupling to gentle-ai internals.
- **Per-call `mode` override** on the `commit_changes` tool: `commit_changes({mode:"review"})` for a feature, `{mode:"bypass"}` for a quick fix — no config edit.
- **`prompt_rules_path`** + `commit-rules/work-unit-commits.md`: work-unit commit discipline appended to the grouping / single-message prompts (byte-identical to upstream when unset).
- **`isGentleAiLoaded`** feature detection + `resolveEffectiveCommitMode` R9 fallback (review requested without gentle-ai → warn + bypass).
- Shared `commit-emission.ts` (`performCommit`) and `prompt-builders.ts` (`buildGroupingPrompt` / `buildSingleMessagePrompt` / `loadCommitRules`) modules — no circular imports.
- Review-mode guardrails: async worker disabled, single-commit forced, automatic triggers (`turn_end`/`tool_result`/`goal_event`) suppressed, stale message file self-cleaning (R8).

### Changed

- Test runner switched from `--experimental-strip-types` to `tsx` (this node build's strip-types is unreliable).
- Worker loader: non-`node_modules` paths resolve `tsx` via `_findTsxLoaderForPath`; `node_modules` paths still use `jiti`.

### Dependencies

- `typebox` declared (was imported but undeclared upstream).
- `jiti` declared as a runtime dependency (worker `.ts` loader).
- `tsx` added as a dev dependency (test runner + dev worker loader).
- pi core packages (`@earendil-works/pi-tui`, `@earendil-works/pi-coding-agent`, `typebox`) moved to `peerDependencies` (`*`) — provided by the pi runtime, not bundled.

## [0.12.8] — 2026-06-10

### Fixed

- **Commit message contamination guard:** Added `isValidDiffContent()`, `isValidDiffStat()`,
  and `isValidCommitMessage()` validation functions that prevent non-diff output (e.g.
  from unrelated shell commands like `df`) from leaking into commit messages. Validation
  is integrated at every entry point: `getDiffContent`, `stageAll`, the grouped commit loop,
  and async worker param ingestion. Invalid content is rejected with a diagnostic log
  and the message falls back to a safe deterministic format.

### Changed

- **File list removed from commit message bodies:** Both deterministic and subagent-generated
  commit messages no longer include a bullet list of changed files. The body now contains
  only the description summary line. Subagent single-message prompt updated to remove the
  "bullet list of changed files" instruction; grouping prompt's `Files:` line is preserved
  as a structured group-assignment format (parsed and stripped before committing).

## [0.12.7] — 2026-06-07

### Changed

- **Changelog completed for v0.12.4, v0.12.5, and v0.12.6:** Full changelog entries
  documenting all changes from the 0.12.3→0.12.6 releases are now included.

## [0.12.6] — 2026-06-07

### Added

- **`subagentMessageMinFiles` config option (default 3):** Decouples the commit-message
  subagent call from the grouping subagent call. When the number of changed files is
  below this threshold (1-2 files), the subagent is skipped entirely and the fully
  deterministic fallback is used — no LLM call at all. For 3-14 files, a single commit
  is made with a subagent-generated message but without grouping. At 15+ files, both
  grouping and message subagent are used. Configurable via TOML key
  `subagent_message_min_files`.
- **SDK extension runtime caching:** `createExtensionRuntime()` is now called once and
  cached across all subagent sessions (`getCachedRuntime()`), avoiding redundant
  runtime creation on every subagent call.

### Changed

- **`subagentGroupingMinFiles` default raised from 4 to 15:** Empirically determined
  via the expanded benchmark suite. Benchmark data shows non-subagent overhead is
  ~100-200ms regardless of file count, while a grouping subagent call costs ~35s.
  Most routine commits (< 15 files) now skip the grouping subagent entirely.
- **`commitStaged` and `doSingleCommit` accept pre-computed diff stat/content:**
  Pre-computed `git diff --cached --stat` and `diffContent` are passed from the
  caller (`tryCommit`), avoiding redundant `execSync` calls.
- **Async commit worker mirrored:** Both `subagentGroupingMinFiles` and
  `subagentMessageMinFiles` are passed through `CommitWorkerParams`. The worker's
  widget phase, skipSubagent logic, and progress tracking all match the sync path.

### Performance

- **1-2 files: ~269× faster** (~35s subagent → ~130ms deterministic).
- **3-14 files: ~2× faster** (subagent called once instead of twice).
- **Full pipeline at scale: ~8.5× faster** from batch git operations.

## [0.12.5] — 2026-06-06

### Changed

- **`skipSubagent` logic now gates on file count:** The commit-message subagent is
  skipped entirely when the file count is below `subagentMessageMinFiles` (default 3).
  For 1-2 file changes, the fully deterministic fallback is used — no model call.
- **Widget phase string corrected:** The widget now displays "committing" instead of
  "analyzing" when the subagent message phase is active but grouping is skipped.
- **README updated:** Documented `subagent_message_min_files` and updated default
  of `subagent_grouping_min_files` from 4 to 15.

## [0.12.4] — 2026-06-05

### Added

- **Diagnostics at every subagent fallback decision point:** 12 DIAG points across
  both `index.ts` (5 points) and `async-commit-worker.ts` (7 points) log the exact
  reason when the subagent is skipped or falls through — SDK load failure, empty
  SDK result, model unavailable, threshold gate, or grouping produces no groups.
  Diagnostics use `ctx.ui.notify` (sync path) or `console.error` with IPC progress
  (async worker) and never crash.
- **Smart deterministic scope via `findCommonAncestor()`:** When all changed files
  share a common directory ancestor, the commit scope is set to that ancestor.
  When files span unrelated directory trees, no scope is emitted at all.
- **Smart deterministic description via `summarizeChanges()`:** Extracts meaningful
  keywords from file names (strips extensions, skips boilerplate like `__init__`,
  `conftest`), converts snake_case to readable words, caps at 6 terms, and appends
  "and tests" when test files are present. No token-expensive diff analysis.
- **Commit body format:** Description summary line followed by a blank line, then
  a full file list with change stats — replaces the old `"Changes:"`-only body.

### Changed

- **Subagent prompts tightened** in all 4 locations (sync single-commit + grouping,
  async single-commit + grouping):
  - "NEVER comma-join multiple scopes" — stops the subagent from producing
    `chore(experiments/exposure,experiments/exposure/src,...)`.
  - "be specific: 'add regression pipeline and tests', not 'update 27 modules'".
  - "max 72 chars" enforced in prompt.
  - Body format rules: "First line is the description. Then a blank line. Then the
    full file list (one per line)."
- **SDK loading hardened in async worker:** `tryLoadSDK()` now attempts ESM dynamic
  `import()` first, then CJS `require()` as fallback — addresses the case where
  dynamic import fails in forked/jiti child processes.
- **Smart scope replaces comma-joined directories:** The old behavior concatenated
  all changed directories with commas; now `findCommonAncestor()` produces clean
  scopes like `experiments/exposure` instead of
  `experiments/exposure,experiments/exposure/src,.../exposure/tests`.
- **Smart description replaces "update N modules":** The old fallback always said
  "update N modules"; now `summarizeChanges()` produces descriptions like
  "add regression pipeline and tests" from file-name keywords.

### Performance

- Deterministic fallback is still O(1) — no LLM calls, just string analysis.
  `findCommonAncestor()` and `summarizeChanges()` operate in a single pass over
  the file list with negligible overhead.

## [0.12.3] — 2026-05-31

### Changed

- **Unstageable file warnings routed to tool result instead of notification popups:**
  Per-file `ctx.ui.notify()` calls in `batchStageFilesForGroup` callbacks are replaced with
  a single concise summary notification after all groups are processed
  (e.g. "3 file(s) could not be staged"). The individual warnings still
  flow into `details.warnings` for the agent to see in the tool result — the fix removes
  only the visually disruptive per-file floating popups.

### Added

- **Summary notification for unstageable files:** One `ctx.ui.notify()` fires at the end of
  group processing if any files were unstageable ("X file(s) could not be staged") or if
  entire groups were skipped ("Y group(s) skipped (all files unstageable)").
- **Integration tests for warning routing:** 3 tests verify `batchStageFilesForGroup` collects
  warnings via callbacks without calling `ctx.ui.notify()`.

## [0.12.2] — 2026-05-30

### Changed

- **Batch staging pipeline optimized:** `batchStageFilesForGroup` in both `index.ts` and
  `async-commit-worker.ts` now uses batch size 5000 (10× the previous 500), reducing
  subprocess calls from 20 to 2-3 for repos with ~10k changed files. The pre-classify
  (`existsSync`) + `git rm --cached` split was evaluated but the simpler approach of
  just using `git add --ignore-errors` (which natively handles deleted tracked files)
  with a larger batch proved both faster and cleaner.
- **`doSingleCommit` in async-commit-worker.ts** also uses batch size 5000.

### Performance

- **1k staging loop:** 81ms → 19ms (**4.3× faster**)
- **10k full pipeline:** 693ms → 142ms (**4.9× faster**)
- **10k staging loop:** ~360ms → ~57ms (**6.3× faster**)
- **Total vs original per-file baseline:** **814× speedup** (15.5s → 19ms at 1k files)

## [0.12.1] — 2026-05-28

### Added

- **Warning summary for unstageable files in `commit_changes` tool response:**
  When files fail to stage during a commit (e.g. matching `.gitignore`, permission errors,
  or subagent-hallucinated paths), warnings are now collected and surfaced in the IPC
  response text the agent sees after the tool runs — not just in the ephemeral widget.
  The format is: `Skipped N unstageable file(s) (file1, file2, ...)` with optional
  `; N group(s) entirely skipped`. Works in both sync and async commit paths.
  (dedicated by file path).

### Fixed

- **Async worker crash on error paths:** A `let` variable declared inside a `try` block
  was inaccessible in the corresponding `catch` block due to block scoping, causing a
  `ReferenceError` that silently killed the worker process without sending an IPC result
  message. Moved the warnings variable outside the `try` block.
- **`unstageExcludedFiles` defensive guards:** Handles `undefined` `files` and
  `excludePatterns` parameters without crashing.

## [0.12.0] — 2026-05-27

### Fixed

- **Worker crash under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING):**
  Node.js 22's built-in `--experimental-strip-types` refuses to process `.ts` files
  under `node_modules`, causing the async commit worker to exit with code 1 immediately.
  The fix detects when the worker path is under `node_modules` and switches to jiti's ESM
  loader (`--import jiti/lib/jiti-register.mjs`) instead, which has no such restriction.
  The `resolveWorkerExecArgv(workerPath?)` helper encapsulates the decision logic.

### Added

- **Full test coverage for the node_modules fix:**
  - 7 unit tests for `resolveWorkerExecArgv`/`_findJitiRegisterForPath` covering all path
    resolution strategies and edge cases.
  - 4 crash-scenario integration tests reproducing the original `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`
    error, verifying jiti succeeds where strip-types fails, and a regression guard.
  - 1 E2E test creating a temp `node_modules` structure with the real worker symlinked in,
    forking with jiti execArgv, and verifying the worker loads and responds.
  - 4 performance benchmarks confirming sub-millisecond overhead for both code paths.

### Changed

- **`forkWorker()` in test infrastructure** now uses `resolveWorkerExecArgv(workerPath)`
  instead of hardcoded `["--experimental-strip-types"]`, matching the production fix.

## [0.11.0] — 2026-05-27

### Fixed

### Fixed

- **Worker always exits with code 0:** Removed the last two `exit code 1` paths in
  `async-commit-worker.ts`. The worker timeout (5-minute watchdog) now calls
  `sendResultAndExit(..., 0)` instead of bare `process.exit(1)`, and the main handler's
  catch block uses exit code 0. All errors (timeout, commit failure, cancellation, worker crash)
  are communicated exclusively through the IPC result message's `error` field, never via the
  process exit code. This eliminates the IPC race where the parent could see
  "Subprocess exited with code 1" before the IPC result message arrived.

### Added

- **Exported worker functions for direct unit testing:** All pure git helpers (`git`, `gitCwd`,
  `getHeadHash`, `unstageAll`, `getDiffContent`, `getChangedFiles`, `isGitignored`,
  `filterGitignoredFiles`, `unstageExcludedFiles`), commit message generators
  (`deterministicCommitMessage`, `parseCommitGroups`), IPC helpers (`sendResultAndExit`), and
  the main commit pipeline functions (`doSingleCommit`, `doGroupedCommits`) are now exported
  from `async-commit-worker.ts`. The pipeline functions accept an optional `ipc` parameter with
  `onProgress` and `onCommit` callbacks, making them testable without a forked IPC environment.
- **40 new comprehensive edge-case tests** in `tests/worker-edge.test.ts` covering:
  - Worker exported git helper functions
  - Deterministic commit message generation
  - Parse commit groups (including hallucinated file filtering)
  - `sendResultAndExit` IPC message delivery
  - Abort/cancel at multiple checkpoints (SIGTERM timing races)
  - Git failure modes (pre-commit hook rejection, empty diffs, missing files, minChanges filter)
  - IPC result guarantees (always exit 0, result message structure, commit log field validation)
  - Large diff handling (getDiffContent with file fallback)
  - Binary file pattern handling (filterGitignoredFiles, unstageExcludedFiles)
  - Widget phase transitions (analyzing, done with error, idle as empty)
  - Exclude pattern glob matching (prefix, suffix, wildcard)

### Changed

- `npm test` now runs both `tests/unit.test.ts` and `tests/worker-edge.test.ts`.

## [0.10.0] — 2026-05-26

### Fixed

- **Async worker crash with no subagent model configured:** Fixed a parameter swap bug in
  `async-commit-worker.ts` where `params.subagentThinkingLevel` (a string like `"off"`) was
  passed in the `onProgress` callback position (position #6) in both `generateCommitGroups()`
  and `generateCommitMessage()` call sites. When the SDK emitted a `message_update` or
  `message_end` event, the callback tried to invoke a string as a function, throwing `TypeError`
  and crashing the worker with "Subprocess exited with code 1". The callback and
  `subagentThinkingLevel` are now in the correct order.

### Added

- **Defensive guards on `onProgress`:** Both `generateCommitMessage` and `generateCommitGroups`
  now use `typeof onProgress === "function"` before calling the progress callback, preventing
  any future type mismatch from crashing the worker.
- **Model-availability shortcut:** Both functions check `!subagentModel` alongside the existing
  `tryLoadSDK()` guard. When no subagent model is configured (common on new installs), the
  worker skips SDK-dependent calls and falls through to deterministic commit messages
  immediately, avoiding unnecessary timeouts.
- **IPC integration tests:** 3 new tests that fork the real worker process and verify
  successful commits with deterministic fallback when no model is configured (grouped mode,
  single-commit mode, staged_commits=false mode).

## [0.9.0] — 2026-05-26

### Added

- **`subagentGroupingMinFiles` config option (default 4):** When `stagedCommits=true` and the
  number of changed files is below this threshold, the expensive subagent grouping step is
  skipped entirely. The commit still uses the subagent for the commit message, but avoids
  the ~90s grouping overhead for small change sets.
- **`subagentThinkingLevel` config option (default `"off"`):** Sets the thinking level on all
  commit subagent sessions to `"off"` by default (accepts: `off`, `minimal`, `low`, `medium`,
  `high`, `xhigh`). The pi SDK defaults to `"medium"` thinking, which generates unnecessary
  reasoning tokens for trivial commit messages.

### Fixed

- **Widget error display for failed grouped commits:** When a grouped commit's `git add` step
  failed (e.g., subagent hallucinated file names that don't exist), the worker no longer crashes
  with "Subprocess exited with code 1". The `git add` loop is wrapped in try/catch, and worker
  IPC uses a callback-based `sendResultAndExit` to avoid a race condition where `process.exit()`
  fired before IPC messages were delivered. In the sync path, grouped commit failures now set
  `progress.error` with a descriptive message instead of showing misleading "done 0 commits".

### Changed

- **Background commit notification simplified:** Removed agent-only instructions ("do not wait
  for it", "do not check git status or git log. Continue with your task") from user-facing
  notification text.

## [0.8.1] — 2026-05-26

### Added

- **Full test coverage for async widget visibility:** 12 new tests verifying the
  committer widget is shown and updated during async commits:
  - 5 widget rendering tests (committing phase, done phase with log, done phase
    with error, cancelled phase, cancelled with partial log)
  - 5 integration tests using fork mock (widget shown on start, full IPC lifecycle,
    error from IPC, error from crash, error from unexpected exit)
  - 1 e2e test (async commit with many files runs in background, verifies commits)

## [0.8.0] — 2026-05-26

### Fixed

- **IPC race condition in async commit worker:** Fixed a bug where `process.exit(N)` was called
  before queued IPC messages (`sendResult`/`sendCommit`) could be delivered to the parent
  process, causing the widget to show "✗ Subprocess exited with code 1" and "0 commits" even
  when the commit had succeeded. The worker now uses a `safeExit()` function that gives the
  event loop 100ms to flush pending IPC messages before terminating.
- **Parent-side fallback:** When the async worker exits with a non-zero code before sending a
  result IPC message, the parent now waits up to 500ms for a delayed result before showing the
  subprocess error (defense-in-depth for the IPC race).

### Added

- **`safeExit()` helper** in `async-commit-worker.ts` — replaces direct `process.exit(N)` after
  IPC sends with a pattern that yields the event loop before terminating.
- **`_getCommitterProgress()` export** in `index.ts` — exposes widget progress state for test
  assertions.
- **2 new unit tests** (126 total): IPC race fix test verifies result-before-exit scenario;
  parent fallback test verifies exit-before-result scenario.

## [0.7.0] — 2026-05-26

### Added

- **Assertive async commit response:** When an async commit is launched, the tool now returns
  "Commit running in background for N file(s). The commit completes automatically — do not
  check git status or git log. Continue with your task." — telling the agent definitively
  not to probe git.
- **Prompt guideline against git probing:** Added guideline to `commit_changes` telling the
  agent "do NOT run git commands (git status, git log, git diff) to check on the commit."

### Changed

- **Notification text aligned:** The session-level notification now reads "Commit running in
  background" with "do not wait for it" guidance instead of "Progress visible in widget".

## [0.6.0] — 2026-05-21

### Added

- **Benchmark suite:** 34 benchmark tests covering all major phases (diff, check-ignore, reset,
  add, diff stat parsing, commit, full pipeline with `stagedCommits` on/off, subagent fallback,
  group parsing) at 3 file sizes (5, 30, 100 files) with per-operation wall-clock timing and
  TAP-formatted summary output. Baseline/optimized result files stored in-repo.
- **Profiling instrumentation:** `_getLastSubagentCallMs()` and `_getLastGroupGenCallMs()` timing
  hooks record real wall-clock duration of subagent message generation and group generation calls
  during actual usage.

### Changed

- **`filterGitignoredFiles`** — batched `git check-ignore --stdin` using a single `execSync` call
  with all paths piped via stdin, replacing `O(n)` per-file `git check-ignore -q` calls.
  **Speedup: ~167×** (2505ms → 15ms for 100 files).
- **`unstageExcludedFiles`** — single `git reset HEAD -- <file1> <file2> ...` call with all
  excluded files, replacing `O(n)` per-file `git reset HEAD` calls, with per-file fallback
  on failure. Applied to both `index.ts` and `async-commit-worker.ts`. **Speedup: ~5×**.
- **`getDiffContent`** — fast path via `execSync` pipe with 10MB `maxBuffer`, replacing
  temp-dir + `--output=<file>` I/O for the common case. Falls back to temp-dir only on
  `ENOBUFS`. Applied to both `index.ts` and `async-commit-worker.ts`.
- **`asyncThreshold` default lowered from 10 to 5** — benchmark-informed decision: at 5 files,
  the optimized pipeline completes in ~90ms vs ~140ms baseline, making async subprocess overhead
  worthwhile at this threshold.

## [0.5.0] — 2026-05-20

### Added

- **Async (background subprocess) commits:** When the number of changed files reaches a
  configurable threshold (`async_threshold`, default 10), `/commit` and `commit_changes`
  now fork the full commit pipeline into a detached child process and return immediately.
  The conversation can continue while the commit runs in the background. A progress widget
  shows real-time status (preparing → analyzing → committing → done). Pressing Esc sends
  SIGTERM to the subprocess and cleans up. The worker has a 5-minute timeout safeguard.

### Changed

- **Bumped version to 0.5.0** — minor release with async commit support.

## [0.4.0] — 2026-05-20

### Fixed

- **ENOBUFS (pipe buffer overflow) on large git diffs:** Replaced pipe-based `execSync` capture
  of `git diff --cached` with file-based `--output=<file>` via a new `getDiffContent()` helper.
  The file-based approach writes the diff directly to a temp file, bypassing the OS pipe buffer
  entirely. This fixes `spawnSync /bin/sh ENOBUFS` errors on macOS when committing repos with
  very large working trees (e.g. monorepos with thousands of changes).
- **Missing `maxBuffer` on git commands:** Added `maxBuffer: 10MB` to all `git diff --cached --stat`
  (3 call sites) and `git status --porcelain` (2 call sites) calls to prevent Node.js buffer
  overflow on large outputs.

### Changed

- **Bumped version to 0.4.0** — minor release with the ENOBUFS fix.

## [0.3.0] — 2026-05-20

### Changed

- **Default `defer_to_goal_audit` is now `false`** (was `true`). `commit_changes` always proceeds
  immediately unless the user explicitly opts in to deferral with `defer_to_goal_audit = true`.
  This fixes a common scenario where the session cwd differs from the project root, causing
  the config file to be missed and `config.deferToGoalAudit` to silently stay at the old default
  of `true`.

### Fixed

- **Defer-to-goal-audit bypass:** Refactored defer check into `shouldDeferToGoalAudit()` function.
  The `commit_changes` tool now correctly honours `defer_to_goal_audit = false` in
  `.pi-committer.toml` — when set to `false`, commits proceed immediately even with an active
  pi-goal goal instead of deferring.
- **Duplicate `try {`:** Fixed syntax artifact in `hasActiveGoal()`.

### Added

- **shouldDeferToGoalAudit(cfg, ctx):** New exported function encapsulating the defer decision
  logic for testability and clarity.
- **Comprehensive defer-logic tests:** 11 new unit tests (107 total) covering every combination
  of `deferToGoalAudit`, trigger mode, goal presence, and goal status, including config file
  round-trips, syntax-error fallbacks, and boolean-string edge cases.
- **`.pi-committer.toml` in pystata-x:** Created project config with `defer_to_goal_audit = false`.

## [0.2.1] — 2026-05-19

### Fixed

- **Gitignore crash:** `git add -- "<file>"` no longer crashes when a changed file matches `.gitignore`. Gitignored files are detected via `git check-ignore -q` and silently skipped before the `git add` call.
- **README typo:** Corrected `completed` → `complete` in trigger mode documentation.

### Added

- **`filterGitignoredFiles(dir, files)`:** New exported function that returns only non-gitignored files from a list, using `git check-ignore -q`.
- **Graceful handling of all-gitignored groups:** Commit groups where every file is gitignored are silently skipped. If all files across all groups are gitignored, the flow returns 0 commits with an informational message.

### Testing

- **21 new unit tests** (96 total, all passing): comprehensive coverage for `filterGitignoredFiles`, `stageAll`, `isDirtyRepo`, and gitignore commit integration (including mid-flow `.gitignore` changes, nested `.gitignore`, and all-ignored edge cases).
- **3 new E2E tests** covering mixed gitignored/non-gitignored files, all-files-gitignored (no crash), and nested `.gitignore` scenarios.

## [0.2.0] — 2026-05-19

### Added

- Subagent-based commit message generation with `pi-subagents`.
- Deterministic commit message fallback for headless/offline environments.
- Auto-commit on goal completion via `pi-goal` lifecycle hooks.
- Abort/cancellation support in the commit workflow.
- Flexible commit grouping via `.pi-committer.toml` or `.pi-committer.json` config.
- Exclusion patterns (`exclude_patterns`) to skip unwanted files.
- Config loading with directory-walking discovery.
- Comprehensive unit and E2E test suite.

[0.5.0]: https://github.com/tmonk/pi-committer/releases/tag/v0.5.0
[0.4.0]: https://github.com/tmonk/pi-committer/releases/tag/v0.4.0
[0.3.0]: https://github.com/tmonk/pi-committer/releases/tag/v0.3.0
[0.2.1]: https://github.com/tmonk/pi-committer/releases/tag/v0.2.1
[0.2.0]: https://github.com/tmonk/pi-committer/releases/tag/v0.2.0
