agent-cloud-kit 1.0.0-rc.7 → 1.0.0-rc.8
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 -0
- package/dist/block.js +76 -0
- package/dist/cli.js +67 -45
- package/dist/config.js +12 -11
- package/dist/docker.js +5 -6
- package/dist/doctor.js +39 -28
- package/dist/init.js +13 -12
- package/dist/installers.js +7 -6
- package/dist/lang.js +84 -0
- package/dist/launcher.js +2 -1
- package/dist/manual.js +13 -10
- package/dist/messages/en.js +226 -0
- package/dist/messages/es.js +234 -0
- package/dist/messages/index.js +75 -0
- package/dist/prepare/banner.js +18 -18
- package/dist/prepare/detect.js +8 -6
- package/dist/prepare/index.js +101 -61
- package/dist/prepare/ui.js +0 -27
- package/dist/run.js +3 -2
- package/dist/select.js +3 -2
- package/dist/session.js +11 -14
- package/dist/setup.js +10 -9
- package/dist/skill.js +7 -6
- package/dist/steps.js +2 -3
- package/package.json +1 -1
package/dist/prepare/index.js
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
import { existsSync, lstatSync, statSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
8
8
|
import { readConfig } from "../config.js";
|
|
9
|
-
import { CONFIG_FILE
|
|
9
|
+
import { CONFIG_FILE } from "../constants.js";
|
|
10
10
|
import { defaultBranch } from "../detect.js";
|
|
11
11
|
import { init } from "../init.js";
|
|
12
|
+
import { renderBlock } from "../block.js";
|
|
12
13
|
import { MANUAL_FILE, renderEnvironmentSection, writeManual } from "../manual.js";
|
|
14
|
+
import { t } from "../messages/index.js";
|
|
13
15
|
import { claudeCommand, continueLine, enterLine, hasFreeScript, renderBanner } from "./banner.js";
|
|
14
16
|
import { preflight } from "./detect.js";
|
|
15
|
-
import { formatLine,
|
|
17
|
+
import { formatLine, nextSteps, styleMarkdown } from "./ui.js";
|
|
16
18
|
import { configurePrompt, verifyPrompt } from "./prompts.js";
|
|
17
19
|
/** Prefix of the branch `prepare` creates; the day follows it. */
|
|
18
20
|
const BRANCH_PREFIX = "chore/cloud-ready-";
|
|
@@ -30,23 +32,14 @@ const KNOWN_PATHS = [
|
|
|
30
32
|
];
|
|
31
33
|
/** Project file of the test-audit skill, committed too when it exists. */
|
|
32
34
|
const TEST_AUDIT_PROJECT_FILE = ".agents/skills/test-audit/project.md";
|
|
33
|
-
/** Message of the commit `prepare` makes. */
|
|
35
|
+
/** Message of the commit `prepare` makes: it lives in the repo, so it is always in Spanish. */
|
|
34
36
|
const COMMIT_MESSAGE = "chore: prepara el repo para la nube de Claude Code";
|
|
35
|
-
/**
|
|
36
|
-
const INCLUDE_QUESTION = "¿Incluir estos cambios en el commit? [s/N]";
|
|
37
|
-
/** Answers to `INCLUDE_QUESTION` that mean yes. */
|
|
37
|
+
/** Answers to the "include these changes?" question that mean yes, in either language. */
|
|
38
38
|
const YES_ANSWER = /^(s|si|sí|y|yes)$/i;
|
|
39
|
-
/** Text shown while the user configures the environment in claude.ai. */
|
|
40
39
|
/** Matches a GitHub remote URL (https or ssh) and captures `owner/repo`. */
|
|
41
40
|
const GITHUB_REMOTE = /github\.com[:/]([^/\s]+\/[^/\s]+?)(?:\.git)?\/?$/;
|
|
42
41
|
/** Matches a pull request URL and captures its number. */
|
|
43
42
|
const PR_URL = /\/pull\/(\d+)/;
|
|
44
|
-
/** Command that continues a prepare run; `npx` because a first-time user does not have the binary on PATH. */
|
|
45
|
-
const RESUME_COMMAND = `npx ${PACKAGE} prepare --resume`;
|
|
46
|
-
/** Question of the place menu. */
|
|
47
|
-
const PLACE_QUESTION = "¿Dónde preparo el repo?";
|
|
48
|
-
/** Command that launches the cloud verification of the current branch. */
|
|
49
|
-
const VERIFY_COMMAND = `npx ${PACKAGE} verify`;
|
|
50
43
|
/** Exit code for success. */
|
|
51
44
|
const EXIT_OK = 0;
|
|
52
45
|
/** Exit code for failure. */
|
|
@@ -59,7 +52,7 @@ const PR_NUMBER = /^\d+$/;
|
|
|
59
52
|
export class InterruptedError extends Error {
|
|
60
53
|
/** Builds the error. */
|
|
61
54
|
constructor() {
|
|
62
|
-
super(
|
|
55
|
+
super(t().prepare.interrupted);
|
|
63
56
|
this.name = "InterruptedError";
|
|
64
57
|
}
|
|
65
58
|
}
|
|
@@ -87,6 +80,23 @@ export function branchDay(date) {
|
|
|
87
80
|
function say(deps, kind, text) {
|
|
88
81
|
deps.print(formatLine(kind, text, deps.color ?? false));
|
|
89
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Prints a final message: a block with Kuro in a terminal, else the plain prefixed lines.
|
|
85
|
+
*
|
|
86
|
+
* @param deps - Dependencies (`print`, `art`, `color`).
|
|
87
|
+
* @param block - What to say.
|
|
88
|
+
* @param plain - Lines to print without a terminal, without prefix.
|
|
89
|
+
* @example
|
|
90
|
+
* finish(deps, { mood: "ok", title: "Listo" }, ["listo"]);
|
|
91
|
+
*/
|
|
92
|
+
function finish(deps, block, plain) {
|
|
93
|
+
if (deps.art)
|
|
94
|
+
for (const line of renderBlock(block, { art: true, color: deps.color ?? false }))
|
|
95
|
+
deps.print(line);
|
|
96
|
+
else
|
|
97
|
+
for (const line of plain)
|
|
98
|
+
say(deps, block.mood === "error" ? "fail" : "ok", line);
|
|
99
|
+
}
|
|
90
100
|
/**
|
|
91
101
|
* Says where the worktree is and how to continue there.
|
|
92
102
|
*
|
|
@@ -97,7 +107,7 @@ function say(deps, kind, text) {
|
|
|
97
107
|
* resumeLine("interrumpido", "/home/u/app-cloud-ready"); // "interrumpido; el worktree está en ...; corre ... --resume ahí para continuar"
|
|
98
108
|
*/
|
|
99
109
|
function resumeLine(reason, worktree) {
|
|
100
|
-
return
|
|
110
|
+
return t().prepare.resume(reason, worktree);
|
|
101
111
|
}
|
|
102
112
|
/**
|
|
103
113
|
* Runs a capture and trims its output; never throws.
|
|
@@ -168,7 +178,7 @@ function worktreePath(repo) {
|
|
|
168
178
|
function configureProblem(worktree) {
|
|
169
179
|
const { config, errors } = readConfig(worktree);
|
|
170
180
|
if (!config || errors.length)
|
|
171
|
-
return errors[0] ??
|
|
181
|
+
return errors[0] ?? t().prepare.noConfig(CONFIG_FILE);
|
|
172
182
|
const notes = config.notes ?? DEFAULT_NOTES_FILE;
|
|
173
183
|
const rel = relative(worktree, join(worktree, notes));
|
|
174
184
|
const inside = !isAbsolute(notes) && rel !== "" && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
@@ -179,7 +189,7 @@ function configureProblem(worktree) {
|
|
|
179
189
|
catch {
|
|
180
190
|
// NOTE: a missing file is reported below.
|
|
181
191
|
}
|
|
182
|
-
return
|
|
192
|
+
return t().prepare.noNotes(notes);
|
|
183
193
|
}
|
|
184
194
|
/**
|
|
185
195
|
* Lists the paths of `git status --porcelain=v1 -z` output.
|
|
@@ -262,11 +272,11 @@ async function findResume(repo, deps) {
|
|
|
262
272
|
*/
|
|
263
273
|
async function createAndConfigure(repo, branch, worktree, deps, progress) {
|
|
264
274
|
if (existsSync(worktree)) {
|
|
265
|
-
say(deps, "fail",
|
|
275
|
+
say(deps, "fail", t().prepare.worktreeExists(worktree));
|
|
266
276
|
return false;
|
|
267
277
|
}
|
|
268
278
|
if ((await tryCapture(deps, "git", ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], repo)) !== undefined) {
|
|
269
|
-
say(deps, "fail",
|
|
279
|
+
say(deps, "fail", t().prepare.branchExists(branch));
|
|
270
280
|
return false;
|
|
271
281
|
}
|
|
272
282
|
const base = `origin/${defaultBranch(repo)}`;
|
|
@@ -274,17 +284,17 @@ async function createAndConfigure(repo, branch, worktree, deps, progress) {
|
|
|
274
284
|
await deps.run("git", ["fetch", "-q", "origin"], { cwd: repo });
|
|
275
285
|
await deps.run("git", ["worktree", "add", "-b", branch, worktree, base], { cwd: repo });
|
|
276
286
|
progress.worktree = worktree;
|
|
277
|
-
say(deps, "ok",
|
|
287
|
+
say(deps, "ok", t().prepare.worktreeCreated(worktree, branch));
|
|
278
288
|
}
|
|
279
289
|
catch (err) {
|
|
280
|
-
say(deps, "fail",
|
|
290
|
+
say(deps, "fail", t().prepare.worktreeFailed(message(err)));
|
|
281
291
|
return false;
|
|
282
292
|
}
|
|
283
|
-
const stopped = resumeLine(
|
|
293
|
+
const stopped = resumeLine(t().prepare.agentStopped, worktree);
|
|
284
294
|
try {
|
|
285
295
|
const result = init(worktree, deps.packageVersion, deps.packageRoot);
|
|
286
|
-
for (const
|
|
287
|
-
say(deps,
|
|
296
|
+
for (const entry of result.entries)
|
|
297
|
+
say(deps, entry.kind, entry.text);
|
|
288
298
|
if (!result.ok) {
|
|
289
299
|
say(deps, "warn", stopped);
|
|
290
300
|
return false;
|
|
@@ -306,8 +316,8 @@ async function createAndConfigure(repo, branch, worktree, deps, progress) {
|
|
|
306
316
|
}
|
|
307
317
|
const problem = configureProblem(worktree);
|
|
308
318
|
if (problem !== undefined) {
|
|
309
|
-
say(deps, "fail",
|
|
310
|
-
say(deps, "warn", resumeLine(
|
|
319
|
+
say(deps, "fail", t().prepare.configureUnfinished(problem));
|
|
320
|
+
say(deps, "warn", resumeLine(t().prepare.prepareStopped, worktree));
|
|
311
321
|
return false;
|
|
312
322
|
}
|
|
313
323
|
return true;
|
|
@@ -334,45 +344,45 @@ async function commitAndPush(worktree, branch, deps) {
|
|
|
334
344
|
const status = await deps.capture("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], { cwd: worktree });
|
|
335
345
|
const others = statusPaths(status).filter((path) => !covered(path, known));
|
|
336
346
|
if (others.length) {
|
|
337
|
-
say(deps, "warn",
|
|
347
|
+
say(deps, "warn", t().prepare.otherChanges);
|
|
338
348
|
for (const path of others)
|
|
339
349
|
deps.print(` ${path}`);
|
|
340
|
-
if (YES_ANSWER.test((await deps.ask(
|
|
350
|
+
if (YES_ANSWER.test((await deps.ask(t().prepare.includeQuestion, "")).trim())) {
|
|
341
351
|
await deps.run("git", ["add", "--", ...others], { cwd: worktree });
|
|
342
352
|
}
|
|
343
353
|
else {
|
|
344
354
|
await deps.run("git", ["reset", "-q", "--", ...others], { cwd: worktree });
|
|
345
|
-
say(deps, "warn",
|
|
355
|
+
say(deps, "warn", t().prepare.leftUncommitted(worktree));
|
|
346
356
|
}
|
|
347
357
|
}
|
|
348
358
|
const staged = await deps.capture("git", ["diff", "--cached", "--name-only"], { cwd: worktree });
|
|
349
359
|
if (staged.trim())
|
|
350
360
|
await deps.run("git", ["commit", "-m", COMMIT_MESSAGE], { cwd: worktree });
|
|
351
361
|
else
|
|
352
|
-
say(deps, "same",
|
|
362
|
+
say(deps, "same", t().prepare.nothingToCommit);
|
|
353
363
|
await deps.run("git", ["push", "-u", "origin", branch], { cwd: worktree });
|
|
354
|
-
say(deps, "ok",
|
|
364
|
+
say(deps, "ok", t().prepare.pushed(branch));
|
|
355
365
|
}
|
|
356
366
|
catch (err) {
|
|
357
367
|
if (err instanceof InterruptedError)
|
|
358
368
|
throw err;
|
|
359
|
-
say(deps, "fail",
|
|
369
|
+
say(deps, "fail", t().prepare.pushFailed(message(err), worktree));
|
|
360
370
|
return false;
|
|
361
371
|
}
|
|
362
372
|
const existing = await tryCapture(deps, "gh", ["pr", "view", branch, "--json", "number", "--jq", ".number"], worktree);
|
|
363
373
|
if (existing !== undefined && PR_NUMBER.test(existing)) {
|
|
364
|
-
say(deps, "ok",
|
|
374
|
+
say(deps, "ok", t().prepare.prReused(existing));
|
|
365
375
|
return { pr: existing };
|
|
366
376
|
}
|
|
367
377
|
try {
|
|
368
378
|
const url = await deps.capture("gh", ["pr", "create", "--draft", "--fill"], { cwd: worktree });
|
|
369
|
-
say(deps, "ok",
|
|
379
|
+
say(deps, "ok", t().prepare.prDraft(url.trim()));
|
|
370
380
|
return { pr: PR_URL.exec(url)?.[1] };
|
|
371
381
|
}
|
|
372
382
|
catch {
|
|
373
383
|
const origin = await tryCapture(deps, "git", ["remote", "get-url", "origin"], worktree);
|
|
374
384
|
const link = compareLink(origin, branch);
|
|
375
|
-
say(deps, "warn",
|
|
385
|
+
say(deps, "warn", t().prepare.prByHand(link, branch));
|
|
376
386
|
return {};
|
|
377
387
|
}
|
|
378
388
|
}
|
|
@@ -472,8 +482,8 @@ async function runConfigure(deps, progress, place, cwd) {
|
|
|
472
482
|
*/
|
|
473
483
|
function interruptedLine(progress) {
|
|
474
484
|
if (progress.here)
|
|
475
|
-
return
|
|
476
|
-
return progress.worktree === undefined ?
|
|
485
|
+
return t().prepare.interruptedHere;
|
|
486
|
+
return progress.worktree === undefined ? t().prepare.interruptedNothing : resumeLine(t().prepare.interrupted, progress.worktree);
|
|
477
487
|
}
|
|
478
488
|
/**
|
|
479
489
|
* Everything after the preflight: resume or create the worktree, commit, wait, launch.
|
|
@@ -493,21 +503,21 @@ async function prepareSteps(gitRoot, deps, options, progress) {
|
|
|
493
503
|
if (options.resume) {
|
|
494
504
|
const found = await findResume(gitRoot, deps);
|
|
495
505
|
if (!found) {
|
|
496
|
-
say(deps, "fail",
|
|
506
|
+
say(deps, "fail", t().prepare.nothingToResume(WORKTREE_SUFFIX, BRANCH_PREFIX));
|
|
497
507
|
return EXIT_FAIL;
|
|
498
508
|
}
|
|
499
509
|
({ worktree, branch } = found);
|
|
500
510
|
progress.worktree = worktree;
|
|
501
511
|
const problem = configureProblem(worktree);
|
|
502
512
|
if (problem) {
|
|
503
|
-
say(deps, "fail",
|
|
513
|
+
say(deps, "fail", t().prepare.resumeUnfinished(worktree, problem));
|
|
504
514
|
return EXIT_FAIL;
|
|
505
515
|
}
|
|
506
516
|
}
|
|
507
517
|
else {
|
|
508
518
|
const place = options.place ?? (await askPlace(gitRoot, deps));
|
|
509
519
|
if (place === null) {
|
|
510
|
-
say(deps, "same",
|
|
520
|
+
say(deps, "same", t().prepare.nothingChanged);
|
|
511
521
|
return EXIT_FAIL;
|
|
512
522
|
}
|
|
513
523
|
if (place === "here")
|
|
@@ -522,17 +532,17 @@ async function prepareSteps(gitRoot, deps, options, progress) {
|
|
|
522
532
|
return EXIT_FAIL;
|
|
523
533
|
const config = readConfig(worktree).config;
|
|
524
534
|
if (config)
|
|
525
|
-
for (const line of styleMarkdown(renderEnvironmentSection(config, deps.packageVersion), deps.color ?? false))
|
|
535
|
+
for (const line of styleMarkdown(renderEnvironmentSection(config, deps.packageVersion, t().environment), deps.color ?? false))
|
|
526
536
|
deps.print(line);
|
|
527
537
|
await deps.waitEnter(continueLine(deps.color ?? false));
|
|
528
538
|
try {
|
|
529
539
|
await runInteractive(deps, progress, { cmd: "claude", args: ["--cloud", verifyPrompt({ branch, pr: pushed.pr })] }, worktree);
|
|
530
540
|
}
|
|
531
541
|
catch (err) {
|
|
532
|
-
say(deps, "fail",
|
|
542
|
+
say(deps, "fail", t().prepare.cloudFailedResume(message(err), worktree));
|
|
533
543
|
return EXIT_FAIL;
|
|
534
544
|
}
|
|
535
|
-
|
|
545
|
+
cloudStarted(deps, branch, pushed.pr, true);
|
|
536
546
|
return EXIT_OK;
|
|
537
547
|
}
|
|
538
548
|
/**
|
|
@@ -545,9 +555,9 @@ async function prepareSteps(gitRoot, deps, options, progress) {
|
|
|
545
555
|
* await askPlace("/home/u/app", deps); // "pr"
|
|
546
556
|
*/
|
|
547
557
|
async function askPlace(gitRoot, deps) {
|
|
548
|
-
return deps.choose(
|
|
549
|
-
{ value: "pr", label:
|
|
550
|
-
{ value: "here", label:
|
|
558
|
+
return deps.choose(t().prepare.placeQuestion, [
|
|
559
|
+
{ value: "pr", label: t().prepare.placePr, hint: t().prepare.placePrHint(defaultBranch(gitRoot)) },
|
|
560
|
+
{ value: "here", label: t().prepare.placeHere, hint: t().prepare.placeHereHint },
|
|
551
561
|
], "pr");
|
|
552
562
|
}
|
|
553
563
|
/**
|
|
@@ -563,11 +573,11 @@ async function askPlace(gitRoot, deps) {
|
|
|
563
573
|
*/
|
|
564
574
|
async function prepareHere(gitRoot, deps, progress) {
|
|
565
575
|
progress.here = true;
|
|
566
|
-
const stopped =
|
|
576
|
+
const stopped = t().prepare.stoppedHere;
|
|
567
577
|
try {
|
|
568
578
|
const result = init(gitRoot, deps.packageVersion, deps.packageRoot);
|
|
569
|
-
for (const
|
|
570
|
-
say(deps,
|
|
579
|
+
for (const entry of result.entries)
|
|
580
|
+
say(deps, entry.kind, entry.text);
|
|
571
581
|
if (!result.ok) {
|
|
572
582
|
say(deps, "warn", stopped);
|
|
573
583
|
return EXIT_FAIL;
|
|
@@ -589,23 +599,26 @@ async function prepareHere(gitRoot, deps, progress) {
|
|
|
589
599
|
}
|
|
590
600
|
const problem = configureProblem(gitRoot);
|
|
591
601
|
if (problem !== undefined) {
|
|
592
|
-
say(deps, "fail",
|
|
602
|
+
say(deps, "fail", t().prepare.hereUnfinished(problem));
|
|
593
603
|
return EXIT_FAIL;
|
|
594
604
|
}
|
|
595
605
|
try {
|
|
596
606
|
writeManual(gitRoot, deps.packageVersion);
|
|
597
607
|
}
|
|
598
608
|
catch (err) {
|
|
599
|
-
say(deps, "fail",
|
|
609
|
+
say(deps, "fail", t().prepare.manualFailed(MANUAL_FILE, message(err)));
|
|
600
610
|
return EXIT_FAIL;
|
|
601
611
|
}
|
|
602
612
|
const config = readConfig(gitRoot).config;
|
|
603
613
|
if (config)
|
|
604
|
-
for (const line of styleMarkdown(renderEnvironmentSection(config, deps.packageVersion), deps.color ?? false))
|
|
614
|
+
for (const line of styleMarkdown(renderEnvironmentSection(config, deps.packageVersion, t().environment), deps.color ?? false))
|
|
615
|
+
deps.print(line);
|
|
616
|
+
const steps = t().prepare.hereSteps(MANUAL_FILE);
|
|
617
|
+
if (deps.art)
|
|
618
|
+
finish(deps, { mood: "ok", title: t().prepare.hereTitle, lines: numbered(steps, deps.color ?? false) }, []);
|
|
619
|
+
else
|
|
620
|
+
for (const line of nextSteps(t().prepare.hereTitle, steps, deps.color ?? false))
|
|
605
621
|
deps.print(line);
|
|
606
|
-
const steps = ["Revisa los cambios con git diff", "Haz commit y push", `Configura el entorno en claude.ai (${MANUAL_FILE})`, `Corre ${VERIFY_COMMAND}`];
|
|
607
|
-
for (const line of nextSteps("Listo, sin commit", steps, deps.color ?? false))
|
|
608
|
-
deps.print(line);
|
|
609
622
|
return EXIT_OK;
|
|
610
623
|
}
|
|
611
624
|
/**
|
|
@@ -631,25 +644,25 @@ export async function verifyBranch(repo, deps) {
|
|
|
631
644
|
}
|
|
632
645
|
const branch = await tryCapture(deps, "git", ["rev-parse", "--abbrev-ref", "HEAD"], gitRoot);
|
|
633
646
|
if (branch === undefined || branch === "HEAD") {
|
|
634
|
-
say(deps, "fail",
|
|
647
|
+
say(deps, "fail", t().prepare.noBranch);
|
|
635
648
|
return EXIT_FAIL;
|
|
636
649
|
}
|
|
637
650
|
const problem = configureProblem(gitRoot);
|
|
638
651
|
if (problem !== undefined) {
|
|
639
|
-
say(deps, "fail",
|
|
652
|
+
say(deps, "fail", t().prepare.notPrepared(problem));
|
|
640
653
|
return EXIT_FAIL;
|
|
641
654
|
}
|
|
642
655
|
const notes = readConfig(gitRoot).config?.notes ?? DEFAULT_NOTES_FILE;
|
|
643
656
|
const files = [CONFIG_FILE, MANUAL_FILE, notes].filter((path) => entryExists(join(gitRoot, path)));
|
|
644
657
|
const dirty = await tryCapture(deps, "git", ["status", "--porcelain", "--", ...files], gitRoot);
|
|
645
658
|
if (dirty) {
|
|
646
|
-
say(deps, "fail",
|
|
659
|
+
say(deps, "fail", t().prepare.dirty);
|
|
647
660
|
return EXIT_FAIL;
|
|
648
661
|
}
|
|
649
662
|
const head = await tryCapture(deps, "git", ["rev-parse", "HEAD"], gitRoot);
|
|
650
663
|
const remote = (await tryCapture(deps, "git", ["ls-remote", "origin", `refs/heads/${branch}`], gitRoot))?.split(/\s/)[0];
|
|
651
664
|
if (!head || remote !== head) {
|
|
652
|
-
say(deps, "fail",
|
|
665
|
+
say(deps, "fail", t().prepare.notPushed(branch));
|
|
653
666
|
return EXIT_FAIL;
|
|
654
667
|
}
|
|
655
668
|
const found = await tryCapture(deps, "gh", ["pr", "view", branch, "--json", "number", "--jq", ".number"], gitRoot);
|
|
@@ -660,12 +673,39 @@ export async function verifyBranch(repo, deps) {
|
|
|
660
673
|
await runInteractive(deps, progress, { cmd: "claude", args: ["--cloud", verifyPrompt({ branch, pr })] }, gitRoot);
|
|
661
674
|
}
|
|
662
675
|
catch (err) {
|
|
663
|
-
say(deps, "fail",
|
|
676
|
+
say(deps, "fail", t().prepare.cloudFailed(message(err)));
|
|
664
677
|
return EXIT_FAIL;
|
|
665
678
|
}
|
|
666
679
|
finally {
|
|
667
680
|
stop?.();
|
|
668
681
|
}
|
|
669
|
-
|
|
682
|
+
cloudStarted(deps, branch, pr, pr !== undefined);
|
|
670
683
|
return EXIT_OK;
|
|
671
684
|
}
|
|
685
|
+
/**
|
|
686
|
+
* Numbers some steps for a block, with the number in color.
|
|
687
|
+
*
|
|
688
|
+
* @param steps - Steps, in order.
|
|
689
|
+
* @param color - Whether to color.
|
|
690
|
+
* @returns The lines.
|
|
691
|
+
* @example
|
|
692
|
+
* numbered(["a"], false); // ["1 a"]
|
|
693
|
+
*/
|
|
694
|
+
function numbered(steps, color) {
|
|
695
|
+
return steps.map((step, i) => `${color ? `\x1b[1;35m${i + 1}\x1b[0m` : i + 1} ${step}`);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* The final message once the cloud session started: the branch, the PR and how it will report.
|
|
699
|
+
*
|
|
700
|
+
* @param deps - Dependencies.
|
|
701
|
+
* @param branch - Branch the session verifies.
|
|
702
|
+
* @param pr - PR number, when known.
|
|
703
|
+
* @param comment - Whether the session will comment on a PR.
|
|
704
|
+
* @example
|
|
705
|
+
* cloudStarted(deps, "main", "7", true);
|
|
706
|
+
*/
|
|
707
|
+
function cloudStarted(deps, branch, pr, comment) {
|
|
708
|
+
const m = t().prepare;
|
|
709
|
+
const rows = [[m.rowBranch, branch], ...(pr ? [[m.rowPr, `#${pr}`]] : [])];
|
|
710
|
+
finish(deps, { mood: "ok", title: m.cloudStarted, rows, lines: [m.cloudWillReport(branch, comment)] }, [m.cloudWillReport(branch, comment)]);
|
|
711
|
+
}
|
package/dist/prepare/ui.js
CHANGED
|
@@ -40,33 +40,6 @@ export function formatLine(kind, text, color) {
|
|
|
40
40
|
const { icon, code } = STYLE[kind];
|
|
41
41
|
return ` ${paint(true, code, icon)} ${kind === "same" ? paint(true, "2", text) : text}`;
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Guesses the kind of a line `init` printed, from its wording.
|
|
45
|
-
*
|
|
46
|
-
* @param text - Line, with or without the prefix.
|
|
47
|
-
* @returns `ok` for something done, `same` for something already there, `warn` otherwise.
|
|
48
|
-
* @example
|
|
49
|
-
* initKind("agent-cloud-kit: se creó .claude/cloud.json"); // "ok"
|
|
50
|
-
*/
|
|
51
|
-
export function initKind(text) {
|
|
52
|
-
const plain = text.startsWith(OUTPUT_PREFIX) ? text.slice(OUTPUT_PREFIX.length) : text;
|
|
53
|
-
if (/^se (creó|agregó|instaló|enlazó|actualizó|escribió)(\s|$)/.test(plain))
|
|
54
|
-
return "ok";
|
|
55
|
-
if (/\bya (existe|tiene|está)\b/.test(plain))
|
|
56
|
-
return "same";
|
|
57
|
-
return "warn";
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Removes the `agent-cloud-kit: ` prefix of a line, if it has it.
|
|
61
|
-
*
|
|
62
|
-
* @param text - Line.
|
|
63
|
-
* @returns The line without prefix.
|
|
64
|
-
* @example
|
|
65
|
-
* unprefixed("agent-cloud-kit: hola"); // "hola"
|
|
66
|
-
*/
|
|
67
|
-
export function unprefixed(text) {
|
|
68
|
-
return text.startsWith(OUTPUT_PREFIX) ? text.slice(OUTPUT_PREFIX.length) : text;
|
|
69
|
-
}
|
|
70
43
|
/**
|
|
71
44
|
* Styles a Markdown section for the terminal: headings in bold (and cyan for `##`), code fences
|
|
72
45
|
* dropped and their lines indented, list items as bullets. Without color, the text is unchanged.
|
package/dist/run.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Modules that execute processes receive a `Run` (or a `Capture`) so tests can inject a fake.
|
|
4
4
|
*/
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { t } from "./messages/index.js";
|
|
6
7
|
/** Signal sent to a process group that ran past its time limit. */
|
|
7
8
|
const KILL_SIGNAL = "SIGKILL";
|
|
8
9
|
/** File descriptor of the parent's stderr, reused as the child's stdout and stderr. */
|
|
@@ -16,7 +17,7 @@ export class RunTimeoutError extends Error {
|
|
|
16
17
|
* @param ms - Time limit, in milliseconds.
|
|
17
18
|
*/
|
|
18
19
|
constructor(cmd, ms) {
|
|
19
|
-
super(
|
|
20
|
+
super(t().run.timedOut(cmd, ms));
|
|
20
21
|
this.name = "RunTimeoutError";
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -82,7 +83,7 @@ function spawnAndWait(cmd, args, opts, stdio, onStdout) {
|
|
|
82
83
|
clearTimeout(timer);
|
|
83
84
|
if (code === 0)
|
|
84
85
|
return resolve();
|
|
85
|
-
reject(new Error(
|
|
86
|
+
reject(new Error(code === null ? t().run.exitSignal(cmd, String(signal)) : t().run.exitCode(cmd, code)));
|
|
86
87
|
});
|
|
87
88
|
});
|
|
88
89
|
}
|
package/dist/select.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* and no dependencies: ↑/↓ move, Enter picks, Esc or Ctrl+C cancels.
|
|
4
4
|
*/
|
|
5
5
|
import { emitKeypressEvents } from "node:readline";
|
|
6
|
+
import { t } from "./messages/index.js";
|
|
6
7
|
/** ANSI codes used by the menu. */
|
|
7
8
|
const ANSI = {
|
|
8
9
|
dim: (text) => `\x1b[2m${text}\x1b[22m`,
|
|
@@ -38,7 +39,7 @@ export function menuKey(state, key, count) {
|
|
|
38
39
|
}
|
|
39
40
|
/**
|
|
40
41
|
* Draws the menu: the question, the options (hint only on the active one) and, once done, the
|
|
41
|
-
* picked option or "Cancelado".
|
|
42
|
+
* picked option or "Cancelado" (in the language of the terminal).
|
|
42
43
|
*
|
|
43
44
|
* @param message - Question.
|
|
44
45
|
* @param choices - Options.
|
|
@@ -52,7 +53,7 @@ export function renderMenu(message, choices, state) {
|
|
|
52
53
|
if (state.done === "picked")
|
|
53
54
|
return [`${ANSI.green("◇")} ${message}`, `${bar} ${ANSI.dim(choices[state.active]?.label ?? "")}`, bar];
|
|
54
55
|
if (state.done === "cancelled")
|
|
55
|
-
return [`${ANSI.dim("■")} ${message}`, `${bar} ${ANSI.dim(
|
|
56
|
+
return [`${ANSI.dim("■")} ${message}`, `${bar} ${ANSI.dim(t().menu.cancelled)}`, bar];
|
|
56
57
|
const lines = [`${ANSI.cyan("◆")} ${message}`];
|
|
57
58
|
choices.forEach((choice, index) => {
|
|
58
59
|
if (index === state.active) {
|
package/dist/session.js
CHANGED
|
@@ -12,10 +12,9 @@ import { realDocker } from "./docker.js";
|
|
|
12
12
|
import { pathLine, toolBinDirs } from "./installers.js";
|
|
13
13
|
import { linkDir } from "./launcher.js";
|
|
14
14
|
import { resolveNotes } from "./manual.js";
|
|
15
|
+
import { t } from "./messages/index.js";
|
|
15
16
|
import { RunTimeoutError } from "./run.js";
|
|
16
17
|
import { fingerprint, readState } from "./state.js";
|
|
17
|
-
/** Generic note shown in every cloud session. */
|
|
18
|
-
const GENERIC_NOTE = "estás en una sesión cloud de Claude Code. No hay puertos abiertos ni previews: prueba contra localhost. gh pr y gh issue fallan (el proxy de GitHub bloquea GraphQL): usa gh api (REST).";
|
|
19
18
|
/** Milliseconds in a second. */
|
|
20
19
|
const MS_PER_SECOND = 1000;
|
|
21
20
|
/** Seconds in a minute. */
|
|
@@ -96,20 +95,18 @@ export function expectedFingerprint(repo, config, state) {
|
|
|
96
95
|
*/
|
|
97
96
|
function machineWarnings(repo, config, state, packageVersion) {
|
|
98
97
|
if (!state) {
|
|
99
|
-
return [
|
|
100
|
-
`el setup del entorno no corrió para ${basename(repo)}. Pon como script de setup del entorno en claude.ai: npx -y ${packageSpec(packageVersion)} setup`,
|
|
101
|
-
];
|
|
98
|
+
return [t().session.setupNotRun(basename(repo), packageSpec(packageVersion))];
|
|
102
99
|
}
|
|
103
100
|
const out = [];
|
|
104
101
|
const failed = state.steps.filter((s) => !s.ok).map((s) => s.name);
|
|
105
102
|
if (failed.length)
|
|
106
|
-
out.push(
|
|
103
|
+
out.push(t().session.setupFailed(failed));
|
|
107
104
|
if (expectedFingerprint(repo, config, state) !== state.fingerprint) {
|
|
108
|
-
out.push(
|
|
105
|
+
out.push(t().session.otherConfig);
|
|
109
106
|
}
|
|
110
107
|
const total = state.steps.reduce((sum, s) => sum + s.ms, 0);
|
|
111
108
|
if (total > SETUP_SLOW_MS)
|
|
112
|
-
out.push(
|
|
109
|
+
out.push(t().session.slow(minutesSeconds(total)));
|
|
113
110
|
return out;
|
|
114
111
|
}
|
|
115
112
|
/**
|
|
@@ -129,7 +126,7 @@ function writeEnvFile(env, lines) {
|
|
|
129
126
|
return undefined;
|
|
130
127
|
}
|
|
131
128
|
catch (err) {
|
|
132
|
-
return
|
|
129
|
+
return t().session.envFileFailed(ENV_FILE_VAR, file, err instanceof Error ? err.message : String(err));
|
|
133
130
|
}
|
|
134
131
|
}
|
|
135
132
|
/**
|
|
@@ -170,7 +167,7 @@ async function runSessionSteps(repo, commands, run, env) {
|
|
|
170
167
|
await run("bash", ["-c", command], { cwd: repo, env, stdio: "stderr", timeoutMs: SESSION_STEP_TIMEOUT_MS });
|
|
171
168
|
}
|
|
172
169
|
catch (err) {
|
|
173
|
-
problems.push(
|
|
170
|
+
problems.push(err instanceof RunTimeoutError ? t().session.stepTimedOut(command) : t().session.stepFailed(command));
|
|
174
171
|
}
|
|
175
172
|
}
|
|
176
173
|
return problems;
|
|
@@ -197,7 +194,7 @@ function startDocker(docker) {
|
|
|
197
194
|
* @param out - Output lines, filled in order.
|
|
198
195
|
*/
|
|
199
196
|
async function sessionBody(repo, ctx, out) {
|
|
200
|
-
out.push(
|
|
197
|
+
out.push(t().session.genericNote);
|
|
201
198
|
if (!existsSync(join(repo, CONFIG_FILE)))
|
|
202
199
|
return;
|
|
203
200
|
const { config, errors } = readConfig(repo);
|
|
@@ -213,13 +210,13 @@ async function sessionBody(repo, ctx, out) {
|
|
|
213
210
|
out.push(...machineWarnings(repo, config, state, ctx.packageVersion));
|
|
214
211
|
const missing = (config?.requiredEnv ?? []).filter((name) => !ctx.env[name]);
|
|
215
212
|
if (missing.length)
|
|
216
|
-
out.push(
|
|
213
|
+
out.push(t().session.missingEnv(missing));
|
|
217
214
|
const notes = resolveNotes(repo, config?.notes)?.trim();
|
|
218
215
|
if (notes)
|
|
219
216
|
out.push(notes);
|
|
220
217
|
const dockerProblem = startDocker(ctx.docker ?? realDocker);
|
|
221
218
|
if (dockerProblem)
|
|
222
|
-
out.push(
|
|
219
|
+
out.push(t().session.dockerFailed(dockerProblem));
|
|
223
220
|
const env = toolsEnv(dirs, ctx.env, ca);
|
|
224
221
|
await syncGit(repo, ctx.run, env);
|
|
225
222
|
out.push(...(await runSessionSteps(repo, config?.session ?? [], ctx.run, env)));
|
|
@@ -240,7 +237,7 @@ export async function session(repo, ctx) {
|
|
|
240
237
|
await sessionBody(repo, ctx, out);
|
|
241
238
|
}
|
|
242
239
|
catch (err) {
|
|
243
|
-
out.push(
|
|
240
|
+
out.push(t().session.error(err instanceof Error ? err.message : String(err)));
|
|
244
241
|
}
|
|
245
242
|
return out.map((line) => OUTPUT_PREFIX + line);
|
|
246
243
|
}
|
package/dist/setup.js
CHANGED
|
@@ -13,6 +13,7 @@ import { configImages, readToolVersions } from "./detect.js";
|
|
|
13
13
|
import { realDocker } from "./docker.js";
|
|
14
14
|
import { curlNodeIndex, installTools, resolveVersions } from "./installers.js";
|
|
15
15
|
import { linkPath, packageBin, packageInstallDir } from "./launcher.js";
|
|
16
|
+
import { STEP, t } from "./messages/index.js";
|
|
16
17
|
import { pathDirs, toolsEnv } from "./session.js";
|
|
17
18
|
import { fingerprint, writeState } from "./state.js";
|
|
18
19
|
import { lanes, step } from "./steps.js";
|
|
@@ -64,16 +65,16 @@ async function aptLane(config, deps) {
|
|
|
64
65
|
const env = { ...deps.env, ...APT_ENV };
|
|
65
66
|
const results = [];
|
|
66
67
|
for (const url of config.aptRepos) {
|
|
67
|
-
const name =
|
|
68
|
+
const name = `${STEP.aptRepo}${url}`;
|
|
68
69
|
if (!url.startsWith(HTTPS_PREFIX)) {
|
|
69
|
-
results.push(await refused(name,
|
|
70
|
+
results.push(await refused(name, t().setup.aptRepoHttps(HTTPS_PREFIX)));
|
|
70
71
|
continue;
|
|
71
72
|
}
|
|
72
73
|
results.push(await step(name, () => deps.run("bash", ["-c", APT_REPO_SCRIPT, SCRIPT_NAME, url], { env, timeoutMs: APT_STEP_TIMEOUT_MS })));
|
|
73
74
|
}
|
|
74
75
|
const valid = config.apt.filter((pkg) => DEBIAN_PACKAGE_NAME.test(pkg));
|
|
75
76
|
for (const pkg of config.apt.filter((p) => !valid.includes(p))) {
|
|
76
|
-
results.push(await refused(
|
|
77
|
+
results.push(await refused(`${STEP.aptPackage}${pkg}`, t().setup.badDebianName));
|
|
77
78
|
}
|
|
78
79
|
if (!valid.length && !config.aptRepos.length)
|
|
79
80
|
return results;
|
|
@@ -119,11 +120,11 @@ async function dockerLane(config, images, docker, deps) {
|
|
|
119
120
|
const results = [];
|
|
120
121
|
const valid = images.filter((image) => !WHITESPACE.test(image) && !image.startsWith("-") && image !== "");
|
|
121
122
|
for (const image of images.filter((i) => !valid.includes(i))) {
|
|
122
|
-
results.push(await refused(`docker pull ${image}`,
|
|
123
|
+
results.push(await refused(`docker pull ${image}`, t().setup.badImage));
|
|
123
124
|
}
|
|
124
125
|
const up = await docker.ensure().catch(() => false);
|
|
125
126
|
if (!up)
|
|
126
|
-
return [...results, await refused("docker",
|
|
127
|
+
return [...results, await refused("docker", t().docker.didNotStart(DOCKERD_LOG))];
|
|
127
128
|
return [...results, ...(await pullAll(valid, deps))];
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
@@ -181,7 +182,7 @@ function manifestVersion(root) {
|
|
|
181
182
|
function copyPackage(source, target, version) {
|
|
182
183
|
const found = manifestVersion(source);
|
|
183
184
|
if (found !== version)
|
|
184
|
-
throw new Error(
|
|
185
|
+
throw new Error(t().setup.packageMismatch(source, found, version));
|
|
185
186
|
const temp = `${target}.${process.pid}.tmp`;
|
|
186
187
|
rmSync(temp, { recursive: true, force: true });
|
|
187
188
|
mkdirSync(temp, { recursive: true });
|
|
@@ -201,7 +202,7 @@ function copyPackage(source, target, version) {
|
|
|
201
202
|
* @returns The self install result.
|
|
202
203
|
*/
|
|
203
204
|
function selfInstall(version, toolsDir, source) {
|
|
204
|
-
return step(
|
|
205
|
+
return step(`${STEP.selfInstall}${version}`, async () => {
|
|
205
206
|
const target = packageInstallDir(toolsDir, version);
|
|
206
207
|
if (manifestVersion(target) !== version)
|
|
207
208
|
copyPackage(source, target, version);
|
|
@@ -243,7 +244,7 @@ async function runPhases(repo, config, ctx) {
|
|
|
243
244
|
steps,
|
|
244
245
|
resolved,
|
|
245
246
|
};
|
|
246
|
-
const saved = await step(
|
|
247
|
+
const saved = await step(STEP.writeState, async () => writeState(ctx.toolsDir, repo, state));
|
|
247
248
|
return saved.ok ? state : { ...state, steps: [...steps, saved] };
|
|
248
249
|
}
|
|
249
250
|
/**
|
|
@@ -267,7 +268,7 @@ export async function setupRepo(repo, ctx) {
|
|
|
267
268
|
});
|
|
268
269
|
const { config, errors } = readConfig(repo);
|
|
269
270
|
if (!config)
|
|
270
|
-
return failed(
|
|
271
|
+
return failed(STEP.config, errors.join("; ") || t().setup.noConfig);
|
|
271
272
|
try {
|
|
272
273
|
return await runPhases(repo, config, ctx);
|
|
273
274
|
}
|