@senad-d/branchme 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # Changelog
2
2
 
3
- ## 0.3.2 - Unreleased
3
+ ## 0.3.3 - Unreleased
4
4
 
5
+ - `update_from_base` now lists every conflicting path in its text result (bounded, with an omitted count) and states whether the merge was aborted or kept.
6
+ - Added optional `update_from_base.keepConflicts` (default `false`, unchanged behavior). When `true` and the merge conflicts, the merge is verified and left in progress (status `conflict_kept`, MERGE_HEAD equal to the captured base commit, HEAD unchanged, conflicted paths unmerged) instead of being aborted; if the conflict paths cannot be captured, the verified abort runs and the error says `keepConflicts` could not be honored.
7
+ - Added `conclude_merge` with `action: "conclude" | "abort"` for the in-progress merge on the current checkout. `conclude` checks unmerged working-tree paths and staged blobs for default/custom-sized conflict markers (including diff3, CRLF, and binary content, naming the paths), stages exactly the currently unmerged paths, checks the candidate index again with `git --literal-pathspecs add --`, commits with `git commit --no-edit`, and verifies MERGE_HEAD is gone and HEAD is an exact two-parent merge of the previous HEAD and MERGE_HEAD; unrelated unstaged changes stay unstaged. Git commits the full index, including previously staged entries, so unrelated edits must not be staged during a merge. Multi-head merges are refused before mutation; lost commit responses report uncertain postconditions requiring inspection. `abort` runs `git merge --abort` with the same restoration verification as the automatic abort. Both refuse when no merge is in progress.
5
8
  - Allowed missing worktree parent directories, resolved through the nearest existing directory ancestor without writing during validation. Non-directory and dangling-symlink ancestors remain rejected; Git creates missing parents only after all preflight checks pass.
6
9
  - Parallelized independent read-only worktree-path, Git-operation-marker, and upstream-configuration lookups while preserving result order and validation.
7
10
  - Updated the Pi development packages to `1.0.2`, replacing vulnerable transitive `brace-expansion` and `undici` versions with `5.0.12` and `8.10.2`. Updated the Git-context smoke verifier to use host-resolved imports and Pi's transcript APIs instead of the old nested dependency layout.
@@ -22,7 +25,7 @@
22
25
  - Added optional `remote`/`branch` parameters to `fetch_branch` for a targeted fetch of one exact remote branch into its remote-tracking ref (default remote `origin`; `remote` requires `branch`) without touching local branches, the working tree, or the current branch's upstream configuration; the no-argument behavior is unchanged.
23
26
  - Added an optional read-only `baseRef` parameter to `create_worktree` for `branchMode: "new"`, starting the new branch from an exact local branch, remote-tracking ref, or full commit regardless of the current checkout's branch, dirt, or staleness; the default from-`HEAD` behavior is unchanged.
24
27
  - Implemented the `branchme` informational slash command with help aliases.
25
- - Registered nineteen strict BranchMe tools, including `list_branches`, `track_branch`, `update_from_base`, and `pull_request_status`, alongside: `init_repository`, `branch_status`, `change_branch`, `fetch_branch`, `pull_branch`, `rebase_branch`, `integrate_branch`, `retire_branch`, `land_branch`, `create_branch`, `push_branch`, `pull_request`, `list_worktrees`, `create_worktree`, and `remove_worktree`; merge-continuation tools are intentionally absent.
28
+ - Registered twenty strict BranchMe tools, including `list_branches`, `track_branch`, `update_from_base`, `conclude_merge`, and `pull_request_status`, alongside: `init_repository`, `branch_status`, `change_branch`, `fetch_branch`, `pull_branch`, `rebase_branch`, `integrate_branch`, `retire_branch`, `land_branch`, `create_branch`, `push_branch`, `pull_request`, `list_worktrees`, `create_worktree`, and `remove_worktree`; generic `continue_merge`/`abort_merge` tools are intentionally absent.
26
29
  - Added argv-style git helpers for repository status, branch validation/creation/switching, clean-worktree preflight, upstream detection, configured-upstream fetch, fast-forward-only current-branch pull, current-branch rebase with automatic abort on failure, verified local branch integration, current-branch push/publish, and bounded NUL-delimited worktree discovery.
27
30
  - Added `integrate_branch` for exact local source-to-target integration from a clean already-current target control worktree. It rejects branch-specific target merge options that could alter the fixed policy and supports already-integrated, fast-forward, verified normal merge-commit, and automatically aborted/restored conflict outcomes with before/after ref and ancestry proofs, while exposing no fetch, push, reset, merge-message, or continuation controls.
28
31
  - Added `retire_branch` for explicit deletion of one exact unoccupied local branch ref after full expected-`HEAD` matching and captured target-ancestry verification. It uses `git update-ref --no-deref -d` with an expected-old-value lease, requires explicit force authorization for unmerged history, leaves branch configuration and remote/remote-tracking refs untouched, and reports uncertain postconditions for manual inspection without reset rollback.
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  ---
18
18
 
19
- BranchMe is a Pi extension for safe Git repository, branch, and worktree workflow automation. Before each agent run, it appends a bounded, read-only snapshot of the current Git repository to the system prompt. It also adds an informational `/branchme` command and nineteen agent-callable tools that initialize a repository, refresh state, manage, integrate, and retire local branches, inspect/create/remove linked worktrees, push the current branch, and create GitHub pull requests.
19
+ BranchMe is a Pi extension for safe Git repository, branch, and worktree workflow automation. Before each agent run, it appends a bounded, read-only snapshot of the current Git repository to the system prompt. It also adds an informational `/branchme` command and twenty agent-callable tools that initialize a repository, refresh state, manage, integrate, and retire local branches, conclude a kept merge, inspect/create/remove linked worktrees, push the current branch, and create GitHub pull requests.
20
20
 
21
21
  <table align="center">
22
22
  <tr>
@@ -33,7 +33,7 @@ BranchMe is a Pi extension for safe Git repository, branch, and worktree workflo
33
33
  - **Repository-scoped:** `init_repository` can initialize only pi's exact current working directory when it is not already inside a repository. Other Git and GitHub operations resolve from the checkout where pi is running. Linked worktree directories may be outside that checkout, but must be verified members of the same repository.
34
34
  - **Explicit history rewrites:** `rebase_branch` runs only when explicitly requested, requires a clean current branch with an upstream, disables autostash and multi-ref updates, and automatically attempts to abort on failure.
35
35
  - **Verified worktree handoff:** `create_worktree` verifies path, branch, `HEAD`, and cleanliness before returning an absolute `handoff.cwd`; starting another Pi session or subagent there remains the caller's responsibility.
36
- - **Commit-safe:** context collection is read-only, and BranchMe never stages files, creates user-authored commits, accepts or generates commit messages, force-pushes, resets, or edits files directly. Explicit `integrate_branch` and `update_from_base` calls may let Git create a standard merge commit for divergent histories.
36
+ - **Commit-safe:** context collection is read-only, and BranchMe never creates user-authored commits, accepts or generates commit messages, force-pushes, resets, or edits files directly. Explicit `integrate_branch` and `update_from_base` calls may let Git create a standard merge commit for divergent histories; `conclude_merge` stages only the formerly unmerged paths of a kept merge and commits them with Git's prepared merge message.
37
37
  - **Strict tools:** tool schemas reject undocumented properties such as `stash`, `discard`, `owner`, `repo`, or `path`; `baseRef` is supported only by `create_worktree` in new-branch mode; the only force decision is the required boolean on `retire_branch`, and every tool accepts only its documented fields.
38
38
  - **PR-ready:** create GitHub pull requests from existing local branches after verifying the `headBranch` matches GitHub and the base is visible. PR fields can stay explicit, or configured autofill can derive omitted fields from the current branch, default branch, and commit subjects.
