@ramon-sg/issue-flow 0.1.0 → 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/README.md +14 -21
- package/dist/cli-setup.js +3 -3
- package/dist/cli.d.ts +0 -12
- package/dist/cli.js +3 -7
- package/dist/commands/doctor.d.ts +4 -3
- package/dist/commands/doctor.js +29 -63
- package/dist/commands/init.d.ts +2 -5
- package/dist/commands/init.js +8 -85
- package/dist/commands/upgrade.d.ts +5 -5
- package/dist/commands/upgrade.js +42 -72
- package/dist/setup-files.d.ts +2 -43
- package/dist/setup-files.js +5 -52
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +7 -7
- package/package.json +1 -1
- package/recipes/staging-main.json +1 -1
- package/dist/guard-config.d.ts +0 -16
- package/dist/guard-config.js +0 -38
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
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
4
|
|
|
5
|
-
> **Instalar en un repo:** `npx @ramon-sg/
|
|
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
6
|
|
|
7
7
|
## Qué va a tener
|
|
8
8
|
|
|
9
|
-
- **`issue-flow init`:** escribe
|
|
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
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
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
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.
|
|
@@ -178,13 +178,15 @@ Es el hook `SessionStart` de Claude Code. Lo que imprime le llega a Claude como
|
|
|
178
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
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
180
|
|
|
181
|
-
##
|
|
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.
|
|
182
184
|
|
|
183
185
|
```sh
|
|
184
|
-
npx @ramon-sg/issue-flow init # pregunta cada
|
|
186
|
+
npx @ramon-sg/issue-flow init # pregunta cada receta y pide el OK
|
|
185
187
|
npx @ramon-sg/issue-flow init --yes # sin preguntas, con las recetas que sugiere el repo
|
|
186
|
-
npx @ramon-sg/issue-flow doctor # revisa que el repo esté como lo deja init
|
|
187
|
-
npx @ramon-sg/issue-flow upgrade
|
|
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)
|
|
188
190
|
```
|
|
189
191
|
|
|
190
192
|
`init` muestra lo que va a escribir y pide el OK; correrlo otra vez solo completa lo que falta.
|
|
@@ -192,15 +194,10 @@ npx @ramon-sg/issue-flow upgrade # pasa todo a la última versión (o --t
|
|
|
192
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.
|
|
193
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.
|
|
194
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.
|
|
195
|
-
4. **`claude-cloud init`**, después de preguntar (`--no-cloud` lo salta).
|
|
196
|
-
5. **`claude-repo-guard`**, después de preguntar (`--no-guard`). Antes escribe en sus opciones que abrir el PR en draft, asignar la issue y agregar el "Slug de rama" son parte del flujo; la `baseBranch` como `integrationBranch`, si el repo tiene una; y en macOS `weakerNetworkIsolation`, que `gh` necesita dentro del sandbox.
|
|
197
|
-
6. **Las skills**, con el menú de `skills-manager` y los dos catálogos: el de este repo en la tag de esta versión y el de `agent-skills` (`--no-skills`). Sin terminal o con `--yes`, elige las del flujo y `code-audit`.
|
|
198
|
-
|
|
199
|
-
Si una de las herramientas falla, `init` sigue con las demás, dice cómo reintentar y sale con 1.
|
|
200
197
|
|
|
201
|
-
**`doctor`** no cambia nada. Revisa la config, las dependencias
|
|
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.
|
|
202
199
|
|
|
203
|
-
**`upgrade`** mueve
|
|
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.
|
|
204
201
|
|
|
205
202
|
### Recetas
|
|
206
203
|
|
|
@@ -213,7 +210,7 @@ Piezas de `flow.json` para lo que muchos repos comparten. `init` ofrece las que
|
|
|
213
210
|
| `direnv` | `.envrc` | `direnv allow` en cada worktree, para que carguen su entorno y sus secretos (Infisical, por ejemplo) |
|
|
214
211
|
|
|
215
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.
|
|
216
|
-
| `staging-main` | Nada: se pide | `baseBranch: "staging"`; `
|
|
213
|
+
| `staging-main` | Nada: se pide | `baseBranch: "staging"`; `agent-kit` deja a `claude-repo-guard` mergear a `staging` |
|
|
217
214
|
|
|
218
215
|
## Skills del flujo
|
|
219
216
|
|
|
@@ -227,11 +224,7 @@ Viven en `skills/` y `skills-catalog.json` las lista. Se instalan con [`skills-m
|
|
|
227
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` |
|
|
228
225
|
| `pr-worktree` | PRs | Revisa un PR en su worktree con `issue-flow pr worktree`, sin tocar el checkout principal | `close-pr` |
|
|
229
226
|
|
|
230
|
-
|
|
231
|
-
npx @ramon-sg/skills-manager@1 add --catalog ramon-sg/issue-flow
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
En un repo sin `.agents/skills.json`, `init` en vez de `add`. `close-pr` y `pr-worktree` usan `code-audit`, del catálogo [`agent-skills`](https://github.com/ramon-sg/agent-skills): instala los dos.
|
|
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.
|
|
235
228
|
|
|
236
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.
|
|
237
230
|
|
|
@@ -285,7 +278,7 @@ Usa [`github-rest`](https://github.com/ramon-sg/github-rest) para hablar con Git
|
|
|
285
278
|
|
|
286
279
|
## Publicar una versión
|
|
287
280
|
|
|
288
|
-
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: `
|
|
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.
|
|
289
282
|
|
|
290
283
|
1. Un PR que sube `version` en `package.json`.
|
|
291
284
|
2. Después del merge, una release `v<versión>` sobre ese commit de `main`.
|
|
@@ -296,7 +289,7 @@ El workflow se detiene si la tag no coincide con `package.json`.
|
|
|
296
289
|
|
|
297
290
|
1. Desde `main`, con sesión en npm (`npm login`): `npm publish --provenance=false`. La procedencia solo se genera dentro de un CI.
|
|
298
291
|
2. En npmjs.com, configurar la publicación desde GitHub (trusted publishing) para `publish.yml`.
|
|
299
|
-
3. Pushear la tag `v0.1.0` sin crear una release: la necesitan
|
|
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.
|
|
300
293
|
|
|
301
294
|
Desde 0.2.0, cada versión es una release y la publica el workflow.
|
|
302
295
|
|
package/dist/cli-setup.js
CHANGED
|
@@ -18,9 +18,9 @@ export async function runSetupCommand(command, flags, rest, run) {
|
|
|
18
18
|
if (rest.length > 0)
|
|
19
19
|
throw new UsageError(`${command} takes no arguments.`);
|
|
20
20
|
if (command === "doctor")
|
|
21
|
-
return doctor(run.cwd, run.out);
|
|
21
|
+
return doctor(run.cwd, flags.json ?? false, run.out);
|
|
22
22
|
if (command === "upgrade")
|
|
23
|
-
return upgrade({ cwd: run.cwd,
|
|
23
|
+
return upgrade({ cwd: run.cwd, dryRun: flags["dry-run"] ?? false }, run.out);
|
|
24
24
|
const recipes = flags.recipe === undefined ? null : flags.recipe.split(",").map((name) => name.trim()).filter((name) => name !== "");
|
|
25
|
-
return init({ cwd: run.cwd, yes: flags.yes ?? false, recipes
|
|
25
|
+
return init({ cwd: run.cwd, yes: flags.yes ?? false, recipes }, run.out);
|
|
26
26
|
}
|
package/dist/cli.d.ts
CHANGED
|
@@ -84,18 +84,6 @@ declare const OPTIONS: {
|
|
|
84
84
|
readonly recipe: {
|
|
85
85
|
readonly type: "string";
|
|
86
86
|
};
|
|
87
|
-
readonly "no-cloud": {
|
|
88
|
-
readonly type: "boolean";
|
|
89
|
-
};
|
|
90
|
-
readonly "no-guard": {
|
|
91
|
-
readonly type: "boolean";
|
|
92
|
-
};
|
|
93
|
-
readonly "no-skills": {
|
|
94
|
-
readonly type: "boolean";
|
|
95
|
-
};
|
|
96
|
-
readonly to: {
|
|
97
|
-
readonly type: "string";
|
|
98
|
-
};
|
|
99
87
|
};
|
|
100
88
|
/** The parsed flags. */
|
|
101
89
|
export type Flags = ReturnType<typeof parseArgs<{
|
package/dist/cli.js
CHANGED
|
@@ -41,10 +41,6 @@ const OPTIONS = {
|
|
|
41
41
|
next: { type: "boolean" },
|
|
42
42
|
"no-setup": { type: "boolean" },
|
|
43
43
|
recipe: { type: "string" },
|
|
44
|
-
"no-cloud": { type: "boolean" },
|
|
45
|
-
"no-guard": { type: "boolean" },
|
|
46
|
-
"no-skills": { type: "boolean" },
|
|
47
|
-
to: { type: "string" },
|
|
48
44
|
};
|
|
49
45
|
/** Flags each command takes, besides `--repo`, `--help` and `--namer`. */
|
|
50
46
|
const COMMAND_FLAGS = {
|
|
@@ -76,9 +72,9 @@ const COMMAND_FLAGS = {
|
|
|
76
72
|
"adapter archive": [],
|
|
77
73
|
"hook worktree-create": [],
|
|
78
74
|
"hook worktree-remove": [],
|
|
79
|
-
init: ["yes", "recipe"
|
|
80
|
-
doctor: [],
|
|
81
|
-
upgrade: ["
|
|
75
|
+
init: ["yes", "recipe"],
|
|
76
|
+
doctor: ["json"],
|
|
77
|
+
upgrade: ["dry-run"],
|
|
82
78
|
};
|
|
83
79
|
/** An issue number: digits only. */
|
|
84
80
|
const NUMBER = /^\d+$/;
|
|
@@ -2,9 +2,10 @@ import type { Output } from "../output.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Checks the repo; see the file's header.
|
|
4
4
|
* @param cwd - Where it runs.
|
|
5
|
+
* @param json - Whether to print the checks as JSON.
|
|
5
6
|
* @param out - Where to write.
|
|
6
7
|
* @returns 0 if everything passes, 1 if not.
|
|
7
|
-
* @throws SetupError outside a checkout.
|
|
8
|
-
* @example doctor(process.cwd(), out)
|
|
8
|
+
* @throws SetupError outside a checkout; a broken file is a failed check.
|
|
9
|
+
* @example doctor(process.cwd(), false, out)
|
|
9
10
|
*/
|
|
10
|
-
export declare function doctor(cwd: string, out: Output): number;
|
|
11
|
+
export declare function doctor(cwd: string, json: boolean, out: Output): number;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file `issue-flow doctor`: whether the repo
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* which
|
|
2
|
+
* @file `issue-flow doctor [--json]`: whether the repo's flow files are as `init` leaves
|
|
3
|
+
* them, at this version: the config, the dev dependencies and the hooks. It changes nothing;
|
|
4
|
+
* each problem says what fixes it. `--json` prints the checks for `@ramon-sg/agent-kit`,
|
|
5
|
+
* which adds those of the skills and the other tools.
|
|
6
6
|
*/
|
|
7
|
-
import { ConfigError
|
|
7
|
+
import { ConfigError } from "../config-base.js";
|
|
8
8
|
import { loadConfig } from "../config.js";
|
|
9
9
|
import { locateRepo } from "../git.js";
|
|
10
|
-
import { WEAKER_NETWORK } from "../guard-config.js";
|
|
11
10
|
import { dependencyVersion } from "../package-edits.js";
|
|
12
11
|
import { hookVersions } from "../settings-hooks.js";
|
|
13
|
-
import {
|
|
14
|
-
/** What fixes
|
|
15
|
-
const
|
|
12
|
+
import { ownVersions, PACKAGE, readJson, SETTINGS_FILE, SetupError } from "../setup-files.js";
|
|
13
|
+
/** What fixes every problem: it runs init or upgrade at the right version. */
|
|
14
|
+
const RUN_AGENT_KIT = "run npx @ramon-sg/agent-kit";
|
|
16
15
|
/**
|
|
17
16
|
* Checks the config.
|
|
18
17
|
* @param root - The repo.
|
|
@@ -41,8 +40,8 @@ function packageChecks(root, version) {
|
|
|
41
40
|
const flow = dependencyVersion(pkg, PACKAGE);
|
|
42
41
|
const rest = dependencyVersion(pkg, "@ramon-sg/github-rest");
|
|
43
42
|
return [
|
|
44
|
-
{ name: PACKAGE, ok: flow === version, detail: flow === null ? `not in package.json: ${
|
|
45
|
-
{ name: "@ramon-sg/github-rest", ok: rest !== null, detail: rest ?? `not in package.json, and the skills call it: ${
|
|
43
|
+
{ name: PACKAGE, ok: flow === version, detail: flow === null ? `not in package.json: ${RUN_AGENT_KIT}` : flow === version ? version : `package.json has ${flow}, this is ${version}: ${RUN_AGENT_KIT}` },
|
|
44
|
+
{ name: "@ramon-sg/github-rest", ok: rest !== null, detail: rest ?? `not in package.json, and the skills call it: ${RUN_AGENT_KIT}` },
|
|
46
45
|
];
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
@@ -55,70 +54,37 @@ function hookChecks(root, version) {
|
|
|
55
54
|
return Object.entries(hookVersions(readJson(root, SETTINGS_FILE))).map(([event, found]) => ({
|
|
56
55
|
name: `${event} hook`,
|
|
57
56
|
ok: found === version,
|
|
58
|
-
detail: found === null ? `not in ${SETTINGS_FILE}: ${
|
|
57
|
+
detail: found === null ? `not in ${SETTINGS_FILE}: ${RUN_AGENT_KIT}` : found === version ? version : `at ${found}, this is ${version}: ${RUN_AGENT_KIT}`,
|
|
59
58
|
}));
|
|
60
59
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Checks that the flow's skills are at this version's tag: they call this CLI, so they go
|
|
63
|
-
* together.
|
|
64
|
-
* @param root - The repo.
|
|
65
|
-
* @param version - This package's version.
|
|
66
|
-
* @returns The check.
|
|
67
|
-
*/
|
|
68
|
-
function skillsCheck(root, version) {
|
|
69
|
-
const skills = flowSkills(readJson(root, SKILLS_LOCK));
|
|
70
|
-
const tag = `v${version}`;
|
|
71
|
-
const off = skills.filter((skill) => skill.tag !== tag);
|
|
72
|
-
if (skills.length === 0)
|
|
73
|
-
return { name: "flow skills", ok: false, detail: `none in ${SKILLS_LOCK}: ${RUN_INIT}` };
|
|
74
|
-
if (off.length === 0)
|
|
75
|
-
return { name: "flow skills", ok: true, detail: `${skills.length} at ${tag}` };
|
|
76
|
-
return { name: "flow skills", ok: false, detail: `${off.map((skill) => `${skill.name} at ${skill.tag ?? "no tag"}`).join(", ")}, this is ${tag}: run issue-flow upgrade` };
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Checks `claude-repo-guard`, if the repo has it: its own doctor and, on macOS, the option
|
|
80
|
-
* `gh` needs.
|
|
81
|
-
* @param root - The repo.
|
|
82
|
-
* @returns The checks.
|
|
83
|
-
*/
|
|
84
|
-
function guardChecks(root) {
|
|
85
|
-
const options = readJson(root, GUARD_CONFIG);
|
|
86
|
-
if (options === null)
|
|
87
|
-
return [{ name: "claude-repo-guard", ok: true, detail: "not installed (Claude opens with your own permissions)" }];
|
|
88
|
-
const checks = [];
|
|
89
|
-
if (process.platform === "darwin" && options[WEAKER_NETWORK] !== true)
|
|
90
|
-
checks.push({ name: WEAKER_NETWORK, ok: false, detail: `not true in ${GUARD_CONFIG}, so gh fails inside the macOS sandbox: ${RUN_INIT}` });
|
|
91
|
-
const result = captureNpx(root, [GUARD_SPEC, "doctor", "--json"]);
|
|
92
|
-
let found;
|
|
93
|
-
try {
|
|
94
|
-
found = JSON.parse(result?.out ?? "");
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
found = null;
|
|
98
|
-
}
|
|
99
|
-
if (!Array.isArray(found))
|
|
100
|
-
return [...checks, { name: "claude-repo-guard", ok: false, detail: `could not run its doctor; run 'npx ${GUARD_SPEC} doctor'` }];
|
|
101
|
-
const failed = found.filter((check) => !isObject(check) || check.ok !== true).map((check) => (isObject(check) ? check : { name: "?", detail: String(check) }));
|
|
102
|
-
if (failed.length === 0)
|
|
103
|
-
return [...checks, { name: "claude-repo-guard", ok: true, detail: "its doctor passes" }];
|
|
104
|
-
return [...checks, ...failed.map((check) => ({ name: `claude-repo-guard: ${String(check.name)}`, ok: false, detail: String(check.detail) }))];
|
|
105
|
-
}
|
|
106
60
|
/**
|
|
107
61
|
* Checks the repo; see the file's header.
|
|
108
62
|
* @param cwd - Where it runs.
|
|
63
|
+
* @param json - Whether to print the checks as JSON.
|
|
109
64
|
* @param out - Where to write.
|
|
110
65
|
* @returns 0 if everything passes, 1 if not.
|
|
111
|
-
* @throws SetupError outside a checkout.
|
|
112
|
-
* @example doctor(process.cwd(), out)
|
|
66
|
+
* @throws SetupError outside a checkout; a broken file is a failed check.
|
|
67
|
+
* @example doctor(process.cwd(), false, out)
|
|
113
68
|
*/
|
|
114
|
-
export function doctor(cwd, out) {
|
|
69
|
+
export function doctor(cwd, json, out) {
|
|
115
70
|
const located = locateRepo(cwd);
|
|
116
71
|
if (located === null)
|
|
117
72
|
throw new SetupError("run doctor inside a checkout of the repo.");
|
|
118
73
|
const root = located.mainRoot;
|
|
119
74
|
const { issueFlow } = ownVersions();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
75
|
+
let checks;
|
|
76
|
+
try {
|
|
77
|
+
checks = [configCheck(root), ...packageChecks(root, issueFlow), ...hookChecks(root, issueFlow)];
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (!(error instanceof SetupError))
|
|
81
|
+
throw error;
|
|
82
|
+
checks = [{ name: "files", ok: false, detail: error.message }];
|
|
83
|
+
}
|
|
84
|
+
if (json)
|
|
85
|
+
out.stdout(`${JSON.stringify(checks)}\n`);
|
|
86
|
+
else
|
|
87
|
+
for (const check of checks)
|
|
88
|
+
out.stdout(`${check.ok ? "✓" : "✗"} ${check.name}: ${check.detail}\n`);
|
|
123
89
|
return checks.every((check) => check.ok) ? 0 : 1;
|
|
124
90
|
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -4,9 +4,6 @@ export interface InitOptions {
|
|
|
4
4
|
cwd: string;
|
|
5
5
|
yes: boolean;
|
|
6
6
|
recipes: string[] | null;
|
|
7
|
-
skills: boolean;
|
|
8
|
-
guard: boolean;
|
|
9
|
-
cloud: boolean;
|
|
10
7
|
}
|
|
11
8
|
/**
|
|
12
9
|
* The URL of the config's schema at a version.
|
|
@@ -19,9 +16,9 @@ export declare function schemaUrl(version: string): string;
|
|
|
19
16
|
* Sets the repo up for the flow; see the file's header.
|
|
20
17
|
* @param options - The options.
|
|
21
18
|
* @param out - Where to write and ask.
|
|
22
|
-
* @returns 0, or 1 if
|
|
19
|
+
* @returns 0, or 1 if the files were not written.
|
|
23
20
|
* @throws SetupError outside the main checkout or with a broken file; ConfigError if the
|
|
24
21
|
* config would be invalid; UsageError for an unknown recipe.
|
|
25
|
-
* @example await init({ cwd, yes: true, recipes: null
|
|
22
|
+
* @example await init({ cwd, yes: true, recipes: null }, out)
|
|
26
23
|
*/
|
|
27
24
|
export declare function init(options: InitOptions, out: Output): Promise<number>;
|
package/dist/commands/init.js
CHANGED
|
@@ -2,27 +2,19 @@
|
|
|
2
2
|
* @file `issue-flow init`: sets a repo up for the flow, and can run again to fill in what
|
|
3
3
|
* is missing. It writes `.agents/flow.json` (from `paseo.json` if the repo has one, plus the
|
|
4
4
|
* recipes it picks), the flow's dev dependencies and scripts in `package.json`, and the
|
|
5
|
-
* `SessionStart`, `WorktreeCreate` and `WorktreeRemove` hooks.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the repo has no manifest, `add` if it has one, because `init` stops then). A tool that
|
|
9
|
-
* fails is reported and the rest goes on.
|
|
5
|
+
* `SessionStart`, `WorktreeCreate` and `WorktreeRemove` hooks. Only the flow's own files:
|
|
6
|
+
* the skills, `claude-cloud` and `claude-repo-guard` are installed by `@ramon-sg/agent-kit`,
|
|
7
|
+
* which runs this command.
|
|
10
8
|
*/
|
|
11
9
|
import { confirm } from "../ask.js";
|
|
12
10
|
import { CONFIG_PATH, isObject } from "../config-base.js";
|
|
13
11
|
import { parseConfig } from "../config.js";
|
|
14
12
|
import { checkoutRoot, locateRepo } from "../git.js";
|
|
15
|
-
import { guardOptions } from "../guard-config.js";
|
|
16
13
|
import { UsageError, warn } from "../output.js";
|
|
17
14
|
import { withFlowPackages } from "../package-edits.js";
|
|
18
15
|
import { applyRecipe, detectedRecipes, loadRecipes, projectPrefix } from "../recipes.js";
|
|
19
16
|
import { registerHooks } from "../settings-hooks.js";
|
|
20
|
-
import {
|
|
21
|
-
/** The skills picked without a terminal: the flow's, and the audit `close-pr` uses. */
|
|
22
|
-
const DEFAULT_SKILLS = [
|
|
23
|
-
...["github-issues", "start-issue", "close-pr", "pr-review-comments", "pr-worktree"].map((name) => `catalog:${FLOW_CATALOG}/${name}`),
|
|
24
|
-
`catalog:${GENERAL_CATALOG}/code-audit`,
|
|
25
|
-
];
|
|
17
|
+
import { FLOW_REPO, ownVersions, packageManager, readJson, SETTINGS_FILE, SetupError, writeJson } from "../setup-files.js";
|
|
26
18
|
/**
|
|
27
19
|
* The URL of the config's schema at a version.
|
|
28
20
|
* @param version - This package's version.
|
|
@@ -30,7 +22,7 @@ const DEFAULT_SKILLS = [
|
|
|
30
22
|
* @example schemaUrl("0.1.0") // "https://raw.githubusercontent.com/ramon-sg/issue-flow/v0.1.0/schema.json"
|
|
31
23
|
*/
|
|
32
24
|
export function schemaUrl(version) {
|
|
33
|
-
return `https://raw.githubusercontent.com/${
|
|
25
|
+
return `https://raw.githubusercontent.com/${FLOW_REPO}/v${version}/schema.json`;
|
|
34
26
|
}
|
|
35
27
|
/**
|
|
36
28
|
* A new config: the schema, and the branch rules of `paseo.json` if the repo has one.
|
|
@@ -107,78 +99,14 @@ function plan(root, versions, recipes) {
|
|
|
107
99
|
});
|
|
108
100
|
return [file(CONFIG_PATH, raw, configChanges), file("package.json", pkg.pkg, pkg.changes), file(SETTINGS_FILE, hooks.settings, hooks.changes)];
|
|
109
101
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Runs one of the other tools after asking, and says if it failed.
|
|
112
|
-
* @param step - What it is and how it runs.
|
|
113
|
-
* @param options - The options.
|
|
114
|
-
* @param out - Where to ask and write.
|
|
115
|
-
* @returns False if it ran and failed.
|
|
116
|
-
*/
|
|
117
|
-
async function external(step, options, out) {
|
|
118
|
-
if (!options.yes && !(await confirm(out, step.question)))
|
|
119
|
-
return true;
|
|
120
|
-
if (step.run())
|
|
121
|
-
return true;
|
|
122
|
-
warn(out, step.failed);
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Installs `claude-repo-guard`, after writing in its options what the flow needs.
|
|
127
|
-
* @param root - The repo.
|
|
128
|
-
* @param out - Where to write.
|
|
129
|
-
* @returns Whether it installed.
|
|
130
|
-
*/
|
|
131
|
-
function installGuard(root, out) {
|
|
132
|
-
const flow = readJson(root, CONFIG_PATH);
|
|
133
|
-
const baseBranch = typeof flow?.baseBranch === "string" ? flow.baseBranch : null;
|
|
134
|
-
const { options, changes } = guardOptions(readJson(root, GUARD_CONFIG), baseBranch, process.platform);
|
|
135
|
-
if (changes.length > 0)
|
|
136
|
-
writeJson(root, GUARD_CONFIG, options);
|
|
137
|
-
for (const change of changes)
|
|
138
|
-
out.stdout(` ${change}\n`);
|
|
139
|
-
return runNpx(root, [GUARD_SPEC, "install", "--yes"]);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* The ids of the default skills the manifest does not take yet: `skills-manager` leaves the
|
|
143
|
-
* ones it has out of the candidates, and fails if `--select` names one.
|
|
144
|
-
* @param manifest - The skills manifest, or null.
|
|
145
|
-
* @returns The ids to pick.
|
|
146
|
-
*/
|
|
147
|
-
function missingDefaults(manifest) {
|
|
148
|
-
const sources = Array.isArray(manifest?.sources) ? manifest.sources.filter(isObject) : [];
|
|
149
|
-
const taken = new Set(sources.flatMap((source) => (typeof source.catalog === "string" && Array.isArray(source.skills) ? source.skills.map((name) => `catalog:${String(source.catalog)}/${String(name)}`) : [])));
|
|
150
|
-
return DEFAULT_SKILLS.filter((id) => !taken.has(id));
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Opens the skills menu with the flow's catalog, at this version, and the general one; without
|
|
154
|
-
* a terminal or with `--yes`, it picks the flow's skills and `code-audit` that are not there yet.
|
|
155
|
-
* @param root - The repo.
|
|
156
|
-
* @param versions - This package's versions.
|
|
157
|
-
* @param pick - Whether to pick without the menu.
|
|
158
|
-
* @param out - Where to say there is nothing to add.
|
|
159
|
-
* @returns Whether `skills-manager` worked, or true if there was nothing to pick.
|
|
160
|
-
*/
|
|
161
|
-
function installSkills(root, versions, pick, out) {
|
|
162
|
-
const manifest = readJson(root, SKILLS_MANIFEST);
|
|
163
|
-
const command = manifest === null ? "init" : "add";
|
|
164
|
-
const args = [`@ramon-sg/skills-manager@${versions.skillsManager}`, command, "--catalog", `${FLOW_CATALOG}#v${versions.issueFlow}`, "--catalog", GENERAL_CATALOG];
|
|
165
|
-
if (!pick)
|
|
166
|
-
return runNpx(root, args);
|
|
167
|
-
const missing = missingDefaults(manifest);
|
|
168
|
-
if (missing.length === 0) {
|
|
169
|
-
out.stdout(`The flow's skills and code-audit are already in ${SKILLS_MANIFEST}.\n`);
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
return runNpx(root, [...args, "--select", missing.join(",")]);
|
|
173
|
-
}
|
|
174
102
|
/**
|
|
175
103
|
* Sets the repo up for the flow; see the file's header.
|
|
176
104
|
* @param options - The options.
|
|
177
105
|
* @param out - Where to write and ask.
|
|
178
|
-
* @returns 0, or 1 if
|
|
106
|
+
* @returns 0, or 1 if the files were not written.
|
|
179
107
|
* @throws SetupError outside the main checkout or with a broken file; ConfigError if the
|
|
180
108
|
* config would be invalid; UsageError for an unknown recipe.
|
|
181
|
-
* @example await init({ cwd, yes: true, recipes: null
|
|
109
|
+
* @example await init({ cwd, yes: true, recipes: null }, out)
|
|
182
110
|
*/
|
|
183
111
|
export async function init(options, out) {
|
|
184
112
|
const located = locateRepo(options.cwd);
|
|
@@ -202,12 +130,7 @@ export async function init(options, out) {
|
|
|
202
130
|
}
|
|
203
131
|
for (const file of files.filter((item) => item.changes.length > 0))
|
|
204
132
|
writeJson(root, file.path, file.value);
|
|
205
|
-
const results = [
|
|
206
|
-
!options.cloud || await external({ question: "Set up Claude cloud sessions with claude-cloud init?", run: () => runNpx(root, [CLOUD_SPEC, "init"]), failed: `claude-cloud init failed; run 'npx ${CLOUD_SPEC} init' to retry.` }, options, out),
|
|
207
|
-
!options.guard || await external({ question: "Install claude-repo-guard (auto mode, with the sandbox)?", run: () => installGuard(root, out), failed: `claude-repo-guard did not install; run 'npx ${GUARD_SPEC} install' to retry.` }, options, out),
|
|
208
|
-
!options.skills || await external({ question: "Pick the skills now?", run: () => installSkills(root, versions, options.yes || !out.interactive, out), failed: "skills-manager failed; run issue-flow init again to retry." }, options, out),
|
|
209
|
-
];
|
|
210
133
|
const manager = packageManager(root, readJson(root, "package.json"));
|
|
211
134
|
out.stdout(`Next: run '${manager} install', and commit .agents/, .claude/ and package.json.\n`);
|
|
212
|
-
return
|
|
135
|
+
return 0;
|
|
213
136
|
}
|
|
@@ -2,15 +2,15 @@ import type { Output } from "../output.js";
|
|
|
2
2
|
/** Options of `issue-flow upgrade`. */
|
|
3
3
|
export interface UpgradeOptions {
|
|
4
4
|
cwd: string;
|
|
5
|
-
to: string | null;
|
|
6
5
|
dryRun: boolean;
|
|
7
6
|
}
|
|
8
7
|
/**
|
|
9
|
-
* Moves the repo to
|
|
8
|
+
* Moves the repo's flow files to this version; see the file's header.
|
|
10
9
|
* @param options - The options.
|
|
11
10
|
* @param out - Where to write.
|
|
12
|
-
* @returns 0
|
|
13
|
-
* @throws SetupError outside the main checkout, if
|
|
14
|
-
*
|
|
11
|
+
* @returns 0.
|
|
12
|
+
* @throws SetupError outside the main checkout, if a file is broken, or if the repo is
|
|
13
|
+
* already at a newer version (a stale local copy must not move it back).
|
|
14
|
+
* @example upgrade({ cwd, dryRun: false }, out)
|
|
15
15
|
*/
|
|
16
16
|
export declare function upgrade(options: UpgradeOptions, out: Output): number;
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,72 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file `issue-flow upgrade [--
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* manifest to its tag, and then runs `skills-manager update` on the flow's skills only (the
|
|
7
|
-
* general ones keep their own versions). The versions come from npm, not from the CLI
|
|
8
|
-
* that runs, which may be the old one.
|
|
2
|
+
* @file `issue-flow upgrade [--dry-run]`: moves the repo's flow files to the version that
|
|
3
|
+
* runs: the dev dependencies to the ones it pins, and the hooks and the config's schema to
|
|
4
|
+
* it. `@ramon-sg/agent-kit` runs it as `npx @ramon-sg/issue-flow@<version> upgrade` and then
|
|
5
|
+
* moves the flow's skills to the same tag, because the skills call this CLI.
|
|
9
6
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import { CONFIG_PATH, isObject } from "../config-base.js";
|
|
7
|
+
import { CONFIG_PATH } from "../config-base.js";
|
|
12
8
|
import { checkoutRoot, locateRepo } from "../git.js";
|
|
13
|
-
import { withFlowPackages } from "../package-edits.js";
|
|
14
|
-
import { registerHooks } from "../settings-hooks.js";
|
|
15
|
-
import {
|
|
9
|
+
import { dependencyVersion, withFlowPackages } from "../package-edits.js";
|
|
10
|
+
import { hookVersions, registerHooks } from "../settings-hooks.js";
|
|
11
|
+
import { FLOW_REPO, ownVersions, PACKAGE, packageManager, readJson, SETTINGS_FILE, SetupError, writeJson } from "../setup-files.js";
|
|
16
12
|
import { schemaUrl } from "./init.js";
|
|
17
|
-
/** A release version. */
|
|
18
|
-
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
19
13
|
/** The start of the schema URL `init` writes, at any version. */
|
|
20
|
-
const SCHEMA_PREFIX = `https://raw.githubusercontent.com/${
|
|
14
|
+
const SCHEMA_PREFIX = `https://raw.githubusercontent.com/${FLOW_REPO}/`;
|
|
15
|
+
/** A release version, after an optional range sign. */
|
|
16
|
+
const RELEASE = /^[\^~=]?(\d+)\.(\d+)\.(\d+)$/;
|
|
21
17
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param
|
|
24
|
-
* @
|
|
25
|
-
* @
|
|
18
|
+
* Whether a version is newer than another; false if either is not a release.
|
|
19
|
+
* @param version - The version, maybe with a range sign.
|
|
20
|
+
* @param than - The other.
|
|
21
|
+
* @returns True if it is newer.
|
|
26
22
|
*/
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
function isNewer(version, than) {
|
|
24
|
+
const [left, right] = [RELEASE.exec(version), RELEASE.exec(than)];
|
|
25
|
+
if (left === null || right === null)
|
|
26
|
+
return false;
|
|
27
|
+
for (let index = 1; index <= 3; index += 1) {
|
|
28
|
+
const diff = Number(left[index]) - Number(right[index]);
|
|
29
|
+
if (diff !== 0)
|
|
30
|
+
return diff > 0;
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
info = null;
|
|
36
|
-
}
|
|
37
|
-
if (result.status !== 0 || !isObject(info))
|
|
38
|
-
throw new SetupError(`npm could not find ${spec}${result.stderr ? `: ${result.stderr.trim()}` : "."}`);
|
|
39
|
-
const deps = isObject(info.dependencies) ? info.dependencies : {};
|
|
40
|
-
const devDeps = isObject(info.devDependencies) ? info.devDependencies : {};
|
|
41
|
-
const githubRest = deps["@ramon-sg/github-rest"];
|
|
42
|
-
const skillsManager = devDeps["@ramon-sg/skills-manager"];
|
|
43
|
-
if (typeof info.version !== "string" || !VERSION.test(info.version) || typeof githubRest !== "string" || typeof skillsManager !== "string")
|
|
44
|
-
throw new SetupError(`${spec} on npm lacks its version or those of github-rest and skills-manager.`);
|
|
45
|
-
return { issueFlow: info.version, githubRest, skillsManager };
|
|
32
|
+
return false;
|
|
46
33
|
}
|
|
47
34
|
/**
|
|
48
|
-
* The
|
|
49
|
-
* @param
|
|
50
|
-
* @
|
|
51
|
-
* @returns The new manifest and whether it changed, or null if the flow's catalog is not in it.
|
|
35
|
+
* The newest version of the flow the repo already has, in package.json or a hook.
|
|
36
|
+
* @param root - The repo.
|
|
37
|
+
* @returns The version, or null if it has none.
|
|
52
38
|
*/
|
|
53
|
-
function
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const source = sources[index];
|
|
57
|
-
if (manifest === null || !isObject(source))
|
|
58
|
-
return null;
|
|
59
|
-
if (source.ref === tag)
|
|
60
|
-
return { manifest, changed: false };
|
|
61
|
-
return { manifest: { ...manifest, sources: sources.map((item, at) => (at === index ? { ...source, ref: tag } : item)) }, changed: true };
|
|
39
|
+
function repoVersion(root) {
|
|
40
|
+
const found = [dependencyVersion(readJson(root, "package.json"), PACKAGE), ...Object.values(hookVersions(readJson(root, SETTINGS_FILE)))];
|
|
41
|
+
return found.filter((version) => version !== null).reduce((newest, version) => (newest === null || isNewer(version, newest) ? version : newest), null);
|
|
62
42
|
}
|
|
63
43
|
/**
|
|
64
|
-
* Moves the repo to
|
|
44
|
+
* Moves the repo's flow files to this version; see the file's header.
|
|
65
45
|
* @param options - The options.
|
|
66
46
|
* @param out - Where to write.
|
|
67
|
-
* @returns 0
|
|
68
|
-
* @throws SetupError outside the main checkout, if
|
|
69
|
-
*
|
|
47
|
+
* @returns 0.
|
|
48
|
+
* @throws SetupError outside the main checkout, if a file is broken, or if the repo is
|
|
49
|
+
* already at a newer version (a stale local copy must not move it back).
|
|
50
|
+
* @example upgrade({ cwd, dryRun: false }, out)
|
|
70
51
|
*/
|
|
71
52
|
export function upgrade(options, out) {
|
|
72
53
|
const located = locateRepo(options.cwd);
|
|
@@ -75,10 +56,10 @@ export function upgrade(options, out) {
|
|
|
75
56
|
const root = located.mainRoot;
|
|
76
57
|
if (checkoutRoot(options.cwd) !== root)
|
|
77
58
|
throw new SetupError(`run upgrade in the main checkout, ${root}, not in a worktree.`);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
59
|
+
const versions = ownVersions();
|
|
60
|
+
const current = repoVersion(root);
|
|
61
|
+
if (current !== null && isNewer(current, versions.issueFlow))
|
|
62
|
+
throw new SetupError(`the repo is at issue-flow ${current}, newer than this ${versions.issueFlow}; run 'npx ${PACKAGE}@${current.replace(/^[\^~=]/, "")} upgrade' or 'npx @ramon-sg/agent-kit'.`);
|
|
82
63
|
const writes = [];
|
|
83
64
|
const lines = [];
|
|
84
65
|
const pkg = withFlowPackages(readJson(root, "package.json"), versions, true);
|
|
@@ -92,24 +73,13 @@ export function upgrade(options, out) {
|
|
|
92
73
|
const config = readJson(root, CONFIG_PATH);
|
|
93
74
|
if (config !== null && typeof config.$schema === "string" && config.$schema.startsWith(SCHEMA_PREFIX) && config.$schema !== schemaUrl(versions.issueFlow)) {
|
|
94
75
|
writes.push({ path: CONFIG_PATH, value: { ...config, $schema: schemaUrl(versions.issueFlow) } });
|
|
95
|
-
lines.push(`${CONFIG_PATH}: $schema at ${
|
|
96
|
-
}
|
|
97
|
-
const manifest = movedManifest(readJson(root, SKILLS_MANIFEST), tag);
|
|
98
|
-
if (manifest?.changed === true) {
|
|
99
|
-
writes.push({ path: SKILLS_MANIFEST, value: manifest.manifest });
|
|
100
|
-
lines.push(`${SKILLS_MANIFEST}: ${FLOW_CATALOG} at ${tag}`);
|
|
76
|
+
lines.push(`${CONFIG_PATH}: $schema at v${versions.issueFlow}`);
|
|
101
77
|
}
|
|
102
|
-
const skills = flowSkills(readJson(root, SKILLS_LOCK)).map((skill) => skill.name);
|
|
103
78
|
out.stdout(lines.length === 0 ? `Already at issue-flow ${versions.issueFlow}.\n` : `To issue-flow ${versions.issueFlow}:\n${lines.map((line) => ` ${line}\n`).join("")}`);
|
|
104
|
-
if (
|
|
105
|
-
out.stdout(` the flow's skills are not in ${SKILLS_MANIFEST}: issue-flow init installs them\n`);
|
|
106
|
-
if (options.dryRun)
|
|
79
|
+
if (options.dryRun || writes.length === 0)
|
|
107
80
|
return 0;
|
|
108
81
|
for (const write of writes)
|
|
109
82
|
writeJson(root, write.path, write.value);
|
|
110
|
-
const updated = manifest === null || skills.length === 0 || runNpx(root, [`@ramon-sg/skills-manager@${versions.skillsManager}`, "update", ...skills]);
|
|
111
|
-
if (!updated)
|
|
112
|
-
out.stderr(`issue-flow: skills-manager update failed; run 'npx @ramon-sg/skills-manager@${versions.skillsManager} update ${skills.join(" ")}' to retry.\n`);
|
|
113
83
|
out.stdout(`Next: run '${packageManager(root, readJson(root, "package.json"))} install', review the diff and commit it.\n`);
|
|
114
|
-
return
|
|
84
|
+
return 0;
|
|
115
85
|
}
|
package/dist/setup-files.d.ts
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
/** This package's name on npm. */
|
|
2
2
|
export declare const PACKAGE = "@ramon-sg/issue-flow";
|
|
3
|
-
/** The GitHub repo of this package,
|
|
4
|
-
export declare const
|
|
5
|
-
/** The catalog of the general skills. */
|
|
6
|
-
export declare const GENERAL_CATALOG = "ramon-sg/agent-skills";
|
|
7
|
-
/** Where `skills-manager` keeps the repo's skills manifest. */
|
|
8
|
-
export declare const SKILLS_MANIFEST = ".agents/skills.json";
|
|
9
|
-
/** Where `skills-manager` keeps the repo's skills lock. */
|
|
10
|
-
export declare const SKILLS_LOCK = ".agents/skills.lock.json";
|
|
3
|
+
/** The GitHub repo of this package, whose tags hold the config's schema of each version. */
|
|
4
|
+
export declare const FLOW_REPO = "ramon-sg/issue-flow";
|
|
11
5
|
/** Claude Code's settings of the repo. */
|
|
12
6
|
export declare const SETTINGS_FILE = ".claude/settings.json";
|
|
13
|
-
/** The options of `claude-repo-guard` in the repo. */
|
|
14
|
-
export declare const GUARD_CONFIG = ".claude/repo-guard.config.json";
|
|
15
|
-
/** How `claude-repo-guard` runs: it is not on npm. */
|
|
16
|
-
export declare const GUARD_SPEC = "github:ramon-sg/claude-repo-guard";
|
|
17
|
-
/** How `claude-cloud` runs. */
|
|
18
|
-
export declare const CLOUD_SPEC = "@ramon-sg/claude-cloud@1";
|
|
19
7
|
/** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
|
|
20
8
|
export declare class SetupError extends Error {
|
|
21
9
|
}
|
|
@@ -58,32 +46,3 @@ export declare function writeJson(root: string, path: string, value: unknown): v
|
|
|
58
46
|
* @example packageManager(root, { packageManager: "pnpm@9.0.0" }) // "pnpm"
|
|
59
47
|
*/
|
|
60
48
|
export declare function packageManager(root: string, pkg: Record<string, unknown> | null): string;
|
|
61
|
-
/**
|
|
62
|
-
* Runs `npx <args>` in the repo, its output on this terminal.
|
|
63
|
-
* @param root - The repo.
|
|
64
|
-
* @param args - The package and its arguments.
|
|
65
|
-
* @returns Whether it exited 0.
|
|
66
|
-
* @example runNpx(root, ["@ramon-sg/claude-cloud@1", "init"])
|
|
67
|
-
*/
|
|
68
|
-
export declare function runNpx(root: string, args: string[]): boolean;
|
|
69
|
-
/**
|
|
70
|
-
* Runs `npx <args>` in the repo and returns what it printed.
|
|
71
|
-
* @param root - The repo.
|
|
72
|
-
* @param args - The package and its arguments.
|
|
73
|
-
* @returns Its stdout and exit code, or null if npx could not start.
|
|
74
|
-
* @example captureNpx(root, ["github:ramon-sg/claude-repo-guard", "doctor", "--json"])
|
|
75
|
-
*/
|
|
76
|
-
export declare function captureNpx(root: string, args: string[]): {
|
|
77
|
-
out: string;
|
|
78
|
-
status: number | null;
|
|
79
|
-
} | null;
|
|
80
|
-
/**
|
|
81
|
-
* The flow's skills in the repo's skills lock, with the tag each one is at.
|
|
82
|
-
* @param lock - The lock, or null.
|
|
83
|
-
* @returns Name and tag of each skill of the flow's catalog.
|
|
84
|
-
* @example flowSkills(lock) // [{ name: "close-pr", tag: "v0.1.0" }]
|
|
85
|
-
*/
|
|
86
|
-
export declare function flowSkills(lock: Record<string, unknown> | null): {
|
|
87
|
-
name: string;
|
|
88
|
-
tag: string | null;
|
|
89
|
-
}[];
|
package/dist/setup-files.js
CHANGED
|
@@ -1,32 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file What `init`, `doctor` and `upgrade` share: this package's own versions, the JSON
|
|
3
|
-
* files of the repo they read and write, its package manager
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* files of the repo they read and write, and its package manager. Every package goes by its
|
|
4
|
+
* scoped name: the unscoped `issue-flow`, `github-rest` and `skills-manager` on npm belong
|
|
5
|
+
* to other people.
|
|
6
6
|
*/
|
|
7
|
-
import { spawnSync } from "node:child_process";
|
|
8
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
8
|
import { dirname, join } from "node:path";
|
|
10
9
|
import { fileURLToPath } from "node:url";
|
|
11
10
|
import { isObject } from "./config-base.js";
|
|
12
11
|
/** This package's name on npm. */
|
|
13
12
|
export const PACKAGE = "@ramon-sg/issue-flow";
|
|
14
|
-
/** The GitHub repo of this package,
|
|
15
|
-
export const
|
|
16
|
-
/** The catalog of the general skills. */
|
|
17
|
-
export const GENERAL_CATALOG = "ramon-sg/agent-skills";
|
|
18
|
-
/** Where `skills-manager` keeps the repo's skills manifest. */
|
|
19
|
-
export const SKILLS_MANIFEST = ".agents/skills.json";
|
|
20
|
-
/** Where `skills-manager` keeps the repo's skills lock. */
|
|
21
|
-
export const SKILLS_LOCK = ".agents/skills.lock.json";
|
|
13
|
+
/** The GitHub repo of this package, whose tags hold the config's schema of each version. */
|
|
14
|
+
export const FLOW_REPO = "ramon-sg/issue-flow";
|
|
22
15
|
/** Claude Code's settings of the repo. */
|
|
23
16
|
export const SETTINGS_FILE = ".claude/settings.json";
|
|
24
|
-
/** The options of `claude-repo-guard` in the repo. */
|
|
25
|
-
export const GUARD_CONFIG = ".claude/repo-guard.config.json";
|
|
26
|
-
/** How `claude-repo-guard` runs: it is not on npm. */
|
|
27
|
-
export const GUARD_SPEC = "github:ramon-sg/claude-repo-guard";
|
|
28
|
-
/** How `claude-cloud` runs. */
|
|
29
|
-
export const CLOUD_SPEC = "@ramon-sg/claude-cloud@1";
|
|
30
17
|
/** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
|
|
31
18
|
export class SetupError extends Error {
|
|
32
19
|
}
|
|
@@ -102,37 +89,3 @@ export function packageManager(root, pkg) {
|
|
|
102
89
|
return field;
|
|
103
90
|
return LOCKFILES.find(([file]) => existsSync(join(root, file)))?.[1] ?? "npm";
|
|
104
91
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Runs `npx <args>` in the repo, its output on this terminal.
|
|
107
|
-
* @param root - The repo.
|
|
108
|
-
* @param args - The package and its arguments.
|
|
109
|
-
* @returns Whether it exited 0.
|
|
110
|
-
* @example runNpx(root, ["@ramon-sg/claude-cloud@1", "init"])
|
|
111
|
-
*/
|
|
112
|
-
export function runNpx(root, args) {
|
|
113
|
-
const result = spawnSync("npx", ["--yes", ...args], { cwd: root, stdio: "inherit" });
|
|
114
|
-
return result.status === 0;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Runs `npx <args>` in the repo and returns what it printed.
|
|
118
|
-
* @param root - The repo.
|
|
119
|
-
* @param args - The package and its arguments.
|
|
120
|
-
* @returns Its stdout and exit code, or null if npx could not start.
|
|
121
|
-
* @example captureNpx(root, ["github:ramon-sg/claude-repo-guard", "doctor", "--json"])
|
|
122
|
-
*/
|
|
123
|
-
export function captureNpx(root, args) {
|
|
124
|
-
const result = spawnSync("npx", ["--yes", ...args], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] });
|
|
125
|
-
return result.error === undefined ? { out: result.stdout, status: result.status } : null;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* The flow's skills in the repo's skills lock, with the tag each one is at.
|
|
129
|
-
* @param lock - The lock, or null.
|
|
130
|
-
* @returns Name and tag of each skill of the flow's catalog.
|
|
131
|
-
* @example flowSkills(lock) // [{ name: "close-pr", tag: "v0.1.0" }]
|
|
132
|
-
*/
|
|
133
|
-
export function flowSkills(lock) {
|
|
134
|
-
const skills = isObject(lock?.skills) ? lock.skills : {};
|
|
135
|
-
return Object.entries(skills)
|
|
136
|
-
.filter(([, entry]) => isObject(entry) && entry.source === `catalog:${FLOW_CATALOG}`)
|
|
137
|
-
.map(([name, entry]) => ({ name, tag: isObject(entry) && typeof entry.tag === "string" ? entry.tag : null }));
|
|
138
|
-
}
|
package/dist/usage.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* @file The usage text of the CLI, printed with `--help` and after a usage error.
|
|
3
3
|
*/
|
|
4
4
|
/** Usage text. */
|
|
5
|
-
export declare const USAGE = "Usage:\n issue-flow issue branch <n>... [--namer claude|title] [--json] [-R owner/repo]\n issue-flow issue branch --missing [--limit <n>] [--namer claude|title] [--dry-run | --yes] [--json] [-R owner/repo]\n issue-flow issue start <n> [--branch] [--dry-run | --yes] [--resume <branch> | --new] [--no-launch]\n [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --plan [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --cloud | --here | --teleport | --adopt [...]\n issue-flow worktree setup [--only <step>]\n issue-flow worktree teardown [--path <dir>]\n issue-flow worktree ports [--next]\n issue-flow worktree cleanup [--path <dir>]\n issue-flow worktree prune [--only <dir>] [--yes] [-R owner/repo]\n issue-flow pr worktree <n> [--no-setup] [-R owner/repo] [-- [<command>...]]\n issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]\n issue-flow session\n issue-flow hook worktree-create | worktree-remove\n issue-flow init [--yes] [--recipe <name>,...]
|
|
5
|
+
export declare const USAGE = "Usage:\n issue-flow issue branch <n>... [--namer claude|title] [--json] [-R owner/repo]\n issue-flow issue branch --missing [--limit <n>] [--namer claude|title] [--dry-run | --yes] [--json] [-R owner/repo]\n issue-flow issue start <n> [--branch] [--dry-run | --yes] [--resume <branch> | --new] [--no-launch]\n [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --plan [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --cloud | --here | --teleport | --adopt [...]\n issue-flow worktree setup [--only <step>]\n issue-flow worktree teardown [--path <dir>]\n issue-flow worktree ports [--next]\n issue-flow worktree cleanup [--path <dir>]\n issue-flow worktree prune [--only <dir>] [--yes] [-R owner/repo]\n issue-flow pr worktree <n> [--no-setup] [-R owner/repo] [-- [<command>...]]\n issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]\n issue-flow session\n issue-flow hook worktree-create | worktree-remove\n issue-flow init [--yes] [--recipe <name>,...]\n issue-flow doctor [--json]\n issue-flow upgrade [--dry-run]\n\nThe slug comes from the issue's \"Slug de rama\" block, then from Claude, then from the title,\nand the branch from the repo's template (an older \"Rama sugerida\" block is used if it fits).\n--missing adds the slug block to every open issue without a block, after confirming.\n\nissue start makes the branch in a worktree (or, with --branch, in this checkout), the\nstart commit, the push, the draft PR and the assignment, after confirming, and opens\nClaude: in auto mode if the repo has claude-repo-guard, with your settings with\n--ask-permissions, and with no sandbox nor questions with --free. --plan prints the branch\nand the prompt as JSON and changes nothing.\n\n--cloud confirms here and opens a Claude cloud session that starts the issue there.\n--here, inside a cloud session, starts it in this checkout and opens nothing. --teleport\nbrings the cloud session noted in the PR to the worktree. --adopt starts it in the\nworktree another tool (Orca, Paseo) just made, without asking nor opening Claude.\n\nworktree runs the steps of \"worktrees\" in .agents/flow.json: setup prepares this worktree,\nteardown cleans it before it goes (steps, ports, Docker), ports prints or moves its block\nof ports, cleanup deletes the Docker of deleted worktrees, and prune deletes the worktrees\nwhose PR was merged or closed. session is the SessionStart hook of Claude Code.\n\npr worktree makes (or reuses) a worktree on a PR's own branch, to review and fix it; with\n--, it runs the command there (Claude if none). adapter is what Orca and Paseo run: setup\nwhen they make a worktree (its steps and, if it came from an issue, issue start --adopt),\nissue to start one by hand, and archive before they delete it. hook implements Claude\nCode's WorktreeCreate and WorktreeRemove for claude --worktree.\n\ninit sets the flow up in the repo: .agents/flow.json (with the recipes its files suggest),\nthe dev dependencies and scripts, and the hooks. doctor checks them at this version, and\nupgrade moves them to this version. npx @ramon-sg/agent-kit runs the three, along with the\nskills, claude-cloud and claude-repo-guard.";
|
package/dist/usage.js
CHANGED
|
@@ -18,9 +18,9 @@ export const USAGE = `Usage:
|
|
|
18
18
|
issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]
|
|
19
19
|
issue-flow session
|
|
20
20
|
issue-flow hook worktree-create | worktree-remove
|
|
21
|
-
issue-flow init [--yes] [--recipe <name>,...]
|
|
22
|
-
issue-flow doctor
|
|
23
|
-
issue-flow upgrade [--
|
|
21
|
+
issue-flow init [--yes] [--recipe <name>,...]
|
|
22
|
+
issue-flow doctor [--json]
|
|
23
|
+
issue-flow upgrade [--dry-run]
|
|
24
24
|
|
|
25
25
|
The slug comes from the issue's "Slug de rama" block, then from Claude, then from the title,
|
|
26
26
|
and the branch from the repo's template (an older "Rama sugerida" block is used if it fits).
|
|
@@ -48,7 +48,7 @@ when they make a worktree (its steps and, if it came from an issue, issue start
|
|
|
48
48
|
issue to start one by hand, and archive before they delete it. hook implements Claude
|
|
49
49
|
Code's WorktreeCreate and WorktreeRemove for claude --worktree.
|
|
50
50
|
|
|
51
|
-
init sets the
|
|
52
|
-
dependencies and scripts, and the hooks
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
init sets the flow up in the repo: .agents/flow.json (with the recipes its files suggest),
|
|
52
|
+
the dev dependencies and scripts, and the hooks. doctor checks them at this version, and
|
|
53
|
+
upgrade moves them to this version. npx @ramon-sg/agent-kit runs the three, along with the
|
|
54
|
+
skills, claude-cloud and claude-repo-guard.`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ramon-sg/issue-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Issue-to-PR workflow for any repo: names each issue's branch, creates its worktree, opens the draft PR and launches Claude Code, locally and in Claude's cloud.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Work starts from staging and PRs go there; main only gets releases.
|
|
2
|
+
"description": "Work starts from staging and PRs go there; main only gets releases.",
|
|
3
3
|
"detect": [],
|
|
4
4
|
"config": { "baseBranch": "staging" }
|
|
5
5
|
}
|
package/dist/guard-config.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** What the auto mode is told about the flow. */
|
|
2
|
-
export declare const GUARD_ENVIRONMENT = "issue-flow is this repo's issue workflow: making the branch and worktree of an issue, its empty start commit, pushing it, opening its draft PR, assigning the issue and adding its \"Slug de rama\" block are normal steps of the work, not risky actions.";
|
|
3
|
-
/** The option that lets `gh` verify TLS inside the macOS sandbox. */
|
|
4
|
-
export declare const WEAKER_NETWORK = "weakerNetworkIsolation";
|
|
5
|
-
/**
|
|
6
|
-
* The guard's options with what the flow needs added; what the repo set stays.
|
|
7
|
-
* @param current - The options, or null if there is no file.
|
|
8
|
-
* @param baseBranch - The flow's `baseBranch`, or null.
|
|
9
|
-
* @param platform - The operating system (`process.platform`).
|
|
10
|
-
* @returns The new options and what changed.
|
|
11
|
-
* @example guardOptions(null, "staging", "darwin").options.integrationBranch // "staging"
|
|
12
|
-
*/
|
|
13
|
-
export declare function guardOptions(current: Record<string, unknown> | null, baseBranch: string | null, platform: string): {
|
|
14
|
-
options: Record<string, unknown>;
|
|
15
|
-
changes: string[];
|
|
16
|
-
};
|
package/dist/guard-config.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What `issue-flow` writes in `claude-repo-guard`'s options before installing it. The
|
|
3
|
-
* auto mode's classifier gets told that the flow's own writes (the start commit, the push,
|
|
4
|
-
* the draft PR, the assignment, the "Slug de rama" block) are the normal workflow. A repo
|
|
5
|
-
* with its own base branch (`staging`) gets it as `integrationBranch`, so merging into it is
|
|
6
|
-
* part of the flow too. On macOS `gh` needs `weakerNetworkIsolation` inside the sandbox.
|
|
7
|
-
*/
|
|
8
|
-
import { GUARD_CONFIG } from "./setup-files.js";
|
|
9
|
-
/** What the auto mode is told about the flow. */
|
|
10
|
-
export const GUARD_ENVIRONMENT = "issue-flow is this repo's issue workflow: making the branch and worktree of an issue, its empty start commit, pushing it, opening its draft PR, assigning the issue and adding its \"Slug de rama\" block are normal steps of the work, not risky actions.";
|
|
11
|
-
/** The option that lets `gh` verify TLS inside the macOS sandbox. */
|
|
12
|
-
export const WEAKER_NETWORK = "weakerNetworkIsolation";
|
|
13
|
-
/**
|
|
14
|
-
* The guard's options with what the flow needs added; what the repo set stays.
|
|
15
|
-
* @param current - The options, or null if there is no file.
|
|
16
|
-
* @param baseBranch - The flow's `baseBranch`, or null.
|
|
17
|
-
* @param platform - The operating system (`process.platform`).
|
|
18
|
-
* @returns The new options and what changed.
|
|
19
|
-
* @example guardOptions(null, "staging", "darwin").options.integrationBranch // "staging"
|
|
20
|
-
*/
|
|
21
|
-
export function guardOptions(current, baseBranch, platform) {
|
|
22
|
-
const options = { ...(current ?? {}) };
|
|
23
|
-
const changes = [];
|
|
24
|
-
const environment = Array.isArray(options.environment) ? options.environment.map(String) : [];
|
|
25
|
-
if (!environment.includes(GUARD_ENVIRONMENT)) {
|
|
26
|
-
options.environment = [...environment, GUARD_ENVIRONMENT];
|
|
27
|
-
changes.push(`${GUARD_CONFIG}: tells the auto mode about the issue flow`);
|
|
28
|
-
}
|
|
29
|
-
if (baseBranch !== null && (options.integrationBranch === undefined || options.integrationBranch === "")) {
|
|
30
|
-
options.integrationBranch = baseBranch;
|
|
31
|
-
changes.push(`${GUARD_CONFIG}: integrationBranch ${baseBranch}`);
|
|
32
|
-
}
|
|
33
|
-
if (platform === "darwin" && options[WEAKER_NETWORK] === undefined) {
|
|
34
|
-
options[WEAKER_NETWORK] = true;
|
|
35
|
-
changes.push(`${GUARD_CONFIG}: ${WEAKER_NETWORK} true, so gh works inside the macOS sandbox`);
|
|
36
|
-
}
|
|
37
|
-
return { options, changes };
|
|
38
|
-
}
|