@coderook/cli 0.34.2 → 0.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "name": "coderook",
3
3
  "displayName": "CodeRook",
4
4
  "description": "Save, browse and restore whole-snapshot versions of a project on CodeRook, with cbx or plain git, from Claude Code.",
5
- "version": "0.34.2",
5
+ "version": "0.34.3",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
@@ -36,6 +36,7 @@ exports.createIssue = createIssue;
36
36
  exports.releases = releases;
37
37
  exports.collaborators = collaborators;
38
38
  exports.invite = invite;
39
+ exports.acceptInvite = acceptInvite;
39
40
  exports.watch = watch;
40
41
  exports.setWatch = setWatch;
41
42
  exports.workflows = workflows;
@@ -688,9 +689,14 @@ async function collaborators(repositoryId) {
688
689
  };
689
690
  }
690
691
  async function invite(repositoryId, input) {
691
- await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/invites`, {
692
+ const row = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/invites`, { method: "POST", body: input });
693
+ return typeof row?.inviteUrl === "string" ? { inviteUrl: row.inviteUrl } : {};
694
+ }
695
+ /** Join a project from an invite link (or the token inside it). */
696
+ async function acceptInvite(token) {
697
+ return call("/v1/invites/accept", {
692
698
  method: "POST",
693
- body: input,
699
+ body: { token },
694
700
  });
695
701
  }
696
702
  async function watch(repositoryId) {
@@ -28,11 +28,7 @@ const api_js_1 = require("./api.js");
28
28
  const project_commands_js_1 = require("./project_commands.js");
29
29
  const version_commands_js_1 = require("./version_commands.js");
30
30
  const api_js_2 = require("./api.js");
31
- const dim = (value) => `${value}`;
32
- const bold = (value) => `${value}`;
33
- const red = (value) => `${value}`;
34
- const green = (value) => `${value}`;
35
- const accent = (value) => `${value}`;
31
+ const colour_js_1 = require("./colour.js");
36
32
  /** What the service will take in one request. */
37
33
  const DIRECT_LIMIT = 95 * 1024 * 1024;
38
34
  /** Comfortably under the cap, so one slow part does not hold the rest up. */
@@ -114,7 +110,7 @@ async function sendFile(repositoryId, file, size, sha256, mediaType, say) {
114
110
  repositoryRole: "chunk",
115
111
  });
116
112
  const parts = Math.ceil(size / PART_SIZE);
117
- say(dim(` ${readable(size)} in ${parts} parts`));
113
+ say((0, colour_js_1.dim)(` ${readable(size)} in ${parts} parts`));
118
114
  const handle = await (0, promises_1.open)(file, "r");
