@coderook/cli 0.22.2 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.22.1",
5
+ "version": "0.24.0",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
package/README.md CHANGED
@@ -1,220 +1,342 @@
1
- # CodeRook CLI
2
-
3
- CodeRook from the terminal, on Windows, macOS and Linux alike. It uses the
4
- same engines as the desktop application the same scanner, the same
5
- `.gitignore` rules, the same upload, download and `.cbx` bundling so a
6
- project moved by one is understood by the other.
7
-
8
- ## Install
9
-
10
- Requires Node 20.11 or later, and nothing else.
11
-
12
- ```
13
- npm install --global @coderook/cli
14
- ```
15
-
16
- Or from this repository:
17
-
18
- ```
19
- cd cli
20
- npm install
21
- npm run build
22
- npm link
23
- ```
24
-
25
- ## Sign in
26
-
27
- Create a personal access token in **Settings** on coderook.com, then:
28
-
29
- ```
30
- coderook sign-in
31
- ```
32
-
33
- The token is written to a file only your account can read:
34
-
35
- | Platform | Location |
36
- | --- | --- |
37
- | Windows | `%APPDATA%\CodeRook` |
38
- | macOS | `~/Library/Application Support/CodeRook` |
39
- | Linux | `$XDG_CONFIG_HOME/coderook`, or `~/.config/coderook` |
40
-
41
- For automation, set `CODEROOK_TOKEN` instead and nothing is written to disk.
42
- `CODEROOK_API_URL` points the tool at a different service.
43
-
44
- ## Everyday use
45
-
46
- ```
47
- coderook status what is here that is not saved yet
48
- coderook submit -m "Fixed the parser"
49
- coderook get fetch the latest version over this folder
50
- coderook clone my-project fetch a project into a new folder
51
- ```
52
-
53
- `status` and `submit` act on the current directory unless you name another.
54
- The first time either runs in a folder, it looks for a project on your account
55
- whose name matches and links the two, so a folder you already uploaded from
56
- the desktop is recognised rather than treated as new.
57
-
58
- `submit` sends only what changed. Files the service already holds are not
59
- uploaded again, and the version it records still names every file in the
60
- project — a version is a snapshot, not a difference.
61
-
62
- ## Lines of work
63
-
64
- A project can have more than one line, so two people can save without one
65
- landing on top of the other.
66
-
67
- ```
68
- coderook tracks the lines this project has
69
- coderook track the line this folder saves to
70
- coderook track spike --new start a line and switch to it
71
- coderook track main switch back
72
- coderook submit --track spike -m "Try the other encoder"
73
- ```
74
-
75
- Switching says where the next save goes and nothing else. No files move and
76
- nothing is fetched, so it is instant and safe to change your mind — run
77
- `coderook get` afterwards to bring that line's files into the folder.
78
-
79
- The line is remembered per folder, not per account, so two checkouts of one
80
- project can sit on different lines. A folder that has never been switched
81
- saves to `main`, which is what every folder meant before lines existed.
82
-
83
- A name that does not exist is refused rather than created. A typo in a branch
84
- name is an ordinary thing to do, and a line called `mian` puts work somewhere
85
- nobody will look for it — `--new` is how you say you meant it.
86
-
87
- If two saves land on the same line anyway, the second becomes a merge waiting
88
- on a decision. `coderook merges` lists them and `coderook merge <ref>` walks
89
- through it; `coderook tracks` shows them beside the ordinary lines, because
90
- somebody looking for where they can save needs to see the one they cannot.
91
-
92
- ## Ignore rules
93
-
94
- ```
95
- coderook rules show the rules in force
96
- coderook rules --init write a starter .gitignore
97
- coderook rules --suggest what is here that probably should not be sent
98
- coderook rules --suggest --apply add the confident ones
99
- ```
100
-
101
- `--suggest` looks at what the folder would actually send and names the
102
- dependency directories, virtual environments and build output in it, with how
103
- much each is costing you. Lines marked `+` are safe to assume and are the ones
104
- `--apply` writes; anything that might be the work itself is marked `?` and left
105
- for you, because the cost of guessing wrong is a project that is not backed up.
106
-
107
- Rules live in the project's own `.gitignore`, so CodeRook, git, the website
108
- and the desktop application all read one file. A `!` line puts something back.
109
- Personal exclusions that should not reach a collaborator belong in
110
- `.git/info/exclude`.
111
-
112
- ## Bundles
113
-
114
- ```
115
- coderook bundle pack this project as <name>.cbx
116
- coderook unbundle backup.cbx extract one
117
- coderook inspect backup.cbx see what it holds (--files to list them)
118
- ```
119
-
120
- A `.cbx` is the CodeBox bundle format: content-defined chunks, Zstandard where
121
- it helps, raw where it does not, and a SHA-256 for every chunk and file.
122
- Extraction is verified and atomic a damaged bundle fails rather than leaving
123
- a half-written tree.
124
-
125
- ## Claude Code
126
-
127
- ```
128
- coderook skill
129
- ```
130
-
131
- That is the whole setup. It writes a short guide into Claude Code's skills
132
- folder, and from then on you ask for what you want in ordinary words:
133
-
134
- > save this to CodeRook
135
-
136
- > what have I changed?
137
-
138
- > what versions does this project have?
139
-
140
- Claude works out which commands to run. You can also call it by name with
141
- `/coderook`.
142
-
143
- `coderook skill --project` writes it into `./.claude` instead of your home
144
- folder, so it travels with the repository and everybody who clones it has it
145
- too.
146
-
147
- Sign in once per machine first `coderook sign-in` or set `CODEROOK_TOKEN`
148
- in automation. The skill will not save anything without asking you first, and
149
- never deletes.
150
-
151
- ## Installing it as a plugin instead
152
-
153
- If you would rather not install the command line first, CodeRook publishes a
154
- plugin marketplace of its own:
155
-
156
- ```
157
- /plugin marketplace add https://coderook.com/marketplace.json
158
- /plugin install coderook@coderook
159
- ```
160
-
161
- That fetches the same package from npm and brings the skill with it. The
162
- marketplace is a plain file on coderook.com rather than a git repository, so
163
- there is no repository to clone and no second account anywhere.
164
-
165
- One thing to know: a plugin install puts the package in Claude Code's plugin
166
- folder, not on your `PATH`. The skill copes with that on its own by falling
167
- back to `npx -y @coderook/cli`, but `npm install --global @coderook/cli` is
168
- faster if you plan to run commands yourself as well.
169
-
170
- ## A structured connection instead
171
-
172
- The skill teaches Claude the command line, which needs no configuration. If you
173
- would rather it had structured tools, there is an MCP server as well. It works
174
- with Codex too, which is the reason it exists.
175
-
176
- Claude Code:
177
-
178
- ```
179
- claude mcp add --scope user coderook -- npx -y @coderook/cli mcp
180
- ```
181
-
182
- Codex, in `~/.codex/config.toml`:
183
-
184
- ```toml
185
- [mcp_servers.coderook]
186
- command = "npx"
187
- args = ["-y", "@coderook/cli", "mcp"]
188
- ```
189
-
190
- To share it with everyone on a project, commit this beside the code:
191
-
192
- ```json
193
- // .mcp.json
194
- {
195
- "mcpServers": {
196
- "coderook": {
197
- "command": "npx",
198
- "args": ["-y", "@coderook/cli", "mcp"]
199
- }
200
- }
201
- }
202
- ```
203
-
204
- It offers five things, all of which read: your projects, a project's versions,
205
- the files in a version, one file's contents at a version, and what has changed
206
- in a local folder. It cannot save, delete, or sign in or out.
207
-
208
- ## Checking things
209
-
210
- ```
211
- coderook doctor
212
- ```
213
-
214
- Reports the tool's version, where its configuration lives, whether the service
215
- is reachable, and who this machine is signed in as.
216
-
217
- ## Exit codes
218
-
219
- `0` on success, `1` on failure. Every command prints why it failed on standard
220
- error, so a script can branch on the status and log the reason.
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 rules show the rules in force
113
+ cbx rules --init write a starter .gitignore
114
+ cbx rules --suggest what is here that probably should not be sent
115
+ cbx rules --suggest --apply add the confident ones
116
+ ```
117
+
118
+ `--suggest` looks at what the folder would actually send and names the
119
+ dependency directories, virtual environments and build output in it, with how
120
+ much each is costing you. Lines marked `+` are safe to assume and are the ones
121
+ `--apply` writes; anything that might be the work itself is marked `?` and left
122
+ for you, because the cost of guessing wrong is a project that is not backed up.
123
+
124
+ Rules live in the project's own `.gitignore`, so CodeRook, git, the website
125
+ and the desktop application all read one file. A `!` line puts something back.
126
+ Personal exclusions that should not reach a collaborator belong in
127
+ `.git/info/exclude`.
128
+
129
+ ## Using git instead
130
+
131
+ Installing the CLI also installs `git-remote-coderook`, so git talks to
132
+ CodeRook directly both ways:
133
+
134
+ ```
135
+ git clone coderook://my-project
136
+ git push coderook main
137
+ git fetch
138
+ ```
139
+
140
+ It exists so that every editor's built-in git panel works with CodeRook
141
+ without a plugin. If you are not already living in git, `cbx submit` is
142
+ the simpler tool and this is not an upgrade on it.
143
+
144
+ **Pushing.** Each commit becomes a version, oldest first, with its message
145
+ kept. Later pushes send only what is new, so the usual case — a few commits —
146
+ is quick; a first push of a long history is not, and the helper says how many
147
+ commits it is about to publish before it starts. Branches become lines, and a
148
+ branch pushed for the first time starts from the commit it forked at rather
149
+ than from wherever the project happens to be.
150
+
151
+ **Cloning and fetching.** Every version becomes a commit, keeping its message,
152
+ its date and who published it. This works on any project, including one that
153
+ has never been near git a project built entirely with `cbx submit`
154
+ clones into a normal git history. A fetch only downloads versions this clone
155
+ does not already have.
156
+
157
+ **What it does not do:**
158
+
159
+ | | |
160
+ | --- | --- |
161
+ | Tag kind | A lightweight tag comes back annotated. CodeRook records a name, not which sort of tag made it. |
162
+ | Force pushes, branch deletion | Refused versions are immutable, so there is nothing to rewind to. |
163
+ | Submodules | Skipped on push, and it says which paths. |
164
+ | The executable bit | Everything arrives as a normal file. A script cloned back needs `chmod +x`. |
165
+
166
+ **On round trips.** Push then clone gives you back the same *files*, not the
167
+ same *commit ids*. A commit id covers its author, committer and timestamps,
168
+ and CodeRook records who published a version rather than the original stamps —
169
+ so the history you get back is a faithful copy of the contents and an honest
170
+ approximation of the commits. Two people cloning the same project do get
171
+ identical ids as each other.
172
+
173
+ Merges are asymmetric for the same reason. A publish states one base version,
174
+ so pushing a git merge records one parent and the fork is lost — the merged
175
+ tree is exact, the shape is not. Reading back is richer: a version that really
176
+ has two parents is rebuilt as a real git merge.
177
+
178
+ Each version records the commit it came from, so a colleague pushing the same
179
+ project does not republish history the project already holds. Pushing a git
180
+ history into a project whose versions came from somewhere else is refused
181
+ rather than interleaved.
182
+
183
+ ### The names are the same
184
+
185
+ There is no translation table to learn, because there is nothing to translate.
186
+ The commands carry git's names:
187
+
188
+ ```
189
+ cbx push cbx pull cbx clone
190
+ cbx log cbx status cbx branch
191
+ cbx checkout cbx merge cbx tag
192
+ ```
193
+
194
+ Each is the CodeRook command it always was, reachable by the name a git user
195
+ would reach for: `push` is `submit`, `pull` is `get`, `log` is `versions`,
196
+ `branch` is `tracks`, `checkout` is `track`, `tag` is `release`. The original
197
+ names still work and mean the same thing.
198
+
199
+ That is also what the git remote does. `git push` performs the operation
200
+ `cbx push` performs — the same code decides what changed, the same code
201
+ publishes it. The remote is a way to reach these commands from git, not a
202
+ second implementation of them that happens to agree.
203
+
204
+ **What does not appear on that list**, because git has no such idea and
205
+ inventing one would be pretending: `projects`, `delete`, `people`, `watch`,
206
+ `issues`, `actions`, `runs`, `logs`, `runner`, `tokens`, `licence`, `ai`. Use
207
+ them by name; nothing about them changes when a version arrives through git.
208
+
209
+ `git commit` has no CodeRook equivalent either, and should not: it is local to
210
+ git, and a version is not made until something is published.
211
+
212
+ ### Tags and releases
213
+
214
+ `git push --tags` publishes each tag as a release, because that is the same
215
+ statement in the other vocabulary the service puts it plainly: *"a release
216
+ is a Version with a name on it, not a different kind of object."*
217
+
218
+ ```
219
+ git tag -a v1.0 -m "First stable release"
220
+ git push coderook v1.0
221
+ ```
222
+
223
+ The tag names the version its commit already became; it does not publish
224
+ anything, so push the branch first. An annotated tag's message becomes the
225
+ release notes and a lightweight tag brings none — the commit's own message is
226
+ deliberately *not* used, since nobody wrote it about the release.
227
+
228
+ Cloning reverses it: every release comes back as a tag on the right commit.
229
+
230
+ Assets are separate and unaffected. They attach to a version from a run's
231
+ artifacts, so a release made by pushing a tag simply has none until something
232
+ adds them — which is what a release without a build has always looked like.
233
+
234
+ ## Bundles
235
+
236
+ ```
237
+ cbx bundle pack this project as <name>.cbx
238
+ cbx unbundle backup.cbx extract one
239
+ cbx inspect backup.cbx see what it holds (--files to list them)
240
+ ```
241
+
242
+ A `.cbx` is the CodeBox bundle format: content-defined chunks, Zstandard where
243
+ it helps, raw where it does not, and a SHA-256 for every chunk and file.
244
+ Extraction is verified and atomic — a damaged bundle fails rather than leaving
245
+ a half-written tree.
246
+
247
+ ## Claude Code
248
+
249
+ ```
250
+ cbx skill
251
+ ```
252
+
253
+ That is the whole setup. It writes a short guide into Claude Code's skills
254
+ folder, and from then on you ask for what you want in ordinary words:
255
+
256
+ > save this to CodeRook
257
+
258
+ > what have I changed?
259
+
260
+ > what versions does this project have?
261
+
262
+ Claude works out which commands to run. You can also call it by name with
263
+ `/coderook`.
264
+
265
+ `cbx skill --project` writes it into `./.claude` instead of your home
266
+ folder, so it travels with the repository and everybody who clones it has it
267
+ too.
268
+
269
+ Sign in once per machine first — `cbx sign-in` — or set `CODEROOK_TOKEN`
270
+ in automation. The skill will not save anything without asking you first, and
271
+ never deletes.
272
+
273
+ ## Installing it as a plugin instead
274
+
275
+ If you would rather not install the command line first, CodeRook publishes a
276
+ plugin marketplace of its own:
277
+
278
+ ```
279
+ /plugin marketplace add https://coderook.com/marketplace.json
280
+ /plugin install coderook@coderook
281
+ ```
282
+
283
+ That fetches the same package from npm and brings the skill with it. The
284
+ marketplace is a plain file on coderook.com rather than a git repository, so
285
+ there is no repository to clone and no second account anywhere.
286
+
287
+ One thing to know: a plugin install puts the package in Claude Code's plugin
288
+ folder, not on your `PATH`. The skill copes with that on its own by falling
289
+ back to `npx -y @coderook/cli`, but `npm install --global @coderook/cli` is
290
+ faster if you plan to run commands yourself as well.
291
+
292
+ ## A structured connection instead
293
+
294
+ The skill teaches Claude the command line, which needs no configuration. If you
295
+ would rather it had structured tools, there is an MCP server as well. It works
296
+ with Codex too, which is the reason it exists.
297
+
298
+ Claude Code:
299
+
300
+ ```
301
+ claude mcp add --scope user coderook -- npx -y @coderook/cli mcp
302
+ ```
303
+
304
+ Codex, in `~/.codex/config.toml`:
305
+
306
+ ```toml
307
+ [mcp_servers.coderook]
308
+ command = "npx"
309
+ args = ["-y", "@coderook/cli", "mcp"]
310
+ ```
311
+
312
+ To share it with everyone on a project, commit this beside the code:
313
+
314
+ ```json
315
+ // .mcp.json
316
+ {
317
+ "mcpServers": {
318
+ "coderook": {
319
+ "command": "npx",
320
+ "args": ["-y", "@coderook/cli", "mcp"]
321
+ }
322
+ }
323
+ }
324
+ ```
325
+
326
+ It offers five things, all of which read: your projects, a project's versions,
327
+ the files in a version, one file's contents at a version, and what has changed
328
+ in a local folder. It cannot save, delete, or sign in or out.
329
+
330
+ ## Checking things
331
+
332
+ ```
333
+ cbx doctor
334
+ ```
335
+
336
+ Reports the tool's version, where its configuration lives, whether the service
337
+ is reachable, and who this machine is signed in as.
338
+
339
+ ## Exit codes
340
+
341
+ `0` on success, `1` on failure. Every command prints why it failed on standard
342
+ error, so a script can branch on the status and log the reason.
@@ -12,6 +12,7 @@ exports.cancelMerge = cancelMerge;
12
12
  exports.aiAccess = aiAccess;
13
13
  exports.setAiAccess = setAiAccess;
14
14
  exports.versions = versions;
15
+ exports.markRelease = markRelease;
15
16
  exports.issues = issues;
16
17
  exports.createIssue = createIssue;
17
18
  exports.releases = releases;
@@ -31,7 +32,7 @@ const config_js_1 = require("./config.js");
31
32
  async function call(route, options = {}) {
32
33
  const token = await (0, config_js_1.loadToken)();
33
34
  if (!token) {
34
- throw new Error("Not signed in. Run: coderook sign-in");
35
+ throw new Error("Not signed in. Run: cbx sign-in");
35
36
  }
36
37
  const response = await fetch(`${(0, config_js_1.apiOrigin)()}${route}`, {
37
38
  method: options.method ?? "GET",
@@ -48,7 +49,7 @@ async function call(route, options = {}) {
48
49
  const body = text ? JSON.parse(text) : {};
49
50
  if (!response.ok) {
50
51
  if (response.status === 401) {
51
- throw new Error("That token was not accepted. Run: coderook sign-in");
52
+ throw new Error("That token was not accepted. Run: cbx sign-in");
52
53
  }
53
54
  throw new Error(body?.error?.message ?? `${route} failed (${response.status})`);
54
55
  }
@@ -158,8 +159,31 @@ async function versions(repositoryId) {
158
159
  fileCount: Number(row.fileCount ?? 0),
159
160
  storedSize: Number(row.storedSize ?? row.sourceSize ?? 0),
160
161
  createdAt: String(row.createdAt ?? ""),
162
+ parentVersionIds: Array.isArray(row.parentVersionIds)
163
+ ? row.parentVersionIds.map(String)
164
+ : [],
165
+ authorName: String(row.author?.displayName ?? ""),
161
166
  }));
162
167
  }
168
+ /**
169
+ * Give a version a name, which is all a release is.
170
+ *
171
+ * Used to carry a pushed git tag across. `markRelease` on the service side is
172
+ * an update on the version row rather than a new object, so this is idempotent
173
+ * — pushing the same tag twice renames the same version to the same thing
174
+ * rather than making a second release.
175
+ */
176
+ async function markRelease(repositoryId, versionId, name, notes) {
177
+ const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}` +
178
+ `/versions/${encodeURIComponent(versionId)}/release`,
179
+ // `call` serialises the body itself; handing it a string would send a
180
+ // JSON-encoded string where the service expects an object.
181
+ { method: "PUT", body: { name, ...(notes ? { notes } : {}) } });
182
+ return {
183
+ name: String(body.release?.name ?? name),
184
+ versionId: String(body.release?.versionId ?? versionId),
185
+ };
186
+ }
163
187
  async function issues(repositoryId) {
164
188
  const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/issues`);
165
189
  return {
@@ -188,6 +212,7 @@ async function createIssue(repositoryId, input) {
188
212
  async function releases(repositoryId) {
189
213
  const body = await call(`/v1/repositories/${encodeURIComponent(repositoryId)}/releases`);
190
214
  return (body.releases ?? []).map((row) => ({
215
+ versionId: String(row.versionId ?? ""),
191
216
  sequence: Number(row.sequence ?? 0),
192
217
  name: String(row.name ?? ""),
193
218
  notes: String(row.notes ?? ""),