agent-cloud-kit 1.0.0-rc.1 → 1.0.0-rc.2

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
@@ -35,17 +35,23 @@ platform does not tell you:
35
35
  ## The flow
36
36
 
37
37
  1. **Install your agent tooling** with agent-kitchen (config, hook and skill arrive in the repo).
38
- 2. **Run `npx agent-cloud-kit prepare`** in a local terminal. It asks nothing up front: it
39
- creates a worktree on a dated branch, runs `init`, and opens
40
- Claude locally with the `cloud-ready` skill so it can write the cloud preparation with you. Then
41
- it writes the manual, commits, pushes and opens a draft PR.
38
+ 2. **Run `npx agent-cloud-kit prepare`** in a local terminal. It asks where to work, with the
39
+ same menu as agent-kitchen (`--pr` or `--here` skip it):
40
+ - **En un PR nuevo** (default): creates a worktree on a dated branch from `origin`, so your
41
+ branch and uncommitted files are untouched; runs `init`, opens Claude locally with the
42
+ `cloud-ready` skill so it can write the cloud preparation with you, then writes the manual,
43
+ commits, pushes and opens a draft PR.
44
+ - **En la rama actual, sin commit:** does the same in your checkout and stops there; you review
45
+ with `git diff`, commit, push, and then run `npx agent-cloud-kit verify`.
42
46
  3. **Configure the environment in claude.ai** following `docs/agent-cloud.md`, which `prepare`
43
47
  generated from your `.claude/cloud.json`. The document keeps growing: when the skill, locally
44
48
  or in the cloud, finds that a domain or an environment variable is missing, it adds it to
45
49
  `.claude/cloud.json`, regenerates the document and tells you in the session what to add in
46
50
  claude.ai.
47
- 4. **Verification in the cloud:** `prepare` launches `claude --cloud` on that branch, so a real
48
- cloud session runs `cloud-ready verify`, fixes what fails and reports in the PR.
51
+ 4. **Verification in the cloud:** `prepare` (new PR) or `verify` (current branch, once pushed)
52
+ launches `claude --cloud` on that branch, so a real cloud session runs `cloud-ready verify`,
53
+ fixes what fails and reports in the PR. Run `npx agent-cloud-kit verify` again whenever you
54
+ change the environment.
49
55
 
50
56
  `claude --cloud` uses the environment chosen with `/remote-env` in `claude`, and the Claude GitHub
51
57
  App must be installed on the repo (without it the session cannot push). Inside the cloud, `gh api`
@@ -145,7 +151,8 @@ A Node version that is not exact (`22`, `22.1`, `lts/iron`, `lts/*` or `node`) i
145
151
  | `agent-cloud-kit doctor` | Checks that the proxy lets the needed hosts through, that the pinned tools are installed, that Docker answers, and the recorded state (fingerprint and each step). Exits 1 if any check fails. |
146
152
  | `agent-cloud-kit init` | Creates `.claude/cloud.json` if missing, adds the `SessionStart` hook to `.claude/settings.json` (keeping the hooks already there), copies the `cloud-ready` skill to `.agents/skills/cloud-ready` when its version differs, and links it from `.claude/skills/cloud-ready`. Safe to run again. |
147
153
  | `agent-cloud-kit manual` | Writes `docs/agent-cloud.md` from `.claude/cloud.json`, deterministically: the exact setup script (`agent-cloud-kit@<major>`, or the exact version for a release candidate), domains, variable names, how to verify the environment. |
148
- | `agent-cloud-kit prepare [--resume]` | Local only, needs an interactive terminal: sets the repo up for the cloud on a new branch in a worktree, opens the PR and verifies it in a real cloud session (see "The flow"). `--resume` continues in the existing worktree. |
154
+ | `agent-cloud-kit prepare [--pr\|--here\|--resume]` | Local only, needs an interactive terminal: sets the repo up for the cloud, in a new PR or in the current branch without committing (asks when no option is given; see "The flow"). `--resume` continues in the existing worktree. |
155
+ | `agent-cloud-kit verify` | Local only, needs an interactive terminal: launches a cloud session that runs `cloud-ready verify` on the current branch, which must be prepared and pushed. |
149
156
 
150
157
  Environment variables: `AGENT_CLOUD_KIT_DIR` overrides `/opt/agent-cloud-kit` and `AGENT_CLOUD_KIT_HOME`
151
158
  overrides `/home/user`; both exist mainly for tests.
@@ -158,7 +165,7 @@ package that `setup` left on the saved machine, so it needs no network and no `n
158
165
  ```json
159
166
  {
160
167
  "type": "command",
161
- "command": "[ \"$CLAUDE_CODE_REMOTE\" = true ] || exit 0; /opt/agent-cloud-kit/bin/agent-cloud-kit session || echo 'agent-cloud-kit: the environment setup did not run; see npx agent-cloud-kit doctor'",
168
+ "command": "[ \"$CLAUDE_CODE_REMOTE\" = true ] || exit 0; /opt/agent-cloud-kit/bin/agent-cloud-kit session || echo 'agent-cloud-kit: el setup del entorno no corrió; revisa con npx agent-cloud-kit doctor'",
162
169
  "timeout": 600
163
170
  }
164
171
  ```
@@ -169,8 +176,8 @@ When the repo pins another version than the one that is running and that version
169
176
  ### Machine fingerprint
170
177
 
171
178
  `setup` stores a fingerprint of the config, the pinned versions, the images and the package
172
- version. If the repo changes afterwards, `session` warns that "the machine was prepared with
173
- another configuration". The saved machine is rebuilt by claude.ai about every 7 days, or when you
179
+ version. If the repo changes afterwards, `session` warns that "la máquina se preparó con
180
+ otra configuración". The saved machine is rebuilt by claude.ai about every 7 days, or when you
174
181
  edit the setup script field.
175
182
 
176
183
  ## The proxy CA and `docker build`
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * @file Command line entry point of agent-cloud-kit: wires `setup`, `session`, `docker`, `doctor`,
4
- * `init`, `manual` and `prepare` to their modules, relaunching or delegating to the version each
4
+ * `init`, `manual`, `prepare` and `verify` to their modules, relaunching or delegating to the version each
5
5
  * repo pins.
6
6
  */
7
7
  import { execFileSync } from "node:child_process";
@@ -16,27 +16,31 @@ import { dockerResponds, realDocker } from "./docker.js";
16
16
  import { init } from "./init.js";
17
17
  import { findRepos, packageBin, planSetup, relaunch } from "./launcher.js";
18
18
  import { MANUAL_FILE, writeManual } from "./manual.js";
19
- import { EXIT_INTERRUPTED, InterruptedError, branchDay, prepare } from "./prepare/index.js";
19
+ import { EXIT_INTERRUPTED, InterruptedError, branchDay, prepare, verifyBranch } from "./prepare/index.js";
20
20
  import { captureProcess, runProcess } from "./run.js";
21
+ import { chooseTerminal } from "./select.js";
21
22
  import { session } from "./session.js";
22
23
  import { setupRepo } from "./setup.js";
23
24
  /** Help text shown by `--help`. */
24
- const HELP = `${OUTPUT_PREFIX}prepares Claude Code cloud sessions.
25
+ const HELP = `${OUTPUT_PREFIX}prepara repos para las sesiones cloud de Claude Code.
25
26
 
26
- Usage: agent-cloud-kit <command>
27
+ Uso: agent-cloud-kit <comando>
27
28
 
28
- Commands:
29
- setup [--repo <path>] prepare the machine for every repo in the home dir (or only <path>)
30
- session report the machine state and wire the tools into the session
31
- docker start the Docker daemon and wait until it answers
32
- doctor check the network allowlist, tools, Docker and the machine state
33
- init create .claude/cloud.json and register the SessionStart hook
34
- manual write docs/agent-cloud.md from .claude/cloud.json
35
- prepare [--resume] run locally: set the repo up for the cloud on a new branch and verify it in a cloud session
29
+ Comandos:
30
+ setup [--repo <path>] prepara la máquina para cada repo del directorio home (o solo <path>)
31
+ session informa el estado de la máquina y conecta las herramientas a la sesión
32
+ docker inicia el demonio de Docker y espera a que responda
33
+ doctor revisa los dominios permitidos, las herramientas, Docker y el estado de la máquina
34
+ init crea .claude/cloud.json y registra el hook SessionStart
35
+ manual escribe docs/agent-cloud.md a partir de .claude/cloud.json
36
+ prepare [--pr|--here|--resume]
37
+ corre en local: deja el repo listo para la nube, en un PR nuevo (--pr) o en la
38
+ rama actual sin commit (--here); sin opción, pregunta dónde
39
+ verify corre en local: lanza una sesión cloud que verifica la rama actual (ya subida)
36
40
 
37
- Options:
38
- --help show this help
39
- --version print the package version
41
+ Opciones:
42
+ --help muestra esta ayuda
43
+ --version muestra la versión del paquete
40
44
  `;
41
45
  /** Environment variable with the project dir that Claude Code passes to hooks. */
42
46
  const PROJECT_DIR_VAR = "CLAUDE_PROJECT_DIR";
@@ -50,6 +54,10 @@ const REPO_OPTION = "--repo";
50
54
  const GIT_TIMEOUT_MS = 10_000;
51
55
  /** Option of `prepare` that continues in an existing prepare worktree. */
52
56
  const RESUME_OPTION = "--resume";
57
+ /** Option of `prepare`: work in a new worktree that ends in a PR, without asking. */
58
+ const PR_OPTION = "--pr";
59
+ /** Option of `prepare`: work in the current checkout, without committing and without asking. */
60
+ const HERE_OPTION = "--here";
53
61
  /** Exit code for success. */
54
62
  const EXIT_OK = 0;
55
63
  /** Exit code for failure or wrong usage. */
@@ -85,7 +93,7 @@ async function runOne(repo, deps) {
85
93
  for (const error of errors)
86
94
  deps.print(`${OUTPUT_PREFIX}${repo}: ${error}`);
87
95
  if (config && config.version !== deps.packageVersion) {
88
- deps.print(`${OUTPUT_PREFIX}running ${deps.packageVersion} although ${CONFIG_FILE} asks for ${config.version}`);
96
+ deps.print(`${OUTPUT_PREFIX}corriendo la versión ${deps.packageVersion}, aunque ${CONFIG_FILE} pide la ${config.version}`);
89
97
  }
90
98
  const state = await setupRepo(repo, {
91
99
  toolsDir: toolsDir(deps.env),
@@ -97,7 +105,7 @@ async function runOne(repo, deps) {
97
105
  });
98
106
  const failed = state.steps.filter((s) => !s.ok).map((s) => s.name);
99
107
  const detail = failed.length ? ` (${failed.join(", ")})` : "";
100
- deps.print(`${OUTPUT_PREFIX}setup ${repo}: ${state.steps.length} steps, ${failed.length} failed${detail}`);
108
+ deps.print(`${OUTPUT_PREFIX}setup ${repo}: ${state.steps.length} pasos, ${failed.length} fallaron${detail}`);
101
109
  }
102
110
  /**
103
111
  * Carries out one item of the setup plan; may throw, the caller reports it and moves on.
@@ -107,19 +115,19 @@ async function runOne(repo, deps) {
107
115
  */
108
116
  async function runPlanItem(item, deps) {
109
117
  if (item.action === "skip") {
110
- deps.print(`${OUTPUT_PREFIX}skipping ${item.repo}: ${item.reason ?? ""}`);
118
+ deps.print(`${OUTPUT_PREFIX}se omite ${item.repo}: ${item.reason ?? ""}`);
111
119
  return;
112
120
  }
113
121
  if (item.action === "run" || !item.version) {
114
122
  await runOne(item.repo, deps);
115
123
  return;
116
124
  }
117
- deps.print(`${OUTPUT_PREFIX}relaunching with ${item.version} for ${item.repo}`);
125
+ deps.print(`${OUTPUT_PREFIX}relanzando con la versión ${item.version} para ${item.repo}`);
118
126
  try {
119
127
  await relaunch(item.repo, item.version, deps.run, deps.env);
120
128
  }
121
129
  catch (err) {
122
- deps.print(`${OUTPUT_PREFIX}relaunch with ${item.version} for ${item.repo} failed: ${message(err)}`);
130
+ deps.print(`${OUTPUT_PREFIX}falló el relanzamiento con la versión ${item.version} para ${item.repo}: ${message(err)}`);
123
131
  }
124
132
  }
125
133
  /**
@@ -136,25 +144,25 @@ async function setupCommand(args, deps) {
136
144
  if (at >= 0) {
137
145
  const value = args[at + 1];
138
146
  if (!value || value.startsWith("-"))
139
- deps.print(`${OUTPUT_PREFIX}${REPO_OPTION} needs a path`);
147
+ deps.print(`${OUTPUT_PREFIX}${REPO_OPTION} necesita una ruta`);
140
148
  else
141
149
  await runOne(resolve(deps.cwd, value), deps);
142
150
  return EXIT_OK;
143
151
  }
144
152
  const repos = findRepos(homeDir(deps.env));
145
153
  if (!repos.length)
146
- deps.print(`${OUTPUT_PREFIX}no repos found in ${homeDir(deps.env)}`);
154
+ deps.print(`${OUTPUT_PREFIX}no se encontraron repos en ${homeDir(deps.env)}`);
147
155
  for (const item of planSetup(repos, deps.packageVersion, readConfig)) {
148
156
  try {
149
157
  await runPlanItem(item, deps);
150
158
  }
151
159
  catch (err) {
152
- deps.print(`${OUTPUT_PREFIX}setup error for ${item.repo}: ${message(err)}`);
160
+ deps.print(`${OUTPUT_PREFIX}error en setup de ${item.repo}: ${message(err)}`);
153
161
  }
154
162
  }
155
163
  }
156
164
  catch (err) {
157
- deps.print(`${OUTPUT_PREFIX}setup error: ${message(err)}`);
165
+ deps.print(`${OUTPUT_PREFIX}error en setup: ${message(err)}`);
158
166
  }
159
167
  return EXIT_OK;
160
168
  }
@@ -198,7 +206,7 @@ async function sessionCommand(deps) {
198
206
  await deps.run(packageBin(tools, wanted), ["session"], { env: { ...deps.env, [DELEGATED_ENV]: DELEGATED_VALUE } });
199
207
  }
200
208
  catch (err) {
201
- deps.print(`${OUTPUT_PREFIX}session ${wanted} failed: ${message(err)}`);
209
+ deps.print(`${OUTPUT_PREFIX}falló session con la versión ${wanted}: ${message(err)}`);
202
210
  }
203
211
  return EXIT_OK;
204
212
  }
@@ -216,7 +224,7 @@ async function sessionCommand(deps) {
216
224
  async function dockerCommand(deps) {
217
225
  if (await deps.docker.ensure().catch(() => false))
218
226
  return EXIT_OK;
219
- deps.print(`${OUTPUT_PREFIX}dockerd did not start; see ${DOCKERD_LOG}`);
227
+ deps.print(`${OUTPUT_PREFIX}dockerd no arrancó; revisa ${DOCKERD_LOG}`);
220
228
  return EXIT_FAIL;
221
229
  }
222
230
  /**
@@ -269,7 +277,7 @@ function manualCommand(deps) {
269
277
  deps.print(`${OUTPUT_PREFIX}${message(err)}`);
270
278
  return EXIT_FAIL;
271
279
  }
272
- deps.print(`${OUTPUT_PREFIX}wrote ${MANUAL_FILE}`);
280
+ deps.print(`${OUTPUT_PREFIX}se escribió ${MANUAL_FILE}`);
273
281
  return EXIT_OK;
274
282
  }
275
283
  /**
@@ -282,7 +290,7 @@ function manualCommand(deps) {
282
290
  * @returns The raw answer.
283
291
  * @throws InterruptedError on Ctrl+C.
284
292
  * @example
285
- * await askTerminal("Include these changes in the commit? [y/N]", ""); // "y"
293
+ * await askTerminal("¿Incluir estos cambios en el commit? [s/N]", ""); // "y"
286
294
  */
287
295
  const askTerminal = async (question, suggestion) => {
288
296
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -319,7 +327,7 @@ function onProcessInterrupt(handler) {
319
327
  *
320
328
  * @param text - Text to show.
321
329
  * @example
322
- * await waitTerminal("Press Enter");
330
+ * await waitTerminal("Presiona Enter");
323
331
  */
324
332
  async function waitTerminal(text) {
325
333
  await askTerminal(text, "");
@@ -332,15 +340,45 @@ async function waitTerminal(text) {
332
340
  * @returns 0 when the cloud session was started, 130 after Ctrl+C, 1 otherwise.
333
341
  */
334
342
  async function prepareCommand(args, deps) {
335
- const unknown = args.filter((arg) => arg !== RESUME_OPTION);
343
+ const known = [RESUME_OPTION, PR_OPTION, HERE_OPTION];
344
+ const unknown = args.filter((arg) => !known.includes(arg));
336
345
  if (unknown.length) {
337
- deps.print(`${OUTPUT_PREFIX}unknown option for prepare: ${unknown.join(" ")}`);
346
+ deps.print(`${OUTPUT_PREFIX}opción desconocida para prepare: ${unknown.join(" ")}`);
338
347
  return EXIT_FAIL;
339
348
  }
340
- return prepare(deps.cwd, {
349
+ if (args.filter((arg) => known.includes(arg)).length > 1) {
350
+ deps.print(`${OUTPUT_PREFIX}usa solo una de ${known.join(", ")}`);
351
+ return EXIT_FAIL;
352
+ }
353
+ const place = args.includes(PR_OPTION) ? "pr" : args.includes(HERE_OPTION) ? "here" : undefined;
354
+ return prepare(deps.cwd, prepareDeps(deps), { resume: args.includes(RESUME_OPTION), place });
355
+ }
356
+ /**
357
+ * Runs `verify`: launches the cloud verification of the current branch.
358
+ *
359
+ * @param args - Arguments after `verify` (none allowed).
360
+ * @param deps - CLI dependencies.
361
+ * @returns 0 when the cloud session was started, 1 otherwise.
362
+ */
363
+ async function verifyCommand(args, deps) {
364
+ if (args.length) {
365
+ deps.print(`${OUTPUT_PREFIX}opción desconocida para verify: ${args.join(" ")}`);
366
+ return EXIT_FAIL;
367
+ }
368
+ return verifyBranch(deps.cwd, prepareDeps(deps));
369
+ }
370
+ /**
371
+ * Builds the dependencies of `prepare` and `verify` from the CLI ones, with terminal defaults.
372
+ *
373
+ * @param deps - CLI dependencies.
374
+ * @returns The dependencies.
375
+ */
376
+ function prepareDeps(deps) {
377
+ return {
341
378
  run: deps.run,
342
379
  capture: deps.capture ?? captureProcess,
343
380
  ask: deps.ask ?? askTerminal,
381
+ choose: deps.choose ?? chooseTerminal,
344
382
  waitEnter: deps.waitEnter ?? waitTerminal,
345
383
  print: deps.print,
346
384
  packageVersion: deps.packageVersion,
@@ -348,7 +386,7 @@ async function prepareCommand(args, deps) {
348
386
  today: deps.today ?? branchDay(new Date()),
349
387
  isTty: deps.isTty ?? (process.stdin.isTTY === true && process.stdout.isTTY === true),
350
388
  onInterrupt: deps.onInterrupt ?? onProcessInterrupt,
351
- }, { resume: args.includes(RESUME_OPTION) });
389
+ };
352
390
  }
353
391
  /**
354
392
  * Runs the CLI.
@@ -375,6 +413,8 @@ export async function main(argv, deps) {
375
413
  return manualCommand(deps);
376
414
  if (command === "prepare")
377
415
  return prepareCommand(rest, deps);
416
+ if (command === "verify")
417
+ return verifyCommand(rest, deps);
378
418
  if (command === "--version") {
379
419
  deps.print(deps.packageVersion);
380
420
  return EXIT_OK;
package/dist/config.js CHANGED
@@ -78,12 +78,12 @@ function parseDocker(value, errors) {
78
78
  if (value === undefined)
79
79
  return { pull: PULL_AUTO };
80
80
  if (!isRecord(value)) {
81
- errors.push(fail('"docker" must be an object'));
81
+ errors.push(fail('"docker" debe ser un objeto'));
82
82
  return { pull: PULL_AUTO };
83
83
  }
84
84
  for (const key of Object.keys(value)) {
85
85
  if (!DOCKER_KEYS.includes(key)) {
86
- errors.push(fail(`unknown field "docker.${key}"`));
86
+ errors.push(fail(`campo desconocido "docker.${key}"`));
87
87
  }
88
88
  }
89
89
  const pull = value.pull;
@@ -91,7 +91,7 @@ function parseDocker(value, errors) {
91
91
  return { pull: PULL_AUTO };
92
92
  if (pull === false || isStringList(pull))
93
93
  return { pull };
94
- errors.push(fail('"docker.pull" must be "auto", false or an array of strings'));
94
+ errors.push(fail('"docker.pull" debe ser "auto", false o un arreglo de strings'));
95
95
  return { pull: PULL_AUTO };
96
96
  }
97
97
  /**
@@ -108,7 +108,7 @@ function parseList(raw, key, errors) {
108
108
  return [];
109
109
  if (isStringList(value))
110
110
  return value;
111
- errors.push(fail(`"${key}" must be an array of strings`));
111
+ errors.push(fail(`"${key}" debe ser un arreglo de strings`));
112
112
  return [];
113
113
  }
114
114
  /**
@@ -124,13 +124,13 @@ function parseDomains(value, errors) {
124
124
  if (value === undefined)
125
125
  return [];
126
126
  if (!isStringList(value)) {
127
- errors.push(fail('"domains" must be an array of strings'));
127
+ errors.push(fail('"domains" debe ser un arreglo de strings'));
128
128
  return [];
129
129
  }
130
130
  return value.filter((host) => {
131
131
  const valid = isHostName(host);
132
132
  if (!valid)
133
- errors.push(fail(`"domains" has an invalid host: ${JSON.stringify(host)}`));
133
+ errors.push(fail(`"domains" tiene un host inválido: ${JSON.stringify(host)}`));
134
134
  return valid;
135
135
  });
136
136
  }
@@ -145,11 +145,11 @@ function parseConfig(raw, errors) {
145
145
  const known = [...CONFIG_KEYS, SCHEMA_KEY];
146
146
  for (const key of Object.keys(raw)) {
147
147
  if (!known.includes(key))
148
- errors.push(fail(`unknown field "${key}"`));
148
+ errors.push(fail(`campo desconocido "${key}"`));
149
149
  }
150
150
  const version = raw.version;
151
151
  if (typeof version !== "string" || !EXACT_SEMVER.test(version)) {
152
- errors.push(fail('"version" must be an exact semver string such as "1.0.0"'));
152
+ errors.push(fail('"version" debe ser un semver exacto, como "1.0.0"'));
153
153
  return undefined;
154
154
  }
155
155
  const lists = Object.fromEntries(LIST_KEYS.map((key) => [key, parseList(raw, key, errors)]));
@@ -166,7 +166,7 @@ function parseConfig(raw, errors) {
166
166
  if (typeof raw.notes === "string")
167
167
  config.notes = raw.notes;
168
168
  else if (raw.notes !== undefined)
169
- errors.push(fail('"notes" must be a string'));
169
+ errors.push(fail('"notes" debe ser un string'));
170
170
  return config;
171
171
  }
172
172
  /**
@@ -187,10 +187,10 @@ export function readConfig(repo) {
187
187
  }
188
188
  catch (error) {
189
189
  const reason = error instanceof Error ? error.message : String(error);
190
- return { errors: [fail(`cannot read file: ${reason}`)] };
190
+ return { errors: [fail(`no se pudo leer el archivo: ${reason}`)] };
191
191
  }
192
192
  if (!isRecord(raw))
193
- return { errors: [fail("must contain a JSON object")] };
193
+ return { errors: [fail("debe contener un objeto JSON")] };
194
194
  const errors = [];
195
195
  const config = parseConfig(raw, errors);
196
196
  return config ? { config, errors } : { errors };
package/dist/docker.js CHANGED
@@ -11,7 +11,7 @@ const POLL_MS = 1000;
11
11
  /** Milliseconds in a second. */
12
12
  const MS_PER_SECOND = 1000;
13
13
  /** Message returned when there is no `dockerd` executable on PATH. */
14
- export const DOCKERD_MISSING = "dockerd not installed";
14
+ export const DOCKERD_MISSING = "dockerd no está instalado";
15
15
  /**
16
16
  * Tells whether a command exits with code 0.
17
17
  *
package/dist/doctor.js CHANGED
@@ -49,10 +49,10 @@ async function checkDomain(host, capture) {
49
49
  try {
50
50
  const status = (await capture("curl", args)).trim();
51
51
  const ok = status !== BLOCKED_STATUS && status !== NO_ANSWER_STATUS;
52
- return { text: `network ${host}: ${ok ? "ok" : "blocked"}`, level: verdict(ok) };
52
+ return { text: `red ${host}: ${ok ? "ok" : "bloqueado"}`, level: verdict(ok) };
53
53
  }
54
54
  catch {
55
- return { text: `network ${host}: blocked`, level: "fail" };
55
+ return { text: `red ${host}: bloqueado`, level: "fail" };
56
56
  }
57
57
  }
58
58
  /**
@@ -82,12 +82,12 @@ function checkTools(repo, toolsDir, state) {
82
82
  if (!version)
83
83
  return [];
84
84
  if (!isValidRequest(tool, version))
85
- return [{ text: `tool ${tool} ${version}: invalid version`, level: "fail" }];
85
+ return [{ text: `herramienta ${tool} ${version}: versión inválida`, level: "fail" }];
86
86
  const executable = toolExecutable(tool, version, toolsDir, state?.resolved ?? {});
87
87
  if (!executable)
88
- return [{ text: `tool ${tool} ${version}: not resolved (setup did not install it)`, level: "fail" }];
88
+ return [{ text: `herramienta ${tool} ${version}: sin resolver (setup no la instaló)`, level: "fail" }];
89
89
  const present = existsSync(executable);
90
- return [{ text: `tool ${tool} ${version}: ${present ? "ok" : "missing"}`, level: verdict(present) }];
90
+ return [{ text: `herramienta ${tool} ${version}: ${present ? "ok" : "falta"}`, level: verdict(present) }];
91
91
  });
92
92
  }
93
93
  /**
@@ -99,19 +99,19 @@ function checkTools(repo, toolsDir, state) {
99
99
  */
100
100
  function checkState(repo, ctx) {
101
101
  if (!existsSync(join(repo, CONFIG_FILE)))
102
- return [{ text: `${CONFIG_FILE}: missing`, level: "fail" }];
102
+ return [{ text: `${CONFIG_FILE}: falta`, level: "fail" }];
103
103
  const { config, errors } = readConfig(repo);
104
104
  const out = errors.map((text) => ({ text: `${CONFIG_FILE}: ${text}`, level: "fail" }));
105
105
  if (!config)
106
106
  return out;
107
107
  const state = readState(ctx.toolsDir, repo);
108
108
  if (!state)
109
- return [...out, { text: "state: missing (setup did not run)", level: "fail" }];
109
+ return [...out, { text: "estado: falta (setup no corrió)", level: "fail" }];
110
110
  const same = expectedFingerprint(repo, config, state) === state.fingerprint;
111
- out.push({ text: `fingerprint: ${same ? "ok" : "mismatch"}`, level: verdict(same) });
111
+ out.push({ text: `huella: ${same ? "ok" : "distinta"}`, level: verdict(same) });
112
112
  for (const s of state.steps) {
113
113
  const seconds = (s.ms / MS_PER_SECOND).toFixed(DURATION_DECIMALS);
114
- out.push({ text: `step ${s.name}: ${s.ok ? "ok" : "failed"} (${seconds}s)`, level: verdict(s.ok) });
114
+ out.push({ text: `paso ${s.name}: ${s.ok ? "ok" : "falló"} (${seconds}s)`, level: verdict(s.ok) });
115
115
  }
116
116
  return out;
117
117
  }
@@ -122,7 +122,7 @@ function checkState(repo, ctx) {
122
122
  * @param repo - Repo root.
123
123
  * @returns One warning per enabled plugin.
124
124
  * @example
125
- * checkPlugins("/home/user/app"); // [{ text: "plugin x@m is enabled but ...", level: "warn" }]
125
+ * checkPlugins("/home/user/app"); // [{ text: "aviso: el plugin x@m está activado pero ...", level: "warn" }]
126
126
  */
127
127
  function checkPlugins(repo) {
128
128
  try {
@@ -133,7 +133,7 @@ function checkPlugins(repo) {
133
133
  return Object.entries(plugins)
134
134
  .filter(([, enabled]) => enabled === true)
135
135
  .map(([name]) => ({
136
- text: `warn: plugin ${name} is enabled but is not in the repo; it will not reach the cloud (copy its skills with skills-manager)`,
136
+ text: `aviso: el plugin ${name} está activado pero no está en el repo; no llegará a la nube (copia sus skills con skills-manager)`,
137
137
  level: "warn",
138
138
  }));
139
139
  }
@@ -148,7 +148,7 @@ function checkPlugins(repo) {
148
148
  * @param packageVersion - Version of this package.
149
149
  * @returns A warning, or nothing when there is no skill or the versions match.
150
150
  * @example
151
- * checkSkill("/home/user/app", "1.0.0"); // [{ text: "warn: .agents/skills/cloud-ready is 0.9.0, ...", level: "warn" }]
151
+ * checkSkill("/home/user/app", "1.0.0"); // [{ text: "aviso: .agents/skills/cloud-ready está en 0.9.0, ...", level: "warn" }]
152
152
  */
153
153
  function checkSkill(repo, packageVersion) {
154
154
  const installed = skillVersion(repo);
@@ -156,7 +156,7 @@ function checkSkill(repo, packageVersion) {
156
156
  return [];
157
157
  return [
158
158
  {
159
- text: `warn: ${SKILL_DIR} is ${installed}, the package is ${packageVersion}; run npx agent-cloud-kit@${packageVersion} init`,
159
+ text: `aviso: ${SKILL_DIR} está en ${installed} y el paquete en ${packageVersion}; corre npx agent-cloud-kit@${packageVersion} init`,
160
160
  level: "warn",
161
161
  },
162
162
  ];
@@ -187,13 +187,13 @@ async function guarded(name, fn) {
187
187
  */
188
188
  export async function doctor(repo, ctx) {
189
189
  const checks = [];
190
- checks.push(...(await guarded("network", async () => Promise.all(domainsToProbe(repo).map((host) => checkDomain(host, ctx.capture))))));
191
- checks.push(...(await guarded("tools", () => checkTools(repo, ctx.toolsDir, readState(ctx.toolsDir, repo)))));
190
+ checks.push(...(await guarded("red", async () => Promise.all(domainsToProbe(repo).map((host) => checkDomain(host, ctx.capture))))));
191
+ checks.push(...(await guarded("herramientas", () => checkTools(repo, ctx.toolsDir, readState(ctx.toolsDir, repo)))));
192
192
  checks.push(...(await guarded("docker", () => {
193
193
  const docker = ctx.dockerInfo();
194
- return [{ text: `docker: ${docker ? "ok" : "missing"}`, level: verdict(docker) }];
194
+ return [{ text: `docker: ${docker ? "ok" : "falta"}`, level: verdict(docker) }];
195
195
  })));
196
- checks.push(...(await guarded("state", () => checkState(repo, ctx))));
196
+ checks.push(...(await guarded("estado", () => checkState(repo, ctx))));
197
197
  checks.push(...(await guarded("plugins", () => checkPlugins(repo))));
198
198
  checks.push(...(await guarded("skill", () => checkSkill(repo, ctx.packageVersion))));
199
199
  return { lines: checks.map((c) => OUTPUT_PREFIX + c.text), ok: checks.every((c) => c.level !== "fail") };
package/dist/init.js CHANGED
@@ -15,7 +15,7 @@ const HOOK_TIMEOUT_SECONDS = 600;
15
15
  /** Indentation of the files written. */
16
16
  const JSON_INDENT = 2;
17
17
  /** Command of the SessionStart hook: does nothing outside cloud sessions. */
18
- const HOOK_COMMAND = "[ \"$CLAUDE_CODE_REMOTE\" = true ] || exit 0; /opt/agent-cloud-kit/bin/agent-cloud-kit session || echo 'agent-cloud-kit: the environment setup did not run; see npx agent-cloud-kit doctor'";
18
+ const HOOK_COMMAND = "[ \"$CLAUDE_CODE_REMOTE\" = true ] || exit 0; /opt/agent-cloud-kit/bin/agent-cloud-kit session || echo 'agent-cloud-kit: el setup del entorno no corrió; revisa con npx agent-cloud-kit doctor'";
19
19
  /** The hook `init` registers. */
20
20
  const HOOK = { type: "command", command: HOOK_COMMAND, timeout: HOOK_TIMEOUT_SECONDS };
21
21
  /**
@@ -36,7 +36,7 @@ function isRecord(value) {
36
36
  */
37
37
  function addByHand(reason) {
38
38
  const entry = JSON.stringify({ hooks: [HOOK] });
39
- return { line: `${SETTINGS_FILE} ${reason}, left untouched; add this entry to hooks.SessionStart by hand: ${entry}`, ok: false };
39
+ return { line: `${SETTINGS_FILE} ${reason}; no se tocó. Agrega esta entrada a hooks.SessionStart a mano: ${entry}`, ok: false };
40
40
  }
41
41
  /**
42
42
  * Writes a JSON file with a trailing newline, creating its folder.
@@ -70,7 +70,7 @@ function hasHook(entries) {
70
70
  function scaffoldConfig(repo, packageVersion) {
71
71
  const file = join(repo, CONFIG_FILE);
72
72
  if (existsSync(file))
73
- return `${CONFIG_FILE} already exists, left as is`;
73
+ return `${CONFIG_FILE} ya existe; se dejó como estaba`;
74
74
  writeJson(file, {
75
75
  $schema: `https://unpkg.com/${PACKAGE}@${packageVersion}/schema.json`,
76
76
  version: packageVersion,
@@ -82,7 +82,7 @@ function scaffoldConfig(repo, packageVersion) {
82
82
  requiredEnv: [],
83
83
  domains: [],
84
84
  });
85
- return `created ${CONFIG_FILE}`;
85
+ return `se creó ${CONFIG_FILE}`;
86
86
  }
87
87
  /**
88
88
  * Adds the SessionStart hook to `.claude/settings.json`, keeping everything else.
@@ -98,21 +98,21 @@ function registerHook(repo) {
98
98
  settings = JSON.parse(readFileSync(file, "utf8"));
99
99
  }
100
100
  catch {
101
- return addByHand("is not valid JSON");
101
+ return addByHand("no es JSON válido");
102
102
  }
103
103
  }
104
104
  if (!isRecord(settings))
105
- return addByHand("is not a JSON object");
105
+ return addByHand("no es un objeto JSON");
106
106
  const hooks = settings.hooks ?? {};
107
107
  if (!isRecord(hooks))
108
- return addByHand('has a "hooks" that is not an object');
108
+ return addByHand('tiene un "hooks" que no es un objeto');
109
109
  const entries = hooks.SessionStart ?? [];
110
110
  if (!Array.isArray(entries))
111
- return addByHand('has a "hooks.SessionStart" that is not an array');
111
+ return addByHand('tiene un "hooks.SessionStart" que no es un arreglo');
112
112
  if (hasHook(entries))
113
- return { line: `${SETTINGS_FILE} already has the SessionStart hook`, ok: true };
113
+ return { line: `${SETTINGS_FILE} ya tiene el hook SessionStart`, ok: true };
114
114
  writeJson(file, { ...settings, hooks: { ...hooks, SessionStart: [...entries, { hooks: [HOOK] }] } });
115
- return { line: `added the SessionStart hook to ${SETTINGS_FILE}`, ok: true };
115
+ return { line: `se agregó el hook SessionStart a ${SETTINGS_FILE}`, ok: true };
116
116
  }
117
117
  /**
118
118
  * Sets a repo up for agent-cloud-kit; safe to run again. It is the only place that copies the
@@ -123,7 +123,7 @@ async function nodeExpectedHash(run, env, dir, version, file) {
123
123
  await run("curl", ["-fsSL", "-o", path, "--", url], { env, timeoutMs: TOOL_INSTALL_TIMEOUT_MS });
124
124
  const hash = parseShasums(readFileSync(path, "utf8"), file);
125
125
  if (!hash)
126
- throw new Error(`${file} is not listed in ${NODE_SHASUMS_FILE}`);
126
+ throw new Error(`${file} no aparece en ${NODE_SHASUMS_FILE}`);
127
127
  return hash;
128
128
  }
129
129
  /**
@@ -138,7 +138,7 @@ async function nodeExpectedHash(run, env, dir, version, file) {
138
138
  export function nodeArch(arch) {
139
139
  const mapped = NODE_ARCHS[arch];
140
140
  if (!mapped)
141
- throw new Error(`unsupported architecture for node: ${arch}`);
141
+ throw new Error(`arquitectura no soportada para node: ${arch}`);
142
142
  return mapped;
143
143
  }
144
144
  /**
@@ -153,7 +153,7 @@ export function nodeArch(arch) {
153
153
  */
154
154
  export function assertVersion(tool, version) {
155
155
  if (!VERSION_PATTERN.test(version))
156
- throw new Error(`invalid version for ${tool}: ${version}`);
156
+ throw new Error(`versión inválida para ${tool}: ${version}`);
157
157
  }
158
158
  /**
159
159
  * Tells whether a version is safe to use.
@@ -254,7 +254,7 @@ export function curlNodeIndex(run, env) {
254
254
  await run("curl", ["-fsSL", "--max-time", NODE_INDEX_TIMEOUT_SECONDS, "-o", file, "--", NODE_INDEX_URL], { env });
255
255
  const parsed = JSON.parse(readFileSync(file, "utf8"));
256
256
  if (!Array.isArray(parsed))
257
- throw new Error("the node release index is not a list");
257
+ throw new Error("el índice de versiones de node no es una lista");
258
258
  return parsed.filter((r) => typeof r === "object" && r !== null && typeof r.version === "string" && (r.lts === false || typeof r.lts === "string"));
259
259
  }
260
260
  finally {
@@ -283,7 +283,7 @@ export async function resolveVersions(versions, fetchIndex) {
283
283
  const result = await step(`node ${requested}`, async () => {
284
284
  exact = resolveNodeVersion(requested, await fetchIndex());
285
285
  if (!exact)
286
- throw new Error(`no node release matches ${requested}`);
286
+ throw new Error(`ninguna versión de node coincide con ${requested}`);
287
287
  });
288
288
  if (!exact)
289
289
  return { resolved, results: [result] };
@@ -403,7 +403,7 @@ export async function installTools(versions, toolsDir, run, env) {
403
403
  }
404
404
  if (tool === "node" && !NODE_EXACT.test(version)) {
405
405
  results.push(await step(`${tool} ${version}`, async () => {
406
- throw new Error(`node ${version} must be resolved to an exact version first`);
406
+ throw new Error(`node ${version} debe resolverse primero a una versión exacta`);
407
407
  }));
408
408
  continue;
409
409
  }
package/dist/launcher.js CHANGED
@@ -58,7 +58,7 @@ export function planSetup(repos, current, read) {
58
58
  return repos.map((repo) => {
59
59
  const { config, errors } = read(repo);
60
60
  if (!config)
61
- return { repo, action: "skip", reason: errors.length ? errors.join("; ") : `no ${CONFIG_FILE}` };
61
+ return { repo, action: "skip", reason: errors.length ? errors.join("; ") : `no hay ${CONFIG_FILE}` };
62
62
  if (config.version === current)
63
63
  return { repo, action: "run", version: current };
64
64
  return { repo, action: "relaunch", version: config.version };