@coderook/cli 0.23.0 → 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.
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  /**
8
- * CodeRook from the command line.
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
@@ -24,6 +24,7 @@ const api_js_1 = require("./api.js");
24
24
  const registry_js_1 = require("./registry.js");
25
25
  const help_js_1 = require("./help.js");
26
26
  const progress_js_1 = require("./progress.js");
27
+ const publish_js_1 = require("./publish.js");
27
28
  const project_commands_js_1 = require("./project_commands.js");
28
29
  const track_commands_js_1 = require("./track_commands.js");
29
30
  const mcp_js_1 = require("./mcp.js");
@@ -43,7 +44,7 @@ const runner_js_1 = require("./runner.js");
43
44
  const config_js_1 = require("./config.js");
44
45
  /*
45
46
  Read from the package rather than written twice. A hardcoded copy had
46
- already drifted from the published version, which makes `coderook doctor`
47
+ already drifted from the published version, which makes `cbx doctor`
47
48
  worse than useless when working out what somebody is actually running.
48
49
  */
49
50
  const VERSION = (() => {
@@ -297,7 +298,7 @@ async function commandStatus(parsed) {
297
298
  if (stale.length) {
298
299
  console.log(dim(`${stale.length} file${stale.length === 1 ? "" : "s"} in the saved version ` +
299
300
  `${stale.length === 1 ? "is" : "are"} newer than the cop${stale.length === 1 ? "y" : "ies"} here ` +
300
- `— run ${accent("coderook get")} to bring ${stale.length === 1 ? "it" : "them"} down.`));
301
+ `— run ${accent("cbx get")} to bring ${stale.length === 1 ? "it" : "them"} down.`));
301
302
  for (const [path] of stale.slice(0, 10))
302
303
  console.log(dim(` behind ${path}`));
303
304
  }
@@ -328,7 +329,7 @@ async function commandImport(parsed) {
328
329
  if (!url) {
329
330
  console.error(red("Nothing to import from."));
330
331
  console.error("Give the address of a repository, for example:");
331
- console.error(` ${accent("coderook import https://github.com/owner/project")}`);
332
+ console.error(` ${accent("cbx import https://github.com/owner/project")}`);
332
333
  return 1;
333
334
  }
334
335
  if (!(0, import_command_js_1.looksLikeRepositoryUrl)(url)) {
@@ -408,7 +409,7 @@ async function commandImport(parsed) {
408
409
  if (code === 0 && plan.temporary) {
409
410
  console.log(dim(`The working copy stays at ${plan.folder} until you remove it.
410
411
  ` +
411
- `Run ${accent("coderook get " + plan.name)} anywhere to fetch it fresh.`));
412
+ `Run ${accent("cbx get " + plan.name)} anywhere to fetch it fresh.`));
412
413
  }
413
414
  return code;
414
415
  }
@@ -455,7 +456,23 @@ async function commandSubmit(parsed) {
455
456
  const rules = importing
456
457
  ? { shared: "", local: "" }
457
458
  : await (0, worktree_js_1.readRules)(folder);
458
- const files = await (0, worktree_js_1.changedFiles)(folder, rules, baseline);
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");
459
476
  /*
460
477
  An upgraded folder with no materialisation record and something that
461
478
  differs from its recorded version is ambiguous in the one way that
@@ -478,8 +495,8 @@ async function commandSubmit(parsed) {
478
495
  console.error(`${files.length} file${files.length === 1 ? " differs" : "s differ"} here:`);
479
496
  for (const file of files.slice(0, 20))
480
497
  console.error(` ${file.path}`);
481
- console.error(`\nIf that is your work, send it with ${accent("coderook submit --force")}.` +
482
- `\nIf it is an old copy, take theirs with ${accent("coderook get --replace")}.`);
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")}.`);
483
500
  return 1;
484
501
  }
485
502
  }
@@ -493,7 +510,7 @@ async function commandSubmit(parsed) {
493
510
  console.log(behindOn
494
511
  ? `Nothing to submit; your own work is all saved. ${behindOn} file` +
495
512
  `${behindOn === 1 ? "" : "s"} here ${behindOn === 1 ? "is" : "are"} ` +
496
- `behind the saved version — run ${accent("coderook get")}.`
513
+ `behind the saved version — run ${accent("cbx get")}.`
497
514
  : "Nothing to submit; this folder matches the saved version.");
498
515
  return 0;
499
516
  }
@@ -591,18 +608,23 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
591
608
  if (added) {
592
609
  console.log(`${accent("Added an MIT licence")}, so other people may use this.`);
593
610
  console.log(dim(` Change it with `) +
594
- accent("coderook licence <name>") +
611
+ accent("cbx licence <name>") +
595
612
  dim(`, or remove it with `) +
596
- accent("coderook licence none") +
613
+ accent("cbx licence none") +
597
614
  dim("."));
598
615
  console.log("");
599
616
  }
600
617
  const line = progressLine();
601
618
  const uploader = new upload_js_1.Uploader(config_js_1.credentials);
602
- const uploadRequest = {
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)({
603
625
  localPath: folder,
604
- include: files.map((file) => file.path),
605
- deletions: files.filter((file) => file.deleted).map((file) => file.path),
626
+ changed: files.filter((file) => !file.deleted).map((file) => file.path),
627
+ deleted: files.filter((file) => file.deleted).map((file) => file.path),
606
628
  message,
607
629
  /*
608
630
  A folder's name is the right default and the wrong answer for import,
@@ -625,11 +647,8 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
625
647
  would reject the publication and the import would be lossy.
626
648
  */
627
649
  ...(hasFlag(parsed, "allow-ignored") ? { allowIgnored: true } : {}),
628
- ...(link?.baseVersionId
629
- ? { expectedHeadVersionId: link.baseVersionId }
630
- : {}),
631
650
  ...(link ? { known: link.local ?? link.manifest ?? {} } : {}),
632
- };
651
+ });
633
652
  let result;
