@staff0rd/assist 0.660.1 → 0.661.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.660.1",
9
+ version: "0.661.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -717,7 +717,12 @@ var assistConfigShape = {
717
717
  readingWordsPerMinute: z3.number().int().positive().optional()
718
718
  }).optional(),
719
719
  review: z3.strictObject({
720
- codexModel: z3.string().optional()
720
+ codexModel: z3.string().optional(),
721
+ highLevel: z3.strictObject({
722
+ criticalPaths: z3.array(z3.string()).default([]),
723
+ uiPaths: z3.array(z3.string()).default([]),
724
+ descriptionWordCap: z3.number().int().positive().default(300)
725
+ }).optional()
721
726
  }).optional(),
722
727
  readTime: z3.strictObject({
723
728
  wordsPerMinute: z3.number().int().positive().default(200)
@@ -1941,10 +1946,10 @@ import fs11 from "fs";
1941
1946
 
1942
1947
  // src/commands/lint/shared.ts
1943
1948
  import chalk24 from "chalk";
1944
- function reportViolations2(violations, checkName, errorMessage, successMessage) {
1949
+ function reportViolations2(violations, checkName2, errorMessage, successMessage) {
1945
1950
  if (violations.length > 0) {
1946
1951
  console.error(chalk24.red(`
1947
- ${checkName} failed:
1952
+ ${checkName2} failed:
1948
1953
  `));
1949
1954
  console.error(chalk24.red(` ${errorMessage}
1950
1955
  `));
@@ -3363,6 +3368,21 @@ var reviewConfigHelp = [
3363
3368
  key: "review.codexModel",
3364
3369
  setter: "assist config set review.codexModel gpt-5-codex",
3365
3370
  note: "optional; runs the codex reviewer on this model via the LiteLLM proxy (needs litellm.baseUrl and litellm.apiKey)"
3371
+ },
3372
+ {
3373
+ key: "review.highLevel.criticalPaths",
3374
+ setter: 'assist config set review.highLevel.criticalPaths "**/*.graphql,en-AU/translation.json"',
3375
+ note: "comma-separated globs whose full diffs the high-level review shows; unset shows none"
3376
+ },
3377
+ {
3378
+ key: "review.highLevel.uiPaths",
3379
+ setter: 'assist config set review.highLevel.uiPaths "src/ui/**"',
3380
+ note: "comma-separated globs that make a change a UI change, so --high-level requires a screenshot or video; unset requires none"
3381
+ },
3382
+ {
3383
+ key: "review.highLevel.descriptionWordCap",
3384
+ setter: "assist config set review.highLevel.descriptionWordCap 300",
3385
+ note: "word cap --high-level holds the PR description to (default 300)"
3366
3386
  }
3367
3387
  ];
3368
3388
 
@@ -5010,12 +5030,12 @@ function addViteBaseConfig() {
5010
5030
  console.log("vite.config.ts already has base config");
5011
5031
  return;
5012
5032
  }
5013
- const updated = content.replace(
5033
+ const updated2 = content.replace(
5014
5034
  /defineConfig\(\{/,
5015
5035
  'defineConfig({\n base: "./",'
5016
5036
  );
5017
- if (updated !== content) {
5018
- writeFileSync13(viteConfigPath, updated);
5037
+ if (updated2 !== content) {
5038
+ writeFileSync13(viteConfigPath, updated2);
5019
5039
  console.log('Added base: "./" to vite.config.ts');
5020
5040
  }
5021
5041
  }
@@ -10716,16 +10736,16 @@ async function patchItemStatus(req, res, id) {
10716
10736
  const result = await findItemOr404(res, id);
10717
10737
  if (!result) return;
10718
10738
  await updateStatus(result.orm, id, status3);
10719
- const updated = { ...result.item, status: status3 };
10720
- respondJson(res, 200, updated);
10739
+ const updated2 = { ...result.item, status: status3 };
10740
+ respondJson(res, 200, updated2);
10721
10741
  }
10722
10742
  async function patchItemStar(req, res, id) {
10723
10743
  const { starred } = await parseStarBody(req);
10724
10744
  const result = await findItemOr404(res, id);
10725
10745
  if (!result) return;
10726
10746
  await updateStarred(result.orm, id, starred);
10727
- const updated = { ...result.item, starred };
10728
- respondJson(res, 200, updated);
10747
+ const updated2 = { ...result.item, starred };
10748
+ respondJson(res, 200, updated2);
10729
10749
  }
10730
10750
 
10731
10751
  // src/commands/backlog/web/deleteItemComment.ts
@@ -13045,15 +13065,15 @@ function setNestedValue(obj, path91, value) {
13045
13065
  }
13046
13066
 
13047
13067
  // src/commands/config/validateConfig.ts
13048
- function validateConfig(updated, key, schema2 = assistConfigSchema) {
13049
- const result = schema2.safeParse(stripLegacyConfigKeys(updated));
13068
+ function validateConfig(updated2, key, schema2 = assistConfigSchema) {
13069
+ const result = schema2.safeParse(stripLegacyConfigKeys(updated2));
13050
13070
  if (result.success) return { ok: true };
13051
13071
  const errors = result.error.issues.flatMap(
13052
13072
  (issue) => formatIssue2(issue, key)
13053
13073
  );
13054
13074
  return {
13055
13075
  ok: false,
13056
- errors: scrubConfigSecrets(errors, updated, describeConfigNode(schema2))
13076
+ errors: scrubConfigSecrets(errors, updated2, describeConfigNode(schema2))
13057
13077
  };
13058
13078
  }
13059
13079
  function formatIssue2(issue, key) {
@@ -13076,14 +13096,14 @@ function applyConfigSet(key, coerced, global, cwd = process.cwd(), globalConfigP
13076
13096
  };
13077
13097
  }
13078
13098
  const raw = global ? loadGlobalConfigRaw(globalConfigPath) : loadProjectConfig(cwd);
13079
- const updated = setNestedValue(raw, key, coerced);
13080
- const validation = validateConfig(updated, key);
13099
+ const updated2 = setNestedValue(raw, key, coerced);
13100
+ const validation = validateConfig(updated2, key);
13081
13101
  if (!validation.ok) return validation;
13082
13102
  if (global) {
13083
- saveGlobalConfig(updated, globalConfigPath);
13103
+ saveGlobalConfig(updated2, globalConfigPath);
13084
13104
  return { ok: true, target: "global" };
13085
13105
  }
13086
- saveConfig(updated, cwd);
13106
+ saveConfig(updated2, cwd);
13087
13107
  return { ok: true, target: "project" };
13088
13108
  }
13089
13109
 
@@ -16467,20 +16487,20 @@ function hasListMutations(options2) {
16467
16487
  options2.add && options2.add.length > 0 || options2.edit || options2.remove
16468
16488
  );
16469
16489
  }
16470
- function applyListMutations(current, options2, flags) {
16490
+ function applyListMutations(current, options2, flags2) {
16471
16491
  let items2 = [...current];
16472
16492
  if (options2.edit) {
16473
16493
  const [rawIndex, ...textParts] = options2.edit;
16474
16494
  if (rawIndex === void 0 || textParts.length === 0) {
16475
16495
  return {
16476
16496
  ok: false,
16477
- error: `${flags.edit} requires an index and replacement text.`
16497
+ error: `${flags2.edit} requires an index and replacement text.`
16478
16498
  };
16479
16499
  }
16480
16500
  const parsed = parseListIndex(
16481
16501
  rawIndex,
16482
16502
  items2.length,
16483
- `${flags.edit} index`
16503
+ `${flags2.edit} index`
16484
16504
  );
16485
16505
  if (!parsed.ok) return parsed;
16486
16506
  items2[parsed.index - 1] = textParts.join(" ");
@@ -16489,7 +16509,7 @@ function applyListMutations(current, options2, flags) {
16489
16509
  const parsed = parseListIndex(
16490
16510
  options2.remove,
16491
16511
  items2.length,
16492
- `${flags.remove} index`
16512
+ `${flags2.remove} index`
16493
16513
  );
16494
16514
  if (!parsed.ok) return parsed;
16495
16515
  items2 = items2.filter((_, i) => i !== parsed.index - 1);
@@ -16501,15 +16521,15 @@ function applyListMutations(current, options2, flags) {
16501
16521
  }
16502
16522
 
16503
16523
  // src/commands/backlog/update/resolveListUpdate.ts
16504
- function resolveListUpdate(whole, current, mutations, wholeFlag, flags) {
16524
+ function resolveListUpdate(whole, current, mutations, wholeFlag, flags2) {
16505
16525
  if (!hasListMutations(mutations)) return { ok: true, items: whole };
16506
16526
  if (whole) {
16507
16527
  return {
16508
16528
  ok: false,
16509
- error: `Cannot combine ${wholeFlag} with ${flags.add}/${flags.edit}/${flags.remove}.`
16529
+ error: `Cannot combine ${wholeFlag} with ${flags2.add}/${flags2.edit}/${flags2.remove}.`
16510
16530
  };
16511
16531
  }
16512
- return applyListMutations(current, mutations, flags);
16532
+ return applyListMutations(current, mutations, flags2);
16513
16533
  }
16514
16534
 
16515
16535
  // src/commands/backlog/resolvePhaseFields.ts
@@ -18428,8 +18448,8 @@ function loadDenyConfig(global) {
18428
18448
  const deny = config.deny ?? [];
18429
18449
  return {
18430
18450
  deny,
18431
- saveDeny: (updated) => {
18432
- config.deny = updated;
18451
+ saveDeny: (updated2) => {
18452
+ config.deny = updated2;
18433
18453
  save(config);
18434
18454
  }
18435
18455
  };
@@ -23278,7 +23298,7 @@ function registerCreateIssue(issueCommand) {
23278
23298
  []
23279
23299
  ).addHelpText(
23280
23300
  "after",
23281
- "\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots or video into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they are discarded \u2014 so never author that section yourself.\n--type, --parent, --project, --status and --label are all resolved before the preview, so an unknown name, an unreadable parent, a missing project scope or --status without --project creates nothing.\nA bare --parent number is read against --repo, or the current repo; a parent in another repository is allowed.\nThe project scope is needed for --project: gh auth refresh -h github.com -s project"
23301
+ "\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots or video into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they stay attached and reappear in the preview you re-propose \u2014 so never author that section yourself.\n--type, --parent, --project, --status and --label are all resolved before the preview, so an unknown name, an unreadable parent, a missing project scope or --status without --project creates nothing.\nA bare --parent number is read against --repo, or the current repo; a parent in another repository is allowed.\nThe project scope is needed for --project: gh auth refresh -h github.com -s project"
23282
23302
  ).action(createIssue);
23283
23303
  }
23284
23304
 
@@ -27471,8 +27491,218 @@ async function reply(commentId, body) {
27471
27491
  }
27472
27492
  }
27473
27493
 
27474
- // src/commands/prs/wontfix.ts
27494
+ // src/commands/prs/status/describeFetchError.ts
27495
+ function firstLine2(text18) {
27496
+ const line = text18.split("\n").map((candidate) => candidate.trim()).find((candidate) => candidate.length > 0);
27497
+ return line ?? null;
27498
+ }
27499
+ function stderrText(error) {
27500
+ const stderr = error?.stderr;
27501
+ if (typeof stderr === "string") return firstLine2(stderr);
27502
+ if (stderr instanceof Uint8Array) {
27503
+ return firstLine2(Buffer.from(stderr).toString("utf8"));
27504
+ }
27505
+ return null;
27506
+ }
27507
+ function describeFetchError(error) {
27508
+ if (isGhNotInstalled(error)) {
27509
+ return "GitHub CLI (gh) is not installed \u2014 see https://cli.github.com/";
27510
+ }
27511
+ const stderr = stderrText(error);
27512
+ if (stderr) return stderr;
27513
+ if (error instanceof Error) return firstLine2(error.message) ?? error.message;
27514
+ return String(error);
27515
+ }
27516
+
27517
+ // src/commands/prs/status/fetchRepoPullRequests.ts
27475
27518
  import { execSync as execSync51 } from "child_process";
27519
+ var FIELDS = "number,title,url,author,isDraft,createdAt,updatedAt,reviewDecision,latestReviews,statusCheckRollup,mergeable";
27520
+ function fetchRepoPullRequests(org, repo) {
27521
+ const output = execSync51(
27522
+ `gh pr list --state open --json ${FIELDS} --limit 100 -R ${org}/${repo}`,
27523
+ { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
27524
+ );
27525
+ const parsed = JSON.parse(output);
27526
+ if (!Array.isArray(parsed)) {
27527
+ throw new Error("unexpected response from gh pr list");
27528
+ }
27529
+ return parsed;
27530
+ }
27531
+
27532
+ // src/commands/prs/status/parseRepoArgument.ts
27533
+ var REPO_ARGUMENT = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/;
27534
+ function parseRepoArgument(value) {
27535
+ const match = REPO_ARGUMENT.exec(value.trim());
27536
+ if (!match) return null;
27537
+ return { org: match[1], repo: match[2] };
27538
+ }
27539
+
27540
+ // src/commands/prs/status/describeAge.ts
27541
+ function describeAge(timestamp6, now = Date.now()) {
27542
+ const parsed = new Date(timestamp6).getTime();
27543
+ if (Number.isNaN(parsed)) return { hours: null, label: "unknown" };
27544
+ const hours = Math.max(0, Math.floor((now - parsed) / 36e5));
27545
+ if (hours < 1) return { hours, label: "<1h" };
27546
+ if (hours < 24) return { hours, label: `${hours}h` };
27547
+ return { hours, label: `${Math.floor(hours / 24)}d` };
27548
+ }
27549
+
27550
+ // src/commands/prs/status/summariseChecks.ts
27551
+ var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "NEUTRAL", "SKIPPED"]);
27552
+ var PENDING_STATES = /* @__PURE__ */ new Set(["PENDING", "EXPECTED"]);
27553
+ function checkName(check2) {
27554
+ return check2.name?.trim() || check2.context?.trim() || "unnamed check";
27555
+ }
27556
+ function isPending(check2) {
27557
+ if (check2.status) return check2.status !== "COMPLETED";
27558
+ return PENDING_STATES.has(check2.state ?? "");
27559
+ }
27560
+ function isFailing(check2) {
27561
+ if (check2.status) return !PASSING_CONCLUSIONS.has(check2.conclusion ?? "");
27562
+ return !PASSING_CONCLUSIONS.has(check2.state ?? "");
27563
+ }
27564
+ function summariseChecks(rollup) {
27565
+ const summary = { failing: [], pending: [] };
27566
+ for (const check2 of rollup ?? []) {
27567
+ if (isPending(check2)) summary.pending.push(checkName(check2));
27568
+ else if (isFailing(check2)) summary.failing.push(checkName(check2));
27569
+ }
27570
+ return summary;
27571
+ }
27572
+
27573
+ // src/commands/prs/status/toPrStatus.ts
27574
+ function toReviews(pr) {
27575
+ return (pr.latestReviews ?? []).map((review2) => ({
27576
+ reviewer: review2.author?.login ?? "unknown",
27577
+ state: review2.state ?? "UNKNOWN"
27578
+ }));
27579
+ }
27580
+ function toPrStatus(pr, now = Date.now()) {
27581
+ const age = describeAge(pr.updatedAt, now);
27582
+ return {
27583
+ number: pr.number,
27584
+ title: pr.title,
27585
+ url: pr.url,
27586
+ author: pr.author?.login ?? "unknown",
27587
+ isBot: pr.author?.is_bot === true,
27588
+ isDraft: pr.isDraft === true,
27589
+ createdAt: pr.createdAt,
27590
+ updatedAt: pr.updatedAt,
27591
+ age: age.label,
27592
+ ageHours: age.hours,
27593
+ reviewDecision: pr.reviewDecision || null,
27594
+ reviews: toReviews(pr),
27595
+ checks: summariseChecks(pr.statusCheckRollup),
27596
+ mergeable: pr.mergeable || "UNKNOWN"
27597
+ };
27598
+ }
27599
+
27600
+ // src/commands/prs/status/buildPrsStatusReport.ts
27601
+ function buildPrsStatusReport(repoArguments, now = Date.now()) {
27602
+ const report2 = { repos: [], errors: [] };
27603
+ for (const argument of repoArguments) {
27604
+ const parsed = parseRepoArgument(argument);
27605
+ if (!parsed) {
27606
+ report2.errors.push({
27607
+ repo: argument,
27608
+ error: "not an owner/repo argument"
27609
+ });
27610
+ continue;
27611
+ }
27612
+ const repo = `${parsed.org}/${parsed.repo}`;
27613
+ try {
27614
+ const pullRequests = fetchRepoPullRequests(parsed.org, parsed.repo).map(
27615
+ (pr) => toPrStatus(pr, now)
27616
+ );
27617
+ report2.repos.push({ repo, pullRequests });
27618
+ } catch (error) {
27619
+ report2.errors.push({ repo, error: describeFetchError(error) });
27620
+ }
27621
+ }
27622
+ return report2;
27623
+ }
27624
+
27625
+ // src/commands/prs/status/printPrsStatus.ts
27626
+ import chalk187 from "chalk";
27627
+
27628
+ // src/commands/prs/status/printPrStatus.ts
27629
+ import chalk186 from "chalk";
27630
+ function flags(pr) {
27631
+ const markers = [];
27632
+ if (pr.isDraft) markers.push("draft");
27633
+ if (pr.isBot) markers.push("bot");
27634
+ return markers.length ? ` ${chalk186.yellow(`[${markers.join(", ")}]`)}` : "";
27635
+ }
27636
+ function updated(pr) {
27637
+ return pr.ageHours === null ? "updated unknown" : `updated ${pr.age} ago`;
27638
+ }
27639
+ function reviewLine(pr) {
27640
+ if (!pr.reviewDecision && pr.reviews.length === 0) return null;
27641
+ const reviewers = pr.reviews.map((review2) => `${review2.reviewer}: ${review2.state}`).join(", ");
27642
+ const decision = pr.reviewDecision ?? "no decision";
27643
+ return reviewers ? `review: ${decision} | ${reviewers}` : `review: ${decision}`;
27644
+ }
27645
+ function checkLines(pr) {
27646
+ const lines2 = [];
27647
+ if (pr.checks.failing.length) {
27648
+ lines2.push(chalk186.red(`failing: ${pr.checks.failing.join(", ")}`));
27649
+ }
27650
+ if (pr.checks.pending.length) {
27651
+ lines2.push(chalk186.yellow(`pending: ${pr.checks.pending.join(", ")}`));
27652
+ }
27653
+ return lines2;
27654
+ }
27655
+ function mergeableLine(pr) {
27656
+ if (pr.mergeable === "CONFLICTING") return chalk186.red("conflicting");
27657
+ if (pr.mergeable === "MERGEABLE") return null;
27658
+ return chalk186.dim(`mergeable: ${pr.mergeable.toLowerCase()}`);
27659
+ }
27660
+ function printPrStatus(pr) {
27661
+ console.log(
27662
+ ` ${chalk186.cyan(`#${pr.number}`)} ${pr.title} ${chalk186.dim(`(${pr.author}, ${updated(pr)})`)}${flags(pr)}`
27663
+ );
27664
+ const details = [
27665
+ reviewLine(pr),
27666
+ ...checkLines(pr),
27667
+ mergeableLine(pr),
27668
+ chalk186.dim(pr.url)
27669
+ ];
27670
+ for (const detail of details) {
27671
+ if (detail) console.log(` ${detail}`);
27672
+ }
27673
+ }
27674
+
27675
+ // src/commands/prs/status/printPrsStatus.ts
27676
+ function printPrsStatus(report2) {
27677
+ for (const repo of report2.repos) {
27678
+ const count8 = repo.pullRequests.length;
27679
+ console.log(`${chalk187.bold(repo.repo)} ${chalk187.dim(`(${count8} open)`)}`);
27680
+ for (const pr of repo.pullRequests) {
27681
+ printPrStatus(pr);
27682
+ }
27683
+ if (count8 === 0) console.log(chalk187.dim(" no open pull requests"));
27684
+ console.log();
27685
+ }
27686
+ if (report2.errors.length === 0) return;
27687
+ console.log(chalk187.bold("Errors"));
27688
+ for (const failure of report2.errors) {
27689
+ console.log(` ${chalk187.red(failure.repo)}: ${failure.error}`);
27690
+ }
27691
+ console.log();
27692
+ }
27693
+
27694
+ // src/commands/prs/status/index.ts
27695
+ function prsStatus(repos2, options2) {
27696
+ const report2 = buildPrsStatusReport(repos2);
27697
+ if (options2.json) {
27698
+ console.log(JSON.stringify(report2, null, 2));
27699
+ return;
27700
+ }
27701
+ printPrsStatus(report2);
27702
+ }
27703
+
27704
+ // src/commands/prs/wontfix.ts
27705
+ import { execSync as execSync52 } from "child_process";
27476
27706
  function validateReason(reason4) {
27477
27707
  const lowerReason = reason4.toLowerCase();
27478
27708
  if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
@@ -27489,7 +27719,7 @@ function validateShaReferences(reason4) {
27489
27719
  const invalidShas = [];
27490
27720
  for (const sha of shas) {
27491
27721
  try {
27492
- execSync51(`git cat-file -t ${sha}`, { stdio: "pipe" });
27722
+ execSync52(`git cat-file -t ${sha}`, { stdio: "pipe" });
27493
27723
  } catch {
27494
27724
  invalidShas.push(sha);
27495
27725
  }
@@ -27734,8 +27964,9 @@ as numbered quoted-span + note pairs on stderr. Address every comment (and the
27734
27964
  reason), then run the command again to re-preview the revised PR. Repeat until it
27735
27965
  is approved. The reviewer may also drop or paste screenshots or video into the
27736
27966
  pane; on approval these are appended to the PR body under a ## Screenshots section
27737
- automatically (they are discarded on rejection), so you never author that section
27738
- yourself. Just compose the sections and run the command.`;
27967
+ automatically; on rejection they stay attached and reappear in the preview you
27968
+ re-propose, so you never author that section yourself. Just compose the sections
27969
+ and run the command.`;
27739
27970
  function raiseHelpText(promptJira, promptGithub, draft) {
27740
27971
  const config = loadConfig().prs;
27741
27972
  const jira = promptJira ?? config?.promptJira ?? false;
@@ -27781,6 +28012,13 @@ function registerPrsRaise(prsCommand) {
27781
28012
  configHelp(raiseCommand, prsRaiseConfigHelp);
27782
28013
  }
27783
28014
 
28015
+ // src/commands/registerPrsStatus.ts
28016
+ function registerPrsStatus(prsCommand) {
28017
+ prsCommand.command("status <repo...>").description(
28018
+ "Report every open pull request in each owner/repo, grouped by repo"
28019
+ ).option("--json", "Output as JSON").action(prsStatus);
28020
+ }
28021
+
27784
28022
  // src/commands/readTime/readTime.ts
27785
28023
  import { readFileSync as readFileSync46 } from "fs";
27786
28024
 
@@ -27870,7 +28108,7 @@ function resolveReadTimeTarget(target) {
27870
28108
  }
27871
28109
 
27872
28110
  // src/commands/prs/fetchPrBody.ts
27873
- import { execSync as execSync52 } from "child_process";
28111
+ import { execSync as execSync53 } from "child_process";
27874
28112
  function exitGhNotInstalled() {
27875
28113
  console.error("Error: GitHub CLI (gh) is not installed.");
27876
28114
  console.error("Install it from https://cli.github.com/");
@@ -27891,7 +28129,7 @@ function currentRepo() {
27891
28129
  function fetchPrBody(number, repo) {
27892
28130
  const { org, repo: name } = repo ?? currentRepo();
27893
28131
  try {
27894
- const raw = execSync52(`gh pr view ${number} --json body -R ${org}/${name}`, {
28132
+ const raw = execSync53(`gh pr view ${number} --json body -R ${org}/${name}`, {
27895
28133
  encoding: "utf8",
27896
28134
  stdio: ["pipe", "pipe", "pipe"]
27897
28135
  });
@@ -27970,6 +28208,7 @@ function registerReadTime(parent) {
27970
28208
  function registerPrs(program2) {
27971
28209
  const prsCommand = program2.command("prs").description("Pull request utilities").option("--open", "List only open pull requests").option("--closed", "List only closed pull requests").action(prs);
27972
28210
  registerPrsRaise(prsCommand);
28211
+ registerPrsStatus(prsCommand);
27973
28212
  registerPrsEdit(prsCommand);
27974
28213
  registerPrsComments(prsCommand);
27975
28214
  registerReadTime(prsCommand);
@@ -27977,10 +28216,10 @@ function registerPrs(program2) {
27977
28216
  }
27978
28217
 
27979
28218
  // src/commands/ravendb/ravendbAuth.ts
27980
- import chalk191 from "chalk";
28219
+ import chalk193 from "chalk";
27981
28220
 
27982
28221
  // src/shared/createConnectionAuth.ts
27983
- import chalk186 from "chalk";
28222
+ import chalk188 from "chalk";
27984
28223
  function listConnections(connections, format2) {
27985
28224
  if (connections.length === 0) {
27986
28225
  console.log("No connections configured.");
@@ -27993,7 +28232,7 @@ function listConnections(connections, format2) {
27993
28232
  function removeConnection(connections, name, save) {
27994
28233
  const filtered = connections.filter((c) => c.name !== name);
27995
28234
  if (filtered.length === connections.length) {
27996
- console.error(chalk186.red(`Connection "${name}" not found.`));
28235
+ console.error(chalk188.red(`Connection "${name}" not found.`));
27997
28236
  process.exit(1);
27998
28237
  }
27999
28238
  save(filtered);
@@ -28039,17 +28278,17 @@ function saveConnections(connections) {
28039
28278
  }
28040
28279
 
28041
28280
  // src/commands/ravendb/promptConnection.ts
28042
- import chalk189 from "chalk";
28281
+ import chalk191 from "chalk";
28043
28282
 
28044
28283
  // src/commands/ravendb/selectOpSecret.ts
28045
- import chalk188 from "chalk";
28284
+ import chalk190 from "chalk";
28046
28285
  import Enquirer2 from "enquirer";
28047
28286
 
28048
28287
  // src/commands/ravendb/searchItems.ts
28049
- import { execSync as execSync53 } from "child_process";
28050
- import chalk187 from "chalk";
28288
+ import { execSync as execSync54 } from "child_process";
28289
+ import chalk189 from "chalk";
28051
28290
  function opExec(args) {
28052
- return execSync53(`op ${args}`, {
28291
+ return execSync54(`op ${args}`, {
28053
28292
  encoding: "utf8",
28054
28293
  stdio: ["pipe", "pipe", "pipe"]
28055
28294
  }).trim();
@@ -28060,7 +28299,7 @@ function searchItems(search2) {
28060
28299
  items2 = JSON.parse(opExec("item list --format=json"));
28061
28300
  } catch {
28062
28301
  console.error(
28063
- chalk187.red(
28302
+ chalk189.red(
28064
28303
  "Failed to search 1Password. Ensure the CLI is installed and you are signed in."
28065
28304
  )
28066
28305
  );
@@ -28074,7 +28313,7 @@ function getItemFields(itemId2) {
28074
28313
  const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
28075
28314
  return item.fields.filter((f) => f.reference && f.label);
28076
28315
  } catch {
28077
- console.error(chalk187.red("Failed to get item details from 1Password."));
28316
+ console.error(chalk189.red("Failed to get item details from 1Password."));
28078
28317
  process.exit(1);
28079
28318
  }
28080
28319
  }
@@ -28093,7 +28332,7 @@ async function selectOpSecret(searchTerm) {
28093
28332
  }).run();
28094
28333
  const items2 = searchItems(search2);
28095
28334
  if (items2.length === 0) {
28096
- console.error(chalk188.red(`No items found matching "${search2}".`));
28335
+ console.error(chalk190.red(`No items found matching "${search2}".`));
28097
28336
  process.exit(1);
28098
28337
  }
28099
28338
  const itemId2 = await selectOne(
@@ -28102,7 +28341,7 @@ async function selectOpSecret(searchTerm) {
28102
28341
  );
28103
28342
  const fields = getItemFields(itemId2);
28104
28343
  if (fields.length === 0) {
28105
- console.error(chalk188.red("No fields with references found on this item."));
28344
+ console.error(chalk190.red("No fields with references found on this item."));
28106
28345
  process.exit(1);
28107
28346
  }
28108
28347
  const ref = await selectOne(
@@ -28116,7 +28355,7 @@ async function selectOpSecret(searchTerm) {
28116
28355
  async function promptConnection(existingNames) {
28117
28356
  const name = await promptInput("name", "Connection name:");
28118
28357
  if (existingNames.includes(name)) {
28119
- console.error(chalk189.red(`Connection "${name}" already exists.`));
28358
+ console.error(chalk191.red(`Connection "${name}" already exists.`));
28120
28359
  process.exit(1);
28121
28360
  }
28122
28361
  const url = await promptInput(
@@ -28125,22 +28364,22 @@ async function promptConnection(existingNames) {
28125
28364
  );
28126
28365
  const database = await promptInput("database", "Database name:");
28127
28366
  if (!name || !url || !database) {
28128
- console.error(chalk189.red("All fields are required."));
28367
+ console.error(chalk191.red("All fields are required."));
28129
28368
  process.exit(1);
28130
28369
  }
28131
28370
  const apiKeyRef = await selectOpSecret();
28132
- console.log(chalk189.dim(`Using: ${apiKeyRef}`));
28371
+ console.log(chalk191.dim(`Using: ${apiKeyRef}`));
28133
28372
  return { name, url, database, apiKeyRef };
28134
28373
  }
28135
28374
 
28136
28375
  // src/commands/ravendb/ravendbSetConnection.ts
28137
- import chalk190 from "chalk";
28376
+ import chalk192 from "chalk";
28138
28377
  function ravendbSetConnection(name) {
28139
28378
  const raw = loadGlobalConfigRaw();
28140
28379
  const ravendb = raw.ravendb ?? {};
28141
28380
  const connections = ravendb.connections ?? [];
28142
28381
  if (!connections.some((c) => c.name === name)) {
28143
- console.error(chalk190.red(`Connection "${name}" not found.`));
28382
+ console.error(chalk192.red(`Connection "${name}" not found.`));
28144
28383
  console.error(
28145
28384
  `Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
28146
28385
  );
@@ -28156,16 +28395,16 @@ function ravendbSetConnection(name) {
28156
28395
  var ravendbAuth = createConnectionAuth({
28157
28396
  load: loadConnections,
28158
28397
  save: saveConnections,
28159
- format: (c) => `${chalk191.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`,
28398
+ format: (c) => `${chalk193.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`,
28160
28399
  promptNew: promptConnection,
28161
28400
  onFirst: (c) => ravendbSetConnection(c.name)
28162
28401
  });
28163
28402
 
28164
28403
  // src/commands/ravendb/ravendbCollections.ts
28165
- import chalk195 from "chalk";
28404
+ import chalk197 from "chalk";
28166
28405
 
28167
28406
  // src/commands/ravendb/ravenFetch.ts
28168
- import chalk193 from "chalk";
28407
+ import chalk195 from "chalk";
28169
28408
 
28170
28409
  // src/commands/ravendb/getAccessToken.ts
28171
28410
  var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
@@ -28201,21 +28440,21 @@ ${errorText}`
28201
28440
  }
28202
28441
 
28203
28442
  // src/commands/ravendb/resolveOpSecret.ts
28204
- import { execSync as execSync54 } from "child_process";
28205
- import chalk192 from "chalk";
28443
+ import { execSync as execSync55 } from "child_process";
28444
+ import chalk194 from "chalk";
28206
28445
  function resolveOpSecret(reference) {
28207
28446
  if (!reference.startsWith("op://")) {
28208
- console.error(chalk192.red(`Invalid secret reference: must start with op://`));
28447
+ console.error(chalk194.red(`Invalid secret reference: must start with op://`));
28209
28448
  process.exit(1);
28210
28449
  }
28211
28450
  try {
28212
- return execSync54(`op read "${reference}"`, {
28451
+ return execSync55(`op read "${reference}"`, {
28213
28452
  encoding: "utf8",
28214
28453
  stdio: ["pipe", "pipe", "pipe"]
28215
28454
  }).trim();
28216
28455
  } catch {
28217
28456
  console.error(
28218
- chalk192.red(
28457
+ chalk194.red(
28219
28458
  "Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
28220
28459
  )
28221
28460
  );
@@ -28242,7 +28481,7 @@ async function ravenFetch(connection, path91) {
28242
28481
  if (!response.ok) {
28243
28482
  const body = await response.text();
28244
28483
  console.error(
28245
- chalk193.red(`RavenDB error: ${response.status} ${response.statusText}`)
28484
+ chalk195.red(`RavenDB error: ${response.status} ${response.statusText}`)
28246
28485
  );
28247
28486
  console.error(body.substring(0, 500));
28248
28487
  process.exit(1);
@@ -28251,7 +28490,7 @@ async function ravenFetch(connection, path91) {
28251
28490
  }
28252
28491
 
28253
28492
  // src/commands/ravendb/resolveConnection.ts
28254
- import chalk194 from "chalk";
28493
+ import chalk196 from "chalk";
28255
28494
  function loadRavendb() {
28256
28495
  const raw = loadGlobalConfigRaw();
28257
28496
  const ravendb = raw.ravendb;
@@ -28265,7 +28504,7 @@ function resolveConnection(name) {
28265
28504
  const connectionName = name ?? defaultConnection;
28266
28505
  if (!connectionName) {
28267
28506
  console.error(
28268
- chalk194.red(
28507
+ chalk196.red(
28269
28508
  "No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
28270
28509
  )
28271
28510
  );
@@ -28273,7 +28512,7 @@ function resolveConnection(name) {
28273
28512
  }
28274
28513
  const connection = connections.find((c) => c.name === connectionName);
28275
28514
  if (!connection) {
28276
- console.error(chalk194.red(`Connection "${connectionName}" not found.`));
28515
+ console.error(chalk196.red(`Connection "${connectionName}" not found.`));
28277
28516
  console.error(
28278
28517
  `Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
28279
28518
  );
@@ -28304,15 +28543,15 @@ async function ravendbCollections(connectionName) {
28304
28543
  return;
28305
28544
  }
28306
28545
  for (const c of collections) {
28307
- console.log(`${chalk195.bold(c.Name)} ${c.CountOfDocuments} docs`);
28546
+ console.log(`${chalk197.bold(c.Name)} ${c.CountOfDocuments} docs`);
28308
28547
  }
28309
28548
  }
28310
28549
 
28311
28550
  // src/commands/ravendb/ravendbQuery.ts
28312
- import chalk197 from "chalk";
28551
+ import chalk199 from "chalk";
28313
28552
 
28314
28553
  // src/commands/ravendb/fetchAllPages.ts
28315
- import chalk196 from "chalk";
28554
+ import chalk198 from "chalk";
28316
28555
 
28317
28556
  // src/commands/ravendb/buildQueryPath.ts
28318
28557
  function buildQueryPath(opts) {
@@ -28350,7 +28589,7 @@ async function fetchAllPages(connection, opts) {
28350
28589
  allResults.push(...results);
28351
28590
  start3 += results.length;
28352
28591
  process.stderr.write(
28353
- `\r${chalk196.dim(`Fetched ${allResults.length}/${totalResults}`)}`
28592
+ `\r${chalk198.dim(`Fetched ${allResults.length}/${totalResults}`)}`
28354
28593
  );
28355
28594
  if (start3 >= totalResults) break;
28356
28595
  if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
@@ -28365,7 +28604,7 @@ async function fetchAllPages(connection, opts) {
28365
28604
  async function ravendbQuery(connectionName, collection, options2) {
28366
28605
  const resolved = resolveArgs(connectionName, collection);
28367
28606
  if (!resolved.collection && !options2.query) {
28368
- console.error(chalk197.red("Provide a collection name or --query filter."));
28607
+ console.error(chalk199.red("Provide a collection name or --query filter."));
28369
28608
  process.exit(1);
28370
28609
  }
28371
28610
  const { collection: col } = resolved;
@@ -28404,7 +28643,7 @@ import { spawn as spawn6 } from "child_process";
28404
28643
  import * as path46 from "path";
28405
28644
 
28406
28645
  // src/commands/refactor/logViolations.ts
28407
- import chalk198 from "chalk";
28646
+ import chalk200 from "chalk";
28408
28647
  var DEFAULT_MAX_LINES2 = 100;
28409
28648
  function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
28410
28649
  if (violations.length === 0) {
@@ -28413,43 +28652,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
28413
28652
  }
28414
28653
  return;
28415
28654
  }
28416
- console.error(chalk198.red(`
28655
+ console.error(chalk200.red(`
28417
28656
  Refactor check failed:
28418
28657
  `));
28419
- console.error(chalk198.red(` The following files exceed ${maxLines} lines:
28658
+ console.error(chalk200.red(` The following files exceed ${maxLines} lines:
28420
28659
  `));
28421
28660
  for (const violation of violations) {
28422
- console.error(chalk198.red(` ${violation.file} (${violation.lines} lines)`));
28661
+ console.error(chalk200.red(` ${violation.file} (${violation.lines} lines)`));
28423
28662
  }
28424
28663
  console.error(
28425
- chalk198.yellow(
28664
+ chalk200.yellow(
28426
28665
  `
28427
28666
  Each file needs to be sensibly refactored, or if there is no sensible
28428
28667
  way to refactor it, ignore it with:
28429
28668
  `
28430
28669
  )
28431
28670
  );
28432
- console.error(chalk198.gray(` assist refactor ignore <file>
28671
+ console.error(chalk200.gray(` assist refactor ignore <file>
28433
28672
  `));
28434
28673
  if (process.env.CLAUDECODE) {
28435
- console.error(chalk198.cyan(`
28674
+ console.error(chalk200.cyan(`
28436
28675
  ## Extracting Code to New Files
28437
28676
  `));
28438
28677
  console.error(
28439
- chalk198.cyan(
28678
+ chalk200.cyan(
28440
28679
  ` When extracting logic from one file to another, consider where the extracted code belongs:
28441
28680
  `
28442
28681
  )
28443
28682
  );
28444
28683
  console.error(
28445
- chalk198.cyan(
28684
+ chalk200.cyan(
28446
28685
  ` 1. Keep related logic together: If the extracted code is tightly coupled to the
28447
28686
  original file's domain, create a new folder containing both the original and extracted files.
28448
28687
  `
28449
28688
  )
28450
28689
  );
28451
28690
  console.error(
28452
- chalk198.cyan(
28691
+ chalk200.cyan(
28453
28692
  ` 2. Share common utilities: If the extracted code can be reused across multiple
28454
28693
  domains, move it to a common/shared folder.
28455
28694
  `
@@ -28459,7 +28698,7 @@ Refactor check failed:
28459
28698
  }
28460
28699
 
28461
28700
  // src/commands/refactor/check/getViolations/index.ts
28462
- import { execSync as execSync55 } from "child_process";
28701
+ import { execSync as execSync56 } from "child_process";
28463
28702
  import fs31 from "fs";
28464
28703
  import { minimatch as minimatch6 } from "minimatch";
28465
28704
 
@@ -28509,7 +28748,7 @@ function getGitFiles(options2) {
28509
28748
  }
28510
28749
  const files = /* @__PURE__ */ new Set();
28511
28750
  if (options2.staged || options2.modified) {
28512
- const staged = execSync55("git diff --cached --name-only", {
28751
+ const staged = execSync56("git diff --cached --name-only", {
28513
28752
  encoding: "utf8"
28514
28753
  });
28515
28754
  for (const file of staged.trim().split("\n").filter(Boolean)) {
@@ -28517,7 +28756,7 @@ function getGitFiles(options2) {
28517
28756
  }
28518
28757
  }
28519
28758
  if (options2.unstaged || options2.modified) {
28520
- const unstaged = execSync55("git diff --name-only", { encoding: "utf8" });
28759
+ const unstaged = execSync56("git diff --name-only", { encoding: "utf8" });
28521
28760
  for (const file of unstaged.trim().split("\n").filter(Boolean)) {
28522
28761
  files.add(file);
28523
28762
  }
@@ -28605,7 +28844,7 @@ async function check(pattern2, options2) {
28605
28844
 
28606
28845
  // src/commands/refactor/extract/index.ts
28607
28846
  import path54 from "path";
28608
- import chalk201 from "chalk";
28847
+ import chalk203 from "chalk";
28609
28848
 
28610
28849
  // src/commands/refactor/extract/applyExtraction.ts
28611
28850
  import { SyntaxKind as SyntaxKind4 } from "ts-morph";
@@ -29204,23 +29443,23 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
29204
29443
 
29205
29444
  // src/commands/refactor/extract/displayPlan.ts
29206
29445
  import path50 from "path";
29207
- import chalk199 from "chalk";
29446
+ import chalk201 from "chalk";
29208
29447
  function section(title) {
29209
29448
  return `
29210
- ${chalk199.cyan(title)}`;
29449
+ ${chalk201.cyan(title)}`;
29211
29450
  }
29212
29451
  function displayImporters(plan2, cwd) {
29213
29452
  if (plan2.importersToUpdate.length === 0) return;
29214
29453
  console.log(section("Update importers:"));
29215
29454
  for (const imp of plan2.importersToUpdate) {
29216
29455
  const rel = path50.relative(cwd, imp.file.getFilePath());
29217
- console.log(` ${chalk199.dim(rel)}: \u2192 import from "${imp.relPath}"`);
29456
+ console.log(` ${chalk201.dim(rel)}: \u2192 import from "${imp.relPath}"`);
29218
29457
  }
29219
29458
  }
29220
29459
  function displayPlan(functionName, relDest, plan2, cwd) {
29221
- console.log(chalk199.bold(`Extract: ${functionName} \u2192 ${relDest}
29460
+ console.log(chalk201.bold(`Extract: ${functionName} \u2192 ${relDest}
29222
29461
  `));
29223
- console.log(` ${chalk199.cyan("Functions to move:")}`);
29462
+ console.log(` ${chalk201.cyan("Functions to move:")}`);
29224
29463
  for (const name of plan2.extractedNames) {
29225
29464
  console.log(` ${name}`);
29226
29465
  }
@@ -29254,7 +29493,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
29254
29493
 
29255
29494
  // src/commands/refactor/extract/loadProjectFile.ts
29256
29495
  import path53 from "path";
29257
- import chalk200 from "chalk";
29496
+ import chalk202 from "chalk";
29258
29497
  import { Project as Project4 } from "ts-morph";
29259
29498
 
29260
29499
  // src/commands/refactor/extract/findTsConfig.ts
@@ -29346,7 +29585,7 @@ function loadProjectFile(file) {
29346
29585
  });
29347
29586
  const sourceFile = project.getSourceFile(sourcePath);
29348
29587
  if (!sourceFile) {
29349
- console.log(chalk200.red(`File not found in project: ${file}`));
29588
+ console.log(chalk202.red(`File not found in project: ${file}`));
29350
29589
  process.exit(1);
29351
29590
  }
29352
29591
  return { project, sourceFile };
@@ -29369,19 +29608,19 @@ async function extract(file, functionName, destination, options2 = {}) {
29369
29608
  displayPlan(functionName, relDest, plan2, cwd);
29370
29609
  if (options2.apply) {
29371
29610
  await applyExtraction(functionName, sourceFile, destPath, plan2, project);
29372
- console.log(chalk201.green("\nExtraction complete"));
29611
+ console.log(chalk203.green("\nExtraction complete"));
29373
29612
  } else {
29374
- console.log(chalk201.dim("\nDry run. Use --apply to execute."));
29613
+ console.log(chalk203.dim("\nDry run. Use --apply to execute."));
29375
29614
  }
29376
29615
  }
29377
29616
 
29378
29617
  // src/commands/refactor/ignore.ts
29379
29618
  import fs34 from "fs";
29380
- import chalk202 from "chalk";
29619
+ import chalk204 from "chalk";
29381
29620
  var REFACTOR_YML_PATH2 = "refactor.yml";
29382
29621
  function ignore2(file) {
29383
29622
  if (!fs34.existsSync(file)) {
29384
- console.error(chalk202.red(`Error: File does not exist: ${file}`));
29623
+ console.error(chalk204.red(`Error: File does not exist: ${file}`));
29385
29624
  process.exit(1);
29386
29625
  }
29387
29626
  const content = fs34.readFileSync(file, "utf8");
@@ -29397,7 +29636,7 @@ function ignore2(file) {
29397
29636
  fs34.writeFileSync(REFACTOR_YML_PATH2, entry);
29398
29637
  }
29399
29638
  console.log(
29400
- chalk202.green(
29639
+ chalk204.green(
29401
29640
  `Added ${file} to refactor ignore list (max ${maxLines} lines)`
29402
29641
  )
29403
29642
  );
@@ -29406,12 +29645,12 @@ function ignore2(file) {
29406
29645
  // src/commands/refactor/rename/index.ts
29407
29646
  import fs37 from "fs";
29408
29647
  import path59 from "path";
29409
- import chalk205 from "chalk";
29648
+ import chalk207 from "chalk";
29410
29649
 
29411
29650
  // src/commands/refactor/rename/applyRename.ts
29412
29651
  import fs36 from "fs";
29413
29652
  import path56 from "path";
29414
- import chalk203 from "chalk";
29653
+ import chalk205 from "chalk";
29415
29654
 
29416
29655
  // src/commands/refactor/restructure/computeRewrites/index.ts
29417
29656
  import path55 from "path";
@@ -29516,13 +29755,13 @@ function applyRename(rewrites, sourcePath, destPath, cwd) {
29516
29755
  const updatedContents = applyRewrites(rewrites);
29517
29756
  for (const [file, content] of updatedContents) {
29518
29757
  fs36.writeFileSync(file, content, "utf8");
29519
- console.log(chalk203.cyan(` Updated imports in ${path56.relative(cwd, file)}`));
29758
+ console.log(chalk205.cyan(` Updated imports in ${path56.relative(cwd, file)}`));
29520
29759
  }
29521
29760
  const destDir = path56.dirname(destPath);
29522
29761
  if (!fs36.existsSync(destDir)) fs36.mkdirSync(destDir, { recursive: true });
29523
29762
  fs36.renameSync(sourcePath, destPath);
29524
29763
  console.log(
29525
- chalk203.white(
29764
+ chalk205.white(
29526
29765
  ` Moved ${path56.relative(cwd, sourcePath)} \u2192 ${path56.relative(cwd, destPath)}`
29527
29766
  )
29528
29767
  );
@@ -29609,16 +29848,16 @@ function computeRenameRewrites(sourcePath, destPath) {
29609
29848
 
29610
29849
  // src/commands/refactor/rename/printRenamePreview.ts
29611
29850
  import path58 from "path";
29612
- import chalk204 from "chalk";
29851
+ import chalk206 from "chalk";
29613
29852
  function printRenamePreview(rewrites, cwd) {
29614
29853
  for (const rewrite of rewrites) {
29615
29854
  console.log(
29616
- chalk204.dim(
29855
+ chalk206.dim(
29617
29856
  ` ${path58.relative(cwd, rewrite.file)}: ${rewrite.oldSpecifier} \u2192 ${rewrite.newSpecifier}`
29618
29857
  )
29619
29858
  );
29620
29859
  }
29621
- console.log(chalk204.dim("Dry run. Use --apply to execute."));
29860
+ console.log(chalk206.dim("Dry run. Use --apply to execute."));
29622
29861
  }
29623
29862
 
29624
29863
  // src/commands/refactor/rename/index.ts
@@ -29629,20 +29868,20 @@ async function rename(source, destination, options2 = {}) {
29629
29868
  const relSource = path59.relative(cwd, sourcePath);
29630
29869
  const relDest = path59.relative(cwd, destPath);
29631
29870
  if (!fs37.existsSync(sourcePath)) {
29632
- console.log(chalk205.red(`File not found: ${source}`));
29871
+ console.log(chalk207.red(`File not found: ${source}`));
29633
29872
  process.exit(1);
29634
29873
  }
29635
29874
  if (destPath !== sourcePath && fs37.existsSync(destPath)) {
29636
- console.log(chalk205.red(`Destination already exists: ${destination}`));
29875
+ console.log(chalk207.red(`Destination already exists: ${destination}`));
29637
29876
  process.exit(1);
29638
29877
  }
29639
- console.log(chalk205.bold(`Rename: ${relSource} \u2192 ${relDest}`));
29640
- console.log(chalk205.dim("Loading project..."));
29641
- console.log(chalk205.dim("Scanning imports across the project..."));
29878
+ console.log(chalk207.bold(`Rename: ${relSource} \u2192 ${relDest}`));
29879
+ console.log(chalk207.dim("Loading project..."));
29880
+ console.log(chalk207.dim("Scanning imports across the project..."));
29642
29881
  const rewrites = computeRenameRewrites(sourcePath, destPath);
29643
29882
  const affectedFiles = new Set(rewrites.map((r) => r.file)).size;
29644
29883
  console.log(
29645
- chalk205.dim(
29884
+ chalk207.dim(
29646
29885
  `${rewrites.length} import path(s) to update across ${affectedFiles} file(s)`
29647
29886
  )
29648
29887
  );
@@ -29651,11 +29890,11 @@ async function rename(source, destination, options2 = {}) {
29651
29890
  return;
29652
29891
  }
29653
29892
  applyRename(rewrites, sourcePath, destPath, cwd);
29654
- console.log(chalk205.green("Done"));
29893
+ console.log(chalk207.green("Done"));
29655
29894
  }
29656
29895
 
29657
29896
  // src/commands/refactor/renameSymbol/index.ts
29658
- import chalk206 from "chalk";
29897
+ import chalk208 from "chalk";
29659
29898
 
29660
29899
  // src/commands/refactor/renameSymbol/findSymbol.ts
29661
29900
  import { SyntaxKind as SyntaxKind15 } from "ts-morph";
@@ -29701,33 +29940,33 @@ async function renameSymbol(file, oldName, newName, options2 = {}) {
29701
29940
  const { project, sourceFile } = loadProjectFile(file);
29702
29941
  const symbol = findSymbol(sourceFile, oldName);
29703
29942
  if (!symbol) {
29704
- console.log(chalk206.red(`Symbol "${oldName}" not found in ${file}`));
29943
+ console.log(chalk208.red(`Symbol "${oldName}" not found in ${file}`));
29705
29944
  process.exit(1);
29706
29945
  }
29707
29946
  const grouped = groupReferences(symbol, cwd);
29708
29947
  const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
29709
29948
  console.log(
29710
- chalk206.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
29949
+ chalk208.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
29711
29950
  `)
29712
29951
  );
29713
29952
  for (const [refFile, lines2] of grouped) {
29714
29953
  console.log(
29715
- ` ${chalk206.dim(refFile)}: lines ${chalk206.cyan(lines2.join(", "))}`
29954
+ ` ${chalk208.dim(refFile)}: lines ${chalk208.cyan(lines2.join(", "))}`
29716
29955
  );
29717
29956
  }
29718
29957
  if (options2.apply) {
29719
29958
  symbol.rename(newName);
29720
29959
  await project.save();
29721
- console.log(chalk206.green(`
29960
+ console.log(chalk208.green(`
29722
29961
  Renamed ${oldName} \u2192 ${newName}`));
29723
29962
  } else {
29724
- console.log(chalk206.dim("\nDry run. Use --apply to execute."));
29963
+ console.log(chalk208.dim("\nDry run. Use --apply to execute."));
29725
29964
  }
29726
29965
  }
29727
29966
 
29728
29967
  // src/commands/refactor/restructure/index.ts
29729
29968
  import path67 from "path";
29730
- import chalk209 from "chalk";
29969
+ import chalk211 from "chalk";
29731
29970
 
29732
29971
  // src/commands/refactor/restructure/clusterDirectories.ts
29733
29972
  import path61 from "path";
@@ -29806,50 +30045,50 @@ function clusterFiles(graph) {
29806
30045
 
29807
30046
  // src/commands/refactor/restructure/displayPlan.ts
29808
30047
  import path63 from "path";
29809
- import chalk207 from "chalk";
30048
+ import chalk209 from "chalk";
29810
30049
  function relPath(filePath) {
29811
30050
  return path63.relative(process.cwd(), filePath);
29812
30051
  }
29813
30052
  function displayMoves(plan2) {
29814
30053
  if (plan2.moves.length === 0) return;
29815
- console.log(chalk207.bold("\nFile moves:"));
30054
+ console.log(chalk209.bold("\nFile moves:"));
29816
30055
  for (const move2 of plan2.moves) {
29817
30056
  console.log(
29818
- ` ${chalk207.red(relPath(move2.from))} \u2192 ${chalk207.green(relPath(move2.to))}`
30057
+ ` ${chalk209.red(relPath(move2.from))} \u2192 ${chalk209.green(relPath(move2.to))}`
29819
30058
  );
29820
- console.log(chalk207.dim(` ${move2.reason}`));
30059
+ console.log(chalk209.dim(` ${move2.reason}`));
29821
30060
  }
29822
30061
  }
29823
30062
  function displayRewrites(rewrites) {
29824
30063
  if (rewrites.length === 0) return;
29825
30064
  const affectedFiles = new Set(rewrites.map((r) => r.file));
29826
- console.log(chalk207.bold(`
30065
+ console.log(chalk209.bold(`
29827
30066
  Import rewrites (${affectedFiles.size} files):`));
29828
30067
  for (const file of affectedFiles) {
29829
- console.log(` ${chalk207.cyan(relPath(file))}:`);
30068
+ console.log(` ${chalk209.cyan(relPath(file))}:`);
29830
30069
  for (const { oldSpecifier, newSpecifier } of rewrites.filter(
29831
30070
  (r) => r.file === file
29832
30071
  )) {
29833
30072
  console.log(
29834
- ` ${chalk207.red(`"${oldSpecifier}"`)} \u2192 ${chalk207.green(`"${newSpecifier}"`)}`
30073
+ ` ${chalk209.red(`"${oldSpecifier}"`)} \u2192 ${chalk209.green(`"${newSpecifier}"`)}`
29835
30074
  );
29836
30075
  }
29837
30076
  }
29838
30077
  }
29839
30078
  function displayPlan2(plan2) {
29840
30079
  if (plan2.warnings.length > 0) {
29841
- console.log(chalk207.yellow("\nWarnings:"));
29842
- for (const w of plan2.warnings) console.log(chalk207.yellow(` ${w}`));
30080
+ console.log(chalk209.yellow("\nWarnings:"));
30081
+ for (const w of plan2.warnings) console.log(chalk209.yellow(` ${w}`));
29843
30082
  }
29844
30083
  if (plan2.newDirectories.length > 0) {
29845
- console.log(chalk207.bold("\nNew directories:"));
30084
+ console.log(chalk209.bold("\nNew directories:"));
29846
30085
  for (const dir of plan2.newDirectories)
29847
- console.log(chalk207.green(` ${dir}/`));
30086
+ console.log(chalk209.green(` ${dir}/`));
29848
30087
  }
29849
30088
  displayMoves(plan2);
29850
30089
  displayRewrites(plan2.rewrites);
29851
30090
  console.log(
29852
- chalk207.dim(
30091
+ chalk209.dim(
29853
30092
  `
29854
30093
  Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports rewritten`
29855
30094
  )
@@ -29859,18 +30098,18 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
29859
30098
  // src/commands/refactor/restructure/executePlan.ts
29860
30099
  import fs38 from "fs";
29861
30100
  import path64 from "path";
29862
- import chalk208 from "chalk";
30101
+ import chalk210 from "chalk";
29863
30102
  function executePlan(plan2) {
29864
30103
  const updatedContents = applyRewrites(plan2.rewrites);
29865
30104
  for (const [file, content] of updatedContents) {
29866
30105
  fs38.writeFileSync(file, content, "utf8");
29867
30106
  console.log(
29868
- chalk208.cyan(` Rewrote imports in ${path64.relative(process.cwd(), file)}`)
30107
+ chalk210.cyan(` Rewrote imports in ${path64.relative(process.cwd(), file)}`)
29869
30108
  );
29870
30109
  }
29871
30110
  for (const dir of plan2.newDirectories) {
29872
30111
  fs38.mkdirSync(dir, { recursive: true });
29873
- console.log(chalk208.green(` Created ${path64.relative(process.cwd(), dir)}/`));
30112
+ console.log(chalk210.green(` Created ${path64.relative(process.cwd(), dir)}/`));
29874
30113
  }
29875
30114
  for (const move2 of plan2.moves) {
29876
30115
  const targetDir = path64.dirname(move2.to);
@@ -29879,7 +30118,7 @@ function executePlan(plan2) {
29879
30118
  }
29880
30119
  fs38.renameSync(move2.from, move2.to);
29881
30120
  console.log(
29882
- chalk208.white(
30121
+ chalk210.white(
29883
30122
  ` Moved ${path64.relative(process.cwd(), move2.from)} \u2192 ${path64.relative(process.cwd(), move2.to)}`
29884
30123
  )
29885
30124
  );
@@ -29894,7 +30133,7 @@ function removeEmptyDirectories(dirs) {
29894
30133
  if (entries.length === 0) {
29895
30134
  fs38.rmdirSync(dir);
29896
30135
  console.log(
29897
- chalk208.dim(
30136
+ chalk210.dim(
29898
30137
  ` Removed empty directory ${path64.relative(process.cwd(), dir)}`
29899
30138
  )
29900
30139
  );
@@ -30027,22 +30266,22 @@ async function restructure(pattern2, options2 = {}) {
30027
30266
  const targetPattern = pattern2 ?? "src";
30028
30267
  const files = findSourceFiles2(targetPattern);
30029
30268
  if (files.length === 0) {
30030
- console.log(chalk209.yellow("No files found matching pattern"));
30269
+ console.log(chalk211.yellow("No files found matching pattern"));
30031
30270
  return;
30032
30271
  }
30033
30272
  const tsConfigPath = findTsConfig(path67.resolve(files[0]));
30034
30273
  const plan2 = buildPlan3(files, tsConfigPath);
30035
30274
  if (plan2.moves.length === 0) {
30036
- console.log(chalk209.green("No restructuring needed"));
30275
+ console.log(chalk211.green("No restructuring needed"));
30037
30276
  return;
30038
30277
  }
30039
30278
  displayPlan2(plan2);
30040
30279
  if (options2.apply) {
30041
- console.log(chalk209.bold("\nApplying changes..."));
30280
+ console.log(chalk211.bold("\nApplying changes..."));
30042
30281
  executePlan(plan2);
30043
- console.log(chalk209.green("\nRestructuring complete"));
30282
+ console.log(chalk211.green("\nRestructuring complete"));
30044
30283
  } else {
30045
- console.log(chalk209.dim("\nDry run. Use --apply to execute."));
30284
+ console.log(chalk211.dim("\nDry run. Use --apply to execute."));
30046
30285
  }
30047
30286
  }
30048
30287
 
@@ -30095,6 +30334,293 @@ async function checkoutOnlySession(number) {
30095
30334
  await done2;
30096
30335
  }
30097
30336
 
30337
+ // src/commands/review/highLevel/runHighLevelReview.ts
30338
+ import chalk213 from "chalk";
30339
+
30340
+ // src/commands/review/fetchPrDiffInfo.ts
30341
+ import { execSync as execSync57 } from "child_process";
30342
+ function getCurrentBranch3() {
30343
+ return execSync57("git rev-parse --abbrev-ref HEAD", {
30344
+ encoding: "utf8"
30345
+ }).trim();
30346
+ }
30347
+ function fetchPrDiffInfo() {
30348
+ const { org, repo } = getRepoInfo();
30349
+ const branch2 = getCurrentBranch3();
30350
+ const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
30351
+ const raw = execSync57(
30352
+ `gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
30353
+ {
30354
+ encoding: "utf8",
30355
+ stdio: ["ignore", "pipe", "pipe"]
30356
+ }
30357
+ );
30358
+ const parsed = JSON.parse(raw);
30359
+ const pr = parsed[0];
30360
+ if (!pr) {
30361
+ console.error(
30362
+ `Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
30363
+ );
30364
+ process.exit(1);
30365
+ }
30366
+ return {
30367
+ prNumber: pr.number,
30368
+ baseRef: pr.baseRefName,
30369
+ baseSha: pr.baseRefOid,
30370
+ headRef: pr.headRefName,
30371
+ headSha: pr.headRefOid
30372
+ };
30373
+ }
30374
+ function fetchPrChangedFiles(prNumber) {
30375
+ const { org, repo } = getRepoInfo();
30376
+ const out = execSync57(
30377
+ `gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
30378
+ {
30379
+ encoding: "utf8",
30380
+ maxBuffer: 64 * 1024 * 1024
30381
+ }
30382
+ );
30383
+ return out.trim().split("\n").filter(Boolean);
30384
+ }
30385
+
30386
+ // src/commands/review/highLevel/checkDescriptionLinksIssue.ts
30387
+ var ISSUE_URL_PATTERN = /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+/g;
30388
+ var CROSS_REPO_PATTERN = /\b[\w.-]+\/[\w.-]+#\d+/g;
30389
+ var SAME_REPO_PATTERN2 = /(?<![\w/#-])#\d+\b/g;
30390
+ function firstMatch(body, pattern2) {
30391
+ return body.match(pattern2)?.[0] ?? null;
30392
+ }
30393
+ function checkDescriptionLinksIssue(body) {
30394
+ const reference = firstMatch(body, ISSUE_URL_PATTERN) ?? firstMatch(body, CROSS_REPO_PATTERN) ?? firstMatch(body, SAME_REPO_PATTERN2);
30395
+ if (!reference)
30396
+ return {
30397
+ status: "fail",
30398
+ reason: "no GitHub issue reference (`#123`, `owner/repo#123`, or an issue URL) in the description"
30399
+ };
30400
+ return { status: "pass", reason: `links ${reference}` };
30401
+ }
30402
+
30403
+ // src/commands/review/highLevel/checkDescriptionSections.ts
30404
+ function sectionContent(body, heading2) {
30405
+ const section2 = parsePrBody(body).find(
30406
+ (candidate) => candidate.heading.trim().toLowerCase() === heading2
30407
+ );
30408
+ return section2 ? section2.content.trim() : null;
30409
+ }
30410
+ function describe5(heading2, content) {
30411
+ if (content === null) return `no \`## ${heading2}\` section`;
30412
+ if (content === "") return `\`## ${heading2}\` is empty`;
30413
+ return null;
30414
+ }
30415
+ function checkDescriptionSections(body) {
30416
+ const problems = [
30417
+ describe5("What", sectionContent(body, "what")),
30418
+ describe5("Why", sectionContent(body, "why"))
30419
+ ].filter((problem) => problem !== null);
30420
+ if (problems.length > 0)
30421
+ return { status: "fail", reason: problems.join("; ") };
30422
+ const how = sectionContent(body, "how");
30423
+ const optional = how ? ", plus an optional `## How`" : "";
30424
+ return {
30425
+ status: "pass",
30426
+ reason: `\`## What\` and \`## Why\` are both present${optional}`
30427
+ };
30428
+ }
30429
+
30430
+ // src/commands/review/highLevel/checkDescriptionWordCap.ts
30431
+ function checkDescriptionWordCap(body, cap2) {
30432
+ const { prose, code } = countReadingWords(body);
30433
+ const words = prose + code;
30434
+ if (words > cap2)
30435
+ return {
30436
+ status: "fail",
30437
+ reason: `${words} words, ${words - cap2} over the ${cap2}-word cap`
30438
+ };
30439
+ return { status: "pass", reason: `${words} of ${cap2} words` };
30440
+ }
30441
+
30442
+ // src/commands/review/highLevel/checkUiEvidence.ts
30443
+ import { minimatch as minimatch7 } from "minimatch";
30444
+ var EVIDENCE_PATTERNS = [
30445
+ /!\[[^\]]*\]\([^)]+\)/g,
30446
+ /<(?:img|video)\b[^>]*>/gi,
30447
+ /https?:\/\/github\.com\/user-attachments\/assets\/\S+/g,
30448
+ /https?:\/\/\S+\.(?:png|jpe?g|gif|webp|avif|svg|mp4|mov|webm)\b/gi
30449
+ ];
30450
+ function countEvidence(body) {
30451
+ return EVIDENCE_PATTERNS.reduce(
30452
+ (total, pattern2) => total + (body.match(pattern2)?.length ?? 0),
30453
+ 0
30454
+ );
30455
+ }
30456
+ function plural(count8, noun) {
30457
+ return `${count8} ${noun}${count8 === 1 ? "" : "s"}`;
30458
+ }
30459
+ function checkUiEvidence(body, changedFiles2, uiPaths) {
30460
+ if (uiPaths.length === 0)
30461
+ return {
30462
+ status: "pass",
30463
+ reason: "review.highLevel.uiPaths is unset, so no UI evidence is required"
30464
+ };
30465
+ const touched = changedFiles2.filter(
30466
+ (file) => uiPaths.some((glob) => minimatch7(file, glob))
30467
+ );
30468
+ if (touched.length === 0)
30469
+ return {
30470
+ status: "pass",
30471
+ reason: "no changed file matches review.highLevel.uiPaths"
30472
+ };
30473
+ const evidence = countEvidence(body);
30474
+ if (evidence === 0)
30475
+ return {
30476
+ status: "fail",
30477
+ reason: `${plural(touched.length, "UI file")} changed (${touched[0]}) but no screenshot or video in the description`
30478
+ };
30479
+ const shown = evidence === 1 ? "1 screenshot or video" : `${evidence} screenshots or videos`;
30480
+ return {
30481
+ status: "pass",
30482
+ reason: `${shown} for ${plural(touched.length, "changed UI file")}`
30483
+ };
30484
+ }
30485
+
30486
+ // src/commands/review/highLevel/highLevelChecklist.ts
30487
+ var highLevelChecklist = [
30488
+ {
30489
+ id: "description-what-why",
30490
+ kind: "deterministic",
30491
+ title: "Description has a What and a Why",
30492
+ backing: "The PR body's `## What` and `## Why` sections"
30493
+ },
30494
+ {
30495
+ id: "description-word-cap",
30496
+ kind: "deterministic",
30497
+ title: "Description is under the word cap",
30498
+ backing: "The PR body's word count against review.highLevel.descriptionWordCap"
30499
+ },
30500
+ {
30501
+ id: "description-links-issue",
30502
+ kind: "deterministic",
30503
+ title: "Description links the GitHub issue the PR resolves",
30504
+ backing: "GitHub issue references in the PR body"
30505
+ },
30506
+ {
30507
+ id: "ui-evidence",
30508
+ kind: "deterministic",
30509
+ title: "UI changes are evidenced by a screenshot or video",
30510
+ backing: "Images and videos in the PR body, when a changed file matches review.highLevel.uiPaths"
30511
+ },
30512
+ {
30513
+ id: "structure-sensible",
30514
+ kind: "manual",
30515
+ title: "The structure of the change is sensible",
30516
+ backing: "The changed-file tree with add/delete/modify counts and per-file GitHub diff links"
30517
+ },
30518
+ {
30519
+ id: "critical-diffs-correct",
30520
+ kind: "manual",
30521
+ title: "The critical-file diffs are correct",
30522
+ backing: "Full diffs of files matching review.highLevel.criticalPaths"
30523
+ },
30524
+ {
30525
+ id: "backend-pr-linked",
30526
+ kind: "manual",
30527
+ title: "The backend PR is linked, if the change needs backend work",
30528
+ backing: "The PR body"
30529
+ }
30530
+ ];
30531
+
30532
+ // src/commands/review/highLevel/evaluateHighLevelChecks.ts
30533
+ var evaluators = {
30534
+ "description-what-why": (subject) => checkDescriptionSections(subject.body),
30535
+ "description-word-cap": (subject) => checkDescriptionWordCap(subject.body, subject.config.descriptionWordCap),
30536
+ "description-links-issue": (subject) => checkDescriptionLinksIssue(subject.body),
30537
+ "ui-evidence": (subject) => checkUiEvidence(subject.body, subject.changedFiles, subject.config.uiPaths)
30538
+ };
30539
+ function evaluateHighLevelChecks(subject) {
30540
+ return highLevelChecklist.map((check2) => {
30541
+ if (check2.kind === "manual")
30542
+ return { ...check2, status: "manual", reason: check2.backing };
30543
+ return { ...check2, ...evaluators[check2.id](subject) };
30544
+ });
30545
+ }
30546
+
30547
+ // src/commands/review/highLevel/formatHighLevelChecklist.ts
30548
+ import chalk212 from "chalk";
30549
+ var MARKERS = {
30550
+ pass: () => chalk212.green("\u2714"),
30551
+ fail: () => chalk212.red("\u2718"),
30552
+ manual: () => chalk212.yellow("\u25A1")
30553
+ };
30554
+ function formatCheck(check2) {
30555
+ const title = check2.status === "fail" ? chalk212.red(check2.title) : chalk212.bold(check2.title);
30556
+ return ` ${MARKERS[check2.status]()} ${title}
30557
+ ${chalk212.dim(check2.reason)}`;
30558
+ }
30559
+ function formatGroup(heading2, checks2) {
30560
+ if (checks2.length === 0) return [];
30561
+ return ["", chalk212.bold.underline(heading2), ...checks2.map(formatCheck)];
30562
+ }
30563
+ function formatHighLevelChecklist(checks2) {
30564
+ const deterministic = checks2.filter(
30565
+ (check2) => check2.kind === "deterministic"
30566
+ );
30567
+ const failed2 = deterministic.filter(
30568
+ (check2) => check2.status === "fail"
30569
+ ).length;
30570
+ const summary = failed2 === 0 ? chalk212.green(`All ${deterministic.length} deterministic checks pass`) : chalk212.red(
30571
+ `${failed2} of ${deterministic.length} deterministic checks fail`
30572
+ );
30573
+ return [
30574
+ ...formatGroup("Deterministic", deterministic),
30575
+ ...formatGroup(
30576
+ "Manual",
30577
+ checks2.filter((check2) => check2.kind === "manual")
30578
+ ),
30579
+ "",
30580
+ summary
30581
+ ].join("\n");
30582
+ }
30583
+
30584
+ // src/commands/review/highLevel/resolveHighLevelConfig.ts
30585
+ var DEFAULT_DESCRIPTION_WORD_CAP = 300;
30586
+ function resolveHighLevelConfig(config) {
30587
+ const highLevel = config.review?.highLevel;
30588
+ return {
30589
+ criticalPaths: highLevel?.criticalPaths ?? [],
30590
+ uiPaths: highLevel?.uiPaths ?? [],
30591
+ descriptionWordCap: highLevel?.descriptionWordCap ?? DEFAULT_DESCRIPTION_WORD_CAP
30592
+ };
30593
+ }
30594
+
30595
+ // src/commands/review/highLevel/runHighLevelReview.ts
30596
+ function resolvePrNumber2(number) {
30597
+ if (number === void 0) return getCurrentPrNumber();
30598
+ const parsed = Number(number);
30599
+ if (!Number.isInteger(parsed) || parsed <= 0) {
30600
+ console.error(`Error: \`${number}\` is not a pull request number.`);
30601
+ process.exit(1);
30602
+ }
30603
+ return parsed;
30604
+ }
30605
+ function runHighLevelReview(number) {
30606
+ const prNumber = resolvePrNumber2(number);
30607
+ const { org, repo } = getRepoInfo();
30608
+ const config = resolveHighLevelConfig(loadConfig());
30609
+ const body = fetchPrBody(prNumber, { org, repo });
30610
+ const changedFiles2 = fetchPrChangedFiles(prNumber);
30611
+ const checks2 = evaluateHighLevelChecks({ body, changedFiles: changedFiles2, config });
30612
+ console.log(
30613
+ chalk213.bold(`High-level review of ${org}/${repo}#${prNumber}`),
30614
+ chalk213.dim(`\xB7 ${changedFiles2.length} changed files`)
30615
+ );
30616
+ console.log(formatHighLevelChecklist(checks2));
30617
+ console.log(
30618
+ chalk213.dim(
30619
+ "\nManual items are for the reviewer to judge; see docs/high-level-review.md."
30620
+ )
30621
+ );
30622
+ }
30623
+
30098
30624
  // src/commands/review/annotateDiffWithLineNumbers.ts
30099
30625
  var FILE_HEADER2 = /^\+\+\+ (?:b\/)?(.+)$/;
30100
30626
  var HUNK_HEADER2 = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
@@ -30228,9 +30754,9 @@ function buildReviewPaths(repoRoot2, key) {
30228
30754
  }
30229
30755
 
30230
30756
  // src/commands/review/fetchExistingComments.ts
30231
- import { execSync as execSync56 } from "child_process";
30757
+ import { execSync as execSync58 } from "child_process";
30232
30758
  function fetchRawComments(org, repo, prNumber) {
30233
- const out = execSync56(
30759
+ const out = execSync58(
30234
30760
  `gh api --paginate repos/${org}/${repo}/pulls/${prNumber}/comments`,
30235
30761
  { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
30236
30762
  );
@@ -30261,14 +30787,14 @@ function fetchExistingComments() {
30261
30787
  }
30262
30788
 
30263
30789
  // src/commands/review/gatherContext.ts
30264
- import { execSync as execSync59 } from "child_process";
30790
+ import { execSync as execSync60 } from "child_process";
30265
30791
 
30266
30792
  // src/commands/review/fetchPrDiff.ts
30267
- import { execSync as execSync57 } from "child_process";
30793
+ import { execSync as execSync59 } from "child_process";
30268
30794
  function fetchPrDiff(prNumber, baseSha, headSha) {
30269
30795
  const { org, repo } = getRepoInfo();
30270
30796
  try {
30271
- return execSync57(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
30797
+ return execSync59(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
30272
30798
  encoding: "utf8",
30273
30799
  maxBuffer: 256 * 1024 * 1024,
30274
30800
  stdio: ["ignore", "pipe", "pipe"]
@@ -30283,68 +30809,22 @@ function isDiffTooLarge(error) {
30283
30809
  }
30284
30810
  function fetchDiffViaGit(baseSha, headSha) {
30285
30811
  try {
30286
- execSync57(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
30812
+ execSync59(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
30287
30813
  } catch {
30288
30814
  }
30289
- return execSync57(`git diff ${baseSha}...${headSha}`, {
30815
+ return execSync59(`git diff ${baseSha}...${headSha}`, {
30290
30816
  encoding: "utf8",
30291
30817
  maxBuffer: 256 * 1024 * 1024
30292
30818
  });
30293
30819
  }
30294
30820
 
30295
- // src/commands/review/fetchPrDiffInfo.ts
30296
- import { execSync as execSync58 } from "child_process";
30297
- function getCurrentBranch3() {
30298
- return execSync58("git rev-parse --abbrev-ref HEAD", {
30299
- encoding: "utf8"
30300
- }).trim();
30301
- }
30302
- function fetchPrDiffInfo() {
30303
- const { org, repo } = getRepoInfo();
30304
- const branch2 = getCurrentBranch3();
30305
- const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
30306
- const raw = execSync58(
30307
- `gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
30308
- {
30309
- encoding: "utf8",
30310
- stdio: ["ignore", "pipe", "pipe"]
30311
- }
30312
- );
30313
- const parsed = JSON.parse(raw);
30314
- const pr = parsed[0];
30315
- if (!pr) {
30316
- console.error(
30317
- `Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
30318
- );
30319
- process.exit(1);
30320
- }
30321
- return {
30322
- prNumber: pr.number,
30323
- baseRef: pr.baseRefName,
30324
- baseSha: pr.baseRefOid,
30325
- headRef: pr.headRefName,
30326
- headSha: pr.headRefOid
30327
- };
30328
- }
30329
- function fetchPrChangedFiles(prNumber) {
30330
- const { org, repo } = getRepoInfo();
30331
- const out = execSync58(
30332
- `gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
30333
- {
30334
- encoding: "utf8",
30335
- maxBuffer: 64 * 1024 * 1024
30336
- }
30337
- );
30338
- return out.trim().split("\n").filter(Boolean);
30339
- }
30340
-
30341
30821
  // src/commands/review/gatherContext.ts
30342
30822
  function gatherContext() {
30343
- const branch2 = execSync59("git rev-parse --abbrev-ref HEAD", {
30823
+ const branch2 = execSync60("git rev-parse --abbrev-ref HEAD", {
30344
30824
  encoding: "utf8"
30345
30825
  }).trim();
30346
- const sha = execSync59("git rev-parse HEAD", { encoding: "utf8" }).trim();
30347
- const shortSha = execSync59("git rev-parse --short=7 HEAD", {
30826
+ const sha = execSync60("git rev-parse HEAD", { encoding: "utf8" }).trim();
30827
+ const shortSha = execSync60("git rev-parse --short=7 HEAD", {
30348
30828
  encoding: "utf8"
30349
30829
  }).trim();
30350
30830
  const prInfo = fetchPrDiffInfo();
@@ -30703,18 +31183,18 @@ function partitionFindingsByDiff(findings, index3) {
30703
31183
  }
30704
31184
 
30705
31185
  // src/commands/review/warnOutOfDiff.ts
30706
- import chalk210 from "chalk";
31186
+ import chalk214 from "chalk";
30707
31187
  function warnOutOfDiff(outOfDiff) {
30708
31188
  if (outOfDiff.length === 0) return;
30709
31189
  console.warn(
30710
- chalk210.yellow(
31190
+ chalk214.yellow(
30711
31191
  `Moved ${outOfDiff.length} finding(s) whose lines fall outside the PR diff into the review body (GitHub cannot anchor a comment on these):`
30712
31192
  )
30713
31193
  );
30714
31194
  for (const finding of outOfDiff) {
30715
31195
  const range = finding.startLine !== void 0 ? `${finding.startLine}-${finding.line}` : `${finding.line}`;
30716
31196
  console.warn(
30717
- ` ${chalk210.yellow("\xB7")} ${finding.title} ${chalk210.dim(
31197
+ ` ${chalk214.yellow("\xB7")} ${finding.title} ${chalk214.dim(
30718
31198
  `(${finding.file}:${range})`
30719
31199
  )}`
30720
31200
  );
@@ -30738,18 +31218,18 @@ function selectInDiffFindings(lineBound, prDiff) {
30738
31218
  }
30739
31219
 
30740
31220
  // src/commands/review/warnUnlocated.ts
30741
- import chalk211 from "chalk";
31221
+ import chalk215 from "chalk";
30742
31222
  function warnUnlocated(unlocated) {
30743
31223
  if (unlocated.length === 0) return;
30744
31224
  console.warn(
30745
- chalk211.yellow(
31225
+ chalk215.yellow(
30746
31226
  `Moved ${unlocated.length} finding(s) without a parseable file:line into the review body:`
30747
31227
  )
30748
31228
  );
30749
31229
  for (const finding of unlocated) {
30750
- const where = finding.location || chalk211.dim("missing");
31230
+ const where = finding.location || chalk215.dim("missing");
30751
31231
  console.warn(
30752
- ` ${chalk211.yellow("\xB7")} ${finding.title} ${chalk211.dim(`(${where})`)}`
31232
+ ` ${chalk215.yellow("\xB7")} ${finding.title} ${chalk215.dim(`(${where})`)}`
30753
31233
  );
30754
31234
  }
30755
31235
  }
@@ -31339,9 +31819,9 @@ import { writeFileSync as writeFileSync39 } from "fs";
31339
31819
  // src/commands/review/finaliseReviewerSpinner.ts
31340
31820
  var SUMMARY_MAX_LEN = 80;
31341
31821
  function summariseStderr(stderr) {
31342
- const firstLine2 = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
31343
- if (!firstLine2) return "";
31344
- const trimmed = firstLine2.trim();
31822
+ const firstLine3 = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
31823
+ if (!firstLine3) return "";
31824
+ const trimmed = firstLine3.trim();
31345
31825
  return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
31346
31826
  }
31347
31827
  function finaliseReviewerSpinner(spinner, outcome) {
@@ -32049,14 +32529,8 @@ async function reviewPr(repoRoot2, options2) {
32049
32529
  console.log(`Done. Review folder: ${paths.reviewDir}`);
32050
32530
  }
32051
32531
 
32052
- // src/commands/review/review.ts
32053
- function resolveRepoRoot() {
32054
- const repoRoot2 = findRepoRoot(process.cwd());
32055
- if (repoRoot2) return repoRoot2;
32056
- console.error("Error: not inside a git repository.");
32057
- process.exit(1);
32058
- }
32059
- function validateOptions(options2) {
32532
+ // src/commands/review/validateReviewOptions.ts
32533
+ function validateReviewOptions(options2) {
32060
32534
  if (options2.apply && options2.refine) {
32061
32535
  console.error("Error: --apply cannot be combined with --refine.");
32062
32536
  process.exit(1);
@@ -32068,6 +32542,7 @@ function validateOptions(options2) {
32068
32542
  process.exit(1);
32069
32543
  }
32070
32544
  validateCheckoutOnly(options2);
32545
+ validateHighLevel(options2);
32071
32546
  }
32072
32547
  function validateCheckoutOnly(options2) {
32073
32548
  if (!options2.checkoutOnly) return;
@@ -32082,13 +32557,31 @@ function validateCheckoutOnly(options2) {
32082
32557
  process.exit(1);
32083
32558
  }
32084
32559
  }
32560
+ function validateHighLevel(options2) {
32561
+ if (!options2.highLevel) return;
32562
+ if (options2.refine || options2.apply || options2.backlog || options2.submit || options2.checkoutOnly) {
32563
+ console.error(
32564
+ "Error: --high-level cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only."
32565
+ );
32566
+ process.exit(1);
32567
+ }
32568
+ }
32569
+
32570
+ // src/commands/review/review.ts
32571
+ function resolveRepoRoot() {
32572
+ const repoRoot2 = findRepoRoot(process.cwd());
32573
+ if (repoRoot2) return repoRoot2;
32574
+ console.error("Error: not inside a git repository.");
32575
+ process.exit(1);
32576
+ }
32085
32577
  async function review(options2 = {}) {
32086
- validateOptions(options2);
32578
+ validateReviewOptions(options2);
32087
32579
  startReviewLog();
32088
32580
  const invokedIn = resolveRepoRoot();
32089
32581
  if (options2.checkoutOnly && options2.number)
32090
32582
  return checkoutOnlySession(options2.number);
32091
32583
  emitActivity({ kind: "command", name: "review" });
32584
+ if (options2.highLevel) return runHighLevelReview(options2.number);
32092
32585
  if (!options2.number) return reviewPr(invokedIn, options2);
32093
32586
  await checkoutPr(options2.number);
32094
32587
  return reviewPr(resolveRepoRoot(), options2);
@@ -32122,6 +32615,9 @@ function registerReview(program2) {
32122
32615
  ).option(
32123
32616
  "--checkout-only",
32124
32617
  "Check the PR out and start an idle interactive Claude session in the checkout tree instead of reviewing; requires a PR number and cannot be combined with --refine, --apply, --backlog or --submit"
32618
+ ).option(
32619
+ "--high-level",
32620
+ "Skip the LLM review; evaluate the high-level review checklist against the PR description and changed files and print each item as pass, fail or manual. Nothing is posted to GitHub; cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only"
32125
32621
  ).option(
32126
32622
  "--address-comments",
32127
32623
  "After posting and submitting the review, start an Address Comments session (assist review-pr-comments <n>) for the PR; no-op when nothing was posted or the review was not submitted, and only inside an assist session"
@@ -32144,7 +32640,7 @@ function registerReview(program2) {
32144
32640
  // src/commands/rules/addRule.ts
32145
32641
  import { existsSync as existsSync68, readFileSync as readFileSync52, writeFileSync as writeFileSync41 } from "fs";
32146
32642
  import path72 from "path";
32147
- import chalk212 from "chalk";
32643
+ import chalk216 from "chalk";
32148
32644
 
32149
32645
  // src/commands/rules/insertRuleBullet.ts
32150
32646
  function ruleBullet({ code, title, text: text18 }) {
@@ -32280,7 +32776,7 @@ function read2(file) {
32280
32776
  function addRule(text18, options2) {
32281
32777
  const rule = text18.trim();
32282
32778
  if (rule === "") {
32283
- console.error(chalk212.red("Rule text is required"));
32779
+ console.error(chalk216.red("Rule text is required"));
32284
32780
  process.exitCode = 1;
32285
32781
  return;
32286
32782
  }
@@ -32298,13 +32794,13 @@ function addRule(text18, options2) {
32298
32794
  );
32299
32795
  updateScopedRulesIndex(root);
32300
32796
  console.log(
32301
- `Added ${chalk212.cyan(code)} to ${path72.relative(process.cwd(), target) || target}`
32797
+ `Added ${chalk216.cyan(code)} to ${path72.relative(process.cwd(), target) || target}`
32302
32798
  );
32303
32799
  }
32304
32800
 
32305
32801
  // src/commands/rules/indexRules.ts
32306
32802
  import path73 from "path";
32307
- import chalk213 from "chalk";
32803
+ import chalk217 from "chalk";
32308
32804
  function indexRules() {
32309
32805
  const startDir = scopeDirectory(process.cwd());
32310
32806
  const root = findRepoRoot(startDir) ?? startDir;
@@ -32312,24 +32808,24 @@ function indexRules() {
32312
32808
  const rootFile = path73.relative(process.cwd(), path73.join(root, "CLAUDE.md"));
32313
32809
  if (directories.length === 0) {
32314
32810
  console.log(
32315
- chalk213.gray(`No directories carry their own \`## Rules\` under ${root}`)
32811
+ chalk217.gray(`No directories carry their own \`## Rules\` under ${root}`)
32316
32812
  );
32317
32813
  return;
32318
32814
  }
32319
32815
  console.log(`Recorded in ${rootFile || "CLAUDE.md"}`);
32320
32816
  for (const directory of directories)
32321
- console.log(` ${chalk213.cyan(directory)}`);
32817
+ console.log(` ${chalk217.cyan(directory)}`);
32322
32818
  }
32323
32819
 
32324
32820
  // src/commands/rules/listRules.ts
32325
32821
  import path74 from "path";
32326
- import chalk214 from "chalk";
32822
+ import chalk218 from "chalk";
32327
32823
  function listRules(target, options2 = {}) {
32328
32824
  const resolved = path74.resolve(target ?? process.cwd());
32329
32825
  const rules3 = readScopedRules(resolved);
32330
32826
  if (rules3.length === 0) {
32331
32827
  const label2 = path74.relative(process.cwd(), resolved) || ".";
32332
- console.log(chalk214.gray(`No rules in scope for ${label2}`));
32828
+ console.log(chalk218.gray(`No rules in scope for ${label2}`));
32333
32829
  return;
32334
32830
  }
32335
32831
  const base = findRepoRoot(scopeDirectory(resolved));
@@ -32339,14 +32835,14 @@ function listRules(target, options2 = {}) {
32339
32835
  if (rule.source !== shown) {
32340
32836
  shown = rule.source;
32341
32837
  console.log(
32342
- chalk214.dim(base ? path74.relative(base, rule.source) : rule.source)
32838
+ chalk218.dim(base ? path74.relative(base, rule.source) : rule.source)
32343
32839
  );
32344
32840
  }
32345
32841
  console.log(
32346
- ` ${chalk214.cyan(rule.code.padEnd(width))} ${rule.title ?? rule.text}`
32842
+ ` ${chalk218.cyan(rule.code.padEnd(width))} ${rule.title ?? rule.text}`
32347
32843
  );
32348
32844
  if (options2.full && rule.title)
32349
- console.log(` ${" ".repeat(width)} ${chalk214.dim(rule.text)}`);
32845
+ console.log(` ${" ".repeat(width)} ${chalk218.dim(rule.text)}`);
32350
32846
  }
32351
32847
  }
32352
32848
 
@@ -32375,7 +32871,7 @@ function registerRules(program2) {
32375
32871
  }
32376
32872
 
32377
32873
  // src/commands/seq/seqAuth.ts
32378
- import chalk216 from "chalk";
32874
+ import chalk220 from "chalk";
32379
32875
 
32380
32876
  // src/commands/seq/loadConnections.ts
32381
32877
  function loadConnections2() {
@@ -32404,10 +32900,10 @@ function setDefaultConnection(name) {
32404
32900
  }
32405
32901
 
32406
32902
  // src/shared/assertUniqueName.ts
32407
- import chalk215 from "chalk";
32903
+ import chalk219 from "chalk";
32408
32904
  function assertUniqueName(existingNames, name) {
32409
32905
  if (existingNames.includes(name)) {
32410
- console.error(chalk215.red(`Connection "${name}" already exists.`));
32906
+ console.error(chalk219.red(`Connection "${name}" already exists.`));
32411
32907
  process.exit(1);
32412
32908
  }
32413
32909
  }
@@ -32425,16 +32921,16 @@ async function promptConnection2(existingNames) {
32425
32921
  var seqAuth = createConnectionAuth({
32426
32922
  load: loadConnections2,
32427
32923
  save: saveConnections2,
32428
- format: (c) => `${chalk216.bold(c.name)} ${c.url}`,
32924
+ format: (c) => `${chalk220.bold(c.name)} ${c.url}`,
32429
32925
  promptNew: promptConnection2,
32430
32926
  onFirst: (c) => setDefaultConnection(c.name)
32431
32927
  });
32432
32928
 
32433
32929
  // src/commands/seq/seqQuery.ts
32434
- import chalk220 from "chalk";
32930
+ import chalk224 from "chalk";
32435
32931
 
32436
32932
  // src/commands/seq/fetchSeq.ts
32437
- import chalk217 from "chalk";
32933
+ import chalk221 from "chalk";
32438
32934
  async function fetchSeq(conn, path91, params) {
32439
32935
  const url = `${conn.url}${path91}?${params}`;
32440
32936
  const response = await fetch(url, {
@@ -32445,7 +32941,7 @@ async function fetchSeq(conn, path91, params) {
32445
32941
  });
32446
32942
  if (!response.ok) {
32447
32943
  const body = await response.text();
32448
- console.error(chalk217.red(`Seq returned ${response.status}: ${body}`));
32944
+ console.error(chalk221.red(`Seq returned ${response.status}: ${body}`));
32449
32945
  process.exit(1);
32450
32946
  }
32451
32947
  return response;
@@ -32504,23 +33000,23 @@ async function fetchSeqEvents(conn, params) {
32504
33000
  }
32505
33001
 
32506
33002
  // src/commands/seq/formatEvent.ts
32507
- import chalk218 from "chalk";
33003
+ import chalk222 from "chalk";
32508
33004
  function levelColor(level) {
32509
33005
  switch (level) {
32510
33006
  case "Fatal":
32511
- return chalk218.bgRed.white;
33007
+ return chalk222.bgRed.white;
32512
33008
  case "Error":
32513
- return chalk218.red;
33009
+ return chalk222.red;
32514
33010
  case "Warning":
32515
- return chalk218.yellow;
33011
+ return chalk222.yellow;
32516
33012
  case "Information":
32517
- return chalk218.cyan;
33013
+ return chalk222.cyan;
32518
33014
  case "Debug":
32519
- return chalk218.gray;
33015
+ return chalk222.gray;
32520
33016
  case "Verbose":
32521
- return chalk218.dim;
33017
+ return chalk222.dim;
32522
33018
  default:
32523
- return chalk218.white;
33019
+ return chalk222.white;
32524
33020
  }
32525
33021
  }
32526
33022
  function levelAbbrev(level) {
@@ -32561,12 +33057,12 @@ function formatTimestamp(iso) {
32561
33057
  function formatEvent(event) {
32562
33058
  const color = levelColor(event.Level);
32563
33059
  const abbrev = levelAbbrev(event.Level);
32564
- const ts8 = chalk218.dim(formatTimestamp(event.Timestamp));
33060
+ const ts8 = chalk222.dim(formatTimestamp(event.Timestamp));
32565
33061
  const msg = renderMessage(event);
32566
33062
  const lines2 = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
32567
33063
  if (event.Exception) {
32568
33064
  for (const line of event.Exception.split("\n")) {
32569
- lines2.push(chalk218.red(` ${line}`));
33065
+ lines2.push(chalk222.red(` ${line}`));
32570
33066
  }
32571
33067
  }
32572
33068
  return lines2.join("\n");
@@ -32599,11 +33095,11 @@ function rejectTimestampFilter(filter) {
32599
33095
  }
32600
33096
 
32601
33097
  // src/shared/resolveNamedConnection.ts
32602
- import chalk219 from "chalk";
33098
+ import chalk223 from "chalk";
32603
33099
  function resolveNamedConnection(connections, requested, defaultName, kind, authCommand) {
32604
33100
  if (connections.length === 0) {
32605
33101
  console.error(
32606
- chalk219.red(
33102
+ chalk223.red(
32607
33103
  `No ${kind} connections configured. Run '${authCommand}' first.`
32608
33104
  )
32609
33105
  );
@@ -32612,7 +33108,7 @@ function resolveNamedConnection(connections, requested, defaultName, kind, authC
32612
33108
  const target = requested ?? defaultName ?? connections[0].name;
32613
33109
  const connection = connections.find((c) => c.name === target);
32614
33110
  if (!connection) {
32615
- console.error(chalk219.red(`${kind} connection "${target}" not found.`));
33111
+ console.error(chalk223.red(`${kind} connection "${target}" not found.`));
32616
33112
  process.exit(1);
32617
33113
  }
32618
33114
  return connection;
@@ -32641,7 +33137,7 @@ async function seqQuery(filter, options2) {
32641
33137
  new URLSearchParams({ filter, count: String(count8) })
32642
33138
  );
32643
33139
  if (events.length === 0) {
32644
- console.log(chalk220.yellow("No events found."));
33140
+ console.log(chalk224.yellow("No events found."));
32645
33141
  return;
32646
33142
  }
32647
33143
  if (options2.json) {
@@ -32652,11 +33148,11 @@ async function seqQuery(filter, options2) {
32652
33148
  for (const event of chronological) {
32653
33149
  console.log(formatEvent(event));
32654
33150
  }
32655
- console.log(chalk220.dim(`
33151
+ console.log(chalk224.dim(`
32656
33152
  ${events.length} events`));
32657
33153
  if (events.length >= count8) {
32658
33154
  console.log(
32659
- chalk220.yellow(
33155
+ chalk224.yellow(
32660
33156
  `Results limited to ${count8}. Use --count to retrieve more.`
32661
33157
  )
32662
33158
  );
@@ -32664,10 +33160,10 @@ ${events.length} events`));
32664
33160
  }
32665
33161
 
32666
33162
  // src/shared/setNamedDefaultConnection.ts
32667
- import chalk221 from "chalk";
33163
+ import chalk225 from "chalk";
32668
33164
  function setNamedDefaultConnection(connections, name, setDefault, kind) {
32669
33165
  if (!connections.find((c) => c.name === name)) {
32670
- console.error(chalk221.red(`Connection "${name}" not found.`));
33166
+ console.error(chalk225.red(`Connection "${name}" not found.`));
32671
33167
  process.exit(1);
32672
33168
  }
32673
33169
  setDefault(name);
@@ -32816,7 +33312,7 @@ function registerSlack(program2) {
32816
33312
  }
32817
33313
 
32818
33314
  // src/commands/sql/sqlAuth.ts
32819
- import chalk223 from "chalk";
33315
+ import chalk227 from "chalk";
32820
33316
 
32821
33317
  // src/commands/sql/loadConnections.ts
32822
33318
  function loadConnections3() {
@@ -32845,7 +33341,7 @@ function setDefaultConnection2(name) {
32845
33341
  }
32846
33342
 
32847
33343
  // src/commands/sql/promptConnection.ts
32848
- import chalk222 from "chalk";
33344
+ import chalk226 from "chalk";
32849
33345
  async function promptConnection3(existingNames) {
32850
33346
  const name = await promptInput("name", "Connection name:", "default");
32851
33347
  assertUniqueName(existingNames, name);
@@ -32853,7 +33349,7 @@ async function promptConnection3(existingNames) {
32853
33349
  const portStr = await promptInput("port", "Port:", "1433");
32854
33350
  const port = Number.parseInt(portStr, 10);
32855
33351
  if (!Number.isFinite(port)) {
32856
- console.error(chalk222.red(`Invalid port "${portStr}".`));
33352
+ console.error(chalk226.red(`Invalid port "${portStr}".`));
32857
33353
  process.exit(1);
32858
33354
  }
32859
33355
  const user = await promptInput("user", "User:");
@@ -32866,13 +33362,13 @@ async function promptConnection3(existingNames) {
32866
33362
  var sqlAuth = createConnectionAuth({
32867
33363
  load: loadConnections3,
32868
33364
  save: saveConnections3,
32869
- format: (c) => `${chalk223.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
33365
+ format: (c) => `${chalk227.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
32870
33366
  promptNew: promptConnection3,
32871
33367
  onFirst: (c) => setDefaultConnection2(c.name)
32872
33368
  });
32873
33369
 
32874
33370
  // src/commands/sql/printTable.ts
32875
- import chalk224 from "chalk";
33371
+ import chalk228 from "chalk";
32876
33372
  function formatCell(value) {
32877
33373
  if (value === null || value === void 0) return "";
32878
33374
  if (value instanceof Date) return value.toISOString();
@@ -32881,7 +33377,7 @@ function formatCell(value) {
32881
33377
  }
32882
33378
  function printTable(rows) {
32883
33379
  if (rows.length === 0) {
32884
- console.log(chalk224.yellow("(no rows)"));
33380
+ console.log(chalk228.yellow("(no rows)"));
32885
33381
  return;
32886
33382
  }
32887
33383
  const columns = Object.keys(rows[0]);
@@ -32889,13 +33385,13 @@ function printTable(rows) {
32889
33385
  (col) => Math.max(col.length, ...rows.map((r) => formatCell(r[col]).length))
32890
33386
  );
32891
33387
  const header = columns.map((c, i) => c.padEnd(widths[i])).join(" ");
32892
- console.log(chalk224.dim(header));
32893
- console.log(chalk224.dim("-".repeat(header.length)));
33388
+ console.log(chalk228.dim(header));
33389
+ console.log(chalk228.dim("-".repeat(header.length)));
32894
33390
  for (const row of rows) {
32895
33391
  const line = columns.map((c, i) => formatCell(row[c]).padEnd(widths[i])).join(" ");
32896
33392
  console.log(line);
32897
33393
  }
32898
- console.log(chalk224.dim(`
33394
+ console.log(chalk228.dim(`
32899
33395
  ${rows.length} row${rows.length === 1 ? "" : "s"}`));
32900
33396
  }
32901
33397
 
@@ -32955,7 +33451,7 @@ async function sqlColumns(table, connectionName) {
32955
33451
  }
32956
33452
 
32957
33453
  // src/commands/sql/sqlMutate.ts
32958
- import chalk225 from "chalk";
33454
+ import chalk229 from "chalk";
32959
33455
 
32960
33456
  // src/commands/sql/isMutation.ts
32961
33457
  var MUTATION_KEYWORDS = [
@@ -32989,7 +33485,7 @@ function isMutation(sql29) {
32989
33485
  async function sqlMutate(query, connectionName) {
32990
33486
  if (!isMutation(query)) {
32991
33487
  console.error(
32992
- chalk225.red(
33488
+ chalk229.red(
32993
33489
  "assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
32994
33490
  )
32995
33491
  );
@@ -32999,18 +33495,18 @@ async function sqlMutate(query, connectionName) {
32999
33495
  const pool = await sqlConnect(conn);
33000
33496
  try {
33001
33497
  const result = await pool.request().query(query);
33002
- console.log(chalk225.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
33498
+ console.log(chalk229.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
33003
33499
  } finally {
33004
33500
  await pool.close();
33005
33501
  }
33006
33502
  }
33007
33503
 
33008
33504
  // src/commands/sql/sqlQuery.ts
33009
- import chalk226 from "chalk";
33505
+ import chalk230 from "chalk";
33010
33506
  async function sqlQuery(query, connectionName) {
33011
33507
  if (isMutation(query)) {
33012
33508
  console.error(
33013
- chalk226.red(
33509
+ chalk230.red(
33014
33510
  "assist sql query refuses mutating statements. Use `assist sql mutate` instead."
33015
33511
  )
33016
33512
  );
@@ -33025,7 +33521,7 @@ async function sqlQuery(query, connectionName) {
33025
33521
  printTable(rows);
33026
33522
  } else {
33027
33523
  console.log(
33028
- chalk226.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
33524
+ chalk230.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
33029
33525
  );
33030
33526
  }
33031
33527
  } finally {
@@ -33399,7 +33895,7 @@ function syncPi(claudeDir, options2) {
33399
33895
  // src/commands/sync/syncSettings.ts
33400
33896
  import * as fs48 from "fs";
33401
33897
  import * as path84 from "path";
33402
- import chalk227 from "chalk";
33898
+ import chalk231 from "chalk";
33403
33899
  async function syncSettings(claudeDir, targetBase, options2) {
33404
33900
  const source = path84.join(claudeDir, "settings.json");
33405
33901
  const target = path84.join(targetBase, "settings.json");
@@ -33418,7 +33914,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
33418
33914
  if (mergedContent !== normalizedTarget) {
33419
33915
  if (!options2?.yes) {
33420
33916
  console.log(
33421
- chalk227.yellow(
33917
+ chalk231.yellow(
33422
33918
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
33423
33919
  )
33424
33920
  );
@@ -33426,7 +33922,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
33426
33922
  printDiff(targetContent, mergedContent);
33427
33923
  printAutoConfirmHint();
33428
33924
  const confirm = await promptConfirm(
33429
- chalk227.red("Overwrite existing settings.json?"),
33925
+ chalk231.red("Overwrite existing settings.json?"),
33430
33926
  false
33431
33927
  );
33432
33928
  if (!confirm) {
@@ -34295,7 +34791,7 @@ import { mkdirSync as mkdirSync31 } from "fs";
34295
34791
  import { join as join93 } from "path";
34296
34792
 
34297
34793
  // src/commands/voice/checkLockFile.ts
34298
- import { execSync as execSync60 } from "child_process";
34794
+ import { execSync as execSync61 } from "child_process";
34299
34795
  import { existsSync as existsSync78, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
34300
34796
  import { join as join92 } from "path";
34301
34797
  function isProcessAlive2(pid) {
@@ -34324,7 +34820,7 @@ function bootstrapVenv() {
34324
34820
  if (existsSync78(getVenvPython())) return;
34325
34821
  console.log("Setting up Python environment...");
34326
34822
  const pythonDir = getPythonDir();
34327
- execSync60(
34823
+ execSync61(
34328
34824
  `uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
34329
34825
  {
34330
34826
  stdio: "inherit",
@@ -34945,11 +35441,11 @@ function runCommandToCompletion(command, args, env, cwd, quiet) {
34945
35441
  }
34946
35442
 
34947
35443
  // src/commands/run/runPreCommands.ts
34948
- import { execSync as execSync61 } from "child_process";
35444
+ import { execSync as execSync62 } from "child_process";
34949
35445
  function runPreCommands(pre, cwd) {
34950
35446
  for (const cmd of pre) {
34951
35447
  try {
34952
- execSync61(cmd, { stdio: "inherit", cwd });
35448
+ execSync62(cmd, { stdio: "inherit", cwd });
34953
35449
  } catch (error) {
34954
35450
  const code = error && typeof error === "object" && "status" in error ? error.status : 1;
34955
35451
  process.exit(code);
@@ -35168,7 +35664,7 @@ function registerWatch(program2) {
35168
35664
 
35169
35665
  // src/commands/roam/auth.ts
35170
35666
  import { randomBytes } from "crypto";
35171
- import chalk228 from "chalk";
35667
+ import chalk232 from "chalk";
35172
35668
 
35173
35669
  // src/commands/roam/waitForCallback.ts
35174
35670
  import { createServer as createServer3 } from "http";
@@ -35299,13 +35795,13 @@ async function auth() {
35299
35795
  saveGlobalConfig(config);
35300
35796
  const state = randomBytes(16).toString("hex");
35301
35797
  console.log(
35302
- chalk228.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
35798
+ chalk232.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
35303
35799
  );
35304
- console.log(chalk228.white("http://localhost:14523/callback\n"));
35305
- console.log(chalk228.blue("Opening browser for authorization..."));
35306
- console.log(chalk228.dim("Waiting for authorization callback..."));
35800
+ console.log(chalk232.white("http://localhost:14523/callback\n"));
35801
+ console.log(chalk232.blue("Opening browser for authorization..."));
35802
+ console.log(chalk232.dim("Waiting for authorization callback..."));
35307
35803
  const { code, redirectUri } = await authorizeInBrowser(clientId, state);
35308
- console.log(chalk228.dim("Exchanging code for tokens..."));
35804
+ console.log(chalk232.dim("Exchanging code for tokens..."));
35309
35805
  const tokens = await exchangeToken({
35310
35806
  code,
35311
35807
  clientId,
@@ -35321,7 +35817,7 @@ async function auth() {
35321
35817
  };
35322
35818
  saveGlobalConfig(config);
35323
35819
  console.log(
35324
- chalk228.green("Roam credentials and tokens saved to ~/.assist.yml")
35820
+ chalk232.green("Roam credentials and tokens saved to ~/.assist.yml")
35325
35821
  );
35326
35822
  }
35327
35823
 
@@ -35676,11 +36172,11 @@ function registerRun(program2) {
35676
36172
  }
35677
36173
 
35678
36174
  // src/commands/screenshot/index.ts
35679
- import { execSync as execSync62 } from "child_process";
36175
+ import { execSync as execSync63 } from "child_process";
35680
36176
  import { existsSync as existsSync85, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
35681
36177
  import { tmpdir as tmpdir9 } from "os";
35682
36178
  import { join as join100, resolve as resolve21 } from "path";
35683
- import chalk229 from "chalk";
36179
+ import chalk233 from "chalk";
35684
36180
 
35685
36181
  // src/commands/screenshot/captureWindowPs1.ts
35686
36182
  var captureWindowPs1 = `
@@ -35819,7 +36315,7 @@ function runPowerShellScript(processName, outputPath) {
35819
36315
  const scriptPath = join100(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
35820
36316
  writeFileSync51(scriptPath, captureWindowPs1, "utf8");
35821
36317
  try {
35822
- execSync62(
36318
+ execSync63(
35823
36319
  `powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
35824
36320
  { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
35825
36321
  );
@@ -35831,13 +36327,13 @@ function screenshot(processName) {
35831
36327
  const config = loadConfig();
35832
36328
  const outputDir = resolve21(config.screenshot.outputDir);
35833
36329
  const outputPath = buildOutputPath(outputDir, processName);
35834
- console.log(chalk229.gray(`Capturing window for process "${processName}" ...`));
36330
+ console.log(chalk233.gray(`Capturing window for process "${processName}" ...`));
35835
36331
  try {
35836
36332
  runPowerShellScript(processName, outputPath);
35837
- console.log(chalk229.green(`Screenshot saved: ${outputPath}`));
36333
+ console.log(chalk233.green(`Screenshot saved: ${outputPath}`));
35838
36334
  } catch (error) {
35839
36335
  const msg = error instanceof Error ? error.message : String(error);
35840
- console.error(chalk229.red(`Failed to capture screenshot: ${msg}`));
36336
+ console.error(chalk233.red(`Failed to capture screenshot: ${msg}`));
35841
36337
  process.exit(1);
35842
36338
  }
35843
36339
  }
@@ -41799,7 +42295,7 @@ async function renameSession(title) {
41799
42295
 
41800
42296
  // src/commands/sessions/summarise/index.ts
41801
42297
  import * as fs57 from "fs";
41802
- import chalk230 from "chalk";
42298
+ import chalk234 from "chalk";
41803
42299
 
41804
42300
  // src/commands/sessions/summarise/shared.ts
41805
42301
  import * as fs56 from "fs";
@@ -41858,22 +42354,22 @@ ${firstMessage}`);
41858
42354
  async function summarise2(options2) {
41859
42355
  const files = await discoverSessionFiles();
41860
42356
  if (files.length === 0) {
41861
- console.log(chalk230.yellow("No sessions found."));
42357
+ console.log(chalk234.yellow("No sessions found."));
41862
42358
  return;
41863
42359
  }
41864
42360
  const toProcess = selectCandidates(files, options2);
41865
42361
  if (toProcess.length === 0) {
41866
- console.log(chalk230.green("All sessions already summarised."));
42362
+ console.log(chalk234.green("All sessions already summarised."));
41867
42363
  return;
41868
42364
  }
41869
42365
  console.log(
41870
- chalk230.cyan(
42366
+ chalk234.cyan(
41871
42367
  `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
41872
42368
  )
41873
42369
  );
41874
42370
  const { succeeded, failed: failed2 } = processSessions(toProcess);
41875
42371
  console.log(
41876
- chalk230.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk230.yellow(`, ${failed2} skipped`) : "")
42372
+ chalk234.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk234.yellow(`, ${failed2} skipped`) : "")
41877
42373
  );
41878
42374
  }
41879
42375
  function selectCandidates(files, options2) {
@@ -41893,16 +42389,16 @@ function processSessions(files) {
41893
42389
  let failed2 = 0;
41894
42390
  for (let i = 0; i < files.length; i++) {
41895
42391
  const file = files[i];
41896
- process.stdout.write(chalk230.dim(` [${i + 1}/${files.length}] `));
42392
+ process.stdout.write(chalk234.dim(` [${i + 1}/${files.length}] `));
41897
42393
  const summary = summariseSession(file);
41898
42394
  if (summary) {
41899
42395
  writeSummary(file, summary);
41900
42396
  succeeded++;
41901
- process.stdout.write(`${chalk230.green("\u2713")} ${summary}
42397
+ process.stdout.write(`${chalk234.green("\u2713")} ${summary}
41902
42398
  `);
41903
42399
  } else {
41904
42400
  failed2++;
41905
- process.stdout.write(` ${chalk230.yellow("skip")}
42401
+ process.stdout.write(` ${chalk234.yellow("skip")}
41906
42402
  `);
41907
42403
  }
41908
42404
  }
@@ -41929,7 +42425,7 @@ function registerSessions(program2) {
41929
42425
  }
41930
42426
 
41931
42427
  // src/commands/statusLine.ts
41932
- import chalk232 from "chalk";
42428
+ import chalk236 from "chalk";
41933
42429
 
41934
42430
  // src/shared/contextLevel.ts
41935
42431
  function contextLevel(pct) {
@@ -41939,7 +42435,7 @@ function contextLevel(pct) {
41939
42435
  }
41940
42436
 
41941
42437
  // src/commands/buildLimitsSegment.ts
41942
- import chalk231 from "chalk";
42438
+ import chalk235 from "chalk";
41943
42439
 
41944
42440
  // src/shared/rateLimitLevel.ts
41945
42441
  var FIVE_HOUR_SECONDS = 5 * 3600;
@@ -41977,9 +42473,9 @@ function rateLimitLevel(pct, resetsAt, windowSeconds, now) {
41977
42473
 
41978
42474
  // src/commands/buildLimitsSegment.ts
41979
42475
  var LEVEL_COLOR = {
41980
- ok: chalk231.green,
41981
- warn: chalk231.yellow,
41982
- over: chalk231.red
42476
+ ok: chalk235.green,
42477
+ warn: chalk235.yellow,
42478
+ over: chalk235.red
41983
42479
  };
41984
42480
  function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel, now) {
41985
42481
  const level = rateLimitLevel(pct, resetsAt, windowSeconds, now);
@@ -42075,7 +42571,7 @@ async function relayUsage(claudeSessionId, transcriptPath2, usedPct) {
42075
42571
  }
42076
42572
 
42077
42573
  // src/commands/statusLine.ts
42078
- chalk232.level = 3;
42574
+ chalk236.level = 3;
42079
42575
  function formatNumber(num) {
42080
42576
  return num.toLocaleString("en-US");
42081
42577
  }
@@ -42083,9 +42579,9 @@ function colorizePercent(pct) {
42083
42579
  const label2 = `${Math.round(pct)}%`;
42084
42580
  switch (contextLevel(pct)) {
42085
42581
  case "red":
42086
- return chalk232.red(label2);
42582
+ return chalk236.red(label2);
42087
42583
  case "yellow":
42088
- return chalk232.yellow(label2);
42584
+ return chalk236.yellow(label2);
42089
42585
  default:
42090
42586
  return label2;
42091
42587
  }
@@ -42098,7 +42594,7 @@ async function statusLine() {
42098
42594
  const usedPct = data.context_window.used_percentage ?? 0;
42099
42595
  const dir = data.workspace?.current_dir ?? data.cwd;
42100
42596
  const branch2 = dir ? readGitBranch(toGitCwd(dir)) : null;
42101
- const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${chalk232.cyan(branch2)} | ` : "";
42597
+ const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${chalk236.cyan(branch2)} | ` : "";
42102
42598
  console.log(
42103
42599
  `${branchSegment}${model} | Tokens - ${formatNumber(totalIn)} \u2191 : ${formatNumber(totalOut)} \u2193 | Context - ${colorizePercent(usedPct)}${buildLimitsSegment(data.rate_limits)}`
42104
42600
  );
@@ -42111,7 +42607,7 @@ async function statusLine() {
42111
42607
  }
42112
42608
 
42113
42609
  // src/commands/update.ts
42114
- import { execSync as execSync63 } from "child_process";
42610
+ import { execSync as execSync64 } from "child_process";
42115
42611
  import * as path90 from "path";
42116
42612
 
42117
42613
  // src/commands/restartDaemonAfterUpdate.ts
@@ -42135,7 +42631,7 @@ function isGlobalNpmInstall(dir) {
42135
42631
  if (resolved.split(path90.sep).includes("node_modules")) {
42136
42632
  return true;
42137
42633
  }
42138
- const globalPrefix = execSync63("npm prefix -g", { stdio: "pipe" }).toString().trim();
42634
+ const globalPrefix = execSync64("npm prefix -g", { stdio: "pipe" }).toString().trim();
42139
42635
  return resolved.toLowerCase().startsWith(path90.resolve(globalPrefix).toLowerCase());
42140
42636
  } catch {
42141
42637
  return false;
@@ -42146,18 +42642,18 @@ async function update2() {
42146
42642
  console.log(`Assist is installed at: ${installDir}`);
42147
42643
  if (isGitRepo(installDir)) {
42148
42644
  console.log("Detected git repo installation, pulling latest...");
42149
- execSync63("git pull", { cwd: installDir, stdio: "inherit" });
42645
+ execSync64("git pull", { cwd: installDir, stdio: "inherit" });
42150
42646
  console.log("Installing dependencies...");
42151
- execSync63("npm i", { cwd: installDir, stdio: "inherit" });
42647
+ execSync64("npm i", { cwd: installDir, stdio: "inherit" });
42152
42648
  console.log("Building...");
42153
- execSync63("npm run build", { cwd: installDir, stdio: "inherit" });
42649
+ execSync64("npm run build", { cwd: installDir, stdio: "inherit" });
42154
42650
  console.log("Syncing commands...");
42155
- execSync63("assist sync", { stdio: "inherit" });
42651
+ execSync64("assist sync", { stdio: "inherit" });
42156
42652
  } else if (isGlobalNpmInstall(installDir)) {
42157
42653
  console.log("Detected global npm installation, updating...");
42158
- execSync63("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
42654
+ execSync64("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
42159
42655
  console.log("Syncing commands...");
42160
- execSync63("assist sync", { stdio: "inherit" });
42656
+ execSync64("assist sync", { stdio: "inherit" });
42161
42657
  } else {
42162
42658
  console.error(
42163
42659
  "Could not determine installation method. Expected a git repo or global npm install."
@@ -42168,10 +42664,10 @@ async function update2() {
42168
42664
  }
42169
42665
 
42170
42666
  // src/reportCliError.ts
42171
- import chalk233 from "chalk";
42667
+ import chalk237 from "chalk";
42172
42668
  function reportCliError(error) {
42173
42669
  if (error instanceof InvalidItemIdError || error instanceof AmbiguousRepoConfigError || error instanceof UnknownRepoConfigError || error instanceof MissingRunCwdError || error instanceof MiroExtractError) {
42174
- console.error(chalk233.red(error.message));
42670
+ console.error(chalk237.red(error.message));
42175
42671
  } else {
42176
42672
  console.error(error);
42177
42673
  }