worktree-for-agents 0.0.0-stage → 1.0.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.
- package/GUIDE.md +45 -0
- package/README.md +180 -2
- package/docs/installation.md +214 -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
|
+
Vas a implementar un cambio, ejecutar una spec o iniciar un flujo. ¿Quieres crear un worktree para hacerlo aislado?
|
|
2
|
+
|
|
3
|
+
Esta guía explica cómo usar el gestor `worktree-for-agents` para trabajar
|
|
4
|
+
aislado en un worktree antes de tocar la rama principal.
|
|
5
|
+
|
|
6
|
+
## Las siete herramientas
|
|
7
|
+
|
|
8
|
+
El servicio MCP expone exactamente siete herramientas:
|
|
9
|
+
|
|
10
|
+
- `worktree_create`: crea un worktree y su rama para un identificador de trabajo.
|
|
11
|
+
- `worktree_complete`: integra el worktree en la rama base y retira el worktree.
|
|
12
|
+
- `worktree_cleanup`: retira el worktree y su rama sin integrar.
|
|
13
|
+
- `worktree_recover`: recupera una transacción pendiente por su identificador.
|
|
14
|
+
- `worktree_list`: lista los worktrees del repositorio.
|
|
15
|
+
- `worktree_prune`: poda la información de worktrees que ya no existen.
|
|
16
|
+
- `worktree_status`: muestra el estado de un trabajo.
|
|
17
|
+
|
|
18
|
+
Todas exigen una ruta absoluta de repositorio Git en `repo`.
|
|
19
|
+
|
|
20
|
+
## Cuándo ofrecer el worktree
|
|
21
|
+
|
|
22
|
+
Antes de trabajar en la rama principal, ofrece crear un worktree con
|
|
23
|
+
`worktree_create`. Trabaja dentro del worktree y luego integra con
|
|
24
|
+
`worktree_complete`. Si el trabajo se descarta, usa `worktree_cleanup`.
|
|
25
|
+
|
|
26
|
+
## Lanzamiento sin bin
|
|
27
|
+
|
|
28
|
+
El paquete no expone bin ni `npx`. El servicio MCP se lanza con Node y la
|
|
29
|
+
ruta absoluta del paquete:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
node <paquete>/mcp-server.mjs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
La CLI se lanza del mismo modo:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
node <paquete>/worktree.mjs --version
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
El diagnóstico de la instalación se obtiene con:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
node <paquete>/setup.mjs
|
|
45
|
+
```
|
package/README.md
CHANGED
|
@@ -1,3 +1,181 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Gestor de worktrees
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Gestor standalone para crear, completar, limpiar y recuperar worktrees de un
|
|
4
|
+
repositorio Git. El proyecto no conoce FlowTask ni depende de sus rutas, refs
|
|
5
|
+
heredadas o mecanismos de coordinación.
|
|
6
|
+
|
|
7
|
+
## Requisitos
|
|
8
|
+
|
|
9
|
+
- **Python 3.9 o posterior.** El entrypoint sólo comprueba que `python3` esté
|
|
10
|
+
disponible en el `PATH`; el piso de 3.9 lo valida el postinstall del paquete
|
|
11
|
+
y `node <paquete>/setup.mjs`. El paquete usa sólo la biblioteca estándar.
|
|
12
|
+
- **Git 2.17.0 o posterior**, por `git worktree remove`, que es el comando más
|
|
13
|
+
reciente que usa el gestor. La línea base de las pruebas es Git 2.54.0. El
|
|
14
|
+
gestor no comprueba la versión de Git en tiempo de ejecución.
|
|
15
|
+
- **Node 18 o posterior** para los lanzadores `mcp-server.mjs`, `worktree.mjs`
|
|
16
|
+
y `setup.mjs`.
|
|
17
|
+
- **`bash`** para ejecutar las suites de pruebas.
|
|
18
|
+
|
|
19
|
+
## Instalación
|
|
20
|
+
|
|
21
|
+
El gestor se distribuye como paquete npm `worktree-for-agents`, versión
|
|
22
|
+
`1.0.0`, sin bin ni `npx`:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm install -g worktree-for-agents@latest
|
|
26
|
+
pnpm add -g worktree-for-agents@latest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Comprueba el diagnóstico de la instalación:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
node <paquete>/setup.mjs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
El clon del repositorio queda solo para desarrollo. No hay `pip install` ni
|
|
36
|
+
dependencias que resolver: el gestor usa sólo la biblioteca estándar de
|
|
37
|
+
Python. Una vez instalado, el gestor funciona sin red.
|
|
38
|
+
|
|
39
|
+
## Uso rápido
|
|
40
|
+
|
|
41
|
+
Las opciones globales van antes del subcomando y `--repo` exige una ruta
|
|
42
|
+
absoluta.
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
worktree.sh [--repo ABS] [--config ABS] [--json] create <id> [--base BRANCH] [--init-submodules] [--prepare] [--verify]
|
|
46
|
+
worktree.sh [globales] complete <id> [--preserve-dirty] [--title TEXTO] [--body TEXTO]
|
|
47
|
+
worktree.sh [globales] cleanup <id> [--discard]
|
|
48
|
+
worktree.sh [globales] recover [<transaction-id>]
|
|
49
|
+
worktree.sh [globales] prune
|
|
50
|
+
worktree.sh [globales] list
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Target-direct opera sobre el repositorio indicado en `--repo` y no crea un
|
|
54
|
+
worktree del parent ni modifica su `HEAD`. Su sintaxis coloca `--parent-repo`
|
|
55
|
+
después de la acción y exige `--submodule-path` en `create`:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
worktree.sh [--repo ABS] target-direct <acción> <id> --parent-repo <parent> [--target submodule] [--submodule-path <REL>] [--base BRANCH] [--link-parent staged-only] [--descriptor PATH]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Las acciones son `create`, `link-parent`, `abort`, `complete` y `recover`, y el
|
|
62
|
+
único modo de enlace admitido es `--link-parent staged-only`.
|
|
63
|
+
|
|
64
|
+
`--json` publica un único documento JSON. `--request-json <archivo|->` publica
|
|
65
|
+
el mismo sobre de nueve claves que usa el transporte MCP, y acepta `--json` de
|
|
66
|
+
forma opcional. Un dry run de `create` publica `planned: true` y no crea
|
|
67
|
+
worktree ni rama.
|
|
68
|
+
|
|
69
|
+
El servicio MCP se publica por stdio. Desde el paquete instalado se lanza sin
|
|
70
|
+
bin:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
node <paquete>/mcp-server.mjs
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
La CLI se usa del mismo modo:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
node <paquete>/worktree.mjs --version
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
El `initialize` del MCP publica la guía en `instructions`:
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
Vas a implementar un cambio, ejecutar una spec o iniciar un flujo. ¿Quieres crear un worktree para hacerlo aislado?
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Para desarrollo, el transporte también arranca desde el checkout:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
PYTHONPATH=. python3 -m worktree_manager.mcp_transport
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Configuración
|
|
95
|
+
|
|
96
|
+
La configuración es un archivo opcional de `clave=valor`, seleccionado con
|
|
97
|
+
`--config` o tomado de `.worktree-manager.conf` en la raíz del repositorio.
|
|
98
|
+
Las once claves y sus valores por defecto son:
|
|
99
|
+
|
|
100
|
+
| Clave | Por defecto | Restricción |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `worktrees_dir` | `.worktrees` | No puede empezar por `-` |
|
|
103
|
+
| `branch_prefix` | `worktree/` | — |
|
|
104
|
+
| `state_dir` | `worktree-manager` | Ruta relativa segura |
|
|
105
|
+
| `backup_dir` | `backups` | No es una ruta absoluta y no contiene `..` |
|
|
106
|
+
| `backup_ref_prefix` | `worktree-manager/backups` | Sin `..` |
|
|
107
|
+
| `lock_dir` | `locks` | Ruta relativa segura |
|
|
108
|
+
| `prepare_command` | `''` (vacío) | Requerida cuando `prepare_enabled=true` |
|
|
109
|
+
| `verify_command` | `''` (vacío) | Requerida cuando `verify_enabled=true` |
|
|
110
|
+
| `prepare_enabled` | `false` | `true` o `false` |
|
|
111
|
+
| `verify_enabled` | `false` | `true` o `false` |
|
|
112
|
+
| `preserve_dirty` | `false` | `true` o `false` |
|
|
113
|
+
|
|
114
|
+
Cada clave puede aparecer una sola vez. Las claves desconocidas, las líneas mal
|
|
115
|
+
formadas, los booleanos inválidos y las rutas inseguras se rechazan como
|
|
116
|
+
`invalid_configuration` con código de salida `3`. La variable de entorno
|
|
117
|
+
`WORKTREE_MANAGER_<CLAVE>`, con la clave en mayúsculas, reemplaza el valor
|
|
118
|
+
leído del archivo.
|
|
119
|
+
|
|
120
|
+
## Arquitectura
|
|
121
|
+
|
|
122
|
+
`worktree.sh` es el entrypoint y delega el lifecycle en el paquete Python
|
|
123
|
+
`worktree_manager`, que se ejecuta con `python3 -m worktree_manager`. El paquete
|
|
124
|
+
es la única implementación del lifecycle: no existe un motor shell alternativo.
|
|
125
|
+
|
|
126
|
+
El paquete se organiza en módulos con una responsabilidad cada uno:
|
|
127
|
+
|
|
128
|
+
| Módulo | Responsabilidad |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| `worktree_manager.cli` | Despachador de comandos, despacho target-direct y contrato de salida. |
|
|
131
|
+
| `worktree_manager.configuration` | Lectura y validación de la configuración `clave=valor`. |
|
|
132
|
+
| `worktree_manager.models` | Valores de dominio compartidos: resultados, errores e identidades. |
|
|
133
|
+
| `worktree_manager.output` | Publicación del resultado como texto legible o como JSON. |
|
|
134
|
+
| `worktree_manager.git_repository` | Acceso compartido a Git para los módulos del lifecycle. |
|
|
135
|
+
| `worktree_manager.worktree_lifecycle` | Operaciones `create`, `complete`, `cleanup` y `prune`. |
|
|
136
|
+
| `worktree_manager.commit_message` | Composición determinista del mensaje de cierre y fallback neutro. |
|
|
137
|
+
| `worktree_manager.target_direct_adapter` | Operaciones target-direct sobre un submódulo. |
|
|
138
|
+
| `worktree_manager.recovery_manager` | Recuperación de transacciones pendientes. |
|
|
139
|
+
| `worktree_manager.transaction_journal` | Journal durable de transacciones y enlaces. |
|
|
140
|
+
| `worktree_manager.backup_store` | Referencias de respaldo retenidas. |
|
|
141
|
+
| `worktree_manager.lock_manager` | Exclusión mutua entre procesos. |
|
|
142
|
+
| `worktree_manager.state_digest` | Digest canónico del estado del repositorio y de un plan. |
|
|
143
|
+
| `worktree_manager.mcp_server` | Servicio MCP publicado por stdio. |
|
|
144
|
+
| `worktree_manager.mcp_contract` | Contratos estrictos y versionados de petición y de sobre. |
|
|
145
|
+
| `worktree_manager.mcp_operations` | Fachada sobre los servicios de lifecycle y de recuperación. |
|
|
146
|
+
| `worktree_manager.mcp_transport` | Punto de entrada del transporte por stdio. |
|
|
147
|
+
|
|
148
|
+
El paquete contiene además el marcador de paquete `__init__.py` y el punto de
|
|
149
|
+
entrada `__main__.py`, que es lo que ejecuta `python3 -m worktree_manager`.
|
|
150
|
+
|
|
151
|
+
## Pruebas
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
bash tests/cli-usage.sh
|
|
155
|
+
bash tests/cli-json.sh
|
|
156
|
+
bash tests/compatibility.sh
|
|
157
|
+
bash tests/smoke.sh
|
|
158
|
+
bash tests/parity.sh
|
|
159
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_*.py'
|
|
160
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp*.py' -v
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Documentación
|
|
164
|
+
|
|
165
|
+
- [Contrato de salida de la CLI](docs/cli-contract-v1.md) — contrato.
|
|
166
|
+
- [Contrato del servicio MCP](docs/mcp-contract.md) — contrato.
|
|
167
|
+
- [Guía de instalación y uso](docs/installation.md) — guía.
|
|
168
|
+
- [Histórico bash → Python](docs/history-bash-to-python.md) — memoria, no contrato.
|
|
169
|
+
|
|
170
|
+
Los contratos fijan el comportamiento verificable. La guía explica cómo usarlo.
|
|
171
|
+
El histórico registra la transición y no sustituye a los contratos.
|
|
172
|
+
|
|
173
|
+
## Fuente y distribución
|
|
174
|
+
|
|
175
|
+
Este repositorio es la fuente de verdad de la implementación, de los contratos,
|
|
176
|
+
de la instalación y del histórico del gestor.
|
|
177
|
+
|
|
178
|
+
El consumidor que integra este repositorio documenta el adaptador, el bridge y
|
|
179
|
+
la frontera del submódulo: **la documentación de esa integración vive en el
|
|
180
|
+
repositorio consumidor**. El gestor no mantiene una copia vendorizada de sí
|
|
181
|
+
mismo y no conoce al consumidor.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Instalación y uso del gestor de worktrees
|
|
2
|
+
|
|
3
|
+
Esta guía explica cómo instalar el gestor como paquete npm y cómo usarlo
|
|
4
|
+
sobre un repositorio de prueba. La integración con un consumidor concreto
|
|
5
|
+
se documenta en el repositorio que integra el gestor.
|
|
6
|
+
|
|
7
|
+
Los ejemplos usan `$HOME` porque `--repo` exige una ruta absoluta. `<paquete>`
|
|
8
|
+
es la ruta del paquete instalado.
|
|
9
|
+
|
|
10
|
+
## 1. Requisitos
|
|
11
|
+
|
|
12
|
+
Comprueba las versiones disponibles:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
python3 --version
|
|
16
|
+
git --version
|
|
17
|
+
node --version
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **Python 3.9 o posterior.** El postinstall del paquete verifica el piso de
|
|
21
|
+
3.9 y falla de forma visible si falta o es anterior. `node
|
|
22
|
+
<paquete>/setup.mjs` informa el intérprete detectado.
|
|
23
|
+
- **Git 2.17.0 o posterior**, por `git worktree remove`. La línea base de las
|
|
24
|
+
pruebas es Git 2.54.0. El gestor no comprueba la versión de Git en tiempo de
|
|
25
|
+
ejecución.
|
|
26
|
+
- **Node 18 o posterior** para los lanzadores `mcp-server.mjs`, `worktree.mjs`
|
|
27
|
+
y `setup.mjs`.
|
|
28
|
+
- **`bash`** para ejecutar las suites de pruebas.
|
|
29
|
+
|
|
30
|
+
## 2. Instalar el paquete
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install -g worktree-for-agents@latest
|
|
34
|
+
pnpm add -g worktree-for-agents@latest
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Comprueba el diagnóstico:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
node <paquete>/setup.mjs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
El paquete no expone bin ni `npx`: el MCP se lanza con `node
|
|
44
|
+
<paquete>/mcp-server.mjs` y la CLI con `node <paquete>/worktree.mjs`. El clon
|
|
45
|
+
del repositorio queda solo para desarrollo. No hay `pip install` ni
|
|
46
|
+
dependencias que resolver: el gestor usa sólo la biblioteca estándar de
|
|
47
|
+
Python. Una vez instalado, el gestor funciona sin red.
|
|
48
|
+
|
|
49
|
+
## 3. Un repositorio de prueba
|
|
50
|
+
|
|
51
|
+
El gestor exige que la ruta del worktree esté ignorada por Git, y
|
|
52
|
+
`.worktrees/` en el `.gitignore` cumple esa condición.
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
mkdir "$HOME/demo" && cd "$HOME/demo"
|
|
56
|
+
git init -b main
|
|
57
|
+
git config user.name "Demo"
|
|
58
|
+
git config user.email "demo@example.invalid"
|
|
59
|
+
printf 'base\n' > file
|
|
60
|
+
printf '.worktrees/\n' > .gitignore
|
|
61
|
+
git add file .gitignore
|
|
62
|
+
git commit -m "base"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Crea el worktree:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" create demo --base main
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Qué deja cada paso:
|
|
72
|
+
|
|
73
|
+
- `git init` crea el repositorio y su rama `main`.
|
|
74
|
+
- El commit inicial fija la base sobre la que se construye el worktree.
|
|
75
|
+
- `create` crea la rama `worktree/demo`, el worktree en `$HOME/demo/.worktrees/demo`
|
|
76
|
+
y el journal de la transacción en el directorio de estado del repositorio.
|
|
77
|
+
|
|
78
|
+
Comprueba el estado:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" list
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Trabaja dentro del worktree y luego intégralo:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
cd "$HOME/demo/.worktrees/demo"
|
|
88
|
+
printf 'cambio\n' >> file
|
|
89
|
+
git add file
|
|
90
|
+
git commit -m "cambio"
|
|
91
|
+
cd "$HOME/demo"
|
|
92
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" complete demo --base main
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`complete` exige que el repositorio esté en la rama base, que el worktree no
|
|
96
|
+
tenga cambios sin commitear y que el destino tampoco los tenga, salvo que se
|
|
97
|
+
use `--preserve-dirty`. Al terminar, el cambio queda integrado como un commit
|
|
98
|
+
de squash en `main`, y el worktree y su rama se retiran.
|
|
99
|
+
|
|
100
|
+
Para dejar un commit de cierre diciente, pasa `--title` y, opcionalmente,
|
|
101
|
+
`--body`:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
"$HOME/worktree-manager/worktree.sh" --repo "$HOME/demo" complete demo --base main \
|
|
105
|
+
--title "Cierre de demo" --body "Entrega: cambio integrado. Motivo: verificacion."
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Si no se pasa `--title`, el gestor usa el fallback generico:
|
|
109
|
+
`Complete <work_id> (generic closure)` con el cuerpo
|
|
110
|
+
`No delivery description was available at closure time.`.
|
|
111
|
+
|
|
112
|
+
## 4. Configuración
|
|
113
|
+
|
|
114
|
+
La configuración es un archivo de `clave=valor`. El gestor lo toma de
|
|
115
|
+
`.worktree-manager.conf` en la raíz del repositorio, o del archivo que se le
|
|
116
|
+
pase con `--config`, que debe existir y ser una ruta absoluta. Si se pasan
|
|
117
|
+
ambos, `--config` prevalece. Después, la variable de entorno
|
|
118
|
+
`WORKTREE_MANAGER_<CLAVE>`, con la clave en mayúsculas, reemplaza el valor
|
|
119
|
+
leído del archivo.
|
|
120
|
+
|
|
121
|
+
Las once claves y sus valores por defecto son:
|
|
122
|
+
|
|
123
|
+
| Clave | Por defecto | Restricción |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `worktrees_dir` | `.worktrees` | No puede empezar por `-` |
|
|
126
|
+
| `branch_prefix` | `worktree/` | — |
|
|
127
|
+
| `state_dir` | `worktree-manager` | Ruta relativa segura |
|
|
128
|
+
| `backup_dir` | `backups` | No es una ruta absoluta y no contiene `..` |
|
|
129
|
+
| `backup_ref_prefix` | `worktree-manager/backups` | Sin `..` |
|
|
130
|
+
| `lock_dir` | `locks` | Ruta relativa segura |
|
|
131
|
+
| `prepare_command` | `''` (vacío) | Requerida cuando `prepare_enabled=true` |
|
|
132
|
+
| `verify_command` | `''` (vacío) | Requerida cuando `verify_enabled=true` |
|
|
133
|
+
| `prepare_enabled` | `false` | `true` o `false` |
|
|
134
|
+
| `verify_enabled` | `false` | `true` o `false` |
|
|
135
|
+
| `preserve_dirty` | `false` | `true` o `false` |
|
|
136
|
+
|
|
137
|
+
Cada clave puede aparecer una sola vez. Una clave desconocida, una línea sin
|
|
138
|
+
`=`, un booleano inválido o una ruta insegura se rechazan como
|
|
139
|
+
`invalid_configuration` con código de salida `3`, antes de cualquier efecto.
|
|
140
|
+
|
|
141
|
+
## 5. Contratos de salida
|
|
142
|
+
|
|
143
|
+
- `--json` publica un único documento JSON con siete claves: `contract_version`,
|
|
144
|
+
`tool_version`, `operation`, `result`, `data`, `warnings` y `error`.
|
|
145
|
+
- `--request-json <archivo|->` publica el mismo documento de nueve claves que
|
|
146
|
+
usa el transporte MCP: las siete anteriores más `repository` y
|
|
147
|
+
`transaction_id`. Acepta `--json` de forma opcional y el documento publicado
|
|
148
|
+
no cambia.
|
|
149
|
+
- Un dry run de `create` publica `planned: true` y no crea worktree ni rama.
|
|
150
|
+
`init_submodules_result` es `planned` o `not_requested`, y `prepare` y
|
|
151
|
+
`verify` son `planned` o `skipped`.
|
|
152
|
+
- El servicio MCP se publica por stdio y expone siete herramientas. Desde el
|
|
153
|
+
paquete instalado se lanza sin bin:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
node <paquete>/mcp-server.mjs
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
El `initialize` publica la guía en `instructions`:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
Vas a implementar un cambio, ejecutar una spec o iniciar un flujo. ¿Quieres crear un worktree para hacerlo aislado?
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Para desarrollo, el transporte también arranca desde el checkout:
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
PYTHONPATH=. python3 -m worktree_manager.mcp_transport
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 6. Pruebas
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
bash tests/cli-usage.sh
|
|
175
|
+
bash tests/cli-json.sh
|
|
176
|
+
bash tests/compatibility.sh
|
|
177
|
+
bash tests/smoke.sh
|
|
178
|
+
bash tests/parity.sh
|
|
179
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_*.py'
|
|
180
|
+
PYTHONPATH=. python3 -m unittest discover -s tests -p 'test_mcp*.py' -v
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 7. Consumo como submódulo y coexistencia
|
|
184
|
+
|
|
185
|
+
Un repositorio consumidor puede materializar el gestor como submódulo. La
|
|
186
|
+
regla es única: el checkout materializado y el gitlink registrado deben ser el
|
|
187
|
+
mismo commit.
|
|
188
|
+
|
|
189
|
+
El paquete npm `worktree-for-agents` es independiente del submódulo: embarca
|
|
190
|
+
el runtime Python y funciona sin red una vez instalado. `node
|
|
191
|
+
<paquete>/setup.mjs` diagnostica qué instalación está activa cuando coexisten
|
|
192
|
+
el paquete npm, la copia global de FlowTask y el submódulo local. El clon del
|
|
193
|
+
repositorio queda solo para desarrollo y no se promete otra distribución.
|
|
194
|
+
|
|
195
|
+
## 8. Solución de problemas
|
|
196
|
+
|
|
197
|
+
- **`python3` no está en el `PATH`.** El entrypoint termina con el código `127`
|
|
198
|
+
y el mensaje
|
|
199
|
+
`worktree manager requires Python 3 (python3) on PATH; install Python 3 and retry.`
|
|
200
|
+
- **Falta el punto de entrada del paquete.** El entrypoint termina con el
|
|
201
|
+
código `126` y el mensaje
|
|
202
|
+
`worktree manager Python entry point is missing: <ruta>/worktree_manager/__main__.py`.
|
|
203
|
+
- **La ruta del repositorio es relativa.** La operación se rechaza con el
|
|
204
|
+
código `invalid_repository`, el código de salida `3` y el mensaje
|
|
205
|
+
`Repository path must be absolute`.
|
|
206
|
+
- **La versión de Python es anterior a 3.9.** El instalador del consumidor lo
|
|
207
|
+
informa como `python <version> is older than the required 3.9`. El entrypoint
|
|
208
|
+
de este repositorio no comprueba la versión.
|
|
209
|
+
|
|
210
|
+
## 9. Qué no cubre
|
|
211
|
+
|
|
212
|
+
Esta guía no cubre la integración con un consumidor: ni el adaptador, ni el
|
|
213
|
+
bridge, ni la validación del gitlink, ni el procedimiento de instalación del
|
|
214
|
+
consumidor. Eso se documenta en el repositorio consumidor.
|
|
@@ -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
|
+
Vas a implementar un cambio, ejecutar una spec o iniciar un flujo. ¿Quieres crear un worktree para hacerlo aislado?
|
|
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
|
+
## Mensaje de cierre
|
|
72
|
+
|
|
73
|
+
`worktree_complete` acepta `commit_title` y `commit_body` (strings opcionales).
|
|
74
|
+
Se validan como strings y se incluyen en el plan hash solo para esta operacion.
|
|
75
|
+
El dry-run publica `commit_subject` y `commit_body` compuestos; execute los
|
|
76
|
+
pasa al lifecycle. Si faltan, el gestor usa el fallback neutro
|
|
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.0",
|
|
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");
|