@staff0rd/assist 0.522.2 → 0.524.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.522.2",
9
+ version: "0.524.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -25218,6 +25218,11 @@ function gatherContext() {
25218
25218
  // src/commands/review/postReviewToPr.ts
25219
25219
  import { readFileSync as readFileSync41 } from "fs";
25220
25220
 
25221
+ // src/commands/review/carriedUnanchoredFindings.ts
25222
+ function carriedUnanchoredFindings(unanchored) {
25223
+ return unanchored.filter((finding) => finding.source !== "already-raised");
25224
+ }
25225
+
25221
25226
  // src/commands/review/chainAddressComments.ts
25222
25227
  function chainAddressComments(prNumber, announce) {
25223
25228
  const args = ["review-pr-comments", String(prNumber)];
@@ -25317,11 +25322,30 @@ function formatSynthesisSummary(summary) {
25317
25322
  }
25318
25323
 
25319
25324
  // src/commands/review/buildReviewSummary.ts
25325
+ var UNANCHOR_REASONS = {
25326
+ "out-of-diff": "its line falls outside the PR diff",
25327
+ unlocated: "it has no parseable file:line"
25328
+ };
25320
25329
  function formatFindingLine(finding) {
25321
25330
  const severity = finding.severity ?? "finding";
25322
25331
  return `- **${severity}: ${finding.title}**`;
25323
25332
  }
25324
- function buildReviewSummary(markdown) {
25333
+ function formatLocation(finding) {
25334
+ const location = finding.location.trim();
25335
+ if (!location || location.toLowerCase() === "n/a") return "no location";
25336
+ return `\`${location}\``;
25337
+ }
25338
+ function formatUnanchoredFinding(finding) {
25339
+ const lines2 = [
25340
+ `${formatFindingLine(finding)} \u2014 ${formatLocation(finding)}`,
25341
+ ` - Not anchored: ${UNANCHOR_REASONS[finding.reason]}`
25342
+ ];
25343
+ if (finding.impact) lines2.push(` - Impact: ${finding.impact}`);
25344
+ if (finding.recommendation)
25345
+ lines2.push(` - Recommendation: ${finding.recommendation}`);
25346
+ return lines2;
25347
+ }
25348
+ function buildReviewSummary(markdown, unanchored = []) {
25325
25349
  const summary = summariseSynthesis(markdown);
25326
25350
  const findings = parseFindings(markdown);
25327
25351
  const lines2 = ["## Code review summary", "", formatSynthesisSummary(summary)];
@@ -25329,6 +25353,18 @@ function buildReviewSummary(markdown) {
25329
25353
  lines2.push("", "### Findings", "");
25330
25354
  for (const finding of findings) lines2.push(formatFindingLine(finding));
25331
25355
  }
25356
+ const carried = carriedUnanchoredFindings(unanchored);
25357
+ if (carried.length > 0) {
25358
+ lines2.push(
25359
+ "",
25360
+ "### Findings not anchored to the diff",
25361
+ "",
25362
+ "No line comment could be attached to these, so the detail is here instead.",
25363
+ ""
25364
+ );
25365
+ for (const finding of carried)
25366
+ lines2.push(...formatUnanchoredFinding(finding));
25367
+ }
25332
25368
  return lines2.join("\n");
25333
25369
  }
25334
25370
 
@@ -25367,13 +25403,12 @@ function postFindings(findings) {
25367
25403
  return { posted, failed: failed2 };
25368
25404
  }
25369
25405
 
25370
- // src/commands/review/submitPendingReview.ts
25371
- var MUTATION = `mutation($prId: ID!, $body: String) { submitPullRequestReview(input: { pullRequestId: $prId, event: COMMENT, body: $body }) { pullRequestReview { id } } }`;
25372
- function submitPendingReview(body) {
25406
+ // src/commands/review/runReviewSubmission.ts
25407
+ function runReviewSubmission(mutation, body) {
25373
25408
  try {
25374
25409
  const prId = getCurrentPrNodeId();
25375
- runGhGraphql(MUTATION, { prId, body });
25376
- console.log("Submitted pending review.");
25410
+ runGhGraphql(mutation, { prId, body });
25411
+ console.log("Submitted review.");
25377
25412
  } catch (error) {
25378
25413
  if (isGhNotInstalled(error)) {
25379
25414
  console.error("Error: GitHub CLI (gh) is not installed.");
@@ -25384,9 +25419,25 @@ function submitPendingReview(body) {
25384
25419
  }
25385
25420
  }
25386
25421
 
25422
+ // src/commands/review/submitBodyOnlyReview.ts
25423
+ var MUTATION = `mutation($prId: ID!, $body: String) { addPullRequestReview(input: { pullRequestId: $prId, event: COMMENT, body: $body }) { pullRequestReview { id } } }`;
25424
+ function submitBodyOnlyReview(body) {
25425
+ runReviewSubmission(MUTATION, body);
25426
+ }
25427
+
25428
+ // src/commands/review/submitPendingReview.ts
25429
+ var MUTATION2 = `mutation($prId: ID!, $body: String) { submitPullRequestReview(input: { pullRequestId: $prId, event: COMMENT, body: $body }) { pullRequestReview { id } } }`;
25430
+ function submitPendingReview(body) {
25431
+ runReviewSubmission(MUTATION2, body);
25432
+ }
25433
+
25387
25434
  // src/commands/review/postAndMaybeSubmit.ts
25388
- function buildReviewBody(markdown) {
25389
- return sanitiseReviewerNames(buildReviewSummary(markdown));
25435
+ function buildReviewBody(markdown, unanchored) {
25436
+ return sanitiseReviewerNames(buildReviewSummary(markdown, unanchored));
25437
+ }
25438
+ function submitReview(body, posted) {
25439
+ if (posted > 0) submitPendingReview(body);
25440
+ else submitBodyOnlyReview(body);
25390
25441
  }
25391
25442
  async function decideSubmit(options2) {
25392
25443
  if (!options2.prompt) return options2.submit;
@@ -25397,17 +25448,19 @@ async function decideSubmit(options2) {
25397
25448
  await setSessionStatus("running");
25398
25449
  }
25399
25450
  }
25400
- async function postAndMaybeSubmit(lineBound, markdown, options2) {
25451
+ async function postAndMaybeSubmit(lineBound, unanchored, markdown, options2) {
25401
25452
  const result = postFindings(lineBound);
25402
25453
  const failedSuffix = result.failed > 0 ? `, ${result.failed} failed` : "";
25403
25454
  console.log(`Posted ${result.posted} comment(s)${failedSuffix}.`);
25404
- if (result.posted === 0) return { posted: 0, submitted: false };
25455
+ const carried = carriedUnanchoredFindings(unanchored);
25456
+ if (result.posted === 0 && carried.length === 0)
25457
+ return { posted: 0, submitted: false };
25405
25458
  const shouldSubmit = await decideSubmit(options2);
25406
25459
  if (shouldSubmit) {
25407
- submitPendingReview(buildReviewBody(markdown));
25460
+ submitReview(buildReviewBody(markdown, unanchored), result.posted);
25408
25461
  return { posted: result.posted, submitted: true };
25409
25462
  }
25410
- console.log("Leaving pending review unsubmitted.");
25463
+ console.log("Leaving the review unsubmitted.");
25411
25464
  return { posted: result.posted, submitted: false };
25412
25465
  }
25413
25466
 
@@ -25503,7 +25556,7 @@ function warnOutOfDiff(outOfDiff) {
25503
25556
  if (outOfDiff.length === 0) return;
25504
25557
  console.warn(
25505
25558
  chalk196.yellow(
25506
- `Skipped ${outOfDiff.length} finding(s) whose lines fall outside the PR diff (GitHub would silently drop these):`
25559
+ `Moved ${outOfDiff.length} finding(s) whose lines fall outside the PR diff into the review body (GitHub cannot anchor a comment on these):`
25507
25560
  )
25508
25561
  );
25509
25562
  for (const finding of outOfDiff) {
@@ -25524,7 +25577,12 @@ function selectInDiffFindings(lineBound, prDiff) {
25524
25577
  buildDiffLineIndex(diff3)
25525
25578
  );
25526
25579
  warnOutOfDiff(outOfDiff);
25527
- return inDiff;
25580
+ return {
25581
+ inDiff,
25582
+ unanchored: outOfDiff.map(
25583
+ (finding) => ({ ...finding, reason: "out-of-diff" })
25584
+ )
25585
+ };
25528
25586
  }
25529
25587
 
25530
25588
  // src/commands/review/warnUnlocated.ts
@@ -25533,7 +25591,7 @@ function warnUnlocated(unlocated) {
25533
25591
  if (unlocated.length === 0) return;
25534
25592
  console.warn(
25535
25593
  chalk197.yellow(
25536
- `Skipped ${unlocated.length} finding(s) without a parseable file:line:`
25594
+ `Moved ${unlocated.length} finding(s) without a parseable file:line into the review body:`
25537
25595
  )
25538
25596
  );
25539
25597
  for (const finding of unlocated) {
@@ -25549,7 +25607,7 @@ function selectPostableFindings(markdown, prInfo) {
25549
25607
  const findings = parseFindings(markdown);
25550
25608
  if (findings.length === 0) {
25551
25609
  console.log("Synthesis contains no findings; nothing to post.");
25552
- return [];
25610
+ return { inDiff: [], unanchored: [] };
25553
25611
  }
25554
25612
  const { lineBound, unlocated, alreadyRaised } = partitionFindings(findings);
25555
25613
  warnUnlocated(unlocated);
@@ -25558,14 +25616,19 @@ function selectPostableFindings(markdown, prInfo) {
25558
25616
  `Skipped ${alreadyRaised.length} finding(s) already raised by prior comments.`
25559
25617
  );
25560
25618
  }
25619
+ const carriedUnlocated = unlocated.map(
25620
+ (finding) => ({ ...finding, reason: "unlocated" })
25621
+ );
25561
25622
  if (lineBound.length === 0) {
25562
- console.log("No line-bound findings to post.");
25563
- return [];
25623
+ console.log("No line-bound findings to comment on.");
25624
+ return { inDiff: [], unanchored: carriedUnlocated };
25564
25625
  }
25565
- const inDiff = selectInDiffFindings(lineBound, prInfo);
25626
+ const { inDiff, unanchored } = selectInDiffFindings(lineBound, prInfo);
25566
25627
  if (inDiff.length === 0)
25567
- console.log("No findings fall within the PR diff; nothing to post.");
25568
- return inDiff;
25628
+ console.log(
25629
+ "No findings fall within the PR diff; no line comments to post."
25630
+ );
25631
+ return { inDiff, unanchored: [...carriedUnlocated, ...unanchored] };
25569
25632
  }
25570
25633
 
25571
25634
  // src/commands/review/stillOnReviewedPr.ts
@@ -25590,23 +25653,29 @@ function stillOnReviewedPr(prNumber) {
25590
25653
 
25591
25654
  // src/commands/review/postReviewToPr.ts
25592
25655
  var NOTHING_POSTED = { posted: 0, submitted: false };
25593
- async function confirmPost(prNumber, count8, options2) {
25656
+ function describeWork(comments3, carried) {
25657
+ const parts = [];
25658
+ if (comments3 > 0) parts.push(`${comments3} line comment(s)`);
25659
+ if (carried > 0) parts.push(`${carried} finding(s) in the review body`);
25660
+ return parts.join(" and ");
25661
+ }
25662
+ async function confirmPost(prNumber, work, options2) {
25594
25663
  if (!options2.prompt) return true;
25595
- return promptConfirm(`Post ${count8} comment(s) to PR #${prNumber}?`, false);
25664
+ return promptConfirm(`Post ${work} to PR #${prNumber}?`, false);
25596
25665
  }
25597
25666
  async function postFindingsToPr(prInfo, synthesisPath, options2) {
25598
25667
  const markdown = readFileSync41(synthesisPath, "utf8");
25599
- const inDiff = selectPostableFindings(markdown, prInfo);
25600
- if (inDiff.length === 0) return NOTHING_POSTED;
25601
- console.log(
25602
- `Found PR #${prInfo.prNumber} with ${inDiff.length} line-bound finding(s) in the diff.`
25603
- );
25604
- const confirmed = await confirmPost(prInfo.prNumber, inDiff.length, options2);
25668
+ const { inDiff, unanchored } = selectPostableFindings(markdown, prInfo);
25669
+ const carried = carriedUnanchoredFindings(unanchored);
25670
+ if (inDiff.length === 0 && carried.length === 0) return NOTHING_POSTED;
25671
+ const work = describeWork(inDiff.length, carried.length);
25672
+ console.log(`Found PR #${prInfo.prNumber} with ${work} to post.`);
25673
+ const confirmed = await confirmPost(prInfo.prNumber, work, options2);
25605
25674
  if (!confirmed) {
25606
25675
  console.log("Skipped posting.");
25607
25676
  return NOTHING_POSTED;
25608
25677
  }
25609
- return postAndMaybeSubmit(inDiff, markdown, options2);
25678
+ return postAndMaybeSubmit(inDiff, unanchored, markdown, options2);
25610
25679
  }
25611
25680
  async function postReviewToPr(synthesisPath, prInfo, options2) {
25612
25681
  if (!stillOnReviewedPr(prInfo.prNumber)) return;
@@ -29590,15 +29659,8 @@ function broadcast(clients, msg) {
29590
29659
  }
29591
29660
  }
29592
29661
 
29593
- // src/commands/sessions/daemon/toSessionInfo.ts
29594
- function toSessionInfo({
29595
- id,
29596
- name,
29597
- title,
29598
- generatedTitle,
29599
- subtitle,
29600
- commandType,
29601
- harness,
29662
+ // src/commands/sessions/daemon/toSessionRunInfo.ts
29663
+ function toSessionRunInfo({
29602
29664
  status: status3,
29603
29665
  startedAt,
29604
29666
  runningMs,
@@ -29611,27 +29673,12 @@ function toSessionInfo({
29611
29673
  serverOrigin,
29612
29674
  assistArgs,
29613
29675
  cwd,
29614
- claudeSessionId,
29615
- restored,
29616
- error,
29617
29676
  activity: activity2,
29618
- autoRun,
29619
- autoAdvance,
29620
- starred,
29621
29677
  usedPct,
29622
- design,
29623
- pendingPrPreview,
29624
- undurable,
29678
+ error,
29625
29679
  closing
29626
29680
  }) {
29627
29681
  return {
29628
- id,
29629
- name,
29630
- title,
29631
- generatedTitle,
29632
- subtitle,
29633
- commandType,
29634
- harness,
29635
29682
  status: status3,
29636
29683
  startedAt,
29637
29684
  runningMs,
@@ -29643,20 +29690,54 @@ function toSessionInfo({
29643
29690
  port: serverPort,
29644
29691
  remoteOrigin: serverOrigin ?? originForCwd(cwd),
29645
29692
  assistArgs,
29693
+ activity: activity2,
29694
+ usedPct,
29695
+ error,
29696
+ closing
29697
+ };
29698
+ }
29699
+
29700
+ // src/commands/sessions/daemon/toSessionInfo.ts
29701
+ function toSessionInfo(session) {
29702
+ const {
29703
+ id,
29704
+ name,
29705
+ title,
29706
+ generatedTitle,
29707
+ subtitle,
29708
+ commandType,
29709
+ harness,
29646
29710
  cwd,
29711
+ launchedFrom,
29712
+ claudeSessionId,
29713
+ restored,
29714
+ autoRun,
29715
+ autoAdvance,
29716
+ starred,
29717
+ design,
29718
+ pendingPrPreview,
29719
+ undurable
29720
+ } = session;
29721
+ return {
29722
+ ...toSessionRunInfo(session),
29723
+ id,
29724
+ name,
29725
+ title,
29726
+ generatedTitle,
29727
+ subtitle,
29728
+ commandType,
29729
+ harness,
29730
+ cwd,
29731
+ launchedFrom,
29647
29732
  claudeSessionId,
29648
29733
  repoGroup: repoGroupForCwd(cwd),
29649
29734
  restored,
29650
- error,
29651
- activity: activity2,
29652
29735
  autoRun,
29653
29736
  autoAdvance,
29654
29737
  starred,
29655
- usedPct,
29656
29738
  design,
29657
29739
  pendingPrPreview,
29658
- undurable,
29659
- closing
29740
+ undurable
29660
29741
  };
29661
29742
  }
29662
29743
 
@@ -29929,7 +30010,8 @@ function createPiSession(id, prompt, cwd, holdPty) {
29929
30010
  initialPrompt: prompt
29930
30011
  };
29931
30012
  }
29932
- function createRunSession(id, runName, runArgs, cwd, meta = serverRunMeta(runName, cwd)) {
30013
+ function createRunSession(id, { runName, runArgs, cwd, meta, launchedFrom }) {
30014
+ const serverMeta = meta ?? serverRunMeta(runName, cwd);
29933
30015
  return {
29934
30016
  ...sessionBase(id, "running"),
29935
30017
  name: `run: ${runName}`,
@@ -29938,9 +30020,10 @@ function createRunSession(id, runName, runArgs, cwd, meta = serverRunMeta(runNam
29938
30020
  runName,
29939
30021
  runArgs,
29940
30022
  cwd,
29941
- server: meta.server || void 0,
29942
- serverPort: meta.port,
29943
- serverOrigin: meta.origin
30023
+ launchedFrom,
30024
+ server: serverMeta.server || void 0,
30025
+ serverPort: serverMeta.port,
30026
+ serverOrigin: serverMeta.origin
29944
30027
  };
29945
30028
  }
29946
30029
 
@@ -30545,8 +30628,9 @@ var PrPreviewCoordinator = class {
30545
30628
 
30546
30629
  // src/commands/sessions/daemon/logSpawnedSession.ts
30547
30630
  function logSpawnedSession(session) {
30631
+ const launchedFrom = session.launchedFrom ? ` launched from ${session.launchedFrom}` : "";
30548
30632
  daemonLog(
30549
- `session ${session.id} spawned: ${session.name} [${session.commandType}] ${session.cwd ?? ""}`
30633
+ `session ${session.id} spawned: ${session.name} [${session.commandType}] ${session.cwd ?? ""}${launchedFrom}`
30550
30634
  );
30551
30635
  }
30552
30636
 
@@ -33598,10 +33682,8 @@ var SessionManager = class {
33598
33682
  addAgent(targetId, prompt, harness) {
33599
33683
  return addAgentToStream(this.treeCtx(), targetId, prompt, harness);
33600
33684
  }
33601
- spawnRun(runName, runArgs, cwd, meta) {
33602
- return this.spawnWith(
33603
- (id) => createRunSession(id, runName, runArgs, cwd, meta)
33604
- );
33685
+ spawnRun(request) {
33686
+ return this.spawnWith((id) => createRunSession(id, request));
33605
33687
  }
33606
33688
  liveServerRun(origin, excludeId) {
33607
33689
  return liveServerRun(this.sessions, origin, excludeId);
@@ -33714,6 +33796,7 @@ function handleCreateRun(client, m, d) {
33714
33796
  const runName = d.runName;
33715
33797
  const cwd = d.cwd;
33716
33798
  const runArgs = d.runArgs ?? [];
33799
+ const launchedFrom = d.launchedFrom;
33717
33800
  const meta = serverRunMeta(runName, cwd);
33718
33801
  if (meta.server && meta.origin) {
33719
33802
  const existing = m.liveServerRun(meta.origin);
@@ -33725,6 +33808,7 @@ function handleCreateRun(client, m, d) {
33725
33808
  type: "run-conflict",
33726
33809
  runName,
33727
33810
  cwd,
33811
+ launchedFrom,
33728
33812
  existing: serverConflictInfo(existing)
33729
33813
  });
33730
33814
  return;
@@ -33738,7 +33822,7 @@ function handleCreateRun(client, m, d) {
33738
33822
  }
33739
33823
  sendTo(client, {
33740
33824
  type: "created",
33741
- sessionId: m.spawnRun(runName, runArgs, cwd, meta),
33825
+ sessionId: m.spawnRun({ runName, runArgs, cwd, meta, launchedFrom }),
33742
33826
  isNew: true
33743
33827
  });
33744
33828
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.522.2",
3
+ "version": "0.524.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {