@senad-d/branchme 0.1.8 → 0.2.0

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.
@@ -23,10 +23,13 @@ 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 `branch_status`, `change_branch`, `create_branch`, `create_worktree`, `fetch_branch`, `list_worktrees`, `pull_branch`, `pull_request`, `push_branch`, `rebase_branch`, and `remove_worktree` are each registered exactly once and active with strict schemas, named prompt guidelines, descriptions, and extension source metadata.
27
- - 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.
28
- - The Pi runtime smoke validates worktree tool registration, strict schemas, and prompt contracts only; it never creates or removes a worktree. Real-Git `list_worktrees`/`create_worktree`/`remove_worktree` lifecycle coverage runs under `npm run test` in temporary local repositories with sibling destinations under the same temporary root and no remotes.
29
- - `npm run smoke:worktree-handoff` loads only BranchMe into a deterministic extension host backed by real local Git, creates a new-branch linked worktree through the registered `create_worktree` tool, and launches a separate Node verification process with the returned absolute `handoff.cwd`. That process verifies its cwd, branch, full `HEAD`, and lack of remotes. The smoke then calls the registered `remove_worktree` tool, verifies the directory is absent, and verifies the retained local branch remains at the original commit.
26
+ - The temporary command verifier calls `pi.getAllTools()` after BranchMe loads and confirms exactly thirteen tools—`branch_status`, `change_branch`, `create_branch`, `create_worktree`, `fetch_branch`, `integrate_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
+ - 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 thirteen tools but forbids `retire_branch`, `integrate_branch`, and every remote or worktree mutation tool from executing.
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
+ - 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
+ - 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.
32
+ - `npm run smoke:worktree-handoff` loads only BranchMe into a deterministic extension host backed by real local Git, creates a new-branch linked worktree through the registered `create_worktree` tool, and launches a separate Node verification process with the returned absolute `handoff.cwd`. That process verifies its cwd, branch, full `HEAD`, and lack of remotes. The smoke then calls the registered `remove_worktree` tool, verifies the directory is absent, verifies the retained local branch remains at the original commit, and asserts that `retire_branch` was never invoked.
30
33
  - The handoff smoke uses only a freshly created temporary source repository and sibling worktree, an empty Git config, a minimal credential-free environment, no remotes, offline Pi flags, and a fetch guard that fails on any network request. Cleanup recursively removes the isolated temporary root even on failure.
31
34
  - `npm run validate` includes the isolated handoff smoke after package-content checks.
32
35
  - The checkout command smoke accepts either `/branchme help` text or the read-only BranchMe status fallback as equivalent non-mutating command output.
@@ -41,11 +44,11 @@ pi --no-extensions -e .
41
44
  ## Result
42
45
 
43
46
  - `npm run typecheck`, `npm run format:check`, `npm run test`, `npm run smoke:pi`, `npm run check:pack`, `npm run validate`, and `npm run smoke:pi:packed` passed.
44
- - `npm run smoke:worktree-handoff` returned `ok: true`, an absolute ready cwd, the expected `feature/isolated-handoff-smoke` branch and full commit ID from a separate process, `branchRetained: true` after force-free removal, zero network requests, and an isolated empty credential source.
45
- - `npm run smoke:pi` loaded BranchMe through Pi, verified the complete BranchMe tool set and prompt metadata through real Pi runtime APIs, and confirmed non-mutating BranchMe command output.
47
+ - `npm run smoke:worktree-handoff` returned `ok: true`, an absolute ready cwd, the expected `feature/isolated-handoff-smoke` branch and full commit ID from a separate process, `branchRetained: true` after force-free removal, `retireBranchInvoked: false`, zero network requests, and an isolated empty credential source.
48
+ - `npm run smoke:pi` loaded BranchMe through Pi, verified exactly thirteen BranchMe tools (including strict `integrate_branch`, `retire_branch`, and nested `branch_status.ancestry` schemas), proved merge-continuation tools absent, and confirmed non-mutating BranchMe command output.
46
49
  - The isolated Git-context prompt smoke observed the `before_agent_start` snapshot, answered branch and dirty-tree state without a tool call, refreshed a verifier-created local change through one real `branch_status` call, returned safe unavailable context without credentials or outside Git, and attempted no network request.
47
50
  - `npm run smoke:pi:packed` packed BranchMe outside the repository, installed the artifact in a temporary production workspace, loaded the installed package through Pi, and confirmed non-mutating BranchMe command output.
48
- - `npm run check:pack` confirmed the package contents are limited to public docs (including worktree behavior and security boundaries), images, source, license, package metadata, `.env.example`, and `tsconfig.json`; private planning specs and generated files remain excluded.
51
+ - `npm run check:pack` confirmed the package contents are limited to public docs (including worktree, local integration, leased local branch-retirement, and security boundaries), images, source, license, package metadata, `.env.example`, and `tsconfig.json`; private planning specs and generated files remain excluded.
49
52
  - The isolated Pi smoke command loaded BranchMe and displayed BranchMe help or status output instead of template behavior.
50
53
  - The bare `pi --no-extensions -e .` smoke command exited cleanly in this non-interactive validation environment.
51
54
  - No template command or template tool output was observed.
package/docs/STRUCTURE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # BranchMe Structure Guide
2
2
 
3
- BranchMe is a TypeScript Pi extension package for current-repository Git branch, verified linked-worktree, push, and GitHub pull request workflows.
3
+ BranchMe is a TypeScript Pi extension package for current-repository Git branch, verified branch-integration and retirement, linked-worktree, push, and GitHub pull request workflows.
4
4
 
5
5
  ## Source layout
6
6
 
@@ -13,8 +13,10 @@ src/
13
13
  ├── commands/
14
14
  │ └── branchme-command.ts # /branchme status/help command; informational only
15
15
  ├── tools/
16
- │ └── branchme-tools.ts # registration for eleven branch/worktree/GitHub workflow tools
17
- ├── git.ts # argv-style branch/worktree helpers and per-repo workflow queue
16
+ │ └── branchme-tools.ts # registration for thirteen branch/integration/retirement/worktree/GitHub tools
17
+ ├── git.ts # shared argv-style Git primitives and per-repo mutation queue
18
+ ├── git-integration.ts # integration preflight, merge, cleanup, and verification state machine
19
+ ├── git-retirement.ts # leased local-ref retirement and postcondition state machine
18
20
  ├── github.ts # GitHub repo resolution, env/.env tokens, branch preflight, REST calls, redaction
19
21
  └── ui/
20
22
  └── branchme-panel.ts # compact /branchme status panel renderer
@@ -22,27 +24,29 @@ src/
22
24
 
23
25
  ## Module boundaries
24
26
 
25
- 1. `src/extension.ts` stays small and registers the command, eleven tools, and one `before_agent_start` context hook.
27
+ 1. `src/extension.ts` stays small and registers the command, thirteen tools, and one `before_agent_start` context hook.
26
28
  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`.
