@coderook/cli 0.23.0 → 0.25.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.
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ /*
8
+ The executable half of `git-remote-coderook`.
9
+
10
+ Split from the module that does the work so that module can be imported by a
11
+ test without starting the protocol loop. Merging the two costs nothing to
12
+ write and means every test of a pure function first has to stop a process
13
+ from reading stdin and answering git.
14
+ */
15
+ const node_process_1 = __importDefault(require("node:process"));
16
+ const git_remote_js_1 = require("./git_remote.js");
17
+ (0, git_remote_js_1.main)(node_process_1.default.argv.slice(2))
18
+ .then((code) => node_process_1.default.exit(code))
19
+ .catch((error) => {
20
+ node_process_1.default.stderr.write(`git-remote-coderook: ${error instanceof Error ? error.message : String(error)}\n`);
21
+ node_process_1.default.exit(1);
22
+ });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * What `coderook help` prints.
3
+ * What `cbx help` prints.
4
4
  *
5
5
  * Generated from the registry rather than written out, so a command that
6
6
  * exists is listed and a command that is listed exists. The previous usage
@@ -30,7 +30,7 @@ function renderHelp(registry, version) {
30
30
  const listed = registry.specs.filter((spec) => !spec.deprecatedBy);
31
31
  const column = Math.min(nameColumn(listed), 34);
32
32
  const lines = [
33
- `${bold("coderook")} ${dim(version)} — CodeRook from the command line`,
33
+ `${bold("cbx")} ${dim(version)} — the CodeBox engine, for CodeRook`,
34
34
  "",
35
35
  ];
36
36
  for (const group of registry_js_1.GROUP_ORDER) {
@@ -43,18 +43,18 @@ function renderHelp(registry, version) {
43
43
  }
44
44
  lines.push("");
45
45
  }
46
- lines.push(dim("coderook help <command> what one command does, in full"), dim("CODEROOK_TOKEN is used when set, so automated runs need nothing on disk."), dim("CODEROOK_API_URL points at another service."));
46
+ lines.push(dim("cbx help <command> what one command does, in full"), dim("CODEROOK_TOKEN is used when set, so automated runs need nothing on disk."), dim("CODEROOK_API_URL points at another service."));
47
47
  return lines.join("\n");
48
48
  }
