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

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.
@@ -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 };
package/dist/manual.js CHANGED
@@ -37,7 +37,7 @@ const HOW_IT_WORKS = [
37
37
  "4. Una sesión en la nube verifica que el entorno quedó bien.",
38
38
  "5. El PR guarda el resultado.",
39
39
  "",
40
- "Este documento se va actualizando. Cuando la skill `cloud-ready`, en local o en la nube, detecta que hace falta un dominio o una variable de entorno nueva, la agrega a `.claude/cloud.json`, regenera este archivo y te lo dice en la sesión. Cuando cambie la sección \"Configurar el entorno en claude.ai\", aplica lo nuevo en claude.ai y abre una sesión nueva.",
40
+ "Este documento se va actualizando. Cuando la skill `cloud-ready`, en local o en la nube, detecta que hace falta un dominio o una variable de entorno nueva, la agrega a `.claude/cloud.json`, regenera este archivo y te lo dice en la sesión. Cuando cambie la sección \"Configurar el entorno en claude.ai\", aplica lo nuevo en claude.ai y vuelve a verificar con `npx agent-cloud-kit verify` desde la rama.",
41
41
  ].join("\n");
42
42
  /**
43
43
  * Extracts the host of an apt repo URL.
@@ -205,7 +205,7 @@ export function mergeManual(existing, block) {
205
205
  export function writeManual(repo, packageVersion) {
206
206
  const { config, errors } = readConfig(repo);
207
207
  if (!config)
208
- throw new Error(errors[0] ?? `${CONFIG_FILE} not found; run init first`);
208
+ throw new Error(errors[0] ?? `no existe ${CONFIG_FILE}; corre init primero`);
209
209
  const file = join(repo, MANUAL_FILE);
210
210
  const existing = existsSync(file) ? readFileSync(file, "utf8") : undefined;
211
211
  const block = renderManual({ config, packageVersion, notes: resolveNotes(repo, config.notes) });
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @file The screen `prepare` shows before opening Claude Code: a small cloud, what will happen and
3
+ * what the user has to do, in a few colored lines.
4
+ */
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ /** Script of claude-repo-guard that opens Claude without sandbox nor permission prompts. */
8
+ export const FREE_SCRIPT = "claude:free";
9
+ /**
10
+ * Wraps a text in an ANSI code when color is on.
11
+ *
12
+ * @param on - Whether color is on.
13
+ * @param code - SGR code, like `36` for cyan.
14
+ * @param text - Text.
15
+ * @returns The text, colored or not.
16
+ * @example
17
+ * paint(false, "36", "a"); // "a"
18
+ */
19
+ function paint(on, code, text) {
20
+ return on ? `\x1b[${code}m${text}\x1b[0m` : text;
21
+ }
22
+ /**
23
+ * Renders the screen shown before opening Claude Code (without the Enter line, which the caller
24
+ * asks).
25
+ *
26
+ * @param input - Place, mode and color.
27
+ * @returns The lines.
28
+ * @example
29
+ * renderBanner({ place: "pr", free: false, color: false })[2]; // " .-( ). Ahora se abre Claude Code"
30
+ */
31
+ export function renderBanner(input) {
32
+ const c = (code, text) => paint(input.color, code, text);
33
+ const cloud = (text) => c("36", text);
34
+ const step = (n, text) => ` ${c("1;35", String(n))} ${text}`;
35
+ const after = input.place === "pr" ? "prepare hace commit, push y PR" : "quedan los cambios sin commit";
36
+ const lines = [
37
+ "",
38
+ cloud(" .--."),
39
+ `${cloud(" .-( ).")} ${c("1", "Ahora se abre Claude Code")}`,
40
+ `${cloud(" (___.__)__)")} ${c("2", "con la skill cloud-ready")}`,
41
+ "",
42
+ step(1, "Claude revisa el repo y te propone cómo prepararlo para la nube"),
43
+ step(2, "Responde sus preguntas"),
44
+ step(3, `Cuando diga ${c("1", "configure terminó")}, sal con ${c("1;33", "/exit")} ${c("2", `→ ${after}`)}`),
45
+ ];
46
+ if (input.free)
47
+ lines.push("", c("2", ` Se abre con ${FREE_SCRIPT} (sin sandbox ni preguntas de permisos): el repo usa claude-repo-guard`));
48
+ lines.push("");
49
+ return lines;
50
+ }
51
+ /**
52
+ * The line asked after the banner.
53
+ *
54
+ * @param color - Whether to use ANSI colors.
55
+ * @returns The text.
56
+ * @example
57
+ * enterLine(false); // " ⏎ Enter abre Claude Code · Ctrl+C cancela "
58
+ */
59
+ export function enterLine(color) {
60
+ return ` ${paint(color, "1;32", "⏎ Enter")} abre Claude Code ${paint(color, "2", "· Ctrl+C cancela")} `;
61
+ }
62
+ /** Config file claude-repo-guard installs; the free mode is used only when it is there. */
63
+ const GUARD_CONFIG = ".claude/repo-guard.config.json";
64
+ /**
65
+ * Tells whether the repo uses claude-repo-guard and has its `claude:free` script. Both are
66
+ * required: the free mode drops the sandbox and the permission prompts, so a script with that
67
+ * name alone is not enough.
68
+ *
69
+ * @param repo - Repo root.
70
+ * @returns True when the guard config exists and `package.json` has the script.
71
+ * @example
72
+ * hasFreeScript("/home/u/app"); // true
73
+ */
74
+ export function hasFreeScript(repo) {
75
+ const file = join(repo, "package.json");
76
+ if (!existsSync(file) || !existsSync(join(repo, GUARD_CONFIG)))
77
+ return false;
78
+ try {
79
+ const scripts = JSON.parse(readFileSync(file, "utf8")).scripts;
80
+ return typeof scripts?.[FREE_SCRIPT] === "string";
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * The command that opens Claude with a prompt: the repo's `claude:free` script through its package
88
+ * manager, or `claude` itself.
89
+ *
90
+ * @param repo - Repo root.
91
+ * @param prompt - First prompt.
92
+ * @returns Command and arguments.
93
+ * @example
94
+ * claudeCommand("/home/u/app", "/cloud-ready configure"); // { cmd: "npm", args: ["run", "claude:free", "--", "/cloud-ready configure"] }
95
+ */
96
+ export function claudeCommand(repo, prompt) {
97
+ if (!hasFreeScript(repo))
98
+ return { cmd: "claude", args: [prompt] };
99
+ const manager = packageManager(repo);
100
+ if (manager === "npm")
101
+ return { cmd: "npm", args: ["run", FREE_SCRIPT, "--", prompt] };
102
+ return { cmd: manager, args: ["run", FREE_SCRIPT, prompt] };
103
+ }
104
+ /** Lockfiles that tell the package manager, checked in order. */
105
+ const LOCKFILES = [
106
+ ["pnpm-lock.yaml", "pnpm"],
107
+ ["bun.lock", "bun"],
108
+ ["bun.lockb", "bun"],
109
+ ["yarn.lock", "yarn"],
110
+ ];
111
+ /**
112
+ * The repo's package manager: `packageManager` in `package.json`, else the lockfile, else npm.
113
+ *
114
+ * @param repo - Repo root.
115
+ * @returns `npm`, `pnpm`, `yarn` or `bun`.
116
+ * @example
117
+ * packageManager("/home/u/app"); // "pnpm"
118
+ */
119
+ function packageManager(repo) {
120
+ try {
121
+ const field = JSON.parse(readFileSync(join(repo, "package.json"), "utf8")).packageManager;
122
+ const name = typeof field === "string" ? field.split("@")[0] : undefined;
123
+ if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun")
124
+ return name;
125
+ }
126
+ catch {
127
+ // NOTE: no readable package.json; fall back to the lockfile.
128
+ }
129
+ return LOCKFILES.find(([file]) => existsSync(join(repo, file)))?.[1] ?? "npm";
130
+ }
@@ -4,26 +4,25 @@
4
4
  /** Flag `claude` must advertise in its help to launch cloud sessions. */
5
5
  const CLOUD_FLAG = "--cloud";
6
6
  /**
7
- * Checks that `prepare` can run.
7
+ * Checks that `prepare` (or `verify`) can run.
8
8
  *
9
9
  * @param probe What was observed about the environment.
10
- * @returns Problems in English, in a fixed order; empty when everything is fine.
10
+ * @param command Command named in the messages.
11
+ * @returns Problems in Spanish, in a fixed order; empty when everything is fine.
11
12
  * @example
12
- * preflight({ isTty: false, hasOrigin: true, clean: true }); // ["prepare needs an interactive terminal", ...]
13
+ * preflight({ isTty: false, hasOrigin: true }); // ["prepare necesita una terminal interactiva", ...]
13
14
  */
14
- export function preflight(probe) {
15
+ export function preflight(probe, command = "prepare") {
15
16
  const problems = [];
16
17
  if (!probe.isTty)
17
- problems.push("prepare needs an interactive terminal");
18
+ problems.push(`${command} necesita una terminal interactiva (corre el comando directamente en tu terminal)`);
18
19
  if (probe.gitRoot === undefined)
19
- problems.push("not inside a git repository");
20
+ problems.push(`esto no es un repositorio git; corre ${command} dentro del repo`);
20
21
  if (!probe.hasOrigin)
21
- problems.push("the repository has no 'origin' remote");
22
- if (!probe.clean)
23
- problems.push("the working tree has uncommitted changes; commit or stash them first");
22
+ problems.push(`el repositorio no tiene el remoto 'origin'; ${command} necesita subir una rama a GitHub`);
24
23
  if (probe.claudeHelp === undefined)
25
- problems.push("claude is not installed or not on the PATH");
24
+ problems.push(`claude no está instalado o no está en el PATH; instala Claude Code y vuelve a correr ${command}`);
26
25
  else if (!probe.claudeHelp.includes(CLOUD_FLAG))
27
- problems.push("claude does not support --cloud; update Claude Code");
26
+ problems.push(`tu versión de claude no soporta --cloud; actualiza Claude Code y vuelve a correr ${command}`);
28
27
  return problems;
29
28
  }