39
39
 
@@ -91,7 +91,7 @@ A typical BranchMe flow is:
91
91
  4. Update it from its configured upstream with `pull_branch`, which requires a clean worktree and uses fast-forward-only semantics.
92
92
  5. Create from the updated `HEAD` with `create_branch`.
93
93
  6. Make edits and commit outside BranchMe.
94
- 7. While working, use `update_from_base({ baseBranch: "main" })` to fetch and merge the remote base into the clean feature branch without rewriting published history or changing its upstream.
94
+ 7. While working, use `update_from_base({ baseBranch: "main" })` to fetch and merge the remote base into the clean feature branch without rewriting published history or changing its upstream. A conflict is aborted and its paths listed; with `keepConflicts: true` the conflicted merge stays in progress so a file-editing step can remove the markers, after which `conclude_merge({ action: "conclude" })` commits it or `conclude_merge({ action: "abort" })` restores the branch.
95
95
  8. Push the current branch with `push_branch`.
96
96
  9. After `push_branch` completes and GitHub can see the branches, create or reuse a matching open pull request with `pull_request`.
97
97
  10. Inspect the PR with `pull_request_status({ number: 123 })`. Merge it on GitHub outside BranchMe, then use `land_branch` from the primary checkout. Supply `pullRequestNumber: 123` for squash/rebase merge evidence.
@@ -245,7 +245,8 @@ Commands are informational only. BranchMe actions are performed by agent-callabl
245
245
  | --- | --- | --- |
246
246
  | `list_branches` | `{}` | Read up to 200 local and cached remote-tracking branches, commits, current/upstream state, ahead/behind counts, symbolic refs, and worktree occupancy. No fetch. Raw refs are limited to 128 KiB and text to 4,000 characters; omitted entries are reported. Names/paths are display metadata, not guaranteed executable identities. |
247
247
  | `track_branch` | `{ "branchName": string, "remote"?: string, "remoteBranch"?: string }` | Fetch one exact remote branch and create/check out a new local tracking branch. Remote defaults to `origin`; remoteBranch defaults to branchName. Requires a clean idle checkout; verifies HEAD and upstream. |
248
- | `update_from_base` | `{ "baseBranch": string, "remote"?: string }` | Fetch the exact remote base (remote defaults to `origin`) and merge its captured commit into the clean current feature. Uses verified normal-merge policy and automatic conflict abort; never rebases, pushes, or changes upstream configuration. |
248
+ | `update_from_base` | `{ "baseBranch": string, "remote"?: string, "keepConflicts"?: boolean }` | Fetch the exact remote base (remote defaults to `origin`) and merge its captured commit into the clean current feature. Uses verified normal-merge policy; a `conflict` is automatically aborted with its paths listed, or with `keepConflicts: true` reported as `conflict_kept` and left in progress (MERGE_HEAD set, conflicted paths unmerged) for `conclude_merge`. Never rebases, pushes, or changes upstream configuration. |
249
+ | `conclude_merge` | `{ "action": "conclude" \| "abort" }` | Finish or abandon the in-progress merge on the current checkout. `conclude` refuses default/custom-sized `<<<<<<<`, `|||||||`, `=======`, or `>>>>>>>` marker lines in unmerged working-tree paths and staged blobs (including CRLF and binary content, naming the paths), stages only the currently unmerged paths, rechecks the candidate index, commits with `git commit --no-edit`, and verifies MERGE_HEAD is gone and HEAD is a two-parent merge of the previous HEAD and MERGE_HEAD; unrelated unstaged changes stay unstaged. `abort` runs `git merge --abort` and verifies the restored HEAD, cleared operation state, and clean tree. Refuses when no merge is in progress. |
249
250
  | `pull_request_status` | `{ "number"?: integer, "headBranch"?: string }` | Read exact same-repository PR lifecycle and head/base/merge identities. Number and headBranch are mutually exclusive. Without number, returns the most recently updated PR for headBranch or the current branch. Does not certify CI checks or review approvals. |
250
251
  | `init_repository` | `{ "initialBranch"?: string }` | Initializes only pi's exact current working directory as a verified non-bare Git repository with an unborn branch (default `main`). Rejects existing or nested repositories and accepts no path, bare, template, shared, remote, commit, or project-file controls. |
251
252
  | `branch_status` | `{ "ancestry"?: { "sourceBranch": string, "targetBranch": string } }` | Explicitly refreshes the same bounded context used at agent start. An optional strict ancestry query captures both exact HEADs and reports whether the source commit is an ancestor of the target commit; each endpoint may be an exact local branch or a remote-tracking ref such as `origin/main` (a local branch of the same name takes precedence). It is read-only and never checks out or resets remote-tracking refs; automatic Git context does not run ancestry queries. |
@@ -263,7 +264,9 @@ Commands are informational only. BranchMe actions are performed by agent-callabl
263
264
  | `push_branch` | `{}` | Pushes the current branch to its configured upstream remote with an explicit `HEAD:<upstream-branch-ref>` refspec, or publishes it with `git push --set-upstream origin <currentBranch>` when no upstream exists. |
264
265
  | `pull_request` | `{ "headBranch"?: string, "baseBranch"?: string, "title"?: string, "body"?: string, "draft"?: boolean }` | Preflights GitHub branch visibility and verifies the GitHub `headBranch` commit matches the local branch, then reuses an exact matching open PR or creates one in the resolved current repository. Existing PR metadata is preserved. Omitted fields require `BRANCHME_PR_AUTOFILL=true`; branch refs must be distinct, exist locally, and cannot use `owner:branch`. |
265
266
 
266
- All schemas reject additional properties. `init_repository` accepts only optional `initialBranch`, never a path or repository-mode controls. `change_branch` never accepts `baseRef`, `force`, `stash`, `discard`, `create`, `owner`, `repo`, or path inputs. `fetch_branch` accepts only the optional `remote` and `branch` pair for a targeted remote-tracking refresh (`remote` requires `branch`) and never accepts refspec, tags, prune, or force controls. `pull_branch` and `rebase_branch` have strict empty schemas and never accept a branch, remote, refspec, force, autostash, or arbitrary rebase target. `integrate_branch` requires exactly `sourceBranch` and `targetBranch`; it accepts no repository, path, remote, strategy, message, squash, signing, commit, continuation, abort, force, fetch, push, deletion, or worktree controls. `create_worktree` requires exactly `worktreePath`, `branchName`, and `branchMode`, plus an optional read-only `baseRef` start point for `branchMode: "new"`; `remove_worktree` requires `worktreePath` and accepts optional boolean `deleteIgnored`. No worktree tool accepts force, move, prune, repair, lock, unlock, detached, orphan, remote, or refspec controls. `retire_branch` requires exactly `branchName`, a full 40- or 64-hex-character `expectedHead`, a distinct local `targetBranch`, and the boolean `force` decision; it accepts no repository, path, remote, refspec, pattern, branch list, prune, remote-delete, worktree-removal, or inferred-target control. `pull_request` never accepts `owner`, `repo`, or owner-prefixed branch refs; BranchMe resolves the repository from local `origin` and/or matching `GITHUB_REPOSITORY`. `continue_merge` and `abort_merge` are not available.
267
+ `conclude_merge` supports only single-head merges and refuses multi-head merges before mutation. Its marker check also covers manually staged resolutions and clean-filter output. Marker-like lines (including Markdown setext underlines) are conservatively refused; configured marker sizes above 4,096 are refused. Git commits the **entire index**, including already-staged merge entries: do not stage unrelated edits during the merge. Unrelated unstaged edits remain outside the commit. A failed/lost commit response reports uncertain postconditions; inspect `HEAD`, `MERGE_HEAD`, and the index before retrying.
268
+
269
+ All schemas reject additional properties. `init_repository` accepts only optional `initialBranch`, never a path or repository-mode controls. `change_branch` never accepts `baseRef`, `force`, `stash`, `discard`, `create`, `owner`, `repo`, or path inputs. `fetch_branch` accepts only the optional `remote` and `branch` pair for a targeted remote-tracking refresh (`remote` requires `branch`) and never accepts refspec, tags, prune, or force controls. `pull_branch` and `rebase_branch` have strict empty schemas and never accept a branch, remote, refspec, force, autostash, or arbitrary rebase target. `integrate_branch` requires exactly `sourceBranch` and `targetBranch`; it accepts no repository, path, remote, strategy, message, squash, signing, commit, continuation, abort, force, fetch, push, deletion, or worktree controls. `conclude_merge` requires exactly `action` (`conclude` or `abort`) and accepts no path list, commit message, strategy, `--ours`/`--theirs`, or force controls. `create_worktree` requires exactly `worktreePath`, `branchName`, and `branchMode`, plus an optional read-only `baseRef` start point for `branchMode: "new"`; `remove_worktree` requires `worktreePath` and accepts optional boolean `deleteIgnored`. No worktree tool accepts force, move, prune, repair, lock, unlock, detached, orphan, remote, or refspec controls. `retire_branch` requires exactly `branchName`, a full 40- or 64-hex-character `expectedHead`, a distinct local `targetBranch`, and the boolean `force` decision; it accepts no repository, path, remote, refspec, pattern, branch list, prune, remote-delete, worktree-removal, or inferred-target control. `pull_request` never accepts `owner`, `repo`, or owner-prefixed branch refs; BranchMe resolves the repository from local `origin` and/or matching `GITHUB_REPOSITORY`. Generic `continue_merge` and `abort_merge` tools are not available; `conclude_merge` is the only merge-continuation surface and only commits paths whose markers are already gone.
267
270
 
268
271
  ---
269
272
 
@@ -463,7 +466,7 @@ BranchMe operates only on the repository where pi is running:
463
466
  - `pull_request` creates PRs only for the resolved current GitHub repository, requires resolved `headBranch` and `baseBranch` values to be distinct and exist locally, requires the GitHub `headBranch` commit to match the local branch, queues behind in-flight same-repository git mutation windows when possible, and rejects `owner:branch` head refs. Missing PR fields fail unless `BRANCHME_PR_AUTOFILL=true`.
464
467
  - If local `origin` and `GITHUB_REPOSITORY` both resolve but disagree, `pull_request` fails closed.
465
468
 
466
- BranchMe intentionally does **not** stage files, create user-authored commits, accept or generate commit messages, force checkout, stash changes, discard changes, force-push, reset, edit files directly, copy ignored/untracked files between worktrees, or delete branches during standalone `remove_worktree`. Rebase-driven rewriting occurs only through explicit `rebase_branch`; a Git-generated standard merge commit is possible through explicit `integrate_branch` or `update_from_base` for divergent histories; one exact local ref can be deleted only through explicit `retire_branch` or merged-only `land_branch` under the leased boundaries above.
469
+ BranchMe intentionally does **not** create user-authored commits, accept or generate commit messages, force checkout, stash changes, discard changes, force-push, reset, edit files directly, copy ignored/untracked files between worktrees, or delete branches during standalone `remove_worktree`. The only staging it performs is `conclude_merge` adding exactly the formerly unmerged paths of an in-progress merge. Rebase-driven rewriting occurs only through explicit `rebase_branch`; a Git-generated standard merge commit is possible through explicit `integrate_branch` or `update_from_base` for divergent histories; one exact local ref can be deleted only through explicit `retire_branch` or merged-only `land_branch` under the leased boundaries above.
467
470
 
468
471
  ---
469
472
 
@@ -519,7 +522,9 @@ Ensure the token and Git credentials have permission for the branch and pull req
519
522
  | Rebase fails or conflicts | `rebase_branch` automatically attempts `git rebase --abort`. Inspect repository state before continuing if automatic cleanup also fails. |
520
523
  | Integration target mismatch or missing local branch | Check out the exact local `targetBranch` in the active control worktree and ensure both distinct branch refs already exist locally; `integrate_branch` never switches, fetches, or accepts remote-only refs. |
521
524
  | Integration rejects branch-specific merge options | Clear `branch.<targetBranch>.mergeOptions` outside BranchMe before retrying; options such as `--no-commit`, `--squash`, `--no-verify`, or a custom strategy would change the fixed policy. |
522
- | Integration reports `conflict` | The initial merge was automatically aborted and exact restoration was verified. Use the returned conflict paths for separate analysis; BranchMe has no `continue_merge` tool and does not resolve semantic conflicts. |
525
+ | Integration reports `conflict` | The initial merge was automatically aborted and exact restoration was verified; the conflict paths are listed in the result text. BranchMe has no generic `continue_merge` tool and does not resolve semantic conflicts. |
526
+ | `update_from_base` reports `conflict_kept` | The merge is in progress with MERGE_HEAD set and the listed paths unmerged. Have a file-editing step remove every conflict marker, then run `conclude_merge({ action: "conclude" })`; run `conclude_merge({ action: "abort" })` to restore the branch instead. Other BranchMe mutations refuse while the merge is in progress. |
527
+ | `conclude_merge` refuses with marker paths | An unmerged working-tree file or staged blob contains a default/custom-sized conflict marker line. Remove the markers and restage any manually staged resolution; marker-like Markdown underlines also count. Retry or abort. |
523
528
  | Integration is uncertain or cleanup fails | Inspect branch refs, `HEAD`, worktree status, and Git operation state manually before retrying. BranchMe does not use reset-based rollback. |
524
529
  | Retirement expected `HEAD` is stale | Refresh the exact local branch commit and target ancestry, confirm the intended commit, then make a new sequential `retire_branch` call. Never reuse a stale commit identity. |
525
530
  | Retirement branch is occupied | Remove the occupying linked worktree separately only after its own safety checks pass. Current, main, locked, prunable/missing, and any other registered occupancy block retirement. |
@@ -533,7 +538,7 @@ Ensure the token and Git credentials have permission for the branch and pull req
533
538
  | PR branch does not exist locally | Create or fetch/check out the local `headBranch` and `baseBranch` branches first; BranchMe does not use remote-only or cross-repository PR refs. |
534
539
  | PR branch is not visible or is stale on GitHub | Run `push_branch`, wait for it to complete, then retry `pull_request`; do not batch `push_branch` and `pull_request` in the same assistant tool call. |
535
540
  | Repository mismatch | Make `origin` and `GITHUB_REPOSITORY` refer to the same `owner/repo`. |
536
- | Need a user-authored commit | Use CommitMe or normal Git commands. BranchMe does not stage files, accept commit messages, or create user-authored commits; `integrate_branch` and `update_from_base` may let Git create a standard merge commit. |
541
+ | Need a user-authored commit | Use CommitMe or normal Git commands. BranchMe does not accept commit messages or create user-authored commits; `integrate_branch`, `update_from_base`, and `conclude_merge` may let Git create a standard merge commit. |
537
542
  | Other extensions interfere | Test with `pi --no-extensions -e .`. |
538
543
 
539
544
  ---
@@ -548,7 +553,7 @@ npm run check:pack
548
553
  printf '/branchme help\n/quit\n' | pi --no-extensions -e .
