@staff0rd/assist 0.415.1 → 0.417.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.415.1",
9
+ version: "0.417.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -6431,6 +6431,20 @@ function sendToDaemon(message) {
6431
6431
  });
6432
6432
  }
6433
6433
 
6434
+ // src/commands/sessions/daemon/readSocketLines.ts
6435
+ function readSocketLines(socket, onLine) {
6436
+ let buffer = "";
6437
+ socket.on("data", (chunk) => {
6438
+ buffer += chunk.toString("utf8");
6439
+ let newline = buffer.indexOf("\n");
6440
+ while (newline !== -1) {
6441
+ onLine(buffer.slice(0, newline));
6442
+ buffer = buffer.slice(newline + 1);
6443
+ newline = buffer.indexOf("\n");
6444
+ }
6445
+ });
6446
+ }
6447
+
6434
6448
  // src/commands/sessions/daemon/sendToDaemonAwaitAck.ts
6435
6449
  var ACK_TIMEOUT_MS = 1e3;
6436
6450
  function sendToDaemonAwaitAck(message) {
@@ -6449,16 +6463,8 @@ function sendToDaemonAwaitAck(message) {
6449
6463
  () => finish(new Error("timed out awaiting daemon ack")),
6450
6464
  ACK_TIMEOUT_MS
6451
6465
  );
6452
- let buffer = "";
6453
- socket.on("data", (chunk) => {
6454
- buffer += chunk.toString("utf8");
6455
- let newline = buffer.indexOf("\n");
6456
- while (newline !== -1) {
6457
- const line = buffer.slice(0, newline);
6458
- buffer = buffer.slice(newline + 1);
6459
- if (isAck(line)) return finish();
6460
- newline = buffer.indexOf("\n");
6461
- }
6466
+ readSocketLines(socket, (line) => {
6467
+ if (isAck(line)) finish();
6462
6468
  });
6463
6469
  socket.on("error", (error) => finish(error));
6464
6470
  socket.on(
@@ -18123,7 +18129,23 @@ async function prs(options2) {
18123
18129
  }
18124
18130
  }
18125
18131
 
18126
- // src/commands/prs/raise.ts
18132
+ // src/commands/prs/buildValidatedBody.ts
18133
+ function buildValidatedBody(options2, usage) {
18134
+ if (!options2.title || !options2.what || !options2.why) {
18135
+ console.error(usage);
18136
+ process.exit(1);
18137
+ }
18138
+ const body = buildPrBody({
18139
+ what: options2.what,
18140
+ why: options2.why,
18141
+ how: options2.how,
18142
+ resolves: options2.resolves
18143
+ });
18144
+ validatePrContent(options2.title, body);
18145
+ return { title: options2.title, body };
18146
+ }
18147
+
18148
+ // src/commands/prs/placePr.ts
18127
18149
  import { execFileSync as execFileSync7 } from "child_process";
18128
18150
 
18129
18151
  // src/commands/prs/buildCreateArgs.ts
@@ -18181,40 +18203,138 @@ function readSessionPrRef() {
18181
18203
  }
18182
18204
  }
18183
18205
 
18184
- // src/commands/prs/raise.ts
18185
- async function raise(options2) {
18186
- if (!options2.title || !options2.what || !options2.why) {
18187
- console.error(
18188
- "Usage: assist prs raise --title <title> --what <what> --why <why> [--how <how>] [--resolves <key>] [--force]"
18206
+ // src/commands/prs/recordPrActivity.ts
18207
+ async function recordPrActivity() {
18208
+ if (resolveSessionItemId() === null) return;
18209
+ const pr = readSessionPrRef();
18210
+ if (pr) await recordSessionRefs([pr]);
18211
+ }
18212
+
18213
+ // src/commands/prs/placePr.ts
18214
+ function hasUpstream() {
18215
+ try {
18216
+ execFileSync7(
18217
+ "git",
18218
+ ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
18219
+ { stdio: "pipe" }
18189
18220
  );
18221
+ return true;
18222
+ } catch {
18223
+ return false;
18224
+ }
18225
+ }
18226
+ function ensureBranchPushed() {
18227
+ const args = hasUpstream() ? ["push"] : ["push", "--set-upstream", "origin", "HEAD"];
18228
+ execFileSync7("git", args, { stdio: "inherit" });
18229
+ }
18230
+ async function placePr(prNumber, title, body, options2) {
18231
+ const args = prNumber !== null ? buildEditArgs(prNumber, title, body) : buildCreateArgs(title, body, options2);
18232
+ try {
18233
+ if (prNumber === null && !options2.head) ensureBranchPushed();
18234
+ execFileSync7("gh", args, { stdio: "inherit" });
18235
+ } catch {
18190
18236
  process.exit(1);
18191
18237
  }
18192
- const body = buildPrBody({
18193
- what: options2.what,
18194
- why: options2.why,
18195
- how: options2.how,
18196
- resolves: options2.resolves
18238
+ await recordPrActivity();
18239
+ }
18240
+
18241
+ // src/commands/prs/previewAndPlace.ts
18242
+ import { randomUUID as randomUUID4 } from "crypto";
18243
+
18244
+ // src/commands/prs/requestPrDecision.ts
18245
+ function parseIncoming(line, requestId) {
18246
+ try {
18247
+ const msg = JSON.parse(line);
18248
+ if (msg.type === "error" && (msg.message ?? "").includes("pr-preview"))
18249
+ return { kind: "error", message: msg.message ?? "pr-preview failed" };
18250
+ if (msg.type !== "pr-decision" || msg.requestId !== requestId) return null;
18251
+ if (msg.decision !== "approve" && msg.decision !== "reject") return null;
18252
+ return {
18253
+ kind: "decision",
18254
+ decision: { decision: msg.decision, reason: msg.reason }
18255
+ };
18256
+ } catch {
18257
+ return null;
18258
+ }
18259
+ }
18260
+ function requestPrDecision(request) {
18261
+ return new Promise((resolve19, reject) => {
18262
+ connectToDaemon().then((socket) => {
18263
+ let settled = false;
18264
+ const finish = (error, decision) => {
18265
+ if (settled) return;
18266
+ settled = true;
18267
+ socket.destroy();
18268
+ if (error) reject(error);
18269
+ else resolve19(decision);
18270
+ };
18271
+ readSocketLines(socket, (line) => {
18272
+ const incoming = parseIncoming(line, request.requestId);
18273
+ if (!incoming) return;
18274
+ if (incoming.kind === "error") finish(new Error(incoming.message));
18275
+ else finish(void 0, incoming.decision);
18276
+ });
18277
+ socket.on("error", (error) => finish(error));
18278
+ socket.on(
18279
+ "close",
18280
+ () => finish(new Error("daemon closed before a decision was made"))
18281
+ );
18282
+ socket.write(`${JSON.stringify({ type: "pr-preview", ...request })}
18283
+ `);
18284
+ }, reject);
18197
18285
  });
18198
- validatePrContent(options2.title, body);
18199
- const existing = findCurrentPrNumber();
18200
- if (existing !== null && !options2.force) {
18286
+ }
18287
+
18288
+ // src/commands/prs/previewAndPlace.ts
18289
+ async function previewAndPlace(args) {
18290
+ console.log("Awaiting your approval in the assist web UI preview pane\u2026");
18291
+ let decision;
18292
+ try {
18293
+ decision = await requestPrDecision({
18294
+ sessionId: args.sessionId,
18295
+ requestId: randomUUID4(),
18296
+ title: args.title,
18297
+ body: args.body,
18298
+ prNumber: args.prNumber
18299
+ });
18300
+ } catch (error) {
18201
18301
  console.error(
18202
- `Error: A pull request already exists for this branch (#${existing}). Pass --force to overwrite it, or use 'assist prs edit' to update individual sections.`
18302
+ `Error: ${error instanceof Error ? error.message : String(error)}`
18203
18303
  );
18204
18304
  process.exit(1);
18205
18305
  }
18206
- const args = existing !== null ? buildEditArgs(existing, options2.title, body) : buildCreateArgs(options2.title, body, options2);
18207
- try {
18208
- execFileSync7("gh", args, { stdio: "inherit" });
18209
- } catch {
18306
+ if (decision.decision === "reject") {
18307
+ console.error(
18308
+ `PR preview rejected${decision.reason ? `: ${decision.reason}` : "."}`
18309
+ );
18210
18310
  process.exit(1);
18211
18311
  }
18212
- await recordPrActivity();
18312
+ await placePr(args.prNumber, args.title, args.body, args.options);
18213
18313
  }
18214
- async function recordPrActivity() {
18215
- if (resolveSessionItemId() === null) return;
18216
- const pr = readSessionPrRef();
18217
- if (pr) await recordSessionRefs([pr]);
18314
+
18315
+ // src/commands/prs/raise.ts
18316
+ var USAGE = "Usage: assist prs raise --title <title> --what <what> --why <why> [--how <how>] [--resolves <key>] [--force]";
18317
+ async function raise(options2) {
18318
+ const { title, body } = buildValidatedBody(options2, USAGE);
18319
+ const existing = findCurrentPrNumber();
18320
+ const sessionId = process.env.ASSIST_SESSION_ID;
18321
+ if (process.env.ASSIST_SESSION === "1" && sessionId) {
18322
+ await previewAndPlace({
18323
+ sessionId,
18324
+ title,
18325
+ body,
18326
+ prNumber: existing,
18327
+ options: options2
18328
+ });
18329
+ return;
18330
+ }
18331
+ if (existing !== null && !options2.force) {
18332
+ console.error(
18333
+ `Error: A pull request already exists for this branch (#${existing}). Pass --force to overwrite it, or use 'assist prs edit' to update individual sections.`
18334
+ );
18335
+ process.exit(1);
18336
+ }
18337
+ await placePr(existing, title, body, options2);
18218
18338
  }
18219
18339
 
18220
18340
  // src/commands/prs/reply.ts
@@ -18316,6 +18436,86 @@ function registerPrsEdit(prsCommand) {
18316
18436
  ).action(edit);
18317
18437
  }
18318
18438
 
18439
+ // src/commands/prs/raiseHelpText.ts
18440
+ var GUIDANCE = `Raise a pull request for the current branch. Use a concise description with no
18441
+ headers, and do not reference Claude or any AI assistance in the title or body.
18442
+
18443
+ The body is assembled from discrete section options; supply at minimum --title,
18444
+ --what, and --why:
18445
+
18446
+ --title <title> short PR title.
18447
+ --what <what> what the change does (rendered as ## What).
18448
+ --why <why> why the change is needed (rendered as ## Why).
18449
+ --how <how> optional; how the change works (rendered as ## How). Omit it
18450
+ unless the approach genuinely needs explaining.
18451
+ --resolves <key> Jira issue key resolved by this PR; repeatable. Each key's
18452
+ browse URL is appended inline to ## Why. Unless a Jira key is
18453
+ already known from the session, ask the user whether this PR
18454
+ resolves a Jira issue and for the key before raising; omit
18455
+ --resolves only if they say there isn't one.
18456
+
18457
+ Wrap symbols, file paths, function names, class names, variable names, config
18458
+ keys, CLI commands, and flag names in backticks.
18459
+
18460
+ One section, one question. Each section answers exactly one thing, and
18461
+ implementation detail lives only in ## How:
18462
+
18463
+ ## What what is observably different for someone using or calling this, and
18464
+ nothing else. Not how it's built, not which functions/files changed.
18465
+ ## Why the problem or motivation that made the change worth doing. Not how
18466
+ the solution works.
18467
+ ## How only the non-obvious decisions the diff alone won't explain: a
18468
+ deliberate trade-off, a workaround, a reason the obvious approach was
18469
+ rejected. Omit it entirely by default. Never restate ## What as
18470
+ mechanism, and never walk through the diff.
18471
+
18472
+ The most common failure is altitude bleed: an implementation detail gets sprayed
18473
+ into whichever section is being written \u2014 ## What narrates the diff, ## How
18474
+ restates ## What, ## Why smuggles in mechanism. If a sentence describes a
18475
+ mechanism, it belongs in ## How, and probably shouldn't exist at all unless it's
18476
+ a genuine non-obvious decision. Litmus \u2014 a sentence is almost certainly mechanism
18477
+ (so ## How, or cut it) if it contains "by \u2026ing", "so that it can", "because the
18478
+ X filters/needs/uses", or names an internal component, property, function, or
18479
+ file.
18480
+
18481
+ Brevity budget \u2014 keep each section within these limits:
18482
+
18483
+ ## What a few sentences (2\u20133).
18484
+ ## Why 1\u20132 sentences.
18485
+ ## How omit by default; a sentence or two, only for non-obvious decisions.
18486
+
18487
+ Write prose. A short paragraph is the natural form for ## What and ## Why \u2014
18488
+ bullets in ## What are a smell. Use bullets only when there are genuinely several
18489
+ independent, parallel items (3+); a single bullet is never right. A single
18490
+ non-list paragraph over ~600 characters or ~4 sentences is a wall of text and
18491
+ will be rejected.
18492
+
18493
+ If a pull request already exists for the branch, this command errors \u2014 pass
18494
+ --force to fully overwrite its title and body, or use 'assist prs edit' to update
18495
+ only individual sections (every other section of the existing body is preserved).`;
18496
+ var TERMINAL_CONFIRM = `Before running this command, the user must see the full proposed title and body \u2014
18497
+ do not assume they can see your reasoning or earlier tool output. Write the
18498
+ complete title and body verbatim in your visible reply, then use the
18499
+ AskUserQuestion tool to ask whether to create the PR, putting the full title and
18500
+ body in the approve option's preview field. This confirmation is mandatory in
18501
+ every permission mode, including auto-accept and bypass-permissions. If the user
18502
+ requests changes, revise and confirm again before running.`;
18503
+ var WEB_CONFIRM = `You are running inside an assist web session. When you run this command the
18504
+ drafted title and body are shown automatically in a preview pane beside the
18505
+ terminal in the web UI, for the user to approve or reject there \u2014 do NOT display
18506
+ the title and body or ask for confirmation in the terminal. The command blocks
18507
+ until the user decides: on approval it creates or updates the PR and reports it;
18508
+ on rejection it exits non-zero with the reason, which you must address before
18509
+ running again. Just compose the sections and run the command.`;
18510
+ function raiseHelpText() {
18511
+ const confirm = process.env.ASSIST_SESSION === "1" ? WEB_CONFIRM : TERMINAL_CONFIRM;
18512
+ return `
18513
+ ${GUIDANCE}
18514
+
18515
+ ${confirm}
18516
+ `;
18517
+ }
18518
+
18319
18519
  // src/commands/registerPrsRaise.ts
18320
18520
  function collect5(value, previous) {
18321
18521
  return previous.concat([value]);
@@ -18341,7 +18541,7 @@ function registerPrsRaise(prsCommand) {
18341
18541
  "Request a review (repeatable)",
18342
18542
  collect5,
18343
18543
  []
18344
- ).option("-m, --milestone <name>", "Add the pull request to a milestone").action(raise);
18544
+ ).option("-m, --milestone <name>", "Add the pull request to a milestone").addHelpText("after", () => raiseHelpText()).action(raise);
18345
18545
  }
18346
18546
 
18347
18547
  // src/commands/registerPrs.ts
@@ -24833,13 +25033,13 @@ function toSessionInfo({
24833
25033
  cwd,
24834
25034
  restored,
24835
25035
  error,
24836
- errorOutput,
24837
25036
  activity: activity2,
24838
25037
  autoRun,
24839
25038
  autoAdvance,
24840
25039
  starred,
24841
25040
  usedPct,
24842
- design
25041
+ design,
25042
+ pendingPrPreview
24843
25043
  }) {
24844
25044
  return {
24845
25045
  id,
@@ -24861,13 +25061,13 @@ function toSessionInfo({
24861
25061
  cwd,
24862
25062
  restored,
24863
25063
  error,
24864
- errorOutput,
24865
25064
  activity: activity2,
24866
25065
  autoRun,
24867
25066
  autoAdvance,
24868
25067
  starred,
24869
25068
  usedPct,
24870
- design
25069
+ design,
25070
+ pendingPrPreview
24871
25071
  };
24872
25072
  }
24873
25073
 
@@ -25028,7 +25228,7 @@ function createAssistSession(id, assistArgs, cwd, meta) {
25028
25228
  }
25029
25229
 
25030
25230
  // src/commands/sessions/daemon/createSession.ts
25031
- import { randomUUID as randomUUID4 } from "crypto";
25231
+ import { randomUUID as randomUUID5 } from "crypto";
25032
25232
 
25033
25233
  // src/commands/sessions/daemon/resolveRunConfig.ts
25034
25234
  function resolveRunConfig(name, cwd) {
@@ -25109,7 +25309,7 @@ function sessionBase(id, status3) {
25109
25309
  }
25110
25310
  function createSession(id, prompt, cwd, design, harness) {
25111
25311
  if (harness === "pi") return createPiSession(id, prompt, cwd);
25112
- const claudeSessionId = randomUUID4();
25312
+ const claudeSessionId = randomUUID5();
25113
25313
  return {
25114
25314
  ...sessionBase(id, prompt ? "running" : "waiting"),
25115
25315
  name: prompt?.slice(0, 40) || `Session ${id}`,
@@ -25260,6 +25460,74 @@ function logSpawnedSession(session) {
25260
25460
  );
25261
25461
  }
25262
25462
 
25463
+ // src/commands/sessions/daemon/PrPreviewCoordinator.ts
25464
+ var PrPreviewCoordinator = class {
25465
+ constructor(sessions, notify2) {
25466
+ this.sessions = sessions;
25467
+ this.notify = notify2;
25468
+ }
25469
+ waiters = /* @__PURE__ */ new Map();
25470
+ set(client, d) {
25471
+ const id = d.sessionId;
25472
+ const session = this.sessions.get(id);
25473
+ if (!session) {
25474
+ daemonLog(`pr-preview for unknown session id=${id} (ignoring)`);
25475
+ sendTo(client, {
25476
+ type: "error",
25477
+ message: `No live session ${id} for pr-preview`
25478
+ });
25479
+ return;
25480
+ }
25481
+ const prNumber = typeof d.prNumber === "number" ? d.prNumber : null;
25482
+ session.pendingPrPreview = {
25483
+ requestId: d.requestId,
25484
+ title: d.title,
25485
+ body: d.body,
25486
+ prNumber
25487
+ };
25488
+ this.waiters.set(id, client);
25489
+ daemonLog(
25490
+ `pr-preview set: id=${id} requestId=${d.requestId} target=${prNumber === null ? "create" : `edit #${prNumber}`}`
25491
+ );
25492
+ this.notify();
25493
+ }
25494
+ decide(d) {
25495
+ const id = d.sessionId;
25496
+ const requestId = d.requestId;
25497
+ const session = this.sessions.get(id);
25498
+ if (!session || session.pendingPrPreview?.requestId !== requestId) {
25499
+ daemonLog(
25500
+ `pr-decision for stale/unknown preview id=${id} requestId=${requestId} (ignoring)`
25501
+ );
25502
+ return;
25503
+ }
25504
+ daemonLog(
25505
+ `pr-decision received: id=${id} requestId=${requestId} decision=${d.decision}`
25506
+ );
25507
+ const waiter = this.waiters.get(id);
25508
+ if (waiter)
25509
+ sendTo(waiter, {
25510
+ type: "pr-decision",
25511
+ requestId,
25512
+ decision: d.decision,
25513
+ reason: d.reason
25514
+ });
25515
+ this.waiters.delete(id);
25516
+ session.pendingPrPreview = void 0;
25517
+ this.notify();
25518
+ }
25519
+ clearWaiter(client) {
25520
+ for (const [id, waiter] of this.waiters) {
25521
+ if (waiter !== client) continue;
25522
+ this.waiters.delete(id);
25523
+ const session = this.sessions.get(id);
25524
+ if (session) session.pendingPrPreview = void 0;
25525
+ daemonLog(`pr-preview cleared: id=${id} (waiting client disconnected)`);
25526
+ this.notify();
25527
+ }
25528
+ }
25529
+ };
25530
+
25263
25531
  // src/commands/sessions/daemon/applySetStatus.ts
25264
25532
  function applySetStatus(sessions, id, status3, source, onStatusChange) {
25265
25533
  const session = sessions.get(id);
@@ -25520,26 +25788,6 @@ function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
25520
25788
  return (s, status3, exitCode) => applyStatusChange(s, status3, exitCode, dismiss, notify2, reuseForRun);
25521
25789
  }
25522
25790
 
25523
- // src/commands/sessions/daemon/errorTail.ts
25524
- var ESC3 = String.fromCharCode(27);
25525
- var CSI = String.fromCharCode(155);
25526
- var BEL = String.fromCharCode(7);
25527
- var ANSI = new RegExp(
25528
- String.raw`[${ESC3}${CSI}][[\]()#;?]*(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?${BEL}|(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])`,
25529
- "g"
25530
- );
25531
- var MAX_TAIL_LINES = 40;
25532
- var MAX_TAIL_CHARS = 4e3;
25533
- function stripAnsi(text17) {
25534
- return text17.replace(ANSI, "");
25535
- }
25536
- function errorTail(scrollback) {
25537
- const clean = stripAnsi(scrollback).replace(/\r\n?/g, "\n").replace(/[ \t]+\n/g, "\n").trimEnd();
25538
- if (clean.length === 0) return "";
25539
- const tail = clean.split("\n").slice(-MAX_TAIL_LINES).join("\n");
25540
- return tail.length > MAX_TAIL_CHARS ? tail.slice(-MAX_TAIL_CHARS) : tail;
25541
- }
25542
-
25543
25791
  // src/commands/sessions/daemon/handleFailedResume.ts
25544
25792
  function handleFailedResume(session, exitCode, onStatusChange) {
25545
25793
  const failed2 = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
@@ -25673,12 +25921,9 @@ function handlePtyExit(session, exitCode, onStatusChange) {
25673
25921
  if (handleFailedResume(session, exitCode, onStatusChange)) return;
25674
25922
  const priorStatus = session.status;
25675
25923
  if (exitCode !== 0) {
25676
- const tail = errorTail(session.scrollback);
25677
25924
  session.error = `process exited with code ${exitCode}`;
25678
- session.errorOutput = tail || void 0;
25679
25925
  daemonLog(
25680
- `session ${session.id} ("${session.name}") pty exited with code ${exitCode} from status "${priorStatus}" \u2014 unexpected exit, marking error; error output:
25681
- ${tail || "(no output captured)"}`
25926
+ `session ${session.id} ("${session.name}") pty exited with code ${exitCode} from status "${priorStatus}" \u2014 unexpected exit, marking error`
25682
25927
  );
25683
25928
  onStatusChange(session, "error", exitCode);
25684
25929
  return;
@@ -25723,7 +25968,7 @@ function respawnSession(session, respawn, status3, clients, onStatusChange) {
25723
25968
  }
25724
25969
 
25725
25970
  // src/commands/sessions/daemon/respawnPlan.ts
25726
- import { randomUUID as randomUUID5 } from "crypto";
25971
+ import { randomUUID as randomUUID6 } from "crypto";
25727
25972
 
25728
25973
  // src/commands/sessions/daemon/assistResumeArgs.ts
25729
25974
  function assistResumeArgs(session) {
@@ -25762,7 +26007,7 @@ function respawnPlan(session) {
25762
26007
  return null;
25763
26008
  }
25764
26009
  function freshClaudePlan(session, prompt, cwd) {
25765
- const claudeSessionId = randomUUID5();
26010
+ const claudeSessionId = randomUUID6();
25766
26011
  return {
25767
26012
  spawn: () => {
25768
26013
  session.claudeSessionId = claudeSessionId;
@@ -27169,6 +27414,10 @@ var SessionManager = class {
27169
27414
  this.onIdleChange = onIdleChange;
27170
27415
  }
27171
27416
  sessions = /* @__PURE__ */ new Map();
27417
+ prPreview = new PrPreviewCoordinator(
27418
+ this.sessions,
27419
+ () => this.notify()
27420
+ );
27172
27421
  // why: dispatch calls active.set() on card click; broadcasts include active.toJSON()
27173
27422
  active = new ActiveSelection(() => this.notify());
27174
27423
  clients = new ClientHub(persistUsagePeak);
@@ -27184,6 +27433,7 @@ var SessionManager = class {
27184
27433
  removeClient(client) {
27185
27434
  this.clients.delete(client);
27186
27435
  this.clients.unsubscribeLogs(client);
27436
+ this.prPreview.clearWaiter(client);
27187
27437
  this.onIdleChange?.(this.isIdle());
27188
27438
  }
27189
27439
  isIdle = () => this.sessions.size === 0 && this.clients.size === 0;
@@ -27556,6 +27806,8 @@ var messageHandlers = {
27556
27806
  ),
27557
27807
  "set-active": (_client, m, d) => m.active.set(d.cwd, d.sessionId),
27558
27808
  "set-status": handleSetStatus,
27809
+ "pr-preview": (client, m, d) => m.prPreview.set(client, d),
27810
+ "pr-decision": (_client, m, d) => m.prPreview.decide(d),
27559
27811
  "ui-status": (_client, _m, d) => daemonLog(`ui rendered: id=${d.sessionId} status=${d.status}`)
27560
27812
  };
27561
27813
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.415.1",
3
+ "version": "0.417.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {