agent-kitchen 0.7.0 → 0.8.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 CHANGED
@@ -59,7 +59,7 @@ npx agent-kitchen doctor # revisa cada pieza con su propio
59
59
 
60
60
  - **Sin terminal y sin `--yes`** (por ejemplo, desde un agente), solo muestra lo que ofrecería.
61
61
  - **Corre en el checkout principal,** no en un worktree, y desde tu terminal. Dentro de una sesión de Claude, el sandbox de `claude-repo-guard` no deja escribir `.claude/settings.json`.
62
- - **Al terminar,** corre el `install` de tu gestor de paquetes, revisa el diff y haz commit. O usa `--commit`.
62
+ - **Al terminar,** si los pasos cambiaron `package.json`, corre el `install` de tu gestor de paquetes. Te queda revisar el diff y hacer commit. O usa `--commit`.
63
63
 
64
64
  **`--commit`** deja los cambios commiteados sin que lo haga un agente:
65
65
 
@@ -11,7 +11,8 @@
11
11
  * flow's version), one line each, with the
12
12
  * tools' output only when one fails; one that fails does not stop the rest. With `--yes`
13
13
  * it does what is suggested without asking (and keeps the repo's own skills); without a
14
- * terminal it only shows the steps. With `--commit` it first checks the repo is clean and on
14
+ * terminal it only shows the steps. Without `--commit`, it installs at the end if the steps
15
+ * changed package.json. With `--commit` it first checks the repo is clean and on
15
16
  * its base branch, and at the end installs if package.json changed and asks what to do with
16
17
  * the changes (see git-finish.ts), also when the only change is the decisions file below;
17
18
  * the steps then show as plain lines, since a spinner before a question leaves it deaf.
@@ -33,11 +34,11 @@ import { issueFlowStatus, NAME as FLOW } from "../pieces/issue-flow.js";
33
34
  import { keepOwn, NAME as SKILLS, ownInPick, skillsStatus } from "../pieces/skills.js";
34
35
  import { agentsMdStatus, NAME as AGENTS_MD } from "../pieces/agents-md.js";
35
36
  import { NAME as SKILLS_MANAGER, skillsManagerStatus } from "../pieces/skills-manager.js";
36
- import { mainCheckout, packageManager } from "../repo.js";
37
+ import { mainCheckout, packageManager, packageText } from "../repo.js";
37
38
  import { download, failedOutput, runInstall, setVerbose } from "../tools.js";
38
39
  import { commitLine, DECISIONS_LINE } from "../commit-texts.js";
39
40
  import { progressFor } from "../progress.js";
40
- import { stepLabel, ui } from "../texts/index.js";
41
+ import { phrases, stepLabel, ui } from "../texts/index.js";
41
42
  /** Every piece, in the order its steps run. */
42
43
  const PIECES = [SKILLS_MANAGER, FLOW, CLOUD, GUARD, SKILLS, AGENTS_MD];
43
44
  /**
@@ -367,6 +368,7 @@ export async function sync(options, out) {
367
368
  out.stdout(noStepsMessage("none", wrote));
368
369
  return endWithoutSteps();
369
370
  }
371
+ const pkgBefore = packageText(root);
370
372
  const failedSteps = [];
371
373
  for (const step of chosen) {
372
374
  if (!(await runStep(root, step, progress, out)))
@@ -375,9 +377,28 @@ export async function sync(options, out) {
375
377
  const failed = failedSteps.map(stepLabel);
376
378
  if (base !== null)
377
379
  return commitChanges(root, base, commitLabels(chosen.filter((step) => !failedSteps.includes(step)), wrote), failed, progress, options, out);
378
- out.stdout(`\n${ui.summary(failed)} ${ui.next(packageManager(root))}\n`);
380
+ const installFailed = packageText(root) !== pkgBefore ? !(await install(root, progress, out)) : false;
381
+ if (installFailed)
382
+ failed.push(phrases({ kind: "install-deps", manager: packageManager(root) }).menu);
383
+ out.stdout(`\n${ui.summary(failed)} ${ui.next(installFailed ? packageManager(root) : null)}\n`);
379
384
  return failed.length === 0 ? 0 : 1;
380
385
  }
386
+ /**
387
+ * Runs the repo's install with the same progress as a step, and shows its output if it fails.
388
+ * @param root - The repo.
389
+ * @param progress - How progress is shown.
390
+ * @param out - Where to write.
391
+ * @returns Whether it worked.
392
+ */
393
+ async function install(root, progress, out) {
394
+ const text = { kind: "install-deps", manager: packageManager(root) };
395
+ progress.start(text);
396
+ const installed = await runInstall(root, text.manager);
397
+ progress.stop(text, installed.ok);
398
+ if (!installed.ok)
399
+ out.stderr(failedOutput(installed.output));
400
+ return installed.ok;
401
+ }
381
402
  /**
382
403
  * The end of `--commit`: the install if package.json changed, the changes, the question (or
383
404
  * the branch with a PR under --yes) and what git did.
@@ -399,16 +420,9 @@ async function commitChanges(root, base, done, failed, progress, options, out) {
399
420
  }
400
421
  const manager = packageManager(root);
401
422
  const packageChanged = changed.includes("package.json");
402
- if (packageChanged) {
403
- const text = { kind: "install-deps", manager };
404
- progress.start(text);
405
- const installed = await runInstall(root, manager);
406
- progress.stop(text, installed.ok);
407
- if (!installed.ok) {
408
- out.stderr(failedOutput(installed.output));
409
- out.stdout(`\n${summary} No commiteé: el install falló y los cambios quedan sin commit.\n`);
410
- return 1;
411
- }
423
+ if (packageChanged && !(await install(root, progress, out))) {
424
+ out.stdout(`\n${summary} No commiteé: el install falló y los cambios quedan sin commit.\n`);
425
+ return 1;
412
426
  }
413
427
  const paths = changedPaths(root);
414
428
  out.stdout(`\n${summary} Cambios:\n${paths.map((path) => ` ${path}`).join("\n")}\n`);
@@ -6,7 +6,8 @@ export interface Progress {
6
6
  stop: (text: StepText, ok: boolean) => void;
7
7
  }
8
8
  /**
9
- * Progress as plain lines: the start, a line now and then while it runs, and the end.
9
+ * Progress as plain lines: the start (marked ◒, like the spinner), a line now and then while it
10
+ * runs, and the end (marked ✓ or ✗).
10
11
  * @param out - Where to write.
11
12
  * @param options - Whether to print while it runs, how often, and the clock.
12
13
  * @returns The progress.
package/dist/progress.js CHANGED
@@ -20,7 +20,8 @@ function endLine(text, ok, ms) {
20
20
  return `${ok ? said.done : ui.failed(said.menu)} (${ui.duration(ms)})`;
21
21
  }
22
22
  /**
23
- * Progress as plain lines: the start, a line now and then while it runs, and the end.
23
+ * Progress as plain lines: the start (marked ◒, like the spinner), a line now and then while it
24
+ * runs, and the end (marked ✓ or ✗).
24
25
  * @param out - Where to write.
25
26
  * @param options - Whether to print while it runs, how often, and the clock.
26
27
  * @returns The progress.
@@ -33,7 +34,7 @@ export function plainProgress(out, options) {
33
34
  return {
34
35
  start: (text) => {
35
36
  started = now();
36
- out.stdout(`${phrases(text).doing}\n`);
37
+ out.stdout(`◒ ${phrases(text).doing}\n`);
37
38
  if (options.ticks)
38
39
  timer = setInterval(() => out.stdout(`${ui.still(ui.duration(now() - started))}\n`), options.tickMs ?? TICK_MS);
39
40
  },
package/dist/repo.d.ts CHANGED
@@ -46,6 +46,13 @@ export declare function dependencyVersion(pkg: Record<string, unknown> | null, n
46
46
  * @example packageManager(root) // "pnpm"
47
47
  */
48
48
  export declare function packageManager(root: string): string;
49
+ /**
50
+ * The text of the repo's package.json, to tell later whether a run changed it.
51
+ * @param root - The repo.
52
+ * @returns Its text, or null if there is none.
53
+ * @example packageText(root) // '{ "devDependencies": … }'
54
+ */
55
+ export declare function packageText(root: string): string | null;
49
56
  /**
50
57
  * The version of a package installed in the repo's `node_modules`.
51
58
  * @param root - The repo.
package/dist/repo.js CHANGED
@@ -115,6 +115,16 @@ export function packageManager(root) {
115
115
  return field;
116
116
  return LOCKFILES.find(([file]) => existsSync(join(root, file)))?.[1] ?? "npm";
117
117
  }
118
+ /**
119
+ * The text of the repo's package.json, to tell later whether a run changed it.
120
+ * @param root - The repo.
121
+ * @returns Its text, or null if there is none.
122
+ * @example packageText(root) // '{ "devDependencies": … }'
123
+ */
124
+ export function packageText(root) {
125
+ const file = join(root, "package.json");
126
+ return existsSync(file) ? readFileSync(file, "utf8") : null;
127
+ }
118
128
  /**
119
129
  * The version of a package installed in the repo's `node_modules`.
120
130
  * @param root - The repo.
@@ -37,8 +37,8 @@ export interface Catalog {
37
37
  suggested: (dryRun: boolean) => string;
38
38
  /** The last line when every step worked or some failed. */
39
39
  summary: (failed: string[]) => string;
40
- /** What is left to do by hand after the steps, without --commit. */
41
- next: (manager: string) => string;
40
+ /** What is left to do by hand after the steps, without --commit; with the manager if its install failed. */
41
+ next: (failedInstall: string | null) => string;
42
42
  /** A new piece left out because no one was asked about it. */
43
43
  undecided: (names: string[]) => string;
44
44
  /** The repo's own skills left out of a pick. */
package/dist/texts/es.js CHANGED
@@ -127,7 +127,7 @@ export const es = {
127
127
  onlyOptional: (menus) => `Todo al día. Opcional, para elegir en una terminal: ${menus.join("; ")}.`,
128
128
  suggested: (dryRun) => `Pasos sugeridos${dryRun ? "" : " (para hacerlos, corre agent-kitchen apply en una terminal, o con --yes)"}:`,
129
129
  summary: (failed) => (failed.length === 0 ? "Listo." : `Terminó, pero fallaron: ${failed.join("; ")}.`),
130
- next: (manager) => `Falta: corre '${manager} install', revisa el diff y haz commit.`,
130
+ next: (failedInstall) => `Falta: ${failedInstall === null ? "" : `corre '${failedInstall} install', `}revisa el diff y haz commit.`,
131
131
  undecided: (names) => `Pieza nueva sin decisión: ${list(names)}. Corre agent-kitchen apply en una terminal para elegir.`,
132
132
  ownSkillsKept: (names) => `Se dejan fuera las skills que el repo ya tiene como propias: ${list(names)}. Para reemplazarlas, corre agent-kitchen apply en una terminal.`,
133
133
  saved: (file, commits) => `Guardé qué piezas usa el repo en ${file}${commits ? "" : "; commitéalo"}.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-kitchen",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "One command to install, update and check a repo's agent tooling: the issue flow and its skills, the general skills, Claude cloud sessions, claude-repo-guard and the installed-versions rule in AGENTS.md.",
5
5
  "license": "MIT",
6
6
  "type": "module",