549
554
  ```
550
555
 
551
- Validation covers TypeScript typechecking, formatting checks, automatic context collection and prompt injection, mocked GitHub lookup, isolated real-Git worktree, branch-integration, and leased branch-retirement lifecycle tests, package checks, checkout Pi runtime smoke, and package-content verification. The checkout smoke loads BranchMe through Pi, then uses a temporary verifier command to confirm all nineteen BranchMe tools are visible through `pi.getAllTools()` with strict schemas and prompt metadata, including `integrate_branch`, `retire_branch`, and targeted `branch_status.ancestry`, with no merge-continuation tool. Runtime smoke inspects retirement and landing registration/schema but never executes either cleanup tool. Smoke-test notes are recorded in [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md), and TUI/help captures are stored in [`docs/TUI_CAPTURE.md`](docs/TUI_CAPTURE.md).
556
+ Validation covers TypeScript typechecking, formatting checks, automatic context collection and prompt injection, mocked GitHub lookup, isolated real-Git worktree, branch-integration, and leased branch-retirement lifecycle tests, package checks, checkout Pi runtime smoke, and package-content verification. The checkout smoke loads BranchMe through Pi, then uses a temporary verifier command to confirm all twenty BranchMe tools are visible through `pi.getAllTools()` with strict schemas and prompt metadata, including `integrate_branch`, `retire_branch`, `conclude_merge`, and targeted `branch_status.ancestry`, with no generic `continue_merge` or `abort_merge` tool. Runtime smoke inspects retirement and landing registration/schema but never executes either cleanup tool. Smoke-test notes are recorded in [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md), and TUI/help captures are stored in [`docs/TUI_CAPTURE.md`](docs/TUI_CAPTURE.md).
552
557
 
553
558
  Refresh TUI captures intentionally with:
554
559
 
package/SECURITY.md CHANGED
@@ -19,7 +19,8 @@ Implemented git mutations are limited to:
19
19
  - `change_branch`: `git switch <branchName>` after branch-name validation, local `refs/heads/<branchName>` verification, and clean-worktree preflight.
20
20
  - `create_branch`: `git switch -c <branchName>` from current `HEAD` after branch-name validation and existing-branch checks.
21
21
  - `track_branch`: narrow fetch followed by `git switch --no-overwrite-ignore --track=direct -c <branchName> refs/remotes/<remote>/<remoteBranch>`. Requires a new local branch, clean idle checkout, direct fetched commit ref, and verified final HEAD/upstream.
22
- - `update_from_base`: narrow fetch followed by the fixed integration merge policy against a captured remote-base commit. Reuses verified outcomes and automatic conflict abort; does not rewrite published history or change upstream. Both new fetch workflows explicitly disable pruning, extra ref mappings, tags, and submodule recursion, and reject symbolic remote-tracking destinations before fetching so Git cannot dereference them into unrelated refs. Base updates compare stored upstream configuration, not whether cached upstream refs happen to resolve.
22
+ - `update_from_base`: narrow fetch followed by the fixed integration merge policy against a captured remote-base commit. Reuses verified outcomes and automatic conflict abort; with explicit `keepConflicts: true` a conflicted merge is instead verified and left in progress (MERGE_HEAD equals the captured base commit, HEAD unchanged) for `conclude_merge`. Does not rewrite published history or change upstream. Both new fetch workflows explicitly disable pruning, extra ref mappings, tags, and submodule recursion, and reject symbolic remote-tracking destinations before fetching so Git cannot dereference them into unrelated refs. Base updates compare stored upstream configuration, not whether cached upstream refs happen to resolve.
23
+ - `conclude_merge`: on the current checkout with MERGE_HEAD present, `action: "conclude"` lists unmerged paths with `git diff --name-only --diff-filter=U -z`, refuses if `git --literal-pathspecs grep` finds a default/custom-sized conflict marker in those working-tree files or staged changed blobs (including CRLF and binary content), then runs `git --literal-pathspecs add -- <those paths>` and checks the candidate index again before `git commit --no-edit`, then verifies MERGE_HEAD is gone and HEAD is an exact two-parent merge of the previous HEAD and MERGE_HEAD on the same branch. It never uses `commit -a`, never stages other paths, accepts no message, and runs repository hooks as Git normally would. `action: "abort"` runs `git merge --abort` and verifies the same restoration as the automatic abort path. It refuses multi-head merges before mutation, honors cancellation until staging begins, and reports lost commit responses as uncertain postconditions. The commit includes the entire index, including previously staged entries; callers must not stage unrelated edits during a merge. Marker-like lines are conservatively refused, and configured marker sizes above 4,096 are rejected.
23
24
  - `fetch_branch`: `git fetch --no-tags --no-recurse-submodules <remote> <remoteBranchRef>:<remoteTrackingRef>`. With no arguments it validates and fetches the current branch's configured upstream; with explicit `branch` and optional configured `remote` (default `origin`) it fetches that exact remote branch. The explicit destination is limited to the selected remote-tracking ref, so local branches and working-tree files are not changed.
24
25
  - `pull_branch`: `git pull --ff-only --no-rebase --no-autostash <upstreamRemote> <upstreamBranchRef>` for the clean current branch after validating its configured upstream target.
25
26
  - `rebase_branch`: `git rebase --no-autostash --no-update-refs <upstream>` for the clean current branch after validating its configured upstream target. It rewrites local commits and automatically attempts `git rebase --abort` without the cancelled caller signal if the rebase fails or is killed.
@@ -36,7 +37,7 @@ Repository initialization creates `.git` metadata in the current directory. Bran
36
37
 
37
38
  BranchMe rejects a dirty control worktree before `change_branch`, `pull_branch`, `rebase_branch`, and `integrate_branch`, and rejects any staged, unstaged, untracked, or unmerged entry in a linked worktree before standalone `remove_worktree`. Standalone removal protects ignored residue by default and deletes it only with explicit `deleteIgnored: true` authorization. `land_branch` also allows ignored residue to be deleted, but still refuses tracked/staged or non-ignored untracked changes. Retirement does not require an unrelated active worktree to be clean, but every registered worktree record is inspected and any occupancy of the retiring branch blocks deletion. Integration also rejects an existing merge, rebase, cherry-pick, revert, or sequencer state and any non-empty target-branch `mergeOptions` setting that could alter the fixed command policy. After merge or retirement mutation begins, cleanup/postcondition inspection ignores caller cancellation and uses bounded timeouts. A `conflict` result is returned only after exact repository-relative conflict paths are captured, `git merge --abort` succeeds, source and target refs are restored, repository/control-worktree identity is preserved, operation state is cleared, and the control worktree is clean. Failed non-conflict merges remain errors; inconclusive cleanup or verification is never reported as success. After retirement is attempted, contradictory or inconclusive repository, target-ref, retiring-ref, or occupancy state is an uncertain error stating that retirement may have completed and requiring manual inspection before retry.
38
39
 
39
- BranchMe does not force checkout/removal, stash, stage files, create user-authored commits, accept commit messages, reset, force-push, or edit project files directly. Rebase-driven rewriting occurs only through explicit `rebase_branch`. Explicit `integrate_branch` or `update_from_base` may let Git create a standard merge commit for divergent histories, but BranchMe exposes no strategy, squash, unrelated-history, signing, force, commit, `continue_merge`, or `abort_merge` control. Explicit `retire_branch` and merged-only `land_branch` are the local branch-deletion surfaces. Standalone retirement has no bulk, pattern, inferred-target, remote-delete, remote-tracking-delete, automatic worktree-removal, or rollback-ref control.
40
+ BranchMe does not force checkout/removal, stash, create user-authored commits, accept commit messages, reset, force-push, or edit project files directly. Rebase-driven rewriting occurs only through explicit `rebase_branch`. Explicit `integrate_branch` or `update_from_base` may let Git create a standard merge commit for divergent histories, but BranchMe exposes no strategy, squash, unrelated-history, signing, force, or commit-message control and no generic `continue_merge` or `abort_merge` tool. The only staging surface is `conclude_merge`, which adds exactly the formerly unmerged paths of an in-progress merge after proving no conflict marker line remains in them. Explicit `retire_branch` and merged-only `land_branch` are the local branch-deletion surfaces. Standalone retirement has no bulk, pattern, inferred-target, remote-delete, remote-tracking-delete, automatic worktree-removal, or rollback-ref control.
40
41
 
41
42
  ## Network behavior
42
43
 
@@ -160,7 +161,7 @@ Do not open public issues for security-sensitive reports that include exploit de
160
161
  - Do not commit secrets, tokens, local `.env`, local `.pi/` state, or generated artifacts.
161
162
  - Keep tool schemas strict and reject unsupported fields.
162
163
  - Keep all git calls argv-style through `pi.exec("git", args)`.
163
- - Preserve the fixed `integrate_branch` merge policy, verified automatic abort/restoration contract, and process-local queue caveat; never add reset-based rollback or merge-continuation controls.
164
+ - Preserve the fixed `integrate_branch` merge policy, verified automatic abort/restoration contract, and process-local queue caveat; never add reset-based rollback. Keep `conclude_merge` limited to marker-free formerly unmerged paths, `git commit --no-edit`, and verified two-parent postconditions; never add `-a`, path, message, strategy, or `--ours`/`--theirs` controls.
164
165
  - Preserve `retire_branch` expected-`HEAD` leasing, complete occupancy checks, exact target ancestry, explicit unmerged force authorization, local-only ref scope, cancellation-safe final inspection, and uncertain-error behavior; never add bulk, inferred, remote, remote-tracking, or automatic worktree deletion.
165
166
  - Treat `reference-transaction` hooks as arbitrary repository-controlled code outside BranchMe's direct retirement argv guarantees.
166
167
  - Treat worktree paths as a filesystem security boundary; canonicalize them, verify current-repository membership before removal, and never add force cleanup.
@@ -15,7 +15,7 @@ Originally approved on 2026-06-30. Updated to describe the implemented `0.3.1` p
15
15
  - Exported extension function: `branchMeExtension`
16
16
  - Repository URL: `https://github.com/senad-d/branchme`
17
17
  - One-sentence pitch: Verified Pi tools for Git repository initialization plus current-repository branch, integration, retirement, linked-worktree, push, and GitHub pull request workflows.
18
- - Tool count: nineteen strict agent-callable tools.
18
+ - Tool count: twenty strict agent-callable tools.
19
19
 
20
20
  ## 3. Users and use cases
21
21
 
@@ -26,7 +26,7 @@ Originally approved on 2026-06-30. Updated to describe the implemented `0.3.1` p
26
26
  - Integrate one exact existing local source branch into the already-current clean local target, returning verified no-op, fast-forward, merge-commit, or restored-conflict details.
27
27
  - List the current repository's main and linked worktrees explicitly.
28
28
  - Discover local/remote-tracking branches, upstream counts, and worktree occupancy with `list_branches`.
29
- - Join an existing remote branch with verified `track_branch`, or merge a fresh remote base into the current feature with `update_from_base`.
29
+ - Join an existing remote branch with verified `track_branch`, or merge a fresh remote base into the current feature with `update_from_base`; keep a conflicted merge in progress with `keepConflicts: true` and commit or abort it with `conclude_merge` after a file-editing step removes the markers.
30
30
  - Create and verify a linked worktree from current `HEAD`, optional read-only `baseRef`, or an unoccupied existing local branch.
31
31
  - Return an exact, absolute, machine-readable worktree handoff for a caller-managed separate Pi session or subagent.
32
32
  - Remove an exact verified clean linked worktree while retaining its local branch; protect ignored residue by default and delete it only with explicit `deleteIgnored: true` authorization.
@@ -52,7 +52,8 @@ Originally approved on 2026-06-30. Updated to describe the implemented `0.3.1` p
52
52
  | Command | `/branchme help` | Runtime requirements and workflow guidance | Informational; no actions |
53
53
  | Tool | `list_branches` | Discover local and cached remote-tracking refs | Bounded, read-only; includes upstream counts and worktree occupancy |
54
54
  | Tool | `track_branch` | Join an existing remote branch | Narrow fetch, clean idle checkout, verified HEAD/upstream |
55
- | Tool | `update_from_base` | Fetch and merge an explicit remote base | Preserves published history; fixed merge policy and automatic conflict abort |
55
+ | Tool | `update_from_base` | Fetch and merge an explicit remote base | Preserves published history; fixed merge policy, listed conflict paths, automatic abort unless `keepConflicts` |
56
+ | Tool | `conclude_merge` | Commit or abort the in-progress kept merge | Refuses remaining markers; stages only formerly unmerged paths; `commit --no-edit`; verified two-parent result or verified abort |
56
57
  | Tool | `pull_request_status` | Read exact PR or latest PR for a head | Open/closed/merged state and commit identities; not a CI/review verdict |
57
58
  | Tool | `init_repository` | Initialize the exact current directory as a new non-bare Git repository | Optional initial branch; rejects reinitialization/nesting; no path, commit, or project-file controls |
58
59
  | Tool | `branch_status` | Refresh bounded current-worktree Git and related-PR context; optionally prove captured local or remote-tracking ancestry | Read-only; targeted ancestry is absent from automatic context |
@@ -92,7 +93,7 @@ Originally approved on 2026-06-30. Updated to describe the implemented `0.3.1` p
92
93
  - `src/github.ts`
93
94
  - `src/ui/branchme-panel.ts`
94
95
  - Module boundaries:
95
- - The extension entry point registers the informational command, nineteen tools, and automatic context hook.
96
+ - The extension entry point registers the informational command, twenty tools, and automatic context hook.
96
97
  - `src/git-workflow.ts` and `src/tools/workflow-tools.ts` own verified remote tracking, base updates, and the new PR status registration.
97
98
  - The context module owns bounded read-only collection, prompt formatting, targeted ancestry rendering, and the `before_agent_start` hook; automatic context never runs ancestry queries.
98
99
  - The command and UI modules own mode-safe informational help/status behavior and never invoke mutations.
@@ -193,7 +194,7 @@ Standalone removal retains its branch and requires explicit `deleteIgnored: true
193
194
  - Slash commands remain informational; tools perform all Git and GitHub actions.
194
195
  - Automatic context remains focused on the active worktree; inventory is available only through `list_worktrees`.
195
196
  - `create_worktree` returns a verified target for a caller-managed session but does not change cwd or create processes.
196
- - `integrate_branch` and `update_from_base` use the verified normal-merge state machine, automatically abort initial conflicts, and expose no merge continuation or semantic-resolution workflow. Only `update_from_base` fetches an explicit remote base before merging its captured commit.
197
+ - `integrate_branch` and `update_from_base` use the verified normal-merge state machine and automatically abort initial conflicts; only `update_from_base` fetches an explicit remote base before merging its captured commit, and only it may keep a conflicted merge in progress with `keepConflicts: true`. `conclude_merge` is the single continuation surface: it commits only marker-free formerly unmerged paths with Git's prepared message or aborts with verified restoration, and performs no semantic resolution.
197
198
  - Worktree removal remains force-free, protects ignored entries by default, requires explicit authorization to delete them, and preserves the local branch.
198
199
  - Local branch retirement remains a separate explicit operation requiring a fresh expected `HEAD`, exact target ancestry, zero complete-inventory occupancy, and an explicit boolean force decision; it deletes only the leased local ref and leaves branch configuration and remote/remote-tracking refs untouched.
199
200
  - `push_branch` uses `origin` only when the current branch has no configured upstream.
@@ -23,9 +23,9 @@ pi --no-extensions -e .
23
23
  ## Automated smoke behavior
24
24
 
25
25
  - `npm run smoke:pi` first runs isolated checkout Pi processes from a temporary non-Git working directory: one with `pi --no-extensions -e <package> -e <temporary verifier>` and `/branchmeverify verify`, then one with `pi --no-extensions -e <package>` and `/branchme help`.
26
- - The temporary command verifier calls `pi.getAllTools()` after BranchMe loads and confirms exactly nineteen tools—`list_branches`, `track_branch`, `update_from_base`, `pull_request_status`, `branch_status`, `init_repository`, `change_branch`, `create_branch`, `create_worktree`, `fetch_branch`, `integrate_branch`, `land_branch`, `list_worktrees`, `pull_branch`, `pull_request`, `push_branch`, `rebase_branch`, `remove_worktree`, and `retire_branch`—are each registered exactly once and active with strict schemas, named prompt guidelines, descriptions, and extension source metadata. It also proves `continue_merge` and `abort_merge` are absent.
26
+ - The temporary command verifier calls `pi.getAllTools()` after BranchMe loads and confirms exactly twenty tools—`list_branches`, `track_branch`, `update_from_base`, `conclude_merge`, `pull_request_status`, `branch_status`, `init_repository`, `change_branch`, `create_branch`, `create_worktree`, `fetch_branch`, `integrate_branch`, `land_branch`, `list_worktrees`, `pull_branch`, `pull_request`, `push_branch`, `rebase_branch`, `remove_worktree`, and `retire_branch`—are each registered exactly once and active with strict schemas, named prompt guidelines, descriptions, and extension source metadata. It also proves `continue_merge` and `abort_merge` are absent.
27
27
  - Runtime schema inspection verifies that `integrate_branch` has exactly the required `sourceBranch` and `targetBranch` fields and that `retire_branch` has exactly the required `branchName`, `expectedHead`, `targetBranch`, and `force` fields with `additionalProperties: false`. It also verifies that `branch_status` has only an optional top-level `ancestry` object whose required nested `sourceBranch` and `targetBranch` fields reject additional nested or top-level properties.
28
- - A second temporary verifier registers a deterministic local smoke model, blocks `fetch`, and runs normal prompts through real Pi lifecycle handling. It verifies automatic no-tool Git context in a temporary repository, one real `branch_status` tool refresh after a verifier-created local change, safe credential-free related-PR status with no request, and non-Git startup fallback. It expects all nineteen tools but forbids `land_branch`, `retire_branch`, `integrate_branch`, and every remote or worktree mutation tool from executing.
28
+ - A second temporary verifier registers a deterministic local smoke model, blocks `fetch`, and runs normal prompts through real Pi lifecycle handling. It verifies automatic no-tool Git context in a temporary repository, one real `branch_status` tool refresh after a verifier-created local change, safe credential-free related-PR status with no request, and non-Git startup fallback. It expects all twenty tools but forbids `land_branch`, `retire_branch`, `integrate_branch`, `conclude_merge`, and every remote or worktree mutation tool from executing.
29
29
  - The Pi runtime smoke validates worktree, integration, and retirement tool registration, strict schemas, and prompt contracts only; it never creates or removes a worktree, runs a merge, or retires a branch. Real-Git lifecycle coverage runs under `npm run test` in isolated temporary local repositories with no remote contact.
30
30
  - Isolated real-Git integration tests cover `already_integrated`, `fast_forward`, exact two-parent `merge_commit`, and conflict-path capture followed by verified automatic abort/restoration. They also cover target mismatch, dirty control state, rejection of branch-specific target merge options, unrelated histories, ignored-file overwrite protection, preserved repository hooks, and a committed source ref checked out in another dirty linked worktree. The recorded merge argv proves autostash and rerere are disabled, ignored-file protection is enabled, and `--no-verify` is absent.
31
31
  - Isolated real-Git retirement tests cover merged and explicitly forced-unmerged local-ref deletion, stale expected-`HEAD` rejection, current and linked-worktree occupancy, dirty unrelated worktrees, non-current targets, and preservation of target refs, remote-tracking refs, worktrees, working-tree files, and `branch.<name>.*` configuration without contacting a remote. Recorded argv proves retirement uses `update-ref --no-deref -d` with the captured expected commit and never uses `git branch -d/-D`, fetch, push, or a remote ref target.
package/docs/STRUCTURE.md CHANGED
@@ -27,18 +27,18 @@ src/
27
27
 
28
28
  ## Module boundaries
29
29
 
30
- 1. `src/extension.ts` stays small and registers the command, nineteen tools, and one `before_agent_start` context hook.
30
+ 1. `src/extension.ts` stays small and registers the command, twenty tools, and one `before_agent_start` context hook.
31
31
  2. `src/git-context.ts` owns the shared read-only collector, escaped/bounded formatter, automatic system-prompt append, and the current-state output used by `branch_status`.
32
32
  3. `src/commands/branchme-command.ts` parses `/branchme`, `/branchme help`, `--help`, and `-h`; it never performs git or GitHub mutations and avoids raw stdout in JSON mode.
33
33
  4. `src/tools/branchme-tools.ts` owns strict TypeBox schemas, prompt metadata, bounded tool content, and safe structured details. `branch_status` delegates to the shared context collector and optional ancestry verifier; `integrate_branch` and `retire_branch` delegate to focused mutation state machines; worktree tools expose explicit inventory and verified handoff operations.
34
34
  5. `src/git.ts` owns verified current-directory repository initialization plus reusable current-repository Git primitives: root and canonical common-Git-directory identity, strict direct local-ref inspection, branch/ref validation, local commit ancestry, operation-state and working-tree inspection, complete bounded worktree occupancy, branch/upstream workflows, worktree parsing/path validation/create/remove verification, and the process-local per-repository mutation queue.
35
- 6. `src/git-integration.ts` owns the integration preflight, one-window merge mutation, conflict-path capture, automatic abort, outcome classification, and repository/ref/worktree/ancestry postcondition verification. It never fetches, pushes, resets, switches branches, or continues merges.
35
+ 6. `src/git-integration.ts` owns the integration preflight, one-window merge mutation, conflict-path capture, automatic abort or verified kept-conflict state, outcome classification, repository/ref/worktree/ancestry postcondition verification, the shared bounded conflict-path text, and `conclude_merge` (marker check, exact-path staging, `commit --no-edit`, two-parent verification, verified abort). It never fetches, pushes, resets, or switches branches.
36
36
  7. `src/git-retirement.ts` owns strict runtime input validation, expected-`HEAD` and target-ancestry preflight, complete worktree-occupancy rejection, expected-old-value `update-ref` deletion, cancellation-safe final inspection, merged/forced-unmerged classification, and bounded uncertain errors. It never deletes remote or remote-tracking refs, removes worktrees, edits branch configuration, or performs reset rollback.
37
37
  8. `src/github.ts` owns GitHub `owner/repo` parsing, repository boundary checks, `GITHUB_TOKEN`/`GH_TOKEN` and `BRANCHME_PR_AUTOFILL` process-env/hardened git-root `.env` resolution, authenticated related/open/all PR lookup, PR lifecycle and merged-evidence validation, PR branch-name syntax validation, GitHub branch visibility/commit preflight, idempotent PR creation/reuse, bounded response validation, and redacted errors.
38
38
  9. `src/redaction.ts` owns shared credential redaction for Git, GitHub, and prompt-bound metadata.
39
39
  10. `src/types.ts` keeps serializable details shared by helpers, context, and tools.
40
40
  11. `src/ui/branchme-panel.ts` renders a compact status panel and clips lines to terminal width.
41
- 12. `src/git-workflow.ts` owns tracking and base-update preflight, narrow fetching, and verified checkout/integration. `src/tools/workflow-tools.ts` registers those tools and explicit PR status.
41
+ 12. `src/git-workflow.ts` owns tracking and base-update preflight, narrow fetching, and verified checkout/integration including the `keepConflicts` pass-through. `src/tools/workflow-tools.ts` registers those tools, `conclude_merge`, and explicit PR status.
42
42
  13. `src/git-landing.ts` owns execution-only `land_branch` orchestration and receipt types. It resolves the primary checkout, routes every Git call with `-C`, and reuses queue-free targeted fetch/removal/retirement helpers under one primary-root mutation window. Remote ancestry or exact merged-PR evidence gates cleanup; final target sync independently reports exact ref observations. No startup work or persistent landing state is added.
43
43
 
44
44
  ## Pi extension conventions
@@ -46,7 +46,7 @@ src/
46
46
  - No long-lived processes, watchers, timers, sockets, or background jobs start in the extension factory.
47
47
  - A single `before_agent_start` handler synchronously collects a fresh snapshot for each agent run and appends it to the existing system prompt; failures degrade to bounded unavailable context rather than blocking startup.
48
48
  - Slash commands are informational; tools perform repository initialization, branch, integration, retirement, worktree, push, and PR actions. Commands never initialize repositories, create/remove worktrees, merge or retire branches, change cwd, or start Pi sessions. There is no context command.
49
- - Every tool uses a strict TypeBox object schema with `additionalProperties: false`; `init_repository` accepts only optional `initialBranch`; `integrate_branch` requires exactly `sourceBranch` and `targetBranch`; `retire_branch` requires exactly `branchName`, full commit `expectedHead`, distinct `targetBranch`, and boolean `force`; worktree creation requires `worktreePath`, `branchName`, and `branchMode` with optional new-mode `baseRef`, listing is empty, and removal requires `worktreePath` with optional boolean `deleteIgnored`.
49
+ - Every tool uses a strict TypeBox object schema with `additionalProperties: false`; `init_repository` accepts only optional `initialBranch`; `integrate_branch` requires exactly `sourceBranch` and `targetBranch`; `retire_branch` requires exactly `branchName`, full commit `expectedHead`, distinct `targetBranch`, and boolean `force`; `update_from_base` accepts optional boolean `keepConflicts`; `conclude_merge` requires exactly `action` (`conclude` or `abort`); worktree creation requires `worktreePath`, `branchName`, and `branchMode` with optional new-mode `baseRef`, listing is empty, and removal requires `worktreePath` with optional boolean `deleteIgnored`.
50
50
  - Every tool defines a description, `promptSnippet`, and tool-specific `promptGuidelines` that explicitly name the tool.
51
51
  - Git commands use `pi.exec("git", args, { cwd, signal, timeout })` with argv arrays; `init_repository` uses Pi's file-mutation queue for the new `.git` entry and queues on the canonical target directory, and repository mutations run from verified locations and same-repository mutation/PR windows are serialized per repository. Retirement uses one canonical-active-worktree-keyed queue window from preflight through final verification. The queue is process-local and does not lock a different active worktree, another Pi process, or an external Git process.
52
52
  - Worktree results expose serializable requested and verified before/after state. Create returns `handoff: { cwd: <absolute>, branch, head, ready: true, summary }`; remove returns the retained branch/HEAD with `cwd: null` and `ready: false`.
@@ -66,7 +66,8 @@ src/
66
66
  - `pull_branch` requires a clean worktree and configured upstream, then updates only the current branch with an explicit `git pull --ff-only --no-rebase --no-autostash <remote> <remote-ref>` command; divergence fails without a rebase or merge commit.
67
67
  - `rebase_branch` requires a clean worktree and configured upstream, then rebases only the current branch with `git rebase --no-autostash --no-update-refs <upstream>`; it rewrites local commits and automatically attempts `git rebase --abort` on failure.
68
68
  - `integrate_branch` requires distinct existing local refs and a clean control worktree already on the target. It rejects non-empty target-branch `mergeOptions`, runs `git -c rerere.enabled=false merge --ff --no-edit --no-autostash --no-rerere-autoupdate --no-overwrite-ignore refs/heads/<sourceBranch>`, verifies before/after refs and ancestry, and classifies already-integrated, fast-forward, exact two-parent merge-commit, or conflict.
69
- - Conflict status is emitted only after bounded lossless paths are captured, `git merge --abort` succeeds, and repository identity, exact refs, target checkout, absent operation state, and clean worktree restoration are verified. Failed or uncertain postconditions throw; no reset rollback or continuation tool exists.
69
+ - Conflict status is emitted only after bounded lossless paths are captured, `git merge --abort` succeeds, and repository identity, exact refs, target checkout, absent operation state, and clean worktree restoration are verified. With `update_from_base` `keepConflicts: true`, `conflict_kept` is emitted only after the paths are captured and MERGE_HEAD, unchanged HEAD, branch, repository identity, and single-merge operation state are verified; a failed capture falls back to the verified abort. Failed or uncertain postconditions throw; no reset rollback exists.
70
+ - `conclude_merge` operates only when MERGE_HEAD contains exactly one commit on a current direct local branch. `conclude` checks unmerged working-tree paths and staged changed blobs for default/custom-sized conflict markers (including diff3, CRLF, and binary content), stages exactly the currently unmerged paths with `git --literal-pathspecs add --`, rechecks the candidate index to cover clean filters, commits the full index with `git commit --no-edit`, and verifies MERGE_HEAD is gone, HEAD moved on the same branch, and HEAD has exactly the previous HEAD and MERGE_HEAD as parents; unrelated unstaged changes stay unstaged. Already-staged entries are included in the commit; callers must not stage unrelated edits during a merge. Pre-mutation reads honor cancellation; after staging begins, bounded mutation/verification ignores cancellation. Lost commit responses report uncertain postconditions. `abort` reuses the integration abort and restoration verification.
70
71
  - `retire_branch` requires one direct local ref, its exact full expected `HEAD`, one distinct direct local target, and a boolean force decision. It rejects complete-inventory worktree occupancy, proves ancestry against captured commits, and allows negative ancestry only with explicit `force: true` authorization.
71
72
  - Retirement deletes only `refs/heads/<branchName>` with `git update-ref --no-deref -d <fullRef> <capturedHead>`. The expected-old-value lease prevents deleting a moved ref; final repository, target, retiring-ref, and occupancy checks run with bounded timeouts after the mutation attempt even if the caller cancels. Contradictory or inconclusive state throws an uncertain error with manual-inspection guidance and no rollback ref mutation.
72
73
  - Local branch configuration, worktrees, remote refs, and remote-tracking refs remain untouched by retirement. Forced unmerged retirement may remove a commit's last local branch reference, and normal expiry/garbage collection can eventually make it unreachable.
@@ -79,7 +80,7 @@ src/
79
80
  - `push_branch` mutates remote refs only for the current branch and uses an explicit upstream remote/refspec instead of bare `git push` when an upstream exists.
80
81
  - `pull_request` requires resolved `headBranch` and `baseBranch` values to be distinct and exist locally, requires `headBranch` to match the GitHub-visible branch commit, queues behind already-started same-repository git mutation windows, makes GitHub REST API calls for the resolved current repository only, and rejects owner-prefixed or unsafe branch refs before the request. Omitted fields require configured autofill.
81
82
  - `pull_request` reads `GITHUB_TOKEN` or `GH_TOKEN` from process environment first; only when neither process token is set does it read those token keys from a small regular `.env` file in the verified git root as a fallback. `BRANCHME_PR_AUTOFILL` uses the same process-first, `.env`-fallback precedence and defaults off.
82
- - BranchMe does not force checkout/removal, move/prune/repair/lock/unlock worktrees, create detached/orphan worktrees, infer remote worktree branches, copy ignored/untracked files such as `.env`, delete ignored worktree residue through standalone `remove_worktree` without explicit `deleteIgnored: true` authorization, delete retained worktree branches during standalone removal, change Pi's cwd, or start Pi sessions. It also does not stash, stage, create user-authored commits, accept commit messages, reset, force-push, directly edit project files, read unsupported `.env` keys, follow unsafe `.env` file types, depend on GitHub CLI, or collect telemetry. Explicit `integrate_branch` and `update_from_base` may let Git create a standard merge commit for divergent histories; only explicit leased `retire_branch` or merged-only `land_branch` may delete one exact local branch ref. Retirement has no bulk, inferred-target, remote, remote-tracking, rollback, or automatic worktree deletion. Git documents submodule worktree support as incomplete; BranchMe adds no force-based submodule cleanup.
83
+ - BranchMe does not force checkout/removal, move/prune/repair/lock/unlock worktrees, create detached/orphan worktrees, infer remote worktree branches, copy ignored/untracked files such as `.env`, delete ignored worktree residue through standalone `remove_worktree` without explicit `deleteIgnored: true` authorization, delete retained worktree branches during standalone removal, change Pi's cwd, or start Pi sessions. It also does not stash, create user-authored commits, accept commit messages, reset, force-push, directly edit project files, read unsupported `.env` keys, follow unsafe `.env` file types, depend on GitHub CLI, or collect telemetry. Explicit `integrate_branch`, `update_from_base`, and `conclude_merge` may let Git create a standard merge commit for divergent histories; `conclude_merge` is the only tool that stages, and only the formerly unmerged paths; only explicit leased `retire_branch` or merged-only `land_branch` may delete one exact local branch ref. Retirement has no bulk, inferred-target, remote, remote-tracking, rollback, or automatic worktree deletion. Git documents submodule worktree support as incomplete; BranchMe adds no force-based submodule cleanup.
83
84
 
84
85
  `list_branches` returns bounded display metadata, including cached upstream counts and occupancy; automatic context is unchanged. `pull_request_status` reads exact-number or latest-head PR state, not CI/review requirements. `pull_request` reuses matching open PRs without overwriting their metadata. PR-aware landing verifies repository/remote identity, exact merged head/base, and merge-commit containment before allowing non-ancestor local retirement; it retains the expected-HEAD lease and reports graph ancestry separately from host evidence.
85
86
 
@@ -36,7 +36,8 @@ Commands only show info; BranchMe tools perform actions.
36
36
 
37
37
  - `list_branches` — discover local/cached remote refs, upstream counts, and worktree occupancy without fetching.
38
38
  - `track_branch` — fetch an existing remote branch and verify a new clean local tracking checkout.
39
- - `update_from_base` — fetch and merge an explicit base into the current clean feature, preserving published history and upstream.
39
+ - `update_from_base` — fetch and merge an explicit base into the current clean feature, preserving published history and upstream; a conflict is aborted unless `keepConflicts: true` leaves it in progress.
40
+ - `conclude_merge` — `action: "conclude"` commits the kept merge once every conflict marker is removed; `action: "abort"` restores the branch.
40
41
  - `pull_request_status` — inspect a PR by number, or the latest PR for a head branch; not a CI/review verdict.
41
42
  - `pull_request` — reuses an exact matching open PR without changing its title, body, or draft state.
42
43
  - `land_branch` — after host merge, clean up from the primary checkout; use `pullRequestNumber` for squash/rebase evidence.
@@ -71,7 +72,7 @@ Commands only show info; BranchMe tools perform actions.
71
72
  - `fetch_branch` without `branch`, `pull_branch`, and `rebase_branch` require a configured upstream.
72
73
  - `pull_branch` and `rebase_branch` require a clean working tree.
73
74
  - `rebase_branch` rewrites local commits only when explicitly requested and auto-aborts on failure.
74
- - BranchMe never stages, creates user-authored commits, or force-pushes.
75
+ - BranchMe never creates user-authored commits or force-pushes; only `conclude_merge` stages, and only the resolved conflict paths.
75
76
  ```
