@coderook/cli 0.45.0 → 0.46.1
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/cbx/src/core/upload.js +40 -0
- package/dist/cbx/src/core/worktree.js +77 -10
- package/dist/cbx/src/local/history.js +6 -3
- package/dist/cbx/src/local/snapshot.js +99 -22
- package/dist/cbx/src/local/store.js +26 -0
- package/dist/cbx/src/shared/native_engine.js +289 -0
- package/dist/cli/src/bin.js +86 -0
- package/dist/cli/src/cli.js +22 -2
- package/dist/cli/src/local_commands.js +68 -116
- package/dist/cli/src/local_status.js +72 -0
- package/dist/cli/src/network.js +21 -7
- package/dist/cli/src/reminder_gate.js +37 -0
- package/dist/cli/src/update_command.js +2 -1
- package/dist/cli/src/workspace_threads.js +3 -13
- package/engine-bin/darwin-arm64/cbx-engine +0 -0
- package/engine-bin/darwin-x64/cbx-engine +0 -0
- package/engine-bin/linux-arm64/cbx-engine +0 -0
- package/engine-bin/linux-x64/cbx-engine +0 -0
- package/engine-bin/win32-x64/cbx-engine.exe +0 -0
- package/package.json +5 -4
|
@@ -36,15 +36,7 @@ const config_js_1 = require("./config.js");
|
|
|
36
36
|
const project_choice_js_1 = require("./project_choice.js");
|
|
37
37
|
const project_commands_js_1 = require("./project_commands.js");
|
|
38
38
|
const publish_js_1 = require("./publish.js");
|
|
39
|
-
const
|
|
40
|
-
const paint = (code) => (value) => `${ESC}[${code}m${value}${ESC}[0m`;
|
|
41
|
-
const dim = paint("2");
|
|
42
|
-
const bold = paint("1");
|
|
43
|
-
const red = paint("31");
|
|
44
|
-
const green = paint("32");
|
|
45
|
-
const accent = paint("33");
|
|
46
|
-
const short = (id) => id.slice(0, 10);
|
|
47
|
-
const plural = (count, one, many = `${one}s`) => `${count.toLocaleString()} ${count === 1 ? one : many}`;
|
|
39
|
+
const local_status_js_1 = require("./local_status.js");
|
|
48
40
|
function text(parsed, ...names) {
|
|
49
41
|
for (const name of names) {
|
|
50
42
|
const value = parsed.flags.get(name);
|
|
@@ -77,23 +69,12 @@ function localFirst(local, otherwise, from = () => node_process_1.default.cwd())
|
|
|
77
69
|
return repository ? local(parsed, repository) : otherwise(parsed);
|
|
78
70
|
};
|
|
79
71
|
}
|
|
80
|
-
function printChanges(changes, limit = 50) {
|
|
81
|
-
const rows = [
|
|
82
|
-
...changes.added.map((file) => [green("added "), file]),
|
|
83
|
-
...changes.modified.map((file) => [accent("changed "), file]),
|
|
84
|
-
...changes.deleted.map((file) => [red("deleted "), file]),
|
|
85
|
-
].sort((left, right) => (left[1] < right[1] ? -1 : left[1] > right[1] ? 1 : 0));
|
|
86
|
-
for (const [mark, file] of rows.slice(0, limit))
|
|
87
|
-
console.log(` ${mark}${file}`);
|
|
88
|
-
if (rows.length > limit)
|
|
89
|
-
console.log(dim(` … and ${rows.length - limit} more`));
|
|
90
|
-
}
|
|
91
72
|
function describeSave(one) {
|
|
92
73
|
const when = new Date(one.created);
|
|
93
74
|
const date = Number.isNaN(when.getTime())
|
|
94
75
|
? one.created
|
|
95
76
|
: when.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" });
|
|
96
|
-
return `${accent(short(one.id))} ${one.message.split("\n")[0]} ${dim(`${one.author} · ${date}`)}`;
|
|
77
|
+
return `${(0, local_status_js_1.accent)((0, local_status_js_1.short)(one.id))} ${one.message.split("\n")[0]} ${(0, local_status_js_1.dim)(`${one.author} · ${date}`)}`;
|
|
97
78
|
}
|
|
98
79
|
/** Refusals are printed as sentences, not stack traces. */
|
|
99
80
|
async function reporting(work) {
|
|
@@ -102,29 +83,29 @@ async function reporting(work) {
|
|
|
102
83
|
}
|
|
103
84
|
catch (error) {
|
|
104
85
|
if (error instanceof remote_js_1.RemoteAhead || error instanceof remote_js_1.RemoteDiffers || error instanceof remote_js_1.NoRemote) {
|
|
105
|
-
console.error(red(error.message));
|
|
86
|
+
console.error((0, local_status_js_1.red)(error.message));
|
|
106
87
|
return 1;
|
|
107
88
|
}
|
|
108
89
|
const failure = (0, publish_js_1.classifyPublishFailure)(error);
|
|
109
90
|
if (failure?.kind === "credentials") {
|
|
110
|
-
console.error(red(failure.message));
|
|
111
|
-
console.error(dim("Take the key out of that save, or push it deliberately with --allow-secrets."));
|
|
91
|
+
console.error((0, local_status_js_1.red)(failure.message));
|
|
92
|
+
console.error((0, local_status_js_1.dim)("Take the key out of that save, or push it deliberately with --allow-secrets."));
|
|
112
93
|
return 1;
|
|
113
94
|
}
|
|
114
95
|
if (failure?.kind === "interrupted") {
|
|
115
|
-
console.error(red(`The connection failed part way: ${failure.message}`));
|
|
116
|
-
console.error(dim("Push again. Saves already on CodeRook are not sent twice."));
|
|
96
|
+
console.error((0, local_status_js_1.red)(`The connection failed part way: ${failure.message}`));
|
|
97
|
+
console.error((0, local_status_js_1.dim)("Push again. Saves already on CodeRook are not sent twice."));
|
|
117
98
|
return 1;
|
|
118
99
|
}
|
|
119
100
|
if (error instanceof history_js_1.UnsavedChanges || error instanceof history_js_1.UnresolvedConflicts) {
|
|
120
|
-
console.error(red(error.message));
|
|
101
|
+
console.error((0, local_status_js_1.red)(error.message));
|
|
121
102
|
for (const file of error.files.slice(0, 20))
|
|
122
103
|
console.error(` ${file}`);
|
|
123
104
|
if (error.files.length > 20)
|
|
124
|
-
console.error(dim(` … and ${error.files.length - 20} more`));
|
|
105
|
+
console.error((0, local_status_js_1.dim)(` … and ${error.files.length - 20} more`));
|
|
125
106
|
return 1;
|
|
126
107
|
}
|
|
127
|
-
console.error(red(error instanceof Error ? error.message : String(error)));
|
|
108
|
+
console.error((0, local_status_js_1.red)(error instanceof Error ? error.message : String(error)));
|
|
128
109
|
return 1;
|
|
129
110
|
}
|
|
130
111
|
}
|
|
@@ -134,13 +115,13 @@ async function commandInit(parsed) {
|
|
|
134
115
|
const folder = node_path_1.default.resolve(parsed.positional[0] ?? node_process_1.default.cwd());
|
|
135
116
|
const inside = await localHistory(folder);
|
|
136
117
|
if (inside && inside.root !== folder) {
|
|
137
|
-
console.error(red(`${folder} is already inside the history at ${inside.root}.`));
|
|
138
|
-
console.error(dim("One history per project. Run cbx commands from anywhere inside it."));
|
|
118
|
+
console.error((0, local_status_js_1.red)(`${folder} is already inside the history at ${inside.root}.`));
|
|
119
|
+
console.error((0, local_status_js_1.dim)("One history per project. Run cbx commands from anywhere inside it."));
|
|
139
120
|
return 1;
|
|
140
121
|
}
|
|
141
122
|
const repository = await (0, history_js_1.init)(folder, text(parsed, "line") ?? undefined);
|
|
142
|
-
console.log(`Started a history in ${bold(repository.root)} on ${accent(await repository.currentLine())}.`);
|
|
143
|
-
console.log(dim("It lives in .cbx/ and never leaves this machine by itself. Run " +
|
|
123
|
+
console.log(`Started a history in ${(0, local_status_js_1.bold)(repository.root)} on ${(0, local_status_js_1.accent)(await repository.currentLine())}.`);
|
|
124
|
+
console.log((0, local_status_js_1.dim)("It lives in .cbx/ and never leaves this machine by itself. Run " +
|
|
144
125
|
`cbx save -m "…" to record the folder as it is.`));
|
|
145
126
|
return 0;
|
|
146
127
|
});
|
|
@@ -152,7 +133,7 @@ async function commandSave(parsed) {
|
|
|
152
133
|
const message = text(parsed, "m", "message") ?? "";
|
|
153
134
|
const merging = await (0, merge_js_1.readMergeState)(repository);
|
|
154
135
|
if (!message.trim() && !merging) {
|
|
155
|
-
console.error(red('A save needs a message: cbx save -m "what changed"'));
|
|
136
|
+
console.error((0, local_status_js_1.red)('A save needs a message: cbx save -m "what changed"'));
|
|
156
137
|
return 1;
|
|
157
138
|
}
|
|
158
139
|
const interactive = node_process_1.default.stdout.isTTY;
|
|
@@ -167,7 +148,7 @@ async function commandSave(parsed) {
|
|
|
167
148
|
if (now - shown < 100 && done < total)
|
|
168
149
|
return;
|
|
169
150
|
shown = now;
|
|
170
|
-
node_process_1.default.stdout.write(`\r${dim(`Reading ${done.toLocaleString()} of ${total.toLocaleString()} files`)} `);
|
|
151
|
+
node_process_1.default.stdout.write(`\r${(0, local_status_js_1.dim)(`Reading ${done.toLocaleString()} of ${total.toLocaleString()} files`)} `);
|
|
171
152
|
}
|
|
172
153
|
: undefined,
|
|
173
154
|
});
|
|
@@ -178,46 +159,17 @@ async function commandSave(parsed) {
|
|
|
178
159
|
return 0;
|
|
179
160
|
}
|
|
180
161
|
const count = (0, history_js_1.changeCount)(result.changes);
|
|
181
|
-
console.log(`Saved ${accent(short(result.save.id))} on ${accent(result.save.line)}: ` +
|
|
162
|
+
console.log(`Saved ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(result.save.id))} on ${(0, local_status_js_1.accent)(result.save.line)}: ` +
|
|
182
163
|
`${result.save.message.split("\n")[0]}`);
|
|
183
|
-
console.log(dim(`${plural(count, "file")} changed · ${plural(result.files, "file")}, ` +
|
|
164
|
+
console.log((0, local_status_js_1.dim)(`${(0, local_status_js_1.plural)(count, "file")} changed · ${(0, local_status_js_1.plural)(result.files, "file")}, ` +
|
|
184
165
|
`${bytes(result.bytes)} in the save`));
|
|
185
|
-
printChanges(result.changes, 20);
|
|
166
|
+
(0, local_status_js_1.printChanges)(result.changes, 20);
|
|
186
167
|
return 0;
|
|
187
168
|
});
|
|
188
169
|
}
|
|
189
170
|
/* ---- status ---------------------------------------------------------- */
|
|
190
171
|
async function localStatus(_parsed, repository) {
|
|
191
|
-
return reporting(
|
|
192
|
-
const now = await (0, history_js_1.status)(repository);
|
|
193
|
-
console.log(`${bold(node_path_1.default.basename(repository.root))} on ${accent(now.line)}` +
|
|
194
|
-
(now.head ? dim(` last saved ${short(now.head.id)}: ${now.head.message.split("\n")[0]}`) : dim(" nothing saved yet")));
|
|
195
|
-
if (now.merging) {
|
|
196
|
-
const left = now.merging.unresolved.length;
|
|
197
|
-
console.log(`Merging ${accent(short(now.merging.theirs))}: ` +
|
|
198
|
-
(left ? red(`${plural(left, "file")} still ${left === 1 ? "has" : "have"} conflict markers`) : green("no conflict markers left")));
|
|
199
|
-
for (const conflict of now.merging.conflicts) {
|
|
200
|
-
const how = conflict.kind === "text"
|
|
201
|
-
? now.merging.unresolved.includes(conflict.path) ? "markers in it" : "edited"
|
|
202
|
-
: conflict.kind === "binary"
|
|
203
|
-
? "both changed; this line's copy is in the folder"
|
|
204
|
-
: conflict.kind === "deleted-here"
|
|
205
|
-
? "deleted here, changed there; their copy is in the folder"
|
|
206
|
-
: "changed here, deleted there; this copy is in the folder";
|
|
207
|
-
console.log(` ${red("conflict")} ${conflict.path} ${dim(how)}`);
|
|
208
|
-
}
|
|
209
|
-
console.log(dim(`Finish with cbx save, take a side with cbx merge --mine or --theirs, or cbx merge --cancel.`));
|
|
210
|
-
}
|
|
211
|
-
const count = (0, history_js_1.changeCount)(now.changes);
|
|
212
|
-
if (!count) {
|
|
213
|
-
console.log("Nothing has changed since the last save.");
|
|
214
|
-
return 0;
|
|
215
|
-
}
|
|
216
|
-
console.log(`${plural(count, "file")} changed since the last save:`);
|
|
217
|
-
printChanges(now.changes);
|
|
218
|
-
console.log(dim(`cbx diff shows the lines; cbx save -m "…" records them.`));
|
|
219
|
-
return 0;
|
|
220
|
-
});
|
|
172
|
+
return reporting(() => (0, local_status_js_1.printLocalStatus)(repository));
|
|
221
173
|
}
|
|
222
174
|
/* ---- log ------------------------------------------------------------- */
|
|
223
175
|
async function localLog(parsed, repository) {
|
|
@@ -228,7 +180,7 @@ async function localLog(parsed, repository) {
|
|
|
228
180
|
limit: Number.isFinite(limit) && limit > 0 ? limit : 20,
|
|
229
181
|
});
|
|
230
182
|
if (!saves.length) {
|
|
231
|
-
console.log(`Nothing saved on ${accent(await repository.currentLine())} yet.`);
|
|
183
|
+
console.log(`Nothing saved on ${(0, local_status_js_1.accent)(await repository.currentLine())} yet.`);
|
|
232
184
|
return 0;
|
|
233
185
|
}
|
|
234
186
|
for (const one of saves)
|
|
@@ -252,30 +204,30 @@ async function localDiff(parsed, repository) {
|
|
|
252
204
|
console.log(to ? "Those two saves hold the same files." : "Nothing has changed since the last save.");
|
|
253
205
|
return 0;
|
|
254
206
|
}
|
|
255
|
-
const mark = { added: green("+"), modified: accent("~"), deleted: red("-") };
|
|
207
|
+
const mark = { added: (0, local_status_js_1.green)("+"), modified: (0, local_status_js_1.accent)("~"), deleted: (0, local_status_js_1.red)("-") };
|
|
256
208
|
for (const change of changes) {
|
|
257
|
-
console.log(`${mark[change.kind]} ${bold(change.path)}`);
|
|
209
|
+
console.log(`${mark[change.kind]} ${(0, local_status_js_1.bold)(change.path)}`);
|
|
258
210
|
if (has(parsed, "name-only"))
|
|
259
211
|
continue;
|
|
260
212
|
if (change.executableChanged)
|
|
261
|
-
console.log(dim(" execute permission changed"));
|
|
213
|
+
console.log((0, local_status_js_1.dim)(" execute permission changed"));
|
|
262
214
|
if (change.summary) {
|
|
263
|
-
console.log(dim(` ${change.summary}`));
|
|
215
|
+
console.log((0, local_status_js_1.dim)(` ${change.summary}`));
|
|
264
216
|
continue;
|
|
265
217
|
}
|
|
266
218
|
for (const chunk of change.chunks ?? []) {
|
|
267
219
|
if (chunk.kind === "gap") {
|
|
268
|
-
console.log(dim(` ... ${chunk.skipped} unchanged`));
|
|
220
|
+
console.log((0, local_status_js_1.dim)(` ... ${chunk.skipped} unchanged`));
|
|
269
221
|
continue;
|
|
270
222
|
}
|
|
271
223
|
for (const line of chunk.lines) {
|
|
272
224
|
const at = String(line.candidateLine ?? line.targetLine ?? "").padStart(5);
|
|
273
225
|
if (line.side === "same")
|
|
274
|
-
console.log(dim(`${at} ${line.text}`));
|
|
226
|
+
console.log((0, local_status_js_1.dim)(`${at} ${line.text}`));
|
|
275
227
|
else if (line.side === "candidate")
|
|
276
|
-
console.log(`${dim(at)} ${green("+")} ${green(line.text)}`);
|
|
228
|
+
console.log(`${(0, local_status_js_1.dim)(at)} ${(0, local_status_js_1.green)("+")} ${(0, local_status_js_1.green)(line.text)}`);
|
|
277
229
|
else
|
|
278
|
-
console.log(`${dim(at)} ${red("-")} ${red(line.text)}`);
|
|
230
|
+
console.log(`${(0, local_status_js_1.dim)(at)} ${(0, local_status_js_1.red)("-")} ${(0, local_status_js_1.red)(line.text)}`);
|
|
279
231
|
}
|
|
280
232
|
}
|
|
281
233
|
}
|
|
@@ -294,18 +246,18 @@ async function commandRestore(parsed) {
|
|
|
294
246
|
});
|
|
295
247
|
const touched = result.written.length + result.removed.length;
|
|
296
248
|
if (!touched) {
|
|
297
|
-
console.log(`Already matches ${accent(short(result.save.id))}; nothing to put back.`);
|
|
249
|
+
console.log(`Already matches ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(result.save.id))}; nothing to put back.`);
|
|
298
250
|
return 0;
|
|
299
251
|
}
|
|
300
|
-
console.log(`Put back ${plural(touched, "file")} from ${accent(short(result.save.id))}: ` +
|
|
252
|
+
console.log(`Put back ${(0, local_status_js_1.plural)(touched, "file")} from ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(result.save.id))}: ` +
|
|
301
253
|
`${result.save.message.split("\n")[0]}`);
|
|
302
254
|
for (const file of result.written.slice(0, 20))
|
|
303
|
-
console.log(` ${green("restored")} ${file}`);
|
|
255
|
+
console.log(` ${(0, local_status_js_1.green)("restored")} ${file}`);
|
|
304
256
|
for (const file of result.removed.slice(0, 20))
|
|
305
|
-
console.log(` ${red("removed ")} ${file}`);
|
|
257
|
+
console.log(` ${(0, local_status_js_1.red)("removed ")} ${file}`);
|
|
306
258
|
if (touched > 40)
|
|
307
|
-
console.log(dim(` … and more`));
|
|
308
|
-
console.log(dim("Your line has not moved. Save to record the folder as it is now."));
|
|
259
|
+
console.log((0, local_status_js_1.dim)(` … and more`));
|
|
260
|
+
console.log((0, local_status_js_1.dim)("Your line has not moved. Save to record the folder as it is now."));
|
|
309
261
|
return 0;
|
|
310
262
|
});
|
|
311
263
|
}
|
|
@@ -316,10 +268,10 @@ async function localSwitch(parsed, repository) {
|
|
|
316
268
|
if (!line) {
|
|
317
269
|
const current = await repository.currentLine();
|
|
318
270
|
for (const one of await repository.lines()) {
|
|
319
|
-
console.log(`${one.name === current ? accent("*") : " "} ${one.name} ${dim(short(one.tip))}`);
|
|
271
|
+
console.log(`${one.name === current ? (0, local_status_js_1.accent)("*") : " "} ${one.name} ${(0, local_status_js_1.dim)((0, local_status_js_1.short)(one.tip))}`);
|
|
320
272
|
}
|
|
321
273
|
if (!(await repository.lineExists(current))) {
|
|
322
|
-
console.log(`${accent("*")} ${current} ${dim("nothing saved yet")}`);
|
|
274
|
+
console.log(`${(0, local_status_js_1.accent)("*")} ${current} ${(0, local_status_js_1.dim)("nothing saved yet")}`);
|
|
323
275
|
}
|
|
324
276
|
return 0;
|
|
325
277
|
}
|
|
@@ -328,14 +280,14 @@ async function localSwitch(parsed, repository) {
|
|
|
328
280
|
force: has(parsed, "force", "f"),
|
|
329
281
|
});
|
|
330
282
|
if (result.created) {
|
|
331
|
-
console.log(`Started ${accent(result.line)}` +
|
|
332
|
-
(result.save ? ` from ${accent(short(result.save.id))}.` : ".") +
|
|
333
|
-
dim(" No files changed."));
|
|
283
|
+
console.log(`Started ${(0, local_status_js_1.accent)(result.line)}` +
|
|
284
|
+
(result.save ? ` from ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(result.save.id))}.` : ".") +
|
|
285
|
+
(0, local_status_js_1.dim)(" No files changed."));
|
|
334
286
|
return 0;
|
|
335
287
|
}
|
|
336
288
|
const touched = result.written.length + result.removed.length;
|
|
337
|
-
console.log(`On ${accent(result.line)} now` +
|
|
338
|
-
(touched ? `, ${plural(touched, "file")} changed.` : ", and no files needed changing."));
|
|
289
|
+
console.log(`On ${(0, local_status_js_1.accent)(result.line)} now` +
|
|
290
|
+
(touched ? `, ${(0, local_status_js_1.plural)(touched, "file")} changed.` : ", and no files needed changing."));
|
|
339
291
|
return 0;
|
|
340
292
|
});
|
|
341
293
|
}
|
|
@@ -397,25 +349,25 @@ async function localPush(parsed, repository) {
|
|
|
397
349
|
if (event.kind === "upload")
|
|
398
350
|
uploadDisplay.report(event.progress);
|
|
399
351
|
if (event.kind === "save" && interactive) {
|
|
400
|
-
node_process_1.default.stdout.write(`\r${dim(`Sending ${event.index + 1} of ${event.total}: ${event.save.message.split("\n")[0].slice(0, 50)}`)} `);
|
|
352
|
+
node_process_1.default.stdout.write(`\r${(0, local_status_js_1.dim)(`Sending ${event.index + 1} of ${event.total}: ${event.save.message.split("\n")[0].slice(0, 50)}`)} `);
|
|
401
353
|
}
|
|
402
354
|
if (event.kind === "pushed" || event.kind === "unchanged") {
|
|
403
355
|
uploadDisplay.done();
|
|
404
356
|
if (interactive)
|
|
405
357
|
node_process_1.default.stdout.write(`\r${" ".repeat(80)}\r`);
|
|
406
|
-
console.log(` ${accent(short(event.save.id))} ` +
|
|
407
|
-
(event.kind === "pushed" ? green(`v${event.sequence}`) : dim("already there")) +
|
|
358
|
+
console.log(` ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(event.save.id))} ` +
|
|
359
|
+
(event.kind === "pushed" ? (0, local_status_js_1.green)(`v${event.sequence}`) : (0, local_status_js_1.dim)("already there")) +
|
|
408
360
|
` ${event.save.message.split("\n")[0]}`);
|
|
409
361
|
}
|
|
410
362
|
},
|
|
411
363
|
}).finally(() => uploadDisplay.done());
|
|
412
364
|
if (!result.pushed.length) {
|
|
413
|
-
console.log(`Everything on ${accent(result.line)} is already on CodeRook.`);
|
|
365
|
+
console.log(`Everything on ${(0, local_status_js_1.accent)(result.line)} is already on CodeRook.`);
|
|
414
366
|
return 0;
|
|
415
367
|
}
|
|
416
368
|
const last = result.pushed[result.pushed.length - 1];
|
|
417
|
-
console.log(`Pushed ${plural(result.pushed.length, "save")} to CodeRook on ${accent(result.line)}; ` +
|
|
418
|
-
`it is at ${accent(`v${last.sequence}`)} now.`);
|
|
369
|
+
console.log(`Pushed ${(0, local_status_js_1.plural)(result.pushed.length, "save")} to CodeRook on ${(0, local_status_js_1.accent)(result.line)}; ` +
|
|
370
|
+
`it is at ${(0, local_status_js_1.accent)(`v${last.sequence}`)} now.`);
|
|
419
371
|
if (result.repositoryId && result.head) {
|
|
420
372
|
await recordLink(repository, result.line, result.repositoryId, result.head, result.head.manifest);
|
|
421
373
|
}
|
|
@@ -442,7 +394,7 @@ async function localPull(parsed, repository) {
|
|
|
442
394
|
if (!interactive)
|
|
443
395
|
return;
|
|
444
396
|
if (event.kind === "version") {
|
|
445
|
-
node_process_1.default.stdout.write(`\r${dim(`Fetching v${event.version.sequence} (${event.index + 1} of ${event.total})`)}${" ".repeat(20)}`);
|
|
397
|
+
node_process_1.default.stdout.write(`\r${(0, local_status_js_1.dim)(`Fetching v${event.version.sequence} (${event.index + 1} of ${event.total})`)}${" ".repeat(20)}`);
|
|
446
398
|
}
|
|
447
399
|
},
|
|
448
400
|
});
|
|
@@ -451,29 +403,29 @@ async function localPull(parsed, repository) {
|
|
|
451
403
|
for (const save of result.imported)
|
|
452
404
|
console.log(` ${describeSave(save)}`);
|
|
453
405
|
if (result.merged?.conflicts) {
|
|
454
|
-
console.log(`CodeRook's ${accent(result.line)} and this history had both moved on, and merging them left ` +
|
|
455
|
-
red(plural(result.merged.conflicts.length, "conflict")) + ":");
|
|
406
|
+
console.log(`CodeRook's ${(0, local_status_js_1.accent)(result.line)} and this history had both moved on, and merging them left ` +
|
|
407
|
+
(0, local_status_js_1.red)((0, local_status_js_1.plural)(result.merged.conflicts.length, "conflict")) + ":");
|
|
456
408
|
for (const conflict of result.merged.conflicts)
|
|
457
|
-
console.log(` ${red("conflict")} ${conflict.path}`);
|
|
458
|
-
console.log(dim("Fix them, then cbx save and cbx push. cbx status lists them; cbx merge --cancel calls it off."));
|
|
409
|
+
console.log(` ${(0, local_status_js_1.red)("conflict")} ${conflict.path}`);
|
|
410
|
+
console.log((0, local_status_js_1.dim)("Fix them, then cbx save and cbx push. cbx status lists them; cbx merge --cancel calls it off."));
|
|
459
411
|
return 1;
|
|
460
412
|
}
|
|
461
413
|
if (result.merged?.save) {
|
|
462
414
|
const touched = result.written.length + result.removed.length;
|
|
463
|
-
console.log(`Merged ${plural(result.imported.length, "save")} from CodeRook's ${accent(result.line)} with this history's own` +
|
|
464
|
-
(touched ? `; ${plural(touched, "file")} changed.` : ".") +
|
|
415
|
+
console.log(`Merged ${(0, local_status_js_1.plural)(result.imported.length, "save")} from CodeRook's ${(0, local_status_js_1.accent)(result.line)} with this history's own` +
|
|
416
|
+
(touched ? `; ${(0, local_status_js_1.plural)(touched, "file")} changed.` : ".") +
|
|
465
417
|
` Push to send the merge.`);
|
|
466
418
|
return 0;
|
|
467
419
|
}
|
|
468
420
|
if (!result.moved) {
|
|
469
421
|
console.log(result.imported.length
|
|
470
|
-
? `Fetched ${plural(result.imported.length, "save")}.`
|
|
471
|
-
: `Already up to date with CodeRook's ${accent(result.line)}.`);
|
|
422
|
+
? `Fetched ${(0, local_status_js_1.plural)(result.imported.length, "save")}.`
|
|
423
|
+
: `Already up to date with CodeRook's ${(0, local_status_js_1.accent)(result.line)}.`);
|
|
472
424
|
return 0;
|
|
473
425
|
}
|
|
474
426
|
const touched = result.written.length + result.removed.length;
|
|
475
|
-
console.log(`Pulled ${plural(result.imported.length, "save")} on ${accent(result.line)}` +
|
|
476
|
-
(touched ? `; ${plural(touched, "file")} changed.` : "."));
|
|
427
|
+
console.log(`Pulled ${(0, local_status_js_1.plural)(result.imported.length, "save")} on ${(0, local_status_js_1.accent)(result.line)}` +
|
|
428
|
+
(touched ? `; ${(0, local_status_js_1.plural)(touched, "file")} changed.` : "."));
|
|
477
429
|
const pulledFrom = (await (0, remote_js_1.readRemote)(repository)).repositoryId;
|
|
478
430
|
const tip = await repository.head();
|
|
479
431
|
if (pulledFrom && result.head && tip) {
|
|
@@ -489,7 +441,7 @@ async function localMerge(parsed, repository) {
|
|
|
489
441
|
return reporting(async () => {
|
|
490
442
|
if (has(parsed, "cancel", "abort")) {
|
|
491
443
|
const put = await (0, history_js_1.cancelMerge)(repository);
|
|
492
|
-
console.log(`Merge called off; ${plural(put.length, "file")} put back as the last save had them.`);
|
|
444
|
+
console.log(`Merge called off; ${(0, local_status_js_1.plural)(put.length, "file")} put back as the last save had them.`);
|
|
493
445
|
return 0;
|
|
494
446
|
}
|
|
495
447
|
const pathText = text(parsed, "path");
|
|
@@ -498,15 +450,15 @@ async function localMerge(parsed, repository) {
|
|
|
498
450
|
if (!has(parsed, side))
|
|
499
451
|
continue;
|
|
500
452
|
const settled = await (0, history_js_1.resolveConflicts)(repository, side, only);
|
|
501
|
-
console.log(`Took ${side === "mine" ? "this line's" : "the other"} side of ${plural(settled.length, "file")}:`);
|
|
453
|
+
console.log(`Took ${side === "mine" ? "this line's" : "the other"} side of ${(0, local_status_js_1.plural)(settled.length, "file")}:`);
|
|
502
454
|
for (const file of settled)
|
|
503
455
|
console.log(` ${file}`);
|
|
504
|
-
console.log(dim("Save when the rest are settled."));
|
|
456
|
+
console.log((0, local_status_js_1.dim)("Save when the rest are settled."));
|
|
505
457
|
return 0;
|
|
506
458
|
}
|
|
507
459
|
const other = parsed.positional[0];
|
|
508
460
|
if (!other) {
|
|
509
|
-
console.error(red("Merge what? Name a line or a save: cbx merge feature"));
|
|
461
|
+
console.error((0, local_status_js_1.red)("Merge what? Name a line or a save: cbx merge feature"));
|
|
510
462
|
return 1;
|
|
511
463
|
}
|
|
512
464
|
const outcome = await (0, history_js_1.merge)(repository, other, { message: text(parsed, "m", "message") });
|
|
@@ -515,16 +467,16 @@ async function localMerge(parsed, repository) {
|
|
|
515
467
|
console.log(`${other} is already part of this line.`);
|
|
516
468
|
}
|
|
517
469
|
else if (outcome.kind === "fast-forward") {
|
|
518
|
-
console.log(`Moved to ${accent(short(outcome.save.id))}; ${plural(touched, "file")} changed. Nothing needed merging.`);
|
|
470
|
+
console.log(`Moved to ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(outcome.save.id))}; ${(0, local_status_js_1.plural)(touched, "file")} changed. Nothing needed merging.`);
|
|
519
471
|
}
|
|
520
472
|
else if (outcome.kind === "merged") {
|
|
521
|
-
console.log(`Merged ${accent(other)} as ${accent(short(outcome.save.id))}; ${plural(touched, "file")} changed.`);
|
|
473
|
+
console.log(`Merged ${(0, local_status_js_1.accent)(other)} as ${(0, local_status_js_1.accent)((0, local_status_js_1.short)(outcome.save.id))}; ${(0, local_status_js_1.plural)(touched, "file")} changed.`);
|
|
522
474
|
}
|
|
523
475
|
else {
|
|
524
|
-
console.log(`Merged what could be merged; ${red(plural(outcome.state.conflicts.length, "conflict"))} left:`);
|
|
476
|
+
console.log(`Merged what could be merged; ${(0, local_status_js_1.red)((0, local_status_js_1.plural)(outcome.state.conflicts.length, "conflict"))} left:`);
|
|
525
477
|
for (const conflict of outcome.state.conflicts)
|
|
526
|
-
console.log(` ${red("conflict")} ${conflict.path}`);
|
|
527
|
-
console.log(dim("Fix them, then cbx save. Or take a side: cbx merge --theirs [--path file]. Or cbx merge --cancel."));
|
|
478
|
+
console.log(` ${(0, local_status_js_1.red)("conflict")} ${conflict.path}`);
|
|
479
|
+
console.log((0, local_status_js_1.dim)("Fix them, then cbx save. Or take a side: cbx merge --theirs [--path file]. Or cbx merge --cancel."));
|
|
528
480
|
return 1;
|
|
529
481
|
}
|
|
530
482
|
return 0;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.plural = exports.short = exports.accent = exports.green = exports.red = exports.bold = exports.dim = void 0;
|
|
7
|
+
exports.printChanges = printChanges;
|
|
8
|
+
exports.printLocalStatus = printLocalStatus;
|
|
9
|
+
/**
|
|
10
|
+
* What `cbx status` prints in a folder with a local history, kept apart from
|
|
11
|
+
* the rest of the local commands so it can run without loading them.
|
|
12
|
+
*
|
|
13
|
+
* Status is the command people run most, and in a local history it needs only
|
|
14
|
+
* the history itself: no sign-in, no network, none of the upload or project
|
|
15
|
+
* code. bin.ts answers it from here directly; the full command table uses the
|
|
16
|
+
* same function, so the two can never print differently.
|
|
17
|
+
*/
|
|
18
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
19
|
+
const history_js_1 = require("../../cbx/src/local/history.js");
|
|
20
|
+
const ESC = String.fromCharCode(27);
|
|
21
|
+
const paint = (code) => (value) => `${ESC}[${code}m${value}${ESC}[0m`;
|
|
22
|
+
exports.dim = paint("2");
|
|
23
|
+
exports.bold = paint("1");
|
|
24
|
+
exports.red = paint("31");
|
|
25
|
+
exports.green = paint("32");
|
|
26
|
+
exports.accent = paint("33");
|
|
27
|
+
const short = (id) => id.slice(0, 10);
|
|
28
|
+
exports.short = short;
|
|
29
|
+
const plural = (count, one, many = `${one}s`) => `${count.toLocaleString()} ${count === 1 ? one : many}`;
|
|
30
|
+
exports.plural = plural;
|
|
31
|
+
function printChanges(changes, limit = 50) {
|
|
32
|
+
const rows = [
|
|
33
|
+
...changes.added.map((file) => [(0, exports.green)("added "), file]),
|
|
34
|
+
...changes.modified.map((file) => [(0, exports.accent)("changed "), file]),
|
|
35
|
+
...changes.deleted.map((file) => [(0, exports.red)("deleted "), file]),
|
|
36
|
+
].sort((left, right) => (left[1] < right[1] ? -1 : left[1] > right[1] ? 1 : 0));
|
|
37
|
+
for (const [mark, file] of rows.slice(0, limit))
|
|
38
|
+
console.log(` ${mark}${file}`);
|
|
39
|
+
if (rows.length > limit)
|
|
40
|
+
console.log((0, exports.dim)(` … and ${rows.length - limit} more`));
|
|
41
|
+
}
|
|
42
|
+
/** The status of a local history, printed. */
|
|
43
|
+
async function printLocalStatus(repository) {
|
|
44
|
+
const now = await (0, history_js_1.status)(repository);
|
|
45
|
+
console.log(`${(0, exports.bold)(node_path_1.default.basename(repository.root))} on ${(0, exports.accent)(now.line)}` +
|
|
46
|
+
(now.head ? (0, exports.dim)(` last saved ${(0, exports.short)(now.head.id)}: ${now.head.message.split("\n")[0]}`) : (0, exports.dim)(" nothing saved yet")));
|
|
47
|
+
if (now.merging) {
|
|
48
|
+
const left = now.merging.unresolved.length;
|
|
49
|
+
console.log(`Merging ${(0, exports.accent)((0, exports.short)(now.merging.theirs))}: ` +
|
|
50
|
+
(left ? (0, exports.red)(`${(0, exports.plural)(left, "file")} still ${left === 1 ? "has" : "have"} conflict markers`) : (0, exports.green)("no conflict markers left")));
|
|
51
|
+
for (const conflict of now.merging.conflicts) {
|
|
52
|
+
const how = conflict.kind === "text"
|
|
53
|
+
? now.merging.unresolved.includes(conflict.path) ? "markers in it" : "edited"
|
|
54
|
+
: conflict.kind === "binary"
|
|
55
|
+
? "both changed; this line's copy is in the folder"
|
|
56
|
+
: conflict.kind === "deleted-here"
|
|
57
|
+
? "deleted here, changed there; their copy is in the folder"
|
|
58
|
+
: "changed here, deleted there; this copy is in the folder";
|
|
59
|
+
console.log(` ${(0, exports.red)("conflict")} ${conflict.path} ${(0, exports.dim)(how)}`);
|
|
60
|
+
}
|
|
61
|
+
console.log((0, exports.dim)(`Finish with cbx save, take a side with cbx merge --mine or --theirs, or cbx merge --cancel.`));
|
|
62
|
+
}
|
|
63
|
+
const count = (0, history_js_1.changeCount)(now.changes);
|
|
64
|
+
if (!count) {
|
|
65
|
+
console.log("Nothing has changed since the last save.");
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
console.log(`${(0, exports.plural)(count, "file")} changed since the last save:`);
|
|
69
|
+
printChanges(now.changes);
|
|
70
|
+
console.log((0, exports.dim)(`cbx diff shows the lines; cbx save -m "…" records them.`));
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
package/dist/cli/src/network.js
CHANGED
|
@@ -16,7 +16,6 @@ exports.pause = pause;
|
|
|
16
16
|
* than 250 ms is abandoned too, so a perfectly healthy connection came back as
|
|
17
17
|
* the two words "fetch failed". curl and browsers never showed it.
|
|
18
18
|
*/
|
|
19
|
-
const node_dns_1 = __importDefault(require("node:dns"));
|
|
20
19
|
const node_net_1 = __importDefault(require("node:net"));
|
|
21
20
|
/** Called once, before any request. Explicit settings in NODE_OPTIONS win. */
|
|
22
21
|
function tuneNetwork() {
|
|
@@ -30,12 +29,27 @@ function tuneNetwork() {
|
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
if (!options.includes("dns-result-order")) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
/*
|
|
33
|
+
Applied just before the first request rather than at start. Loading
|
|
34
|
+
node:dns sets up its resolver, about twenty milliseconds, which commands
|
|
35
|
+
that never touch the network (a local status, a local save) were paying
|
|
36
|
+
on every run. Every request here goes through fetch.
|
|
37
|
+
*/
|
|
38
|
+
const original = globalThis.fetch;
|
|
39
|
+
let tuned = false;
|
|
40
|
+
globalThis.fetch = ((...args) => {
|
|
41
|
+
if (!tuned) {
|
|
42
|
+
tuned = true;
|
|
43
|
+
try {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
45
|
+
require("node:dns").setDefaultResultOrder("ipv4first");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* Older Node: nothing to tune. */
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return original(...args);
|
|
52
|
+
});
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
55
|
/** Whether a failure happened before any answer came back. */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.reminderStamp = exports.REMIND_EVERY_MS = void 0;
|
|
7
|
+
exports.reminderDue = reminderDue;
|
|
8
|
+
/**
|
|
9
|
+
* Whether the unread-messages reminder is due after a command.
|
|
10
|
+
*
|
|
11
|
+
* Kept apart from workspace_threads.ts, which needs the API client, so the
|
|
12
|
+
* quick status in bin.ts can ask without loading it: when the reminder is
|
|
13
|
+
* due, status goes the long way and the reminder is shown exactly as before.
|
|
14
|
+
*/
|
|
15
|
+
const promises_1 = require("node:fs/promises");
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const config_js_1 = require("./config.js");
|
|
18
|
+
exports.REMIND_EVERY_MS = 5 * 60_000;
|
|
19
|
+
/** When the reminder last asked, at most every REMIND_EVERY_MS. */
|
|
20
|
+
const reminderStamp = () => node_path_1.default.join((0, config_js_1.configDirectory)(), "discussions-checked");
|
|
21
|
+
exports.reminderStamp = reminderStamp;
|
|
22
|
+
/** Only at a terminal, never for these commands, and at most every five minutes. */
|
|
23
|
+
async function reminderDue(command) {
|
|
24
|
+
if (!process.stdout.isTTY || process.env.CODEROOK_NO_REMINDERS)
|
|
25
|
+
return false;
|
|
26
|
+
if (["workspace", "sign-in", "sign-out", "help", "mcp"].includes(command))
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
const last = Number((await (0, promises_1.readFile)((0, exports.reminderStamp)(), "utf8")).trim());
|
|
30
|
+
if (Date.now() - last < exports.REMIND_EVERY_MS)
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* Never checked on this machine. */
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
@@ -132,7 +132,8 @@ async function commandUpdate(flags) {
|
|
|
132
132
|
const kind = installKind(script, root.code === 0 ? root.out.trim() : null);
|
|
133
133
|
const target = `${PACKAGE}@${newest}`;
|
|
134
134
|
if (kind === "npx") {
|
|
135
|
-
|
|
135
|
+
/* The package has three commands (cbx, coderook, git-remote-coderook), so npx must be told which: -p names the package. */
|
|
136
|
+
console.log(`This copy runs through npx. Ask for the newest each time with ${accent(`npx -p ${PACKAGE}@latest cbx`)}.`);
|
|
136
137
|
return 0;
|
|
137
138
|
}
|
|
138
139
|
if (kind === "other") {
|
|
@@ -31,6 +31,7 @@ const node_readline_1 = __importDefault(require("node:readline"));
|
|
|
31
31
|
const api_js_1 = require("./api.js");
|
|
32
32
|
const colour_js_1 = require("./colour.js");
|
|
33
33
|
const config_js_1 = require("./config.js");
|
|
34
|
+
const reminder_gate_js_1 = require("./reminder_gate.js");
|
|
34
35
|
const workspace_watch_js_1 = require("./workspace_watch.js");
|
|
35
36
|
exports.THREAD_OPERATIONS = new Set(["say", "thread", "mute", "unmute"]);
|
|
36
37
|
/** The name this machine posts and reads as, if it gave one. */
|
|
@@ -70,7 +71,6 @@ async function commandWorkspaceThread(operation, root, workspaceId, parsed) {
|
|
|
70
71
|
return 0;
|
|
71
72
|
}
|
|
72
73
|
/* ----------------------------------------------------------- the reminder */
|
|
73
|
-
const REMIND_EVERY_MS = 5 * 60_000;
|
|
74
74
|
/**
|
|
75
75
|
* One line after a command when a workspace thread has unread messages. Only
|
|
76
76
|
* at a terminal, at most every five minutes, never slower than 1.5 s, and
|
|
@@ -78,19 +78,9 @@ const REMIND_EVERY_MS = 5 * 60_000;
|
|
|
78
78
|
* a script.
|
|
79
79
|
*/
|
|
80
80
|
async function unreadReminder(command, parsed) {
|
|
81
|
-
if (!
|
|
81
|
+
if (!(await (0, reminder_gate_js_1.reminderDue)(command)))
|
|
82
82
|
return;
|
|
83
|
-
|
|
84
|
-
return;
|
|
85
|
-
const stamp = node_path_1.default.join((0, config_js_1.configDirectory)(), "discussions-checked");
|
|
86
|
-
try {
|
|
87
|
-
const last = Number((await (0, promises_1.readFile)(stamp, "utf8")).trim());
|
|
88
|
-
if (Date.now() - last < REMIND_EVERY_MS)
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
/* Never checked on this machine. */
|
|
93
|
-
}
|
|
83
|
+
const stamp = (0, reminder_gate_js_1.reminderStamp)();
|
|
94
84
|
try {
|
|
95
85
|
await (0, promises_1.mkdir)(node_path_1.default.dirname(stamp), { recursive: true });
|
|
96
86
|
await (0, promises_1.writeFile)(stamp, String(Date.now()));
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderook/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.1",
|
|
4
4
|
"description": "The command line for CodeRook (coderook.com): version control and project hosting for projects of any size. Every save is a compressed snapshot, private or public, never used to train AI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://coderook.com",
|
|
@@ -23,12 +23,13 @@
|
|
|
23
23
|
"node": ">=22.15.0"
|
|
24
24
|
},
|
|
25
25
|
"bin": {
|
|
26
|
-
"cbx": "dist/cli/src/
|
|
27
|
-
"coderook": "dist/cli/src/
|
|
26
|
+
"cbx": "dist/cli/src/bin.js",
|
|
27
|
+
"coderook": "dist/cli/src/bin.js",
|
|
28
28
|
"git-remote-coderook": "dist/cli/src/git_remote_bin.js"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
|
-
".claude-plugin",
|
|
31
|
+
".claude-plugin",
|
|
32
|
+
"engine-bin",
|
|
32
33
|
"dist",
|
|
33
34
|
"skills",
|
|
34
35
|
"NOTICE"
|