27
29
  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.
28
- 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; `list_worktrees`, `create_worktree`, and `remove_worktree` expose explicit repository inventory and verified handoff operations.
29
- 5. `src/git.ts` owns current-repository Git behavior: root and common-Git-directory detection, branch/upstream/ahead-behind inspection, working-tree parsing, recent-commit collection, PR base/commit-subject inference, branch validation and branch workflows, NUL-delimited worktree parsing/inventory, canonical worktree path validation, verified create/remove postconditions, and the per-repository workflow queue.
30
- 6. `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-PR lookup, PR branch-name syntax validation, GitHub branch visibility/commit preflight, PR REST calls, bounded response validation, and redacted errors.
31
- 7. `src/redaction.ts` owns shared credential redaction for Git, GitHub, and prompt-bound metadata.
32
- 8. `src/types.ts` keeps serializable details shared by helpers, context, and tools.
33
- 9. `src/ui/branchme-panel.ts` renders a compact status panel and clips lines to terminal width.
30
+ 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.
31
+ 5. `src/git.ts` owns 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.
32
+ 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.
33
+ 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.
34
+ 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-PR lookup, PR branch-name syntax validation, GitHub branch visibility/commit preflight, PR REST calls, bounded response validation, and redacted errors.
35
+ 9. `src/redaction.ts` owns shared credential redaction for Git, GitHub, and prompt-bound metadata.
36
+ 10. `src/types.ts` keeps serializable details shared by helpers, context, and tools.
37
+ 11. `src/ui/branchme-panel.ts` renders a compact status panel and clips lines to terminal width.
34
38
 
35
39
  ## Pi extension conventions
36
40
 
37
41
  - No long-lived processes, watchers, timers, sockets, or background jobs start in the extension factory.
38
42
  - 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.
39
- - Slash commands are informational; tools perform branch, worktree, push, and PR actions. Commands never create/remove worktrees, change cwd, or start Pi sessions. There is no context command.
40
- - Every tool uses a strict TypeBox object schema with `additionalProperties: false`; worktree creation requires exactly `worktreePath`, `branchName`, and `branchMode`, while listing is empty and removal accepts only `worktreePath`.
43
+ - Slash commands are informational; tools perform branch, integration, retirement, worktree, push, and PR actions. Commands never create/remove worktrees, merge or retire branches, change cwd, or start Pi sessions. There is no context command.
44
+ - Every tool uses a strict TypeBox object schema with `additionalProperties: false`; `integrate_branch` requires exactly `sourceBranch` and `targetBranch`; `retire_branch` requires exactly `branchName`, full commit `expectedHead`, distinct `targetBranch`, and boolean `force`; worktree creation requires exactly `worktreePath`, `branchName`, and `branchMode`, listing is empty, and removal accepts only `worktreePath`.
41
45
  - Every tool defines a description, `promptSnippet`, and tool-specific `promptGuidelines` that explicitly name the tool.
42
- - Git commands use `pi.exec("git", args, { cwd, signal, timeout })` with argv arrays; repository mutations run from verified locations and same-repository mutation/PR windows are serialized per repository.
46
+ - Git commands use `pi.exec("git", args, { cwd, signal, timeout })` with argv arrays; 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.
43
47
  - 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`.