76
77
 
77
78
  ## Panel: Tiny mode: clean branch with token
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senad-d/branchme",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "type": "module",
5
5
  "description": "Pi extension for verified Git repository initialization, branch, worktree, integration, retirement, push, and pull request workflows.",
6
6
  "license": "MIT",
@@ -45,7 +45,8 @@ export function getBranchMeHelpText(): string {
45
45
  "",
46
46
  "- `list_branches` — discover local/cached remote refs, upstream counts, and worktree occupancy without fetching.",
47
47
  "- `track_branch` — fetch an existing remote branch and verify a new clean local tracking checkout.",
48
- "- `update_from_base` — fetch and merge an explicit base into the current clean feature, preserving published history and upstream.",
48
+ "- `update_from_base` — fetch and merge an explicit base into the current clean feature, preserving published history and upstream; a conflict is aborted unless `keepConflicts: true` leaves it in progress.",
49
+ "- `conclude_merge` — `action: \"conclude\"` commits the kept merge once every conflict marker is removed; `action: \"abort\"` restores the branch.",
49
50
  "- `pull_request_status` — inspect a PR by number, or the latest PR for a head branch; not a CI/review verdict.",
50
51
  "- `pull_request` — reuses an exact matching open PR without changing its title, body, or draft state.",
51
52
  "- `land_branch` — after host merge, clean up from the primary checkout; use `pullRequestNumber` for squash/rebase evidence.",
@@ -80,7 +81,7 @@ export function getBranchMeHelpText(): string {
80
81
  "- `fetch_branch` without `branch`, `pull_branch`, and `rebase_branch` require a configured upstream.",
81
82
  "- `pull_branch` and `rebase_branch` require a clean working tree.",
82
83
  "- `rebase_branch` rewrites local commits only when explicitly requested and auto-aborts on failure.",
83
- "- BranchMe never stages, creates user-authored commits, or force-pushes.",
84
+ "- BranchMe never creates user-authored commits or force-pushes; only `conclude_merge` stages, and only the resolved conflict paths.",
84
85
  ].join("\n");
85
86
  }
