@coderook/cli 0.31.8 → 0.31.10
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 +455 -389
- package/dist/cli/src/cli.js +32 -0
- package/dist/cli/src/config.js +21 -0
- package/dist/cli/src/project_commands.js +206 -0
- package/package.json +9 -3
- package/skills/coderook/SKILL.md +12 -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.31.
|
|
5
|
+
"version": "0.31.10",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ACCA Gaming Productions",
|
|
8
8
|
"url": "https://coderook.com"
|
package/README.md
CHANGED
|
@@ -1,389 +1,455 @@
|
|
|
1
|
-
# cbx
|
|
2
|
-
|
|
3
|
-
The command
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
npm
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`cbx
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
the
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
cbx
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
##
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
##
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
1
|
+
# cbx
|
|
2
|
+
|
|
3
|
+
The command line for [CodeRook](https://coderook.com), version control and
|
|
4
|
+
project hosting for projects of any size. Every save is a complete,
|
|
5
|
+
compressed snapshot of the folder; projects are private or public at your
|
|
6
|
+
choice; and nothing stored on CodeRook is used to train AI. There is also a
|
|
7
|
+
[desktop app](https://coderook.com/download) and the website, and all three do
|
|
8
|
+
the same things. [How CodeRook works](https://coderook.com/docs).
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
npm install -g @coderook/cli
|
|
12
|
+
cbx login
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The command is `cbx`, and the split it names is the same one git and GitHub
|
|
16
|
+
have. **CBX is the engine** — content-defined chunking, whole-snapshot
|
|
17
|
+
versions, the `.cbx` bundle format. **CodeRook is the host** it talks to. You
|
|
18
|
+
run `cbx`; you sign in to CodeRook.
|
|
19
|
+
|
|
20
|
+
That is why the names do not match everywhere, and the mismatch is on purpose:
|
|
21
|
+
|
|
22
|
+
| | |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `cbx …` | the tool — what you type |
|
|
25
|
+
| `coderook.com`, `@coderook/cli` | the service, and the package it ships in |
|
|
26
|
+
| `coderook://project` | a remote URL, which names the host it points at |
|
|
27
|
+
| `CODEROOK_TOKEN`, `%APPDATA%\CodeRook` | credentials for a CodeRook account |
|
|
28
|
+
|
|
29
|
+
It uses the same engines as the desktop application — the same scanner, the
|
|
30
|
+
same `.gitignore` rules, the same upload, download and `.cbx` bundling — so a
|
|
31
|
+
project moved by one is understood by the other.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
Requires Node 20.11 or later, and nothing else.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
npm install --global @coderook/cli
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
That installs `cbx`. It also installs `coderook` as an alias for it, so
|
|
42
|
+
anything written against the old name keeps working; the two are the same
|
|
43
|
+
program and either can be used.
|
|
44
|
+
|
|
45
|
+
Or from this repository:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
cd cli
|
|
49
|
+
npm install
|
|
50
|
+
npm run build
|
|
51
|
+
npm link
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Sign in
|
|
55
|
+
|
|
56
|
+
Create a personal access token in **Settings** on coderook.com, then:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
cbx sign-in
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The token is written to a file only your account can read:
|
|
63
|
+
|
|
64
|
+
| Platform | Location |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| Windows | `%APPDATA%\CodeRook` |
|
|
67
|
+
| macOS | `~/Library/Application Support/CodeRook` |
|
|
68
|
+
| Linux | `$XDG_CONFIG_HOME/coderook`, or `~/.config/coderook` |
|
|
69
|
+
|
|
70
|
+
For automation, set `CODEROOK_TOKEN` instead and nothing is written to disk.
|
|
71
|
+
`CODEROOK_API_URL` points the tool at a different service.
|
|
72
|
+
|
|
73
|
+
## Everyday use
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
cbx status what is here that is not saved yet
|
|
77
|
+
cbx submit -m "Fixed the parser"
|
|
78
|
+
cbx get fetch the latest version over this folder
|
|
79
|
+
cbx clone my-project fetch a project into a new folder
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`status` and `submit` act on the current directory unless you name another.
|
|
83
|
+
The first time either runs in a folder, it looks for a project on your account
|
|
84
|
+
whose name matches and links the two, so a folder you already uploaded from
|
|
85
|
+
the desktop is recognised rather than treated as new.
|
|
86
|
+
|
|
87
|
+
`submit` sends only what changed. Files the service already holds are not
|
|
88
|
+
uploaded again, and the version it records still names every file in the
|
|
89
|
+
project — a version is a snapshot, not a difference.
|
|
90
|
+
|
|
91
|
+
## Lines of work
|
|
92
|
+
|
|
93
|
+
A project can have more than one line, so two people can save without one
|
|
94
|
+
landing on top of the other.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
cbx tracks the lines this project has
|
|
98
|
+
cbx track the line this folder saves to
|
|
99
|
+
cbx track spike --new start a line and switch to it
|
|
100
|
+
cbx track main switch back
|
|
101
|
+
cbx submit --track spike -m "Try the other encoder"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Switching says where the next save goes and nothing else. No files move and
|
|
105
|
+
nothing is fetched, so it is instant and safe to change your mind — run
|
|
106
|
+
`cbx get` afterwards to bring that line's files into the folder.
|
|
107
|
+
|
|
108
|
+
The line is remembered per folder, not per account, so two checkouts of one
|
|
109
|
+
project can sit on different lines. A folder that has never been switched
|
|
110
|
+
saves to `main`, which is what every folder meant before lines existed.
|
|
111
|
+
|
|
112
|
+
A name that does not exist is refused rather than created. A typo in a branch
|
|
113
|
+
name is an ordinary thing to do, and a line called `mian` puts work somewhere
|
|
114
|
+
nobody will look for it — `--new` is how you say you meant it.
|
|
115
|
+
|
|
116
|
+
If two saves land on the same line anyway, the second becomes a merge waiting
|
|
117
|
+
on a decision. `cbx merges` lists them and `cbx merge <ref>` walks
|
|
118
|
+
through it; `cbx tracks` shows them beside the ordinary lines, because
|
|
119
|
+
somebody looking for where they can save needs to see the one they cannot.
|
|
120
|
+
|
|
121
|
+
## Proposals
|
|
122
|
+
|
|
123
|
+
A line gets back to main by being proposed. The proposal is checked and
|
|
124
|
+
reviewed before it lands, and it follows the line: saving onto it afterwards
|
|
125
|
+
changes what is proposed rather than needing a second one.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
cbx propose -m "Faster encoder" offer this folder's line to main
|
|
129
|
+
cbx proposals what is open (--all for the rest)
|
|
130
|
+
cbx merge M-3 look at one: reviews, checks, decisions
|
|
131
|
+
cbx merge M-3 --ask sam,priya ask people to review it
|
|
132
|
+
cbx merge M-3 --apply land it, once nothing is in the way
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`--into` proposes into a line other than main, and `--from` proposes a line
|
|
136
|
+
other than the one this folder is on. "Fixes #12" in the title or body closes
|
|
137
|
+
issue 12 when it lands. The same proposals are on the website's Merges page
|
|
138
|
+
and behind **propose** in the desktop application.
|
|
139
|
+
|
|
140
|
+
A line can require approvals, and an approval from whoever proposed it does
|
|
141
|
+
not count. Checks come from actions: `cbx actions --on-proposal Tests` runs
|
|
142
|
+
Tests whenever a line is proposed or saved onto, and its result is a required
|
|
143
|
+
check. `--on-save` runs an action on every save, and `--by-hand` only when
|
|
144
|
+
somebody starts it.
|
|
145
|
+
|
|
146
|
+
A **protected** line refuses saves made straight onto it — from `cbx submit`,
|
|
147
|
+
the desktop application and `git push` alike — so the way in is a line of your
|
|
148
|
+
own and a proposal. It also cannot be put back on an earlier save, and a merge
|
|
149
|
+
cannot land on it over a conflict or a failing check.
|
|
150
|
+
|
|
151
|
+
## Who can reach a project
|
|
152
|
+
|
|
153
|
+
cbx visibility # what it is now, and what each level means
|
|
154
|
+
cbx visibility public # list it on your profile
|
|
155
|
+
cbx visibility unlisted my-project # link only: kept off your profile and out of search
|
|
156
|
+
cbx visibility private # back to you and the people you invite
|
|
157
|
+
|
|
158
|
+
Making a project public or unlisted asks first and says what becomes readable.
|
|
159
|
+
In a script there is nobody to ask, so pass `--yes` (or `-y`); without it the
|
|
160
|
+
command refuses rather than waiting. Going private never asks.
|
|
161
|
+
|
|
162
|
+
A public project's page offers only its named versions that have not been
|
|
163
|
+
hidden (see `cbx promote` and `cbx mark --hide`).
|
|
164
|
+
|
|
165
|
+
## Ignore rules
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
cbx ignore show the rules in force
|
|
169
|
+
cbx ignore --init write a starter .gitignore
|
|
170
|
+
cbx ignore --suggest what is here that probably should not be sent
|
|
171
|
+
cbx ignore --suggest --apply add the confident ones
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`cbx rules` is the same command under another name, so anything already
|
|
175
|
+
written against it keeps working.
|
|
176
|
+
|
|
177
|
+
`--suggest` looks at what the folder would actually send and names the
|
|
178
|
+
dependency directories, virtual environments and build output in it, with how
|
|
179
|
+
much each is costing you. Lines marked `+` are safe to assume and are the ones
|
|
180
|
+
`--apply` writes; anything that might be the work itself is marked `?` and left
|
|
181
|
+
for you, because the cost of guessing wrong is a project that is not backed up.
|
|
182
|
+
|
|
183
|
+
Rules live in the project's own `.gitignore`, so CodeRook, git, the website
|
|
184
|
+
and the desktop application all read one file. A `!` line puts something back.
|
|
185
|
+
Personal exclusions that should not reach a collaborator belong in
|
|
186
|
+
`.git/info/exclude`.
|
|
187
|
+
|
|
188
|
+
In a git repository, a file git already tracks is sent even if a rule matches
|
|
189
|
+
it — the same exception git makes, so a broad rule written after the fact does
|
|
190
|
+
not quietly drop committed source. Git's own `.git` directory is never sent,
|
|
191
|
+
and the service refuses a save with any path inside one.
|
|
192
|
+
|
|
193
|
+
## Moving a repository here
|
|
194
|
+
|
|
195
|
+
`cbx import` takes a snapshot — the files as they are now, one version, no
|
|
196
|
+
history. To move off a host entirely, with the history intact:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
cbx transfer https://github.com/owner/project
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
GitHub, GitLab, Codeberg and any self-hosted Gitea or Forgejo. Every commit
|
|
203
|
+
becomes a version and every branch a line, so it takes a while on a long
|
|
204
|
+
history — the commit count and an estimate are printed before it starts.
|
|
205
|
+
|
|
206
|
+
**It goes through the public route on purpose.** The history is published by
|
|
207
|
+
running `git push` against the CodeRook remote, exactly as you would by hand.
|
|
208
|
+
A transfer that used a private path would prove only that a private path
|
|
209
|
+
exists.
|
|
210
|
+
|
|
211
|
+
| | |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| Comes across | commits, every branch, tags as releases, the description |
|
|
214
|
+
| With `--issues` and a read token | the open issues |
|
|
215
|
+
| Does not come across | pull requests and reviews, CI configuration, collaborators, stars |
|
|
216
|
+
|
|
217
|
+
The project is created **private** whatever the source was — publishing
|
|
218
|
+
somebody's code as a side effect of moving it is not recoverable. Pass
|
|
219
|
+
`--visibility same` to mirror the source, or `--visibility public`.
|
|
220
|
+
|
|
221
|
+
A private repository needs a read token in the environment — `GITHUB_TOKEN`,
|
|
222
|
+
`GITLAB_TOKEN` or `FORGE_TOKEN`. It is never taken as a flag, never stored,
|
|
223
|
+
and there is no OAuth flow: holding your forge credentials to run a one-off
|
|
224
|
+
migration sits badly beside a promise that your work is not handed to anybody.
|
|
225
|
+
|
|
226
|
+
The report at the end lists what arrived and what did not, rather than leaving
|
|
227
|
+
the absence to be discovered later.
|
|
228
|
+
|
|
229
|
+
## Using git instead
|
|
230
|
+
|
|
231
|
+
Installing the CLI also installs `git-remote-coderook`, so git talks to
|
|
232
|
+
CodeRook directly — both ways:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
git clone coderook://my-project
|
|
236
|
+
git push coderook main
|
|
237
|
+
git fetch
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
It exists so that every editor's built-in git panel works with CodeRook
|
|
241
|
+
without a plugin. If you are not already living in git, `cbx submit` is
|
|
242
|
+
the simpler tool and this is not an upgrade on it.
|
|
243
|
+
|
|
244
|
+
**Pushing.** Each commit becomes a version, oldest first, with its message
|
|
245
|
+
kept. Later pushes send only what is new, so the usual case — a few commits —
|
|
246
|
+
is quick; a first push of a long history is not, and the helper says how many
|
|
247
|
+
commits it is about to publish before it starts. Branches become lines, and a
|
|
248
|
+
branch pushed for the first time starts from the commit it forked at rather
|
|
249
|
+
than from wherever the project happens to be.
|
|
250
|
+
|
|
251
|
+
**Cloning and fetching.** Every version becomes a commit, keeping its message,
|
|
252
|
+
its date and who published it. This works on any project, including one that
|
|
253
|
+
has never been near git — a project built entirely with `cbx submit`
|
|
254
|
+
clones into a normal git history. A fetch only downloads versions this clone
|
|
255
|
+
does not already have.
|
|
256
|
+
|
|
257
|
+
**What it does not do:**
|
|
258
|
+
|
|
259
|
+
| | |
|
|
260
|
+
| --- | --- |
|
|
261
|
+
| Tag kind | A lightweight tag comes back annotated. CodeRook records a name, not which sort of tag made it. |
|
|
262
|
+
| Force pushes, branch deletion | Refused — versions are immutable, so there is nothing to rewind to. |
|
|
263
|
+
| Submodules | Skipped on push, and it says which paths. |
|
|
264
|
+
| The executable bit | Everything arrives as a normal file. A script cloned back needs `chmod +x`. |
|
|
265
|
+
|
|
266
|
+
**On round trips.** Push then clone gives you back the same *files*, not the
|
|
267
|
+
same *commit ids*. A commit id covers its author, committer and timestamps,
|
|
268
|
+
and CodeRook records who published a version rather than the original stamps —
|
|
269
|
+
so the history you get back is a faithful copy of the contents and an honest
|
|
270
|
+
approximation of the commits. Two people cloning the same project do get
|
|
271
|
+
identical ids as each other.
|
|
272
|
+
|
|
273
|
+
Merges are asymmetric for the same reason. A publish states one base version,
|
|
274
|
+
so pushing a git merge records one parent and the fork is lost — the merged
|
|
275
|
+
tree is exact, the shape is not. Reading back is richer: a version that really
|
|
276
|
+
has two parents is rebuilt as a real git merge.
|
|
277
|
+
|
|
278
|
+
Each version records the commit it came from, so a colleague pushing the same
|
|
279
|
+
project does not republish history the project already holds. Pushing a git
|
|
280
|
+
history into a project whose versions came from somewhere else is refused
|
|
281
|
+
rather than interleaved.
|
|
282
|
+
|
|
283
|
+
### The names are the same
|
|
284
|
+
|
|
285
|
+
There is no translation table to learn, because there is nothing to translate.
|
|
286
|
+
The commands carry git's names:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
cbx push cbx pull cbx clone
|
|
290
|
+
cbx log cbx status cbx branch
|
|
291
|
+
cbx checkout cbx merge cbx tag
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Each is the CodeRook command it always was, reachable by the name a git user
|
|
295
|
+
would reach for: `push` is `submit`, `pull` is `get`, `log` is `versions`,
|
|
296
|
+
`branch` is `tracks`, `checkout` is `track`, `tag` is `release`. The original
|
|
297
|
+
names still work and mean the same thing.
|
|
298
|
+
|
|
299
|
+
That is also what the git remote does. `git push` performs the operation
|
|
300
|
+
`cbx push` performs — the same code decides what changed, the same code
|
|
301
|
+
publishes it. The remote is a way to reach these commands from git, not a
|
|
302
|
+
second implementation of them that happens to agree.
|
|
303
|
+
|
|
304
|
+
**What does not appear on that list**, because git has no such idea and
|
|
305
|
+
inventing one would be pretending: `projects`, `delete`, `people`, `watch`,
|
|
306
|
+
`issues`, `propose`, `proposals`, `actions`, `runs`, `logs`, `runner`,
|
|
307
|
+
`tokens`, `licence`, `ai`. Use
|
|
308
|
+
them by name; nothing about them changes when a version arrives through git.
|
|
309
|
+
|
|
310
|
+
`git commit` has no CodeRook equivalent either, and should not: it is local to
|
|
311
|
+
git, and a version is not made until something is published.
|
|
312
|
+
|
|
313
|
+
### Tags and releases
|
|
314
|
+
|
|
315
|
+
`git push --tags` publishes each tag as a release, because that is the same
|
|
316
|
+
statement in the other vocabulary — the service puts it plainly: *"a release
|
|
317
|
+
is a Version with a name on it, not a different kind of object."*
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
git tag -a v1.0 -m "First stable release"
|
|
321
|
+
git push coderook v1.0
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The tag names the version its commit already became; it does not publish
|
|
325
|
+
anything, so push the branch first. An annotated tag's message becomes the
|
|
326
|
+
release notes and a lightweight tag brings none — the commit's own message is
|
|
327
|
+
deliberately *not* used, since nobody wrote it about the release.
|
|
328
|
+
|
|
329
|
+
Cloning reverses it: every release comes back as a tag on the right commit.
|
|
330
|
+
|
|
331
|
+
From the command line, `cbx release v1.0` names the newest save the same way,
|
|
332
|
+
and `cbx notes 41 --edit` writes its release notes.
|
|
333
|
+
|
|
334
|
+
Assets are separate and unaffected. They attach to a version from a run's
|
|
335
|
+
artifacts or with `cbx attach <file>`, so a release made by pushing a tag
|
|
336
|
+
simply has none until something adds them — which is what a release without a
|
|
337
|
+
build has always looked like.
|
|
338
|
+
|
|
339
|
+
## Bundles
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
cbx bundle pack this project as <name>.cbx
|
|
343
|
+
cbx unbundle backup.cbx extract one
|
|
344
|
+
cbx inspect backup.cbx see what it holds (--files to list them)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
A `.cbx` is the CodeBox bundle format: content-defined chunks, Zstandard where
|
|
348
|
+
it helps, raw where it does not, and a SHA-256 for every chunk and file.
|
|
349
|
+
Extraction is verified and atomic — a damaged bundle fails rather than leaving
|
|
350
|
+
a half-written tree.
|
|
351
|
+
|
|
352
|
+
## Claude Code
|
|
353
|
+
|
|
354
|
+
Run this **in a terminal**, not inside Claude Code:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
cbx skill
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
That is the whole setup. It writes `~/.claude/skills/coderook/SKILL.md`, and
|
|
361
|
+
from then on you ask for what you want in ordinary words:
|
|
362
|
+
|
|
363
|
+
> save this to CodeRook
|
|
364
|
+
|
|
365
|
+
> what have I changed?
|
|
366
|
+
|
|
367
|
+
> what versions does this project have?
|
|
368
|
+
|
|
369
|
+
Claude works out which commands to run. You can also call it by name with
|
|
370
|
+
`/coderook`.
|
|
371
|
+
|
|
372
|
+
**The skill is called `coderook`, not `cbx`.** `cbx` is the command line, run
|
|
373
|
+
in a terminal; the skill is the file that teaches Claude how to use it. Typing
|
|
374
|
+
`cbx skill` *into* Claude Code asks it for a skill by that name and it will
|
|
375
|
+
tell you there is not one — which is correct, and is why the line above says
|
|
376
|
+
where to run it.
|
|
377
|
+
|
|
378
|
+
`cbx skill --project` writes it into `./.claude` instead of your home
|
|
379
|
+
folder, so it travels with the repository and everybody who clones it has it
|
|
380
|
+
too.
|
|
381
|
+
|
|
382
|
+
Sign in once per machine first — `cbx sign-in` — or set `CODEROOK_TOKEN`
|
|
383
|
+
in automation. The skill will not save anything without asking you first, and
|
|
384
|
+
never deletes.
|
|
385
|
+
|
|
386
|
+
## Installing it as a plugin instead
|
|
387
|
+
|
|
388
|
+
If you would rather not install the command line first, CodeRook publishes a
|
|
389
|
+
plugin marketplace of its own:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
/plugin marketplace add https://coderook.com/marketplace.json
|
|
393
|
+
/plugin install coderook@coderook
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
That fetches the same package from npm and brings the skill with it. The
|
|
397
|
+
marketplace is a plain file on coderook.com rather than a git repository, so
|
|
398
|
+
there is no repository to clone and no second account anywhere.
|
|
399
|
+
|
|
400
|
+
One thing to know: a plugin install puts the package in Claude Code's plugin
|
|
401
|
+
folder, not on your `PATH`. The skill copes with that on its own by falling
|
|
402
|
+
back to `npx -y @coderook/cli`, but `npm install --global @coderook/cli` is
|
|
403
|
+
faster if you plan to run commands yourself as well.
|
|
404
|
+
|
|
405
|
+
## A structured connection instead
|
|
406
|
+
|
|
407
|
+
The skill teaches Claude the command line, which needs no configuration. If you
|
|
408
|
+
would rather it had structured tools, there is an MCP server as well. It works
|
|
409
|
+
with Codex too, which is the reason it exists.
|
|
410
|
+
|
|
411
|
+
Claude Code:
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
claude mcp add --scope user coderook -- npx -y @coderook/cli mcp
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Codex, in `~/.codex/config.toml`:
|
|
418
|
+
|
|
419
|
+
```toml
|
|
420
|
+
[mcp_servers.coderook]
|
|
421
|
+
command = "npx"
|
|
422
|
+
args = ["-y", "@coderook/cli", "mcp"]
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
To share it with everyone on a project, commit this beside the code:
|
|
426
|
+
|
|
427
|
+
```json
|
|
428
|
+
// .mcp.json
|
|
429
|
+
{
|
|
430
|
+
"mcpServers": {
|
|
431
|
+
"coderook": {
|
|
432
|
+
"command": "npx",
|
|
433
|
+
"args": ["-y", "@coderook/cli", "mcp"]
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
It offers five things, all of which read: your projects, a project's versions,
|
|
440
|
+
the files in a version, one file's contents at a version, and what has changed
|
|
441
|
+
in a local folder. It cannot save, delete, or sign in or out.
|
|
442
|
+
|
|
443
|
+
## Checking things
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
cbx doctor
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Reports the tool's version, where its configuration lives, whether the service
|
|
450
|
+
is reachable, and who this machine is signed in as.
|
|
451
|
+
|
|
452
|
+
## Exit codes
|
|
453
|
+
|
|
454
|
+
`0` on success, `1` on failure. Every command prints why it failed on standard
|
|
455
|
+
error, so a script can branch on the status and log the reason.
|
package/dist/cli/src/cli.js
CHANGED
|
@@ -2267,6 +2267,36 @@ const SPECS = [
|
|
|
2267
2267
|
],
|
|
2268
2268
|
run: project_commands_js_1.commandAi,
|
|
2269
2269
|
},
|
|
2270
|
+
{
|
|
2271
|
+
/*
|
|
2272
|
+
Not `publish`, which already reaches `submit` and would read as a
|
|
2273
|
+
release. This is who can reach the project, and it is named for that.
|
|
2274
|
+
*/
|
|
2275
|
+
name: "visibility",
|
|
2276
|
+
group: "Your projects",
|
|
2277
|
+
summary: "who can reach a project: private, link only or public",
|
|
2278
|
+
usage: "visibility [level] [project]",
|
|
2279
|
+
detail: "The level is public, unlisted or private. With none, says what the\n" +
|
|
2280
|
+
"project is set to now.\n\n" +
|
|
2281
|
+
"Private is you and the people you invite. Unlisted, which the website\n" +
|
|
2282
|
+
"calls link only, keeps it off your profile and out of search. Public\n" +
|
|
2283
|
+
"lists it on your profile, and its page offers the saves somebody named\n" +
|
|
2284
|
+
"and did not hide.\n\n" +
|
|
2285
|
+
"Opening a project up asks first and says what becomes readable: any\n" +
|
|
2286
|
+
"account signed in to CodeRook that has its link can open every save\n" +
|
|
2287
|
+
"through the API, not only the named versions. Going private asks\n" +
|
|
2288
|
+
"nothing.",
|
|
2289
|
+
options: [
|
|
2290
|
+
{ flags: "-y, --yes", description: "open it up without asking, for scripts" },
|
|
2291
|
+
],
|
|
2292
|
+
examples: [
|
|
2293
|
+
"cbx visibility",
|
|
2294
|
+
"cbx visibility public",
|
|
2295
|
+
"cbx visibility unlisted my-project --yes",
|
|
2296
|
+
"cbx visibility private my-project",
|
|
2297
|
+
],
|
|
2298
|
+
run: project_commands_js_1.commandVisibility,
|
|
2299
|
+
},
|
|
2270
2300
|
{
|
|
2271
2301
|
name: "issues",
|
|
2272
2302
|
group: "Your projects",
|
|
@@ -2715,6 +2745,8 @@ const SPECS = [
|
|
|
2715
2745
|
{ flags: "--theirs", description: "keep theirs" },
|
|
2716
2746
|
{ flags: "--both", description: "keep both" },
|
|
2717
2747
|
{ flags: "--drop", description: "abandon the upload" },
|
|
2748
|
+
{ flags: "--path <file>", description: "decide one file rather than every one outstanding" },
|
|
2749
|
+
{ flags: "--cancel", description: "call the merge off; your upload stays stored" },
|
|
2718
2750
|
{ flags: "--ask <usernames>", description: "ask people to review it, comma separated" },
|
|
2719
2751
|
],
|
|
2720
2752
|
run: commandMerge,
|
package/dist/cli/src/config.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.DamagedLinkError = exports.credentials = exports.DEFAULT_API = void 0;
|
|
7
7
|
exports.apiOrigin = apiOrigin;
|
|
8
|
+
exports.siteOrigin = siteOrigin;
|
|
8
9
|
exports.configDirectory = configDirectory;
|
|
9
10
|
exports.storeToken = storeToken;
|
|
10
11
|
exports.loadToken = loadToken;
|
|
@@ -33,6 +34,26 @@ exports.DEFAULT_API = "https://api.coderook.com";
|
|
|
33
34
|
function apiOrigin() {
|
|
34
35
|
return (process.env.CODEROOK_API_URL || exports.DEFAULT_API).replace(/\/+$/, "");
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The website that goes with the service, for a link somebody can open.
|
|
39
|
+
*
|
|
40
|
+
* Derived rather than configured: the service answers on `api.` in front of
|
|
41
|
+
* the site's own host, so taking that off gives the address people use. A
|
|
42
|
+
* service anywhere else — a local one on a port — has no site this can name,
|
|
43
|
+
* and null says so rather than printing a link that goes nowhere.
|
|
44
|
+
*/
|
|
45
|
+
function siteOrigin(api = apiOrigin()) {
|
|
46
|
+
try {
|
|
47
|
+
const url = new URL(api);
|
|
48
|
+
if (!url.hostname.startsWith("api."))
|
|
49
|
+
return null;
|
|
50
|
+
url.hostname = url.hostname.slice("api.".length);
|
|
51
|
+
return url.origin;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
36
57
|
/** The per-user configuration directory, per platform convention. */
|
|
37
58
|
function configDirectory() {
|
|
38
59
|
if (process.env.CODEROOK_CONFIG_DIR)
|
|
@@ -14,8 +14,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.resolveProject = resolveProject;
|
|
16
16
|
exports.commandAi = commandAi;
|
|
17
|
+
exports.readLevel = readLevel;
|
|
18
|
+
exports.splitVisibility = splitVisibility;
|
|
19
|
+
exports.offeredPublicly = offeredPublicly;
|
|
20
|
+
exports.commandVisibility = commandVisibility;
|
|
17
21
|
exports.commandVersions = commandVersions;
|
|
18
22
|
const node_process_1 = __importDefault(require("node:process"));
|
|
23
|
+
const promises_1 = require("node:readline/promises");
|
|
19
24
|
const api_js_1 = require("./api.js");
|
|
20
25
|
const config_js_1 = require("./config.js");
|
|
21
26
|
const dim = (value) => `[2m${value}[0m`;
|
|
@@ -121,6 +126,207 @@ async function commandAi(parsed) {
|
|
|
121
126
|
console.log(dim("one that refuses model training."));
|
|
122
127
|
return 0;
|
|
123
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Who can reach a project, in the order the site shows them.
|
|
131
|
+
*
|
|
132
|
+
* The service calls the middle one `unlisted` and the website calls it "Link
|
|
133
|
+
* only"; both are accepted, and the prose uses the website's words.
|
|
134
|
+
*/
|
|
135
|
+
const LEVELS = [
|
|
136
|
+
["private", "private", "you and the people you invite"],
|
|
137
|
+
[
|
|
138
|
+
"unlisted",
|
|
139
|
+
"link only",
|
|
140
|
+
"not listed anywhere; any account signed in with its link can read it",
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
"public",
|
|
144
|
+
"public",
|
|
145
|
+
"listed on your profile and in search; its page offers the named versions",
|
|
146
|
+
],
|
|
147
|
+
];
|
|
148
|
+
const wordFor = (level) => LEVELS.find(([name]) => name === level)?.[1] ?? level;
|
|
149
|
+
/** A typed word as a level, or null when it is not one. */
|
|
150
|
+
function readLevel(word) {
|
|
151
|
+
const text = (word ?? "").trim().toLowerCase();
|
|
152
|
+
if (text === "link" || text === "link-only")
|
|
153
|
+
return "unlisted";
|
|
154
|
+
const found = LEVELS.find(([name]) => name === text);
|
|
155
|
+
return found ? found[0] : null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* `[level] [project]`, both optional.
|
|
159
|
+
*
|
|
160
|
+
* The level is recognised by being one, so `cbx visibility my-project` asks
|
|
161
|
+
* and `cbx visibility public` changes. Two words where the first is not a
|
|
162
|
+
* level is a mistyped level rather than a project, and is said so — reading
|
|
163
|
+
* it as a project would answer "no project matching" to somebody who typed
|
|
164
|
+
* "pubic".
|
|
165
|
+
*/
|
|
166
|
+
function splitVisibility(positional) {
|
|
167
|
+
const [first, second] = positional;
|
|
168
|
+
const level = readLevel(first);
|
|
169
|
+
if (level)
|
|
170
|
+
return { level, project: second };
|
|
171
|
+
if (second !== undefined) {
|
|
172
|
+
return {
|
|
173
|
+
error: `"${first}" is not a visibility. Pick public, unlisted or private.`,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return { project: first };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The versions a public page offers, by the service's own rule.
|
|
180
|
+
*
|
|
181
|
+
* Named, not hidden, finished arriving and not taken down — the four things
|
|
182
|
+
* `public_rule.ts` asks. Counted here so the question before a project goes
|
|
183
|
+
* public can say what the page will show rather than describe it.
|
|
184
|
+
*/
|
|
185
|
+
function offeredPublicly(saved) {
|
|
186
|
+
return saved.filter((one) => Boolean(one.name) &&
|
|
187
|
+
one.visibility === "public" &&
|
|
188
|
+
one.state === "verified" &&
|
|
189
|
+
!one.removedAt);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The public page's address, once it answers.
|
|
193
|
+
*
|
|
194
|
+
* Asked of the service rather than assembled, because the page lives under
|
|
195
|
+
* the owner's name and the owner is not always the person running this — a
|
|
196
|
+
* collaborator can open up somebody else's project. A link is printed only
|
|
197
|
+
* when the public side has confirmed it is the same project.
|
|
198
|
+
*/
|
|
199
|
+
async function publicAddress(project) {
|
|
200
|
+
const site = (0, config_js_1.siteOrigin)();
|
|
201
|
+
if (!site)
|
|
202
|
+
return null;
|
|
203
|
+
const me = await (0, api_js_1.whoami)().catch(() => null);
|
|
204
|
+
if (!me?.username)
|
|
205
|
+
return null;
|
|
206
|
+
const found = await (0, api_js_1.findPublicProject)(me.username, project.slug);
|
|
207
|
+
if (found?.id !== project.id)
|
|
208
|
+
return null;
|
|
209
|
+
return `${site}/${encodeURIComponent(me.username)}/${encodeURIComponent(project.slug)}`;
|
|
210
|
+
}
|
|
211
|
+
/** A plain yes or no, where anything but yes is no. */
|
|
212
|
+
async function agreed(question) {
|
|
213
|
+
const prompt = (0, promises_1.createInterface)({
|
|
214
|
+
input: node_process_1.default.stdin,
|
|
215
|
+
output: node_process_1.default.stdout,
|
|
216
|
+
});
|
|
217
|
+
const typed = await prompt.question(question);
|
|
218
|
+
prompt.close();
|
|
219
|
+
return /^y(es)?$/i.test(typed.trim());
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Show or change who can reach a project.
|
|
223
|
+
*
|
|
224
|
+
* Opening one up asks first and says what becomes readable, because that is
|
|
225
|
+
* the step that cannot be taken back — somebody may already have read it by
|
|
226
|
+
* the time it is private again. Closing one asks nothing: it takes nothing
|
|
227
|
+
* from anybody who should have it.
|
|
228
|
+
*/
|
|
229
|
+
async function commandVisibility(parsed) {
|
|
230
|
+
const asked = splitVisibility(parsed.positional);
|
|
231
|
+
if (asked.error) {
|
|
232
|
+
console.error(red(asked.error));
|
|
233
|
+
return 1;
|
|
234
|
+
}
|
|
235
|
+
const project = await resolveProject(asked.project);
|
|
236
|
+
if (!project)
|
|
237
|
+
return 1;
|
|
238
|
+
const current = readLevel(project.visibility) ?? "private";
|
|
239
|
+
if (!asked.level) {
|
|
240
|
+
console.log(`${bold(project.name)} is ${accent(wordFor(current))}.`);
|
|
241
|
+
for (const [level, , meaning] of LEVELS) {
|
|
242
|
+
const mark = level === current ? accent("›") : " ";
|
|
243
|
+
console.log(` ${mark} ${level.padEnd(9)} ${dim(meaning)}`);
|
|
244
|
+
}
|
|
245
|
+
if (current === "public") {
|
|
246
|
+
const address = await publicAddress(project);
|
|
247
|
+
if (address)
|
|
248
|
+
console.log(`\n ${address}`);
|
|
249
|
+
}
|
|
250
|
+
console.log("");
|
|
251
|
+
console.log(dim(`Change it with: cbx visibility <level>` +
|
|
252
|
+
(asked.project ? ` ${asked.project}` : "")));
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
const target = asked.level;
|
|
256
|
+
if (target === current) {
|
|
257
|
+
const address = target === "public" ? await publicAddress(project) : null;
|
|
258
|
+
console.log(`${project.name} is already ${wordFor(target)}` +
|
|
259
|
+
(address ? `: ${address}` : "."));
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
const sure = parsed.flags.get("yes") === true || parsed.flags.get("y") === true;
|
|
263
|
+
const asking = target !== "private" && !sure;
|
|
264
|
+
/*
|
|
265
|
+
Refused rather than asked when nobody can answer. A prompt on a pipe
|
|
266
|
+
either hangs a script for ever or reads an empty line as no, and neither
|
|
267
|
+
tells whoever wrote the script what to do about it.
|
|
268
|
+
*/
|
|
269
|
+
if (asking && !node_process_1.default.stdin.isTTY) {
|
|
270
|
+
console.error(red(`Making ${project.name} ${wordFor(target)} needs a yes, and nobody is here to give one.`));
|
|
271
|
+
console.error(dim(" Run it in a terminal to be asked, or add --yes."));
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
const offered = target === "public" ? offeredPublicly(await (0, api_js_1.versions)(project.id)) : [];
|
|
275
|
+
if (asking) {
|
|
276
|
+
if (target === "public") {
|
|
277
|
+
console.log(`Making ${bold(project.name)} public lists it on your profile and in search.`);
|
|
278
|
+
if (offered.length) {
|
|
279
|
+
const names = offered.slice(0, 5).map((one) => one.name).join(", ");
|
|
280
|
+
const more = offered.length > 5 ? ` and ${offered.length - 5} more` : "";
|
|
281
|
+
console.log(` Its page offers ${offered.length} named version${offered.length === 1 ? "" : "s"}: ${names}${more}.`);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.log(" Its page will be empty until a save is named — cbx promote does that.");
|
|
285
|
+
}
|
|
286
|
+
console.log(dim(" Saves nobody named, and versions you hid, are not on the page."));
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
console.log(`Making ${bold(project.name)} link only keeps it off your profile and out of search.`);
|
|
290
|
+
}
|
|
291
|
+
/*
|
|
292
|
+
Said because it is true today, not because it is the design. The public
|
|
293
|
+
page follows the named-versions rule; the signed-in routes let any
|
|
294
|
+
account read an unlisted or public project whole, commits and hidden
|
|
295
|
+
versions included. Somebody deciding whether to open a project up
|
|
296
|
+
should be deciding about that.
|
|
297
|
+
*/
|
|
298
|
+
console.log(dim(" Any account signed in to CodeRook that has its link can open every save\n" +
|
|
299
|
+
" through the API, not only the named versions. Check the history holds\n" +
|
|
300
|
+
" nothing you would not share."));
|
|
301
|
+
if (!(await agreed(`Make ${project.name} ${wordFor(target)}? [y/N] `))) {
|
|
302
|
+
console.log(dim(`Nothing changed. ${project.name} is still ${wordFor(current)}.`));
|
|
303
|
+
return 1;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
await (0, api_js_1.updateProject)(project.id, { visibility: target });
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
console.error(red(error instanceof Error ? error.message : String(error)));
|
|
311
|
+
return 1;
|
|
312
|
+
}
|
|
313
|
+
if (target === "public") {
|
|
314
|
+
const address = await publicAddress(project);
|
|
315
|
+
console.log(green(`${project.name} is now public`) + (address ? `: ${address}` : "."));
|
|
316
|
+
console.log(dim(offered.length
|
|
317
|
+
? ` Its page offers ${offered.length} named version${offered.length === 1 ? "" : "s"}.`
|
|
318
|
+
: " Its page is empty until a save is named — cbx promote does that."));
|
|
319
|
+
}
|
|
320
|
+
else if (target === "unlisted") {
|
|
321
|
+
console.log(green(`${project.name} is now link only.`));
|
|
322
|
+
console.log(dim(" It is not listed anywhere; any account signed in with its link can read it."));
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
console.log(green(`${project.name} is now private.`));
|
|
326
|
+
console.log(dim(" Only you and the people you invite can reach it."));
|
|
327
|
+
}
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
124
330
|
/** Every saved version of a project, newest first. */
|
|
125
331
|
async function commandVersions(parsed) {
|
|
126
332
|
const project = await resolveProject(parsed.positional[0]);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coderook/cli",
|
|
3
|
-
"version": "0.31.
|
|
4
|
-
"description": "CodeRook
|
|
3
|
+
"version": "0.31.10",
|
|
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": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"homepage": "https://coderook.com",
|
|
7
7
|
"bugs": {
|
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"coderook",
|
|
12
|
+
"cbx",
|
|
13
|
+
"version-control",
|
|
14
|
+
"vcs",
|
|
12
15
|
"versioning",
|
|
16
|
+
"project-hosting",
|
|
13
17
|
"backup",
|
|
14
|
-
"
|
|
18
|
+
"snapshots",
|
|
19
|
+
"large-files",
|
|
20
|
+
"gamedev"
|
|
15
21
|
],
|
|
16
22
|
"engines": {
|
|
17
23
|
"node": ">=20.11.0"
|
package/skills/coderook/SKILL.md
CHANGED
|
@@ -103,6 +103,18 @@ project standing on it, so the next person to pull still lands on the mistake.
|
|
|
103
103
|
**Ask before `undo`, `promote` and `take-down`.** They change what other
|
|
104
104
|
people see. `cbx versions` first, so the person can say which save they mean.
|
|
105
105
|
|
|
106
|
+
## Who can reach a project
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cbx visibility # what it is now: private, unlisted or public
|
|
110
|
+
cbx visibility public --yes # list it on the owner's profile
|
|
111
|
+
cbx visibility private # back to the people invited
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Never make a project public or unlisted unless the person has asked for
|
|
115
|
+
exactly that.** `--yes` skips the question the command would otherwise ask
|
|
116
|
+
them, so only pass it once they have answered it. Going private is always safe.
|
|
117
|
+
|
|
106
118
|
## When two saves collide
|
|
107
119
|
|
|
108
120
|
If somebody saved while this folder was behind, the second save becomes a merge
|