44
48
  - Tool details avoid token values, abort signals, runtime objects, and unbounded raw command/API output.
45
- - Automatic and explicit context include branch/upstream/ahead-behind state, working-tree counts, up to 20 unstaged/untracked entries, related open PR state, and up to 5 recent commits. Metadata values default to 512 characters and rendered context to 4,000 characters.
49
+ - Automatic and explicit context include branch/upstream/ahead-behind state, working-tree counts, up to 20 unstaged/untracked entries, related open PR state, and up to 5 recent commits. Explicit `branch_status` may additionally verify one strict local source/target ancestry query; automatic context never does. Metadata values default to 512 characters and rendered context to 4,000 characters.
46
50
  - Pi core packages, including `@earendil-works/pi-tui` for key/width utilities, remain in `peerDependencies` with `"*"`.
47
51
 
48
52
  ## Security-sensitive areas
@@ -50,11 +54,17 @@ src/
50
54
  - Automatic context and `branch_status` share bounded, read-only collection. They run no mutations and capture repository metadata only—never diffs or file contents.
51
55
  - Repository-controlled paths, branch names, commit subjects, and PR fields are escaped, quoted, redacted, bounded, and labeled untrusted before system-prompt insertion.
52
56
  - Related-PR lookup may issue an authenticated `GET /pulls` before every agent run and on explicit refresh. It has a 4-second timeout and 64 KiB response limit, and makes no unauthenticated fallback request.
53
- - The start-of-run snapshot may be stale after a mutation in that same run; `branch_status` is the explicit read-only refresh.
57
+ - The start-of-run snapshot may be stale after a mutation in that same run; `branch_status` is the explicit read-only refresh. Its optional targeted ancestry proof must run after `integrate_branch`, not in the same parallel tool batch.
54
58
  - `change_branch` mutates local HEAD and working-tree files only through `git switch <branchName>` for existing local branches after a clean-worktree preflight.
55
59
  - `fetch_branch` requires a configured upstream and runs `git fetch --no-tags --no-recurse-submodules <remote> <remote-ref>:<remote-tracking-ref>`; its explicit refspec updates only that tracking ref without changing local branches or working-tree files.
56
60
  - `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.
57
61
  - `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.
62
+ - `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.
63
+ - 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.
64
+ - `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.
65
+ - 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.
66
+ - 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.
67
+ - Git hooks, custom merge drivers, clean/smudge filters, signing policy, and `reference-transaction` hooks stay active where Git invokes them and may execute commands or network operations outside BranchMe's direct argv boundary.
58
68
  - `create_branch` mutates local branch/HEAD only with `git switch -c`.
59
69
  - `list_worktrees` reads a bounded `git worktree list --porcelain -z` inventory and remains explicit rather than expanding automatic active-worktree context.
60
70
  - `create_worktree` requires an explicitly approved absolute destination, canonicalizes its existing parent, rejects existing or nested/common-Git-directory destinations, and creates only from current `HEAD` or an unoccupied existing local branch. It verifies canonical path, branch, `HEAD`, and cleanliness before returning a ready handoff.