119
115
  try {
120
116
  for (let index = 0; index < parts; index += 1) {
@@ -123,7 +119,7 @@ async function sendFile(repositoryId, file, size, sha256, mediaType, say) {
123
119
  const buffer = Buffer.alloc(length);
124
120
  await handle.read(buffer, 0, length, start);
125
121
  await (0, api_js_1.uploadPart)(session.uploadSessionId, index + 1, buffer);
126
- say(dim(` part ${index + 1}/${parts}`));
122
+ say((0, colour_js_1.dim)(` part ${index + 1}/${parts}`));
127
123
  }
128
124
  }
129
125
  finally {
@@ -134,24 +130,24 @@ async function sendFile(repositoryId, file, size, sha256, mediaType, say) {
134
130
  async function commandAttach(parsed) {
135
131
  const file = parsed.positional[0];
136
132
  if (!file) {
137
- console.error(red("Which file?") + dim(" cbx attach ./release/Installer.exe"));
133
+ console.error((0, colour_js_1.red)("Which file?") + (0, colour_js_1.dim)(" cbx attach ./release/Installer.exe"));
138
134
  return 1;
139
135
  }
140
136
  let size;
141
137
  try {
142
138
  const info = await (0, promises_1.stat)(file);
143
139
  if (!info.isFile()) {
144
- console.error(red(`${file} is not a file.`));
140
+ console.error((0, colour_js_1.red)(`${file} is not a file.`));
145
141
  return 1;
146
142
  }
147
143
  size = info.size;
148
144
  }
149
145
  catch {
150
- console.error(red(`There is no file at ${file}.`));
146
+ console.error((0, colour_js_1.red)(`There is no file at ${file}.`));
151
147
  return 1;
152
148
  }
153
149
  if (size === 0) {
154
- console.error(red("That file is empty."));
150
+ console.error((0, colour_js_1.red)("That file is empty."));
155
151
  return 1;
156
152
  }
157
153
  /*
@@ -168,26 +164,26 @@ async function commandAttach(parsed) {
168
164
  return 1;
169
165
  const all = await (0, api_js_2.versions)(project.id);
170
166
  if (!all.length) {
171
- console.error(red("This project has no versions yet."));
167
+ console.error((0, colour_js_1.red)("This project has no versions yet."));
172
168
  return 1;
173
169
  }
174
170
  const target = which.n
175
171
  ? all.find((one) => String(one.sequence) === which.n.replace(/^v/i, ""))
176
172
  : all.reduce((newest, one) => (one.sequence > newest.sequence ? one : newest));
177
173
  if (!target) {
178
- console.error(red(`This project has no version ${which.n}.`));
174
+ console.error((0, colour_js_1.red)(`This project has no version ${which.n}.`));
179
175
  return 1;
180
176
  }
181
177
  const named = parsed.flags.get("name");
182
178
  const name = typeof named === "string" && named.trim() ? named.trim() : node_path_1.default.basename(file);
183
- console.log(`${bold(name)} ${dim(`(${readable(size)})`)} → ${accent(`v${target.sequence}`)} of ${project.name}`);
179
+ console.log(`${(0, colour_js_1.bold)(name)} ${(0, colour_js_1.dim)(`(${readable(size)})`)} → ${(0, colour_js_1.accent)(`v${target.sequence}`)} of ${project.name}`);
184
180
  let objectId;
185
181
  try {
186
182
  const sha256 = await digestOf(file);
187
183
  objectId = await sendFile(project.id, file, size, sha256, mediaTypeOf(name), (text) => console.log(text));
188
184
  }
189
185
  catch (error) {
190
- console.error(red(error instanceof Error ? error.message : String(error)));
186
+ console.error((0, colour_js_1.red)(error instanceof Error ? error.message : String(error)));
191
187
  return 1;
192
188
  }
193
189
  try {
@@ -195,23 +191,23 @@ async function commandAttach(parsed) {
195
191
  }
196
192
  catch (error) {
197
193
  const message = error instanceof Error ? error.message : String(error);
198
- console.error(red(message));
194
+ console.error((0, colour_js_1.red)(message));
199
195
  if (/already has something called/i.test(message)) {
200
- console.error(dim(" Use --name to give this one a different name on the version."));
196
+ console.error((0, colour_js_1.dim)(" Use --name to give this one a different name on the version."));
201
197
  }
202
198
  return 1;
203
199
  }
204
- console.log(green(`Attached. It is a download on v${target.sequence}.`));
200
+ console.log((0, colour_js_1.green)(`Attached. It is a download on v${target.sequence}.`));
205
201
  if (!target.name) {
206
202
  /*
207
203
  Said because it is the difference between attaching and publishing. A
208
204
  commit is not offered to anybody, so the download exists and nobody
209
205
  outside the project can reach it yet.
210
206
  */
211
- console.log(dim(" v") +
212
- dim(String(target.sequence)) +
213
- dim(" is a commit, so nobody outside the project can take it yet."));
214
- console.log(dim(` Make it a version: cbx mark ${target.sequence} --name v1.0`));
207
+ console.log((0, colour_js_1.dim)(" v") +
208
+ (0, colour_js_1.dim)(String(target.sequence)) +
209
+ (0, colour_js_1.dim)(" is a commit, so nobody outside the project can take it yet."));
210
+ console.log((0, colour_js_1.dim)(` Make it a version: cbx mark ${target.sequence} --name v1.0`));
215
211
  }
216
212
  return 0;
217
213
  }
@@ -73,6 +73,10 @@ const bold = (text) => (colour ? `${text}` : text);
73
73
  const accent = (text) => (colour ? `${text}` : text);
74
74
  const red = (text) => (colour ? `${text}` : text);
75
75
  const green = (text) => (colour ? `${text}` : text);
76
+ /** "1 file", "3 files". */
77
+ function fileCount(count) {
78
+ return `${count} ${count === 1 ? "file" : "files"}`;
79
+ }
76
80
  function bytes(value) {
77
81
  if (value >= 1024 ** 3)
78
82
  return `${(value / 1024 ** 3).toFixed(2)} GB`;
@@ -288,7 +292,7 @@ async function reconcile(folder, options = {}) {
288
292
  await (0, config_js_1.writeLink)(folder, known);
289
293
  }
290
294
  if (behind && !options.quiet) {
291
- console.log(dim(`The account is on v${behind.sequence}; this folder is based on v${known.sequence}.`));
295
+ console.log(dim(`The project is on v${behind.sequence}; this folder is based on v${known.sequence}.`));
292
296
  }
293
297
  return {
294
298
  link: known,
@@ -732,6 +736,23 @@ function noteKeptDeletions(folder, baseline, syncing) {
732
736
  ` (${gone.slice(0, 3).join(", ")}${gone.length > 3 ? ", …" : ""}). A save adds and updates;` +
733
737
  ` run cbx submit --sync to remove ${gone.length === 1 ? "it" : "them"}.`));
734
738
  }
739
+ /*
740
+ The service names each clash by its kind ("text_region", "add_add", …).
741
+ Those are its words, not the reader's: say what actually happened.
742
+ */
743
+ const CONFLICT_WORDS = {
744
+ text_region: "you both changed the same lines",
745
+ modify_delete: "changed on one side, removed on the other",
746
+ add_add: "added on both sides, differently",
747
+ rename: "renamed differently on each side",
748
+ path_collision: "two files now want the same name",
749
+ binary: "not text, so it can't be combined line by line",
750
+ symlink: "a link both sides changed",
751
+ structured: "both changed the same entry",
752
+ };
753
+ function conflictWords(kind) {
754
+ return CONFLICT_WORDS[kind] ?? kind.replace(/_/g, " ");
755
+ }
735
756
  async function commandSubmit(parsed) {
736
757
  const folder = folderFor(parsed);
737
758
  const message = flagText(parsed, "m", "message") ?? "";
@@ -1114,7 +1135,14 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
1114
1135
  if (ahead.collidingPaths.length > 5) {
1115
1136
  console.log(dim(` and ${ahead.collidingPaths.length - 5} more`));
1116
1137
  }
1117
- console.log(dim(" Your upload will be kept whole and put on a merge for you to settle."));
1138
+ /*
1139
+ This check compares whole files, so it cannot know yet whether the
1140
+ two of you touched the same lines. The service combines text line
1141
+ by line when it can; saying "it will be put on a merge" here was a
1142
+ promise the next line ("Saved v3") then broke.
1143
+ */
1144
+ console.log(dim(" If you changed different lines of a text file, the two combine on their own."));
1145
+ console.log(dim(" Otherwise your upload is kept whole on a merge for you to settle."));
1118
1146
  console.log(dim(" Run cbx get first if you would rather build on theirs."));
1119
1147
  console.log("");
1120
1148
  }
@@ -1238,7 +1266,7 @@ The connection failed: ${text}`));
1238
1266
  : `${result.mergeTrack.conflicts.length} files overlap`}.`);
1239
1267
  console.log(`Your work is complete and kept as ${accent(result.mergeTrack.reference)}:`);
1240
1268
  for (const conflict of result.mergeTrack.conflicts.slice(0, 20)) {
1241
- console.log(` ${red(conflict.path)} ${dim(conflict.kind)}`);
1269
+ console.log(` ${red(conflict.path)} ${dim(conflictWords(conflict.kind))}`);
1242
1270
  }
1243
1271
  console.log(`\nRun ${accent(`cbx merge ${result.mergeTrack.reference}`)} to decide,` +
1244
1272
  ` or ${accent("cbx merges")} to see everything waiting.`);
@@ -1655,7 +1683,7 @@ trackName) {
1655
1683
  });
1656
1684
  // The record is written; the run has not reported success yet.
1657
1685
  (0, faults_js_1.maybeFail)("get:after-record");
1658
- console.log(`${accent(`v${latest.sequence}`)} · ${result.files} files · ${bytes(result.bytes)} into ${destination}`);
1686
+ console.log(`${accent(`v${latest.sequence}`)} · ${fileCount(result.files)} · ${bytes(result.bytes)} into ${destination}`);
1659
1687
  return 0;
1660
1688
  }
1661
1689
  /**
@@ -1789,7 +1817,7 @@ async function commandUnbundle(parsed) {
1789
1817
  dim(progress.path.slice(-40)));
1790
1818
  });
1791
1819
  done(line);
1792
- console.log(`${result.files} files · ${bytes(result.bytes)} into ${destination}`);
1820
+ console.log(`${fileCount(result.files)} · ${bytes(result.bytes)} into ${destination}`);
1793
1821
  return 0;
1794
1822
  }
1795
1823
  async function commandInspect(parsed) {
@@ -1801,7 +1829,7 @@ async function commandInspect(parsed) {
1801
1829
  const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
1802
1830
  const total = manifest.files.reduce((sum, file) => sum + file.size, 0);
1803
1831
  console.log(`${bold(manifest.source_name)} · format ${manifest.format} v${manifest.format_version}`);
1804
- console.log(`${manifest.files.length} files · ${bytes(total)} · ` +
1832
+ console.log(`${fileCount(manifest.files.length)} · ${bytes(total)} · ` +
1805
1833
  `${Object.keys(manifest.chunks).length} chunks`);
1806
1834
  if (hasFlag(parsed, "files")) {
1807
1835
  for (const file of manifest.files) {
@@ -2022,13 +2050,17 @@ async function commandMerge(parsed) {
2022
2050
  }
2023
2051
  }
2024
2052
  console.log(`
2025
- ${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
2053
+ ${accent(now.mergeTrack.reference)} · ` +
2054
+ /*
2055
+ The combined version is only counted once it can be built; until then
2056
+ its file count is 0, which read as "your upload was empty".
2057
+ */
2026
2058
  (now.provisional.ready
2027
- ? "ready to apply"
2059
+ ? `${fileCount(now.provisional.fileCount)} · ready to apply`
2028
2060
  : `${now.provisional.unresolvedPaths.length} still to decide`));
2029
2061
  for (const conflict of now.conflicts) {
2030
2062
  console.log(` ${conflict.resolvedAt ? accent("decided") : red("waiting")} ${conflict.path}` +
2031
- ` ${dim(conflict.kind)}${conflict.resolution ? dim(` (${conflict.resolution})`) : ""}`);
2063
+ ` ${dim(conflictWords(conflict.kind))}${conflict.resolution ? dim(` (${conflict.resolution})`) : ""}`);
2032
2064
  }
2033
2065
  if (hasFlag(parsed, "apply")) {
2034
2066
  if (!now.provisional.ready) {
@@ -2048,7 +2080,7 @@ Applied as ${accent(`v${applied.version.sequence}`)}.`);
2048
2080
  if (!decision && stillOpen) {
2049
2081
  console.log(dim(`
2050
2082
  --mine keeps yours, --theirs keeps what was already saved,` +
2051
- ` --drop removes the file.
2083
+ ` --both keeps theirs and saves yours beside it, --drop removes the file.
2052
2084
  Add --path <file> for one file, then --apply when ready.`));
2053
2085
  }
2054
2086
  else if (now.provisional.ready) {
@@ -3098,9 +3130,13 @@ const SPECS = [
3098
3130
  usage: "people [project]",
3099
3131
  options: [
3100
3132
  { flags: "--invite <email>", description: "ask somebody to join" },
3101
- { flags: "--role <role>", description: "what they may do (default member)" },
3133
+ { flags: "--role <role>", description: "write, read or admin (default write)" },
3134
+ { flags: "--accept <link>", description: "join a project you were invited to" },
3135
+ ],
3136
+ examples: [
3137
+ "cbx people my-project --invite sam@example.com",
3138
+ "cbx people --accept https://coderook.com/?invite=…",
3102
3139
  ],
3103
- examples: ["cbx people my-project --invite sam@example.com"],
3104
3140
  run: service_commands_js_1.commandCollaborators,
3105
3141
  },
3106
3142
  {
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.accent = exports.green = exports.red = exports.bold = exports.dim = void 0;
4
+ /*
5
+ Colour for the command modules, on the same terms as cli.ts: only at a
6
+ terminal, and never when NO_COLOR is set. Each module used to carry its own
7
+ always-on copy, so output piped to a file, or a script asking for plain
8
+ text, still came back full of escape codes.
9
+ */
10
+ const colour = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
11
+ const paint = (code) => (text) => colour ? `[${code}m${text}` : text;
12
+ exports.dim = paint("2");
13
+ exports.bold = paint("1");
14
+ exports.red = paint("31");
15
+ exports.green = paint("32");
16
+ exports.accent = paint("33");
@@ -19,9 +19,7 @@ exports.renderHelp = renderHelp;
19
19
  exports.renderCommandHelp = renderCommandHelp;
20
20
  exports.renderUnknown = renderUnknown;
21
21
  const registry_js_1 = require("./registry.js");
22
- const bold = (value) => `${value}`;
23
- const dim = (value) => `${value}`;
24
- const accent = (value) => `${value}`;
22
+ const colour_js_1 = require("./colour.js");
25
23
  /** Longest visible name, so the summary column lines up across every group. */
26
24
  function nameColumn(specs) {
27
25
  return specs.reduce((widest, spec) => Math.max(widest, spec.usage.length), 0);
@@ -30,46 +28,46 @@ function renderHelp(registry, version) {
30
28
  const listed = registry.specs.filter((spec) => !spec.deprecatedBy);
31
29
  const column = Math.min(nameColumn(listed), 34);
32
30
  const lines = [
33
- `${bold("cbx")} ${dim(version)} — the CodeBox engine, for CodeRook`,
31
+ `${(0, colour_js_1.bold)("cbx")} ${(0, colour_js_1.dim)(version)} — the CodeBox engine, for CodeRook`,
34
32
  "",
35
33
  ];
36
34
  for (const group of registry_js_1.GROUP_ORDER) {
37
35
  const inGroup = listed.filter((spec) => spec.group === group);
38
36
  if (!inGroup.length)
39
37
  continue;
40
- lines.push(bold(group));
38
+ lines.push((0, colour_js_1.bold)(group));
41
39
  for (const spec of inGroup) {
42
40
  lines.push(` ${spec.usage.padEnd(column)} ${spec.summary}`);
43
41
  }
44
42
  lines.push("");
45
43
  }
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."));
44
+ lines.push((0, colour_js_1.dim)("cbx help <command> what one command does, in full"), (0, colour_js_1.dim)("CODEROOK_TOKEN is used when set, so automated runs need nothing on disk."), (0, colour_js_1.dim)("CODEROOK_API_URL points at another service."));
47
45
  return lines.join("\n");
48
46
  }
49
47
  function renderCommandHelp(spec) {
50
48
  const lines = [
51
- `${bold("cbx " + spec.name)} — ${spec.summary}`,
49
+ `${(0, colour_js_1.bold)("cbx " + spec.name)} — ${spec.summary}`,
52
50
  "",
53
- bold("Usage"),
51
+ (0, colour_js_1.bold)("Usage"),
54
52
  ` cbx ${spec.usage}`,
55
53
  ];
56
54
  if (spec.aliases?.length) {
57
- lines.push("", bold("Also"), ` ${spec.aliases.map((alias) => `cbx ${alias}`).join(" ")}`);
55
+ lines.push("", (0, colour_js_1.bold)("Also"), ` ${spec.aliases.map((alias) => `cbx ${alias}`).join(" ")}`);
58
56
  }
59
57
  if (spec.detail) {
60
58
  lines.push("", spec.detail.trim());
61
59
  }
62
60
  if (spec.options?.length) {
63
61
  const width = spec.options.reduce((widest, option) => Math.max(widest, option.flags.length), 0);
64
- lines.push("", bold("Options"));
62
+ lines.push("", (0, colour_js_1.bold)("Options"));
65
63
  for (const option of spec.options) {
66
64
  lines.push(` ${option.flags.padEnd(width)} ${option.description}`);
67
65
  }
68
66
  }
69
67
  if (spec.examples?.length) {
70
- lines.push("", bold("Examples"));
68
+ lines.push("", (0, colour_js_1.bold)("Examples"));
71
69
  for (const example of spec.examples)
72
- lines.push(` ${accent(example)}`);
70
+ lines.push(` ${(0, colour_js_1.accent)(example)}`);
73
71
  }
74
72
  return lines.join("\n");
75
73
  }
@@ -82,7 +80,7 @@ function renderCommandHelp(spec) {
82
80
  function renderUnknown(typed, suggestion) {
83
81
  const lines = [`Unknown command: ${typed}`];
84
82
  if (suggestion)
85
- lines.push(`Did you mean ${accent("cbx " + suggestion)}?`);
86
- lines.push(dim("Run cbx help to see everything."));
83
+ lines.push(`Did you mean ${(0, colour_js_1.accent)("cbx " + suggestion)}?`);
84
+ lines.push((0, colour_js_1.dim)("Run cbx help to see everything."));
87
85
  return lines.join("\n");
88
86
  }
@@ -26,11 +26,7 @@ Object.defineProperty(exports, "licenceNewProject", { enumerable: true, get: fun
26
26
  const ignore_templates_js_1 = require("../../cbx/src/core/ignore_templates.js");
27
27
  const worktree_js_1 = require("../../cbx/src/core/worktree.js");
28
28
  const api_js_1 = require("./api.js");
29
- const dim = (value) => `${value}`;
30
- const bold = (value) => `${value}`;
31
- const red = (value) => `${value}`;
32
- const green = (value) => `${value}`;
33
- const accent = (value) => `${value}`;
29
+ const colour_js_1 = require("./colour.js");
34
30
  const SHAPES = {
35
31
  permissive: "Do what you like, keep the notice",
36
32
  "weak-copyleft": "Changes to these files come back",
@@ -56,25 +52,25 @@ async function commandLicence(parsed) {
56
52
  .then((text) => text.split("\n")[0]?.trim() ?? "")
57
53
  .catch(() => null);
58
54
  if (existing !== null) {
59
- console.log(`${bold("This folder has a licence")} — ${existing}`);
60
- console.log(dim(` ${node_path_1.default.join(folder, licences_js_1.LICENCE_FILE)}`));
55
+ console.log(`${(0, colour_js_1.bold)("This folder has a licence")} — ${existing}`);
56
+ console.log((0, colour_js_1.dim)(` ${node_path_1.default.join(folder, licences_js_1.LICENCE_FILE)}`));
61
57
  console.log("");
62
58
  }
63
- console.log(bold("Licences you can put on this project"));
59
+ console.log((0, colour_js_1.bold)("Licences you can put on this project"));
64
60
  let shape = null;
65
61
  for (const licence of licences_js_1.LICENCES) {
66
62
  if (licence.shape !== shape) {
67
63
  shape = licence.shape;
68
64
  console.log("");
69
- console.log(dim(` ${SHAPES[shape].toUpperCase()}`));
65
+ console.log((0, colour_js_1.dim)(` ${SHAPES[shape].toUpperCase()}`));
70
66
  }
71
- const mark = licence.id === licences_js_1.DEFAULT_LICENCE ? accent(" ·") : " ";
72
- console.log(`${mark} ${bold(licence.id.padEnd(20))} ${licence.name}`);
73
- console.log(` ${dim(licence.summary)}`);
67
+ const mark = licence.id === licences_js_1.DEFAULT_LICENCE ? (0, colour_js_1.accent)(" ·") : " ";
68
+ console.log(`${mark} ${(0, colour_js_1.bold)(licence.id.padEnd(20))} ${licence.name}`);
69
+ console.log(` ${(0, colour_js_1.dim)(licence.summary)}`);
74
70
  }
75
71
  console.log("");
76
- console.log(`Add one with ${accent("cbx licence MIT")}. ` +
77
- dim(`Nothing is written until you name one.`));
72
+ console.log(`Add one with ${(0, colour_js_1.accent)("cbx licence MIT")}. ` +
73
+ (0, colour_js_1.dim)(`Nothing is written until you name one.`));
78
74
  return 0;
79
75
  }
80
76
  /*
@@ -90,15 +86,15 @@ async function commandLicence(parsed) {
90
86
  return 0;
91
87
  }
92
88
  await (0, promises_1.rm)(target);
93
- console.log(green(`Removed ${licences_js_1.LICENCE_FILE}.`));
94
- console.log(dim("With no licence, nobody else may legally use this — which is a"));
95
- console.log(dim("position you can hold, as long as you are holding it on purpose."));
89
+ console.log((0, colour_js_1.green)(`Removed ${licences_js_1.LICENCE_FILE}.`));
90
+ console.log((0, colour_js_1.dim)("With no licence, nobody else may legally use this — which is a"));
91
+ console.log((0, colour_js_1.dim)("position you can hold, as long as you are holding it on purpose."));
96
92
  return 0;
97
93
  }
98
94
  const licence = (0, licences_js_1.licenceById)(wanted);
99
95
  if (!licence) {
100
- console.error(red(`No licence called ${wanted}.`));
101
- console.error(`Run ${accent("cbx licence")} to see the list.`);
96
+ console.error((0, colour_js_1.red)(`No licence called ${wanted}.`));
97
+ console.error(`Run ${(0, colour_js_1.accent)("cbx licence")} to see the list.`);
102
98
  return 1;
103
99
  }
104
100
  /*
@@ -125,13 +121,13 @@ async function commandLicence(parsed) {
125
121
  });
126
122
  if (!holder) {
127
123
  const reason = failure instanceof Error ? failure.message : "";
128
- console.error(red("Could not work out whose name goes on the licence."));
124
+ console.error((0, colour_js_1.red)("Could not work out whose name goes on the licence."));
129
125
  if (/sign-in|not signed in|not accepted/i.test(reason) || !failure) {
130
- console.error(`Sign in, or pass ${accent('--holder "Your Name"')} to say directly.`);
126
+ console.error(`Sign in, or pass ${(0, colour_js_1.accent)('--holder "Your Name"')} to say directly.`);
131
127
  }
132
128
  else {
133
- console.error(dim(reason));
134
- console.error(`Try again, or pass ${accent('--holder "Your Name"')} to say directly.`);
129
+ console.error((0, colour_js_1.dim)(reason));
130
+ console.error(`Try again, or pass ${(0, colour_js_1.accent)('--holder "Your Name"')} to say directly.`);
135
131
  }
136
132
  return 1;
137
133
  }
@@ -139,19 +135,19 @@ async function commandLicence(parsed) {
139
135
  const target = node_path_1.default.join(folder, licences_js_1.LICENCE_FILE);
140
136
  const already = await (0, promises_1.readFile)(target, "utf8").catch(() => null);
141
137
  if (already !== null && !parsed.flags.has("force")) {
142
- console.error(red("This folder already has a LICENSE."));
143
- console.error(dim(` ${already.split("\n")[0]?.trim() ?? ""}`));
138
+ console.error((0, colour_js_1.red)("This folder already has a LICENSE."));
139
+ console.error((0, colour_js_1.dim)(` ${already.split("\n")[0]?.trim() ?? ""}`));
144
140
  console.error(`\nReplacing a licence changes the terms other people already received.` +
145
- `\nPass ${accent("--force")} if that is what you mean.`);
141
+ `\nPass ${(0, colour_js_1.accent)("--force")} if that is what you mean.`);
146
142
  return 1;
147
143
  }
148
144
  await (0, promises_1.writeFile)(target, (0, licences_js_1.licenceText)(licence.id, holder), "utf8");
149
- console.log(green(`Wrote ${licence.name} to ${licences_js_1.LICENCE_FILE}.`));
145
+ console.log((0, colour_js_1.green)(`Wrote ${licence.name} to ${licences_js_1.LICENCE_FILE}.`));
150
146
  if (licence.personalised)
151
- console.log(dim(` in the name of ${holder}`));
152
- console.log(dim(` ${licence.summary}`));
147
+ console.log((0, colour_js_1.dim)(` in the name of ${holder}`));
148
+ console.log((0, colour_js_1.dim)(` ${licence.summary}`));
153
149
  console.log("");
154
- console.log(dim("It is a file like any other, so it goes up with your next save."));
150
+ console.log((0, colour_js_1.dim)("It is a file like any other, so it goes up with your next save."));
155
151
  return 0;
156
152
  }
157
153
  /**
@@ -169,7 +165,7 @@ async function commandIgnoreTemplate(parsed) {
169
165
  const names = Object.values(ignore_templates_js_1.IGNORE_TEMPLATES)
170
166
  .map((template) => template.name)
171
167
  .sort((left, right) => left.localeCompare(right));
172
- console.log(bold(`${names.length} sets of ignore rules`));
168
+ console.log((0, colour_js_1.bold)(`${names.length} sets of ignore rules`));
173
169
  console.log("");
174
170
  /* Four columns, because a list this long as one column is unreadable. */
175
171
  const width = Math.max(...names.map((name) => name.length)) + 2;
@@ -178,14 +174,14 @@ async function commandIgnoreTemplate(parsed) {
178
174
  console.log(" " + names.slice(at, at + perRow).map((name) => name.padEnd(width)).join(""));
179
175
  }
180
176
  console.log("");
181
- console.log(`Add one with ${accent("cbx ignore --template Python")}. ` +
182
- dim("Several is normal — a language, an editor, an operating system."));
177
+ console.log(`Add one with ${(0, colour_js_1.accent)("cbx ignore --template Python")}. ` +
178
+ (0, colour_js_1.dim)("Several is normal — a language, an editor, an operating system."));
183
179
  return 0;
184
180
  }
185
181
  const template = ignore_templates_js_1.IGNORE_TEMPLATES[wanted.toLowerCase()];
186
182
  if (!template) {
187
- console.error(red(`No rules called ${wanted}.`));
188
- console.error(`Run ${accent("cbx ignore --template list")} to see what there is.`);
183
+ console.error((0, colour_js_1.red)(`No rules called ${wanted}.`));
184
+ console.error(`Run ${(0, colour_js_1.accent)("cbx ignore --template list")} to see what there is.`);
189
185
  return 1;
190
186
  }
191
187
  const rules = await (0, worktree_js_1.readRules)(folder);
@@ -198,10 +194,10 @@ async function commandIgnoreTemplate(parsed) {
198
194
  const shared = `${body ? `${body}\n\n` : ""}${heading}\n${template.body}`;
199
195
  await (0, worktree_js_1.writeRules)(folder, { ...rules, shared });
200
196
  const lines = template.body.split("\n").filter((line) => line.trim() && !line.startsWith("#"));
201
- console.log(green(`Added ${template.name} — ${lines.length} rules.`));
202
- console.log(dim(` ${node_path_1.default.join(folder, ".gitignore")}`));
197
+ console.log((0, colour_js_1.green)(`Added ${template.name} — ${lines.length} rules.`));
198
+ console.log((0, colour_js_1.dim)(` ${node_path_1.default.join(folder, ".gitignore")}`));
203
199
  console.log("");
204
- console.log(dim(`See what it leaves behind with `) + accent("cbx status"));
200
+ console.log((0, colour_js_1.dim)(`See what it leaves behind with `) + (0, colour_js_1.accent)("cbx status"));
205
201
  return 0;
206
202
  }
207
203
  function flagValue(parsed, name) {