86
87
 
package/src/constants.ts CHANGED
@@ -5,6 +5,7 @@ export const BRANCH_STATUS_TOOL_NAME = "branch_status";
5
5
  export const LIST_BRANCHES_TOOL_NAME = "list_branches";
6
6
  export const TRACK_BRANCH_TOOL_NAME = "track_branch";
7
7
  export const UPDATE_FROM_BASE_TOOL_NAME = "update_from_base";
8
+ export const CONCLUDE_MERGE_TOOL_NAME = "conclude_merge";
8
9
  export const INIT_REPOSITORY_TOOL_NAME = "init_repository";
9
10
  export const CREATE_BRANCH_TOOL_NAME = "create_branch";
10
11
  export const CHANGE_BRANCH_TOOL_NAME = "change_branch";
@@ -27,6 +28,7 @@ export const BRANCHME_TOOL_NAMES = [
27
28
  LIST_BRANCHES_TOOL_NAME,
28
29
  TRACK_BRANCH_TOOL_NAME,
29
30
  UPDATE_FROM_BASE_TOOL_NAME,
31
+ CONCLUDE_MERGE_TOOL_NAME,
30
32
  INIT_REPOSITORY_TOOL_NAME,
31
33
  CREATE_BRANCH_TOOL_NAME,
32
34
  CHANGE_BRANCH_TOOL_NAME,