agent-kitchen 0.2.0 → 0.3.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/dist/menu.js CHANGED
@@ -1,9 +1,21 @@
1
1
  /**
2
- * @file The checkbox menu of the steps (`@clack/prompts`): one line per step, the ones a
3
- * piece suggests already ticked; space toggles, enter runs (the library shows the keys). It
4
- * only runs with a terminal.
2
+ * @file The questions, in Spanish (`@clack/prompts`), all asked before any step runs: the
3
+ * quick path (do everything suggested, pick, or leave), the checkbox menu of the steps, the
4
+ * skills of a step grouped by catalog with their description, and whether to replace the
5
+ * repo's own skills. They only run with a terminal.
5
6
  */
6
- import { isCancel, multiselect } from "@clack/prompts";
7
+ import { styleText } from "node:util";
8
+ import { confirm, groupMultiselect, isCancel, MULTISELECT_INSTRUCTIONS, multiselect, select, SELECT_INSTRUCTIONS } from "@clack/prompts";
9
+ /** Width a skill's description is cut to in the menu. */
10
+ const DESCRIPTION_WIDTH = 70;
11
+ /**
12
+ * Puts the keys' help of the menus in Spanish. The library exports those lines as arrays,
13
+ * so they are replaced in place.
14
+ */
15
+ function spanishKeys() {
16
+ MULTISELECT_INSTRUCTIONS.splice(0, MULTISELECT_INSTRUCTIONS.length, `${styleText("dim", "↑/↓")} moverse`, `${styleText("dim", "Espacio:")} marcar`, `${styleText("dim", "Enter:")} seguir`);
17
+ SELECT_INSTRUCTIONS.splice(0, SELECT_INSTRUCTIONS.length, `${styleText("dim", "↑/↓")} moverse`, `${styleText("dim", "Enter:")} elegir`);
18
+ }
7
19
  /**
8
20
  * The menu's options: each step's index and label, which names its piece.
9
21
  * @param steps - The steps.
@@ -16,6 +28,36 @@ export function menuOptions(steps) {
16
28
  initialValues: steps.flatMap((step, index) => (step.checked ? [index] : [])),
17
29
  };
18
30
  }
31
+ /**
32
+ * The skills menu's options: grouped by catalog, each with its description cut short.
33
+ * @param candidates - What the catalogs offer.
34
+ * @returns The groups.
35
+ * @example Object.keys(skillOptions(candidates)) // ["ramon-sg/issue-flow", "ramon-sg/agent-skills"]
36
+ */
37
+ export function skillOptions(candidates) {
38
+ const groups = {};
39
+ for (const candidate of candidates) {
40
+ const hint = candidate.description.length > DESCRIPTION_WIDTH ? `${candidate.description.slice(0, DESCRIPTION_WIDTH - 1)}…` : candidate.description;
41
+ const group = candidate.catalog === "" ? "skills" : candidate.catalog;
42
+ groups[group] = [...(groups[group] ?? []), { value: candidate.id, label: candidate.name, ...(hint === "" ? {} : { hint }) }];
43
+ }
44
+ return groups;
45
+ }
46
+ /**
47
+ * Asks what to do: everything suggested, pick step by step, or leave.
48
+ * @param count - How many steps are suggested; with none, the option to do them is not shown.
49
+ * @returns The answer; "leave" if the person cancelled.
50
+ * @example await askPath(4) // "all"
51
+ */
52
+ export async function askPath(count) {
53
+ spanishKeys();
54
+ const all = { value: "all", label: `Todo lo sugerido (${count} ${count === 1 ? "paso" : "pasos"})`, hint: "recomendado" };
55
+ const answer = await select({
56
+ message: "¿Qué hago?",
57
+ options: [...(count === 0 ? [] : [all]), { value: "pick", label: "Elegir qué hacer" }, { value: "leave", label: "Salir sin cambiar nada" }],
58
+ });
59
+ return isCancel(answer) ? "leave" : answer;
60
+ }
19
61
  /**
20
62
  * Asks which steps to run.
21
63
  * @param steps - The steps.
@@ -23,9 +65,36 @@ export function menuOptions(steps) {
23
65
  * @example await askSteps(steps)
24
66
  */
25
67
  export async function askSteps(steps) {
68
+ spanishKeys();
26
69
  const { options, initialValues } = menuOptions(steps);
27
- const picked = await multiselect({ message: "What to do?", options, initialValues, required: false });
70
+ const picked = await multiselect({ message: "¿Qué pasos hago?", options, initialValues, required: false });
28
71
  if (isCancel(picked))
29
72
  return null;
30
73
  return steps.filter((_, index) => picked.includes(index));
31
74
  }
75
+ /**
76
+ * Asks which skills to install.
77
+ * @param candidates - What the catalogs offer.
78
+ * @param ticked - The ids ticked at the start.
79
+ * @returns The picked ids, or null if the person cancelled.
80
+ * @example await askSkills(candidates, ["catalog:ramon-sg/agent-skills/planning"])
81
+ */
82
+ export async function askSkills(candidates, ticked) {
83
+ spanishKeys();
84
+ const picked = await groupMultiselect({ message: "¿Qué skills instalo?", options: skillOptions(candidates), initialValues: ticked, required: false });
85
+ return isCancel(picked) ? null : picked;
86
+ }
87
+ /**
88
+ * Asks whether the catalog's skills replace the repo's own ones with the same name.
89
+ * @param own - Their names.
90
+ * @returns True to replace them, false to keep them, or null if the person cancelled.
91
+ * @example await askReplace(["planning"])
92
+ */
93
+ export async function askReplace(own) {
94
+ const answer = await confirm({
95
+ message: `El repo ya tiene su propia versión de: ${own.join(", ")}. ¿Las reemplazo por las del catálogo? Su project.md se conserva.`,
96
+ active: "Sí, reemplazar",
97
+ inactive: "No, dejarlas fuera",
98
+ });
99
+ return isCancel(answer) ? null : answer;
100
+ }
package/dist/piece.d.ts CHANGED
@@ -5,7 +5,6 @@
5
5
  * and runs the picked ones in order. Every step runs the piece's own tool, which owns its
6
6
  * files: agent-kitchen only writes the few lines that join two pieces.
7
7
  */
8
- import type { Output } from "./output.js";
9
8
  /** The issue flow on npm. */
10
9
  export declare const FLOW_PACKAGE = "@ramon-sg/issue-flow";
11
10
  /** The GitHub repo of the issue flow, which is also the catalog of its skills. */
@@ -20,12 +19,29 @@ export declare const SKILLS_MANIFEST = ".agents/skills.json";
20
19
  export declare const SKILLS_LOCK = ".agents/skills.lock.json";
21
20
  /** The issue flow's config. */
22
21
  export declare const FLOW_CONFIG = ".agents/flow.json";
23
- /** One step a piece offers: what it does, and how it runs. */
22
+ /** A skill a catalog offers, as `skills-manager --candidates` lists it. */
23
+ export interface Candidate {
24
+ id: string;
25
+ name: string;
26
+ catalog: string;
27
+ description: string;
28
+ skills: string[];
29
+ requires: string[];
30
+ }
31
+ /** The skills a step installs: what it offers, and what the person picked and lets it replace. */
32
+ export interface SkillPick {
33
+ candidates: Candidate[];
34
+ picked: string[];
35
+ replace: string[];
36
+ }
37
+ /** One step a piece offers: what it does, what it downloads, and how it runs. */
24
38
  export interface Step {
25
39
  piece: string;
26
40
  label: string;
27
41
  checked: boolean;
28
- run: (context: Context) => boolean;
42
+ downloads: string[];
43
+ skills?: SkillPick;
44
+ run: (context: Context) => Promise<boolean>;
29
45
  }
30
46
  /** What a piece found in the repo: whether it has it, how it is, and the steps offered. */
31
47
  export interface PieceStatus {
@@ -35,11 +51,15 @@ export interface PieceStatus {
35
51
  state: string;
36
52
  steps: Step[];
37
53
  }
38
- /** What a step gets when it runs. */
54
+ /**
55
+ * What a step gets when it runs: the repo, and where to say what it did (`note`, one short
56
+ * line under the step) or what went wrong (`fail`, with the command to run by hand and the
57
+ * tool's output).
58
+ */
39
59
  export interface Context {
40
60
  root: string;
41
- yes: boolean;
42
- out: Output;
61
+ note: (text: string) => void;
62
+ fail: (text: string, output?: string) => void;
43
63
  }
44
64
  /** One check of `agent-kitchen doctor`: passed, failed, or only worth knowing. */
45
65
  export interface Check {
package/dist/piece.js CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @file What agent-kitchen knows of each piece it manages, and the names they share. A piece
3
+ * (the issue flow, claude-cloud, claude-repo-guard, the skills) tells what the repo has and
4
+ * the steps that would install or update it; agent-kitchen shows them, lets the person pick,
5
+ * and runs the picked ones in order. Every step runs the piece's own tool, which owns its
6
+ * files: agent-kitchen only writes the few lines that join two pieces.
7
+ */
1
8
  /** The issue flow on npm. */
2
9
  export const FLOW_PACKAGE = "@ramon-sg/issue-flow";
3
10
  /** The GitHub repo of the issue flow, which is also the catalog of its skills. */
@@ -1,17 +1,26 @@
1
+ /**
2
+ * @file Claude cloud sessions (`@ramon-sg/claude-cloud`). The repo's version is the one
3
+ * `.claude/cloud.json` pins: the cloud machine runs exactly that one. Installing runs its
4
+ * `init`; updating only moves that version (and a `$schema` pinned to a version), which is
5
+ * how `claude-cloud` updates. Outside
6
+ * the cloud its hook does nothing, so a repo nobody opens in the cloud loses nothing.
7
+ */
1
8
  import type { Check, PieceStatus } from "../piece.js";
2
9
  /** The piece's name, as `--skip` takes it. */
3
10
  export declare const NAME = "claude-cloud";
11
+ /** The package on npm. */
12
+ export declare const PACKAGE = "@ramon-sg/claude-cloud";
4
13
  /**
5
14
  * What the repo has of claude-cloud, and the step it needs.
6
15
  * @param root - The repo.
7
16
  * @returns The piece's status.
8
- * @example cloudStatus(root).state // "1.0.0, the latest"
17
+ * @example (await cloudStatus(root)).state // "1.0.0, al día"
9
18
  */
10
- export declare function cloudStatus(root: string): PieceStatus;
19
+ export declare function cloudStatus(root: string): Promise<PieceStatus>;
11
20
  /**
12
21
  * Whether `.claude/cloud.json` pins a version, and whether a newer one exists.
13
22
  * @param root - The repo.
14
23
  * @returns The checks.
15
- * @example cloudChecks(root)[0].status // "ok"
24
+ * @example (await cloudChecks(root))[0].status // "ok"
16
25
  */
17
- export declare function cloudChecks(root: string): Check[];
26
+ export declare function cloudChecks(root: string): Promise<Check[]>;
@@ -1,17 +1,9 @@
1
- /**
2
- * @file Claude cloud sessions (`@ramon-sg/claude-cloud`). The repo's version is the one
3
- * `.claude/cloud.json` pins: the cloud machine runs exactly that one. Installing runs its
4
- * `init`; updating only moves that version (and a `$schema` pinned to a version), which is
5
- * how `claude-cloud` updates. Outside
6
- * the cloud its hook does nothing, so a repo nobody opens in the cloud loses nothing.
7
- */
8
- import { warn } from "../output.js";
9
1
  import { readJson, writeJson } from "../repo.js";
10
2
  import { compareVersions, isRelease, npmLatest, runNpx } from "../tools.js";
11
3
  /** The piece's name, as `--skip` takes it. */
12
4
  export const NAME = "claude-cloud";
13
5
  /** The package on npm. */
14
- const PACKAGE = "@ramon-sg/claude-cloud";
6
+ export const PACKAGE = "@ramon-sg/claude-cloud";
15
7
  /** Its config in the repo, which pins its version. */
16
8
  const CONFIG = ".claude/cloud.json";
17
9
  /**
@@ -35,11 +27,13 @@ function stepFor(config, latest) {
35
27
  return {
36
28
  piece: NAME,
37
29
  checked: true,
38
- label: `claude-cloud ${latest ?? "1.x"}: prepares Claude cloud sessions (${CONFIG})`,
39
- run: (context) => {
40
- if (runNpx(context.root, [spec, "init"]))
30
+ downloads: [spec],
31
+ label: `Instalar claude-cloud ${latest ?? "1.x"}: prepara las sesiones en la nube de Claude`,
32
+ run: async (context) => {
33
+ const run = await runNpx(context.root, [spec, "init"]);
34
+ if (run.ok)
41
35
  return true;
42
- warn(context.out, `claude-cloud init failed; run 'npx ${spec} init'.`);
36
+ context.fail(`claude-cloud init falló; corre 'npx ${spec} init'.`, run.output);
43
37
  return false;
44
38
  },
45
39
  };
@@ -49,8 +43,9 @@ function stepFor(config, latest) {
49
43
  return {
50
44
  piece: NAME,
51
45
  checked: true,
52
- label: `claude-cloud ${version ?? "?"} → ${latest}: the version ${CONFIG} pins`,
53
- run: (context) => {
46
+ downloads: [],
47
+ label: `Actualizar claude-cloud ${version ?? "?"} → ${latest} (la versión que fija ${CONFIG})`,
48
+ run: async (context) => {
54
49
  const current = readJson(context.root, CONFIG) ?? {};
55
50
  const schema = typeof current.$schema === "string" ? current.$schema.replace(/@ramon-sg\/claude-cloud@[^/]+\//, `${PACKAGE}@${latest}/`) : undefined;
56
51
  writeJson(context.root, CONFIG, { ...current, ...(schema === undefined ? {} : { $schema: schema }), version: latest });
@@ -62,32 +57,32 @@ function stepFor(config, latest) {
62
57
  * What the repo has of claude-cloud, and the step it needs.
63
58
  * @param root - The repo.
64
59
  * @returns The piece's status.
65
- * @example cloudStatus(root).state // "1.0.0, the latest"
60
+ * @example (await cloudStatus(root)).state // "1.0.0, al día"
66
61
  */
67
- export function cloudStatus(root) {
62
+ export async function cloudStatus(root) {
68
63
  const config = readJson(root, CONFIG);
69
- const latest = npmLatest(PACKAGE);
64
+ const latest = await npmLatest(PACKAGE);
70
65
  const version = pinned(config);
71
66
  const step = stepFor(config, latest);
72
- const known = latest === null ? " (npm cannot tell the latest)" : "";
73
- const state = config === null ? `not installed${known}` : version === null ? `${CONFIG} pins no version` : step === null ? `${version}${known}` : `${version}, ${latest ?? "?"} available`;
74
- return { name: NAME, summary: "Claude cloud sessions: tools, Docker and the full clone", installed: config !== null, state, steps: step === null ? [] : [step] };
67
+ const known = latest === null ? " (npm no responde)" : "";
68
+ const state = config === null ? `no instalado${known}` : version === null ? `${CONFIG} no fija versión` : step === null ? `${version}, al día${known}` : `${version}, hay ${latest ?? "?"}`;
69
+ return { name: NAME, summary: "sesiones en la nube de Claude", installed: config !== null, state, steps: step === null ? [] : [step] };
75
70
  }
76
71
  /**
77
72
  * Whether `.claude/cloud.json` pins a version, and whether a newer one exists.
78
73
  * @param root - The repo.
79
74
  * @returns The checks.
80
- * @example cloudChecks(root)[0].status // "ok"
75
+ * @example (await cloudChecks(root))[0].status // "ok"
81
76
  */
82
- export function cloudChecks(root) {
77
+ export async function cloudChecks(root) {
83
78
  const config = readJson(root, CONFIG);
84
79
  if (config === null)
85
- return [{ name: NAME, status: "info", detail: "not installed" }];
80
+ return [{ name: NAME, status: "info", detail: "no instalado" }];
86
81
  const version = pinned(config);
82
+ const latest = await npmLatest(PACKAGE);
87
83
  if (version === null)
88
- return [{ name: NAME, status: "fail", detail: `${CONFIG} pins no exact version: set "version" to the one to run, like "${npmLatest(PACKAGE) ?? "1.0.0"}"` }];
89
- const latest = npmLatest(PACKAGE);
84
+ return [{ name: NAME, status: "fail", detail: `${CONFIG} no fija una versión exacta: pon en "version" la que se usa, como "${latest ?? "1.0.0"}"` }];
90
85
  if (latest !== null && compareVersions(latest, version) > 0)
91
- return [{ name: NAME, status: "info", detail: `${version}, ${latest} available: run npx agent-kitchen` }];
86
+ return [{ name: NAME, status: "info", detail: `${version}, hay ${latest}: corre npx agent-kitchen` }];
92
87
  return [{ name: NAME, status: "ok", detail: version }];
93
88
  }
@@ -1,6 +1,18 @@
1
+ /**
2
+ * @file The guardrails (`claude-repo-guard`): auto mode with the sandbox, installed in the
3
+ * repo. It is not on npm, so it runs from GitHub at its `vX.Y.Z` tag; the repo's version is
4
+ * the one its manifest records. Before installing or updating, agent-kitchen writes in its
5
+ * options what the issue flow needs, if the repo has the flow: the auto mode is told that
6
+ * the flow's writes are normal, the flow's base branch becomes `integrationBranch`, and on
7
+ * macOS `weakerNetworkIsolation` lets `gh` work inside the sandbox (the guard's own default
8
+ * is false, so false counts as missing; the step can be unticked). When the flow is
9
+ * installed in the same run, the guard gets those options in that run too.
10
+ */
1
11
  import { type Check, type PieceStatus } from "../piece.js";
2
12
  /** The piece's name, as `--skip` takes it. */
3
13
  export declare const NAME = "claude-repo-guard";
14
+ /** Its GitHub repo. */
15
+ export declare const REPO = "ramon-sg/claude-repo-guard";
4
16
  /** What the auto mode is told about the issue flow. */
5
17
  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.";
6
18
  /** The option that lets `gh` verify TLS inside the macOS sandbox. */
@@ -17,7 +29,7 @@ export declare function guardSpec(version: string | null): string;
17
29
  * @param root - The repo.
18
30
  * @param platform - The operating system (`process.platform`).
19
31
  * @returns The new options and what changed.
20
- * @example guardOptions(root, "darwin").changes // [".claude/repo-guard.config.json: weakerNetworkIsolation true, …"]
32
+ * @example guardOptions(root, "darwin").changes // ["weakerNetworkIsolation activado, …"]
21
33
  */
22
34
  export declare function guardOptions(root: string, platform: string): {
23
35
  options: Record<string, unknown>;
@@ -28,13 +40,13 @@ export declare function guardOptions(root: string, platform: string): {
28
40
  * @param root - The repo.
29
41
  * @param flowComing - Whether an earlier step of this run installs the issue flow.
30
42
  * @returns The piece's status.
31
- * @example guardStatus(root, false).state // "not installed"
43
+ * @example (await guardStatus(root, false)).state // "no instalado"
32
44
  */
33
- export declare function guardStatus(root: string, flowComing: boolean): PieceStatus;
45
+ export declare function guardStatus(root: string, flowComing: boolean): Promise<PieceStatus>;
34
46
  /**
35
47
  * The guard's own doctor at the repo's version and, on macOS, the option `gh` needs.
36
48
  * @param root - The repo.
37
49
  * @returns The checks.
38
- * @example guardChecks(root).map((check) => check.status) // ["ok"]
50
+ * @example (await guardChecks(root)).map((check) => check.status) // ["ok"]
39
51
  */
40
- export declare function guardChecks(root: string): Check[];
52
+ export declare function guardChecks(root: string): Promise<Check[]>;
@@ -8,14 +8,13 @@
8
8
  * is false, so false counts as missing; the step can be unticked). When the flow is
9
9
  * installed in the same run, the guard gets those options in that run too.
10
10
  */
11
- import { warn } from "../output.js";
12
11
  import { FLOW_CONFIG } from "../piece.js";
13
12
  import { readJson, writeJson } from "../repo.js";
14
13
  import { captureNpx, compareVersions, isRelease, latestTag, runNpx } from "../tools.js";
15
14
  /** The piece's name, as `--skip` takes it. */
16
15
  export const NAME = "claude-repo-guard";
17
16
  /** Its GitHub repo. */
18
- const REPO = "ramon-sg/claude-repo-guard";
17
+ export const REPO = "ramon-sg/claude-repo-guard";
19
18
  /** Its options in the repo. */
20
19
  const CONFIG = ".claude/repo-guard.config.json";
21
20
  /** What its install records, with its version. */
@@ -38,7 +37,7 @@ export function guardSpec(version) {
38
37
  * @param root - The repo.
39
38
  * @param platform - The operating system (`process.platform`).
40
39
  * @returns The new options and what changed.
41
- * @example guardOptions(root, "darwin").changes // [".claude/repo-guard.config.json: weakerNetworkIsolation true, …"]
40
+ * @example guardOptions(root, "darwin").changes // ["weakerNetworkIsolation activado, …"]
42
41
  */
43
42
  export function guardOptions(root, platform) {
44
43
  const options = { ...(readJson(root, CONFIG) ?? {}) };
@@ -47,16 +46,16 @@ export function guardOptions(root, platform) {
47
46
  const environment = Array.isArray(options.environment) ? options.environment.map(String) : [];
48
47
  if (flow !== null && !environment.includes(GUARD_ENVIRONMENT)) {
49
48
  options.environment = [...environment, GUARD_ENVIRONMENT];
50
- changes.push(`${CONFIG}: tells the auto mode about the issue flow`);
49
+ changes.push("le explica el flujo de issues al modo auto");
51
50
  }
52
51
  const baseBranch = typeof flow?.baseBranch === "string" ? flow.baseBranch : null;
53
52
  if (baseBranch !== null && (options.integrationBranch === undefined || options.integrationBranch === "")) {
54
53
  options.integrationBranch = baseBranch;
55
- changes.push(`${CONFIG}: integrationBranch ${baseBranch}`);
54
+ changes.push(`rama de integración ${baseBranch}`);
56
55
  }
57
56
  if (platform === "darwin" && options[WEAKER_NETWORK] !== true) {
58
57
  options[WEAKER_NETWORK] = true;
59
- changes.push(`${CONFIG}: ${WEAKER_NETWORK} true, so gh works inside the macOS sandbox`);
58
+ changes.push(`${WEAKER_NETWORK} activado, para que gh funcione en el sandbox de macOS`);
60
59
  }
61
60
  return { options, changes };
62
61
  }
@@ -66,15 +65,16 @@ export function guardOptions(root, platform) {
66
65
  * @param version - The version, or null for the default branch.
67
66
  * @returns Whether it installed.
68
67
  */
69
- function install(context, version) {
68
+ async function install(context, version) {
70
69
  const { options, changes } = guardOptions(context.root, process.platform);
71
70
  if (changes.length > 0)
72
71
  writeJson(context.root, CONFIG, options);
73
72
  for (const change of changes)
74
- context.out.stdout(` ${change}\n`);
75
- if (runNpx(context.root, [guardSpec(version), "install", "--yes"]))
73
+ context.note(change);
74
+ const run = await runNpx(context.root, [guardSpec(version), "install", "--yes"]);
75
+ if (run.ok)
76
76
  return true;
77
- failed(context.out, version);
77
+ context.fail(`claude-repo-guard no se instaló; corre 'npx ${guardSpec(version)} install' desde tu terminal (dentro de una sesión de Claude su sandbox bloquea .claude/settings.json).`, run.output);
78
78
  return false;
79
79
  }
80
80
  /**
@@ -84,20 +84,12 @@ function install(context, version) {
84
84
  * @param version - The guard's version, or null.
85
85
  * @returns Whether it worked, or true if nothing was missing.
86
86
  */
87
- function addOptions(context, version) {
87
+ async function addOptions(context, version) {
88
88
  if (guardOptions(context.root, process.platform).changes.length > 0)
89
89
  return install(context, version);
90
- context.out.stdout(" its options already have what the issue flow needs\n");
90
+ context.note("sus opciones ya tienen lo que necesita el flujo");
91
91
  return true;
92
92
  }
93
- /**
94
- * Says how to retry an install that failed.
95
- * @param out - Where to write.
96
- * @param version - The version it tried.
97
- */
98
- function failed(out, version) {
99
- warn(out, `claude-repo-guard did not install; run 'npx ${guardSpec(version)} install' from your terminal (inside a Claude session its sandbox blocks .claude/settings.json).`);
100
- }
101
93
  /**
102
94
  * The version its manifest records.
103
95
  * @param root - The repo.
@@ -118,11 +110,11 @@ function installedVersion(root) {
118
110
  */
119
111
  function stepFor(root, installed, latest, flowComing) {
120
112
  if (installed === undefined)
121
- return { piece: NAME, checked: true, label: `claude-repo-guard ${latest ?? "(main)"}: auto mode with the sandbox, and a doc of what it protects`, run: (context) => install(context, latest) };
113
+ return { piece: NAME, checked: true, downloads: [guardSpec(latest)], label: `Instalar claude-repo-guard ${latest ?? "(main)"}: modo auto con sandbox, y un documento de qué protege`, run: (context) => install(context, latest) };
122
114
  if (latest !== null && (installed === null || compareVersions(latest, installed) > 0))
123
- return { piece: NAME, checked: true, label: `claude-repo-guard ${installed ?? "?"} → ${latest}`, run: (context) => install(context, latest) };
115
+ return { piece: NAME, checked: true, downloads: [guardSpec(latest)], label: `Actualizar claude-repo-guard ${installed ?? "?"} → ${latest}`, run: (context) => install(context, latest) };
124
116
  if (flowComing || guardOptions(root, process.platform).changes.length > 0)
125
- return { piece: NAME, checked: true, label: "claude-repo-guard: what the issue flow needs in its options", run: (context) => addOptions(context, installed) };
117
+ return { piece: NAME, checked: true, downloads: [guardSpec(installed)], label: "Ajustar claude-repo-guard a lo que necesita el flujo de issues", run: (context) => addOptions(context, installed) };
126
118
  return null;
127
119
  }
128
120
  /**
@@ -130,30 +122,30 @@ function stepFor(root, installed, latest, flowComing) {
130
122
  * @param root - The repo.
131
123
  * @param flowComing - Whether an earlier step of this run installs the issue flow.
132
124
  * @returns The piece's status.
133
- * @example guardStatus(root, false).state // "not installed"
125
+ * @example (await guardStatus(root, false)).state // "no instalado"
134
126
  */
135
- export function guardStatus(root, flowComing) {
127
+ export async function guardStatus(root, flowComing) {
136
128
  const installed = installedVersion(root);
137
- const latest = latestTag(REPO);
138
- const known = latest === null ? " (GitHub has no release tag)" : "";
139
- const state = installed === undefined ? `not installed${known}` : latest !== null && (installed === null || compareVersions(latest, installed) > 0) ? `${installed ?? "unknown version"}, ${latest} available` : `${installed ?? "unknown version"}${known}`;
129
+ const latest = await latestTag(REPO);
130
+ const known = latest === null ? " (GitHub no tiene una versión)" : "";
131
+ const state = installed === undefined ? `no instalado${known}` : latest !== null && (installed === null || compareVersions(latest, installed) > 0) ? `${installed ?? "versión desconocida"}, hay ${latest}` : `${installed ?? "versión desconocida"}, al día${known}`;
140
132
  const step = stepFor(root, installed, latest, flowComing);
141
- return { name: NAME, summary: "guardrails: auto mode with the sandbox, writes only inside the worktree", installed: installed !== undefined, state, steps: step === null ? [] : [step] };
133
+ return { name: NAME, summary: "barandas: modo auto con sandbox", installed: installed !== undefined, state, steps: step === null ? [] : [step] };
142
134
  }
143
135
  /**
144
136
  * The guard's own doctor at the repo's version and, on macOS, the option `gh` needs.
145
137
  * @param root - The repo.
146
138
  * @returns The checks.
147
- * @example guardChecks(root).map((check) => check.status) // ["ok"]
139
+ * @example (await guardChecks(root)).map((check) => check.status) // ["ok"]
148
140
  */
149
- export function guardChecks(root) {
141
+ export async function guardChecks(root) {
150
142
  const installed = installedVersion(root);
151
143
  if (installed === undefined)
152
- return [{ name: NAME, status: "info", detail: "not installed (Claude opens with your own permissions)" }];
144
+ return [{ name: NAME, status: "info", detail: "no instalado (Claude se abre con tus propios permisos)" }];
153
145
  const checks = [];
154
146
  if (process.platform === "darwin" && readJson(root, CONFIG)?.[WEAKER_NETWORK] !== true)
155
- checks.push({ name: `${NAME}: ${WEAKER_NETWORK}`, status: "fail", detail: `not true in ${CONFIG}, so gh fails inside the macOS sandbox: run npx agent-kitchen` });
156
- const result = captureNpx(root, [guardSpec(installed), "doctor", "--json"]);
147
+ checks.push({ name: `${NAME}: ${WEAKER_NETWORK}`, status: "fail", detail: `no está activado en ${CONFIG}, así que gh falla en el sandbox de macOS: corre npx agent-kitchen` });
148
+ const result = await captureNpx(root, [guardSpec(installed), "doctor", "--json"]);
157
149
  let found;
158
150
  try {
159
151
  found = JSON.parse(result?.out ?? "");
@@ -162,10 +154,10 @@ export function guardChecks(root) {
162
154
  found = null;
163
155
  }
164
156
  if (!Array.isArray(found))
165
- return [...checks, { name: NAME, status: "fail", detail: `could not run its doctor; run 'npx ${guardSpec(installed)} doctor'` }];
157
+ return [...checks, { name: NAME, status: "fail", detail: `no se pudo correr su doctor; corre 'npx ${guardSpec(installed)} doctor'` }];
166
158
  const bad = found.filter((check) => typeof check !== "object" || check === null || check.ok !== true);
167
159
  if (bad.length === 0)
168
- return [...checks, { name: NAME, status: "ok", detail: `${installed ?? "unknown version"}, its doctor passes` }];
160
+ return [...checks, { name: NAME, status: "ok", detail: `${installed ?? "versión desconocida"}, su doctor pasa` }];
169
161
  return [...checks, ...bad.map((check) => {
170
162
  const entry = typeof check === "object" && check !== null ? check : { name: "?", detail: String(check) };
171
163
  return { name: `${NAME}: ${String(entry.name)}`, status: "fail", detail: String(entry.detail) };
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @file The issue flow (`@ramon-sg/issue-flow`): its CLI, hooks and config, and its skills.
3
+ * The repo's version is the one in package.json. Installing runs its `init` at the latest
4
+ * version; updating runs its `upgrade` at the new one and then moves its skills to the same
5
+ * tag, because the skills call the CLI. If only the skills are off, it moves them alone.
6
+ * `init` runs with `--yes`: the person answered agent-kitchen's questions before the steps.
7
+ */
1
8
  import { type Check, type PieceStatus } from "../piece.js";
2
9
  /** The piece's name, as `--skip` takes it. */
3
10
  export declare const NAME = "issue-flow";
@@ -20,13 +27,13 @@ export declare function flowVersion(root: string): string | null;
20
27
  * What the repo has of the issue flow, and the step it needs.
21
28
  * @param root - The repo.
22
29
  * @returns The piece's status.
23
- * @example issueFlowStatus(root).state // "0.1.0, 0.2.0 available"
30
+ * @example (await issueFlowStatus(root)).state // "0.1.0, hay 0.2.0"
24
31
  */
25
- export declare function issueFlowStatus(root: string): PieceStatus;
32
+ export declare function issueFlowStatus(root: string): Promise<PieceStatus>;
26
33
  /**
27
34
  * The flow's own doctor at the repo's version, and whether its skills are at that tag.
28
35
  * @param root - The repo.
29
36
  * @returns The checks.
30
- * @example issueFlowChecks(root)[0].name // "issue-flow: .agents/flow.json"
37
+ * @example (await issueFlowChecks(root))[0].name // "issue-flow: .agents/flow.json"
31
38
  */
32
- export declare function issueFlowChecks(root: string): Check[];
39
+ export declare function issueFlowChecks(root: string): Promise<Check[]>;