@senad-d/branchme 0.1.6 → 0.1.8
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/.env.example +6 -2
- package/CHANGELOG.md +9 -5
- package/README.md +96 -19
- package/SECURITY.md +25 -7
- package/docs/PROJECT_DEFINITION_BRIEF.md +112 -77
- package/docs/SMOKE_TEST.md +21 -11
- package/docs/STRUCTURE.md +22 -17
- package/docs/TUI_CAPTURE.md +53 -22
- package/package.json +11 -7
- package/src/commands/branchme-command.ts +10 -1
- package/src/constants.ts +15 -0
- package/src/git-context.ts +22 -2
- package/src/git.ts +1124 -4
- package/src/github.ts +76 -27
- package/src/tools/branchme-tools.ts +340 -21
- package/src/types.ts +120 -0
- package/src/ui/branchme-panel.ts +14 -3
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Project Definition Brief
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Originally approved on 2026-06-30. Updated to describe the implemented `0.1.8` package.
|
|
4
4
|
|
|
5
|
-
## 1. Bootstrap
|
|
5
|
+
## 1. Bootstrap history
|
|
6
6
|
|
|
7
7
|
- Template source: `/Users/senad/Documents/Code/Moj_git/pi-tmp`
|
|
8
8
|
- Target directory: `/Users/senad/Documents/Code/Moj_git/pi-branchme`
|
|
9
|
-
- Copy status: copied; target
|
|
9
|
+
- Copy status: copied; the target's existing `.git/` and `.pi/` directories were preserved and excluded from the copy.
|
|
10
10
|
|
|
11
11
|
## 2. Project identity
|
|
12
12
|
|
|
@@ -14,101 +14,136 @@ Approved on 2026-06-30.
|
|
|
14
14
|
- Display name: `BranchMe`
|
|
15
15
|
- Exported extension function: `branchMeExtension`
|
|
16
16
|
- Repository URL: `https://github.com/senad-d/branchme`
|
|
17
|
-
- One-sentence pitch:
|
|
17
|
+
- One-sentence pitch: Verified current-repository Pi tools for branch, linked-worktree, push, and GitHub pull request workflows.
|
|
18
|
+
- Tool count: eleven strict agent-callable tools.
|
|
18
19
|
|
|
19
20
|
## 3. Users and use cases
|
|
20
21
|
|
|
21
|
-
- Primary users: Pi users and CI/GitHub Actions workflows.
|
|
22
|
+
- Primary users: Pi users, specialized Git subagents, orchestrators, and CI/GitHub Actions workflows.
|
|
22
23
|
- Primary use cases:
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- Create and
|
|
24
|
+
- Inspect bounded current-repository branch, upstream, working-tree, related-PR, and recent-commit state.
|
|
25
|
+
- List the current repository's main and linked worktrees explicitly.
|
|
26
|
+
- Create and verify a linked worktree for a new branch from current `HEAD` or an unoccupied existing local branch.
|
|
27
|
+
- Return an exact, absolute, machine-readable worktree handoff for a caller-managed separate Pi session or subagent.
|
|
28
|
+
- Remove an exact verified linked worktree only when it is clean and contains no ignored entries, while retaining its local branch.
|
|
29
|
+
- Switch to an existing local branch after a clean-worktree preflight or create a new branch from current `HEAD`.
|
|
30
|
+
- Fetch a configured upstream tracking ref, fast-forward the current branch, or explicitly rebase it.
|
|
26
31
|
- Push the current branch to its configured upstream, or publish it to `origin` when no upstream exists.
|
|
27
|
-
- Create a
|
|
32
|
+
- Create a pull request in the resolved current GitHub repository through the REST API.
|
|
28
33
|
- Non-goals:
|
|
29
|
-
- No
|
|
30
|
-
- No
|
|
31
|
-
- No cross-repository
|
|
32
|
-
- No labels, reviewers, projects, or issue
|
|
34
|
+
- No staging, direct working-tree edits, user-authored commits, diff generation, stashing, resets, force pushes, or merge commits.
|
|
35
|
+
- No automatic Pi cwd changes, process/session creation, or copying of `.env` and other ignored/untracked files into linked worktrees.
|
|
36
|
+
- No GitHub CLI dependency or cross-repository pull requests.
|
|
37
|
+
- No labels, reviewers, projects, or issue-linking behavior.
|
|
33
38
|
|
|
34
39
|
## 4. Pi integration surface
|
|
35
40
|
|
|
36
41
|
| Surface | Name | Purpose | Notes |
|
|
37
42
|
| --- | --- | --- | --- |
|
|
38
|
-
| Command | `/branchme` |
|
|
39
|
-
| Command | `/branchme help` |
|
|
40
|
-
| Tool | `branch_status` |
|
|
41
|
-
| Tool | `
|
|
42
|
-
| Tool | `
|
|
43
|
-
| Tool | `
|
|
44
|
-
| Tool | `
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
43
|
+
| Command | `/branchme` | Compact TUI status and workflow panel | Informational; no Git or GitHub mutations |
|
|
44
|
+
| Command | `/branchme help` | Runtime requirements and workflow guidance | Informational; no actions |
|
|
45
|
+
| Tool | `branch_status` | Refresh bounded current-worktree Git and related-PR context | Read-only |
|
|
46
|
+
| Tool | `list_worktrees` | List bounded main/linked worktree inventory | Read-only and explicit |
|
|
47
|
+
| Tool | `create_worktree` | Create and verify a linked worktree | Exact absolute handoff cwd; new/existing local branch modes only |
|
|
48
|
+
| Tool | `remove_worktree` | Remove an exact verified clean linked worktree | Force-free; ignored entries block removal; branch retained |
|
|
49
|
+
| Tool | `change_branch` | Switch to an existing local branch | Rejects dirty worktrees |
|
|
50
|
+
| Tool | `fetch_branch` | Refresh the current branch's configured tracking ref | Explicit fetch refspec; no checkout change |
|
|
51
|
+
| Tool | `pull_branch` | Fast-forward the clean current branch | No rebase, merge commit, or autostash |
|
|
52
|
+
| Tool | `rebase_branch` | Rebase the clean current branch onto its upstream | Explicit history rewrite; automatic abort attempt on failure |
|
|
53
|
+
| Tool | `create_branch` | Create and check out a new branch from current `HEAD` | Fails when invalid or already present |
|
|
54
|
+
| Tool | `push_branch` | Push or publish the current branch | Explicit remote/refspec behavior |
|
|
55
|
+
| Tool | `pull_request` | Preflight branch state and create a GitHub pull request | Current repository only; autofill is opt-in |
|
|
56
|
+
| Event | `before_agent_start` | Append fresh bounded Git context to the system prompt | Read-only collection; no worktree inventory |
|
|
57
|
+
| UI | TUI panel | Compact BranchMe workflow/configuration view | Responsive and width-bounded |
|
|
58
|
+
| Resource | none | No bundled skills, prompts, or themes | Package remains extension-focused |
|
|
48
59
|
|
|
49
60
|
## 5. Architecture
|
|
50
61
|
|
|
51
|
-
-
|
|
62
|
+
- Implemented source layout:
|
|
52
63
|
- `src/extension.ts`
|
|
53
64
|
- `src/constants.ts`
|
|
65
|
+
- `src/types.ts`
|
|
66
|
+
- `src/redaction.ts`
|
|
67
|
+
- `src/git-context.ts`
|
|
54
68
|
- `src/commands/branchme-command.ts`
|
|
55
69
|
- `src/tools/branchme-tools.ts`
|
|
56
70
|
- `src/git.ts`
|
|
57
71
|
- `src/github.ts`
|
|
58
|
-
- `src/
|
|
72
|
+
- `src/ui/branchme-panel.ts`
|
|
59
73
|
- Module boundaries:
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
74
|
+
- The extension entry point registers the informational command, eleven tools, and automatic context hook.
|
|
75
|
+
- The context module owns bounded read-only collection, prompt formatting, and the `before_agent_start` hook.
|
|
76
|
+
- The command and UI modules own mode-safe informational help/status behavior and never invoke mutations.
|
|
77
|
+
- The tools module owns strict TypeBox schemas, descriptions, prompt metadata, bounded display content, and serializable result details.
|
|
78
|
+
- The Git helper owns argv-style current-repository inspection, branch/upstream workflows, worktree parsing/path validation/create/remove verification, and same-repository mutation serialization.
|
|
79
|
+
- The GitHub helper owns repository resolution, token/autofill configuration, related-PR lookup, branch visibility and commit preflight, and pull request REST calls.
|
|
80
|
+
- The redaction module owns shared credential redaction for display and prompt-bound metadata.
|
|
81
|
+
- Shared public details remain JSON-serializable and contain no runtime objects or abort signals.
|
|
64
82
|
- Dependencies:
|
|
65
|
-
-
|
|
66
|
-
- `@earendil-works/pi-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
- `@earendil-works/pi-coding-agent`, `@earendil-works/pi-ai`, `@earendil-works/pi-tui`, and `typebox` are direct peer/development dependencies; Pi peers use `"*"` for host compatibility.
|
|
84
|
+
- `@earendil-works/pi-ai` supplies `StringEnum` for the strict `create_worktree.branchMode` schema.
|
|
85
|
+
- `@earendil-works/pi-tui` supplies panel width and key utilities.
|
|
86
|
+
- Node 22 supplies `fetch`; BranchMe does not depend on Octokit or GitHub CLI.
|
|
87
|
+
|
|
88
|
+
## 6. Configuration, state, and filesystem boundary
|
|
89
|
+
|
|
90
|
+
- Config source: no separate BranchMe config file. `GITHUB_TOKEN`, `GH_TOKEN`, and `BRANCHME_PR_AUTOFILL` use process-environment values first and may fall back to supported keys in a hardened regular `.env` file at the verified Git root.
|
|
91
|
+
- Session state: no persisted BranchMe state. Tool calls return serializable details, and mutation/PR coordination is in-memory only.
|
|
92
|
+
- Active-checkout mutations: explicit branch switching, creation, pull, and rebase operations can update Git metadata and working-tree files through Git. Push and fetch operations can update remote or remote-tracking refs through Git.
|
|
93
|
+
- Linked-worktree mutations: `create_worktree` can create a checkout directory outside the active checkout after canonical destination and repository-boundary validation. `remove_worktree` can recursively remove only an exact verified linked-worktree directory after clean and ignored-entry preflights.
|
|
94
|
+
- BranchMe does not directly edit project files, stage content, create user-authored commits, copy local-only files into new worktrees, delete the retained branch during removal, or mutate worktrees through slash commands.
|
|
95
|
+
|
|
96
|
+
## 7. Worktree handoff contract
|
|
97
|
+
|
|
98
|
+
- `list_worktrees` reads bounded NUL-delimited porcelain inventory and keeps worktree discovery out of automatic active-worktree context.
|
|
99
|
+
- `create_worktree` requires an explicitly approved absolute destination whose immediate parent exists. It rejects existing destinations and locations inside registered worktrees or the repository's common Git directory.
|
|
100
|
+
- New mode creates a local branch from current `HEAD` only. Existing mode accepts only an existing local branch not checked out in another worktree; no remote branch is inferred.
|
|
101
|
+
- Before mutation, canonical cwd and branch identity must fit documented limits and remain unchanged by redaction, escaping, Unicode handling, or truncation.
|
|
102
|
+
- Successful creation verifies canonical path, local branch, full `HEAD`, and clean state, then returns the exact canonical absolute cwd and local branch in `handoff: { cwd, branch, head, ready: true, summary }`.
|
|
103
|
+
- Removal accepts only an exact fresh inventory match that is linked, present, unlocked, non-prunable, non-bare, branch-attached, and neither main nor current.
|
|
104
|
+
- Staged, unstaged, untracked, unmerged, and ignored entries all block removal. The bounded ignored-entry preflight does not disclose ignored paths or contents.
|
|
105
|
+
- Successful force-free removal verifies that the worktree entry is absent and the retained local branch still points to its captured commit, then returns `handoff: { cwd: null, branch: <exact retained branch>, head, ready: false, summary }`.
|
|
106
|
+
- BranchMe never returns a ready handoff containing `[REDACTED]`, escaped control sequences, or a BranchMe-introduced truncation ellipsis in machine-readable cwd or branch identity fields.
|
|
107
|
+
|
|
108
|
+
## 8. Security and privacy
|
|
109
|
+
|
|
110
|
+
- Every Git command uses `pi.exec("git", args, { cwd, signal, timeout })` with an argv array rather than shell interpolation.
|
|
111
|
+
- Paths, branch names, Git output, commit subjects, and pull request metadata are treated as untrusted. Display and prompt surfaces are escaped, redacted, and bounded separately from prevalidated exact handoff identities.
|
|
112
|
+
- Worktree paths are canonicalized and checked against a fresh current-repository inventory or the common Git directory before mutation. User-supplied removal paths are never passed directly to Git.
|
|
113
|
+
- Worktree removal has no force path and rejects dirty, ignored-entry-containing, detached, locked, prunable/missing, bare, main, current, and foreign entries before mutation.
|
|
114
|
+
- Worktree tools expose no force, move, prune, repair, lock, unlock, detached, orphan, remote-inference, arbitrary refspec, or arbitrary start-point controls. Git's incomplete submodule worktree support is not bypassed with force cleanup.
|
|
115
|
+
- Automatic context and `branch_status` never collect diffs or file contents. Related-PR lookup may make a bounded authenticated GitHub request only when credentials and repository identity resolve; there is no unauthenticated fallback.
|
|
116
|
+
- `pull_request` uses the resolved current GitHub repository, preflights local and GitHub branch identity, and rejects cross-repository head refs. Git fetch/pull/push use the user's normal Git credentials.
|
|
117
|
+
- Tokens are resolved from supported process or verified-root `.env` keys and redacted from prompts, errors, content, and details. BranchMe collects no telemetry.
|
|
118
|
+
- Creation and removal require explicit user intent and an exact approved path. BranchMe does not silently infer worktree destinations or start a separate agent session.
|
|
119
|
+
|
|
120
|
+
## 9. Documentation and packaging
|
|
121
|
+
|
|
122
|
+
- `README.md` is the primary public workflow and tool reference.
|
|
123
|
+
- `SECURITY.md` documents local filesystem, Git, GitHub, credential, and prompt-insertion boundaries.
|
|
124
|
+
- `docs/STRUCTURE.md` describes the implemented source and test layout.
|
|
125
|
+
- `docs/SMOKE_TEST.md` records isolated checkout, handoff, and installed-package smoke behavior.
|
|
126
|
+
- `CHANGELOG.md` tracks the active `0.1.8` unreleased changes.
|
|
127
|
+
- npm distribution uses package `@senad-d/branchme`; package-content checks exclude private specs, credentials, generated files, caches, and local state.
|
|
128
|
+
|
|
129
|
+
## 10. Validation plan
|
|
94
130
|
|
|
95
131
|
- Typecheck: `npm run typecheck`
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- PR tool requires all PR fields explicitly.
|
|
132
|
+
- Formatting and documentation checks: `npm run format:check`
|
|
133
|
+
- Unit and isolated real-Git integration tests: `npm run test`
|
|
134
|
+
- Checkout Pi runtime/context smoke: `npm run smoke:pi`
|
|
135
|
+
- Isolated specialized-agent handoff smoke: `npm run smoke:worktree-handoff`
|
|
136
|
+
- Package dry-run/content boundary: `npm run check:pack`
|
|
137
|
+
- Complete checkout validation: `npm run validate`
|
|
138
|
+
- Installed-artifact smoke: `npm run smoke:pi:packed`
|
|
139
|
+
- Canonical local and automated release gate: `npm run release:check`
|
|
140
|
+
|
|
141
|
+
## 11. Current decisions
|
|
142
|
+
|
|
143
|
+
- Slash commands remain informational; tools perform all Git and GitHub actions.
|
|
144
|
+
- Automatic context remains focused on the active worktree; inventory is available only through `list_worktrees`.
|
|
145
|
+
- `create_worktree` returns a verified target for a caller-managed session but does not change cwd or create processes.
|
|
146
|
+
- Worktree removal remains force-free, blocks ignored entries, and preserves the local branch.
|
|
147
|
+
- `push_branch` uses `origin` only when the current branch has no configured upstream.
|
|
148
|
+
- `pull_request` infers owner/repository from the current checkout or matching `GITHUB_REPOSITORY`, never accepts owner/repository tool inputs, and requires local branch refs with the head matching GitHub.
|
|
149
|
+
- Pull request fields remain explicit unless `BRANCHME_PR_AUTOFILL=true`; explicit values always take precedence.
|
package/docs/SMOKE_TEST.md
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# BranchMe Smoke Test Notes
|
|
2
2
|
|
|
3
|
-
Date: 2026-
|
|
3
|
+
Date: 2026-08-23
|
|
4
4
|
|
|
5
5
|
Validated from the repository checkout with no discovered extensions enabled.
|
|
6
6
|
|
|
7
7
|
## Commands
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm run
|
|
10
|
+
npm run typecheck
|
|
11
|
+
npm run format:check
|
|
12
|
+
npm run test
|
|
11
13
|
npm run smoke:pi
|
|
12
|
-
npm run smoke:pi:packed
|
|
13
14
|
npm run check:pack
|
|
15
|
+
npm run smoke:worktree-handoff
|
|
16
|
+
npm run validate
|
|
17
|
+
npm run smoke:pi:packed
|
|
18
|
+
npm run release:check
|
|
14
19
|
printf '/branchme help\n/quit\n' | pi --no-extensions -e .
|
|
15
20
|
pi --no-extensions -e .
|
|
16
21
|
```
|
|
@@ -18,24 +23,29 @@ pi --no-extensions -e .
|
|
|
18
23
|
## Automated smoke behavior
|
|
19
24
|
|
|
20
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`.
|
|
21
|
-
- The temporary command verifier calls `pi.getAllTools()` after BranchMe loads and confirms `branch_status`, `change_branch`, `fetch_branch`, `
|
|
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.
|
|
22
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.
|
|
30
|
+
- 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
|
+
- `npm run validate` includes the isolated handoff smoke after package-content checks.
|
|
23
32
|
- The checkout command smoke accepts either `/branchme help` text or the read-only BranchMe status fallback as equivalent non-mutating command output.
|
|
24
33
|
- `npm run smoke:pi:packed` creates an npm tarball under a temporary directory, installs that tarball into a separate temporary package with `npm install --omit=dev`, and runs pi against the installed package instead of the source checkout.
|
|
25
|
-
- `npm run
|
|
26
|
-
- Both smoke runs disable discovered extensions, skills, prompt templates, themes, context files, persistent sessions, telemetry, startup network checks, and GitHub token environment variables.
|
|
27
|
-
- Both smoke runs are credential-free, allow documented credential variable names in help text, reject credential value patterns, do not call BranchMe mutation tools, and do not contact GitHub.
|
|
28
|
-
- Pi's documented `getAllTools()` metadata
|
|
34
|
+
- `npm run release:check` is the canonical release gate: it runs `npm run validate` and then `npm run smoke:pi:packed`. Both `node scripts/publish-npm.mjs` and the GitHub `Publish to npm` workflow run it before npm publication; a packed install/load failure stops publication and the workflow's Git tag step. Everyday `npm run validate` keeps the faster checkout smoke.
|
|
35
|
+
- Both Pi smoke runs disable discovered extensions, skills, prompt templates, themes, context files, persistent sessions, telemetry, startup network checks, and GitHub token environment variables.
|
|
36
|
+
- Both Pi smoke runs are credential-free, allow documented credential variable names in help text, reject credential value patterns, do not call BranchMe mutation or remote tools, and do not contact GitHub.
|
|
37
|
+
- Pi's documented `getAllTools()` metadata exposes parameter schemas, descriptions, prompt guidelines, and source metadata, while command context exposes active `promptSnippet` values through `getSystemPromptOptions().toolSnippets`; the runtime verifier checks both surfaces. The deterministic local smoke model exercises `branch_status` through Pi's normal model tool-call loop without provider or GitHub network access.
|
|
29
38
|
- Set `BRANCHME_SKIP_PI_SMOKE=1` to skip intentionally, or `BRANCHME_PI_BIN=/path/to/pi` to test a specific Pi binary for the checkout smoke.
|
|
30
39
|
- If no Pi binary is available, the checkout smoke script prints an explicit skip message; default CI installs the Pi dev dependency, so `npm run validate` exercises the real Pi loading path.
|
|
31
40
|
|
|
32
41
|
## Result
|
|
33
42
|
|
|
34
|
-
- `npm run validate` passed.
|
|
35
|
-
- `npm run smoke:
|
|
43
|
+
- `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.
|
|
36
46
|
- 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.
|
|
37
47
|
- `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.
|
|
38
|
-
- `npm run check:pack` confirmed the package contents are limited to public docs, images, source, license, package metadata, `.env.example`, and `tsconfig.json
|
|
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.
|
|
39
49
|
- The isolated Pi smoke command loaded BranchMe and displayed BranchMe help or status output instead of template behavior.
|
|
40
50
|
- The bare `pi --no-extensions -e .` smoke command exited cleanly in this non-interactive validation environment.
|
|
41
51
|
- 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
|
|
3
|
+
BranchMe is a TypeScript Pi extension package for current-repository Git branch, verified linked-worktree, push, and GitHub pull request workflows.
|
|
4
4
|
|
|
5
5
|
## Source layout
|
|
6
6
|
|
|
@@ -13,8 +13,8 @@ src/
|
|
|
13
13
|
├── commands/
|
|
14
14
|
│ └── branchme-command.ts # /branchme status/help command; informational only
|
|
15
15
|
├── tools/
|
|
16
|
-
│ └── branchme-tools.ts # registration for
|
|
17
|
-
├── git.ts # argv-style
|
|
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
|
|
18
18
|
├── github.ts # GitHub repo resolution, env/.env tokens, branch preflight, REST calls, redaction
|
|
19
19
|
└── ui/
|
|
20
20
|
└── branchme-panel.ts # compact /branchme status panel renderer
|
|
@@ -22,12 +22,12 @@ src/
|
|
|
22
22
|
|
|
23
23
|
## Module boundaries
|
|
24
24
|
|
|
25
|
-
1. `src/extension.ts` stays small and registers the command,
|
|
25
|
+
1. `src/extension.ts` stays small and registers the command, eleven tools, and one `before_agent_start` context hook.
|
|
26
26
|
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
27
|
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 TypeBox schemas, prompt metadata, tool content, and safe structured details. `branch_status` delegates to the shared context collector
|
|
29
|
-
5. `src/git.ts` owns current-repository
|
|
30
|
-
6. `src/github.ts` owns GitHub `owner/repo` parsing, repository boundary checks, `GITHUB_TOKEN`/`GH_TOKEN` process-env
|
|
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
31
|
7. `src/redaction.ts` owns shared credential redaction for Git, GitHub, and prompt-bound metadata.
|
|
32
32
|
8. `src/types.ts` keeps serializable details shared by helpers, context, and tools.
|
|
33
33
|
9. `src/ui/branchme-panel.ts` renders a compact status panel and clips lines to terminal width.
|
|
@@ -36,11 +36,12 @@ src/
|
|
|
36
36
|
|
|
37
37
|
- No long-lived processes, watchers, timers, sockets, or background jobs start in the extension factory.
|
|
38
38
|
- 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
|
|
40
|
-
- Every tool uses a strict TypeBox object schema with `additionalProperties: false`.
|
|
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`.
|
|
41
41
|
- 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
|
|
43
|
-
-
|
|
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.
|
|
43
|
+
- 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
|
+
- Tool details avoid token values, abort signals, runtime objects, and unbounded raw command/API output.
|
|
44
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.
|
|
45
46
|
- Pi core packages, including `@earendil-works/pi-tui` for key/width utilities, remain in `peerDependencies` with `"*"`.
|
|
46
47
|
|
|
@@ -55,10 +56,13 @@ src/
|
|
|
55
56
|
- `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.
|
|
56
57
|
- `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.
|
|
57
58
|
- `create_branch` mutates local branch/HEAD only with `git switch -c`.
|
|
59
|
+
- `list_worktrees` reads a bounded `git worktree list --porcelain -z` inventory and remains explicit rather than expanding automatic active-worktree context.
|
|
60
|
+
- `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.
|
|
61
|
+
- `remove_worktree` resolves an exact fresh current-repository inventory match, rejects main/current/dirty/ignored-entry-containing/detached/locked/prunable/missing/bare entries, runs a bounded removal-specific ignored-entry scan, performs force-free removal with the verified path, and confirms that the local branch remains at the same commit.
|
|
58
62
|
- `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.
|
|
59
|
-
- `pull_request` requires `headBranch` and `baseBranch` to 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
|
|
60
|
-
- `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.
|
|
61
|
-
- BranchMe does not force checkout, stash, stage, create user-authored commits, reset, force-push, create merge commits, directly edit files, read
|
|
63
|
+
- `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
|
+
- `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.
|
|
62
66
|
|
|
63
67
|
## Documentation
|
|
64
68
|
|
|
@@ -73,11 +77,12 @@ src/
|
|
|
73
77
|
test/
|
|
74
78
|
├── command.test.mjs # /branchme parsing, help, fallback, panel width
|
|
75
79
|
├── git-context.test.mjs # collection, prompt hook, formatting, safety, and output bounds
|
|
76
|
-
├── git.test.mjs #
|
|
77
|
-
├── git-integration.test.mjs # isolated real-
|
|
80
|
+
├── 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
|
|
78
82
|
├── github.test.mjs # GitHub parsing, token resolution, related-PR lookup, redaction
|
|
79
83
|
├── preparation.test.mjs # package/docs/source metadata checks
|
|
80
|
-
├──
|
|
84
|
+
├── schema-validation.test.mjs # strict TypeBox schema validation, including worktree fields/modes
|
|
85
|
+
├── tools.test.mjs # extension registration, prompt metadata, shared refresh, tool behavior
|
|
81
86
|
└── tui-capture.test.mjs # generated text capture for TUI/help visual baselines
|
|
82
87
|
```
|
|
83
88
|
|
package/docs/TUI_CAPTURE.md
CHANGED
|
@@ -16,7 +16,7 @@ UPDATE_TUI_CAPTURE=1 node --test test/tui-capture.test.mjs
|
|
|
16
16
|
```text
|
|
17
17
|
# BranchMe
|
|
18
18
|
|
|
19
|
-
Current-repository
|
|
19
|
+
Current-repository Git workflow tools for Pi.
|
|
20
20
|
|
|
21
21
|
Commands only show info; BranchMe tools perform actions.
|
|
22
22
|
|
|
@@ -31,10 +31,19 @@ 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
|
+
## Worktree handoff
|
|
35
|
+
|
|
36
|
+
- `list_worktrees` — inspect the main and linked worktrees in the current repository.
|
|
37
|
+
- `create_worktree` — create a linked worktree and return a ready handoff with an absolute `handoff.cwd`.
|
|
38
|
+
- A separate orchestrator starts the next Pi session or subagent in `handoff.cwd`.
|
|
39
|
+
- `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.
|
|
41
|
+
|
|
34
42
|
## Requirements
|
|
35
43
|
|
|
36
44
|
- Run inside a Git repo with `git` available.
|
|
37
45
|
- For PRs: GitHub `origin` and `GITHUB_TOKEN` or `GH_TOKEN` (environment or `.env`).
|
|
46
|
+
- Optional: set `BRANCHME_PR_AUTOFILL=true` in the environment or `.env` to generate omitted PR fields.
|
|
38
47
|
- `fetch_branch`, `pull_branch`, and `rebase_branch` require a configured upstream.
|
|
39
48
|
- `pull_branch` and `rebase_branch` require a clean working tree.
|
|
40
49
|
- `rebase_branch` rewrites local commits only when explicitly requested and auto-aborts on failure.
|
|
@@ -71,7 +80,7 @@ Width: 40
|
|
|
71
80
|
│ │
|
|
72
81
|
│ │
|
|
73
82
|
├──────────────────────────────────────┤
|
|
74
|
-
│ 1/
|
|
83
|
+
│ 1/3 • status • current repository on…│
|
|
75
84
|
╰──────────────────────────────────────╯
|
|
76
85
|
```
|
|
77
86
|
|
|
@@ -85,7 +94,7 @@ Width: 80
|
|
|
85
94
|
├─────────────────────┬────────────────────────────────────────────────────────┤
|
|
86
95
|
│▶ Status │ STATUS │
|
|
87
96
|
│ Workflow │ Current branch: feature/current │
|
|
88
|
-
│
|
|
97
|
+
│ Worktrees │ GitHub repository: senad-d/branchme │
|
|
89
98
|
│ │ GitHub token: present │
|
|
90
99
|
│ │ │
|
|
91
100
|
│ │ │
|
|
@@ -93,7 +102,7 @@ Width: 80
|
|
|
93
102
|
│ │ │
|
|
94
103
|
│ │ │
|
|
95
104
|
├─────────────────────┴────────────────────────────────────────────────────────┤
|
|
96
|
-
│ 1/
|
|
105
|
+
│ 1/3 • status • current repository only • tools perform actions │
|
|
97
106
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
98
107
|
```
|
|
99
108
|
|
|
@@ -106,16 +115,38 @@ Width: 80
|
|
|
106
115
|
│ ↑↓ section • q quit • /branchme help │
|
|
107
116
|
├─────────────────────┬────────────────────────────────────────────────────────┤
|
|
108
117
|
│ Status │ WORKFLOW │
|
|
109
|
-
│▶ Workflow │ branch_status
|
|
110
|
-
│
|
|
111
|
-
│ │ fetch_branch
|
|
112
|
-
│ │ pull_branch
|
|
113
|
-
│ │ rebase_branch
|
|
114
|
-
│ │ create_branch
|
|
115
|
-
│ │ push_branch
|
|
116
|
-
│ │ pull_request
|
|
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 │
|
|
126
|
+
├─────────────────────┴────────────────────────────────────────────────────────┤
|
|
127
|
+
│ 2/3 • workflow • inspect → change → fetch/pull/rebase → create → push → PR │
|
|
128
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Panel: Wide mode: Worktrees selected
|
|
132
|
+
|
|
133
|
+
Width: 80
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
╭ BranchMe ───────────────────────────────────────────────────────── Worktrees ╮
|
|
137
|
+
│ ↑↓ section • q quit • /branchme help │
|
|
138
|
+
├─────────────────────┬────────────────────────────────────────────────────────┤
|
|
139
|
+
│ Status │ WORKTREES │
|
|
140
|
+
│ Workflow │ list_worktrees -> inspect inventory │
|
|
141
|
+
│▶ Worktrees │ create_worktree -> ready handoff.cwd │
|
|
142
|
+
│ │ remove_worktree -> clean linked; branch retained │
|
|
143
|
+
│ │ │
|
|
144
|
+
│ │ │
|
|
145
|
+
│ │ │
|
|
146
|
+
│ │ │
|
|
147
|
+
│ │ │
|
|
117
148
|
├─────────────────────┴────────────────────────────────────────────────────────┤
|
|
118
|
-
│
|
|
149
|
+
│ 3/3 • worktrees • create → handoff cwd → next session • remove retains branch│
|
|
119
150
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
120
151
|
```
|
|
121
152
|
|
|
@@ -129,7 +160,7 @@ Width: 112
|
|
|
129
160
|
├──────────────────────┬───────────────────────────────────────────────────────────────────────┤
|
|
130
161
|
│▶ Status │ STATUS │
|
|
131
162
|
│ Workflow │ Current branch: main │
|
|
132
|
-
│
|
|
163
|
+
│ Worktrees │ GitHub repository: senad-d/BranchMe │
|
|
133
164
|
│ │ GitHub token: not set │
|
|
134
165
|
│ │ │
|
|
135
166
|
│ │ │
|
|
@@ -137,7 +168,7 @@ Width: 112
|
|
|
137
168
|
│ │ │
|
|
138
169
|
│ │ │
|
|
139
170
|
├──────────────────────┴───────────────────────────────────────────────────────────────────────┤
|
|
140
|
-
│ 1/
|
|
171
|
+
│ 1/3 • status • current repository only • tools perform actions │
|
|
141
172
|
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
142
173
|
```
|
|
143
174
|
|
|
@@ -160,7 +191,7 @@ Width: 50
|
|
|
160
191
|
│ │
|
|
161
192
|
│ │
|
|
162
193
|
├────────────────────────────────────────────────┤
|
|
163
|
-
│ 1/
|
|
194
|
+
│ 1/3 • warning • Unable to resolve a GitHub rep…│
|
|
164
195
|
╰────────────────────────────────────────────────╯
|
|
165
196
|
```
|
|
166
197
|
|
|
@@ -174,7 +205,7 @@ Width: 80
|
|
|
174
205
|
├─────────────────────┬────────────────────────────────────────────────────────┤
|
|
175
206
|
│▶ Status │ STATUS │
|
|
176
207
|
│ Workflow │ Current branch: main │
|
|
177
|
-
│
|
|
208
|
+
│ Worktrees │ GitHub repository: warning: Repository boundary misma…│
|
|
178
209
|
│ │ GitHub token: present │
|
|
179
210
|
│ │ │
|
|
180
211
|
│ │ │
|
|
@@ -182,7 +213,7 @@ Width: 80
|
|
|
182
213
|
│ │ │
|
|
183
214
|
│ │ │
|
|
184
215
|
├─────────────────────┴────────────────────────────────────────────────────────┤
|
|
185
|
-
│ 1/
|
|
216
|
+
│ 1/3 • warning • Repository boundary mismatch: local origin resolves to senad…│
|
|
186
217
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
187
218
|
```
|
|
188
219
|
|
|
@@ -196,7 +227,7 @@ Width: 72
|
|
|
196
227
|
├───────────────────┬──────────────────────────────────────────────────┤
|
|
197
228
|
│▶ Status │ STATUS │
|
|
198
229
|
│ Workflow │ Current branch: main │
|
|
199
|
-
│
|
|
230
|
+
│ Worktrees │ GitHub repository: senad-d/branchme │
|
|
200
231
|
│ │ GitHub token: warning: Unable to read .env…│
|
|
201
232
|
│ │ │
|
|
202
233
|
│ │ │
|
|
@@ -204,7 +235,7 @@ Width: 72
|
|
|
204
235
|
│ │ │
|
|
205
236
|
│ │ │
|
|
206
237
|
├───────────────────┴──────────────────────────────────────────────────┤
|
|
207
|
-
│ 1/
|
|
238
|
+
│ 1/3 • warning • Unable to read .env file for GitHub token fallback: …│
|
|
208
239
|
╰──────────────────────────────────────────────────────────────────────╯
|
|
209
240
|
```
|
|
210
241
|
|
|
@@ -218,7 +249,7 @@ Width: 72
|
|
|
218
249
|
├───────────────────┬──────────────────────────────────────────────────┤
|
|
219
250
|
│▶ Status │ STATUS │
|
|
220
251
|
│ Workflow │ Current branch: feature/super-long-branch-na…│
|
|
221
|
-
│
|
|
252
|
+
│ Worktrees │ GitHub repository: very-long-owner-name/very-lo…│
|
|
222
253
|
│ │ GitHub token: present │
|
|
223
254
|
│ │ │
|
|
224
255
|
│ │ │
|
|
@@ -226,6 +257,6 @@ Width: 72
|
|
|
226
257
|
│ │ │
|
|
227
258
|
│ │ │
|
|
228
259
|
├───────────────────┴──────────────────────────────────────────────────┤
|
|
229
|
-
│ 1/
|
|
260
|
+
│ 1/3 • warning • This deliberately long status note is captured to de…│
|
|
230
261
|
╰──────────────────────────────────────────────────────────────────────╯
|
|
231
262
|
```
|