49
49
  function renderCommandHelp(spec) {
50
50
  const lines = [
51
- `${bold("coderook " + spec.name)} — ${spec.summary}`,
51
+ `${bold("cbx " + spec.name)} — ${spec.summary}`,
52
52
  "",
53
53
  bold("Usage"),
54
- ` coderook ${spec.usage}`,
54
+ ` cbx ${spec.usage}`,
55
55
  ];
56
56
  if (spec.aliases?.length) {
57
- lines.push("", bold("Also"), ` ${spec.aliases.map((alias) => `coderook ${alias}`).join(" ")}`);
57
+ lines.push("", bold("Also"), ` ${spec.aliases.map((alias) => `cbx ${alias}`).join(" ")}`);
58
58
  }
59
59
  if (spec.detail) {
60
60
  lines.push("", spec.detail.trim());
@@ -82,7 +82,7 @@ function renderCommandHelp(spec) {
82
82
  function renderUnknown(typed, suggestion) {
83
83
  const lines = [`Unknown command: ${typed}`];
84
84
  if (suggestion)
85
- lines.push(`Did you mean ${accent("coderook " + suggestion)}?`);
86
- lines.push(dim("Run coderook help to see everything."));
85
+ lines.push(`Did you mean ${accent("cbx " + suggestion)}?`);
86
+ lines.push(dim("Run cbx help to see everything."));
87
87
  return lines.join("\n");
88
88
  }
@@ -73,7 +73,7 @@ async function commandLicence(parsed) {
73
73
  console.log(` ${dim(licence.summary)}`);
74
74
  }
75
75
  console.log("");
76
- console.log(`Add one with ${accent("coderook licence MIT")}. ` +
76
+ console.log(`Add one with ${accent("cbx licence MIT")}. ` +
77
77
  dim(`Nothing is written until you name one.`));
78
78
  return 0;
79
79
  }
@@ -98,7 +98,7 @@ async function commandLicence(parsed) {
98
98
  const licence = (0, licences_js_1.licenceById)(wanted);
99
99
  if (!licence) {
100
100
  console.error(red(`No licence called ${wanted}.`));
101
- console.error(`Run ${accent("coderook licence")} to see the list.`);
101
+ console.error(`Run ${accent("cbx licence")} to see the list.`);
102
102
  return 1;
103
103
  }
104
104
  /*
@@ -162,14 +162,14 @@ async function commandIgnoreTemplate(parsed) {
162
162
  console.log(" " + names.slice(at, at + perRow).map((name) => name.padEnd(width)).join(""));
163
163
  }
164
164
  console.log("");
165
- console.log(`Add one with ${accent("coderook ignore --template Python")}. ` +
165
+ console.log(`Add one with ${accent("cbx ignore --template Python")}. ` +
166
166
  dim("Several is normal — a language, an editor, an operating system."));
167
167
  return 0;
168
168
  }
169
169
  const template = ignore_templates_js_1.IGNORE_TEMPLATES[wanted.toLowerCase()];
170
170
  if (!template) {
171
171
  console.error(red(`No rules called ${wanted}.`));
172
- console.error(`Run ${accent("coderook ignore --template list")} to see what there is.`);
172
+ console.error(`Run ${accent("cbx ignore --template list")} to see what there is.`);
173
173
  return 1;
174
174
  }
175
175
  const rules = await (0, worktree_js_1.readRules)(folder);
@@ -185,7 +185,7 @@ async function commandIgnoreTemplate(parsed) {
185
185
  console.log(green(`Added ${template.name} — ${lines.length} rules.`));
186
186
  console.log(dim(` ${node_path_1.default.join(folder, ".gitignore")}`));
187
187
  console.log("");
188
- console.log(dim(`See what it leaves behind with `) + accent("coderook status"));
188
+ console.log(dim(`See what it leaves behind with `) + accent("cbx status"));
189
189
  return 0;
190
190
  }
191
191
  function flagValue(parsed, name) {
@@ -196,7 +196,7 @@ const TOOLS = [
196
196
  throw new Error(`No file "${wanted}" in v${version.sequence}.`);
197
197
  if (file.sourceSize > READ_LIMIT) {
198
198
  return (`${wanted} is ${bytes(file.sourceSize)}, larger than this tool will ` +
199
- `paste into a conversation. Fetch the project with \`coderook get\` ` +
199
+ `paste into a conversation. Fetch the project with \`cbx get\` ` +
200
200
  `and read it from disk.`);
201
201
  }
202
202
  /*
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadRequestFor = uploadRequestFor;
4
+ exports.classifyPublishFailure = classifyPublishFailure;
5
+ /** Turn what a caller has into what the uploader expects. */
6
+ function uploadRequestFor(input) {
7
+ return {
8
+ localPath: input.localPath,
9
+ // Both lists. See the note at the top of this file.
10
+ include: [...input.changed, ...input.deleted],
11
+ deletions: input.deleted,
12
+ message: input.message,
13
+ projectName: input.projectName,
14
+ repositoryId: input.repositoryId,
15
+ baseVersionId: input.baseVersionId,
16
+ ...(input.track ? { track: input.track } : {}),
17
+ ...(input.allowIgnored ? { allowIgnored: true } : {}),
18
+ ...(input.acknowledged ? { acknowledged: true } : {}),
19
+ ...(input.acknowledgedNoLicence ? { acknowledgedNoLicence: true } : {}),
20
+ ...(input.baseVersionId
21
+ ? { expectedHeadVersionId: input.baseVersionId }
22
+ : {}),
23
+ ...(input.known ? { known: input.known } : {}),
24
+ };
25
+ }
26
+ function classifyPublishFailure(error) {
27
+ const code = error?.code;
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ if (code === "merge_required" || code === "track_moved") {
30
+ return { kind: "conflict", message };
31
+ }
32
+ if (!code &&
33
+ /fetch failed|ECONNRESET|socket hang up|network|ETIMEDOUT/i.test(message)) {
34
+ return { kind: "interrupted", message };
35
+ }
36
+ return null;
37
+ }
@@ -7,7 +7,7 @@
7
7
  * command gets added and the text does not mention it, or an option is renamed
8
8
  * and the text still lists the old one. Nobody notices, because nothing checks.
9
9
  *
10
- * Here the description is part of the command. `coderook help` is generated
10
+ * Here the description is part of the command. `cbx help` is generated
11
11
  * from the same rows that decide what runs, so the two cannot disagree — and a
12
12
  * command added without a summary is a type error rather than an omission.
13
13
  */
@@ -45,7 +45,7 @@ const FLUSH_MS = 2_000;
45
45
  async function call(route, options = {}) {
46
46
  const token = await (0, config_js_1.loadToken)();
47
47
  if (!token)
48
- throw new Error("Not signed in. Run: coderook sign-in");
48
+ throw new Error("Not signed in. Run: cbx sign-in");
49
49
  const response = await fetch(`${(0, config_js_1.apiOrigin)()}${route}`, {
50
50
  method: options.method ?? "GET",
51
51
  headers: {
@@ -127,7 +127,7 @@ class LogShipper {
127
127
  *
128
128
  * Running somebody's command on your machine is the deal a runner makes, and
129
129
  * it is stated at every startup. Handing them the token that machine signs in
130
- * with is not: `coderook runner` reads CODEROOK_TOKEN from the environment so
130
+ * with is not: `cbx runner` reads CODEROOK_TOKEN from the environment so
131
131
  * automated runs can supply it, and every child process inherited it. A
132
132
  * workflow set to `env` by anyone with write access printed it straight into
133
133
  * the run log, which read access is enough to see — so a collaborator could
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.commandIssues = commandIssues;
15
+ exports.commandRelease = commandRelease;
15
16
  exports.commandReleases = commandReleases;
16
17
  exports.commandCollaborators = commandCollaborators;
17
18
  exports.commandWatch = commandWatch;
@@ -72,6 +73,55 @@ async function commandIssues(parsed) {
72
73
  return 0;
73
74
  }
74
75
  /** Published releases, newest first. */
76
+ /**
77
+ * Name a version, which is what a release is.
78
+ *
79
+ * The service has had `markRelease` since releases existed, and nothing on
80
+ * this side could reach it — a project could be given a release by the
81
+ * website and by nothing else. That gap is why `git push --tags` needs this:
82
+ * a git tag is a name for a version, so it has to call the same thing a person
83
+ * calls, not a path of its own. A capability git can reach and the CLI cannot
84
+ * would be a feature that exists twice and agrees by luck.
85
+ */
86
+ async function commandRelease(parsed) {
87
+ const name = parsed.positional[0];
88
+ if (!name) {
89
+ console.error(red("Name the release: cbx release v1.0"));
90
+ return 1;
91
+ }
92
+ const project = await (0, project_commands_js_1.resolveProject)(parsed.positional[1]);
93
+ if (!project)
94
+ return 1;
95
+ const all = await (0, api_js_1.versions)(project.id);
96
+ if (!all.length) {
97
+ console.error(red("This project has no versions to release."));
98
+ return 1;
99
+ }
100
+ /*
101
+ The newest version unless one is named. A release usually means "what I
102
+ have just finished", and asking for the number every time would make the
103
+ common case the awkward one.
104
+ */
105
+ const wanted = parsed.flags.get("version");
106
+ const target = typeof wanted === "string"
107
+ ? all.find((version) => String(version.sequence) === wanted.replace(/^v/i, ""))
108
+ : all[0];
109
+ if (!target) {
110
+ console.error(red(`This project has no version ${String(wanted)}.`));
111
+ return 1;
112
+ }
113
+ const notes = parsed.flags.get("notes");
114
+ try {
115
+ await (0, api_js_1.markRelease)(project.id, target.id, name, typeof notes === "string" ? notes : null);
116
+ }
117
+ catch (error) {
118
+ console.error(red(error instanceof Error ? error.message : String(error)));
119
+ return 1;
120
+ }
121
+ console.log(`${green("Released")} ${bold(name)} ${dim("at")} v${target.sequence}` +
122
+ `${dim(" — ")}${target.message.split("\n")[0].slice(0, 48)}`);
123
+ return 0;
124
+ }
75
125
  async function commandReleases(parsed) {
76
126
  const project = await (0, project_commands_js_1.resolveProject)(parsed.positional[0]);
77
127
  if (!project)
@@ -243,7 +293,7 @@ async function commandRuns(parsed) {
243
293
  console.log(` ${dim(aside.join(" · "))}`);
244
294
  }
245
295
  console.log("");
246
- console.log(dim(" Read one with: coderook logs <number>"));
296
+ console.log(dim(" Read one with: cbx logs <number>"));
247
297
  return 0;
248
298
  }
249
299
  /**
@@ -255,7 +305,7 @@ async function commandRuns(parsed) {
255
305
  async function commandLogs(parsed) {
256
306
  const asked = parsed.positional[0];
257
307
  if (!asked) {
258
- console.error(red("Which run? Try: coderook logs 12"));
308
+ console.error(red("Which run? Try: cbx logs 12"));
259
309
  return 1;
260
310
  }
261
311
  const number = Number(asked.replace(/^#/, ""));
@@ -314,7 +364,7 @@ async function commandTokens(parsed) {
314
364
  console.log(` ${token.name.padEnd(26)} ${used.padEnd(16)} ${dim(token.id)}`);
315
365
  }
316
366
  console.log("");
317
- console.log(dim(" Revoke one with: coderook tokens --revoke <id>"));
367
+ console.log(dim(" Revoke one with: cbx tokens --revoke <id>"));
318
368
  return 0;
319
369
  }
320
370
  /**
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * A skill is a file in a folder. The command line is already installed and
11
11
  * already knows where that folder is, so it writes it: no marketplace, no
12
- * clone, no second account. `coderook skill` and the assistant knows what
12
+ * clone, no second account. `cbx skill` and the assistant knows what
13
13
  * CodeRook is and how to drive it from then on, in every project.
14
14
  *
15
15
  * The skill teaches it the command line rather than the MCP server. Both work,
@@ -106,8 +106,8 @@ async function commandSkill(parsed) {
106
106
  console.log(dim("or run it by name — ") + accent("/coderook"));
107
107
  console.log("");
108
108
  console.log(dim("Signed in? ") +
109
- bold("coderook whoami") +
109
+ bold("cbx whoami") +
110
110
  dim(" · if not: ") +
111
- bold("coderook sign-in"));
111
+ bold("cbx sign-in"));
112
112
  return 0;
113
113
  }
@@ -88,7 +88,7 @@ async function commandTracks(parsed) {
88
88
  console.log(bold("Waiting on a decision"));
89
89
  for (const track of merges)
90
90
  console.log(` ${describe(track, current)}`);
91
- console.log(dim(" Finish one with ") + "coderook merge");
91
+ console.log(dim(" Finish one with ") + "cbx merge");
92
92
  }
93
93
  console.log("");
94
94
  console.log(dim("This folder saves to ") + accent(current));
@@ -100,20 +100,20 @@ async function commandTracks(parsed) {
100
100
  * Switching is a statement about where the next save goes and nothing else:
101
101
  * no files move and nothing is fetched, which is why it is instant and why
102
102
  * it is safe to change your mind. Getting the other line's files is
103
- * `coderook get`, deliberately a separate act.
103
+ * `cbx get`, deliberately a separate act.
104
104
  */
105
105
  async function commandTrack(parsed) {
106
106
  const folder = node_process_1.default.cwd();
107
107
  const link = await (0, config_js_1.readLink)(folder);
108
108
  if (!link) {
109
- console.error(red("This folder is not linked to a project. Run ") + "coderook get" + red(" first."));
109
+ console.error(red("This folder is not linked to a project. Run ") + "cbx get" + red(" first."));
110
110
  return 1;
111
111
  }
112
112
  const wanted = parsed.positional[0];
113
113
  const current = link.track?.trim() || exports.DEFAULT_TRACK;
114
114
  if (!wanted) {
115
115
  console.log(accent(current));
116
- console.log(dim("Switch with ") + "coderook track <name>");
116
+ console.log(dim("Switch with ") + "cbx track <name>");
117
117
  return 0;
118
118
  }
119
119
  const starting = parsed.flags.has("new") || parsed.flags.has("n");
@@ -130,12 +130,12 @@ async function commandTrack(parsed) {
130
130
  work somewhere nobody will look for it.
131
131
  */
132
132
  console.error(red(`No line called "${wanted}" on this project.`));
133
- console.error(dim("Start one with ") + `coderook track ${wanted} --new`);
133
+ console.error(dim("Start one with ") + `cbx track ${wanted} --new`);
134
134
  return 1;
135
135
  }
136
136
  if (existing?.kind === "merge") {
137
137
  console.error(red(`"${wanted}" is a merge waiting on a decision, not a line to save onto.`));
138
- console.error(dim("Finish it with ") + "coderook merge");
138
+ console.error(dim("Finish it with ") + "cbx merge");
139
139
  return 1;
140
140
  }
141
141
  if (starting) {
@@ -151,7 +151,7 @@ async function commandTrack(parsed) {
151
151
  await (0, config_js_1.writeLink)(folder, { ...link, track: wanted });
152
152
  console.log(dim("This folder now saves to ") + accent(wanted));
153
153
  if (existing) {
154
- console.log(dim("Its files are not here yet — fetch them with ") + `coderook get`);
154
+ console.log(dim("Its files are not here yet — fetch them with ") + `cbx get`);
155
155
  }
156
156
  return 0;
157
157
  }