@@ -62,7 +72,7 @@ src/
62
72
  - `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.
63
73
  - `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.
64
74
  - `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.
65
- - 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`, remove linked worktrees that contain ignored entries, delete retained worktree branches, change Pi's cwd, or start Pi sessions. It also does not stash, stage, create user-authored commits, reset, force-push, create merge commits, directly edit files, read unsupported `.env` keys, follow unsafe `.env` file types, depend on GitHub CLI, or collect telemetry. Git documents submodule worktree support as incomplete; BranchMe adds no force-based submodule cleanup.
75
+ - 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`, remove linked worktrees that contain ignored entries, delete retained worktree branches during 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 files, read unsupported `.env` keys, follow unsafe `.env` file types, depend on GitHub CLI, or collect telemetry. Only explicit `integrate_branch` may let Git create a standard merge commit for divergent histories; only explicit leased `retire_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.
66
76
 
67
77
  ## Documentation
68
78
 
@@ -78,10 +88,12 @@ test/
78
88
  ├── command.test.mjs # /branchme parsing, help, fallback, panel width
79
89
  ├── git-context.test.mjs # collection, prompt hook, formatting, safety, and output bounds
80
90
  ├── git.test.mjs # branch/worktree parsing, validation, command construction, postconditions, failures
81
- ├── git-integration.test.mjs # isolated real-Git context, branch, and worktree lifecycle coverage
91
+ ├── git-integration.test.mjs # isolated real-Git context, branch integration, and worktree lifecycle coverage
92
+ ├── git-retirement.test.mjs # mocked retirement preflight, lease, postconditions, and failures
93
+ ├── git-retirement-integration.test.mjs # isolated real-Git local-ref retirement lifecycle coverage
82
94
  ├── github.test.mjs # GitHub parsing, token resolution, related-PR lookup, redaction
83
95
  ├── preparation.test.mjs # package/docs/source metadata checks
84
- ├── schema-validation.test.mjs # strict TypeBox schema validation, including worktree fields/modes
96
+ ├── schema-validation.test.mjs # strict TypeBox schema validation, including worktree and retirement fields
85
97
  ├── tools.test.mjs # extension registration, prompt metadata, shared refresh, tool behavior
86
98
  └── tui-capture.test.mjs # generated text capture for TUI/help visual baselines
87
99
  ```
@@ -31,13 +31,27 @@ Commands only show info; BranchMe tools perform actions.
31
31
  7. `push_branch` — push the current branch.
32
32
  8. `pull_request` — open a PR after `push_branch` completes and GitHub sees the branches.
33
33
 
34
+ ## Branch integration
35
+
36
+ - `integrate_branch` — integrate an exact local source into an exact local target; the clean control worktree must already have the target checked out.
37
+ - It never fetches or pushes; a conflict is automatically aborted after paths are captured and restoration is verified.
38
+ - BranchMe does not resolve semantic conflicts; handle them in a separate developer workflow.
39
+
40
+ ## Branch retirement
41
+
42
+ - `remove_worktree` and `retire_branch` are separate: removal retains the branch; retirement later deletes only the exact local branch ref.
43
+ - `retire_branch` requires the exact local branch, its full expected `HEAD`, and an exact local target for ancestry verification.
44
+ - Any registered worktree occupancy rejects retirement; remove an occupying linked worktree separately first.
45
+ - Merged retirement uses `force: false`; unmerged retirement requires explicit authorization with `force: true` and may make commits unreachable.
46
+ - It never directly deletes a remote or remote-tracking branch.
47
+
34
48
  ## Worktree handoff
35
49
 
36
50
  - `list_worktrees` — inspect the main and linked worktrees in the current repository.
37
51
  - `create_worktree` — create a linked worktree and return a ready handoff with an absolute `handoff.cwd`.
38
52
  - A separate orchestrator starts the next Pi session or subagent in `handoff.cwd`.
39
53
  - `remove_worktree` — remove a verified clean linked worktree while retaining its local branch.
40
- - BranchMe does not change cwd, start Pi, copy `.env`, or remove branches automatically.
54
+ - BranchMe does not change cwd, start Pi, or copy `.env`; `remove_worktree` never removes its retained branch automatically.
41
55
 
42
56
  ## Requirements
43
57
 
@@ -47,7 +61,7 @@ Commands only show info; BranchMe tools perform actions.
47
61
  - `fetch_branch`, `pull_branch`, and `rebase_branch` require a configured upstream.
48
62
  - `pull_branch` and `rebase_branch` require a clean working tree.
49
63
  - `rebase_branch` rewrites local commits only when explicitly requested and auto-aborts on failure.
50
- - BranchMe never stages, creates user-authored commits, force-pushes, or creates merge commits.
64
+ - BranchMe never stages, creates user-authored commits, or force-pushes.
51
65
  ```
52
66
 
53
67
  ## Panel: Tiny mode: clean branch with token
@@ -80,7 +94,30 @@ Width: 40
80
94
  │ │
81
95
  │ │
82
96
  ├──────────────────────────────────────┤
83
- │ 1/3 • status • current repository on…│
97
+ │ 1/4 • status • current repository on…│
98
+ ╰──────────────────────────────────────╯
99
+ ```
100
+
101
+ ## Panel: Narrow mode: Lifecycle selected
102
+
103
+ Width: 40
104
+
105
+ ```text
106
+ ╭ BranchMe ───────────────── Lifecycle ╮
107
+ │current repo only • informational │
108
+ │ ↑↓ section • q quit • /branchme help │
109
+ ├──────────────────────────────────────┤
110
+ │ LIFECYCLE │
111
+ │ integrate_branch -> exact local sou…│
112
+ │ control target -> checked out + c…│
113
+ │ conflict -> automatic verif…│
114
+ │ semantic intent -> separate develo…│
115
+ │ remove_worktree -> separate; branc…│
116
+ │ retire_branch -> leased local re…│
117
+ │ retirement guard -> unoccupied + ta…│
118
+ │ remote effects -> never delete re…│
119
+ ├──────────────────────────────────────┤
120
+ │ 3/4 • lifecycle • integrate → remove…│
84
121
  ╰──────────────────────────────────────╯
85
122
  ```
86
123
 
@@ -94,15 +131,15 @@ Width: 80
94
131
  ├─────────────────────┬────────────────────────────────────────────────────────┤
95
132
  │▶ Status │ STATUS │
96
133
  │ Workflow │ Current branch: feature/current │
97
- │ Worktrees │ GitHub repository: senad-d/branchme │
98
- │ │ GitHub token: present │
134
+ │ Lifecycle │ GitHub repository: senad-d/branchme │
135
+ │ Worktrees │ GitHub token: present │
99
136
  │ │ │
100
137
  │ │ │
101
138
  │ │ │
102
139
  │ │ │
103
140
  │ │ │
104
141
  ├─────────────────────┴────────────────────────────────────────────────────────┤
105
- │ 1/3 • status • current repository only • tools perform actions │
142
+ │ 1/4 • status • current repository only • tools perform actions │
106
143
  ╰──────────────────────────────────────────────────────────────────────────────╯
107
144
  ```
108
145
 
@@ -115,16 +152,38 @@ Width: 80
115
152
  │ ↑↓ section • q quit • /branchme help │
116
153
  ├─────────────────────┬────────────────────────────────────────────────────────┤
117
154
  │ Status │ WORKFLOW │
118
- │▶ Workflow │ branch_status -> inspect │
119
- │ Worktrees │ change_branch -> existing local │
120
- │ │ fetch_branch -> upstream remote │
121
- │ │ pull_branch -> fast-forward │
122
- │ │ rebase_branch -> onto upstream │
123
- │ │ create_branch -> from HEAD │
124
- │ │ push_branch -> current branch │
125
- │ │ pull_request -> after push │
155
+ │▶ Workflow │ branch_status -> inspect │
156
+ │ Lifecycle │ change_branch -> existing local │
157
+ │ Worktrees │ fetch_branch -> upstream remote │
158
+ │ │ pull_branch -> fast-forward │
159
+ │ │ rebase_branch -> onto upstream │
160
+ │ │ create_branch -> from HEAD │
161
+ │ │ push_branch -> current branch │
162
+ │ │ pull_request -> after push │
163
+ ├─────────────────────┴────────────────────────────────────────────────────────┤
164
+ │ 2/4 • workflow • inspect → change → fetch/pull/rebase → create → push → PR │
165
+ ╰──────────────────────────────────────────────────────────────────────────────╯
166
+ ```
167
+
168
+ ## Panel: Wide mode: Lifecycle selected
169
+
170
+ Width: 80
171
+
172
+ ```text
173
+ ╭ BranchMe ───────────────────────────────────────────────────────── Lifecycle ╮
174
+ │ ↑↓ section • q quit • /branchme help │
175
+ ├─────────────────────┬────────────────────────────────────────────────────────┤
176
+ │ Status │ LIFECYCLE │
177
+ │ Workflow │ integrate_branch -> exact local source -> target │
178
+ │▶ Lifecycle │ control target -> checked out + clean │
179
+ │ Worktrees │ conflict -> automatic verified abort │
180
+ │ │ semantic intent -> separate developer workflow │
181
+ │ │ remove_worktree -> separate; branch retained │
182
+ │ │ retire_branch -> leased local ref deletion │
183
+ │ │ retirement guard -> unoccupied + target ancestry │
184
+ │ │ remote effects -> never delete remote refs │
126
185
  ├─────────────────────┴────────────────────────────────────────────────────────┤
127
- │ 2/3 • workflow • inspect → change → fetch/pull/rebase → create → push → PR │
186
+ │ 3/4 • lifecycle • integrate → remove worktree separately → retire local ref │
128
187
  ╰──────────────────────────────────────────────────────────────────────────────╯
129
188
  ```
130
189
 
@@ -137,16 +196,16 @@ Width: 80
137
196
  │ ↑↓ section • q quit • /branchme help │
138
197
  ├─────────────────────┬────────────────────────────────────────────────────────┤
139
198
  │ Status │ WORKTREES │
140
- │ Workflow │ list_worktrees -> inspect inventory │
141
- │▶ Worktrees │ create_worktree -> ready handoff.cwd │
142
- │ │ remove_worktree -> clean linked; branch retained │
199
+ │ Workflow │ list_worktrees -> inspect inventory │
200
+ │ Lifecycle │ create_worktree -> ready handoff.cwd │
201
+ │▶ Worktrees │ remove_worktree -> clean linked; branch retained │
143
202
  │ │ │
144
203
  │ │ │
145
204
  │ │ │
146
205
  │ │ │
147
206
  │ │ │
148
207
  ├─────────────────────┴────────────────────────────────────────────────────────┤
149
- │ 3/3 • worktrees • create → handoff cwd → next session • remove retains branch│
208
+ │ 4/4 • worktrees • create → handoff cwd → next session • remove retains branch│
150
209
  ╰──────────────────────────────────────────────────────────────────────────────╯
151
210
  ```
152
211
 
@@ -160,15 +219,15 @@ Width: 112
160
219
  ├──────────────────────┬───────────────────────────────────────────────────────────────────────┤
161
220
  │▶ Status │ STATUS │
162
221
  │ Workflow │ Current branch: main │
163
- │ Worktrees │ GitHub repository: senad-d/BranchMe │
164
- │ │ GitHub token: not set │
222
+ │ Lifecycle │ GitHub repository: senad-d/BranchMe │
223
+ │ Worktrees │ GitHub token: not set │
165
224
  │ │ │
166
225
  │ │ │
167
226
  │ │ │
168
227
  │ │ │
169
228
  │ │ │
170
229
  ├──────────────────────┴───────────────────────────────────────────────────────────────────────┤
171
- │ 1/3 • status • current repository only • tools perform actions │
230
+ │ 1/4 • status • current repository only • tools perform actions │
172
231
  ╰──────────────────────────────────────────────────────────────────────────────────────────────╯
173
232
  ```
174
233
 
@@ -191,7 +250,7 @@ Width: 50
191
250
  │ │
192
251
  │ │
193
252
  ├────────────────────────────────────────────────┤
194
- │ 1/3 • warning • Unable to resolve a GitHub rep…│
253
+ │ 1/4 • warning • Unable to resolve a GitHub rep…│
195
254
  ╰────────────────────────────────────────────────╯
196
255
  ```
197
256
 
@@ -205,15 +264,15 @@ Width: 80
205
264
  ├─────────────────────┬────────────────────────────────────────────────────────┤
206
265
  │▶ Status │ STATUS │
207
266
  │ Workflow │ Current branch: main │
208
- │ Worktrees │ GitHub repository: warning: Repository boundary misma…│
209
- │ │ GitHub token: present │
267
+ │ Lifecycle │ GitHub repository: warning: Repository boundary misma…│
268
+ │ Worktrees │ GitHub token: present │
210
269
  │ │ │
211
270
  │ │ │
212
271
  │ │ │
213
272
  │ │ │
214
273
  │ │ │
215
274
  ├─────────────────────┴────────────────────────────────────────────────────────┤
216
- │ 1/3 • warning • Repository boundary mismatch: local origin resolves to senad…│
275
+ │ 1/4 • warning • Repository boundary mismatch: local origin resolves to senad…│
217
276
  ╰──────────────────────────────────────────────────────────────────────────────╯
218
277
  ```
219
278
 
@@ -227,15 +286,15 @@ Width: 72
227
286
  ├───────────────────┬──────────────────────────────────────────────────┤
228
287
  │▶ Status │ STATUS │
229
288
  │ Workflow │ Current branch: main │
230
- │ Worktrees │ GitHub repository: senad-d/branchme │
231
- │ │ GitHub token: warning: Unable to read .env…│
289
+ │ Lifecycle │ GitHub repository: senad-d/branchme │
290
+ │ Worktrees │ GitHub token: warning: Unable to read .env…│
232
291
  │ │ │
233
292
  │ │ │
234
293
  │ │ │
235
294
  │ │ │
236
295
  │ │ │
237
296
  ├───────────────────┴──────────────────────────────────────────────────┤
238
- │ 1/3 • warning • Unable to read .env file for GitHub token fallback: …│
297
+ │ 1/4 • warning • Unable to read .env file for GitHub token fallback: …│
239
298
  ╰──────────────────────────────────────────────────────────────────────╯
240
299
  ```
241
300
 
@@ -249,14 +308,14 @@ Width: 72
249
308
  ├───────────────────┬──────────────────────────────────────────────────┤
250
309
  │▶ Status │ STATUS │
251
310
  │ Workflow │ Current branch: feature/super-long-branch-na…│
252
- │ Worktrees │ GitHub repository: very-long-owner-name/very-lo…│
253
- │ │ GitHub token: present │
311
+ │ Lifecycle │ GitHub repository: very-long-owner-name/very-lo…│
312
+ │ Worktrees │ GitHub token: present │
254
313
  │ │ │
255
314
  │ │ │
256
315
  │ │ │
257
316
  │ │ │
258
317
  │ │ │
259
318
  ├───────────────────┴──────────────────────────────────────────────────┤
260
- │ 1/3 • warning • This deliberately long status note is captured to de…│
319
+ │ 1/4 • warning • This deliberately long status note is captured to de…│
261
320
  ╰──────────────────────────────────────────────────────────────────────╯
262
321
  ```
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@senad-d/branchme",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
- "description": "Pi extension for verified current-repository Git branch, worktree, push, and pull request workflows.",
5
+ "description": "Pi extension for verified current-repository Git branch, worktree, integration, retirement, push, and pull request workflows.",
6
6
  "license": "MIT",
7
7
  "author": "Senad Dizdarević <112484166+senad-d@users.noreply.github.com>",
8
8
  "repository": {
@@ -40,13 +40,27 @@ export function getBranchMeHelpText(): string {
40
40
  "7. `push_branch` — push the current branch.",
41
41
  "8. `pull_request` — open a PR after `push_branch` completes and GitHub sees the branches.",
42
42
  "",
43
+ "## Branch integration",
44
+ "",
45
+ "- `integrate_branch` — integrate an exact local source into an exact local target; the clean control worktree must already have the target checked out.",
46
+ "- It never fetches or pushes; a conflict is automatically aborted after paths are captured and restoration is verified.",
47
+ "- BranchMe does not resolve semantic conflicts; handle them in a separate developer workflow.",
48
+ "",
49
+ "## Branch retirement",
50
+ "",
51
+ "- `remove_worktree` and `retire_branch` are separate: removal retains the branch; retirement later deletes only the exact local branch ref.",
52
+ "- `retire_branch` requires the exact local branch, its full expected `HEAD`, and an exact local target for ancestry verification.",
53
+ "- Any registered worktree occupancy rejects retirement; remove an occupying linked worktree separately first.",
54
+ "- Merged retirement uses `force: false`; unmerged retirement requires explicit authorization with `force: true` and may make commits unreachable.",
55
+ "- It never directly deletes a remote or remote-tracking branch.",
56
+ "",
43
57
  "## Worktree handoff",
44
58
  "",
45
59
  "- `list_worktrees` — inspect the main and linked worktrees in the current repository.",
46
60
  "- `create_worktree` — create a linked worktree and return a ready handoff with an absolute `handoff.cwd`.",
47
61
  "- A separate orchestrator starts the next Pi session or subagent in `handoff.cwd`.",
48
62
  "- `remove_worktree` — remove a verified clean linked worktree while retaining its local branch.",
49
- "- BranchMe does not change cwd, start Pi, copy `.env`, or remove branches automatically.",
63
+ "- BranchMe does not change cwd, start Pi, or copy `.env`; `remove_worktree` never removes its retained branch automatically.",
50
64
  "",
51
65
  "## Requirements",
52
66
  "",
@@ -56,7 +70,7 @@ export function getBranchMeHelpText(): string {
56
70
  "- `fetch_branch`, `pull_branch`, and `rebase_branch` require a configured upstream.",
57
71
  "- `pull_branch` and `rebase_branch` require a clean working tree.",
58
72
  "- `rebase_branch` rewrites local commits only when explicitly requested and auto-aborts on failure.",
59
- "- BranchMe never stages, creates user-authored commits, force-pushes, or creates merge commits.",
73
+ "- BranchMe never stages, creates user-authored commits, or force-pushes.",
60
74
  ].join("\n");
61
75
  }
62
76
 
package/src/constants.ts CHANGED
@@ -12,6 +12,8 @@ export const PULL_REQUEST_TOOL_NAME = "pull_request";
12
12
  export const LIST_WORKTREES_TOOL_NAME = "list_worktrees";
13
13
  export const CREATE_WORKTREE_TOOL_NAME = "create_worktree";
14
14
  export const REMOVE_WORKTREE_TOOL_NAME = "remove_worktree";
15
+ export const INTEGRATE_BRANCH_TOOL_NAME = "integrate_branch";
16
+ export const RETIRE_BRANCH_TOOL_NAME = "retire_branch";
15
17
  export const PULL_REQUEST_AUTOFILL_ENV_NAME = "BRANCHME_PR_AUTOFILL";
16
18
 
17
19
  export const BRANCHME_TOOL_NAMES = [
@@ -21,6 +23,8 @@ export const BRANCHME_TOOL_NAMES = [
21
23
  FETCH_BRANCH_TOOL_NAME,
22
24
  PULL_BRANCH_TOOL_NAME,
23
25
  REBASE_BRANCH_TOOL_NAME,
26
+ INTEGRATE_BRANCH_TOOL_NAME,
27
+ RETIRE_BRANCH_TOOL_NAME,
24
28
  PUSH_BRANCH_TOOL_NAME,
25
29
  PULL_REQUEST_TOOL_NAME,
26
30
  LIST_WORKTREES_TOOL_NAME,
@@ -35,6 +39,8 @@ export const GIT_PULL_TIMEOUT_MS = 120_000;
35
39
  export const GIT_REBASE_TIMEOUT_MS = 120_000;
36
40
  export const GIT_PUSH_TIMEOUT_MS = 120_000;
37
41
  export const GIT_WORKTREE_MUTATION_TIMEOUT_MS = 120_000;
42
+ export const GIT_INTEGRATION_TIMEOUT_MS = 120_000;
43
+ export const GIT_RETIREMENT_MUTATION_TIMEOUT_MS = 30_000;
38
44
  export const GITHUB_RELATED_PR_TIMEOUT_MS = 4_000;
39
45
 
40
46
  export const GIT_CONTEXT_RECENT_COMMIT_LIMIT = 5;
@@ -48,6 +54,11 @@ export const GIT_WORKTREE_ENTRY_LIMIT = 100;
48
54
  export const GIT_WORKTREE_PATH_LIMIT_CHARS = 4_096;
49
55
  export const GIT_WORKTREE_REASON_LIMIT_CHARS = 512;
50
56
  export const GIT_WORKTREE_SUMMARY_LIMIT_CHARS = 4_000;
57
+ export const GIT_INTEGRATION_CONFLICT_RAW_OUTPUT_LIMIT_BYTES = 128 * 1024;
58
+ export const GIT_INTEGRATION_CONFLICT_ENTRY_LIMIT = 100;
59
+ export const GIT_INTEGRATION_CONFLICT_PATH_LIMIT_CHARS = 4_096;
60
+ export const GIT_INTEGRATION_SUMMARY_LIMIT_CHARS = 4_000;
61
+ export const GIT_RETIREMENT_SUMMARY_LIMIT_CHARS = 4_000;
51
62
 
52
63
  export const GITHUB_API_BASE_URL = "https://api.github.com";
53
64
  export const GITHUB_API_VERSION = "2022-11-28";