@staff0rd/assist 0.297.3 → 0.298.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.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.297.3",
9
+ version: "0.298.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -4897,17 +4897,25 @@ async function deleteItemComment(res, itemId, commentId) {
4897
4897
 
4898
4898
  // src/commands/backlog/web/rewindItemPhase.ts
4899
4899
  import { eq as eq14 } from "drizzle-orm";
4900
+
4901
+ // src/commands/backlog/resolveRewindPlan.ts
4902
+ function resolveRewindPlan(item) {
4903
+ return [...resolvePlan(item), buildReviewPhase()];
4904
+ }
4905
+
4906
+ // src/commands/backlog/web/rewindItemPhase.ts
4900
4907
  async function rewindItemPhase(req, res, id2) {
4901
4908
  const { phase, reason } = await parseRewindBody(req);
4902
4909
  const result = await findItemOr404(res, id2);
4903
4910
  if (!result) return;
4904
4911
  const { orm, item } = result;
4905
- const error = validateRewind(item, phase);
4912
+ const plan2 = resolveRewindPlan(item);
4913
+ const error = validateRewind(item, plan2, phase);
4906
4914
  if (error) {
4907
4915
  respondJson(res, 400, { error });
4908
4916
  return;
4909
4917
  }
4910
- const phaseName = item.plan?.[phase - 1].name;
4918
+ const phaseName = plan2[phase - 1].name;
4911
4919
  await appendComment(
4912
4920
  orm,
4913
4921
  id2,
@@ -4917,12 +4925,9 @@ async function rewindItemPhase(req, res, id2) {
4917
4925
  await orm.update(items).set({ currentPhase: phase, status: "in-progress" }).where(eq14(items.id, id2));
4918
4926
  respondJson(res, 200, await loadItem(orm, id2));
4919
4927
  }
4920
- function validateRewind(item, phase) {
4921
- if (!item.plan || item.plan.length === 0) {
4922
- return "Item has no plan phases.";
4923
- }
4924
- if (phase < 1 || phase > item.plan.length) {
4925
- return `Phase ${phase} does not exist. Valid range: 1\u2013${item.plan.length}.`;
4928
+ function validateRewind(item, plan2, phase) {
4929
+ if (phase < 1 || phase > plan2.length) {
4930
+ return `Phase ${phase} does not exist. Valid range: 1\u2013${plan2.length}.`;
4926
4931
  }
4927
4932
  const currentPhase = item.currentPhase ?? 1;
4928
4933
  if (phase >= currentPhase) {
@@ -6832,12 +6837,9 @@ function registerRefineCommand(cmd) {
6832
6837
 
6833
6838
  // src/commands/backlog/rewindPhase.ts
6834
6839
  import chalk66 from "chalk";
6835
- function validateRewind2(item, phaseNumber) {
6836
- if (!item.plan || item.plan.length === 0) {
6837
- return `Item #${item.id} has no plan phases.`;
6838
- }
6839
- if (phaseNumber < 1 || phaseNumber > item.plan.length) {
6840
- return `Phase ${phaseNumber} does not exist. Valid range: 1\u2013${item.plan.length}.`;
6840
+ function validateRewind2(item, plan2, phaseNumber) {
6841
+ if (phaseNumber < 1 || phaseNumber > plan2.length) {
6842
+ return `Phase ${phaseNumber} does not exist. Valid range: 1\u2013${plan2.length}.`;
6841
6843
  }
6842
6844
  const currentPhase = item.currentPhase ?? 1;
6843
6845
  if (phaseNumber >= currentPhase) {
@@ -6854,13 +6856,14 @@ async function rewindPhase(id2, phase, opts) {
6854
6856
  console.log(chalk66.red(`Item #${id2} not found.`));
6855
6857
  return;
6856
6858
  }
6857
- const error = validateRewind2(item, phaseNumber);
6859
+ const plan2 = resolveRewindPlan(item);
6860
+ const error = validateRewind2(item, plan2, phaseNumber);
6858
6861
  if (error) {
6859
6862
  console.log(chalk66.red(error));
6860
6863
  process.exitCode = 1;
6861
6864
  return;
6862
6865
  }
6863
- const phaseName = item.plan?.[phaseIndex].name;
6866
+ const phaseName = plan2[phaseIndex].name;
6864
6867
  await appendComment(
6865
6868
  orm,
6866
6869
  item.id,
@@ -18409,6 +18412,44 @@ function daemonLog(message) {
18409
18412
  console.log(`${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${message}`);
18410
18413
  }
18411
18414
 
18415
+ // src/commands/sessions/daemon/loadActiveSelection.ts
18416
+ import { z as z5 } from "zod";
18417
+ var ACTIVE_FILE = "active.json";
18418
+ var activeSelectionSchema = z5.record(z5.string(), z5.string());
18419
+ function loadActiveSelection() {
18420
+ const parsed = activeSelectionSchema.safeParse(
18421
+ loadJson(ACTIVE_FILE)
18422
+ );
18423
+ return parsed.success ? parsed.data : {};
18424
+ }
18425
+ function saveActiveSelection(active) {
18426
+ saveJson(ACTIVE_FILE, active);
18427
+ }
18428
+
18429
+ // src/commands/sessions/daemon/ActiveSelection.ts
18430
+ var ActiveSelection = class {
18431
+ // why: onChange triggers a broadcast only when the selection actually changes
18432
+ constructor(onChange) {
18433
+ this.onChange = onChange;
18434
+ }
18435
+ byRepo = /* @__PURE__ */ new Map();
18436
+ set(cwd, sessionId) {
18437
+ if (!cwd) return;
18438
+ this.byRepo.delete(cwd);
18439
+ this.byRepo.set(cwd, sessionId);
18440
+ saveActiveSelection(this.toJSON());
18441
+ this.onChange();
18442
+ }
18443
+ // why: on daemon restart, reload persisted selections but drop any whose session was not restored
18444
+ restore(isLive) {
18445
+ for (const [cwd, sessionId] of Object.entries(loadActiveSelection()))
18446
+ if (isLive(sessionId)) this.byRepo.set(cwd, sessionId);
18447
+ }
18448
+ toJSON() {
18449
+ return Object.fromEntries(this.byRepo);
18450
+ }
18451
+ };
18452
+
18412
18453
  // src/commands/sessions/daemon/broadcast.ts
18413
18454
  function sendTo(client, msg) {
18414
18455
  client.send(JSON.stringify(msg));
@@ -18421,17 +18462,17 @@ function broadcast(clients, msg) {
18421
18462
  }
18422
18463
 
18423
18464
  // src/commands/sessions/daemon/loadPersistedSessions.ts
18424
- import { z as z5 } from "zod";
18465
+ import { z as z6 } from "zod";
18425
18466
  var SESSIONS_FILE = "sessions.json";
18426
- var persistedSessionSchema = z5.object({
18427
- name: z5.string(),
18428
- commandType: z5.enum(["claude", "run", "assist"]),
18429
- cwd: z5.string(),
18430
- startedAt: z5.number(),
18431
- claudeSessionId: z5.string().optional(),
18432
- runName: z5.string().optional(),
18433
- runArgs: z5.array(z5.string()).optional(),
18434
- assistArgs: z5.array(z5.string()).optional(),
18467
+ var persistedSessionSchema = z6.object({
18468
+ name: z6.string(),
18469
+ commandType: z6.enum(["claude", "run", "assist"]),
18470
+ cwd: z6.string(),
18471
+ startedAt: z6.number(),
18472
+ claudeSessionId: z6.string().optional(),
18473
+ runName: z6.string().optional(),
18474
+ runArgs: z6.array(z6.string()).optional(),
18475
+ assistArgs: z6.array(z6.string()).optional(),
18435
18476
  activity: activitySchema.optional()
18436
18477
  });
18437
18478
  function loadPersistedSessions() {
@@ -18498,11 +18539,13 @@ function toSessionInfo({
18498
18539
  }
18499
18540
 
18500
18541
  // src/commands/sessions/daemon/broadcastSessions.ts
18501
- function broadcastSessions(sessions, clients, windowsSessions = []) {
18542
+ function broadcastSessions(sessions, clients, windowsSessions = [], active) {
18502
18543
  persistLiveSessions(sessions);
18544
+ const local = [...sessions.values()].map(toSessionInfo);
18503
18545
  broadcast(clients, {
18504
18546
  type: "sessions",
18505
- sessions: [...sessions.values()].map(toSessionInfo).concat(windowsSessions)
18547
+ sessions: local.concat(windowsSessions),
18548
+ active: active?.toJSON() ?? {}
18506
18549
  });
18507
18550
  }
18508
18551
 
@@ -18640,8 +18683,7 @@ function replayScrollback(sessions, client) {
18640
18683
  }
18641
18684
 
18642
18685
  // src/commands/sessions/daemon/greetClient.ts
18643
- function greetClient(client, sessions, list4, windowsProxy) {
18644
- sendTo(client, { type: "sessions", sessions: list4() });
18686
+ function greetClient(client, sessions, windowsProxy) {
18645
18687
  replayScrollback(sessions, client);
18646
18688
  windowsProxy.replayScrollback(client);
18647
18689
  void windowsProxy.discover();
@@ -19685,6 +19727,8 @@ var SessionManager = class {
19685
19727
  this.onIdleChange = onIdleChange;
19686
19728
  }
19687
19729
  sessions = /* @__PURE__ */ new Map();
19730
+ // why: dispatch calls active.set() on card click; broadcasts include active.toJSON()
19731
+ active = new ActiveSelection(() => this.notify());
19688
19732
  clients = new ClientHub();
19689
19733
  nextId = 1;
19690
19734
  shuttingDown = false;
@@ -19692,16 +19736,14 @@ var SessionManager = class {
19692
19736
  windowsProxy = new WindowsProxy(this.clients, () => this.notify());
19693
19737
  addClient(client) {
19694
19738
  this.clients.add(client);
19695
- this.onIdleChange?.(this.isIdle());
19696
- greetClient(client, this.sessions, this.listSessions, this.windowsProxy);
19739
+ this.notify();
19740
+ greetClient(client, this.sessions, this.windowsProxy);
19697
19741
  }
19698
19742
  removeClient(client) {
19699
19743
  this.clients.delete(client);
19700
19744
  this.onIdleChange?.(this.isIdle());
19701
19745
  }
19702
- isIdle() {
19703
- return this.sessions.size === 0 && this.clients.size === 0;
19704
- }
19746
+ isIdle = () => this.sessions.size === 0 && this.clients.size === 0;
19705
19747
  shutdown() {
19706
19748
  this.shuttingDown = true;
19707
19749
  shutdownSessions(this.sessions);
@@ -19769,7 +19811,7 @@ var SessionManager = class {
19769
19811
  notify = () => {
19770
19812
  if (this.shuttingDown) return;
19771
19813
  const windows = this.windowsProxy.sessions();
19772
- broadcastSessions(this.sessions, this.clients, windows);
19814
+ broadcastSessions(this.sessions, this.clients, windows, this.active);
19773
19815
  this.onIdleChange?.(this.isIdle());
19774
19816
  };
19775
19817
  };
@@ -19947,7 +19989,8 @@ var handlers = {
19947
19989
  ),
19948
19990
  "set-autoadvance": routed(
19949
19991
  (_client, m, d) => m.setAutoAdvance(d.sessionId, d.enabled)
19950
- )
19992
+ ),
19993
+ "set-active": (_client, m, d) => m.active.set(d.cwd, d.sessionId)
19951
19994
  };
19952
19995
  function dispatchMessage(client, manager, data) {
19953
19996
  handlers[data.type]?.(client, manager, data);
@@ -20018,6 +20061,8 @@ function onListening(manager, checkAutoExit) {
20018
20061
  });
20019
20062
  process.on("exit", cleanupOwnedFiles);
20020
20063
  const restored = manager.restore();
20064
+ const liveIds = new Set(manager.listSessions().map((s) => s.id));
20065
+ manager.active.restore((id2) => liveIds.has(id2));
20021
20066
  daemonLog(
20022
20067
  restored.length > 0 ? `restored ${restored.length} session(s): ${restored.join(", ")}` : "no persisted sessions to restore"
20023
20068
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.297.3",
3
+ "version": "0.298.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {