@coderook/cli 0.34.1 → 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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/cli/src/api.js +8 -2
- package/dist/cli/src/attach_command.js +18 -22
- package/dist/cli/src/cli.js +62 -14
- package/dist/cli/src/colour.js +16 -0
- package/dist/cli/src/help.js +12 -14
- package/dist/cli/src/licence_commands.js +34 -38
- package/dist/cli/src/project_commands.js +43 -47
- package/dist/cli/src/service_commands.js +169 -120
- package/dist/cli/src/track_commands.js +21 -25
- package/dist/cli/src/transfer_command.js +28 -32
- package/dist/cli/src/version_commands.js +66 -70
- package/package.json +1 -1
- package/skills/coderook/SKILL.md +1 -1
|
@@ -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.
|
|
5
|
+
"version": "0.34.3",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
package/dist/cli/src/api.js
CHANGED
|
@@ -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:
|
|
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
|
|
32
|
-
const bold = (value) => `[1m${value}[0m`;
|
|
33
|
-
const red = (value) => `[31m${value}[0m`;
|
|
34
|
-
const green = (value) => `[32m${value}[0m`;
|
|
35
|
-
const accent = (value) => `[33m${value}[0m`;
|
|
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
|
}
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -73,6 +73,10 @@ const bold = (text) => (colour ? `[1m${text}[0m` : text);
|
|
|
73
73
|
const accent = (text) => (colour ? `[33m${text}[0m` : text);
|
|
74
74
|
const red = (text) => (colour ? `[31m${text}[0m` : text);
|
|
75
75
|
const green = (text) => (colour ? `[32m${text}[0m` : 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`;
|
|
@@ -258,7 +262,19 @@ async function reconcile(folder, options = {}) {
|
|
|
258
262
|
return { link: link ?? null, baseline: null, behind: null, member };
|
|
259
263
|
}
|
|
260
264
|
const downloader = new download_js_1.Downloader(config_js_1.credentials);
|
|
261
|
-
const
|
|
265
|
+
const all = await downloader.versions(project.id);
|
|
266
|
+
/*
|
|
267
|
+
The head of this folder's line, not the newest version in the project.
|
|
268
|
+
|
|
269
|
+
They differ after `cbx undo`: the line goes back to v41 and v42 stays in
|
|
270
|
+
the history, passed over. Taking the newest here linked a folder "at v42",
|
|
271
|
+
and its first save — sent after uploading everything — was refused because
|
|
272
|
+
v42 is not on the line it was saving to. fetchInto learned the same thing.
|
|
273
|
+
*/
|
|
274
|
+
const lineName = link?.track ?? project.defaultBranch ?? "main";
|
|
275
|
+
const line = (await new tracks_js_1.Tracks(config_js_1.credentials).list(project.id)).find((candidate) => candidate.kind === "line" && candidate.name === lineName);
|
|
276
|
+
const latest = (line?.headVersionId && all.find((version) => version.id === line.headVersionId)) ||
|
|
277
|
+
all[0];
|
|
262
278
|
if (!latest)
|
|
263
279
|
return { link: link ?? null, baseline: null, behind: null, member };
|
|
264
280
|
/*
|
|
@@ -276,7 +292,7 @@ async function reconcile(folder, options = {}) {
|
|
|
276
292
|
await (0, config_js_1.writeLink)(folder, known);
|
|
277
293
|
}
|
|
278
294
|
if (behind && !options.quiet) {
|
|
279
|
-
console.log(dim(`The
|
|
295
|
+
console.log(dim(`The project is on v${behind.sequence}; this folder is based on v${known.sequence}.`));
|
|
280
296
|
}
|
|
281
297
|
return {
|
|
282
298
|
link: known,
|
|
@@ -720,6 +736,23 @@ function noteKeptDeletions(folder, baseline, syncing) {
|
|
|
720
736
|
` (${gone.slice(0, 3).join(", ")}${gone.length > 3 ? ", …" : ""}). A save adds and updates;` +
|
|
721
737
|
` run cbx submit --sync to remove ${gone.length === 1 ? "it" : "them"}.`));
|
|
722
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
|
+
}
|
|
723
756
|
async function commandSubmit(parsed) {
|
|
724
757
|
const folder = folderFor(parsed);
|
|
725
758
|
const message = flagText(parsed, "m", "message") ?? "";
|
|
@@ -1102,7 +1135,14 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
|
1102
1135
|
if (ahead.collidingPaths.length > 5) {
|
|
1103
1136
|
console.log(dim(` and ${ahead.collidingPaths.length - 5} more`));
|
|
1104
1137
|
}
|
|
1105
|
-
|
|
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."));
|
|
1106
1146
|
console.log(dim(" Run cbx get first if you would rather build on theirs."));
|
|
1107
1147
|
console.log("");
|
|
1108
1148
|
}
|
|
@@ -1142,7 +1182,7 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
|
1142
1182
|
console.log(`${accent("Upload plan")} ${bytes(plan.sourceBytes)} selected → ` +
|
|
1143
1183
|
`${bytes(plan.compactedBytes)} compacted; ${bytes(plan.chargeableBytes)} new storage.`);
|
|
1144
1184
|
console.log(plan.allowance.exempt
|
|
1145
|
-
? dim("
|
|
1185
|
+
? dim("No monthly upload steps on this plan; the storage limit is the only one.")
|
|
1146
1186
|
: dim(`Monthly stage ${plan.allowance.stage}: ${plan.allowance.unlockedPercent}% unlocked, ` +
|
|
1147
1187
|
`${bytes(plan.allowance.remainingBytes)} remains after this reservation.`));
|
|
1148
1188
|
if (dryRun) {
|
|
@@ -1226,7 +1266,7 @@ The connection failed: ${text}`));
|
|
|
1226
1266
|
: `${result.mergeTrack.conflicts.length} files overlap`}.`);
|
|
1227
1267
|
console.log(`Your work is complete and kept as ${accent(result.mergeTrack.reference)}:`);
|
|
1228
1268
|
for (const conflict of result.mergeTrack.conflicts.slice(0, 20)) {
|
|
1229
|
-
console.log(` ${red(conflict.path)} ${dim(conflict.kind)}`);
|
|
1269
|
+
console.log(` ${red(conflict.path)} ${dim(conflictWords(conflict.kind))}`);
|
|
1230
1270
|
}
|
|
1231
1271
|
console.log(`\nRun ${accent(`cbx merge ${result.mergeTrack.reference}`)} to decide,` +
|
|
1232
1272
|
` or ${accent("cbx merges")} to see everything waiting.`);
|
|
@@ -1643,7 +1683,7 @@ trackName) {
|
|
|
1643
1683
|
});
|
|
1644
1684
|
// The record is written; the run has not reported success yet.
|
|
1645
1685
|
(0, faults_js_1.maybeFail)("get:after-record");
|
|
1646
|
-
console.log(`${accent(`v${latest.sequence}`)} · ${result.files}
|
|
1686
|
+
console.log(`${accent(`v${latest.sequence}`)} · ${fileCount(result.files)} · ${bytes(result.bytes)} into ${destination}`);
|
|
1647
1687
|
return 0;
|
|
1648
1688
|
}
|
|
1649
1689
|
/**
|
|
@@ -1777,7 +1817,7 @@ async function commandUnbundle(parsed) {
|
|
|
1777
1817
|
dim(progress.path.slice(-40)));
|
|
1778
1818
|
});
|
|
1779
1819
|
done(line);
|
|
1780
|
-
console.log(`${result.files}
|
|
1820
|
+
console.log(`${fileCount(result.files)} · ${bytes(result.bytes)} into ${destination}`);
|
|
1781
1821
|
return 0;
|
|
1782
1822
|
}
|
|
1783
1823
|
async function commandInspect(parsed) {
|
|
@@ -1789,7 +1829,7 @@ async function commandInspect(parsed) {
|
|
|
1789
1829
|
const manifest = await (0, cbx_js_1.readManifest)(node_path_1.default.resolve(source));
|
|
1790
1830
|
const total = manifest.files.reduce((sum, file) => sum + file.size, 0);
|
|
1791
1831
|
console.log(`${bold(manifest.source_name)} · format ${manifest.format} v${manifest.format_version}`);
|
|
1792
|
-
console.log(`${manifest.files.length}
|
|
1832
|
+
console.log(`${fileCount(manifest.files.length)} · ${bytes(total)} · ` +
|
|
1793
1833
|
`${Object.keys(manifest.chunks).length} chunks`);
|
|
1794
1834
|
if (hasFlag(parsed, "files")) {
|
|
1795
1835
|
for (const file of manifest.files) {
|
|
@@ -2010,13 +2050,17 @@ async function commandMerge(parsed) {
|
|
|
2010
2050
|
}
|
|
2011
2051
|
}
|
|
2012
2052
|
console.log(`
|
|
2013
|
-
${accent(now.mergeTrack.reference)} ·
|
|
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
|
+
*/
|
|
2014
2058
|
(now.provisional.ready
|
|
2015
|
-
?
|
|
2059
|
+
? `${fileCount(now.provisional.fileCount)} · ready to apply`
|
|
2016
2060
|
: `${now.provisional.unresolvedPaths.length} still to decide`));
|
|
2017
2061
|
for (const conflict of now.conflicts) {
|
|
2018
2062
|
console.log(` ${conflict.resolvedAt ? accent("decided") : red("waiting")} ${conflict.path}` +
|
|
2019
|
-
` ${dim(conflict.kind)}${conflict.resolution ? dim(` (${conflict.resolution})`) : ""}`);
|
|
2063
|
+
` ${dim(conflictWords(conflict.kind))}${conflict.resolution ? dim(` (${conflict.resolution})`) : ""}`);
|
|
2020
2064
|
}
|
|
2021
2065
|
if (hasFlag(parsed, "apply")) {
|
|
2022
2066
|
if (!now.provisional.ready) {
|
|
@@ -2036,7 +2080,7 @@ Applied as ${accent(`v${applied.version.sequence}`)}.`);
|
|
|
2036
2080
|
if (!decision && stillOpen) {
|
|
2037
2081
|
console.log(dim(`
|
|
2038
2082
|
--mine keeps yours, --theirs keeps what was already saved,` +
|
|
2039
|
-
` --drop removes the file.
|
|
2083
|
+
` --both keeps theirs and saves yours beside it, --drop removes the file.
|
|
2040
2084
|
Add --path <file> for one file, then --apply when ready.`));
|
|
2041
2085
|
}
|
|
2042
2086
|
else if (now.provisional.ready) {
|
|
@@ -3086,9 +3130,13 @@ const SPECS = [
|
|
|
3086
3130
|
usage: "people [project]",
|
|
3087
3131
|
options: [
|
|
3088
3132
|
{ flags: "--invite <email>", description: "ask somebody to join" },
|
|
3089
|
-
{ flags: "--role <role>", description: "
|
|
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=…",
|
|
3090
3139
|
],
|
|
3091
|
-
examples: ["cbx people my-project --invite sam@example.com"],
|
|
3092
3140
|
run: service_commands_js_1.commandCollaborators,
|
|
3093
3141
|
},
|
|
3094
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}[0m` : 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");
|
package/dist/cli/src/help.js
CHANGED
|
@@ -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
|
|
23
|
-
const dim = (value) => `[2m${value}[0m`;
|
|
24
|
-
const accent = (value) => `[33m${value}[0m`;
|
|
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
|
|
30
|
-
const bold = (value) => `[1m${value}[0m`;
|
|
31
|
-
const red = (value) => `[31m${value}[0m`;
|
|
32
|
-
const green = (value) => `[32m${value}[0m`;
|
|
33
|
-
const accent = (value) => `[33m${value}[0m`;
|
|
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) {
|