634
653
  try {
635
654
  const plan = await uploader.plan(uploadRequest, (progress) => {
@@ -660,23 +679,22 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
660
679
  }
661
680
  catch (error) {
662
681
  done(line);
663
- const code = error.code;
664
- const text = error instanceof Error ? error.message : String(error);
665
682
  /*
666
- A connection that fails part way through says nothing about whether
667
- the work was done. It may well have beenthe service records the
668
- attempt, so running the same command again is answered with the
669
- version it already made rather than a second one. Saying so is the
670
- 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.
671
687
  */
672
- if (!code && /fetch failed|ECONNRESET|socket hang up|network|ETIMEDOUT/i.test(text)) {
688
+ const failure = (0, publish_js_1.classifyPublishFailure)(error);
689
+ const text = error instanceof Error ? error.message : String(error);
690
+ if (failure?.kind === "interrupted") {
673
691
  console.error(red(`
674
692
  The connection failed: ${text}`));
675
693
  console.error(`Your work may already have been saved. Run the same command again —` +
676
694
  ` it will not create a second version.`);
677
695
  return 1;
678
696
  }
679
- if (code === "merge_required" || code === "track_moved") {
697
+ if (failure?.kind === "conflict") {
680
698
  /*
681
699
  Somebody else saved to this project first. Everything that did not
682
700
  overlap has already been combined by the service; what is left is a
@@ -684,7 +702,7 @@ The connection failed: ${text}`));
684
702
  at it rather than to try again harder.
685
703
  */
686
704
  console.error(red(error instanceof Error ? error.message : String(error)));
687
- console.error(`\nRun ${accent("coderook get")} to bring the latest version down, then` +
705
+ console.error(`\nRun ${accent("cbx get")} to bring the latest version down, then` +
688
706
  ` submit again. Nothing was changed on your account.`);
689
707
  return 1;
690
708
  }
@@ -704,8 +722,8 @@ The connection failed: ${text}`));
704
722
  for (const conflict of result.mergeTrack.conflicts.slice(0, 20)) {
705
723
  console.log(` ${red(conflict.path)} ${dim(conflict.kind)}`);
706
724
  }
707
- console.log(`\nRun ${accent(`coderook merge ${result.mergeTrack.reference}`)} to decide,` +
708
- ` or ${accent("coderook merges")} to see everything waiting.`);
725
+ console.log(`\nRun ${accent(`cbx merge ${result.mergeTrack.reference}`)} to decide,` +
726
+ ` or ${accent("cbx merges")} to see everything waiting.`);
709
727
  return 0;
710
728
  }
711
729
  /*
@@ -714,9 +732,29 @@ The connection failed: ${text}`));
714
732
  attempt name exists to make survivable.
715
733
  */
716
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
+ }
717
755
  await (0, config_js_1.writeLink)(folder, {
718
756
  repositoryId: result.repositoryId,
719
- slug: link?.slug ?? node_path_1.default.basename(folder),
757
+ slug: recordedSlug ?? uploadRequest.projectName ?? node_path_1.default.basename(folder),
720
758
  sequence: result.sequence,
721
759
  // What this folder is now working from, so the next submit can say so.
722
760
  versionId: result.versionId,
@@ -815,8 +853,8 @@ async function commandGet(parsed) {
815
853
  console.error(red("This folder was connected by an older version of CodeRook,"));
816
854
  console.error("which did not record what it had received, so changed files here");
817
855
  console.error("cannot be told apart from copies left behind by a merge.\n");
818
- console.error(`Save them with ${accent("coderook submit")} if they are your work, or ` +
819
- `take the\nsaved version exactly with ${accent("coderook get --replace")}.`);
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")}.`);
820
858
  return 1;
821
859
  }
822
860
  }
@@ -853,8 +891,8 @@ async function commandGet(parsed) {
853
891
  for (const file of changedInTheGap.slice(0, 20))
854
892
  console.error(` ${file.path}`);
855
893
  console.error(`
856
- Save them with ${accent("coderook submit")}, or finish the fetch and ` +
857
- `discard them with ${accent("coderook get --replace")}.`);
894
+ Save them with ${accent("cbx submit")}, or finish the fetch and ` +
895
+ `discard them with ${accent("cbx get --replace")}.`);
858
896
  return 1;
859
897
  }
860
898
  /*
@@ -874,8 +912,8 @@ Save them with ${accent("coderook submit")}, or finish the fetch and ` +
874
912
  console.error(` ${file.path}`);
875
913
  if (atRisk.length > 20)
876
914
  console.error(dim(` …and ${atRisk.length - 20} more`));
877
- console.error(`\nSave them first with ${accent("coderook submit")}, or discard them ` +
878
- `with ${accent("coderook get --replace")}.`);
915
+ console.error(`\nSave them first with ${accent("cbx submit")}, or discard them ` +
916
+ `with ${accent("cbx get --replace")}.`);
879
917
  return 1;
880
918
  }
881
919
  }
@@ -890,7 +928,7 @@ Save them with ${accent("coderook submit")}, or finish the fetch and ` +
890
928
  async function commandClone(parsed) {
891
929
  const reference = parsed.positional[0];
892
930
  if (!reference) {
893
- console.error(red("Which project? Try: coderook clone <project>"));
931
+ console.error(red("Which project? Try: cbx clone <project>"));
894
932
  return 1;
895
933
  }
896
934
  const project = await (0, api_js_2.findProject)(reference);
@@ -994,7 +1032,7 @@ async function suggestRules(folder, apply) {
994
1032
  const recommended = suggestions.filter((one) => one.recommended);
995
1033
  if (!apply) {
996
1034
  console.log("");
997
- console.log(dim("Add the + ones with ") + accent("coderook ignore --suggest --apply"));
1035
+ console.log(dim("Add the + ones with ") + accent("cbx ignore --suggest --apply"));
998
1036
  return 0;
999
1037
  }
1000
1038
  if (!recommended.length) {
@@ -1056,7 +1094,7 @@ async function commandBundle(parsed) {
1056
1094
  async function commandUnbundle(parsed) {
1057
1095
  const source = parsed.positional[0];
1058
1096
  if (!source) {
1059
- console.error(red("Which bundle? Try: coderook unbundle <file.cbx> [folder]"));
1097
+ console.error(red("Which bundle? Try: cbx unbundle <file.cbx> [folder]"));
1060
1098
  return 1;
1061
1099
  }
1062
1100
  const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
@@ -1073,7 +1111,7 @@ async function commandUnbundle(parsed) {
1073
1111
  async function commandInspect(parsed) {
1074
1112
  const source = parsed.positional[0];
1075
1113
  if (!source) {
1076
- console.error(red("Which bundle? Try: coderook inspect <file.cbx>"));
1114
+ console.error(red("Which bundle? Try: cbx inspect <file.cbx>"));
1077
1115
  return 1;
1078
1116
  }
1079
1117
  const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
@@ -1090,7 +1128,7 @@ async function commandInspect(parsed) {
1090
1128
  }
1091
1129
  async function commandDoctor() {
1092
1130
  const service = await (0, api_js_2.health)().catch(() => null);
1093
- console.log(`CodeRook CLI ${VERSION} · Node ${node_process_1.default.versions.node} · ${node_process_1.default.platform}`);
1131
+ console.log(`cbx ${VERSION} · Node ${node_process_1.default.versions.node} · ${node_process_1.default.platform}`);
1094
1132
  console.log(`Config: ${(0, config_js_1.configDirectory)()}`);
1095
1133
  console.log(service
1096
1134
  ? `Service: ${service.status} · schema ${service.schemaVersion}` +
@@ -1134,7 +1172,7 @@ async function commandMerges(parsed) {
1134
1172
  console.log(`${accent(merge.reference)} ${counts ? `${counts.unresolved} of ${counts.total} still to decide` : ""} ${dim(new Date(merge.createdAt).toLocaleString())}`);
1135
1173
  }
1136
1174
  console.log(dim(`
1137
- Run coderook merge <reference> to look at one.`));
1175
+ Run cbx merge <reference> to look at one.`));
1138
1176
  return 0;
1139
1177
  }
1140
1178
  /** Find a merge by the reference a person would type, such as M-2. */
@@ -1157,7 +1195,7 @@ async function findMerge(folder, reference) {
1157
1195
  async function commandMerge(parsed) {
1158
1196
  const reference = parsed.positional[0];
1159
1197
  if (!reference) {
1160
- console.error(red("Which merge? Try: coderook merge M-1"));
1198
+ console.error(red("Which merge? Try: cbx merge M-1"));
1161
1199
  return 1;
1162
1200
  }
1163
1201
  const folder = folderFor({ ...parsed, positional: parsed.positional.slice(1) });
@@ -1216,7 +1254,7 @@ ${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
1216
1254
  const applied = await (0, api_js_1.applyMerge)(summary.id);
1217
1255
  console.log(`
1218
1256
  Applied as ${accent(`v${applied.version.sequence}`)}.`);
1219
- console.log(dim("Run coderook get to bring it down to this folder."));
1257
+ console.log(dim("Run cbx get to bring it down to this folder."));
1220
1258
  return 0;
1221
1259
  }
1222
1260
  if (!decision) {
@@ -1227,7 +1265,7 @@ Add --path <file> for one file, then --apply when ready.`));
1227
1265
  }
1228
1266
  else if (now.provisional.ready) {
1229
1267
  console.log(dim(`
1230
- Run coderook merge ${now.mergeTrack.reference} --apply to publish it.`));
1268
+ Run cbx merge ${now.mergeTrack.reference} --apply to publish it.`));
1231
1269
  }
1232
1270
  return 0;
1233
1271
  }
@@ -1247,7 +1285,7 @@ Run coderook merge ${now.mergeTrack.reference} --apply to publish it.`));
1247
1285
  async function commandRunner(parsed) {
1248
1286
  const reference = parsed.positional[0] ?? flagText(parsed, "project", "p");
1249
1287
  if (!reference) {
1250
- console.error(red("Which project? coderook runner <project>"));
1288
+ console.error(red("Which project? cbx runner <project>"));
1251
1289
  return 1;
1252
1290
  }
1253
1291
  const project = await (0, api_js_2.findProject)(reference);
@@ -1393,7 +1431,7 @@ const SPECS = [
1393
1431
  },
1394
1432
  {
1395
1433
  name: "submit",
1396
- aliases: ["publish"],
1434
+ aliases: ["publish", "push"],
1397
1435
  group: "Working with a folder",
1398
1436
  summary: "send the changes as a new version",
1399
1437
  usage: 'submit [folder] -m "…"',
@@ -1411,6 +1449,10 @@ const SPECS = [
1411
1449
  flags: "--allow-secrets",
1412
1450
  description: "send files that look like credentials, and files with keys inside",
1413
1451
  },
1452
+ {
1453
+ flags: "--sync",
1454
+ description: "treat a file that is no longer here as deleted, rather than keeping the saved copy",
1455
+ },
1414
1456
  {
1415
1457
  flags: "--track <name>",
1416
1458
  description: "save onto this line, just this once",
@@ -1421,8 +1463,8 @@ const SPECS = [
1421
1463
  },
1422
1464
  ],
1423
1465
  examples: [
1424
- 'coderook submit -m "Fix the export dialog"',
1425
- 'coderook submit --track spike -m "Try the other encoder"',
1466
+ 'cbx submit -m "Fix the export dialog"',
1467
+ 'cbx submit --track spike -m "Try the other encoder"',
1426
1468
  ],
1427
1469
  run: commandSubmit,
1428
1470
  },
@@ -1453,8 +1495,8 @@ const SPECS = [
1453
1495
  },
1454
1496
  ],
1455
1497
  examples: [
1456
- "coderook import https://github.com/owner/project",
1457
- "coderook import git@github.com:owner/project.git ./project",
1498
+ "cbx import https://github.com/owner/project",
1499
+ "cbx import git@github.com:owner/project.git ./project",
1458
1500
  ],
1459
1501
  run: commandImport,
1460
1502
  },
@@ -1465,14 +1507,14 @@ const SPECS = [
1465
1507
  different lines, which is most of the point of having them.
1466
1508
  */
1467
1509
  name: "track",
1468
- aliases: ["switch"],
1510
+ aliases: ["switch", "checkout"],
1469
1511
  group: "Working with a folder",
1470
1512
  summary: "show or change the line this folder saves to",
1471
1513
  usage: "track [name]",
1472
1514
  detail: "With no name, prints the line this folder saves to. With one, switches\n" +
1473
1515
  "to it. Switching says where the next save goes and nothing else: no\n" +
1474
1516
  "files move and nothing is fetched, so it is instant and safe to change\n" +
1475
- "your mind. Run `coderook get` afterwards to bring that line's files\n" +
1517
+ "your mind. Run `cbx get` afterwards to bring that line's files\n" +
1476
1518
  "into the folder.\n\n" +
1477
1519
  "A name that does not exist is refused rather than created, because a\n" +
1478
1520
  "typo in a branch name is an ordinary thing to do and a line called\n" +
@@ -1481,23 +1523,25 @@ const SPECS = [
1481
1523
  options: [
1482
1524
  { flags: "-n, --new", description: "start this line from where the project is now" },
1483
1525
  ],
1484
- examples: ["coderook track", "coderook track spike --new", "coderook track main"],
1526
+ examples: ["cbx track", "cbx track spike --new", "cbx track main"],
1485
1527
  run: track_commands_js_1.commandTrack,
1486
1528
  },
1487
1529
  {
1488
1530
  name: "tracks",
1531
+ aliases: ["branch", "branches"],
1489
1532
  group: "Your projects",
1490
1533
  summary: "the lines a project has, and any waiting on a decision",
1491
1534
  usage: "tracks [project]",
1492
1535
  detail: "Lists every line on the project, marking the one this folder saves to.\n" +
1493
1536
  "Merges waiting on a decision are listed beside them rather than hidden,\n" +
1494
1537
  "because somebody looking for where they can save needs to see the one\n" +
1495
- "they cannot. Finish one with `coderook merge`.",
1496
- examples: ["coderook tracks", "coderook tracks my-project"],
1538
+ "they cannot. Finish one with `cbx merge`.",
1539
+ examples: ["cbx tracks", "cbx tracks my-project"],
1497
1540
  run: track_commands_js_1.commandTracks,
1498
1541
  },
1499
1542
  {
1500
1543
  name: "get",
1544
+ aliases: ["pull"],
1501
1545
  group: "Working with a folder",
1502
1546
  summary: "bring this folder up to date",
1503
1547
  usage: "get [folder]",
@@ -1540,7 +1584,7 @@ const SPECS = [
1540
1584
  description: "replace a licence that is already there",
1541
1585
  },
1542
1586
  ],
1543
- examples: ["coderook licence", "coderook licence MIT", "coderook licence Apache-2.0"],
1587
+ examples: ["cbx licence", "cbx licence MIT", "cbx licence Apache-2.0"],
1544
1588
  run: licence_commands_js_1.commandLicence,
1545
1589
  },
1546
1590
  {
@@ -1587,6 +1631,7 @@ const SPECS = [
1587
1631
  },
1588
1632
  {
1589
1633
  name: "versions",
1634
+ aliases: ["log"],
1590
1635
  group: "Your projects",
1591
1636
  summary: "what has been saved to a project",
1592
1637
  usage: "versions [project]",
@@ -1612,9 +1657,9 @@ const SPECS = [
1612
1657
  { flags: "--request [off]", description: "automated calls to its endpoints" },
1613
1658
  ],
1614
1659
  examples: [
1615
- "coderook ai my-project",
1616
- "coderook ai my-project --read off",
1617
- "coderook ai my-project --download off --request off",
1660
+ "cbx ai my-project",
1661
+ "cbx ai my-project --read off",
1662
+ "cbx ai my-project --download off --request off",
1618
1663
  ],
1619
1664
  run: project_commands_js_1.commandAi,
1620
1665
  },
@@ -1627,16 +1672,43 @@ const SPECS = [
1627
1672
  { flags: '--new "<title>"', description: "open a new issue" },
1628
1673
  { flags: "--body <text>", description: "the description for a new one" },
1629
1674
  ],
1630
- examples: ['coderook issues my-project --new "Crash on export"'],
1675
+ examples: ['cbx issues my-project --new "Crash on export"'],
1631
1676
  run: service_commands_js_1.commandIssues,
1632
1677
  },
1633
1678
  {
1634
1679
  name: "releases",
1680
+ aliases: ["tags"],
1635
1681
  group: "Your projects",
1636
1682
  summary: "what has been released, and its files",
1637
1683
  usage: "releases [project]",
1638
1684
  run: service_commands_js_1.commandReleases,
1639
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
+ },
1640
1712
  {
1641
1713
  name: "actions",
1642
1714
  aliases: ["workflows"],
@@ -1644,7 +1716,7 @@ const SPECS = [
1644
1716
  summary: "the automations a project has",
1645
1717
  usage: "actions [project]",
1646
1718
  detail: "Shows what each action runs, what it runs on, and how many of its\n" +
1647
- "runs have passed. Use `coderook runner` to execute them on this machine.",
1719
+ "runs have passed. Use `cbx runner` to execute them on this machine.",
1648
1720
  run: service_commands_js_1.commandWorkflows,
1649
1721
  },
1650
1722
  {
@@ -1661,7 +1733,7 @@ const SPECS = [
1661
1733
  description: "queued, running, passed, failed or cancelled",
1662
1734
  },
1663
1735
  ],
1664
- examples: ["coderook runs --status failed"],
1736
+ examples: ["cbx runs --status failed"],
1665
1737
  run: service_commands_js_1.commandRuns,
1666
1738
  },
1667
1739
  {
@@ -1672,7 +1744,7 @@ const SPECS = [
1672
1744
  detail: "The output the runner sent up, in order. Anything the command wrote to\n" +
1673
1745
  "stderr is shown in red. Exits 1 when the run failed, so this can be\n" +
1674
1746
  "the last line of a script.",
1675
- examples: ["coderook logs 12", "coderook logs #12 my-project"],
1747
+ examples: ["cbx logs 12", "cbx logs #12 my-project"],
1676
1748
  run: service_commands_js_1.commandLogs,
1677
1749
  },
1678
1750
  {
@@ -1696,7 +1768,7 @@ const SPECS = [
1696
1768
  { flags: "--invite <email>", description: "ask somebody to join" },
1697
1769
  { flags: "--role <role>", description: "what they may do (default member)" },
1698
1770
  ],
1699
- examples: ["coderook people my-project --invite sam@example.com"],
1771
+ examples: ["cbx people my-project --invite sam@example.com"],
1700
1772
  run: service_commands_js_1.commandCollaborators,
1701
1773
  },
1702
1774
  {
@@ -1781,7 +1853,7 @@ const SPECS = [
1781
1853
  { flags: "--labels <a,b>", description: "what kinds of run it answers to" },
1782
1854
  { flags: "--poll <seconds>", description: "how long between asks" },
1783
1855
  ],
1784
- examples: ["coderook runner my-game --labels windows,signing"],
1856
+ examples: ["cbx runner my-game --labels windows,signing"],
1785
1857
  run: commandRunner,
1786
1858
  },
1787
1859
  {
@@ -1805,7 +1877,7 @@ const SPECS = [
1805
1877
  "repository for everybody who clones it.\n" +
1806
1878
  "\n" +
1807
1879
  "It teaches the command line rather than the MCP server, because that\n" +
1808
- "needs no configuration at all. `coderook mcp` is still there when a\n" +
1880
+ "needs no configuration at all. `cbx mcp` is still there when a\n" +
1809
1881
  "structured connection is wanted.\n",
1810
1882
  options: [
1811
1883
  {
@@ -1813,7 +1885,7 @@ const SPECS = [
1813
1885
  description: "install into this project rather than for you",
1814
1886
  },
1815
1887
  ],
1816
- examples: ["coderook skill", "coderook skill --project"],
1888
+ examples: ["cbx skill", "cbx skill --project"],
1817
1889
  run: skill_command_js_1.commandSkill,
1818
1890
  },
1819
1891
  {
@@ -1832,11 +1904,11 @@ const SPECS = [
1832
1904
  "assistant at it and it starts and stops the process itself.\n" +
1833
1905
  "\n" +
1834
1906
  "Claude Code:\n" +
1835
- " claude mcp add coderook -- coderook mcp\n" +
1907
+ " claude mcp add coderook -- cbx mcp\n" +
1836
1908
  "\n" +
1837
1909
  "Codex, in ~/.codex/config.toml:\n" +
1838
1910
  " [mcp_servers.coderook]\n" +
1839
- " command = 'coderook'\n" +
1911
+ " command = 'cbx'\n" +
1840
1912
  " args = ['mcp']\n" +
1841
1913
  "\n" +
1842
1914
  "Everything it offers reads. It lists projects, versions and files, shows\n" +
@@ -1846,7 +1918,7 @@ const SPECS = [
1846
1918
  "\n" +
1847
1919
  "A project whose owner has turned off machine reading is refused, in\n" +
1848
1920
  "words rather than as a status code.\n",
1849
- examples: ["claude mcp add coderook -- coderook mcp"],
1921
+ examples: ["claude mcp add coderook -- cbx mcp"],
1850
1922
  run: () => (0, mcp_js_1.commandMcp)(VERSION),
1851
1923
  },
1852
1924
  {
@@ -1869,7 +1941,7 @@ async function main(argv) {
1869
1941
  return 0;
1870
1942
  }
1871
1943
  /*
1872
- `coderook help submit` and `coderook submit --help` reach the same page.
1944
+ `cbx help submit` and `cbx submit --help` reach the same page.
1873
1945
  People reach for both, and one of them silently doing something else is
1874
1946
  the kind of small betrayal that makes a tool feel unreliable.
1875
1947
  */