@ramon-sg/issue-flow 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +297 -2
- package/dist/ask.d.ts +28 -0
- package/dist/ask.js +73 -0
- package/dist/branch-name.d.ts +113 -0
- package/dist/branch-name.js +226 -0
- package/dist/claude-namer.d.ts +51 -0
- package/dist/claude-namer.js +140 -0
- package/dist/cli-setup.d.ts +22 -0
- package/dist/cli-setup.js +26 -0
- package/dist/cli-worktree.d.ts +74 -0
- package/dist/cli-worktree.js +123 -0
- package/dist/cli.d.ts +120 -0
- package/dist/cli.js +344 -0
- package/dist/commands/adapter.d.ts +49 -0
- package/dist/commands/adapter.js +231 -0
- package/dist/commands/claude-hooks.d.ts +37 -0
- package/dist/commands/claude-hooks.js +162 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +90 -0
- package/dist/commands/init.d.ts +24 -0
- package/dist/commands/init.js +136 -0
- package/dist/commands/issue-branch.d.ts +36 -0
- package/dist/commands/issue-branch.js +182 -0
- package/dist/commands/issue-start.d.ts +11 -0
- package/dist/commands/issue-start.js +361 -0
- package/dist/commands/pr-worktree.d.ts +31 -0
- package/dist/commands/pr-worktree.js +122 -0
- package/dist/commands/session.d.ts +15 -0
- package/dist/commands/session.js +102 -0
- package/dist/commands/upgrade.d.ts +16 -0
- package/dist/commands/upgrade.js +85 -0
- package/dist/commands/worktree.d.ts +82 -0
- package/dist/commands/worktree.js +244 -0
- package/dist/config-base.d.ts +32 -0
- package/dist/config-base.js +47 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.js +230 -0
- package/dist/docker.d.ts +53 -0
- package/dist/docker.js +153 -0
- package/dist/git.d.ts +130 -0
- package/dist/git.js +192 -0
- package/dist/issue-text.d.ts +128 -0
- package/dist/issue-text.js +210 -0
- package/dist/launch.d.ts +90 -0
- package/dist/launch.js +158 -0
- package/dist/output.d.ts +33 -0
- package/dist/output.js +37 -0
- package/dist/package-edits.d.ts +35 -0
- package/dist/package-edits.js +78 -0
- package/dist/plan-doc.d.ts +26 -0
- package/dist/plan-doc.js +93 -0
- package/dist/ports.d.ts +90 -0
- package/dist/ports.js +257 -0
- package/dist/propose.d.ts +42 -0
- package/dist/propose.js +111 -0
- package/dist/recipes.d.ts +43 -0
- package/dist/recipes.js +123 -0
- package/dist/session-lock.d.ts +32 -0
- package/dist/session-lock.js +69 -0
- package/dist/settings-hooks.d.ts +31 -0
- package/dist/settings-hooks.js +133 -0
- package/dist/setup-files.d.ts +48 -0
- package/dist/setup-files.js +91 -0
- package/dist/start-modes.d.ts +162 -0
- package/dist/start-modes.js +277 -0
- package/dist/start-place.d.ts +61 -0
- package/dist/start-place.js +301 -0
- package/dist/start-state.d.ts +72 -0
- package/dist/start-state.js +13 -0
- package/dist/start-summary.d.ts +42 -0
- package/dist/start-summary.js +217 -0
- package/dist/stop-apps.d.ts +14 -0
- package/dist/stop-apps.js +127 -0
- package/dist/usage.d.ts +5 -0
- package/dist/usage.js +54 -0
- package/dist/work-state.d.ts +51 -0
- package/dist/work-state.js +58 -0
- package/dist/worktree-config.d.ts +35 -0
- package/dist/worktree-config.js +139 -0
- package/dist/worktree-steps.d.ts +53 -0
- package/dist/worktree-steps.js +210 -0
- package/package.json +20 -4
- package/recipes/bun.json +5 -0
- package/recipes/direnv.json +17 -0
- package/recipes/docker-compose.json +30 -0
- package/recipes/npm.json +5 -0
- package/recipes/pnpm.json +5 -0
- package/recipes/staging-main.json +5 -0
- package/schema.json +281 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ramón Soto
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,298 @@
|
|
|
1
|
-
#
|
|
1
|
+
# issue-flow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Flujo de trabajo de issue a PR para cualquier repo, en local y en las sesiones de Claude en la nube: nombra la rama de cada issue, crea su worktree con puertos y Docker propios, abre el PR en draft y limpia cuando se mergea. Trae las skills que usan ese flujo.
|
|
4
|
+
|
|
5
|
+
> **Instalar en un repo:** `npx @ramon-sg/agent-kit`, que instala este flujo con sus skills, las skills generales, `claude-cloud` y `claude-repo-guard`, y los actualiza ([agent-kit](https://github.com/ramon-sg/agent-kit)). El paquete es `@ramon-sg/issue-flow` y el comando, `issue-flow`; sin el scope, `issue-flow` en npm es otro paquete.
|
|
6
|
+
|
|
7
|
+
## Qué va a tener
|
|
8
|
+
|
|
9
|
+
- **`issue-flow init|doctor|upgrade`:** escribe, revisa y mueve a su versión lo que el flujo deja en el repo: `.agents/flow.json` con sus recetas, las dependencias y los hooks. Los corre `agent-kit`.
|
|
10
|
+
- **`issue-flow issue branch <n>`:** propone la rama de una issue (con Claude por defecto, o desde el título). Con `--missing`, agrega el slug de rama a todas las issues abiertas que no lo tienen.
|
|
11
|
+
- **`issue-flow issue start <n>`:** crea la rama y el worktree, hace el commit inicial, abre el PR en draft, asigna la issue y abre Claude. Con `--cloud`, lo hace en una sesión en la nube.
|
|
12
|
+
- **`issue-flow worktree setup|teardown|ports|cleanup|prune`:** prepara y limpia cada worktree con los pasos del repo, le da un bloque de puertos y borra los worktrees con el PR cerrado.
|
|
13
|
+
- **`issue-flow pr worktree <n>`:** abre un PR en su propio worktree, en la rama del PR, para revisarlo sin soltar lo que estás haciendo.
|
|
14
|
+
- **`issue-flow adapter setup|issue|archive`:** lo que corren Orca y Paseo al crear o borrar un worktree.
|
|
15
|
+
- **`issue-flow session`:** el hook `SessionStart`; en la nube llama a [`claude-cloud`](https://github.com/ramon-sg/claude-cloud).
|
|
16
|
+
- **`issue-flow hook worktree-create|worktree-remove`:** los hooks `WorktreeCreate` y `WorktreeRemove` de Claude Code, para `claude --worktree`.
|
|
17
|
+
- **Skills:** `github-issues`, `start-issue`, `close-pr`, `pr-review-comments` y `pr-worktree`.
|
|
18
|
+
|
|
19
|
+
## Nombre de la rama
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
issue-flow issue branch 42 57 # propone la rama de cada issue, sin escribir nada
|
|
23
|
+
issue-flow issue branch --missing # la de cada issue abierta sin bloque, y lo agrega tras confirmar
|
|
24
|
+
issue-flow issue branch --missing --dry-run --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
La issue guarda solo el slug, en un bloque así:
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
**Slug de rama**
|
|
31
|
+
`cart-total-with-credit`
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
El número es el de la issue y el tipo sale de las labels (o, si no hay, del prefijo del título: `bug:` da `fix`). Así el cuerpo se escribe antes de que la issue tenga número, de una sola vez, y no queda viejo si cambia el label o la plantilla. Es lo mismo que hacen GitHub, GitLab y Linear: la rama se calcula al crearla.
|
|
35
|
+
|
|
36
|
+
La rama se arma con la plantilla del repo y el slug sale, en este orden:
|
|
37
|
+
|
|
38
|
+
1. **Del bloque "Slug de rama"** de la issue.
|
|
39
|
+
2. **Del bloque antiguo "Rama sugerida"**, con la rama completa (`fix/42-cart`), si es `<prefijo>/<n>-<slug>` de esa issue. Se usa tal cual, así las issues de antes siguen funcionando; nunca se escribe.
|
|
40
|
+
3. **De Claude** (`claude -p`, una sola llamada para todas las issues): Claude propone el slug en inglés y, si la issue no tiene tipo, lo elige de la lista del repo. Su respuesta se valida.
|
|
41
|
+
4. **Del título**, si Claude no está, falla o responde algo que no sirve.
|
|
42
|
+
|
|
43
|
+
`--missing` agrega el bloque "Slug de rama" después del primer párrafo de cada issue abierta sin bloque (ni nuevo ni antiguo), solo con `--yes` o si respondes que sí en la terminal; `--dry-run` nunca escribe. Como solo guarda el slug, no usa el tipo que elegiría Claude: muestra la rama que dará la issue y avisa de las que no tienen label de tipo.
|
|
44
|
+
|
|
45
|
+
## Empezar una issue
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
issue-flow issue start 42 # worktree en .claude/worktrees/, PR en draft y Claude
|
|
49
|
+
issue-flow issue start 42 --dry-run # el resumen, sin cambiar nada (solo hace git fetch)
|
|
50
|
+
issue-flow issue start 42 --yes --no-launch # lo que corre la skill después de tu OK
|
|
51
|
+
issue-flow issue start 42 --plan # una línea JSON con la rama y el pedido, sin efectos
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Qué hace, en orden:
|
|
55
|
+
|
|
56
|
+
1. **Lee la issue y su trabajo:** se detiene si la issue está cerrada; hace `git fetch` y lee con [`github-rest`](https://github.com/ramon-sg/github-rest) sus PRs y ramas, en GitHub y en esta máquina.
|
|
57
|
+
2. **Elige la rama:**
|
|
58
|
+
- **Sin ramas vivas:** crea una nueva, con el nombre de `issue branch`. Si ese nombre ya se usó en una tanda mergeada, le suma `-2`, `-3`…
|
|
59
|
+
- **Con una:** la retoma.
|
|
60
|
+
- **Con varias:** en la terminal te pregunta cuál seguir. Con `--yes` hay que elegir con `--resume <rama>` o `--new`.
|
|
61
|
+
3. **Muestra un resumen y pregunta `[y/N]`.** Una respuesta vacía es no.
|
|
62
|
+
4. **Crea lo que falta:**
|
|
63
|
+
- el worktree, o con `--branch` la rama en este checkout;
|
|
64
|
+
- el commit vacío con `[skip ci]`, el push y el PR en draft;
|
|
65
|
+
- reabre el PR cerrado de una rama que se retoma, y te asigna la issue.
|
|
66
|
+
|
|
67
|
+
El PR dice `Closes #n`, o `Part of #n` si la issue tiene otra rama viva; en ese caso los otros PRs abiertos que la cerraban también pasan a `Part of`. El worktree recibe los archivos ignorados que lista `.worktreeinclude` (credenciales, por ejemplo), como `claude --worktree`.
|
|
68
|
+
5. **Abre Claude en la misma terminal** con un pedido y el color de la prioridad.
|
|
69
|
+
- Cada rama guarda su sesión, así que empezar otra vez la retoma.
|
|
70
|
+
- Mientras Claude corre, el worktree queda bloqueado (`git worktree lock`). Si el bloqueo es de un `issue-flow` que ya no corre, lo toma.
|
|
71
|
+
6. **Al salir de Claude:**
|
|
72
|
+
- Si el PR se mergeó y no queda trabajo sin pushear, ofrece borrar el worktree y la rama.
|
|
73
|
+
- Si no, dice cómo retomar.
|
|
74
|
+
|
|
75
|
+
**Permisos:** si el repo tiene [`claude-repo-guard`](https://github.com/ramon-sg/claude-repo-guard), Claude se abre en modo auto con su contexto (lo mismo que `claude:auto`). Con `--ask-permissions` se abre con tus settings, y con `--free` sin sandbox ni preguntas (como `claude:free`). Sin `claude-repo-guard`, se abre con tus settings. `--plan` devuelve esos mismos argumentos en `claudeArgs`.
|
|
76
|
+
|
|
77
|
+
### Con la nube y con otras herramientas
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
issue-flow issue start 42 --cloud # confirma acá y abre una sesión de Claude en la nube
|
|
81
|
+
issue-flow issue start 42 --here # dentro de esa sesión: empieza la issue en este checkout
|
|
82
|
+
issue-flow issue start 42 --teleport # trae al worktree la sesión de la nube anotada en el PR
|
|
83
|
+
issue-flow issue start 42 --adopt # dentro de un worktree que creó Orca o Paseo
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **`--cloud`** lee la issue, muestra el resumen y pregunta, igual que en local. Después corre `claude --cloud "/start-issue 42 --yes"` desde el checkout principal, sin crear rama ni PR acá: los crea la sesión con `--here`. Si la issue tiene varias ramas, le pasa la elegida con `--resume <rama>` o `--new`. Se detiene si la rama ya está abierta en un checkout o existe solo en local, porque la nube no la vería.
|
|
87
|
+
- **`--here`** es para la skill dentro de una sesión en la nube. Deja este checkout en la rama (desde `origin/<base>`, o desde `origin/<rama>` si ya existe), hace el commit vacío, el push, el PR en draft y la asignación, y no abre Claude. Si corre en una sesión (`CLAUDE_CODE_REMOTE_SESSION_ID`), anota su link en el cuerpo del PR (`Cloud session: …`); queda una sola línea, la de la última sesión. Termina con una línea `Prompt: …` que la skill sigue. Se detiene si hay cambios sin commitear: no hace stash.
|
|
88
|
+
- **`--teleport`** retoma como siempre (worktree, PR) y abre `claude --teleport` con la sesión anotada en el PR, o con la lista de sesiones si no hay ninguna. Sin `--teleport`, retomar abre la sesión local y el resumen avisa si hay una en la nube.
|
|
89
|
+
- **`--adopt`** usa el worktree donde se corre, que creó otra herramienta. No pregunta ni abre Claude. Renombra su rama a la de la issue y publica; si la rama ya lleva el número de la issue (`fix/42-mi-nombre`), la deja como está. Si el worktree ya está en una rama viva de la issue, la retoma. Si no, solo adopta un worktree recién creado: se detiene si su rama está en origin, tiene commits propios o hay cambios sin commitear, o si la issue ya tiene su rama en otro lado.
|
|
90
|
+
|
|
91
|
+
## Worktrees: preparar, limpiar y borrar
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
issue-flow worktree setup # corre los pasos de preparación en este worktree
|
|
95
|
+
issue-flow worktree setup --only deps # uno solo
|
|
96
|
+
issue-flow worktree teardown # sus pasos de limpieza, sus puertos y su Docker
|
|
97
|
+
issue-flow worktree ports [--next] # el primer puerto de su bloque, o lo pasa al siguiente
|
|
98
|
+
issue-flow worktree cleanup # el Docker de los worktrees que ya no existen
|
|
99
|
+
issue-flow worktree prune # borra los worktrees cuyo PR se mergeó o se cerró
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Lo propio de cada repo (instalar, armar la base, levantar Docker) son **pasos** en `worktrees` de `.agents/flow.json`; `issue-flow` solo los corre:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"worktrees": {
|
|
107
|
+
"setup": [
|
|
108
|
+
{ "name": "deps", "run": "npm ci", "trusted": true, "once": true },
|
|
109
|
+
{ "name": "env", "run": "cp -n .env.example .env" }
|
|
110
|
+
],
|
|
111
|
+
"teardown": [{ "name": "docker", "run": "docker compose -p acme-dev-$ISSUE_FLOW_DOCKER_SUFFIX down -v" }],
|
|
112
|
+
"ports": true,
|
|
113
|
+
"dockerProjects": ["acme-dev"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- **Cuándo corren:** `issue start` corre el `setup` en cada worktree nuevo, después de copiar `.worktreeinclude`. El hook `session` lo corre al abrir Claude en un worktree. El `teardown` corre antes de borrar un worktree, con `prune` o al limpiar después del merge.
|
|
119
|
+
- **De dónde salen:** siempre de la config del checkout principal, nunca de la del worktree. Así una rama no puede sumar comandos a su propio setup. En la nube no hay otro checkout: ahí la config es la de la rama de la sesión.
|
|
120
|
+
- **Pueden correr más de una vez:** `issue start` y el hook `session` corren el `setup` del mismo worktree, así que un paso sin `once` tiene que poder repetirse.
|
|
121
|
+
- **`trusted`:** el paso corre solo si el código del worktree está en `origin/<base>` y no tiene cambios. Es para un install, que corre los scripts de `package.json` de esa rama. En la nube corre siempre, porque la máquina es descartable.
|
|
122
|
+
- **`once`:** el paso no se repite una vez que funcionó (deja una marca en el git dir del worktree).
|
|
123
|
+
- **Variables de cada paso:** `ISSUE_FLOW_WORKTREE`, `ISSUE_FLOW_MAIN_ROOT`, `ISSUE_FLOW_DOCKER_SUFFIX` y, con `ports`, `ISSUE_FLOW_PORT_BASE`.
|
|
124
|
+
- **Puertos:** cada worktree recibe un bloque fijo. Sale de un hash de la máquina y la carpeta, salta los bloques donde algo escucha o que otro worktree de la máquina registró, y queda guardado. macOS usa la primera mitad del rango y el resto la segunda, así un worktree de la laptop no choca con uno del servidor que Orca reenvía.
|
|
125
|
+
- **Docker:** el proyecto de Compose de cada worktree es `<prefijo>-$ISSUE_FLOW_DOCKER_SUFFIX`. `cleanup` borra contenedores, volúmenes y redes de los proyectos con un prefijo de `dockerProjects` cuya carpeta ya no es un checkout. Nunca toca uno sin sufijo, el del checkout principal.
|
|
126
|
+
- **Apps:** el `teardown` detiene los procesos que escuchan en un puerto del bloque y corren dentro del worktree (TERM a su grupo y, a los 5 segundos, KILL). Nunca toca uno de afuera. Necesita `lsof`.
|
|
127
|
+
- **`prune`** pregunta antes de borrar. Nunca toca un worktree con cambios, con commits sin pushear o bloqueado por una sesión de Claude, y revisa cada uno otra vez justo antes de borrarlo.
|
|
128
|
+
|
|
129
|
+
Los nombres de Docker y los bloques de puertos salen iguales que los de Abastio.
|
|
130
|
+
|
|
131
|
+
## Revisar un PR: `issue-flow pr worktree`
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
issue-flow pr worktree 42 # crea (o reusa) .claude/worktrees/pr-42 en la rama del PR
|
|
135
|
+
issue-flow pr worktree 42 -- claude # y corre el comando ahí; con `--` solo, abre Claude
|
|
136
|
+
issue-flow pr worktree 42 --no-setup # sin los pasos de setup
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- La rama sigue a `origin`, así que lo que commiteas y pusheas actualiza el PR. Si ya tienes la rama en local, avanza a la de origin cuando puede y avisa si no.
|
|
140
|
+
- Copia `.worktreeinclude` y corre el `setup`, pero salta los pasos `trusted`: el código del PR todavía no está en la rama base. Dice qué correr cuando lo revisaste.
|
|
141
|
+
- Nunca toca el checkout principal: si la rama está abierta ahí o en otro worktree, se detiene. Un PR de un fork no se abre, porque su rama no está en origin.
|
|
142
|
+
- Se borra con `issue-flow worktree prune` cuando el PR se mergea o se cierra, como cualquier worktree.
|
|
143
|
+
|
|
144
|
+
## Orca y Paseo: `issue-flow adapter`
|
|
145
|
+
|
|
146
|
+
`orca.yaml` y `paseo.json` solo llaman al adaptador, que deja el worktree igual que uno de `issue start`:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
# orca.yaml
|
|
150
|
+
setupAgentStartupPolicy: wait-for-setup
|
|
151
|
+
scripts:
|
|
152
|
+
setup: npx @ramon-sg/issue-flow adapter setup
|
|
153
|
+
archive: npx @ramon-sg/issue-flow adapter archive
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{ "worktree": { "setup": ["npx @ramon-sg/issue-flow adapter setup"], "teardown": "npx @ramon-sg/issue-flow adapter archive" } }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- **`setup [--only <paso>]`:** copia `.worktreeinclude` (Paseo no lo copia), corre los pasos de `setup` y, sin `--only`, empieza la issue de la que salió el worktree como `issue start <n> --adopt`. Con Paseo la issue sale de la rama (`feat/42-x`, `42-x`); con Orca, de su CLI (`orca worktree current --json`). Siempre sale con 0: si algo falla, dice qué correr.
|
|
161
|
+
- **`issue [<url>]`:** solo empieza la issue. Con la URL a mano sale con el código de `issue start`.
|
|
162
|
+
- **`archive`:** el `teardown` del worktree: pasos, apps, puertos y Docker. Siempre sale con 0, porque un archive que falla no puede bloquear el borrado. Si el enlace de git se rompió, igual borra el Docker de la carpeta con la config del checkout que nombran `ORCA_ROOT_PATH` o `PASEO_SOURCE_CHECKOUT_PATH`.
|
|
163
|
+
|
|
164
|
+
## Hooks de Claude Code para `claude --worktree`
|
|
165
|
+
|
|
166
|
+
`issue-flow hook worktree-create` y `issue-flow hook worktree-remove` son los hooks `WorktreeCreate` y `WorktreeRemove`. Con ellos, un worktree de `claude --worktree <nombre>` recibe lo mismo que uno de `issue start`, y se limpia cuando Claude lo borra. Claude solo llama a `WorktreeRemove` para los worktrees que hizo un hook `WorktreeCreate`.
|
|
167
|
+
|
|
168
|
+
- **`worktree-create`:** crea `<worktrees.dir>/<nombre>` en la rama `worktree-<nombre>` desde `origin/<base>` (la `baseBranch` de la config), o lo reusa. Copia `.worktreeinclude`, corre el `setup` e imprime solo la ruta, que es lo que Claude lee. Rechaza `pr-<n>`: para eso está `pr worktree`.
|
|
169
|
+
- **`worktree-remove`:** corre el `teardown` y borra la carpeta, y la rama si todos sus commits están en origin. Conserva un worktree con cambios sin commitear.
|
|
170
|
+
|
|
171
|
+
## Hook de sesión: `issue-flow session`
|
|
172
|
+
|
|
173
|
+
Es el hook `SessionStart` de Claude Code. Lo que imprime le llega a Claude como contexto, así que ahí van los avisos. Nunca hace fallar la sesión.
|
|
174
|
+
|
|
175
|
+
1. En una sesión en la nube (`CLAUDE_CODE_REMOTE=true`), corre `claude-cloud session`. Si no está, dice qué setup script poner en claude.ai.
|
|
176
|
+
2. En un worktree (o en el checkout de la nube), corre los pasos de `setup`. La carpeta sale del `cwd` que manda Claude, que con `claude --worktree` es la del worktree, y si no viene, de `CLAUDE_PROJECT_DIR`.
|
|
177
|
+
3. Si un worktree local está en la rama de una issue que no está en origin, avisa que la issue no quedó empezada.
|
|
178
|
+
4. Borra el Docker de los worktrees que ya no existen: Claude Code no llama al hook `WorktreeRemove` de los worktrees que no hizo un hook `WorktreeCreate`.
|
|
179
|
+
5. Si hay `.envrc` y `direnv`, agrega su entorno a `CLAUDE_ENV_FILE`, para que los comandos de la sesión lo tengan.
|
|
180
|
+
|
|
181
|
+
## Lo que el flujo deja en el repo: `issue-flow init`
|
|
182
|
+
|
|
183
|
+
`npx @ramon-sg/agent-kit` corre estos tres comandos en la versión que corresponde. Correrlos a mano sirve para probar el flujo en un repo sin lo demás.
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
npx @ramon-sg/issue-flow init # pregunta cada receta y pide el OK
|
|
187
|
+
npx @ramon-sg/issue-flow init --yes # sin preguntas, con las recetas que sugiere el repo
|
|
188
|
+
npx @ramon-sg/issue-flow doctor # revisa que el repo esté como lo deja init (--json para agent-kit)
|
|
189
|
+
npx @ramon-sg/issue-flow@<versión> upgrade # mueve el repo a esa versión (--dry-run)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`init` muestra lo que va a escribir y pide el OK; correrlo otra vez solo completa lo que falta.
|
|
193
|
+
|
|
194
|
+
1. **`.agents/flow.json`:** si no existe, lo crea con el `$schema` de esta versión y, si el repo tiene `paseo.json`, con su plantilla de rama y sus tipos. Suma las recetas que elijas.
|
|
195
|
+
2. **`package.json`:** `@ramon-sg/issue-flow`, `@ramon-sg/github-rest` y `@ramon-sg/skills-manager` como dependencias de desarrollo con versión exacta, y los scripts `issue:start`, `worktree:prune` y `pr:worktree`. Lo que el repo ya tiene con otro valor se queda, con un aviso.
|
|
196
|
+
3. **`.claude/settings.json`:** los hooks `SessionStart`, `WorktreeCreate` y `WorktreeRemove`, fijados a esta versión y con 600 segundos. El de `SessionStart` corre `claude-cloud session` en la nube, así que un hook propio de `claude-cloud` se quita para que no corra dos veces.
|
|
197
|
+
|
|
198
|
+
**`doctor`** no cambia nada. Revisa la config, las dependencias y que los hooks estén en esta versión. Cada problema dice qué lo arregla.
|
|
199
|
+
|
|
200
|
+
**`upgrade`** mueve el repo a la versión que corre: las dependencias a las que fija, los hooks y el `$schema`. No consulta npm, así que se corre con la versión: `npx @ramon-sg/issue-flow@<versión> upgrade`. Nunca baja de versión: si el repo ya tiene una más nueva (por ejemplo, una copia local vieja en `node_modules`), se detiene y dice qué correr. Las skills del flujo llaman a la CLI, así que tienen que ir a la misma tag: `agent-kit` las mueve después. `--dry-run` solo dice qué cambiaría.
|
|
201
|
+
|
|
202
|
+
### Recetas
|
|
203
|
+
|
|
204
|
+
Piezas de `flow.json` para lo que muchos repos comparten. `init` ofrece las que sugieren los archivos del repo; `--recipe a,b` elige otras. Una receta solo agrega lo que falta: un paso con el mismo nombre o una clave que el repo ya tiene no se tocan.
|
|
205
|
+
|
|
206
|
+
| Receta | La sugiere | Qué agrega |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `npm`, `pnpm`, `bun` | Su lockfile | El paso `deps`, `trusted`, que instala sin cambiar el lockfile |
|
|
209
|
+
| `docker-compose` | `compose.yaml` o `docker-compose.yml` | Un proyecto de Compose y un bloque de puertos por worktree: `up -d` al preparar y `down -v` al limpiar. El compose toma sus puertos de `ISSUE_FLOW_PORT_BASE` |
|
|
210
|
+
| `direnv` | `.envrc` | `direnv allow` en cada worktree, para que carguen su entorno y sus secretos (Infisical, por ejemplo) |
|
|
211
|
+
|
|
212
|
+
Los pasos de preparación de las recetas son `trusted`: corren el código del worktree (el lockfile, el compose, el `.envrc`), así que en un worktree con código que no está en la rama base, como el de `pr worktree`, se saltan y el comando dice qué correr cuando lo revisaste.
|
|
213
|
+
| `staging-main` | Nada: se pide | `baseBranch: "staging"`; `agent-kit` deja a `claude-repo-guard` mergear a `staging` |
|
|
214
|
+
|
|
215
|
+
## Skills del flujo
|
|
216
|
+
|
|
217
|
+
Viven en `skills/` y `skills-catalog.json` las lista. Se instalan con [`skills-manager`](https://github.com/ramon-sg/skills-manager), que las copia a `.agents/skills/` del repo para que lleguen también a la nube. Están en español y llaman a `npx @ramon-sg/issue-flow` y `npx @ramon-sg/github-rest`, que el repo tiene como dependencias de desarrollo. Van siempre con el scope: `issue-flow`, `github-rest` y `skills-manager` sin scope son paquetes de otras personas en npm, y `npx` los bajaría y correría si el repo no tiene los suyos instalados.
|
|
218
|
+
|
|
219
|
+
| Skill | Categoría | Para qué sirve | Trae también |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| `github-issues` | Issues | Una issue corta con cabecera de tres líneas, criterios comprobables y el slug de su rama; comprueba duplicados y el tope de palabras | — |
|
|
222
|
+
| `start-issue` | Issues | Empieza una issue desde una sesión con `issue-flow issue start`, en local, en un worktree o en la nube | — |
|
|
223
|
+
| `close-pr` | PRs | Auditoría, documentos al día, ready, revisión y CI, y merge con merge commit solo con un review aprobado | `pr-review-comments` |
|
|
224
|
+
| `pr-review-comments` | PRs | Verifica cada comentario y check contra el código, propone un plan, aplica lo aprobado y responde los hilos | `github-issues` |
|
|
225
|
+
| `pr-worktree` | PRs | Revisa un PR en su worktree con `issue-flow pr worktree`, sin tocar el checkout principal | `close-pr` |
|
|
226
|
+
|
|
227
|
+
`agent-kit` las instala con el catálogo en la tag de la versión del flujo y las mueve con él. `close-pr` y `pr-worktree` usan `code-audit`, del catálogo [`agent-skills`](https://github.com/ramon-sg/agent-skills): `agent-kit` ofrece los dos.
|
|
228
|
+
|
|
229
|
+
**Lo propio de cada repo va en `project.md`**, que cada skill (salvo `pr-worktree`) crea desde su plantilla al instalarse: los labels y el assignee, qué flujo sigue cada issue, qué corre sin preguntar al cerrar, qué bots revisan y qué cuenta como review aprobado. Ese archivo es del repo: `check` no lo marca y las actualizaciones lo conservan.
|
|
230
|
+
|
|
231
|
+
**En la nube** las skills no usan `gh pr` ni `gh issue`, que van por GraphQL. Sacar un PR de draft y resolver un hilo solo existen en GraphQL, y el merge va con `gh pr merge`, que el modo auto de Claude Code revisa. En local las tres van con `gh`; en la nube las skills se detienen y lo dicen. Si el sandbox bloquea algo, no lo reintentan de otra forma: muestran qué correr en una terminal.
|
|
232
|
+
|
|
233
|
+
## Config: `.agents/flow.json`
|
|
234
|
+
|
|
235
|
+
Todo es opcional; sin el archivo valen los valores por defecto. Una clave desconocida es un error, así un error de tipeo no pasa en silencio. Se lee siempre del checkout principal, también desde un worktree. `schema.json` da autocompletado en el editor:
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"$schema": "https://raw.githubusercontent.com/ramon-sg/issue-flow/main/schema.json",
|
|
240
|
+
"branch": {
|
|
241
|
+
"template": "{type}/{number}-{slug}",
|
|
242
|
+
"namer": "claude",
|
|
243
|
+
"types": {
|
|
244
|
+
"labels": { "bug": "fix", "enhancement": "feat", "documentation": "docs", "spike": "spike" },
|
|
245
|
+
"titlePrefixes": { "bug:": "fix", "mejora:": "feat", "docs:": "docs", "spike:": "spike" }
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
| Clave | Por defecto | Para qué |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| `repo` | `-R`, `GH_REPO` u `origin` | El repo en GitHub |
|
|
254
|
+
| `baseBranch` | La rama por defecto del repo | De dónde parte el trabajo |
|
|
255
|
+
| `branch.template` | `{type}/{number}-{slug}` | Nombre de la rama; necesita `{number}` y `{slug}` |
|
|
256
|
+
| `branch.namer` | `claude` | `claude` o `title` |
|
|
257
|
+
| `branch.claudeModel` | `haiku` | Modelo de `claude -p` |
|
|
258
|
+
| `branch.types` | Los de arriba | Tipo por label y por prefijo; cada mapa reemplaza al de por defecto |
|
|
259
|
+
| `worktrees.dir` | `.claude/worktrees` | Dónde van los worktrees |
|
|
260
|
+
| `worktrees.setup`, `worktrees.teardown` | Ninguno | Pasos para preparar y limpiar un worktree (ver arriba) |
|
|
261
|
+
| `worktrees.ports` | Apagado | `true` para bloques de 10 desde 20000, o `{ start, blockSize, blocks }` |
|
|
262
|
+
| `worktrees.dockerProjects` | Ninguno | Prefijos de los proyectos de Compose de los worktrees |
|
|
263
|
+
| `start.commitMessage` | `chore: empieza el trabajo en la issue #{number} [skip ci]` | Commit vacío inicial |
|
|
264
|
+
| `start.prBody` | `{keyword} #{number}` y "En progreso." | Cuerpo del PR en draft |
|
|
265
|
+
| `start.prompt`, `start.resumePrompt` | Resumir la issue y seguir `start-issue`, o retomar desde `{plan}` | Pedido con que se abre Claude |
|
|
266
|
+
| `start.summaryPrompt`, `start.summaryPlanPrompt` | Resumir y esperar | Pedido de `--plan` |
|
|
267
|
+
| `start.cloudPrompt` | `/start-issue {number} --yes{flags}` | Lo que `--cloud` le pide a la sesión; `{flags}` es `--new` o `--resume <rama>` |
|
|
268
|
+
| `plans.dirs` | `docs/plans`, `docs/specs` | Dónde buscar el plan de la issue: lo que tocó la rama, o el documento cuyo `prs:` nombra su PR |
|
|
269
|
+
| `colors` | `critical-path` y `P1:` rojo, `P2:` naranjo, `P3:` amarillo, `P4:` cian | Color de la sesión por prefijo de label; gana el primero que calce |
|
|
270
|
+
|
|
271
|
+
## Desarrollo
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
npm install && npm test
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Usa [`github-rest`](https://github.com/ramon-sg/github-rest) para hablar con GitHub solo por REST (en la nube GraphQL está bloqueado) y [`skills-manager`](https://github.com/ramon-sg/skills-manager) para instalar las skills. Los tests de las skills comprueban que el catálogo y las carpetas coincidan, que ninguna nombre el proyecto del que salió, que cada comando de `issue-flow` que nombran exista, el script de `github-issues` y una instalación completa con `skills-manager`. Las skills generales (`planning`, `code-audit`, …) están en [`agent-skills`](https://github.com/ramon-sg/agent-skills).
|
|
278
|
+
|
|
279
|
+
## Publicar una versión
|
|
280
|
+
|
|
281
|
+
Una versión es una release `vX.Y.Z` en GitHub, con el mismo número que `package.json`. Al publicarla, GitHub Actions corre los tests y publica en npm con procedencia (`.github/workflows/publish.yml`). La tag es también la versión de las skills del flujo: `agent-kit` instala el catálogo en la tag de la versión del flujo, así que la CLI y sus skills van siempre juntas.
|
|
282
|
+
|
|
283
|
+
1. Un PR que sube `version` en `package.json`.
|
|
284
|
+
2. Después del merge, una release `v<versión>` sobre ese commit de `main`.
|
|
285
|
+
|
|
286
|
+
El workflow se detiene si la tag no coincide con `package.json`.
|
|
287
|
+
|
|
288
|
+
**La primera versión (0.1.0) va a mano**, porque npm solo deja configurar la publicación desde GitHub en un paquete que ya existe:
|
|
289
|
+
|
|
290
|
+
1. Desde `main`, con sesión en npm (`npm login`): `npm publish --provenance=false`. La procedencia solo se genera dentro de un CI.
|
|
291
|
+
2. En npmjs.com, configurar la publicación desde GitHub (trusted publishing) para `publish.yml`.
|
|
292
|
+
3. Pushear la tag `v0.1.0` sin crear una release: la necesitan las skills y el `$schema`, y una release volvería a publicar la misma versión y fallaría.
|
|
293
|
+
|
|
294
|
+
Desde 0.2.0, cada versión es una release y la publica el workflow.
|
|
295
|
+
|
|
296
|
+
## Licencia
|
|
297
|
+
|
|
298
|
+
MIT
|
package/dist/ask.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Questions on the terminal, one line each. It reads stdin as it comes (a terminal
|
|
3
|
+
* or a pipe), keeps what was typed ahead for the next question, and pauses stdin between
|
|
4
|
+
* questions so Claude, opened later, gets the terminal to itself.
|
|
5
|
+
*/
|
|
6
|
+
import type { Output } from "./output.js";
|
|
7
|
+
/**
|
|
8
|
+
* Drops what was typed ahead and not used, so it never answers a question asked later
|
|
9
|
+
* (after Claude, the one about deleting the worktree).
|
|
10
|
+
* @example forgetTypedAhead()
|
|
11
|
+
*/
|
|
12
|
+
export declare function forgetTypedAhead(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Asks a question and returns the answer, trimmed.
|
|
15
|
+
* @param out - Where the question goes (stderr, so stdout stays for results).
|
|
16
|
+
* @param question - The question, with its options.
|
|
17
|
+
* @returns The answer; empty if stdin ended.
|
|
18
|
+
* @example await ask(out, "Start the issue? [y/N] ")
|
|
19
|
+
*/
|
|
20
|
+
export declare function ask(out: Output, question: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Asks a yes or no question; anything but a yes, an empty answer included, is no.
|
|
23
|
+
* @param out - Where the question goes.
|
|
24
|
+
* @param question - The question, without `[y/N]`.
|
|
25
|
+
* @returns True if the answer is yes.
|
|
26
|
+
* @example await confirm(out, "Start the issue?")
|
|
27
|
+
*/
|
|
28
|
+
export declare function confirm(out: Output, question: string): Promise<boolean>;
|
package/dist/ask.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** What was read and not used yet. */
|
|
2
|
+
let pending = "";
|
|
3
|
+
/** Whether stdin ended. */
|
|
4
|
+
let ended = false;
|
|
5
|
+
/**
|
|
6
|
+
* The next line of stdin, without its end.
|
|
7
|
+
* @returns The line, or null if stdin ended with nothing left.
|
|
8
|
+
*/
|
|
9
|
+
function readLine() {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
const take = () => {
|
|
12
|
+
const end = pending.indexOf("\n");
|
|
13
|
+
if (end === -1 && !ended)
|
|
14
|
+
return false;
|
|
15
|
+
const line = end === -1 ? pending : pending.slice(0, end);
|
|
16
|
+
pending = end === -1 ? "" : pending.slice(end + 1);
|
|
17
|
+
cleanUp();
|
|
18
|
+
resolve(end === -1 && line === "" ? null : line.replace(/\r$/, ""));
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
const onData = (chunk) => {
|
|
22
|
+
pending += chunk;
|
|
23
|
+
take();
|
|
24
|
+
};
|
|
25
|
+
const onEnd = () => {
|
|
26
|
+
ended = true;
|
|
27
|
+
take();
|
|
28
|
+
};
|
|
29
|
+
const cleanUp = () => {
|
|
30
|
+
process.stdin.off("data", onData);
|
|
31
|
+
process.stdin.off("end", onEnd);
|
|
32
|
+
process.stdin.pause();
|
|
33
|
+
};
|
|
34
|
+
if (take())
|
|
35
|
+
return;
|
|
36
|
+
process.stdin.setEncoding("utf8");
|
|
37
|
+
process.stdin.on("data", onData);
|
|
38
|
+
process.stdin.on("end", onEnd);
|
|
39
|
+
process.stdin.resume();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Drops what was typed ahead and not used, so it never answers a question asked later
|
|
44
|
+
* (after Claude, the one about deleting the worktree).
|
|
45
|
+
* @example forgetTypedAhead()
|
|
46
|
+
*/
|
|
47
|
+
export function forgetTypedAhead() {
|
|
48
|
+
pending = "";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Asks a question and returns the answer, trimmed.
|
|
52
|
+
* @param out - Where the question goes (stderr, so stdout stays for results).
|
|
53
|
+
* @param question - The question, with its options.
|
|
54
|
+
* @returns The answer; empty if stdin ended.
|
|
55
|
+
* @example await ask(out, "Start the issue? [y/N] ")
|
|
56
|
+
*/
|
|
57
|
+
export async function ask(out, question) {
|
|
58
|
+
out.stderr(question);
|
|
59
|
+
const answer = await readLine();
|
|
60
|
+
if (answer === null)
|
|
61
|
+
out.stderr("\n");
|
|
62
|
+
return (answer ?? "").trim();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Asks a yes or no question; anything but a yes, an empty answer included, is no.
|
|
66
|
+
* @param out - Where the question goes.
|
|
67
|
+
* @param question - The question, without `[y/N]`.
|
|
68
|
+
* @returns True if the answer is yes.
|
|
69
|
+
* @example await confirm(out, "Start the issue?")
|
|
70
|
+
*/
|
|
71
|
+
export async function confirm(out, question) {
|
|
72
|
+
return /^(y|yes|s|si|sí)$/i.test(await ask(out, `${question} [y/N] `));
|
|
73
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Branch names of issues, with no git, GitHub nor Claude: read the "Slug de rama"
|
|
3
|
+
* block (or the older "Rama sugerida" one, with the whole branch), check that a name fits
|
|
4
|
+
* the issue, find the issue's type, make a slug from the title and render the repo's
|
|
5
|
+
* template. Ported from Abastio's `scripts/lib/issue.sh` and `scripts/lib/project.sh`.
|
|
6
|
+
*/
|
|
7
|
+
import type { TypeRules } from "./config.js";
|
|
8
|
+
/**
|
|
9
|
+
* Start of the block that holds the branch slug in the issue body, as the `github-issues`
|
|
10
|
+
* skill writes it. Only the slug: the number is the issue's and the type comes from its
|
|
11
|
+
* labels, so the block can be written before the issue has a number and never goes stale.
|
|
12
|
+
* Without the closing `**`, so `**Slug de rama:**` also matches.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SLUG_HEADING = "**Slug de rama";
|
|
15
|
+
/** The full heading written when a block is added. */
|
|
16
|
+
export declare const SLUG_BLOCK_TITLE = "**Slug de rama**";
|
|
17
|
+
/**
|
|
18
|
+
* Start of the older block, with the whole branch (`fix/42-cart`), that Abastio and Clean
|
|
19
|
+
* Queen issues still have. It is read, never written.
|
|
20
|
+
*/
|
|
21
|
+
export declare const BRANCH_HEADING = "**Rama sugerida";
|
|
22
|
+
/** Longest slug made from a title (the `github-issues` skill asks for about 40). */
|
|
23
|
+
export declare const SLUG_MAX = 40;
|
|
24
|
+
/** A valid slug: lowercase words of letters and digits joined by single dashes. */
|
|
25
|
+
export declare const SLUG: RegExp;
|
|
26
|
+
/**
|
|
27
|
+
* The slug of the "Slug de rama" block.
|
|
28
|
+
* @param body - The issue body.
|
|
29
|
+
* @returns The slug as written (maybe invalid), or null if there is no block.
|
|
30
|
+
* @example suggestedSlug("**Slug de rama**\n`cart-total`") // "cart-total"
|
|
31
|
+
*/
|
|
32
|
+
export declare function suggestedSlug(body: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* The branch of the older "Rama sugerida" block.
|
|
35
|
+
* @param body - The issue body.
|
|
36
|
+
* @returns The branch, or null if the block is missing or has no backticks.
|
|
37
|
+
* @example suggestedBranch("**Rama sugerida**\n`fix/9-a`") // "fix/9-a"
|
|
38
|
+
*/
|
|
39
|
+
export declare function suggestedBranch(body: string): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the body already has a branch block, new or old.
|
|
42
|
+
* @param body - The issue body.
|
|
43
|
+
* @returns True if it has one.
|
|
44
|
+
* @example hasBranchBlock("**Rama sugerida**\n`fix/9-a`") // true
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasBranchBlock(body: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a value is a slug this tool accepts, from a person or from Claude: lowercase
|
|
49
|
+
* words joined by single dashes (as `start-issue` checks), at most `SLUG_MAX` characters.
|
|
50
|
+
* @param value - The value.
|
|
51
|
+
* @returns True if it is one.
|
|
52
|
+
* @example isSlug("cart-total") // true
|
|
53
|
+
*/
|
|
54
|
+
export declare function isSlug(value: unknown): value is string;
|
|
55
|
+
/**
|
|
56
|
+
* Why a branch does not fit the issue: it must be `<prefix>/<n>-<slug>`, with any number
|
|
57
|
+
* of prefix folders (or none) and a lowercase slug. No part may start with `.` or `-`, nor
|
|
58
|
+
* hold `..`: the branch later becomes a folder under the worktrees directory.
|
|
59
|
+
* @param branch - The branch, or null if the issue has none.
|
|
60
|
+
* @param number - The issue number.
|
|
61
|
+
* @returns The reason, or null if it fits.
|
|
62
|
+
* @example branchProblem("feat/21-x", 214) // "the suggested branch \"feat/21-x\" is not <prefix>/214-<slug>"
|
|
63
|
+
*/
|
|
64
|
+
export declare function branchProblem(branch: string | null, number: number): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* The type of an issue: by label first (in the order of the config), then by title prefix
|
|
67
|
+
* ignoring case.
|
|
68
|
+
* @param types - The repo's rules.
|
|
69
|
+
* @param labels - The issue's labels.
|
|
70
|
+
* @param title - The issue's title.
|
|
71
|
+
* @returns The type, or null if nothing matches.
|
|
72
|
+
* @example branchType(rules, ["bug"], "x") // "fix"
|
|
73
|
+
*/
|
|
74
|
+
export declare function branchType(types: TypeRules, labels: string[], title: string): string | null;
|
|
75
|
+
/**
|
|
76
|
+
* Every type the config can give, without repeats, in order.
|
|
77
|
+
* @param types - The repo's rules.
|
|
78
|
+
* @returns The types.
|
|
79
|
+
* @example typeNames(rules) // ["fix", "feat", "docs", "spike"]
|
|
80
|
+
*/
|
|
81
|
+
export declare function typeNames(types: TypeRules): string[];
|
|
82
|
+
/**
|
|
83
|
+
* Branch slug from the title, for an issue with no usable block: without the
|
|
84
|
+
* type prefix (`bug:`, `mejora:`…) or the phase tag (`[F0-09]`), lowercase, without
|
|
85
|
+
* accents or symbols, cut at `SLUG_MAX` characters by whole words.
|
|
86
|
+
* @param title - The title.
|
|
87
|
+
* @param prefixes - The repo's title prefixes, removed as well.
|
|
88
|
+
* @returns The slug, or `issue` if nothing is left.
|
|
89
|
+
* @example titleSlug("bug: El carro (año 2026)", []) // "el-carro-ano-2026"
|
|
90
|
+
*/
|
|
91
|
+
export declare function titleSlug(title: string, prefixes?: string[]): string;
|
|
92
|
+
/**
|
|
93
|
+
* The branch from the repo's template. Without a type, the separators left over are
|
|
94
|
+
* removed (`{type}/{number}-{slug}` gives `42-slug`).
|
|
95
|
+
* @param template - The template, with `{type}`, `{number}` and `{slug}`.
|
|
96
|
+
* @param type - The type, or null.
|
|
97
|
+
* @param number - The issue number.
|
|
98
|
+
* @param slug - The slug.
|
|
99
|
+
* @returns The branch.
|
|
100
|
+
* @example renderBranch("{type}/{number}-{slug}", "fix", 42, "cart") // "fix/42-cart"
|
|
101
|
+
*/
|
|
102
|
+
export declare function renderBranch(template: string, type: string | null, number: number, slug: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* The issue body with a "Slug de rama" block after its first paragraph (the header the
|
|
105
|
+
* `github-issues` skill writes), or as the whole body if it is empty. If the first
|
|
106
|
+
* paragraph opens a code block, the block goes first, so it never lands inside the code.
|
|
107
|
+
* The rest of the body is kept byte for byte.
|
|
108
|
+
* @param body - The current body.
|
|
109
|
+
* @param slug - The slug.
|
|
110
|
+
* @returns The new body.
|
|
111
|
+
* @example withSlugBlock("Header.\n\nDetail.", "cart") // "Header.\n\n**Slug de rama**\n`cart`\n\nDetail."
|
|
112
|
+
*/
|
|
113
|
+
export declare function withSlugBlock(body: string, slug: string): string;
|