@coderook/cli 0.25.4 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/cli/src/api.js +86 -0
- package/dist/cli/src/cli.js +267 -23
- package/dist/cli/src/git_remote.js +100 -15
- package/dist/cli/src/project_commands.js +67 -4
- package/dist/cli/src/service_commands.js +2 -1
- package/dist/cli/src/version_commands.js +652 -0
- package/dist/desktop-app/src/main/download.js +15 -6
- package/dist/desktop-app/src/main/secret_patterns.js +1 -0
- package/dist/desktop-app/src/main/tracks.js +76 -0
- package/dist/desktop-app/src/main/upload.js +5 -0
- package/dist/desktop-app/src/main/worktree.js +26 -8
- package/package.json +1 -1
- package/skills/coderook/SKILL.md +24 -0
|
@@ -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, from Claude Code.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.27.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
package/dist/cli/src/api.js
CHANGED
|
@@ -28,6 +28,14 @@ exports.runLogs = runLogs;
|
|
|
28
28
|
exports.tokens = tokens;
|
|
29
29
|
exports.revokeToken = revokeToken;
|
|
30
30
|
exports.deleteProject = deleteProject;
|
|
31
|
+
exports.projectLabels = projectLabels;
|
|
32
|
+
exports.createProjectLabel = createProjectLabel;
|
|
33
|
+
exports.deleteProjectLabel = deleteProjectLabel;
|
|
34
|
+
exports.changeVersion = changeVersion;
|
|
35
|
+
exports.reviewVersion = reviewVersion;
|
|
36
|
+
exports.removeVersionContent = removeVersionContent;
|
|
37
|
+
exports.versionAttachments = versionAttachments;
|
|
38
|
+
exports.undoTo = undoTo;
|
|
31
39
|
/** The small part of the API the command-line tool needs directly. */
|
|
32
40
|
const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
33
41
|
const config_js_1 = require("./config.js");
|
|
@@ -242,6 +250,26 @@ async function versions(repositoryId) {
|
|
|
242
250
|
? row.parentVersionIds.map(String)
|
|
243
251
|
: [],
|
|
244
252
|
authorName: String(row.author?.displayName ?? ""),
|
|
253
|
+
name: row.name == null ? null : String(row.name),
|
|
254
|
+
notes: row.notes == null ? null : String(row.notes),
|
|
255
|
+
/*
|
|
256
|
+
Falls back to the older field, so this keeps working against a service
|
|
257
|
+
that has not been updated yet rather than reporting everything private.
|
|
258
|
+
*/
|
|
259
|
+
visibility: row.visibility ??
|
|
260
|
+
(row.public === false ? "private" : "public"),
|
|
261
|
+
state: String(row.state ?? "verified"),
|
|
262
|
+
pinned: row.pinned === true,
|
|
263
|
+
labels: Array.isArray(row.labels)
|
|
264
|
+
? row.labels.map((one) => ({
|
|
265
|
+
id: String(one.id ?? ""),
|
|
266
|
+
name: String(one.name ?? ""),
|
|
267
|
+
colour: String(one.colour ?? "#888888"),
|
|
268
|
+
description: one.description == null ? null : String(one.description),
|
|
269
|
+
}))
|
|
270
|
+
: [],
|
|
271
|
+
removedAt: row.removedAt == null ? null : String(row.removedAt),
|
|
272
|
+
head: row.head === true,
|
|
245
273
|
}));
|
|
246
274
|
}
|
|
247
275
|
/**
|
|
@@ -410,3 +438,61 @@ async function deleteProject(repositoryId) {
|
|
|
410
438
|
method: "DELETE",
|
|
411
439
|
});
|
|
412
440
|
}
|
|
441
|
+
async function projectLabels(repositoryId) {
|
|
442
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/labels`);
|
|
443
|
+
return (body.labels ?? []).map((row) => ({
|
|
444
|
+
id: String(row.id ?? ""),
|
|
445
|
+
name: String(row.name ?? ""),
|
|
446
|
+
colour: String(row.colour ?? "#888888"),
|
|
447
|
+
description: row.description == null ? null : String(row.description),
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
async function createProjectLabel(repositoryId, name, colour, description) {
|
|
451
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/labels`, { method: "POST", body: { name, colour, ...(description ? { description } : {}) } });
|
|
452
|
+
return {
|
|
453
|
+
id: String(body.label?.id ?? ""),
|
|
454
|
+
name: String(body.label?.name ?? name),
|
|
455
|
+
colour: String(body.label?.colour ?? colour),
|
|
456
|
+
description: body.label?.description == null ? null : String(body.label.description),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
async function deleteProjectLabel(repositoryId, labelId) {
|
|
460
|
+
await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/labels/${encodeURIComponent(labelId)}`, { method: "DELETE" });
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Everything about a version, changed in one call.
|
|
464
|
+
*
|
|
465
|
+
* Deliberately one request rather than one per field. The service takes a
|
|
466
|
+
* patch, so a command that sets a name and a colour at once is one round trip
|
|
467
|
+
* and, more to the point, one thing that either happened or did not.
|
|
468
|
+
*/
|
|
469
|
+
async function changeVersion(repositoryId, versionId, patch) {
|
|
470
|
+
await call(`/v1/repositories/${encodeURIComponent(repositoryId)}` +
|
|
471
|
+
`/versions/${encodeURIComponent(versionId)}`, { method: "PATCH", body: patch });
|
|
472
|
+
}
|
|
473
|
+
/** Accept a held version, or turn it down with a reason. */
|
|
474
|
+
async function reviewVersion(repositoryId, versionId, decision, note) {
|
|
475
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}` +
|
|
476
|
+
`/versions/${encodeURIComponent(versionId)}/review`, { method: "POST", body: { decision, ...(note ? { note } : {}) } });
|
|
477
|
+
return { state: String(body.state ?? decision) };
|
|
478
|
+
}
|
|
479
|
+
/** Take a version's content down, leaving the version itself as a record. */
|
|
480
|
+
async function removeVersionContent(repositoryId, versionId, reason) {
|
|
481
|
+
const query = reason ? `?reason=${encodeURIComponent(reason)}` : "";
|
|
482
|
+
await call(`/v1/repositories/${encodeURIComponent(repositoryId)}` +
|
|
483
|
+
`/versions/${encodeURIComponent(versionId)}/content${query}`, { method: "DELETE" });
|
|
484
|
+
}
|
|
485
|
+
async function versionAttachments(repositoryId, versionId) {
|
|
486
|
+
const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}` +
|
|
487
|
+
`/versions/${encodeURIComponent(versionId)}/attachments`);
|
|
488
|
+
return (body.attachments ?? []).map((row) => ({
|
|
489
|
+
id: String(row.id ?? ""),
|
|
490
|
+
name: String(row.name ?? ""),
|
|
491
|
+
sizeBytes: Number(row.sizeBytes ?? 0),
|
|
492
|
+
mediaType: String(row.mediaType ?? "application/octet-stream"),
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
/** Put the project back on an earlier commit. */
|
|
496
|
+
async function undoTo(repositoryId, to) {
|
|
497
|
+
return await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/undo`, { method: "POST", body: to ? { to } : {} });
|
|
498
|
+
}
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -37,6 +37,7 @@ const worktree_js_1 = require("../../desktop-app/src/main/worktree.js");
|
|
|
37
37
|
const upload_js_1 = require("../../desktop-app/src/main/upload.js");
|
|
38
38
|
const download_js_1 = require("../../desktop-app/src/main/download.js");
|
|
39
39
|
const faults_js_1 = require("../../desktop-app/src/main/faults.js");
|
|
40
|
+
const version_commands_js_1 = require("./version_commands.js");
|
|
40
41
|
const identify_js_1 = require("../../desktop-app/src/main/identify.js");
|
|
41
42
|
const detect_js_1 = require("../../desktop-app/src/main/detect.js");
|
|
42
43
|
const cbx_js_1 = require("../../desktop-app/src/main/cbx.js");
|
|
@@ -92,18 +93,62 @@ const done = (line) => {
|
|
|
92
93
|
node_process_1.default.stdout.write("\r");
|
|
93
94
|
}
|
|
94
95
|
};
|
|
95
|
-
|
|
96
|
+
/**
|
|
97
|
+
* The flags a command declares as taking a value.
|
|
98
|
+
*
|
|
99
|
+
* Read from the option's own help text, which already says so: a value is
|
|
100
|
+
* written as a placeholder after the name, as in `--name <text>`, and a
|
|
101
|
+
* switch has nothing after it. So there is no second list to keep in step
|
|
102
|
+
* with the first — the thing already written for the reader is the thing
|
|
103
|
+
* the parser uses.
|
|
104
|
+
*/
|
|
105
|
+
function valueTaking(spec) {
|
|
106
|
+
const names = new Set();
|
|
107
|
+
for (const option of spec?.options ?? []) {
|
|
108
|
+
if (!/[<[]/.test(option.flags))
|
|
109
|
+
continue;
|
|
110
|
+
for (const match of option.flags.matchAll(/--?([A-Za-z0-9][\w-]*)/g)) {
|
|
111
|
+
names.add(match[1]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return names;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* What was typed, split into positions and flags.
|
|
118
|
+
*
|
|
119
|
+
* A flag takes the word after it only when the command says it takes one.
|
|
120
|
+
* Guessing from the shape of the next word — anything not starting with a
|
|
121
|
+
* dash — meant `cbx take-down 1 --yes my-project` read the project name as
|
|
122
|
+
* the value of `--yes` and then reported that `--yes` had not been given,
|
|
123
|
+
* having also swallowed the argument that said which project. Every switch
|
|
124
|
+
* followed by a positional had the same fault.
|
|
125
|
+
*/
|
|
126
|
+
function parse(argv, spec) {
|
|
96
127
|
const positional = [];
|
|
97
128
|
const flags = new Map();
|
|
129
|
+
const takesValue = valueTaking(spec);
|
|
98
130
|
for (let at = 0; at < argv.length; at += 1) {
|
|
99
131
|
const item = argv[at];
|
|
100
132
|
if (!item.startsWith("-")) {
|
|
101
133
|
positional.push(item);
|
|
102
134
|
continue;
|
|
103
135
|
}
|
|
104
|
-
|
|
136
|
+
/* `--name=value` says so itself and needs no declaration. */
|
|
137
|
+
const written = item.replace(/^--?/, "");
|
|
138
|
+
const split = written.indexOf("=");
|
|
139
|
+
if (split > 0) {
|
|
140
|
+
flags.set(written.slice(0, split), written.slice(split + 1));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const name = written;
|
|
105
144
|
const next = argv[at + 1];
|
|
106
|
-
|
|
145
|
+
/*
|
|
146
|
+
An undeclared flag keeps the old guess. Commands that never declared
|
|
147
|
+
their options rely on it, and narrowing that here would trade one
|
|
148
|
+
quiet misreading for another.
|
|
149
|
+
*/
|
|
150
|
+
const wants = takesValue.size ? takesValue.has(name) : true;
|
|
151
|
+
if (wants && next !== undefined && !next.startsWith("-")) {
|
|
107
152
|
flags.set(name, next);
|
|
108
153
|
at += 1;
|
|
109
154
|
}
|
|
@@ -397,7 +442,7 @@ async function commandImport(parsed) {
|
|
|
397
442
|
flags: submitFlags,
|
|
398
443
|
});
|
|
399
444
|
if (code === 0 && plan.temporary) {
|
|
400
|
-
console.log(dim(`The working copy stays at ${plan.folder} until you remove it.
|
|
445
|
+
console.log(dim(`The working copy stays at ${plan.folder} until you remove it.
|
|
401
446
|
` +
|
|
402
447
|
`Run ${accent("cbx get " + plan.name)} anywhere to fetch it fresh.`));
|
|
403
448
|
}
|
|
@@ -525,17 +570,17 @@ async function commandSubmit(parsed) {
|
|
|
525
570
|
*/
|
|
526
571
|
const shielded = (await (0, worktree_js_1.detectPrivateDirectories)(folder)).filter((finding) => [...sending].some((file) => file === finding.path || file.startsWith(`${finding.path}/`)));
|
|
527
572
|
if (shielded.length && !hasFlag(parsed, "allow-private")) {
|
|
528
|
-
console.log(red(`
|
|
573
|
+
console.log(red(`
|
|
529
574
|
${shielded.length} folder${shielded.length === 1 ? "" : "s"} here belong${shielded.length === 1 ? "s" : ""} to a program, not to your project:`));
|
|
530
575
|
for (const finding of shielded) {
|
|
531
576
|
console.log(` ${finding.path} ${dim(`— ${finding.because}`)}`);
|
|
532
577
|
}
|
|
533
|
-
console.log(`
|
|
578
|
+
console.log(`
|
|
534
579
|
Nothing was sent. To leave them behind:`);
|
|
535
580
|
for (const finding of shielded) {
|
|
536
581
|
console.log(` ${accent(`echo "${finding.rule}" >> .gitignore`)}`);
|
|
537
582
|
}
|
|
538
|
-
console.error(`
|
|
583
|
+
console.error(`
|
|
539
584
|
Or pass ${accent("--allow-private")} if they genuinely belong in the project.`);
|
|
540
585
|
return 1;
|
|
541
586
|
}
|
|
@@ -570,7 +615,7 @@ Or pass ${accent("--allow-private")} if they genuinely belong in the project.`);
|
|
|
570
615
|
const pasted = await (0, worktree_js_1.detectPastedCredentials)(folder, files.map((file) => file.path));
|
|
571
616
|
const stillPasted = pasted.filter((finding) => !exposed.includes(finding.path));
|
|
572
617
|
if (stillPasted.length) {
|
|
573
|
-
console.log(red(`
|
|
618
|
+
console.log(red(`
|
|
574
619
|
${stillPasted.length} file${stillPasted.length === 1 ? " has a credential" : "s have credentials"} inside:`));
|
|
575
620
|
for (const finding of stillPasted.slice(0, 20)) {
|
|
576
621
|
console.log(` ${finding.path}`);
|
|
@@ -585,11 +630,11 @@ ${stillPasted.length} file${stillPasted.length === 1 ? " has a credential" : "s
|
|
|
585
630
|
console.log(dim(` …and ${stillPasted.length - 20} more`));
|
|
586
631
|
}
|
|
587
632
|
if (!hasFlag(parsed, "allow-secrets")) {
|
|
588
|
-
console.error(`
|
|
633
|
+
console.error(`
|
|
589
634
|
Nothing was sent. Move the key into an environment variable, and if` +
|
|
590
635
|
` it has ever been published, replace it at the service that issued` +
|
|
591
636
|
` it — a key that has leaked stays leaked.` +
|
|
592
|
-
`
|
|
637
|
+
`
|
|
593
638
|
Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
594
639
|
return 1;
|
|
595
640
|
}
|
|
@@ -687,7 +732,7 @@ Pass ${accent("--allow-secrets")} if these are not real keys.`);
|
|
|
687
732
|
const failure = (0, publish_js_1.classifyPublishFailure)(error);
|
|
688
733
|
const text = error instanceof Error ? error.message : String(error);
|
|
689
734
|
if (failure?.kind === "interrupted") {
|
|
690
|
-
console.error(red(`
|
|
735
|
+
console.error(red(`
|
|
691
736
|
The connection failed: ${text}`));
|
|
692
737
|
console.error(`Your work may already have been saved. Run the same command again —` +
|
|
693
738
|
` it will not create a second version.`);
|
|
@@ -889,7 +934,7 @@ async function commandGet(parsed) {
|
|
|
889
934
|
`changed while the interrupted fetch was stopped:`));
|
|
890
935
|
for (const file of changedInTheGap.slice(0, 20))
|
|
891
936
|
console.error(` ${file.path}`);
|
|
892
|
-
console.error(`
|
|
937
|
+
console.error(`
|
|
893
938
|
Save them with ${accent("cbx submit")}, or finish the fetch and ` +
|
|
894
939
|
`discard them with ${accent("cbx get --replace")}.`);
|
|
895
940
|
return 1;
|
|
@@ -1121,8 +1166,8 @@ async function suggestRules(folder, apply) {
|
|
|
1121
1166
|
const addition = (0, detect_js_1.rulesFromSuggestions)(recommended);
|
|
1122
1167
|
const shared = rules.shared.trimEnd();
|
|
1123
1168
|
await (0, worktree_js_1.writeRules)(folder, {
|
|
1124
|
-
shared: shared ? `${shared}
|
|
1125
|
-
|
|
1169
|
+
shared: shared ? `${shared}
|
|
1170
|
+
|
|
1126
1171
|
${addition}` : addition,
|
|
1127
1172
|
local: rules.local,
|
|
1128
1173
|
});
|
|
@@ -1249,7 +1294,7 @@ async function commandMerges(parsed) {
|
|
|
1249
1294
|
const counts = merge.conflicts;
|
|
1250
1295
|
console.log(`${accent(merge.reference)} ${counts ? `${counts.unresolved} of ${counts.total} still to decide` : ""} ${dim(new Date(merge.createdAt).toLocaleString())}`);
|
|
1251
1296
|
}
|
|
1252
|
-
console.log(dim(`
|
|
1297
|
+
console.log(dim(`
|
|
1253
1298
|
Run cbx merge <reference> to look at one.`));
|
|
1254
1299
|
return 0;
|
|
1255
1300
|
}
|
|
@@ -1315,7 +1360,7 @@ async function commandMerge(parsed) {
|
|
|
1315
1360
|
}
|
|
1316
1361
|
}
|
|
1317
1362
|
const now = await (0, api_js_1.mergeTrack)(summary.id);
|
|
1318
|
-
console.log(`
|
|
1363
|
+
console.log(`
|
|
1319
1364
|
${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
|
|
1320
1365
|
(now.provisional.ready
|
|
1321
1366
|
? "ready to apply"
|
|
@@ -1330,19 +1375,19 @@ ${accent(now.mergeTrack.reference)} · ${now.provisional.fileCount} files · ` +
|
|
|
1330
1375
|
return 1;
|
|
1331
1376
|
}
|
|
1332
1377
|
const applied = await (0, api_js_1.applyMerge)(summary.id);
|
|
1333
|
-
console.log(`
|
|
1378
|
+
console.log(`
|
|
1334
1379
|
Applied as ${accent(`v${applied.version.sequence}`)}.`);
|
|
1335
1380
|
console.log(dim("Run cbx get to bring it down to this folder."));
|
|
1336
1381
|
return 0;
|
|
1337
1382
|
}
|
|
1338
1383
|
if (!decision) {
|
|
1339
|
-
console.log(dim(`
|
|
1384
|
+
console.log(dim(`
|
|
1340
1385
|
--mine keeps yours, --theirs keeps what was already saved,` +
|
|
1341
|
-
` --drop removes the file.
|
|
1386
|
+
` --drop removes the file.
|
|
1342
1387
|
Add --path <file> for one file, then --apply when ready.`));
|
|
1343
1388
|
}
|
|
1344
1389
|
else if (now.provisional.ready) {
|
|
1345
|
-
console.log(dim(`
|
|
1390
|
+
console.log(dim(`
|
|
1346
1391
|
Run cbx merge ${now.mergeTrack.reference} --apply to publish it.`));
|
|
1347
1392
|
}
|
|
1348
1393
|
return 0;
|
|
@@ -1757,8 +1802,20 @@ const SPECS = [
|
|
|
1757
1802
|
group: "Your projects",
|
|
1758
1803
|
summary: "what has been saved to a project",
|
|
1759
1804
|
usage: "versions [project]",
|
|
1760
|
-
detail: "Newest first. Run it inside a linked folder to leave the name out
|
|
1761
|
-
|
|
1805
|
+
detail: "Newest first. Run it inside a linked folder to leave the name out.\n\n" +
|
|
1806
|
+
"A save is a commit; a commit somebody named is a version, and versions\n" +
|
|
1807
|
+
"are the only thing the public side shows. Both are listed together\n" +
|
|
1808
|
+
"unless you ask for one lane — the same three views the website has.",
|
|
1809
|
+
options: [
|
|
1810
|
+
{ flags: "--limit <n>", description: "how many to show (default 20)" },
|
|
1811
|
+
{ flags: "--versions", description: "only the ones that were named" },
|
|
1812
|
+
{ flags: "--commits", description: "only the ones that were not" },
|
|
1813
|
+
{ flags: "--notes", description: "print what each one says about itself" },
|
|
1814
|
+
],
|
|
1815
|
+
examples: [
|
|
1816
|
+
"cbx versions --versions",
|
|
1817
|
+
"cbx versions my-project --commits",
|
|
1818
|
+
],
|
|
1762
1819
|
run: project_commands_js_1.commandVersions,
|
|
1763
1820
|
},
|
|
1764
1821
|
{
|
|
@@ -1805,6 +1862,193 @@ const SPECS = [
|
|
|
1805
1862
|
usage: "releases [project]",
|
|
1806
1863
|
run: service_commands_js_1.commandReleases,
|
|
1807
1864
|
},
|
|
1865
|
+
{
|
|
1866
|
+
/*
|
|
1867
|
+
The half that hiding never covered.
|
|
1868
|
+
|
|
1869
|
+
Hiding a bad push stops strangers reading it and leaves the project
|
|
1870
|
+
standing on it, so the next person to pull still lands on the mistake.
|
|
1871
|
+
This moves the project.
|
|
1872
|
+
*/
|
|
1873
|
+
name: "undo",
|
|
1874
|
+
group: "Your projects",
|
|
1875
|
+
summary: "put the project back on an earlier save",
|
|
1876
|
+
usage: "undo [n] [project]",
|
|
1877
|
+
detail: "Goes back one save unless you name another to go back to.\n\n" +
|
|
1878
|
+
"Nothing is deleted and nothing is renumbered — the saves that get\n" +
|
|
1879
|
+
"passed over stay in the history, and the next save carries on from\n" +
|
|
1880
|
+
"wherever the project now stands.",
|
|
1881
|
+
examples: ["cbx undo", "cbx undo 41"],
|
|
1882
|
+
run: version_commands_js_1.commandUndo,
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
/*
|
|
1886
|
+
The decision that separates a working history from a published one.
|
|
1887
|
+
|
|
1888
|
+
Every save is a commit and most of them are nobody else's business. This
|
|
1889
|
+
is where one becomes a version — the thing the public side of a project
|
|
1890
|
+
actually offers.
|
|
1891
|
+
*/
|
|
1892
|
+
name: "promote",
|
|
1893
|
+
group: "Your projects",
|
|
1894
|
+
summary: "turn a commit into a version people can fetch",
|
|
1895
|
+
usage: "promote [n] [project]",
|
|
1896
|
+
detail: "Shows the last ten commits and asks which one. Give a number to skip\n" +
|
|
1897
|
+
"the list.\n\n" +
|
|
1898
|
+
"A version is a commit with a name on it. Until a project promotes its\n" +
|
|
1899
|
+
"first one its public page shows everything, as it always did.",
|
|
1900
|
+
options: [
|
|
1901
|
+
{ flags: "--name <text>", description: "what to call it" },
|
|
1902
|
+
{ flags: "--notes <text>", description: "what changed" },
|
|
1903
|
+
{ flags: "--notes-file <path>", description: "what changed, from a file" },
|
|
1904
|
+
{ flags: "--edit", description: "write what changed in your editor" },
|
|
1905
|
+
],
|
|
1906
|
+
examples: [
|
|
1907
|
+
"cbx promote",
|
|
1908
|
+
"cbx promote 41 --name v2.1",
|
|
1909
|
+
'cbx promote --name "Client build" --notes "Fixes the export dialog"',
|
|
1910
|
+
],
|
|
1911
|
+
run: version_commands_js_1.commandPromote,
|
|
1912
|
+
},
|
|
1913
|
+
{
|
|
1914
|
+
/*
|
|
1915
|
+
One command for everything a version can be, because the service takes
|
|
1916
|
+
one patch. Three separate commands would be three requests, and three
|
|
1917
|
+
chances for a person to end up with a version that is named but not
|
|
1918
|
+
pinned because the second one failed.
|
|
1919
|
+
*/
|
|
1920
|
+
/*
|
|
1921
|
+
Not called "version".
|
|
1922
|
+
|
|
1923
|
+
`cbx version` has printed the version number since the first release,
|
|
1924
|
+
and quietly changing that when arguments follow is the kind of thing
|
|
1925
|
+
that works until somebody scripts it. This marks a save — pins it,
|
|
1926
|
+
hides it, labels it — which is what it does anyway.
|
|
1927
|
+
*/
|
|
1928
|
+
name: "notes",
|
|
1929
|
+
aliases: ["release-notes"],
|
|
1930
|
+
group: "Your projects",
|
|
1931
|
+
summary: "read or write what a version says about itself",
|
|
1932
|
+
usage: "notes [n] [project]",
|
|
1933
|
+
detail: "The release info the website shows on a version, and the same field\n" +
|
|
1934
|
+
"`--notes` writes when promoting. With nothing to write it prints what\n" +
|
|
1935
|
+
"is there, which the terminal could not do before: `cbx releases` shows\n" +
|
|
1936
|
+
"the first line and stops.\n\n" +
|
|
1937
|
+
"Written in Markdown, because that is what the page renders. --edit\n" +
|
|
1938
|
+
"opens $EDITOR (or $VISUAL, or notepad on Windows) the way git does,\n" +
|
|
1939
|
+
"which is the only comfortable way to write more than a sentence.",
|
|
1940
|
+
options: [
|
|
1941
|
+
{ flags: "--edit", description: "write it in your editor" },
|
|
1942
|
+
{ flags: "--notes-file <path>", description: "take the text from a file" },
|
|
1943
|
+
{ flags: "--notes <text>", description: "set it in one line" },
|
|
1944
|
+
{ flags: "--clear", description: "remove what is there" },
|
|
1945
|
+
{ flags: "--project <name>", description: "which project" },
|
|
1946
|
+
],
|
|
1947
|
+
examples: [
|
|
1948
|
+
"cbx notes",
|
|
1949
|
+
"cbx notes 41",
|
|
1950
|
+
"cbx notes 41 --edit",
|
|
1951
|
+
"cbx notes 41 --notes-file RELEASE.md",
|
|
1952
|
+
],
|
|
1953
|
+
run: version_commands_js_1.commandNotes,
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
name: "mark",
|
|
1957
|
+
aliases: ["set"],
|
|
1958
|
+
group: "Your projects",
|
|
1959
|
+
summary: "name, hide, pin or label one save",
|
|
1960
|
+
usage: "mark [n] [project]",
|
|
1961
|
+
detail: "Changes the newest version unless you name another.\n" +
|
|
1962
|
+
"Everything you ask for happens in one request, so it either all\n" +
|
|
1963
|
+
"lands or none of it does.",
|
|
1964
|
+
options: [
|
|
1965
|
+
{ flags: "--name <text>", description: "give it a code name" },
|
|
1966
|
+
{ flags: "--unname", description: "take the name off again" },
|
|
1967
|
+
{ flags: "--notes <text>", description: "what changed" },
|
|
1968
|
+
{ flags: "--notes-file <path>", description: "what changed, from a file" },
|
|
1969
|
+
{ flags: "--edit", description: "write what changed in your editor" },
|
|
1970
|
+
{ flags: "--hide", description: "nobody outside the project can read it" },
|
|
1971
|
+
{ flags: "--show", description: "anybody can read it" },
|
|
1972
|
+
{
|
|
1973
|
+
flags: "--visibility <who>",
|
|
1974
|
+
description: "public, or private to hold it back",
|
|
1975
|
+
},
|
|
1976
|
+
{ flags: "--pin", description: "keep it at the top of the list" },
|
|
1977
|
+
{ flags: "--unpin", description: "put it back in order" },
|
|
1978
|
+
{ flags: "--labels <a,b>", description: "the labels it should wear" },
|
|
1979
|
+
{ flags: "--clear-labels", description: "take them all off" },
|
|
1980
|
+
],
|
|
1981
|
+
examples: [
|
|
1982
|
+
"cbx mark --name v2.1 --pin",
|
|
1983
|
+
"cbx mark 41 --visibility private",
|
|
1984
|
+
'cbx mark 41 --labels "shipped,client work"',
|
|
1985
|
+
],
|
|
1986
|
+
run: version_commands_js_1.commandVersion,
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
name: "labels",
|
|
1990
|
+
group: "Your projects",
|
|
1991
|
+
summary: "the labels this project puts on versions",
|
|
1992
|
+
usage: "labels [list|add|remove] [name] [colour]",
|
|
1993
|
+
detail: "A label is a name and a colour. Put as many on a version as you like —\n" +
|
|
1994
|
+
"how you group and colour your own history is yours to decide.",
|
|
1995
|
+
examples: [
|
|
1996
|
+
"cbx labels",
|
|
1997
|
+
"cbx labels add shipped green",
|
|
1998
|
+
"cbx labels add urgent #e0574a",
|
|
1999
|
+
"cbx labels remove shipped",
|
|
2000
|
+
],
|
|
2001
|
+
run: version_commands_js_1.commandLabels,
|
|
2002
|
+
},
|
|
2003
|
+
{
|
|
2004
|
+
name: "held",
|
|
2005
|
+
aliases: ["waiting"],
|
|
2006
|
+
group: "Your projects",
|
|
2007
|
+
summary: "versions waiting for somebody to approve them",
|
|
2008
|
+
usage: "held [project]",
|
|
2009
|
+
detail: "A project can be set to hold pushes from anyone below admin until an\n" +
|
|
2010
|
+
"admin accepts them. Those versions are real and stored — they are just\n" +
|
|
2011
|
+
"not the current version, and not on the public page.",
|
|
2012
|
+
run: version_commands_js_1.commandHeld,
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
name: "review",
|
|
2016
|
+
group: "Your projects",
|
|
2017
|
+
summary: "accept a held version, or turn it down",
|
|
2018
|
+
usage: "review <n> --approve | --decline",
|
|
2019
|
+
options: [
|
|
2020
|
+
{
|
|
2021
|
+
flags: "--approve",
|
|
2022
|
+
description: "accept it; it becomes the current version",
|
|
2023
|
+
},
|
|
2024
|
+
{ flags: "--decline", description: "turn it down" },
|
|
2025
|
+
{ flags: "--note <text>", description: "why" },
|
|
2026
|
+
],
|
|
2027
|
+
examples: [
|
|
2028
|
+
"cbx review 41 --approve",
|
|
2029
|
+
'cbx review 41 --decline --note "wrong branch"',
|
|
2030
|
+
],
|
|
2031
|
+
run: version_commands_js_1.commandReview,
|
|
2032
|
+
},
|
|
2033
|
+
{
|
|
2034
|
+
/*
|
|
2035
|
+
Named for what it does rather than "delete", because it does not delete
|
|
2036
|
+
the version. The files go; the version stays in the history as a gap
|
|
2037
|
+
saying what was removed and by whom.
|
|
2038
|
+
*/
|
|
2039
|
+
name: "take-down",
|
|
2040
|
+
group: "Your projects",
|
|
2041
|
+
summary: "remove a version's files, for something published by mistake",
|
|
2042
|
+
usage: "take-down <n> --yes",
|
|
2043
|
+
detail: "The files go and do not come back. The version stays in the history as\n" +
|
|
2044
|
+
"a gap saying what was removed and by whom, so nothing is quietly\n" +
|
|
2045
|
+
"rewritten.",
|
|
2046
|
+
options: [
|
|
2047
|
+
{ flags: "--yes", description: "confirm; without it nothing happens" },
|
|
2048
|
+
{ flags: "--reason <text>", description: "recorded against the gap" },
|
|
2049
|
+
],
|
|
2050
|
+
run: version_commands_js_1.commandTakeDown,
|
|
2051
|
+
},
|
|
1808
2052
|
{
|
|
1809
2053
|
/*
|
|
1810
2054
|
A release is a version with a name on it, so this names one rather than
|
|
@@ -2093,7 +2337,7 @@ async function main(argv) {
|
|
|
2093
2337
|
if (spec.deprecatedBy) {
|
|
2094
2338
|
console.error(dim(`"${name}" is now "${spec.deprecatedBy}". The old name still works.`));
|
|
2095
2339
|
}
|
|
2096
|
-
return spec.run(parse(rest));
|
|
2340
|
+
return spec.run(parse(rest, spec));
|
|
2097
2341
|
}
|
|
2098
2342
|
/**
|
|
2099
2343
|
* Set the status and let Node wind down on its own.
|