@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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +378 -220
- package/dist/cli/src/api.js +41 -2
- package/dist/cli/src/cli.js +271 -78
- package/dist/cli/src/forge_url.js +68 -0
- package/dist/cli/src/git_history.js +188 -0
- package/dist/cli/src/git_remote.js +1217 -0
- package/dist/cli/src/git_remote_bin.js +22 -0
- package/dist/cli/src/help.js +8 -8
- package/dist/cli/src/licence_commands.js +5 -5
- package/dist/cli/src/mcp.js +1 -1
- package/dist/cli/src/publish.js +37 -0
- package/dist/cli/src/registry.js +1 -1
- package/dist/cli/src/runner.js +2 -2
- package/dist/cli/src/service_commands.js +53 -3
- package/dist/cli/src/skill_command.js +3 -3
- package/dist/cli/src/track_commands.js +7 -7
- package/dist/cli/src/transfer_command.js +331 -0
- package/dist/desktop-app/src/main/tracks.js +12 -2
- package/package.json +7 -4
- package/skills/coderook/SKILL.md +130 -130
|
@@ -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
|
+
});
|
package/dist/cli/src/help.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* What `
|
|
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("
|
|
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("
|
|
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("
|
|
51
|
+
`${bold("cbx " + spec.name)} — ${spec.summary}`,
|
|
52
52
|
"",
|
|
53
53
|
bold("Usage"),
|
|
54
|
-
`
|
|
54
|
+
` cbx ${spec.usage}`,
|
|
55
55
|
];
|
|
56
56
|
if (spec.aliases?.length) {
|
|
57
|
-
lines.push("", bold("Also"), ` ${spec.aliases.map((alias) => `
|
|
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("
|
|
86
|
-
lines.push(dim("Run
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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) {
|
package/dist/cli/src/mcp.js
CHANGED
|
@@ -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 \`
|
|
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
|
+
}
|
package/dist/cli/src/registry.js
CHANGED
|
@@ -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. `
|
|
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
|
*/
|
package/dist/cli/src/runner.js
CHANGED
|
@@ -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:
|
|
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: `
|
|
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:
|
|
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:
|
|
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:
|
|
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. `
|
|
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("
|
|
109
|
+
bold("cbx whoami") +
|
|
110
110
|
dim(" · if not: ") +
|
|
111
|
-
bold("
|
|
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 ") + "
|
|
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
|
-
* `
|
|
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 ") + "
|
|
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 ") + "
|
|
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 ") + `
|
|
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 ") + "
|
|
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 ") + `
|
|
154
|
+
console.log(dim("Its files are not here yet — fetch them with ") + `cbx get`);
|
|
155
155
|
}
|
|
156
156
|
return 0;
|
|
157
157
|
}
|