@coderook/cli 0.31.7 → 0.31.9

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.
@@ -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.7",
5
+ "version": "0.31.9",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
package/README.md CHANGED
@@ -1,389 +1,401 @@
1
- # cbx
2
-
3
- The command is `cbx`, and the split it names is the same one git and GitHub
4
- have. **CBX is the engine** — content-defined chunking, whole-snapshot
5
- versions, the `.cbx` bundle format. **CodeRook is the host** it talks to. You
6
- run `cbx`; you sign in to CodeRook.
7
-
8
- That is why the names do not match everywhere, and the mismatch is on purpose:
9
-
10
- | | |
11
- | --- | --- |
12
- | `cbx …` | the tool — what you type |
13
- | `coderook.com`, `@coderook/cli` | the service, and the package it ships in |
14
- | `coderook://project` | a remote URL, which names the host it points at |
15
- | `CODEROOK_TOKEN`, `%APPDATA%\CodeRook` | credentials for a CodeRook account |
16
-
17
- It uses the same engines as the desktop application — the same scanner, the
18
- same `.gitignore` rules, the same upload, download and `.cbx` bundling — so a
19
- project moved by one is understood by the other.
20
-
21
- ## Install
22
-
23
- Requires Node 20.11 or later, and nothing else.
24
-
25
- ```
26
- npm install --global @coderook/cli
27
- ```
28
-
29
- That installs `cbx`. It also installs `coderook` as an alias for it, so
30
- anything written against the old name keeps working; the two are the same
31
- program and either can be used.
32
-
33
- Or from this repository:
34
-
35
- ```
36
- cd cli
37
- npm install
38
- npm run build
39
- npm link
40
- ```
41
-
42
- ## Sign in
43
-
44
- Create a personal access token in **Settings** on coderook.com, then:
45
-
46
- ```
47
- cbx sign-in
48
- ```
49
-
50
- The token is written to a file only your account can read:
51
-
52
- | Platform | Location |
53
- | --- | --- |
54
- | Windows | `%APPDATA%\CodeRook` |
55
- | macOS | `~/Library/Application Support/CodeRook` |
56
- | Linux | `$XDG_CONFIG_HOME/coderook`, or `~/.config/coderook` |
57
-
58
- For automation, set `CODEROOK_TOKEN` instead and nothing is written to disk.
59
- `CODEROOK_API_URL` points the tool at a different service.
60
-
61
- ## Everyday use
62
-
63
- ```
64
- cbx status what is here that is not saved yet
65
- cbx submit -m "Fixed the parser"
66
- cbx get fetch the latest version over this folder
67
- cbx clone my-project fetch a project into a new folder
68
- ```
69
-
70
- `status` and `submit` act on the current directory unless you name another.
71
- The first time either runs in a folder, it looks for a project on your account
72
- whose name matches and links the two, so a folder you already uploaded from
73
- the desktop is recognised rather than treated as new.
74
-
75
- `submit` sends only what changed. Files the service already holds are not
76
- uploaded again, and the version it records still names every file in the
77
- project — a version is a snapshot, not a difference.
78
-
79
- ## Lines of work
80
-
81
- A project can have more than one line, so two people can save without one
82
- landing on top of the other.
83
-
84
- ```
85
- cbx tracks the lines this project has
86
- cbx track the line this folder saves to
87
- cbx track spike --new start a line and switch to it
88
- cbx track main switch back
89
- cbx submit --track spike -m "Try the other encoder"
90
- ```
91
-
92
- Switching says where the next save goes and nothing else. No files move and
93
- nothing is fetched, so it is instant and safe to change your mind — run
94
- `cbx get` afterwards to bring that line's files into the folder.
95
-
96
- The line is remembered per folder, not per account, so two checkouts of one
97
- project can sit on different lines. A folder that has never been switched
98
- saves to `main`, which is what every folder meant before lines existed.
99
-
100
- A name that does not exist is refused rather than created. A typo in a branch
101
- name is an ordinary thing to do, and a line called `mian` puts work somewhere
102
- nobody will look for it — `--new` is how you say you meant it.
103
-
104
- If two saves land on the same line anyway, the second becomes a merge waiting
105
- on a decision. `cbx merges` lists them and `cbx merge <ref>` walks
106
- through it; `cbx tracks` shows them beside the ordinary lines, because
107
- somebody looking for where they can save needs to see the one they cannot.
108
-
109
- ## Ignore rules
110
-
111
- ```
112
- cbx ignore show the rules in force
113
- cbx ignore --init write a starter .gitignore
114
- cbx ignore --suggest what is here that probably should not be sent
115
- cbx ignore --suggest --apply add the confident ones
116
- ```
117
-
118
- `cbx rules` is the same command under another name, so anything already
119
- written against it keeps working.
120
-
121
- `--suggest` looks at what the folder would actually send and names the
122
- dependency directories, virtual environments and build output in it, with how
123
- much each is costing you. Lines marked `+` are safe to assume and are the ones
124
- `--apply` writes; anything that might be the work itself is marked `?` and left
125
- for you, because the cost of guessing wrong is a project that is not backed up.
126
-
127
- Rules live in the project's own `.gitignore`, so CodeRook, git, the website
128
- and the desktop application all read one file. A `!` line puts something back.
129
- Personal exclusions that should not reach a collaborator belong in
130
- `.git/info/exclude`.
131
-
132
- ## Moving a repository here
133
-
134
- `cbx import` takes a snapshot — the files as they are now, one version, no
135
- history. To move off a host entirely, with the history intact:
136
-
137
- ```
138
- cbx transfer https://github.com/owner/project
139
- ```
140
-
141
- GitHub, GitLab, Codeberg and any self-hosted Gitea or Forgejo. Every commit
142
- becomes a version and every branch a line, so it takes a while on a long
143
- history — the commit count and an estimate are printed before it starts.
144
-
145
- **It goes through the public route on purpose.** The history is published by
146
- running `git push` against the CodeRook remote, exactly as you would by hand.
147
- A transfer that used a private path would prove only that a private path
148
- exists.
149
-
150
- | | |
151
- | --- | --- |
152
- | Comes across | commits, every branch, tags as releases, the description |
153
- | With `--issues` and a read token | the open issues |
154
- | Does not come across | pull requests and reviews, CI configuration, collaborators, stars |
155
-
156
- The project is created **private** whatever the source was — publishing
157
- somebody's code as a side effect of moving it is not recoverable. Pass
158
- `--visibility same` to mirror the source, or `--public`.
159
-
160
- A private repository needs a read token in the environment — `GITHUB_TOKEN`,
161
- `GITLAB_TOKEN` or `FORGE_TOKEN`. It is never taken as a flag, never stored,
162
- and there is no OAuth flow: holding your forge credentials to run a one-off
163
- migration sits badly beside a promise that your work is not handed to anybody.
164
-
165
- The report at the end lists what arrived and what did not, rather than leaving
166
- the absence to be discovered later.
167
-
168
- ## Using git instead
169
-
170
- Installing the CLI also installs `git-remote-coderook`, so git talks to
171
- CodeRook directly — both ways:
172
-
173
- ```
174
- git clone coderook://my-project
175
- git push coderook main
176
- git fetch
177
- ```
178
-
179
- It exists so that every editor's built-in git panel works with CodeRook
180
- without a plugin. If you are not already living in git, `cbx submit` is
181
- the simpler tool and this is not an upgrade on it.
182
-
183
- **Pushing.** Each commit becomes a version, oldest first, with its message
184
- kept. Later pushes send only what is new, so the usual case — a few commits —
185
- is quick; a first push of a long history is not, and the helper says how many
186
- commits it is about to publish before it starts. Branches become lines, and a
187
- branch pushed for the first time starts from the commit it forked at rather
188
- than from wherever the project happens to be.
189
-
190
- **Cloning and fetching.** Every version becomes a commit, keeping its message,
191
- its date and who published it. This works on any project, including one that
192
- has never been near git — a project built entirely with `cbx submit`
193
- clones into a normal git history. A fetch only downloads versions this clone
194
- does not already have.
195
-
196
- **What it does not do:**
197
-
198
- | | |
199
- | --- | --- |
200
- | Tag kind | A lightweight tag comes back annotated. CodeRook records a name, not which sort of tag made it. |
201
- | Force pushes, branch deletion | Refused — versions are immutable, so there is nothing to rewind to. |
202
- | Submodules | Skipped on push, and it says which paths. |
203
- | The executable bit | Everything arrives as a normal file. A script cloned back needs `chmod +x`. |
204
-
205
- **On round trips.** Push then clone gives you back the same *files*, not the
206
- same *commit ids*. A commit id covers its author, committer and timestamps,
207
- and CodeRook records who published a version rather than the original stamps —
208
- so the history you get back is a faithful copy of the contents and an honest
209
- approximation of the commits. Two people cloning the same project do get
210
- identical ids as each other.
211
-
212
- Merges are asymmetric for the same reason. A publish states one base version,
213
- so pushing a git merge records one parent and the fork is lost — the merged
214
- tree is exact, the shape is not. Reading back is richer: a version that really
215
- has two parents is rebuilt as a real git merge.
216
-
217
- Each version records the commit it came from, so a colleague pushing the same
218
- project does not republish history the project already holds. Pushing a git
219
- history into a project whose versions came from somewhere else is refused
220
- rather than interleaved.
221
-
222
- ### The names are the same
223
-
224
- There is no translation table to learn, because there is nothing to translate.
225
- The commands carry git's names:
226
-
227
- ```
228
- cbx push cbx pull cbx clone
229
- cbx log cbx status cbx branch
230
- cbx checkout cbx merge cbx tag
231
- ```
232
-
233
- Each is the CodeRook command it always was, reachable by the name a git user
234
- would reach for: `push` is `submit`, `pull` is `get`, `log` is `versions`,
235
- `branch` is `tracks`, `checkout` is `track`, `tag` is `release`. The original
236
- names still work and mean the same thing.
237
-
238
- That is also what the git remote does. `git push` performs the operation
239
- `cbx push` performs — the same code decides what changed, the same code
240
- publishes it. The remote is a way to reach these commands from git, not a
241
- second implementation of them that happens to agree.
242
-
243
- **What does not appear on that list**, because git has no such idea and
244
- inventing one would be pretending: `projects`, `delete`, `people`, `watch`,
245
- `issues`, `actions`, `runs`, `logs`, `runner`, `tokens`, `licence`, `ai`. Use
246
- them by name; nothing about them changes when a version arrives through git.
247
-
248
- `git commit` has no CodeRook equivalent either, and should not: it is local to
249
- git, and a version is not made until something is published.
250
-
251
- ### Tags and releases
252
-
253
- `git push --tags` publishes each tag as a release, because that is the same
254
- statement in the other vocabulary — the service puts it plainly: *"a release
255
- is a Version with a name on it, not a different kind of object."*
256
-
257
- ```
258
- git tag -a v1.0 -m "First stable release"
259
- git push coderook v1.0
260
- ```
261
-
262
- The tag names the version its commit already became; it does not publish
263
- anything, so push the branch first. An annotated tag's message becomes the
264
- release notes and a lightweight tag brings none — the commit's own message is
265
- deliberately *not* used, since nobody wrote it about the release.
266
-
267
- Cloning reverses it: every release comes back as a tag on the right commit.
268
-
269
- Assets are separate and unaffected. They attach to a version from a run's
270
- artifacts, so a release made by pushing a tag simply has none until something
271
- adds them — which is what a release without a build has always looked like.
272
-
273
- ## Bundles
274
-
275
- ```
276
- cbx bundle pack this project as <name>.cbx
277
- cbx unbundle backup.cbx extract one
278
- cbx inspect backup.cbx see what it holds (--files to list them)
279
- ```
280
-
281
- A `.cbx` is the CodeBox bundle format: content-defined chunks, Zstandard where
282
- it helps, raw where it does not, and a SHA-256 for every chunk and file.
283
- Extraction is verified and atomic — a damaged bundle fails rather than leaving
284
- a half-written tree.
285
-
286
- ## Claude Code
287
-
288
- Run this **in a terminal**, not inside Claude Code:
289
-
290
- ```bash
291
- cbx skill
292
- ```
293
-
294
- That is the whole setup. It writes `~/.claude/skills/coderook/SKILL.md`, and
295
- from then on you ask for what you want in ordinary words:
296
-
297
- > save this to CodeRook
298
-
299
- > what have I changed?
300
-
301
- > what versions does this project have?
302
-
303
- Claude works out which commands to run. You can also call it by name with
304
- `/coderook`.
305
-
306
- **The skill is called `coderook`, not `cbx`.** `cbx` is the command line, run
307
- in a terminal; the skill is the file that teaches Claude how to use it. Typing
308
- `cbx skill` *into* Claude Code asks it for a skill by that name and it will
309
- tell you there is not one — which is correct, and is why the line above says
310
- where to run it.
311
-
312
- `cbx skill --project` writes it into `./.claude` instead of your home
313
- folder, so it travels with the repository and everybody who clones it has it
314
- too.
315
-
316
- Sign in once per machine first — `cbx sign-in` — or set `CODEROOK_TOKEN`
317
- in automation. The skill will not save anything without asking you first, and
318
- never deletes.
319
-
320
- ## Installing it as a plugin instead
321
-
322
- If you would rather not install the command line first, CodeRook publishes a
323
- plugin marketplace of its own:
324
-
325
- ```
326
- /plugin marketplace add https://coderook.com/marketplace.json
327
- /plugin install coderook@coderook
328
- ```
329
-
330
- That fetches the same package from npm and brings the skill with it. The
331
- marketplace is a plain file on coderook.com rather than a git repository, so
332
- there is no repository to clone and no second account anywhere.
333
-
334
- One thing to know: a plugin install puts the package in Claude Code's plugin
335
- folder, not on your `PATH`. The skill copes with that on its own by falling
336
- back to `npx -y @coderook/cli`, but `npm install --global @coderook/cli` is
337
- faster if you plan to run commands yourself as well.
338
-
339
- ## A structured connection instead
340
-
341
- The skill teaches Claude the command line, which needs no configuration. If you
342
- would rather it had structured tools, there is an MCP server as well. It works
343
- with Codex too, which is the reason it exists.
344
-
345
- Claude Code:
346
-
347
- ```
348
- claude mcp add --scope user coderook -- npx -y @coderook/cli mcp
349
- ```
350
-
351
- Codex, in `~/.codex/config.toml`:
352
-
353
- ```toml
354
- [mcp_servers.coderook]
355
- command = "npx"
356
- args = ["-y", "@coderook/cli", "mcp"]
357
- ```
358
-
359
- To share it with everyone on a project, commit this beside the code:
360
-
361
- ```json
362
- // .mcp.json
363
- {
364
- "mcpServers": {
365
- "coderook": {
366
- "command": "npx",
367
- "args": ["-y", "@coderook/cli", "mcp"]
368
- }
369
- }
370
- }
371
- ```
372
-
373
- It offers five things, all of which read: your projects, a project's versions,
374
- the files in a version, one file's contents at a version, and what has changed
375
- in a local folder. It cannot save, delete, or sign in or out.
376
-
377
- ## Checking things
378
-
379
- ```
380
- cbx doctor
381
- ```
382
-
383
- Reports the tool's version, where its configuration lives, whether the service
384
- is reachable, and who this machine is signed in as.
385
-
386
- ## Exit codes
387
-
388
- `0` on success, `1` on failure. Every command prints why it failed on standard
389
- error, so a script can branch on the status and log the reason.
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
+ ## Ignore rules
122
+
123
+ ```
124
+ cbx ignore show the rules in force
125
+ cbx ignore --init write a starter .gitignore
126
+ cbx ignore --suggest what is here that probably should not be sent
127
+ cbx ignore --suggest --apply add the confident ones
128
+ ```
129
+
130
+ `cbx rules` is the same command under another name, so anything already
131
+ written against it keeps working.
132
+
133
+ `--suggest` looks at what the folder would actually send and names the
134
+ dependency directories, virtual environments and build output in it, with how
135
+ much each is costing you. Lines marked `+` are safe to assume and are the ones
136
+ `--apply` writes; anything that might be the work itself is marked `?` and left
137
+ for you, because the cost of guessing wrong is a project that is not backed up.
138
+
139
+ Rules live in the project's own `.gitignore`, so CodeRook, git, the website
140
+ and the desktop application all read one file. A `!` line puts something back.
141
+ Personal exclusions that should not reach a collaborator belong in
142
+ `.git/info/exclude`.
143
+
144
+ ## Moving a repository here
145
+
146
+ `cbx import` takes a snapshot — the files as they are now, one version, no
147
+ history. To move off a host entirely, with the history intact:
148
+
149
+ ```
150
+ cbx transfer https://github.com/owner/project
151
+ ```
152
+
153
+ GitHub, GitLab, Codeberg and any self-hosted Gitea or Forgejo. Every commit
154
+ becomes a version and every branch a line, so it takes a while on a long
155
+ history — the commit count and an estimate are printed before it starts.
156
+
157
+ **It goes through the public route on purpose.** The history is published by
158
+ running `git push` against the CodeRook remote, exactly as you would by hand.
159
+ A transfer that used a private path would prove only that a private path
160
+ exists.
161
+
162
+ | | |
163
+ | --- | --- |
164
+ | Comes across | commits, every branch, tags as releases, the description |
165
+ | With `--issues` and a read token | the open issues |
166
+ | Does not come across | pull requests and reviews, CI configuration, collaborators, stars |
167
+
168
+ The project is created **private** whatever the source was — publishing
169
+ somebody's code as a side effect of moving it is not recoverable. Pass
170
+ `--visibility same` to mirror the source, or `--public`.
171
+
172
+ A private repository needs a read token in the environment — `GITHUB_TOKEN`,
173
+ `GITLAB_TOKEN` or `FORGE_TOKEN`. It is never taken as a flag, never stored,
174
+ and there is no OAuth flow: holding your forge credentials to run a one-off
175
+ migration sits badly beside a promise that your work is not handed to anybody.
176
+
177
+ The report at the end lists what arrived and what did not, rather than leaving
178
+ the absence to be discovered later.
179
+
180
+ ## Using git instead
181
+
182
+ Installing the CLI also installs `git-remote-coderook`, so git talks to
183
+ CodeRook directly — both ways:
184
+
185
+ ```
186
+ git clone coderook://my-project
187
+ git push coderook main
188
+ git fetch
189
+ ```
190
+
191
+ It exists so that every editor's built-in git panel works with CodeRook
192
+ without a plugin. If you are not already living in git, `cbx submit` is
193
+ the simpler tool and this is not an upgrade on it.
194
+
195
+ **Pushing.** Each commit becomes a version, oldest first, with its message
196
+ kept. Later pushes send only what is new, so the usual case — a few commits —
197
+ is quick; a first push of a long history is not, and the helper says how many
198
+ commits it is about to publish before it starts. Branches become lines, and a
199
+ branch pushed for the first time starts from the commit it forked at rather
200
+ than from wherever the project happens to be.
201
+
202
+ **Cloning and fetching.** Every version becomes a commit, keeping its message,
203
+ its date and who published it. This works on any project, including one that
204
+ has never been near git — a project built entirely with `cbx submit`
205
+ clones into a normal git history. A fetch only downloads versions this clone
206
+ does not already have.
207
+
208
+ **What it does not do:**
209
+
210
+ | | |
211
+ | --- | --- |
212
+ | Tag kind | A lightweight tag comes back annotated. CodeRook records a name, not which sort of tag made it. |
213
+ | Force pushes, branch deletion | Refused — versions are immutable, so there is nothing to rewind to. |
214
+ | Submodules | Skipped on push, and it says which paths. |
215
+ | The executable bit | Everything arrives as a normal file. A script cloned back needs `chmod +x`. |
216
+
217
+ **On round trips.** Push then clone gives you back the same *files*, not the
218
+ same *commit ids*. A commit id covers its author, committer and timestamps,
219
+ and CodeRook records who published a version rather than the original stamps —
220
+ so the history you get back is a faithful copy of the contents and an honest
221
+ approximation of the commits. Two people cloning the same project do get
222
+ identical ids as each other.
223
+
224
+ Merges are asymmetric for the same reason. A publish states one base version,
225
+ so pushing a git merge records one parent and the fork is lost — the merged
226
+ tree is exact, the shape is not. Reading back is richer: a version that really
227
+ has two parents is rebuilt as a real git merge.
228
+
229
+ Each version records the commit it came from, so a colleague pushing the same
230
+ project does not republish history the project already holds. Pushing a git
231
+ history into a project whose versions came from somewhere else is refused
232
+ rather than interleaved.
233
+
234
+ ### The names are the same
235
+
236
+ There is no translation table to learn, because there is nothing to translate.
237
+ The commands carry git's names:
238
+
239
+ ```
240
+ cbx push cbx pull cbx clone
241
+ cbx log cbx status cbx branch
242
+ cbx checkout cbx merge cbx tag
243
+ ```
244
+
245
+ Each is the CodeRook command it always was, reachable by the name a git user
246
+ would reach for: `push` is `submit`, `pull` is `get`, `log` is `versions`,
247
+ `branch` is `tracks`, `checkout` is `track`, `tag` is `release`. The original
248
+ names still work and mean the same thing.
249
+
250
+ That is also what the git remote does. `git push` performs the operation
251
+ `cbx push` performs — the same code decides what changed, the same code
252
+ publishes it. The remote is a way to reach these commands from git, not a
253
+ second implementation of them that happens to agree.
254
+
255
+ **What does not appear on that list**, because git has no such idea and
256
+ inventing one would be pretending: `projects`, `delete`, `people`, `watch`,
257
+ `issues`, `actions`, `runs`, `logs`, `runner`, `tokens`, `licence`, `ai`. Use
258
+ them by name; nothing about them changes when a version arrives through git.
259
+
260
+ `git commit` has no CodeRook equivalent either, and should not: it is local to
261
+ git, and a version is not made until something is published.
262
+
263
+ ### Tags and releases
264
+
265
+ `git push --tags` publishes each tag as a release, because that is the same
266
+ statement in the other vocabulary — the service puts it plainly: *"a release
267
+ is a Version with a name on it, not a different kind of object."*
268
+
269
+ ```
270
+ git tag -a v1.0 -m "First stable release"
271
+ git push coderook v1.0
272
+ ```
273
+
274
+ The tag names the version its commit already became; it does not publish
275
+ anything, so push the branch first. An annotated tag's message becomes the
276
+ release notes and a lightweight tag brings none — the commit's own message is
277
+ deliberately *not* used, since nobody wrote it about the release.
278
+
279
+ Cloning reverses it: every release comes back as a tag on the right commit.
280
+
281
+ Assets are separate and unaffected. They attach to a version from a run's
282
+ artifacts, so a release made by pushing a tag simply has none until something
283
+ adds them — which is what a release without a build has always looked like.
284
+
285
+ ## Bundles
286
+
287
+ ```
288
+ cbx bundle pack this project as <name>.cbx
289
+ cbx unbundle backup.cbx extract one
290
+ cbx inspect backup.cbx see what it holds (--files to list them)
291
+ ```
292
+
293
+ A `.cbx` is the CodeBox bundle format: content-defined chunks, Zstandard where
294
+ it helps, raw where it does not, and a SHA-256 for every chunk and file.
295
+ Extraction is verified and atomic — a damaged bundle fails rather than leaving
296
+ a half-written tree.
297
+
298
+ ## Claude Code
299
+
300
+ Run this **in a terminal**, not inside Claude Code:
301
+
302
+ ```bash
303
+ cbx skill
304
+ ```
305
+
306
+ That is the whole setup. It writes `~/.claude/skills/coderook/SKILL.md`, and
307
+ from then on you ask for what you want in ordinary words:
308
+
309
+ > save this to CodeRook
310
+
311
+ > what have I changed?
312
+
313
+ > what versions does this project have?
314
+
315
+ Claude works out which commands to run. You can also call it by name with
316
+ `/coderook`.
317
+
318
+ **The skill is called `coderook`, not `cbx`.** `cbx` is the command line, run
319
+ in a terminal; the skill is the file that teaches Claude how to use it. Typing
320
+ `cbx skill` *into* Claude Code asks it for a skill by that name and it will
321
+ tell you there is not one — which is correct, and is why the line above says
322
+ where to run it.
323
+
324
+ `cbx skill --project` writes it into `./.claude` instead of your home
325
+ folder, so it travels with the repository and everybody who clones it has it
326
+ too.
327
+
328
+ Sign in once per machine first — `cbx sign-in` — or set `CODEROOK_TOKEN`
329
+ in automation. The skill will not save anything without asking you first, and
330
+ never deletes.
331
+
332
+ ## Installing it as a plugin instead
333
+
334
+ If you would rather not install the command line first, CodeRook publishes a
335
+ plugin marketplace of its own:
336
+
337
+ ```
338
+ /plugin marketplace add https://coderook.com/marketplace.json
339
+ /plugin install coderook@coderook
340
+ ```
341
+
342
+ That fetches the same package from npm and brings the skill with it. The
343
+ marketplace is a plain file on coderook.com rather than a git repository, so
344
+ there is no repository to clone and no second account anywhere.
345
+
346
+ One thing to know: a plugin install puts the package in Claude Code's plugin
347
+ folder, not on your `PATH`. The skill copes with that on its own by falling
348
+ back to `npx -y @coderook/cli`, but `npm install --global @coderook/cli` is
349
+ faster if you plan to run commands yourself as well.
350
+
351
+ ## A structured connection instead
352
+
353
+ The skill teaches Claude the command line, which needs no configuration. If you
354
+ would rather it had structured tools, there is an MCP server as well. It works
355
+ with Codex too, which is the reason it exists.
356
+
357
+ Claude Code:
358
+
359
+ ```
360
+ claude mcp add --scope user coderook -- npx -y @coderook/cli mcp
361
+ ```
362
+
363
+ Codex, in `~/.codex/config.toml`:
364
+
365
+ ```toml
366
+ [mcp_servers.coderook]
367
+ command = "npx"
368
+ args = ["-y", "@coderook/cli", "mcp"]
369
+ ```
370
+
371
+ To share it with everyone on a project, commit this beside the code:
372
+
373
+ ```json
374
+ // .mcp.json
375
+ {
376
+ "mcpServers": {
377
+ "coderook": {
378
+ "command": "npx",
379
+ "args": ["-y", "@coderook/cli", "mcp"]
380
+ }
381
+ }
382
+ }
383
+ ```
384
+
385
+ It offers five things, all of which read: your projects, a project's versions,
386
+ the files in a version, one file's contents at a version, and what has changed
387
+ in a local folder. It cannot save, delete, or sign in or out.
388
+
389
+ ## Checking things
390
+
391
+ ```
392
+ cbx doctor
393
+ ```
394
+
395
+ Reports the tool's version, where its configuration lives, whether the service
396
+ is reachable, and who this machine is signed in as.
397
+
398
+ ## Exit codes
399
+
400
+ `0` on success, `1` on failure. Every command prints why it failed on standard
401
+ error, so a script can branch on the status and log the reason.
@@ -19,6 +19,7 @@ const node_path_1 = __importDefault(require("node:path"));
19
19
  const faults_js_1 = require("./faults.js");
20
20
  const identify_js_1 = require("./identify.js");
21
21
  const worktree_js_1 = require("./worktree.js");
22
+ const rules_js_1 = require("./rules.js");
22
23
  const publish_name_js_1 = require("./publish_name.js");
23
24
  const compress_js_1 = require("./compress.js");
24
25
  const compression_policy_js_1 = require("../shared/compression_policy.js");
@@ -142,6 +143,8 @@ class Uploader {
142
143
  */
143
144
  plannedLayouts = new Map();
144
145
  plannedQuote = null;
146
+ /** Files sent only because git tracks them; named in the save so the service allows them. */
147
+ trackedByGit = [];
145
148
  /*
146
149
  Digests worked out while planning, so sending does not work them out again.
147
150
 
@@ -407,6 +410,13 @@ class Uploader {
407
410
  const rules = request.allowIgnored
408
411
  ? { shared: "", local: "" }
409
412
  : await (0, worktree_js_1.readRules)(request.localPath);
413
+ /*
414
+ What the survey keeps only because git tracks it, named for the
415
+ service: its own check of the rules would otherwise refuse them as a
416
+ rule this client forgot. See gitTracked.
417
+ */
418
+ this.trackedByGit = [];
419
+ const tracked = request.allowIgnored ? null : await (0, worktree_js_1.gitTracked)(request.localPath);
410
420
  /*
411
421
  Surveyed, not listed.
412
422
 
@@ -443,6 +453,12 @@ class Uploader {
443
453
  percent: 6,
444
454
  bytesPerSecond: 0,
445
455
  })));
456
+ if (tracked) {
457
+ const layers = await (0, worktree_js_1.collectLayers)(request.localPath, rules);
458
+ this.trackedByGit = everything
459
+ .map((file) => file.path)
460
+ .filter((file) => tracked.files.has(file) && (0, rules_js_1.excludes)(file, false, layers));
461
+ }
446
462
  (0, profile_js_1.counted)("files surveyed", everything.length);
447
463
  const unticked = new Set(request.excluded ?? []);
448
464
  const ticked = request.excluded
@@ -854,6 +870,7 @@ class Uploader {
854
870
  : { expectedHeadVersionId: request.expectedHeadVersionId }),
855
871
  ...(request.track ? { track: request.track } : {}),
856
872
  ...(request.allowIgnored ? { allowIgnored: true } : {}),
873
+ ...(this.trackedByGit.length ? { trackedByGit: this.trackedByGit } : {}),
857
874
  ...(request.allowSecrets ? { allowSecrets: true } : {}),
858
875
  ...(request.linesAdded === undefined ? {} : { linesAdded: request.linesAdded }),
859
876
  ...(request.linesRemoved === undefined
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.IGNORE_HEADING = exports.KEEP_HEADING = exports.LOCAL_EXCLUDE_FILE = exports.IGNORE_FILE = exports.STARTER_IGNORE = exports.CODEROOK_TAIL = exports.CODEROOK_TAIL_HEADING = exports.KEPT_ANYWAY = exports.KEPT_ANYWAY_HEADING = exports.ALSO_LEFT_OUT = exports.ALSO_LEFT_OUT_HEADING = exports.STARTER_MARKER = exports.EVALUATION_FILE_LIMIT = void 0;
6
+ exports.IGNORE_HEADING = exports.KEEP_HEADING = exports.TRACKED_BY_GIT = exports.LOCAL_EXCLUDE_FILE = exports.IGNORE_FILE = exports.STARTER_IGNORE = exports.CODEROOK_TAIL = exports.CODEROOK_TAIL_HEADING = exports.KEPT_ANYWAY = exports.KEPT_ANYWAY_HEADING = exports.ALSO_LEFT_OUT = exports.ALSO_LEFT_OUT_HEADING = exports.STARTER_MARKER = exports.EVALUATION_FILE_LIMIT = void 0;
7
7
  exports.ruleTarget = ruleTarget;
8
8
  exports.keptTargets = keptTargets;
9
9
  exports.withoutOpposites = withoutOpposites;
@@ -11,6 +11,7 @@ exports.starterAuthored = starterAuthored;
11
11
  exports.missingStarterRules = missingStarterRules;
12
12
  exports.missingEngineRules = missingEngineRules;
13
13
  exports.isRepository = isRepository;
14
+ exports.gitTracked = gitTracked;
14
15
  exports.migrateRetiredRules = migrateRetiredRules;
15
16
  exports.starterFor = starterFor;
16
17
  exports.moveRule = moveRule;
@@ -404,6 +405,39 @@ async function isRepository(root) {
404
405
  return false;
405
406
  }
406
407
  }
408
+ /** The label a file kept because git tracks it carries, where a rule would be. */
409
+ exports.TRACKED_BY_GIT = "(tracked by git)";
410
+ /**
411
+ * The files git tracks here, which an ignore rule does not take away.
412
+ *
413
+ * Git applies .gitignore only to files it does not track yet. Once a file is
414
+ * committed, a rule written later — or a broad one like `storage/` that also
415
+ * matches a source folder three levels down — leaves it exactly where it is.
416
+ * This applied the rules to everything, so a project whose .gitignore said
417
+ * `storage/` arrived without `internal/storage/*.go`: in git, whole; on
418
+ * CodeRook, missing the six files that made it build.
419
+ *
420
+ * Null when the folder is not a repository, or git cannot be asked — then the
421
+ * rules alone decide, as they always did.
422
+ */
423
+ async function gitTracked(root) {
424
+ if (!(await isRepository(root)))
425
+ return null;
426
+ const listed = await git(root, "ls-files", "-z");
427
+ if (listed === null)
428
+ return null;
429
+ const files = new Set(listed.split(String.fromCharCode(0)).filter(Boolean));
430
+ const folders = new Set();
431
+ for (const file of files) {
432
+ for (let cut = file.lastIndexOf("/"); cut > 0; cut = file.lastIndexOf("/", cut - 1)) {
433
+ const folder = file.slice(0, cut);
434
+ if (folders.has(folder))
435
+ break;
436
+ folders.add(folder);
437
+ }
438
+ }
439
+ return { files, folders };
440
+ }
407
441
  /**
408
442
  * Convert the rules an earlier build wrote into one .gitignore.
409
443
  *
@@ -519,6 +553,15 @@ async function moveRule(root, file, keep) {
519
553
  const directory = await (0, promises_1.stat)(node_path_1.default.join(root, bare))
520
554
  .then((entry) => entry.isDirectory())
521
555
  .catch(() => file.endsWith("/"));
556
+ /*
557
+ A file git tracks cannot be ignored by a rule — git keeps it, and so does
558
+ this. Writing the rule anyway would look like it worked and change
559
+ nothing, so it is refused with what would actually leave it out.
560
+ */
561
+ if (!keep && !directory && (await gitTracked(root))?.files.has(bare)) {
562
+ throw new Error(`git tracks ${bare}, so a rule cannot leave it out. Untick it for one ` +
563
+ `save, or stop git tracking it: git rm --cached "${bare}"`);
564
+ }
522
565
  const target = directory ? `${bare}/` : bare;
523
566
  const line = keep ? `!${target}` : target;
524
567
  /*
@@ -885,6 +928,8 @@ stats,
885
928
  /** Filled with how much of the folder the cap allowed through. */
886
929
  outcome) {
887
930
  const layers = await collectLayers(root, rules);
931
+ /* What git already tracks goes up whatever a rule says; see gitTracked. */
932
+ const tracked = await gitTracked(root);
888
933
  const files = [];
889
934
  const present = new Set();
890
935
  const pending = [root];
@@ -926,7 +971,9 @@ outcome) {
926
971
  // Skipping an excluded directory outright is what keeps this fast,
927
972
  // but a `!` rule exists to rescue what is inside one, so the subtree
928
973
  // is only skipped when no negation could reach into it.
929
- if ((0, rules_js_1.excludes)(relative, true, layers) && !(0, rules_js_1.negationReachesInto)(relative, layers)) {
974
+ if ((0, rules_js_1.excludes)(relative, true, layers) &&
975
+ !(0, rules_js_1.negationReachesInto)(relative, layers) &&
976
+ !tracked?.folders.has(relative)) {
930
977
  continue;
931
978
  }
932
979
  pending.push(full);
@@ -934,7 +981,7 @@ outcome) {
934
981
  }
935
982
  if (!entry.isFile())
936
983
  continue;
937
- if ((0, rules_js_1.excludes)(relative, false, layers))
984
+ if ((0, rules_js_1.excludes)(relative, false, layers) && !tracked?.files.has(relative))
938
985
  continue;
939
986
  wanted.push({ full, relative });
940
987
  }
@@ -1060,6 +1107,7 @@ async function surveyFiles(root, rules,
1060
1107
  */
1061
1108
  onFound) {
1062
1109
  const layers = await collectLayers(root, rules);
1110
+ const tracked = await gitTracked(root);
1063
1111
  const found = [];
1064
1112
  /*
1065
1113
  A level at a time, eight in flight.
@@ -1094,7 +1142,8 @@ onFound) {
1094
1142
  const relative = node_path_1.default.relative(root, full).split(node_path_1.default.sep).join("/");
1095
1143
  if (entry.isDirectory()) {
1096
1144
  if ((0, rules_js_1.excludes)(relative, true, layers) &&
1097
- !(0, rules_js_1.negationReachesInto)(relative, layers)) {
1145
+ !(0, rules_js_1.negationReachesInto)(relative, layers) &&
1146
+ !tracked?.folders.has(relative)) {
1098
1147
  continue;
1099
1148
  }
1100
1149
  next.push(full);
@@ -1102,7 +1151,7 @@ onFound) {
1102
1151
  }
1103
1152
  if (!entry.isFile())
1104
1153
  continue;
1105
- if ((0, rules_js_1.excludes)(relative, false, layers))
1154
+ if ((0, rules_js_1.excludes)(relative, false, layers) && !tracked?.files.has(relative))
1106
1155
  continue;
1107
1156
  here.push({ full, relative });
1108
1157
  }
@@ -1408,6 +1457,7 @@ function relativeTo(root, full) {
1408
1457
  */
1409
1458
  async function evaluateRules(root, rules, options = {}) {
1410
1459
  const layers = await collectLayers(root, rules);
1460
+ const tracked = await gitTracked(root);
1411
1461
  const impactBytes = new Map();
1412
1462
  const impactFiles = new Map();
1413
1463
  const excluded = [];
@@ -1449,6 +1499,18 @@ async function evaluateRules(root, rules, options = {}) {
1449
1499
  });
1450
1500
  continue;
1451
1501
  }
1502
+ if (rule && tracked?.files.has(relative)) {
1503
+ /*
1504
+ A rule matches it, and git tracks it, so it goes up regardless — and
1505
+ is listed with what kept it, beside the files a `!` rule keeps.
1506
+ */
1507
+ keptFiles += 1;
1508
+ keptBytes += size;
1509
+ uploadFiles += 1;
1510
+ uploadBytes += size;
1511
+ excluded.push({ path: relative, bytes: size, rule: exports.TRACKED_BY_GIT, forceKept: true });
1512
+ continue;
1513
+ }
1452
1514
  if (rule) {
1453
1515
  skippedFiles += 1;
1454
1516
  skippedBytes += size;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderook/cli",
3
- "version": "0.31.7",
4
- "description": "CodeRook from the command line, on any operating system",
3
+ "version": "0.31.9",
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
- "cbx"
18
+ "snapshots",
19
+ "large-files",
20
+ "gamedev"
15
21
  ],
16
22
  "engines": {
17
23
  "node": ">=20.11.0"