@staff0rd/assist 0.551.0 → 0.552.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.551.0",
9
+ version: "0.552.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -7564,7 +7564,7 @@ var BACKLOG_MARKERS = [
7564
7564
  function findBacklogUp(startDir) {
7565
7565
  let current = startDir;
7566
7566
  while (current !== dirname14(current)) {
7567
- if (BACKLOG_MARKERS.some((marker) => existsSync21(join17(current, marker)))) {
7567
+ if (BACKLOG_MARKERS.some((marker2) => existsSync21(join17(current, marker2)))) {
7568
7568
  return current;
7569
7569
  }
7570
7570
  current = dirname14(current);
@@ -9376,9 +9376,9 @@ function printPhaseTasks(phase) {
9376
9376
  // src/commands/backlog/show/printPlan.ts
9377
9377
  function phaseHeader(index3, name, isCurrent) {
9378
9378
  const phaseNumber = index3 + 1;
9379
- const marker = isCurrent ? chalk56.green("\u25B6 ") : " ";
9379
+ const marker2 = isCurrent ? chalk56.green("\u25B6 ") : " ";
9380
9380
  const label2 = isCurrent ? chalk56.green.bold(`Phase ${phaseNumber}: ${name}`) : `${chalk56.bold(`Phase ${phaseNumber}:`)} ${name}`;
9381
- return `${marker}${label2}`;
9381
+ return `${marker2}${label2}`;
9382
9382
  }
9383
9383
  function printPhaseSessions(sessions) {
9384
9384
  if (sessions.length === 0) return;
@@ -14678,12 +14678,12 @@ async function launchMode(slashCommand, options2) {
14678
14678
  resumeSessionId,
14679
14679
  cwd: process.cwd()
14680
14680
  });
14681
- const marker = watchForMarker(child, { actOnDone: options2?.once });
14681
+ const marker2 = watchForMarker(child, { actOnDone: options2?.once });
14682
14682
  const exitCode = await awaitClaude(done2, `/${slashCommand}`);
14683
14683
  stopWatching();
14684
14684
  if (exitCode === CLAUDE_SPAWN_FAILED) return;
14685
14685
  await handleLaunchSignal(slashCommand, options2?.once);
14686
- if (exitCode !== 0 && !marker.killedOnMarker()) process.exitCode = exitCode;
14686
+ if (exitCode !== 0 && !marker2.killedOnMarker()) process.exitCode = exitCode;
14687
14687
  }
14688
14688
 
14689
14689
  // src/commands/backlog/refine.ts
@@ -17262,15 +17262,15 @@ function codeCommentConfirm(pin) {
17262
17262
  process.exitCode = 1;
17263
17263
  return;
17264
17264
  }
17265
- const marker = isHashCommentFile(state.file) ? "#" : "//";
17265
+ const marker2 = isHashCommentFile(state.file) ? "#" : "//";
17266
17266
  const indentSource = lines2[index3] ?? "";
17267
17267
  const indent3 = indentSource.match(/^\s*/)?.[0] ?? "";
17268
- lines2.splice(index3, 0, `${indent3}${marker} ${state.text}`);
17268
+ lines2.splice(index3, 0, `${indent3}${marker2} ${state.text}`);
17269
17269
  writeFileSync24(state.file, lines2.join("\n"));
17270
17270
  unlinkSync8(getPinStatePath(pin));
17271
17271
  console.log(
17272
17272
  chalk121.green(
17273
- `Inserted "${marker} ${state.text}" at ${state.file}:${state.line}`
17273
+ `Inserted "${marker2} ${state.text}" at ${state.file}:${state.line}`
17274
17274
  )
17275
17275
  );
17276
17276
  }
@@ -17332,7 +17332,7 @@ function codeCommentSet(file, line, text17) {
17332
17332
  return;
17333
17333
  }
17334
17334
  const hash = isHashCommentFile(file);
17335
- const marker = hash ? "#" : "//";
17335
+ const marker2 = hash ? "#" : "//";
17336
17336
  const validation = validateCommentText(text17, hash);
17337
17337
  if (!validation.ok) {
17338
17338
  console.error(chalk122.red(`Refused: ${validation.reason}`));
@@ -17357,7 +17357,7 @@ function codeCommentSet(file, line, text17) {
17357
17357
  }
17358
17358
  console.log(
17359
17359
  `A confirmation pin was sent to your desktop notifications.
17360
- To insert "${marker} ${validation.text}" at ${file}:${lineNumber}, run:
17360
+ To insert "${marker2} ${validation.text}" at ${file}:${lineNumber}, run:
17361
17361
  ${chalk122.cyan(" assist code-comment confirm <PIN>")}
17362
17362
  using the pin from that notification.`
17363
17363
  );
@@ -19826,9 +19826,9 @@ function selectCommentExtractor(filePath) {
19826
19826
  }
19827
19827
 
19828
19828
  // src/commands/editHook/decideCommentGuard.ts
19829
- function denyReason(marker) {
19830
- const blockClause = marker === "//" ? ", no block comments" : "";
19831
- return `This edit introduces a code comment (${marker}), which is blocked by the comment gate. Comments are a last resort \u2014 prefer a clearer name, a smaller function, or a test that makes the comment unnecessary. The comment must not appear in your edit itself. If this one line genuinely earns its keep, use the escape hatch: run \`assist code-comment set <file> <line> "<text>"\` (single line, max 50 chars${blockClause}) to get a pin, then \`assist code-comment confirm <pin>\` to insert it.`;
19829
+ function denyReason(marker2) {
19830
+ const blockClause = marker2 === "//" ? ", no block comments" : "";
19831
+ return `This edit introduces a code comment (${marker2}), which is blocked by the comment gate. Comments are a last resort \u2014 prefer a clearer name, a smaller function, or a test that makes the comment unnecessary. The comment must not appear in your edit itself. If this one line genuinely earns its keep, use the escape hatch: run \`assist code-comment set <file> <line> "<text>"\` (single line, max 50 chars${blockClause}) to get a pin, then \`assist code-comment confirm <pin>\` to insert it.`;
19832
19832
  }
19833
19833
  function decideCommentGuard(input, existingContent) {
19834
19834
  const { file_path, content } = input.tool_input;
@@ -20439,15 +20439,15 @@ function renderNodes(nodes, indent3) {
20439
20439
  function isListNode(node) {
20440
20440
  return node.type === "orderedList" || node.type === "bulletList";
20441
20441
  }
20442
- function renderListChild(child, indent3, pad, marker, isFirst) {
20442
+ function renderListChild(child, indent3, pad, marker2, isFirst) {
20443
20443
  if (isListNode(child)) return renderNodes([child], indent3 + 1);
20444
20444
  if (child.type !== "paragraph") return renderNode(child, indent3);
20445
20445
  const text17 = renderChildren(child, indent3);
20446
- return isFirst ? `${pad}${marker} ${text17}` : `${pad} ${text17}`;
20446
+ return isFirst ? `${pad}${marker2} ${text17}` : `${pad} ${text17}`;
20447
20447
  }
20448
- function renderListItem(node, indent3, marker) {
20448
+ function renderListItem(node, indent3, marker2) {
20449
20449
  const pad = " ".repeat(indent3);
20450
- return (node.content ?? []).map((child, i) => renderListChild(child, indent3, pad, marker, i === 0)).join("\n");
20450
+ return (node.content ?? []).map((child, i) => renderListChild(child, indent3, pad, marker2, i === 0)).join("\n");
20451
20451
  }
20452
20452
  function adfToText(doc) {
20453
20453
  return renderNodes([doc], 0);
@@ -29545,9 +29545,9 @@ function renderWatchReport({
29545
29545
  } else {
29546
29546
  lines2.push("| SHA | When | Subject |", "| --- | --- | --- |");
29547
29547
  for (const commit2 of commits2) {
29548
- const marker = isNew.has(commit2.sha) ? " \u2190 new" : "";
29548
+ const marker2 = isNew.has(commit2.sha) ? " \u2190 new" : "";
29549
29549
  lines2.push(
29550
- `| \`${commit2.short}\` | ${commit2.when} | ${escapeCell(commit2.subject)}${marker} |`
29550
+ `| \`${commit2.short}\` | ${commit2.when} | ${escapeCell(commit2.subject)}${marker2} |`
29551
29551
  );
29552
29552
  }
29553
29553
  }
@@ -31079,7 +31079,8 @@ function toSessionInfo(session) {
31079
31079
  design,
31080
31080
  verifying,
31081
31081
  pendingPrPreview,
31082
- undurable
31082
+ undurable,
31083
+ lastUserMessage
31083
31084
  } = session;
31084
31085
  return {
31085
31086
  ...toSessionRunInfo(session),
@@ -31103,6 +31104,7 @@ function toSessionInfo(session) {
31103
31104
  verifying,
31104
31105
  pendingPrPreview,
31105
31106
  undurable,
31107
+ lastUserMessage,
31106
31108
  joinable: joinRefusal(session) === void 0
31107
31109
  };
31108
31110
  }
@@ -32549,6 +32551,61 @@ function resolvedToolUseIds(events, afterIdx) {
32549
32551
  return ids;
32550
32552
  }
32551
32553
 
32554
+ // src/commands/sessions/shared/extractLastUserMessage.ts
32555
+ var MAX_LENGTH2 = 2048;
32556
+ var INTERRUPT_PREFIX2 = "[Request interrupted";
32557
+ function extractLastUserMessage(entries, maxLength = MAX_LENGTH2) {
32558
+ for (let i = entries.length - 1; i >= 0; i--) {
32559
+ const entry = entries[i];
32560
+ if (entry.isSidechain || entry.isMeta) continue;
32561
+ if (entry.type !== "user") continue;
32562
+ const raw = promptTextOrNull(entry);
32563
+ if (raw === null) continue;
32564
+ const message3 = renderPrompt(raw);
32565
+ if (message3) return cap(message3, maxLength);
32566
+ }
32567
+ return void 0;
32568
+ }
32569
+ function promptTextOrNull(entry) {
32570
+ const content = asRecord3(entry.message)?.content;
32571
+ if (typeof content === "string") return isInterrupt(content) ? null : content;
32572
+ if (!Array.isArray(content)) return null;
32573
+ const parts = [];
32574
+ for (const block of content) {
32575
+ const b = asRecord3(block);
32576
+ if (b?.type !== "text") continue;
32577
+ const text17 = typeof b.text === "string" ? b.text : "";
32578
+ if (isInterrupt(text17)) return null;
32579
+ parts.push(text17);
32580
+ }
32581
+ return parts.length === 0 ? null : parts.join("\n");
32582
+ }
32583
+ function renderPrompt(raw) {
32584
+ const command = marker(raw, "command-name");
32585
+ if (command) {
32586
+ const args = marker(raw, "command-args");
32587
+ const name = `/${command.replace(/^\/+/, "")}`;
32588
+ return args ? `${name} ${args}` : name;
32589
+ }
32590
+ return stripBlocks(raw);
32591
+ }
32592
+ function stripBlocks(raw) {
32593
+ return raw.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").replace(/<command-[^>]*>[\s\S]*?<\/command-[^>]*>/g, "").replace(/<local-command-[^>]*>[\s\S]*?<\/local-command-[^>]*>/g, "").trim();
32594
+ }
32595
+ function marker(raw, tag) {
32596
+ const match = raw.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
32597
+ return match ? match[1].trim() : "";
32598
+ }
32599
+ function cap(message3, maxLength) {
32600
+ return message3.length <= maxLength ? message3 : `${message3.slice(0, maxLength).trimEnd()}\u2026`;
32601
+ }
32602
+ function isInterrupt(value) {
32603
+ return value.trimStart().startsWith(INTERRUPT_PREFIX2);
32604
+ }
32605
+ function asRecord3(value) {
32606
+ return value && typeof value === "object" ? value : null;
32607
+ }
32608
+
32552
32609
  // src/commands/sessions/shared/readTranscriptTail.ts
32553
32610
  import * as fs46 from "fs";
32554
32611
  var DEFAULT_MAX_BYTES = 256 * 1024;
@@ -32632,9 +32689,19 @@ async function resolveTranscriptPath(session) {
32632
32689
  if (filePath) session.transcriptPath = filePath;
32633
32690
  return filePath;
32634
32691
  }
32635
- async function reconcileTranscriptStatus(session, onStatusChange) {
32636
- if (session.status === "done" || session.status === "error" || session.status === "stopped")
32637
- return;
32692
+ function updateLastUserMessage(session, entries, notify2) {
32693
+ const message3 = extractLastUserMessage(entries);
32694
+ if (message3 === session.lastUserMessage) return;
32695
+ session.lastUserMessage = message3;
32696
+ daemonLog(
32697
+ `session ${session.id} last user message: ${JSON.stringify(message3?.slice(0, 120) ?? null)}`
32698
+ );
32699
+ notify2?.();
32700
+ }
32701
+ function isFinished(session) {
32702
+ return session.status === "done" || session.status === "error" || session.status === "stopped";
32703
+ }
32704
+ async function reconcileTranscriptStatus(session, onStatusChange, notify2) {
32638
32705
  const filePath = await resolveTranscriptPath(session);
32639
32706
  if (!filePath) return;
32640
32707
  const entries = await readTranscriptTail(filePath);
@@ -32642,6 +32709,8 @@ async function reconcileTranscriptStatus(session, onStatusChange) {
32642
32709
  if (fingerprint !== null && fingerprint === session.transcriptFingerprint)
32643
32710
  return;
32644
32711
  session.transcriptFingerprint = fingerprint ?? void 0;
32712
+ updateLastUserMessage(session, entries, notify2);
32713
+ if (isFinished(session)) return;
32645
32714
  const derived = deriveTranscriptStatus(entries, {
32646
32715
  permissionActive: session.permissionActive
32647
32716
  });
@@ -32812,7 +32881,7 @@ function watchTranscript(session, notify2, onStatusChange) {
32812
32881
  let timer = null;
32813
32882
  const run4 = () => {
32814
32883
  timer = null;
32815
- void reconcileTranscriptStatus(session, onStatusChange);
32884
+ void reconcileTranscriptStatus(session, onStatusChange, notify2);
32816
32885
  startTranscriptTitleGeneration(session, notify2);
32817
32886
  };
32818
32887
  try {
@@ -33421,7 +33490,7 @@ function restoreBase(id, persisted) {
33421
33490
  }
33422
33491
 
33423
33492
  // src/commands/sessions/daemon/deferredSession.ts
33424
- function deferredSession(id, persisted, cap) {
33493
+ function deferredSession(id, persisted, cap2) {
33425
33494
  return {
33426
33495
  ...restoreBase(id, persisted),
33427
33496
  status: "stopped",
@@ -33435,13 +33504,13 @@ function deferredSession(id, persisted, cap) {
33435
33504
  runArgs: persisted.runArgs,
33436
33505
  activity: persisted.activity,
33437
33506
  restored: false,
33438
- scrollback: deferralNotice(persisted, cap)
33507
+ scrollback: deferralNotice(persisted, cap2)
33439
33508
  };
33440
33509
  }
33441
- function deferralNotice(persisted, cap) {
33510
+ function deferralNotice(persisted, cap2) {
33442
33511
  return [
33443
33512
  `\r
33444
- \x1B[33mNot resumed: restore was already at its cap of ${cap} session(s) (raise it with: assist config set sessions.maxLive <n> -g)\x1B[0m\r
33513
+ \x1B[33mNot resumed: restore was already at its cap of ${cap2} session(s) (raise it with: assist config set sessions.maxLive <n> -g)\x1B[0m\r
33445
33514
  `,
33446
33515
  `This card is the handle for it. Its command, working directory (${persisted.cwd})\r
33447
33516
  `,
@@ -33677,23 +33746,23 @@ function logRestoreError(persisted, error) {
33677
33746
  // src/commands/sessions/daemon/restoreAll.ts
33678
33747
  function restoreAll(spawner, sessions) {
33679
33748
  const persisted = loadPersistedSessions();
33680
- const cap = sessionLimits.maxRestore();
33749
+ const cap2 = sessionLimits.maxRestore();
33681
33750
  const restoredIds = /* @__PURE__ */ new Map();
33682
- const names = persisted.slice(0, cap).map((entry) => {
33751
+ const names = persisted.slice(0, cap2).map((entry) => {
33683
33752
  const id = restoreOne(entry, spawner, sessions);
33684
33753
  if (entry.id != null && id != null) restoredIds.set(entry.id, id);
33685
33754
  return entry.name;
33686
33755
  });
33687
- for (const over of persisted.slice(cap))
33688
- deferOne(over, spawner.recoveryCard, cap);
33756
+ for (const over of persisted.slice(cap2))
33757
+ deferOne(over, spawner.recoveryCard, cap2);
33689
33758
  remapLaunchedFrom(sessions, restoredIds);
33690
33759
  return names;
33691
33760
  }
33692
- function deferOne(persisted, spawnRecoveryCard, cap) {
33761
+ function deferOne(persisted, spawnRecoveryCard, cap2) {
33693
33762
  try {
33694
- const id = spawnRecoveryCard((sid) => deferredSession(sid, persisted, cap));
33763
+ const id = spawnRecoveryCard((sid) => deferredSession(sid, persisted, cap2));
33695
33764
  daemonLog(
33696
- `restore capped at ${cap} (sessions.maxLive): ${describePersistedSession(persisted)} deferred to stopped card ${id}`
33765
+ `restore capped at ${cap2} (sessions.maxLive): ${describePersistedSession(persisted)} deferred to stopped card ${id}`
33697
33766
  );
33698
33767
  } catch (error) {
33699
33768
  logDroppedSession(persisted, error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.551.0",
3
+ "version": "0.552.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {