worktree-for-agents 0.0.0-stage → 1.0.1
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/GUIDE.md +45 -0
- package/README.md +179 -2
- package/docs/installation.md +213 -0
- package/docs/mcp-contract.md +78 -0
- package/mcp-server.mjs +54 -0
- package/package.json +37 -4
- package/scripts/postinstall.mjs +11 -0
- package/scripts/python.mjs +69 -0
- package/setup.mjs +208 -0
- package/worktree.mjs +47 -0
- package/worktree.sh +18 -0
- package/worktree_manager/__init__.py +2 -0
- package/worktree_manager/__main__.py +2 -0
- package/worktree_manager/backup_store.py +16 -0
- package/worktree_manager/cli.py +137 -0
- package/worktree_manager/commit_message.py +63 -0
- package/worktree_manager/configuration.py +61 -0
- package/worktree_manager/git_repository.py +72 -0
- package/worktree_manager/lock_manager.py +50 -0
- package/worktree_manager/mcp_contract.py +93 -0
- package/worktree_manager/mcp_operations.py +171 -0
- package/worktree_manager/mcp_server.py +61 -0
- package/worktree_manager/mcp_transport.py +5 -0
- package/worktree_manager/models.py +69 -0
- package/worktree_manager/output.py +11 -0
- package/worktree_manager/recovery_manager.py +114 -0
- package/worktree_manager/state_digest.py +40 -0
- package/worktree_manager/target_direct_adapter.py +197 -0
- package/worktree_manager/transaction_journal.py +36 -0
- package/worktree_manager/worktree_lifecycle.py +274 -0
package/GUIDE.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
You are going to implement a change, run a spec, or start a flow. Do you want to create a worktree to work in isolation?
|
|
2
|
+
|
|
3
|
+
This guide explains how to use the `worktree-for-agents` manager to work
|
|
4
|
+
isolated in a worktree before touching the main branch.
|
|
5
|
+
|
|
6
|
+
## The seven tools
|
|
7
|
+
|
|
8
|
+
The MCP service exposes exactly seven tools:
|
|
9
|
+
|
|
10
|
+
- `worktree_create`: creates a worktree and its branch for a work identifier.
|
|
11
|
+
- `worktree_complete`: merges the worktree into the base branch and removes the worktree.
|
|
12
|
+
- `worktree_cleanup`: removes the worktree and its branch without merging.
|
|
13
|
+
- `worktree_recover`: recovers a pending transaction by its identifier.
|
|
14
|
+
- `worktree_list`: lists the repository worktrees.
|
|
15
|
+
- `worktree_prune`: prunes worktree information that no longer exists.
|
|
16
|
+
- `worktree_status`: shows the status of a work item.
|
|
17
|
+
|
|
18
|
+
All of them require an absolute Git repository path in `repo`.
|
|
19
|
+
|
|
20
|
+
## When to offer the worktree
|
|
21
|
+
|
|
22
|
+
Before working on the main branch, offer to create a worktree with
|
|
23
|
+
`worktree_create`. Work inside the worktree and then merge with
|
|
24
|
+
`worktree_complete`. If the work is discarded, use `worktree_cleanup`.
|
|
25
|
+
|
|
26
|
+
## Launch without bin
|
|
27
|
+
|
|
28
|
+
The package exposes no bin or `npx`. The MCP service is launched with Node and the
|
|
29
|
+
absolute package path:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
node <package>/mcp-server.mjs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The CLI is launched the same way:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
node <package>/worktree.mjs --version
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The installation diagnostics are obtained with:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
node <package>/setup.mjs
|
|
45
|
+
```
|
package/README.md
CHANGED
|
@@ -1,3 +1,180 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Worktree manager
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Standalone manager to create, complete, clean up, and recover worktrees of a
|
|
4
|
+
Git repository. The project does not know FlowTask and does not depend on its paths, legacy
|
|
5
|
+
refs, or coordination mechanisms.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- **Python 3.9 or later.** The entrypoint only checks that `python3` is
|
|
10
|
+
available on the `PATH`; the 3.9 floor is validated by the package postinstall
|
|
11
|
+
and `node <package>/setup.mjs`. The package uses only the standard library.
|
|
12
|
+
- **Git 2.17.0 or later**, for `git worktree remove`, which is the most
|
|
13
|
+
recent command used by the manager. The test baseline is Git 2.54.0. The
|
|
14
|
+
manager does not check the Git version at runtime.
|
|
15
|
+
- **Node 18 or later** for the `mcp-server.mjs`, `worktree.mjs`
|
|
16
|
+
and `setup.mjs` launchers.
|
|
17
|
+
- **`bash`** to run the test suites.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
The manager is distributed as the npm package `worktree-for-agents`, version
|
|
22
|
+
`1.0.0`, with no bin or `npx`:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install -g worktree-for-agents@latest
|
|
26
|
+
pnpm add -g worktree-for-agents@latest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Check the installation diagnostics:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
node <package>/setup.mjs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The repository clone is for development only. There is no `pip install` and no
|
|
36
|
+
dependencies to resolve: the manager uses only the Python standard
|
|
37
|
+
library. Once installed, the manager works without a network.
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
Global options go before the subcommand and `--repo` requires an absolute
|
|
42
|
+
path.
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
worktree.sh [--repo ABS] [--config ABS] [--json] create <id> [--base BRANCH] [--init-submodules] [--prepare] [--verify]
|
|
46
|
+
worktree.sh [globals] complete <id> [--preserve-dirty] [--title TEXT] [--body TEXT]
|
|
47
|
+
worktree.sh [globals] cleanup <id> [--discard]
|
|
48
|
+
worktree.sh [globals] recover [<transaction-id>]
|
|
49
|
+
worktree.sh [globals] prune
|
|
50
|
+
worktree.sh [globals] list
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Target-direct operates on the repository given in `--repo` and does not create a
|
|
54
|
+
parent worktree nor modify its `HEAD`. Its syntax places `--parent-repo`
|
|
55
|
+
after the action and requires `--submodule-path` in `create`:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
worktree.sh [--repo ABS] target-direct <action> <id> --parent-repo <parent> [--target submodule] [--submodule-path <REL>] [--base BRANCH] [--link-parent staged-only] [--descriptor PATH]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The actions are `create`, `link-parent`, `abort`, `complete` and `recover`, and the
|
|
62
|
+
only supported link mode is `--link-parent staged-only`.
|
|
63
|
+
|
|
64
|
+
`--json` publishes a single JSON document. `--request-json <file|->` publishes
|
|
65
|
+
the same nine-key envelope used by the MCP transport, and optionally accepts `--json`.
|
|
66
|
+
A dry run of `create` publishes `planned: true` and creates no
|
|
67
|
+
worktree or branch.
|
|
68
|
+
|
|
69
|
+
The MCP service is published over stdio. From the installed package it is launched without
|
|
70
|
+
bin:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
node <package>/mcp-server.mjs
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The CLI is used the same way:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
node <package>/worktree.mjs --version
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The MCP `initialize` publishes the guide in `instructions`:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
You are going to implement a change, run a spec, or start a flow. Do you want to create a worktree to work in isolation?
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For development, the transport also starts from the checkout:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
PYTHONPATH=. python3 -m worktree_manager.mcp_transport
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Configuration
|
|
95
|
+
|
|
96
|
+
The configuration is an optional `key=value` file, selected with
|
|
97
|
+
`--config` or taken from `.worktree-manager.conf` at the repository root.
|
|
98
|
+
The eleven keys and their default values are:
|
|
99
|
+
|
|
100
|
+
| Key | Default | Restriction |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `worktrees_dir` | `.worktrees` | Must not start with `-` |
|
|
103
|
+
| `branch_prefix` | `worktree/` | — |
|
|
104
|
+
| `state_dir` | `worktree-manager` | Safe relative path |
|
|
105
|
+
| `backup_dir` | `backups` | Not an absolute path and contains no `..` |
|
|
106
|
+
| `backup_ref_prefix` | `worktree-manager/backups` | No `..` |
|
|
107
|
+
| `lock_dir` | `locks` | Safe relative path |
|
|
108
|
+
| `prepare_command` | `''` (empty) | Required when `prepare_enabled=true` |
|
|
109
|
+
| `verify_command` | `''` (empty) | Required when `verify_enabled=true` |
|
|
110
|
+
| `prepare_enabled` | `false` | `true` or `false` |
|
|
111
|
+
| `verify_enabled` | `false` | `true` or `false` |
|
|
112
|
+
| `preserve_dirty` | `false` | `true` or `false` |
|
|
113
|
+
|
|
114
|
+
Each key may appear only once. Unknown keys, malformed lines, invalid booleans, and unsafe paths are rejected as
|
|
115
|
+
`invalid_configuration` with exit code `3`. The environment variable
|
|
116
|
+
`WORKTREE_MANAGER_<KEY>`, with the key in uppercase, overrides the value
|
|
117
|
+
read from the file.
|
|
118
|
+
|
|
119
|
+
## Architecture
|
|
120
|
+
|
|
121
|
+
`worktree.sh` is the entrypoint and delegates the lifecycle to the Python
|
|
122
|
+
package `worktree_manager`, which runs with `python3 -m worktree_manager`. The package
|
|
123
|
+
is the only lifecycle implementation: there is no alternative shell engine.
|
|
124
|
+
|
|
125
|
+
The package is organized in modules with one responsibility each:
|
|
126
|
+
|
|
127
|
+
| Module | Responsibility |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `worktree_manager.cli` | Command dispatcher, target-direct dispatch, and output contract. |
|
|
130
|
+
| `worktree_manager.configuration` | Reading and validating the `key=value` configuration. |
|
|
131
|
+
| `worktree_manager.models` | Shared domain values: results, errors, and identities. |
|
|
132
|
+
| `worktree_manager.output` | Publishing the result as readable text or as JSON. |
|
|
133
|
+
| `worktree_manager.git_repository` | Shared Git access for the lifecycle modules. |
|
|
134
|
+
| `worktree_manager.worktree_lifecycle` | `create`, `complete`, `cleanup`, and `prune` operations. |
|
|
135
|
+
| `worktree_manager.commit_message` | Deterministic closing message composition and neutral fallback. |
|
|
136
|
+
| `worktree_manager.target_direct_adapter` | Target-direct operations on a submodule. |
|
|
137
|
+
| `worktree_manager.recovery_manager` | Recovery of pending transactions. |
|
|
138
|
+
| `worktree_manager.transaction_journal` | Durable transaction and link journal. |
|
|
139
|
+
| `worktree_manager.backup_store` | Retained backup references. |
|
|
140
|
+
| `worktree_manager.lock_manager` | Mutual exclusion between processes. |
|
|
141
|
+
| `worktree_manager.state_digest` | Canonical digest of the repository state and of a plan. |
|
|
142
|
+
| `worktree_manager.mcp_server` | MCP service published over stdio. |
|
|
143
|
+
| `worktree_manager.mcp_contract` | Strict versioned request and envelope contracts. |
|
|
144
|
+
| `worktree_manager.mcp_operations` | Facade over the lifecycle and recovery services. |
|
|
145
|
+
| `worktree_manager.mcp_transport` | Stdio transport entrypoint. |
|
|
146
|
+
|
|
147
|
+
The package also contains the package marker `__init__.py` and the
|
|
148
|
+
entrypoint `__main__.py`, which is what `python3 -m worktree_manager` runs.
|
|
149
|
+
|
|
150
|
+
## Tests
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
bash tests/cli-usage.sh
|
|
154
|
+
bash tests/cli-json.sh
|
|
155
|
+
bash tests/compatibility.sh
|
|
156
|
+
bash tests/smoke.sh
|
|
157
|
+
bash tests/parity.sh
|
|
158
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_*.py'
|
|
159
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp*.py' -v
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
- [CLI output contract](docs/cli-contract-v1.md) — contract.
|
|
165
|
+
- [MCP service contract](docs/mcp-contract.md) — contract.
|
|
166
|
+
- [Installation and usage guide](docs/installation.md) — guide.
|
|
167
|
+
- [History bash → Python](docs/history-bash-to-python.md) — notes, not a contract.
|
|
168
|
+
|
|
169
|
+
Contracts pin the verifiable behavior. The guide explains how to use it.
|
|
170
|
+
The history records the transition and does not replace the contracts.
|
|
171
|
+
|
|
172
|
+
## Source and distribution
|
|
173
|
+
|
|
174
|
+
This repository is the source of truth for the implementation, the contracts,
|
|
175
|
+
the installation, and the manager history.
|
|
176
|
+
|
|
177
|
+
The consumer that integrates this repository documents the adapter, the bridge, and
|
|
178
|
+
the submodule boundary: **that integration documentation lives in the
|
|
179
|
+
consumer repository**. The manager does not keep a vendored copy of itself
|
|
180
|
+
and does not know the consumer.
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Installation and usage of the worktree manager
|
|
2
|
+
|
|
3
|
+
This guide explains how to install the manager as an npm package and how to use it
|
|
4
|
+
on a test repository. Integration with a specific consumer
|
|
5
|
+
is documented in the repository that integrates the manager.
|
|
6
|
+
|
|
7
|
+
The examples use `$HOME` because `--repo` requires an absolute path. `<package>`
|
|
8
|
+
is the installed package path.
|
|
9
|
+
|
|
10
|
+
## 1. Requirements
|
|
11
|
+
|
|
12
|
+
Check the available versions:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
python3 --version
|
|
16
|
+
git --version
|
|
17
|
+
node --version
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Python 3.9 or later.** The package postinstall checks the
|
|
21
|
+
3.9 floor and fails visibly if it is missing or older. `node
|
|
22
|
+
<package>/setup.mjs` reports the detected interpreter.
|
|
23
|
+
- **Git 2.17.0 or later**, for `git worktree remove`. The test baseline is
|
|
24
|
+
Git 2.54.0. The manager does not check the Git version at runtime.
|
|
25
|
+
- **Node 18 or later** for the `mcp-server.mjs`, `worktree.mjs`
|
|
26
|
+
and `setup.mjs` launchers.
|
|
27
|
+
- **`bash`** to run the test suites.
|
|
28
|
+
|
|
29
|
+
## 2. Install the package
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm install -g worktree-for-agents@latest
|
|
33
|
+
pnpm add -g worktree-for-agents@latest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Check the diagnostics:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
node <package>/setup.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The package exposes no bin or `npx`: the MCP is launched with `node
|
|
43
|
+
<package>/mcp-server.mjs` and the CLI with `node <package>/worktree.mjs`. The
|
|
44
|
+
repository clone is for development only. There is no `pip install` and no
|
|
45
|
+
dependencies to resolve: the manager uses only the Python standard
|
|
46
|
+
library. Once installed, the manager works without a network.
|
|
47
|
+
|
|
48
|
+
## 3. A test repository
|
|
49
|
+
|
|
50
|
+
The manager requires the worktree path to be ignored by Git, and
|
|
51
|
+
`.worktrees/` in `.gitignore` satisfies that condition.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
mkdir "$HOME/demo" && cd "$HOME/demo"
|
|
55
|
+
git init -b main
|
|
56
|
+
git config user.name "Demo"
|
|
57
|
+
git config user.email "demo@example.invalid"
|
|
58
|
+
printf 'base\n' > file
|
|
59
|
+
printf '.worktrees/\n' > .gitignore
|
|
60
|
+
git add file .gitignore
|
|
61
|
+
git commit -m "base"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Create the worktree:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" create demo --base main
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
What each step leaves behind:
|
|
71
|
+
|
|
72
|
+
- `git init` creates the repository and its `main` branch.
|
|
73
|
+
- The initial commit pins the base on top of which the worktree is built.
|
|
74
|
+
- `create` creates the `worktree/demo` branch, the worktree at `$HOME/demo/.worktrees/demo`
|
|
75
|
+
and the transaction journal in the repository state directory.
|
|
76
|
+
|
|
77
|
+
Check the status:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" list
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Work inside the worktree and then merge it:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
cd "$HOME/demo/.worktrees/demo"
|
|
87
|
+
printf 'change\n' >> file
|
|
88
|
+
git add file
|
|
89
|
+
git commit -m "change"
|
|
90
|
+
cd "$HOME/demo"
|
|
91
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" complete demo --base main
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`complete` requires the repository to be on the base branch, the worktree to have
|
|
95
|
+
no uncommitted changes, and the destination to have none either, unless
|
|
96
|
+
`--preserve-dirty` is used. When finished, the change is merged as a squash
|
|
97
|
+
commit on `main`, and the worktree and its branch are removed.
|
|
98
|
+
|
|
99
|
+
To leave an expressive closing commit, pass `--title` and optionally
|
|
100
|
+
`--body`:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" complete demo --base main \
|
|
104
|
+
--title "Demo closing" --body "Delivery: merged change. Reason: verification."
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If `--title` is not passed, the manager uses the generic fallback:
|
|
108
|
+
`Complete <work_id> (generic closure)` with the body
|
|
109
|
+
`No delivery description was available at closure time.`.
|
|
110
|
+
|
|
111
|
+
## 4. Configuration
|
|
112
|
+
|
|
113
|
+
The configuration is a `key=value` file. The manager reads it from
|
|
114
|
+
`.worktree-manager.conf` at the repository root, or from the file passed
|
|
115
|
+
with `--config`, which must exist and be an absolute path. If both are
|
|
116
|
+
given, `--config` wins. Afterwards, the environment variable
|
|
117
|
+
`WORKTREE_MANAGER_<KEY>`, with the key in uppercase, overrides the value
|
|
118
|
+
read from the file.
|
|
119
|
+
|
|
120
|
+
The eleven keys and their default values are:
|
|
121
|
+
|
|
122
|
+
| Key | Default | Restriction |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `worktrees_dir` | `.worktrees` | Must not start with `-` |
|
|
125
|
+
| `branch_prefix` | `worktree/` | — |
|
|
126
|
+
| `state_dir` | `worktree-manager` | Safe relative path |
|
|
127
|
+
| `backup_dir` | `backups` | Not an absolute path and contains no `..` |
|
|
128
|
+
| `backup_ref_prefix` | `worktree-manager/backups` | No `..` |
|
|
129
|
+
| `lock_dir` | `locks` | Safe relative path |
|
|
130
|
+
| `prepare_command` | `''` (empty) | Required when `prepare_enabled=true` |
|
|
131
|
+
| `verify_command` | `''` (empty) | Required when `verify_enabled=true` |
|
|
132
|
+
| `prepare_enabled` | `false` | `true` or `false` |
|
|
133
|
+
| `verify_enabled` | `false` | `true` or `false` |
|
|
134
|
+
| `preserve_dirty` | `false` | `true` or `false` |
|
|
135
|
+
|
|
136
|
+
Each key may appear only once. An unknown key, a line without
|
|
137
|
+
`=`, an invalid boolean, or an unsafe path is rejected as
|
|
138
|
+
`invalid_configuration` with exit code `3`, before any effect.
|
|
139
|
+
|
|
140
|
+
## 5. Output contracts
|
|
141
|
+
|
|
142
|
+
- `--json` publishes a single JSON document with seven keys: `contract_version`,
|
|
143
|
+
`tool_version`, `operation`, `result`, `data`, `warnings` and `error`.
|
|
144
|
+
- `--request-json <file|->` publishes the same nine-key document used by
|
|
145
|
+
the MCP transport: the previous seven plus `repository` and
|
|
146
|
+
`transaction_id`. It optionally accepts `--json` and the published document
|
|
147
|
+
does not change.
|
|
148
|
+
- A dry run of `create` publishes `planned: true` and creates no worktree or branch.
|
|
149
|
+
`init_submodules_result` is `planned` or `not_requested`, and `prepare` and
|
|
150
|
+
`verify` are `planned` or `skipped`.
|
|
151
|
+
- The MCP service is published over stdio and exposes seven tools. From the
|
|
152
|
+
installed package it is launched without bin:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
node <package>/mcp-server.mjs
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`initialize` publishes the guide in `instructions`:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
You are going to implement a change, run a spec, or start a flow. Do you want to create a worktree to work in isolation?
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For development, the transport also starts from the checkout:
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
PYTHONPATH=. python3 -m worktree_manager.mcp_transport
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 6. Tests
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
bash tests/cli-usage.sh
|
|
174
|
+
bash tests/cli-json.sh
|
|
175
|
+
bash tests/compatibility.sh
|
|
176
|
+
bash tests/smoke.sh
|
|
177
|
+
bash tests/parity.sh
|
|
178
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_*.py'
|
|
179
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp*.py' -v
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 7. Consumption as a submodule and coexistence
|
|
183
|
+
|
|
184
|
+
A consumer repository may materialize the manager as a submodule. The
|
|
185
|
+
rule is unique: the materialized checkout and the registered gitlink must be the
|
|
186
|
+
same commit.
|
|
187
|
+
|
|
188
|
+
The npm package `worktree-for-agents` is independent of the submodule: it ships
|
|
189
|
+
the Python runtime and works without a network once installed. `node
|
|
190
|
+
<package>/setup.mjs` diagnoses which installation is active when the
|
|
191
|
+
npm package, the global FlowTask copy, and the local submodule coexist. The repository
|
|
192
|
+
clone is for development only and no other distribution is promised.
|
|
193
|
+
|
|
194
|
+
## 8. Troubleshooting
|
|
195
|
+
|
|
196
|
+
- **`python3` is not on the `PATH`.** The entrypoint exits with code `127`
|
|
197
|
+
and the message
|
|
198
|
+
`worktree manager requires Python 3 (python3) on PATH; install Python 3 and retry.`
|
|
199
|
+
- **The package entrypoint is missing.** The entrypoint exits with
|
|
200
|
+
code `126` and the message
|
|
201
|
+
`worktree manager Python entry point is missing: <path>/worktree_manager/__main__.py`.
|
|
202
|
+
- **The repository path is relative.** The operation is rejected with
|
|
203
|
+
code `invalid_repository`, exit code `3`, and the message
|
|
204
|
+
`Repository path must be absolute`.
|
|
205
|
+
- **The Python version is older than 3.9.** The consumer installer reports
|
|
206
|
+
it as `python <version> is older than the required 3.9`. The entrypoint
|
|
207
|
+
of this repository does not check the version.
|
|
208
|
+
|
|
209
|
+
## 9. What it does not cover
|
|
210
|
+
|
|
211
|
+
This guide does not cover integration with a consumer: neither the adapter, nor the
|
|
212
|
+
bridge, nor gitlink validation, nor the consumer installation procedure.
|
|
213
|
+
That is documented in the consumer repository.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Worktree manager MCP service
|
|
2
|
+
|
|
3
|
+
The optional stdio service starts independently from the existing CLI:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
python3 -m worktree_manager.mcp_transport
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
It implements JSON-RPC `initialize`, `ping`, `tools/list` and `tools/call` and
|
|
10
|
+
publishes exactly `worktree_create`, `worktree_complete`, `worktree_cleanup`,
|
|
11
|
+
`worktree_recover`, `worktree_list`, `worktree_prune` and `worktree_status`.
|
|
12
|
+
Each call requires an absolute existing Git repository path; the process
|
|
13
|
+
working directory is never used as a repository default. Unknown arguments
|
|
14
|
+
are rejected. Responses carry contract version 1, operation, repository,
|
|
15
|
+
result, data, warnings, structured error and a nullable transaction ID.
|
|
16
|
+
|
|
17
|
+
The `initialize` result carries `serverInfo.name "worktree-for-agents"` and
|
|
18
|
+
an `instructions` field with the literal guide text:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
You are going to implement a change, run a spec, or start a flow. Do you want to create a worktree to work in isolation?
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
When installed from npm, the service is launched without a bin entry via:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
node <package>/mcp-server.mjs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A rejected call publishes the same nine-key envelope as an accepted one, with
|
|
31
|
+
`result` set to `failure` or `pending_intervention` and `error` carrying
|
|
32
|
+
`code`, `message` and `exit_code`. The command line publishes that identical
|
|
33
|
+
envelope for `--request-json`, so both transports can be read by one client.
|
|
34
|
+
|
|
35
|
+
`worktree_create` accepts the optional booleans `init_submodules`, `prepare`
|
|
36
|
+
and `verify`. They are off by default and report the observed state of each
|
|
37
|
+
step; `prepare` and `verify` require the matching configured command and are
|
|
38
|
+
rejected as `invalid_configuration` before any worktree or branch is created.
|
|
39
|
+
|
|
40
|
+
Mutating dry runs do not invoke lifecycle operations. A per-call authorization is
|
|
41
|
+
required only in an execution, that is, when `dry_run` is absent or `false`; a
|
|
42
|
+
dry run of a destructive operation succeeds without it. A destructive execute
|
|
43
|
+
accepts `authorization: "I_AUTHORIZE_THIS_CALL"` directly, or `confirm: true`,
|
|
44
|
+
from which that authorization is derived. A `confirm: false` sent together with
|
|
45
|
+
the literal is rejected as `authorization_mismatch`. A `cleanup` with a discard
|
|
46
|
+
additionally requires the separate boolean `discard_consent: true`. A
|
|
47
|
+
target-direct `create`, `complete` or `recover` has no safe simulation and is
|
|
48
|
+
rejected as `unsupported_dry_run` with exit code `3` when a dry run is
|
|
49
|
+
requested. Authorization is not retained across requests. Do not expose this
|
|
50
|
+
local service to untrusted clients. CLI JSON v1 and its entrypoint remain
|
|
51
|
+
unchanged.
|
|
52
|
+
|
|
53
|
+
## End-to-end verification
|
|
54
|
+
|
|
55
|
+
Run the real stdio client tests from the repository root:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp*.py' -v
|
|
59
|
+
MCP_E2E_EVIDENCE_DIR=<evidence-dir> \
|
|
60
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp_e2e.py' -v
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`test_mcp_e2e.py` uses isolated temporary Git repositories and launches the
|
|
64
|
+
stdio server as a subprocess. It exercises discovery and every public tool,
|
|
65
|
+
dry-run immutability, authorization and separate discard consent, transaction
|
|
66
|
+
recovery, worktree/ref/index/status effects, plus MCP-vs-Python-CLI completion
|
|
67
|
+
on independent equivalent repositories. When `MCP_E2E_EVIDENCE_DIR` is set,
|
|
68
|
+
each test writes its request/response trace there as JSON. No evidence or Git
|
|
69
|
+
fixture is written into the checked-out repository by default.
|
|
70
|
+
|
|
71
|
+
## Closing message
|
|
72
|
+
|
|
73
|
+
`worktree_complete` accepts `commit_title` and `commit_body` (optional strings).
|
|
74
|
+
They are validated as strings and included in the plan hash only for this operation.
|
|
75
|
+
The dry run publishes the composed `commit_subject` and `commit_body`; execute passes them
|
|
76
|
+
to the lifecycle. If missing, the manager uses the neutral fallback
|
|
77
|
+
`Complete <work_id> (generic closure)` / `No delivery description was available
|
|
78
|
+
at closure time.`.
|
package/mcp-server.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { probePython } from "./scripts/python.mjs";
|
|
6
|
+
|
|
7
|
+
const packageDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const transport = join(packageDir, "worktree_manager", "mcp_transport.py");
|
|
9
|
+
const entry = join(packageDir, "worktree_manager", "__main__.py");
|
|
10
|
+
|
|
11
|
+
if (!existsSync(transport)) {
|
|
12
|
+
process.stderr.write(
|
|
13
|
+
"worktree-for-agents is incomplete: missing worktree_manager/mcp_transport.py. Reinstall the package.\n"
|
|
14
|
+
);
|
|
15
|
+
process.exit(126);
|
|
16
|
+
}
|
|
17
|
+
if (!existsSync(entry)) {
|
|
18
|
+
process.stderr.write(
|
|
19
|
+
"worktree-for-agents is incomplete: missing worktree_manager/__main__.py. Reinstall the package.\n"
|
|
20
|
+
);
|
|
21
|
+
process.exit(126);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const probed = probePython();
|
|
25
|
+
if (!probed.version) {
|
|
26
|
+
process.stderr.write(
|
|
27
|
+
"worktree-for-agents requires Python 3.9 or later. Install Python 3.9+ and rerun the installation.\n"
|
|
28
|
+
);
|
|
29
|
+
process.exit(127);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const env = {
|
|
33
|
+
...process.env,
|
|
34
|
+
PYTHONPATH: packageDir + (process.env.PYTHONPATH ? `:${process.env.PYTHONPATH}` : ""),
|
|
35
|
+
};
|
|
36
|
+
const child = spawn(probed.bin, ["-m", "worktree_manager.mcp_transport"], {
|
|
37
|
+
cwd: packageDir,
|
|
38
|
+
env,
|
|
39
|
+
stdio: "inherit",
|
|
40
|
+
});
|
|
41
|
+
const forward = (signal) => {
|
|
42
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
43
|
+
child.kill(signal);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
process.on("SIGINT", () => forward("SIGINT"));
|
|
47
|
+
process.on("SIGTERM", () => forward("SIGTERM"));
|
|
48
|
+
child.on("exit", (code, signal) => {
|
|
49
|
+
if (signal) {
|
|
50
|
+
process.kill(process.pid, signal);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
process.exit(code === null ? 1 : code);
|
|
54
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "worktree-for-agents",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Standalone Git worktree manager with a stdio MCP service usable by any coding agent",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"worktree.sh",
|
|
11
|
+
"worktree_manager/**/*.py",
|
|
12
|
+
"mcp-server.mjs",
|
|
13
|
+
"worktree.mjs",
|
|
14
|
+
"setup.mjs",
|
|
15
|
+
"GUIDE.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"docs/installation.md",
|
|
18
|
+
"docs/mcp-contract.md",
|
|
19
|
+
"scripts/python.mjs",
|
|
20
|
+
"scripts/postinstall.mjs"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
24
|
+
"prepublishOnly": "node scripts/verify-package.mjs"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/Saez520/worktree-manager.git"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"git",
|
|
35
|
+
"worktree",
|
|
36
|
+
"mcp",
|
|
37
|
+
"coding-agent"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { probePython } from "./python.mjs";
|
|
2
|
+
|
|
3
|
+
const probed = probePython();
|
|
4
|
+
if (!probed.version) {
|
|
5
|
+
process.stderr.write(
|
|
6
|
+
"worktree-for-agents requires Python 3.9 or later. Install Python 3.9+ and rerun the installation.\n"
|
|
7
|
+
);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
process.stdout.write(`worktree-for-agents: Python ${probed.version} (${probed.bin})\n`);
|
|
11
|
+
process.stdout.write("Next: node <package>/setup.mjs\n");
|