@coderook/cli 0.22.2 → 0.24.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/.claude-plugin/plugin.json +1 -1
- package/README.md +342 -220
- package/dist/cli/src/api.js +27 -2
- package/dist/cli/src/cli.js +323 -87
- package/dist/cli/src/git_history.js +188 -0
- package/dist/cli/src/git_remote.js +1118 -0
- package/dist/cli/src/git_remote_bin.js +22 -0
- package/dist/cli/src/help.js +8 -8
- package/dist/cli/src/import_command.js +150 -0
- package/dist/cli/src/licence_commands.js +5 -5
- package/dist/cli/src/mcp.js +1 -1
- package/dist/cli/src/publish.js +37 -0
- package/dist/cli/src/registry.js +1 -1
- package/dist/cli/src/runner.js +2 -2
- package/dist/cli/src/service_commands.js +53 -3
- package/dist/cli/src/skill_command.js +3 -3
- package/dist/cli/src/track_commands.js +7 -7
- package/dist/desktop-app/src/main/tracks.js +12 -2
- package/dist/desktop-app/src/main/upload.js +11 -1
- package/package.json +53 -51
- package/skills/coderook/SKILL.md +130 -130
package/dist/cli/src/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* cbx — the CodeBox engine from the command line, talking to CodeRook.
|
|
9
9
|
*
|
|
10
10
|
* The same engines the desktop application uses — the same scanner, the same
|
|
11
11
|
* ignore rules, the same upload and download — with a terminal in front of
|
|
@@ -17,12 +17,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
*/
|
|
18
18
|
const promises_1 = require("node:readline/promises");
|
|
19
19
|
const node_os_1 = __importDefault(require("node:os"));
|
|
20
|
+
const promises_2 = require("node:fs/promises");
|
|
20
21
|
const node_path_1 = __importDefault(require("node:path"));
|
|
21
22
|
const node_process_1 = __importDefault(require("node:process"));
|
|
22
23
|
const api_js_1 = require("./api.js");
|
|
23
24
|
const registry_js_1 = require("./registry.js");
|
|
24
25
|
const help_js_1 = require("./help.js");
|
|
25
26
|
const progress_js_1 = require("./progress.js");
|
|
27
|
+
const publish_js_1 = require("./publish.js");
|
|
26
28
|
const project_commands_js_1 = require("./project_commands.js");
|
|
27
29
|
const track_commands_js_1 = require("./track_commands.js");
|
|
28
30
|
const mcp_js_1 = require("./mcp.js");
|
|
@@ -37,11 +39,12 @@ const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
|
37
39
|
const detect_js_1 = require("../../desktop-app/src/main/detect.js");
|
|
38
40
|
const cbx_js_1 = require("../../desktop-app/src/main/cbx.js");
|
|
39
41
|
const api_js_2 = require("./api.js");
|
|
42
|
+
const import_command_js_1 = require("./import_command.js");
|
|
40
43
|
const runner_js_1 = require("./runner.js");
|
|
41
44
|
const config_js_1 = require("./config.js");
|
|
42
45
|
/*
|
|
43
46
|
Read from the package rather than written twice. A hardcoded copy had
|
|
44
|
-
already drifted from the published version, which makes `
|
|
47
|
+
already drifted from the published version, which makes `cbx doctor`
|
|
45
48
|
worse than useless when working out what somebody is actually running.
|
|
46
49
|
*/
|
|
47
50
|
const VERSION = (() => {
|
|
@@ -295,7 +298,7 @@ async function commandStatus(parsed) {
|
|
|
295
298
|
if (stale.length) {
|
|
296
299
|
console.log(dim(`${stale.length} file${stale.length === 1 ? "" : "s"} in the saved version ` +
|
|
297
300
|
`${stale.length === 1 ? "is" : "are"} newer than the cop${stale.length === 1 ? "y" : "ies"} here ` +
|
|
298
|
-
`— run ${accent("
|
|
301
|
+
`— run ${accent("cbx get")} to bring ${stale.length === 1 ? "it" : "them"} down.`));
|
|
299
302
|
for (const [path] of stale.slice(0, 10))
|
|
300
303
|
console.log(dim(` behind ${path}`));
|
|
301
304
|
}
|
|
@@ -313,6 +316,103 @@ async function commandStatus(parsed) {
|
|
|
313
316
|
console.log(dim(` …and ${files.length - 50} more`));
|
|
314
317
|
return 0;
|
|
315
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Bring an existing repository in from another host.
|
|
321
|
+
*
|
|
322
|
+
* Snapshot only, and it says so. The files arrive, the history does not —
|
|
323
|
+
* see `import_command.ts` for why that is a limit rather than an omission.
|
|
324
|
+
* Everything after the fetch is the ordinary save path, so an import
|
|
325
|
+
* produces exactly the Version that saving the same folder would.
|
|
326
|
+
*/
|
|
327
|
+
async function commandImport(parsed) {
|
|
328
|
+
const url = parsed.positional[0];
|
|
329
|
+
if (!url) {
|
|
330
|
+
console.error(red("Nothing to import from."));
|
|
331
|
+
console.error("Give the address of a repository, for example:");
|
|
332
|
+
console.error(` ${accent("cbx import https://github.com/owner/project")}`);
|
|
333
|
+
return 1;
|
|
334
|
+
}
|
|
335
|
+
if (!(0, import_command_js_1.looksLikeRepositoryUrl)(url)) {
|
|
336
|
+
console.error(red(`${url} does not look like a repository address.`));
|
|
337
|
+
console.error("Expected something like https://github.com/owner/project or\n" +
|
|
338
|
+
"git@github.com:owner/project.git");
|
|
339
|
+
return 1;
|
|
340
|
+
}
|
|
341
|
+
if (!(await (0, import_command_js_1.gitAvailable)())) {
|
|
342
|
+
console.error(red("Import needs git on this machine, and it was not found."));
|
|
343
|
+
console.error("Git is used to fetch the files once; nothing about your project\n" +
|
|
344
|
+
"afterwards depends on it.");
|
|
345
|
+
return 1;
|
|
346
|
+
}
|
|
347
|
+
const destination = parsed.positional[1] ?? null;
|
|
348
|
+
let plan;
|
|
349
|
+
try {
|
|
350
|
+
plan = await (0, import_command_js_1.fetchSnapshot)(url, destination, (line) => console.log(dim(line)));
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
console.error(red("Could not fetch that repository."));
|
|
354
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
355
|
+
/*
|
|
356
|
+
git puts the useful sentence last and a stack of its own noise first.
|
|
357
|
+
A private repository with no credentials is by far the most common
|
|
358
|
+
failure, so it is named rather than left to be inferred.
|
|
359
|
+
*/
|
|
360
|
+
console.error(detail.split("\n").filter(Boolean).slice(-3).join("\n"));
|
|
361
|
+
if (/authentication|denied|not found|could not read/i.test(detail)) {
|
|
362
|
+
console.error("\nIf it is private, sign in to it with git first — CodeRook uses\n" +
|
|
363
|
+
"the credentials git already has and never asks for a token.");
|
|
364
|
+
}
|
|
365
|
+
return 1;
|
|
366
|
+
}
|
|
367
|
+
const { files, bytes } = await (0, import_command_js_1.measure)(plan.folder);
|
|
368
|
+
if (!files) {
|
|
369
|
+
console.error(red("That repository has no files in it."));
|
|
370
|
+
if (plan.temporary)
|
|
371
|
+
await (0, promises_2.rm)(node_path_1.default.dirname(plan.folder), { recursive: true, force: true });
|
|
372
|
+
return 1;
|
|
373
|
+
}
|
|
374
|
+
console.log(`Fetched ${files} file${files === 1 ? "" : "s"}, ${(0, import_command_js_1.humanBytes)(bytes)}, ` +
|
|
375
|
+
`into ${plan.folder}`);
|
|
376
|
+
/*
|
|
377
|
+
Said before the upload rather than after it fails. The window is a share
|
|
378
|
+
of the plan allowance per week, so a large import is a thing somebody
|
|
379
|
+
should know about while they can still choose a smaller repository.
|
|
380
|
+
*/
|
|
381
|
+
if (bytes > 1024 ** 3) {
|
|
382
|
+
console.log(dim("This is a large import. Uploads are limited to a share of your\n" +
|
|
383
|
+
"allowance each week, so a project this size may need more than one\n" +
|
|
384
|
+
"sitting; the save resumes rather than starting over."));
|
|
385
|
+
}
|
|
386
|
+
console.log(dim("History is not imported — this becomes the first version."));
|
|
387
|
+
/*
|
|
388
|
+
Hand the fetched folder to the ordinary save path. --allow-ignored is set
|
|
389
|
+
because git tracks files committed before the rule that excludes them,
|
|
390
|
+
and dropping those would make the import quietly lossy.
|
|
391
|
+
*/
|
|
392
|
+
const submitFlags = new Map(parsed.flags);
|
|
393
|
+
submitFlags.set("allow-ignored", true);
|
|
394
|
+
/*
|
|
395
|
+
Name the project after the repository, not after wherever the files were
|
|
396
|
+
put. Without this an import into a temporary folder produces a project
|
|
397
|
+
called something like `tmp-4f21`, and the name is the thing somebody
|
|
398
|
+
types afterwards to fetch it.
|
|
399
|
+
*/
|
|
400
|
+
if (!submitFlags.has("name"))
|
|
401
|
+
submitFlags.set("name", plan.name);
|
|
402
|
+
if (!submitFlags.has("m") && !submitFlags.has("message")) {
|
|
403
|
+
submitFlags.set("message", `Imported from ${url}`);
|
|
404
|
+
}
|
|
405
|
+
const code = await commandSubmit({
|
|
406
|
+
positional: [plan.folder],
|
|
407
|
+
flags: submitFlags,
|
|
408
|
+
});
|
|
409
|
+
if (code === 0 && plan.temporary) {
|
|
410
|
+
console.log(dim(`The working copy stays at ${plan.folder} until you remove it.
|
|
411
|
+
` +
|
|
412
|
+
`Run ${accent("cbx get " + plan.name)} anywhere to fetch it fresh.`));
|
|
413
|
+
}
|
|
414
|
+
return code;
|
|
415
|
+
}
|
|
316
416
|
async function commandSubmit(parsed) {
|
|
317
417
|
const folder = folderFor(parsed);
|
|
318
418
|
const message = flagText(parsed, "m", "message") ?? "";
|
|
@@ -340,8 +440,39 @@ async function commandSubmit(parsed) {
|
|
|
340
440
|
await (0, api_js_2.whoami)()
|
|
341
441
|
.then((account) => account.displayName || account.username || "")
|
|
342
442
|
.catch(() => ""), hasFlag(parsed, "no-licence"));
|
|
343
|
-
|
|
344
|
-
|
|
443
|
+
/*
|
|
444
|
+
Import sends what it fetched, filtering nothing.
|
|
445
|
+
|
|
446
|
+
The ignore rules exist to keep build output and local mess out of a
|
|
447
|
+
working folder, and applying them to a fresh clone gets the wrong answer
|
|
448
|
+
twice over: a clone holds exactly the files the source repository tracked
|
|
449
|
+
and nothing else, so there is no mess to exclude — while a repository
|
|
450
|
+
that tracks something its own `.gitignore` now names (which git does,
|
|
451
|
+
for anything committed before the rule) would have those files dropped.
|
|
452
|
+
Dropping them makes an import quietly lossy, which is worse than
|
|
453
|
+
refusing to import at all.
|
|
454
|
+
*/
|
|
455
|
+
const importing = hasFlag(parsed, "allow-ignored");
|
|
456
|
+
const rules = importing
|
|
457
|
+
? { shared: "", local: "" }
|
|
458
|
+
: await (0, worktree_js_1.readRules)(folder);
|
|
459
|
+
/*
|
|
460
|
+
Whether a file that is no longer here means "delete it".
|
|
461
|
+
|
|
462
|
+
The default says no, and that is the right default: a missing path is
|
|
463
|
+
usually an unmounted drive, a folder moved while something was open, or a
|
|
464
|
+
scan that ran mid-copy — and the cost of guessing wrong is somebody's work
|
|
465
|
+
removed from the one place it was safe. So the saved copy is left alone
|
|
466
|
+
(docs/UPLOAD_POLICY.md).
|
|
467
|
+
|
|
468
|
+
It also meant nothing on this side could ever remove a file. Publishing
|
|
469
|
+
from git can, because there the question does not arise: `git rm` is a
|
|
470
|
+
recorded act, so the deletion is known rather than inferred. That left the
|
|
471
|
+
remote helper as the only route to something the tool itself could not do,
|
|
472
|
+
which is the wrong way round — so this is that capability, asked for
|
|
473
|
+
plainly.
|
|
474
|
+
*/
|
|
475
|
+
const files = await (0, worktree_js_1.changedFiles)(folder, rules, baseline, hasFlag(parsed, "sync") ? "synchronize" : "add-and-update");
|
|
345
476
|
/*
|
|
346
477
|
An upgraded folder with no materialisation record and something that
|
|
347
478
|
differs from its recorded version is ambiguous in the one way that
|
|
@@ -364,8 +495,8 @@ async function commandSubmit(parsed) {
|
|
|
364
495
|
console.error(`${files.length} file${files.length === 1 ? " differs" : "s differ"} here:`);
|
|
365
496
|
for (const file of files.slice(0, 20))
|
|
366
497
|
console.error(` ${file.path}`);
|
|
367
|
-
console.error(`\nIf that is your work, send it with ${accent("
|
|
368
|
-
`\nIf it is an old copy, take theirs with ${accent("
|
|
498
|
+
console.error(`\nIf that is your work, send it with ${accent("cbx submit --force")}.` +
|
|
499
|
+
`\nIf it is an old copy, take theirs with ${accent("cbx get --replace")}.`);
|
|
369
500
|
return 1;
|
|
370
501
|
}
|
|
371
502
|
}
|
|
@@ -379,7 +510,7 @@ async function commandSubmit(parsed) {
|
|
|
379
510
|
console.log(behindOn
|
|
380
511
|
? `Nothing to submit; your own work is all saved. ${behindOn} file` +
|
|
381
512
|
`${behindOn === 1 ? "" : "s"} here ${behindOn === 1 ? "is" : "are"} ` +
|
|
382
|
-
`behind the saved version — run ${accent("
|
|
513
|
+
`behind the saved version — run ${accent("cbx get")}.`
|
|
383
514
|
: "Nothing to submit; this folder matches the saved version.");
|
|
384
515
|
return 0;
|
|
385
516
|
}
|
|
@@ -404,17 +535,17 @@ async function commandSubmit(parsed) {
|
|
|
404
535
|
*/
|
|
405
536
|
const shielded = (await (0, worktree_js_1.detectPrivateDirectories)(folder)).filter((finding) => [...sending].some((file) => file === finding.path || file.startsWith(`${finding.path}/`)));
|
|
406
537
|
if (shielded.length && !hasFlag(parsed, "allow-private")) {
|
|
407
|
-
console.log(red(`
|
|
538
|
+
console.log(red(`
|
|
408
539
|
${shielded.length} folder${shielded.length === 1 ? "" : "s"} here belong${shielded.length === 1 ? "s" : ""} to a program, not to your project:`));
|
|
409
540
|
for (const finding of shielded) {
|
|
410
541
|
console.log(` ${finding.path} ${dim(`— ${finding.because}`)}`);
|
|
411
542
|
}
|
|
412
|
-
console.log(`
|
|
543
|
+
console.log(`
|
|
413
544
|
Nothing was sent. To leave them behind:`);
|
|
414
545
|
for (const finding of shielded) {
|
|
415
546
|
console.log(` ${accent(`echo "${finding.rule}" >> .gitignore`)}`);
|
|
416
547
|
}
|
|
417
|
-
console.error(`
|
|
548
|
+
console.error(`
|
|
418
549
|
Or pass ${accent("--allow-private")} if they genuinely belong in the project.`);
|
|
419
550
|
return 1;
|
|
420
551
|
}
|
|
@@ -449,7 +580,7 @@ Or pass ${accent("--allow-private")} if they genuinely belong in the project.`);
|
|
|
449
580
|
const pasted = await (0, worktree_js_1.detectPastedCredentials)(folder, files.map((file) => file.path));
|
|
450
581
|
const stillPasted = pasted.filter((finding) => !exposed.includes(finding.path));
|
|
451
582
|
if (stillPasted.length) {
|
|
452
|
-
console.log(red(`
|
|
583
|
+
console.log(red(`
|
|
453
584
|
${stillPasted.length} file${stillPasted.length === 1 ? " has a credential" : "s have credentials"} inside:`));
|
|
454
585
|
for (const finding of stillPasted.slice(0, 20)) {
|
|
455
586
|
console.log(` ${finding.path}`);
|
|
@@ -464,11 +595,11 @@ ${stillPasted.length} file${stillPasted.length === 1 ? " has a credential" : "s
|
|
|
464
595
|
console.log(dim(` …and ${stillPasted.length - 20} more`));
|
|
465
596
|
}
|
|
466
597
|
if (!hasFlag(parsed, "allow-secrets")) {
|
|
467
|
-
console.error(`
|
|
598
|
+
console.error(`
|
|
468
599
|
Nothing was sent. Move the key into an environment variable, and if` +
|
|
469
600
|
` it has ever been published, replace it at the service that issued` +
|
|
470
601
|
` it — a key that has leaked stays leaked.` +
|
|
471
|
-
`
|
|
602
|
+
`
|
|
472
603
|
Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
473
604
|
return 1;
|
|
474
605
|
}
|
|
@@ -477,31 +608,47 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
|
477
608
|
if (added) {
|
|
478
609
|
console.log(`${accent("Added an MIT licence")}, so other people may use this.`);
|
|
479
610
|
console.log(dim(` Change it with `) +
|
|
480
|
-
accent("
|
|
611
|
+
accent("cbx licence <name>") +
|
|
481
612
|
dim(`, or remove it with `) +
|
|
482
|
-
accent("
|
|
613
|
+
accent("cbx licence none") +
|
|
483
614
|
dim("."));
|
|
484
615
|
console.log("");
|
|
485
616
|
}
|
|
486
617
|
const line = progressLine();
|
|
487
618
|
const uploader = new upload_js_1.Uploader(config_js_1.credentials);
|
|
488
|
-
|
|
619
|
+
/*
|
|
620
|
+
Built by the shared description, so this and the git remote helper cannot
|
|
621
|
+
drift. They already had: the helper listed deletions in `deletions` alone
|
|
622
|
+
and published versions that still held the deleted file.
|
|
623
|
+
*/
|
|
624
|
+
const uploadRequest = (0, publish_js_1.uploadRequestFor)({
|
|
489
625
|
localPath: folder,
|
|
490
|
-
|
|
491
|
-
|
|
626
|
+
changed: files.filter((file) => !file.deleted).map((file) => file.path),
|
|
627
|
+
deleted: files.filter((file) => file.deleted).map((file) => file.path),
|
|
492
628
|
message,
|
|
493
|
-
|
|
629
|
+
/*
|
|
630
|
+
A folder's name is the right default and the wrong answer for import,
|
|
631
|
+
where the folder is somewhere temporary and the project should carry
|
|
632
|
+
the name it had at the place it came from. An existing link always
|
|
633
|
+
wins: renaming somebody's project because they passed a flag would be
|
|
634
|
+
a surprise, and the flag exists for projects that do not exist yet.
|
|
635
|
+
*/
|
|
636
|
+
projectName: link?.slug ?? flagText(parsed, "name") ?? node_path_1.default.basename(folder),
|
|
494
637
|
repositoryId: link?.repositoryId ?? null,
|
|
495
638
|
// Every current CLI publish states its ancestry. A brand-new project is
|
|
496
639
|
// explicitly based on an empty Track; a linked folder names the immutable
|
|
497
640
|
// Version it was last reconciled with.
|
|
498
641
|
baseVersionId: link?.baseVersionId ?? null,
|
|
499
642
|
track: flagText(parsed, "track") ?? (await (0, track_commands_js_1.trackFor)(folder)),
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
643
|
+
/*
|
|
644
|
+
Only import sets this. Git keeps tracking files committed before the
|
|
645
|
+
rule that excludes them, so a faithful import carries paths the
|
|
646
|
+
project's own ignore rules now refuse; without the override the server
|
|
647
|
+
would reject the publication and the import would be lossy.
|
|
648
|
+
*/
|
|
649
|
+
...(hasFlag(parsed, "allow-ignored") ? { allowIgnored: true } : {}),
|
|
503
650
|
...(link ? { known: link.local ?? link.manifest ?? {} } : {}),
|
|
504
|
-
};
|
|
651
|
+
});
|
|
505
652
|
let result;
|
|
506
653
|
try {
|
|
507
654
|
const plan = await uploader.plan(uploadRequest, (progress) => {
|
|
@@ -532,23 +679,22 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
|
532
679
|
}
|
|
533
680
|
catch (error) {
|
|
534
681
|
done(line);
|
|
535
|
-
const code = error.code;
|
|
536
|
-
const text = error instanceof Error ? error.message : String(error);
|
|
537
682
|
/*
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
difference between a person retrying and a person wondering.
|
|
683
|
+
Classified by the shared description rather than by a private regular
|
|
684
|
+
expression, so a push and a submit disagree about nothing — including
|
|
685
|
+
what counts as "the connection failed" and what that means for whether
|
|
686
|
+
the work was saved.
|
|
543
687
|
*/
|
|
544
|
-
|
|
545
|
-
|
|
688
|
+
const failure = (0, publish_js_1.classifyPublishFailure)(error);
|
|
689
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
690
|
+
if (failure?.kind === "interrupted") {
|
|
691
|
+
console.error(red(`
|
|
546
692
|
The connection failed: ${text}`));
|
|
547
693
|
console.error(`Your work may already have been saved. Run the same command again —` +
|
|
548
694
|
` it will not create a second version.`);
|
|
549
695
|
return 1;
|
|
550
696
|
}
|
|
551
|
-
if (
|
|
697
|
+
if (failure?.kind === "conflict") {
|
|
552
698
|
/*
|
|
553
699
|
Somebody else saved to this project first. Everything that did not
|
|
554
700
|
overlap has already been combined by the service; what is left is a
|
|
@@ -556,7 +702,7 @@ The connection failed: ${text}`));
|
|
|
556
702
|
at it rather than to try again harder.
|
|
557
703
|
*/
|
|
558
704
|
console.error(red(error instanceof Error ? error.message : String(error)));
|
|
559
|
-
console.error(`\nRun ${accent("
|
|
705
|
+
console.error(`\nRun ${accent("cbx get")} to bring the latest version down, then` +
|
|
560
706
|
` submit again. Nothing was changed on your account.`);
|
|
561
707
|
return 1;
|
|
562
708
|
}
|
|
@@ -576,8 +722,8 @@ The connection failed: ${text}`));
|
|
|
576
722
|
for (const conflict of result.mergeTrack.conflicts.slice(0, 20)) {
|
|
577
723
|
console.log(` ${red(conflict.path)} ${dim(conflict.kind)}`);
|
|
578
724
|
}
|
|
579
|
-
console.log(`\nRun ${accent(`
|
|
580
|
-
` or ${accent("
|
|
725
|
+
console.log(`\nRun ${accent(`cbx merge ${result.mergeTrack.reference}`)} to decide,` +
|
|
726
|
+
` or ${accent("cbx merges")} to see everything waiting.`);
|
|
581
727
|
return 0;
|
|
582
728
|
}
|
|
583
729
|
/*
|
|
@@ -586,9 +732,29 @@ The connection failed: ${text}`));
|
|
|
586
732
|
attempt name exists to make survivable.
|
|
587
733
|
*/
|
|
588
734
|
(0, faults_js_1.maybeFail)("submit:after-commit");
|
|
735
|
+
/*
|
|
736
|
+
What the project is actually called, not what this folder is called.
|
|
737
|
+
|
|
738
|
+
A first publish recorded the folder's own name, which is right only when
|
|
739
|
+
nothing renamed the project — and two things routinely do: `--name`, and
|
|
740
|
+
the service turning a display name into a slug. When they differ, every
|
|
741
|
+
command that finds the project through this folder looks up a name that
|
|
742
|
+
does not exist: `tracks`, `versions`, `issues`, `releases`, `people`,
|
|
743
|
+
`watch`, `delete` and the rest all answered "No project matching …" in a
|
|
744
|
+
folder that had just published to it successfully.
|
|
745
|
+
|
|
746
|
+
Asked of the service rather than guessed, and only on a first publish,
|
|
747
|
+
because that is the only time this is not already known.
|
|
748
|
+
*/
|
|
749
|
+
let recordedSlug = link?.slug;
|
|
750
|
+
if (!recordedSlug) {
|
|
751
|
+
recordedSlug = await (0, api_js_2.projects)()
|
|
752
|
+
.then((all) => all.find((candidate) => candidate.id === result.repositoryId)?.slug)
|
|
753
|
+
.catch(() => undefined);
|
|
754
|
+
}
|
|
589
755
|
await (0, config_js_1.writeLink)(folder, {
|
|
590
756
|
repositoryId: result.repositoryId,
|
|
591
|
-
slug:
|
|
757
|
+
slug: recordedSlug ?? uploadRequest.projectName ?? node_path_1.default.basename(folder),
|
|
592
758
|
sequence: result.sequence,
|
|
593
759
|
// What this folder is now working from, so the next submit can say so.
|
|
594
760
|
versionId: result.versionId,
|
|
@@ -687,8 +853,8 @@ async function commandGet(parsed) {
|
|
|
687
853
|
console.error(red("This folder was connected by an older version of CodeRook,"));
|
|
688
854
|
console.error("which did not record what it had received, so changed files here");
|
|
689
855
|
console.error("cannot be told apart from copies left behind by a merge.\n");
|
|
690
|
-
console.error(`Save them with ${accent("
|
|
691
|
-
`take the\nsaved version exactly with ${accent("
|
|
856
|
+
console.error(`Save them with ${accent("cbx submit")} if they are your work, or ` +
|
|
857
|
+
`take the\nsaved version exactly with ${accent("cbx get --replace")}.`);
|
|
692
858
|
return 1;
|
|
693
859
|
}
|
|
694
860
|
}
|
|
@@ -724,9 +890,9 @@ async function commandGet(parsed) {
|
|
|
724
890
|
`changed while the interrupted fetch was stopped:`));
|
|
725
891
|
for (const file of changedInTheGap.slice(0, 20))
|
|
726
892
|
console.error(` ${file.path}`);
|
|
727
|
-
console.error(`
|
|
728
|
-
Save them with ${accent("
|
|
729
|
-
`discard them with ${accent("
|
|
893
|
+
console.error(`
|
|
894
|
+
Save them with ${accent("cbx submit")}, or finish the fetch and ` +
|
|
895
|
+
`discard them with ${accent("cbx get --replace")}.`);
|
|
730
896
|
return 1;
|
|
731
897
|
}
|
|
732
898
|
/*
|
|
@@ -746,8 +912,8 @@ Save them with ${accent("coderook submit")}, or finish the fetch and ` +
|
|
|
746
912
|
console.error(` ${file.path}`);
|
|
747
913
|
if (atRisk.length > 20)
|
|
748
914
|
console.error(dim(` …and ${atRisk.length - 20} more`));
|
|
749
|
-
console.error(`\nSave them first with ${accent("
|
|
750
|
-
`with ${accent("
|
|
915
|
+
console.error(`\nSave them first with ${accent("cbx submit")}, or discard them ` +
|
|
916
|
+
`with ${accent("cbx get --replace")}.`);
|
|
751
917
|
return 1;
|
|
752
918
|
}
|
|
753
919
|
}
|
|
@@ -762,7 +928,7 @@ Save them with ${accent("coderook submit")}, or finish the fetch and ` +
|
|
|
762
928
|
async function commandClone(parsed) {
|
|
763
929
|
const reference = parsed.positional[0];
|
|
764
930
|
if (!reference) {
|
|
765
|
-
console.error(red("Which project? Try:
|
|
931
|
+
console.error(red("Which project? Try: cbx clone <project>"));
|
|
766
932
|
return 1;
|
|
767
933
|
}
|
|
768
934
|
const project = await (0, api_js_2.findProject)(reference);
|
|
@@ -866,7 +1032,7 @@ async function suggestRules(folder, apply) {
|
|
|
866
1032
|
const recommended = suggestions.filter((one) => one.recommended);
|
|
867
1033
|
if (!apply) {
|
|
868
1034
|
console.log("");
|
|
869
|
-
console.log(dim("Add the + ones with ") + accent("
|
|
1035
|
+
console.log(dim("Add the + ones with ") + accent("cbx ignore --suggest --apply"));
|
|
870
1036
|
return 0;
|
|
871
1037
|
}
|
|
872
1038
|
if (!recommended.length) {
|
|
@@ -877,8 +1043,8 @@ async function suggestRules(folder, apply) {
|
|
|
877
1043
|
const addition = (0, detect_js_1.rulesFromSuggestions)(recommended);
|
|
878
1044
|
const shared = rules.shared.trimEnd();
|
|
879
1045
|
await (0, worktree_js_1.writeRules)(folder, {
|
|
880
|
-
shared: shared ? `${shared}
|
|
881
|
-
|
|
1046
|
+
shared: shared ? `${shared}
|
|
1047
|
+
|
|
882
1048
|
${addition}` : addition,
|
|
883
1049
|
local: rules.local,
|
|
884
1050
|
});
|
|
@@ -928,7 +1094,7 @@ async function commandBundle(parsed) {
|
|
|
928
1094
|
async function commandUnbundle(parsed) {
|
|
929
1095
|
const source = parsed.positional[0];
|
|
930
1096
|
if (!source) {
|
|
931
|
-
console.error(red("Which bundle? Try:
|
|
1097
|
+
console.error(red("Which bundle? Try: cbx unbundle <file.cbx> [folder]"));
|
|
932
1098
|
return 1;
|
|
933
1099
|
}
|
|
934
1100
|
const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
|
|
@@ -945,7 +1111,7 @@ async function commandUnbundle(parsed) {
|
|
|
945
1111
|
async function commandInspect(parsed) {
|
|
946
1112
|
const source = parsed.positional[0];
|
|
947
1113
|
if (!source) {
|
|
948
|
-
console.error(red("Which bundle? Try:
|
|
1114
|
+
console.error(red("Which bundle? Try: cbx inspect <file.cbx>"));
|
|
949
1115
|
return 1;
|
|
950
1116
|
}
|
|
951
1117
|
const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
|
|
@@ -962,7 +1128,7 @@ async function commandInspect(parsed) {
|
|
|
962
1128
|
}
|
|
963
1129
|
async function commandDoctor() {
|
|
964
1130
|
const service = await (0, api_js_2.health)().catch(() => null);
|
|
965
|
-
console.log(`
|
|
1131
|
+
console.log(`cbx ${VERSION} · Node ${node_process_1.default.versions.node} · ${node_process_1.default.platform}`);
|
|
966
1132
|
console.log(`Config: ${(0, config_js_1.configDirectory)()}`);
|
|
967
1133
|
console.log(service
|
|
968
1134
|
? `Service: ${service.status} · schema ${service.schemaVersion}` +
|
|
@@ -1005,8 +1171,8 @@ async function commandMerges(parsed) {
|
|
|
1005
1171
|
const counts = merge.conflicts;
|
|
1006
1172
|
console.log(`${accent(merge.reference)} ${counts ? `${counts.unresolved} of ${counts.total} still to decide` : ""} ${dim(new Date(merge.createdAt).toLocaleString())}`);
|
|
1007
1173
|
}
|
|
1008
|
-
console.log(dim(`
|
|
1009
|
-
Run
|
|
1174
|
+
console.log(dim(`
|
|
1175
|
+
Run cbx merge <reference> to look at one.`));
|
|
1010
1176
|
return 0;
|
|
1011
1177
|
}
|
|
1012
1178
|
/** Find a merge by the reference a person would type, such as M-2. */
|
|
@@ -1029,7 +1195,7 @@ async function findMerge(folder, reference) {
|
|
|
1029
1195
|
async function commandMerge(parsed) {
|
|
1030
1196
|
const reference = parsed.positional[0];
|
|
1031
1197
|
if (!reference) {
|
|
1032
|
-
console.error(red("Which merge? Try:
|
|
1198
|
+
console.error(red("Which merge? Try: cbx merge M-1"));
|
|
1033
1199
|
return 1;
|
|
1034
1200
|
}
|
|
1035
1201
|
const folder = folderFor({ ...parsed, positional: parsed.positional.slice(1) });
|
|
@@ -1071,7 +1237,7 @@ async function commandMerge(parsed) {
|
|
|
1071
1237
|
}
|
|
1072
1238
|
}
|
|
1073
1239
|
const now = await (0, api_js_1.mergeTrack)(summary.id);
|
|
1074
|
-
console.log(`
|
|
1240
|
+
console.log(`
|
|
1075
1241
|
${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
|
|
1076
1242
|
(now.provisional.ready
|
|
1077
1243
|
? "ready to apply"
|
|
@@ -1086,20 +1252,20 @@ ${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
|
|
|
1086
1252
|
return 1;
|
|
1087
1253
|
}
|
|
1088
1254
|
const applied = await (0, api_js_1.applyMerge)(summary.id);
|
|
1089
|
-
console.log(`
|
|
1255
|
+
console.log(`
|
|
1090
1256
|
Applied as ${accent(`v${applied.version.sequence}`)}.`);
|
|
1091
|
-
console.log(dim("Run
|
|
1257
|
+
console.log(dim("Run cbx get to bring it down to this folder."));
|
|
1092
1258
|
return 0;
|
|
1093
1259
|
}
|
|
1094
1260
|
if (!decision) {
|
|
1095
|
-
console.log(dim(`
|
|
1261
|
+
console.log(dim(`
|
|
1096
1262
|
--mine keeps yours, --theirs keeps what was already saved,` +
|
|
1097
|
-
` --drop removes the file.
|
|
1263
|
+
` --drop removes the file.
|
|
1098
1264
|
Add --path <file> for one file, then --apply when ready.`));
|
|
1099
1265
|
}
|
|
1100
1266
|
else if (now.provisional.ready) {
|
|
1101
|
-
console.log(dim(`
|
|
1102
|
-
Run
|
|
1267
|
+
console.log(dim(`
|
|
1268
|
+
Run cbx merge ${now.mergeTrack.reference} --apply to publish it.`));
|
|
1103
1269
|
}
|
|
1104
1270
|
return 0;
|
|
1105
1271
|
}
|
|
@@ -1119,7 +1285,7 @@ Run coderook merge ${now.mergeTrack.reference} --apply to publish it.`));
|
|
|
1119
1285
|
async function commandRunner(parsed) {
|
|
1120
1286
|
const reference = parsed.positional[0] ?? flagText(parsed, "project", "p");
|
|
1121
1287
|
if (!reference) {
|
|
1122
|
-
console.error(red("Which project?
|
|
1288
|
+
console.error(red("Which project? cbx runner <project>"));
|
|
1123
1289
|
return 1;
|
|
1124
1290
|
}
|
|
1125
1291
|
const project = await (0, api_js_2.findProject)(reference);
|
|
@@ -1265,7 +1431,7 @@ const SPECS = [
|
|
|
1265
1431
|
},
|
|
1266
1432
|
{
|
|
1267
1433
|
name: "submit",
|
|
1268
|
-
aliases: ["publish"],
|
|
1434
|
+
aliases: ["publish", "push"],
|
|
1269
1435
|
group: "Working with a folder",
|
|
1270
1436
|
summary: "send the changes as a new version",
|
|
1271
1437
|
usage: 'submit [folder] -m "…"',
|
|
@@ -1275,10 +1441,18 @@ const SPECS = [
|
|
|
1275
1441
|
options: [
|
|
1276
1442
|
{ flags: "-m, --message <text>", description: "what changed, in a sentence" },
|
|
1277
1443
|
{ flags: "-n, --dry-run", description: "show what would be sent, send nothing" },
|
|
1444
|
+
{
|
|
1445
|
+
flags: "--name <name>",
|
|
1446
|
+
description: "name a new project this, instead of after the folder",
|
|
1447
|
+
},
|
|
1278
1448
|
{
|
|
1279
1449
|
flags: "--allow-secrets",
|
|
1280
1450
|
description: "send files that look like credentials, and files with keys inside",
|
|
1281
1451
|
},
|
|
1452
|
+
{
|
|
1453
|
+
flags: "--sync",
|
|
1454
|
+
description: "treat a file that is no longer here as deleted, rather than keeping the saved copy",
|
|
1455
|
+
},
|
|
1282
1456
|
{
|
|
1283
1457
|
flags: "--track <name>",
|
|
1284
1458
|
description: "save onto this line, just this once",
|
|
@@ -1289,11 +1463,43 @@ const SPECS = [
|
|
|
1289
1463
|
},
|
|
1290
1464
|
],
|
|
1291
1465
|
examples: [
|
|
1292
|
-
'
|
|
1293
|
-
'
|
|
1466
|
+
'cbx submit -m "Fix the export dialog"',
|
|
1467
|
+
'cbx submit --track spike -m "Try the other encoder"',
|
|
1294
1468
|
],
|
|
1295
1469
|
run: commandSubmit,
|
|
1296
1470
|
},
|
|
1471
|
+
{
|
|
1472
|
+
name: "import",
|
|
1473
|
+
group: "Getting started",
|
|
1474
|
+
summary: "bring a project in from another host",
|
|
1475
|
+
usage: "import <address> [folder]",
|
|
1476
|
+
detail: "Fetches a repository from GitHub, GitLab or anywhere else git can\n" +
|
|
1477
|
+
"reach, and saves it as the first version of a CodeRook project.\n\n" +
|
|
1478
|
+
"The files come across; the history does not. Every past commit would\n" +
|
|
1479
|
+
"have to be published as its own version, which on a large project\n" +
|
|
1480
|
+
"takes days — so this takes an honest snapshot rather than leaving a\n" +
|
|
1481
|
+
"half-finished import behind. What arrives is the current state of the\n" +
|
|
1482
|
+
"default branch.\n\n" +
|
|
1483
|
+
"A public repository needs nothing. A private one uses the credentials\n" +
|
|
1484
|
+
"git already has on this machine; CodeRook never asks for, stores or\n" +
|
|
1485
|
+
"forwards a token.\n\n" +
|
|
1486
|
+
"Files that the project's own ignore rules exclude are sent anyway,\n" +
|
|
1487
|
+
"because git keeps tracking anything committed before the rule that\n" +
|
|
1488
|
+
"excludes it, and leaving them out would lose files the source has.",
|
|
1489
|
+
options: [
|
|
1490
|
+
{ flags: "-m, --message <text>", description: "the first version's message" },
|
|
1491
|
+
{ flags: "--track <name>", description: "save onto this line" },
|
|
1492
|
+
{
|
|
1493
|
+
flags: "--no-licence",
|
|
1494
|
+
description: "do not add a licence to the new project",
|
|
1495
|
+
},
|
|
1496
|
+
],
|
|
1497
|
+
examples: [
|
|
1498
|
+
"cbx import https://github.com/owner/project",
|
|
1499
|
+
"cbx import git@github.com:owner/project.git ./project",
|
|
1500
|
+
],
|
|
1501
|
+
run: commandImport,
|
|
1502
|
+
},
|
|
1297
1503
|
{
|
|
1298
1504
|
/*
|
|
1299
1505
|
Where the next save goes, which is a property of this folder rather
|
|
@@ -1301,14 +1507,14 @@ const SPECS = [
|
|
|
1301
1507
|
different lines, which is most of the point of having them.
|
|
1302
1508
|
*/
|
|
1303
1509
|
name: "track",
|
|
1304
|
-
aliases: ["switch"],
|
|
1510
|
+
aliases: ["switch", "checkout"],
|
|
1305
1511
|
group: "Working with a folder",
|
|
1306
1512
|
summary: "show or change the line this folder saves to",
|
|
1307
1513
|
usage: "track [name]",
|
|
1308
1514
|
detail: "With no name, prints the line this folder saves to. With one, switches\n" +
|
|
1309
1515
|
"to it. Switching says where the next save goes and nothing else: no\n" +
|
|
1310
1516
|
"files move and nothing is fetched, so it is instant and safe to change\n" +
|
|
1311
|
-
"your mind. Run `
|
|
1517
|
+
"your mind. Run `cbx get` afterwards to bring that line's files\n" +
|
|
1312
1518
|
"into the folder.\n\n" +
|
|
1313
1519
|
"A name that does not exist is refused rather than created, because a\n" +
|
|
1314
1520
|
"typo in a branch name is an ordinary thing to do and a line called\n" +
|
|
@@ -1317,23 +1523,25 @@ const SPECS = [
|
|
|
1317
1523
|
options: [
|
|
1318
1524
|
{ flags: "-n, --new", description: "start this line from where the project is now" },
|
|
1319
1525
|
],
|
|
1320
|
-
examples: ["
|
|
1526
|
+
examples: ["cbx track", "cbx track spike --new", "cbx track main"],
|
|
1321
1527
|
run: track_commands_js_1.commandTrack,
|
|
1322
1528
|
},
|
|
1323
1529
|
{
|
|
1324
1530
|
name: "tracks",
|
|
1531
|
+
aliases: ["branch", "branches"],
|
|
1325
1532
|
group: "Your projects",
|
|
1326
1533
|
summary: "the lines a project has, and any waiting on a decision",
|
|
1327
1534
|
usage: "tracks [project]",
|
|
1328
1535
|
detail: "Lists every line on the project, marking the one this folder saves to.\n" +
|
|
1329
1536
|
"Merges waiting on a decision are listed beside them rather than hidden,\n" +
|
|
1330
1537
|
"because somebody looking for where they can save needs to see the one\n" +
|
|
1331
|
-
"they cannot. Finish one with `
|
|
1332
|
-
examples: ["
|
|
1538
|
+
"they cannot. Finish one with `cbx merge`.",
|
|
1539
|
+
examples: ["cbx tracks", "cbx tracks my-project"],
|
|
1333
1540
|
run: track_commands_js_1.commandTracks,
|
|
1334
1541
|
},
|
|
1335
1542
|
{
|
|
1336
1543
|
name: "get",
|
|
1544
|
+
aliases: ["pull"],
|
|
1337
1545
|
group: "Working with a folder",
|
|
1338
1546
|
summary: "bring this folder up to date",
|
|
1339
1547
|
usage: "get [folder]",
|
|
@@ -1376,7 +1584,7 @@ const SPECS = [
|
|
|
1376
1584
|
description: "replace a licence that is already there",
|
|
1377
1585
|
},
|
|
1378
1586
|
],
|
|
1379
|
-
examples: ["
|
|
1587
|
+
examples: ["cbx licence", "cbx licence MIT", "cbx licence Apache-2.0"],
|
|
1380
1588
|
run: licence_commands_js_1.commandLicence,
|
|
1381
1589
|
},
|
|
1382
1590
|
{
|
|
@@ -1423,6 +1631,7 @@ const SPECS = [
|
|
|
1423
1631
|
},
|
|
1424
1632
|
{
|
|
1425
1633
|
name: "versions",
|
|
1634
|
+
aliases: ["log"],
|
|
1426
1635
|
group: "Your projects",
|
|
1427
1636
|
summary: "what has been saved to a project",
|
|
1428
1637
|
usage: "versions [project]",
|
|
@@ -1448,9 +1657,9 @@ const SPECS = [
|
|
|
1448
1657
|
{ flags: "--request [off]", description: "automated calls to its endpoints" },
|
|
1449
1658
|
],
|
|
1450
1659
|
examples: [
|
|
1451
|
-
"
|
|
1452
|
-
"
|
|
1453
|
-
"
|
|
1660
|
+
"cbx ai my-project",
|
|
1661
|
+
"cbx ai my-project --read off",
|
|
1662
|
+
"cbx ai my-project --download off --request off",
|
|
1454
1663
|
],
|
|
1455
1664
|
run: project_commands_js_1.commandAi,
|
|
1456
1665
|
},
|
|
@@ -1463,16 +1672,43 @@ const SPECS = [
|
|
|
1463
1672
|
{ flags: '--new "<title>"', description: "open a new issue" },
|
|
1464
1673
|
{ flags: "--body <text>", description: "the description for a new one" },
|
|
1465
1674
|
],
|
|
1466
|
-
examples: ['
|
|
1675
|
+
examples: ['cbx issues my-project --new "Crash on export"'],
|
|
1467
1676
|
run: service_commands_js_1.commandIssues,
|
|
1468
1677
|
},
|
|
1469
1678
|
{
|
|
1470
1679
|
name: "releases",
|
|
1680
|
+
aliases: ["tags"],
|
|
1471
1681
|
group: "Your projects",
|
|
1472
1682
|
summary: "what has been released, and its files",
|
|
1473
1683
|
usage: "releases [project]",
|
|
1474
1684
|
run: service_commands_js_1.commandReleases,
|
|
1475
1685
|
},
|
|
1686
|
+
{
|
|
1687
|
+
/*
|
|
1688
|
+
A release is a version with a name on it, so this names one rather than
|
|
1689
|
+
creating anything. Pushing a git tag does the same thing by the same
|
|
1690
|
+
route — the tag is the name, the tagged commit picks the version.
|
|
1691
|
+
*/
|
|
1692
|
+
name: "release",
|
|
1693
|
+
aliases: ["tag"],
|
|
1694
|
+
group: "Your projects",
|
|
1695
|
+
summary: "give a version a name, so people know which one to fetch",
|
|
1696
|
+
usage: "release <name> [project]",
|
|
1697
|
+
detail: "Names the newest version unless you name another with --version.\n" +
|
|
1698
|
+
"Nothing is uploaded: a release is a version with a name on it, so the\n" +
|
|
1699
|
+
"version has to exist already.\n\n" +
|
|
1700
|
+
"The same thing happens when a git tag is pushed to a CodeRook remote —\n" +
|
|
1701
|
+
"`git push cbx v1.0` and `cbx release v1.0` are one action.",
|
|
1702
|
+
options: [
|
|
1703
|
+
{ flags: "--version <n>", description: "name this version instead of the newest" },
|
|
1704
|
+
{ flags: "--notes <text>", description: "what changed, for the release page" },
|
|
1705
|
+
],
|
|
1706
|
+
examples: [
|
|
1707
|
+
"cbx release v1.0",
|
|
1708
|
+
'cbx release v1.2 --version 41 --notes "Fixes the export dialog"',
|
|
1709
|
+
],
|
|
1710
|
+
run: service_commands_js_1.commandRelease,
|
|
1711
|
+
},
|
|
1476
1712
|
{
|
|
1477
1713
|
name: "actions",
|
|
1478
1714
|
aliases: ["workflows"],
|
|
@@ -1480,7 +1716,7 @@ const SPECS = [
|
|
|
1480
1716
|
summary: "the automations a project has",
|
|
1481
1717
|
usage: "actions [project]",
|
|
1482
1718
|
detail: "Shows what each action runs, what it runs on, and how many of its\n" +
|
|
1483
|
-
"runs have passed. Use `
|
|
1719
|
+
"runs have passed. Use `cbx runner` to execute them on this machine.",
|
|
1484
1720
|
run: service_commands_js_1.commandWorkflows,
|
|
1485
1721
|
},
|
|
1486
1722
|
{
|
|
@@ -1497,7 +1733,7 @@ const SPECS = [
|
|
|
1497
1733
|
description: "queued, running, passed, failed or cancelled",
|
|
1498
1734
|
},
|
|
1499
1735
|
],
|
|
1500
|
-
examples: ["
|
|
1736
|
+
examples: ["cbx runs --status failed"],
|
|
1501
1737
|
run: service_commands_js_1.commandRuns,
|
|
1502
1738
|
},
|
|
1503
1739
|
{
|
|
@@ -1508,7 +1744,7 @@ const SPECS = [
|
|
|
1508
1744
|
detail: "The output the runner sent up, in order. Anything the command wrote to\n" +
|
|
1509
1745
|
"stderr is shown in red. Exits 1 when the run failed, so this can be\n" +
|
|
1510
1746
|
"the last line of a script.",
|
|
1511
|
-
examples: ["
|
|
1747
|
+
examples: ["cbx logs 12", "cbx logs #12 my-project"],
|
|
1512
1748
|
run: service_commands_js_1.commandLogs,
|
|
1513
1749
|
},
|
|
1514
1750
|
{
|
|
@@ -1532,7 +1768,7 @@ const SPECS = [
|
|
|
1532
1768
|
{ flags: "--invite <email>", description: "ask somebody to join" },
|
|
1533
1769
|
{ flags: "--role <role>", description: "what they may do (default member)" },
|
|
1534
1770
|
],
|
|
1535
|
-
examples: ["
|
|
1771
|
+
examples: ["cbx people my-project --invite sam@example.com"],
|
|
1536
1772
|
run: service_commands_js_1.commandCollaborators,
|
|
1537
1773
|
},
|
|
1538
1774
|
{
|
|
@@ -1617,7 +1853,7 @@ const SPECS = [
|
|
|
1617
1853
|
{ flags: "--labels <a,b>", description: "what kinds of run it answers to" },
|
|
1618
1854
|
{ flags: "--poll <seconds>", description: "how long between asks" },
|
|
1619
1855
|
],
|
|
1620
|
-
examples: ["
|
|
1856
|
+
examples: ["cbx runner my-game --labels windows,signing"],
|
|
1621
1857
|
run: commandRunner,
|
|
1622
1858
|
},
|
|
1623
1859
|
{
|
|
@@ -1641,7 +1877,7 @@ const SPECS = [
|
|
|
1641
1877
|
"repository for everybody who clones it.\n" +
|
|
1642
1878
|
"\n" +
|
|
1643
1879
|
"It teaches the command line rather than the MCP server, because that\n" +
|
|
1644
|
-
"needs no configuration at all. `
|
|
1880
|
+
"needs no configuration at all. `cbx mcp` is still there when a\n" +
|
|
1645
1881
|
"structured connection is wanted.\n",
|
|
1646
1882
|
options: [
|
|
1647
1883
|
{
|
|
@@ -1649,7 +1885,7 @@ const SPECS = [
|
|
|
1649
1885
|
description: "install into this project rather than for you",
|
|
1650
1886
|
},
|
|
1651
1887
|
],
|
|
1652
|
-
examples: ["
|
|
1888
|
+
examples: ["cbx skill", "cbx skill --project"],
|
|
1653
1889
|
run: skill_command_js_1.commandSkill,
|
|
1654
1890
|
},
|
|
1655
1891
|
{
|
|
@@ -1668,11 +1904,11 @@ const SPECS = [
|
|
|
1668
1904
|
"assistant at it and it starts and stops the process itself.\n" +
|
|
1669
1905
|
"\n" +
|
|
1670
1906
|
"Claude Code:\n" +
|
|
1671
|
-
" claude mcp add coderook --
|
|
1907
|
+
" claude mcp add coderook -- cbx mcp\n" +
|
|
1672
1908
|
"\n" +
|
|
1673
1909
|
"Codex, in ~/.codex/config.toml:\n" +
|
|
1674
1910
|
" [mcp_servers.coderook]\n" +
|
|
1675
|
-
" command = '
|
|
1911
|
+
" command = 'cbx'\n" +
|
|
1676
1912
|
" args = ['mcp']\n" +
|
|
1677
1913
|
"\n" +
|
|
1678
1914
|
"Everything it offers reads. It lists projects, versions and files, shows\n" +
|
|
@@ -1682,7 +1918,7 @@ const SPECS = [
|
|
|
1682
1918
|
"\n" +
|
|
1683
1919
|
"A project whose owner has turned off machine reading is refused, in\n" +
|
|
1684
1920
|
"words rather than as a status code.\n",
|
|
1685
|
-
examples: ["claude mcp add coderook --
|
|
1921
|
+
examples: ["claude mcp add coderook -- cbx mcp"],
|
|
1686
1922
|
run: () => (0, mcp_js_1.commandMcp)(VERSION),
|
|
1687
1923
|
},
|
|
1688
1924
|
{
|
|
@@ -1705,7 +1941,7 @@ async function main(argv) {
|
|
|
1705
1941
|
return 0;
|
|
1706
1942
|
}
|
|
1707
1943
|
/*
|
|
1708
|
-
`
|
|
1944
|
+
`cbx help submit` and `cbx submit --help` reach the same page.
|
|
1709
1945
|
People reach for both, and one of them silently doing something else is
|
|
1710
1946
|
the kind of small betrayal that makes a tool feel unreliable.
|
|
1711
1947
|
*/
|