@coderook/cli 0.42.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, with cbx or plain git, from Claude Code.",
5
- "version": "0.42.0",
5
+ "version": "0.42.1",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
@@ -145,6 +145,26 @@ function key() {
145
145
  process.stdin.on("keypress", handler);
146
146
  });
147
147
  }
148
+ /*
149
+ A key, or nothing within `ms`. The views wait on this so they can look for
150
+ new messages while somebody is reading, rather than only when a key is
151
+ pressed: a thread that only updates on a keystroke reads as a dead one.
152
+ */
153
+ function keyWithin(ms) {
154
+ return new Promise((resolve) => {
155
+ const handler = (sequence, info) => {
156
+ clearTimeout(timer);
157
+ process.stdin.off("keypress", handler);
158
+ resolve({ name: info?.name ?? sequence, sequence, ctrl: Boolean(info?.ctrl) });
159
+ };
160
+ const timer = setTimeout(() => {
161
+ process.stdin.off("keypress", handler);
162
+ resolve(null);
163
+ }, ms);
164
+ process.stdin.on("keypress", handler);
165
+ });
166
+ }
167
+ const REFRESH_MS = 5000;
148
168
  function raw(on) {
149
169
  if (process.stdin.isTTY)
150
170
  process.stdin.setRawMode(on);
@@ -187,7 +207,19 @@ async function interactiveWorkspaces(parsed) {
187
207
  });
188
208
  console.log((0, colour_js_1.dim)(`\n${shown.length} open${filter ? ` matching "${filter}"` : ""} · ${listed.archived} archived (cbx workspace list <project> shows them)`));
189
209
  raw(true);
190
- const pressed = await key();
210
+ /* Redrawn only when something on it changed: new messages, or a workspace opened or closed. */
211
+ const drawn = JSON.stringify(listed.workspaces.map((w) => [w.id, w.unread, w.activeAt]));
212
+ let pressed = null;
213
+ while (!pressed) {
214
+ pressed = await keyWithin(REFRESH_MS);
215
+ if (pressed)
216
+ break;
217
+ const now = await (0, api_js_1.callApi)(`/v1/account/workspace-threads${join(asQuery(name))}`).catch(() => null);
218
+ if (now && JSON.stringify(now.workspaces.map((w) => [w.id, w.unread, w.activeAt])) !== drawn)
219
+ break;
220
+ }
221
+ if (!pressed)
222
+ continue;
191
223
  if (pressed.ctrl && pressed.name === "c")
192
224
  return 0;
193
225
  if (pressed.name === "q" || pressed.name === "escape")
@@ -217,7 +249,12 @@ async function openWorkspace(listed, name) {
217
249
  let notice = "";
218
250
  for (;;) {
219
251
  const shown = await (0, api_js_1.callApi)(root);
220
- const thread = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name))}`);
252
+ /* Every page, oldest first, so the newest messages are the ones shown (a page holds 50). */
253
+ let thread = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name))}`);
254
+ for (let more = 0; thread.next && more < 20; more += 1) {
255
+ const page = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name), `after=${encodeURIComponent(thread.next)}`)}`);
256
+ thread = { ...page, messages: [...thread.messages, ...page.messages] };
257
+ }
221
258
  const w = shown.workspace;
222
259
  clear();
223
260
  console.log(`${(0, colour_js_1.bold)(w.name)} ${(0, colour_js_1.dim)(`${listed.project} · ${w.track} · revision ${w.revision}${w.archived ? " · archived" : ""}`)}`);
@@ -240,7 +277,19 @@ async function openWorkspace(listed, name) {
240
277
  console.log(`\n${notice}`);
241
278
  notice = "";
242
279
  raw(true);
243
- const pressed = await key();
280
+ /* New messages appear on their own: a light check every few seconds, redrawn only when there is something new. */
281
+ const newestAt = thread.messages[thread.messages.length - 1]?.createdAt ?? null;
282
+ let pressed = null;
283
+ while (!pressed) {
284
+ pressed = await keyWithin(REFRESH_MS);
285
+ if (pressed)
286
+ break;
287
+ const peek = await (0, api_js_1.callApi)(`${root}/messages${join(asQuery(name), "peek=true", newestAt ? `after=${encodeURIComponent(newestAt)}` : "")}`).catch(() => null);
288
+ if (peek && peek.messages.length)
289
+ break;
290
+ }
291
+ if (!pressed)
292
+ continue;
244
293
  if (pressed.ctrl && pressed.name === "c")
245
294
  return true;
246
295
  if (pressed.name === "q" || pressed.name === "escape")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coderook/cli",
3
- "version": "0.42.0",
3
+ "version": "0.42.1",
4
4
  "description": "The command line for CodeRook (coderook.com): version control and project hosting for projects of any size. Every save is a compressed snapshot, private or public, never used to train AI.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://coderook.com",