@staff0rd/assist 0.238.1 → 0.239.1

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/README.md CHANGED
@@ -86,6 +86,7 @@ After installation, the `assist` command will be available globally. You can als
86
86
  - `assist commit <message>` - Commit staged changes with validation
87
87
  - `assist commit <message> [files...]` - Stage files and create a git commit with validation
88
88
  - `assist prs` - List pull requests for the current repository
89
+ - `assist prs create --title <title> --body <body>` - Create a pull request via `gh pr create`
89
90
  - `assist prs list-comments` - List all comments on the current branch's pull request
90
91
  - `assist prs fixed <comment-id> <sha>` - Reply with commit link and resolve thread
91
92
  - `assist prs wontfix <comment-id> <reason>` - Reply with reason and resolve thread
@@ -161,7 +162,7 @@ The first backlog command in a repository that still has a local `.assist/backlo
161
162
  - `assist devlog repos` - Show which github.com/staff0rd repos are missing devlog entries
162
163
  - `assist devlog skip <date>` - Add a date to the skip list
163
164
  - `assist devlog version` - Show current repo name and version info
164
- - `assist cli-hook` - PreToolUse hook for auto-approving CLI commands (reads from `allowed.cli-reads` and `allowed.cli-writes`, also auto-approves read-only `gh api` calls). Supports compound commands (`|`, `&&`, `||`, `;`) by checking each sub-command independently
165
+ - `assist cli-hook` - PreToolUse hook for auto-approving CLI commands (reads from `allowed.cli-reads` and `allowed.cli-writes`, also auto-approves read-only `gh api` calls). Supports compound commands (`|`, `&&`, `||`, `;`) by checking each sub-command independently.
165
166
  - `assist cli-hook add <cli>` - Discover a CLI's commands and auto-permit read-only ones
166
167
  - `assist cli-hook check <command> [--tool <tool>]` - Check whether a command would be auto-approved by `cli-hook` (tool defaults to `Bash`)
167
168
  - `assist cli-hook deny` - List all deny rules
@@ -4,6 +4,8 @@ description: Raise a PR with a concise description
4
4
 
5
5
  Raise a pull request for the current branch. Use a concise description with no headers. Do not reference Claude or any AI assistance in the title or body.
6
6
 
7
- Use `gh pr create` to create the PR. Keep the title short and the body to a brief plain-text summary of the changes. Wrap symbols, file paths, function names, class names, variable names, config keys, CLI commands, and flag names in backticks.
7
+ Use `assist prs create --title <title> --body <body>` to create the PR — do not use `gh pr create` directly. Keep the title short and the body to a brief plain-text summary of the changes. Wrap symbols, file paths, function names, class names, variable names, config keys, CLI commands, and flag names in backticks.
8
8
 
9
9
  Write the description in terms of behaviour and user-facing impact: what the change does, what's different for someone using it, and why. Keep technical detail to a minimum — do not walk through the implementation approach step by step, and do not restate what is already obvious from the diff or changelog (which files changed, which functions were added). The reviewer can read the code; the description should tell them what to expect from the change, not narrate how it was built.
10
+
11
+ Before creating the PR, the user must see the full proposed title and body — do not assume they can see your reasoning or earlier tool output. Write the complete title and body verbatim in your visible reply, then use the AskUserQuestion tool to ask whether to create the PR, putting the full title and body in the approve option's `preview` field so the dialog itself displays them. This confirmation is mandatory in every permission mode, including auto-accept and bypass-permissions — never run `assist prs create` until the user has explicitly approved the title and body through AskUserQuestion. If the user requests changes, revise and confirm again before creating.
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.238.1",
9
+ version: "0.239.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -6806,6 +6806,27 @@ function matchesConfigDeny(command) {
6806
6806
  );
6807
6807
  }
6808
6808
 
6809
+ // src/commands/cliHook/findBuiltinDeny.ts
6810
+ var BUILTIN_DENIES = [
6811
+ {
6812
+ pattern: "gh pr create",
6813
+ message: "Do not run 'gh pr create' directly. Use 'assist prs create --title <title> --body <body>' instead \u2014 it validates the title and body before delegating to gh pr create. Before running it, get explicit approval via the AskUserQuestion tool, regardless of permission mode, with the full proposed title and body in the approve option's preview field so the user actually sees them."
6814
+ }
6815
+ ];
6816
+ function matchesBuiltinDeny(part) {
6817
+ return BUILTIN_DENIES.find(
6818
+ (rule) => part === rule.pattern || part.startsWith(`${rule.pattern} `)
6819
+ );
6820
+ }
6821
+ function findBuiltinDeny(parts) {
6822
+ const rule = parts.map(matchesBuiltinDeny).find(Boolean);
6823
+ if (!rule) return void 0;
6824
+ return {
6825
+ permissionDecision: "deny",
6826
+ permissionDecisionReason: rule.message
6827
+ };
6828
+ }
6829
+
6809
6830
  // src/commands/cliHook/resolvePermission.ts
6810
6831
  var SUBCOMMAND_READS = [
6811
6832
  {
@@ -6832,6 +6853,8 @@ function findSubcommandAdvice(parts) {
6832
6853
  return void 0;
6833
6854
  }
6834
6855
  function findDeny(toolName, parts) {
6856
+ const builtinDeny = findBuiltinDeny(parts);
6857
+ if (builtinDeny) return builtinDeny;
6835
6858
  for (const part of parts) {
6836
6859
  const configDeny = matchesConfigDeny(part);
6837
6860
  if (configDeny) {
@@ -6914,23 +6937,24 @@ async function cliHook() {
6914
6937
  }
6915
6938
 
6916
6939
  // src/commands/cliHook/cliHookCheck.ts
6940
+ function reportDeny(toolName, parts) {
6941
+ const denied = findDeny(toolName, parts);
6942
+ if (!denied) return false;
6943
+ console.log(`denied: ${denied.permissionDecisionReason}`);
6944
+ process.exitCode = 1;
6945
+ return true;
6946
+ }
6917
6947
  function cliHookCheck(command, toolName = "Bash") {
6918
6948
  const trimmed = command.trim();
6919
6949
  const result = splitCompound(trimmed);
6920
6950
  if (!result.ok) {
6951
+ if (reportDeny(toolName, [trimmed])) return;
6921
6952
  console.log(`not approved (${result.error})`);
6922
6953
  process.exitCode = 1;
6923
6954
  return;
6924
6955
  }
6925
6956
  const parts = result.parts;
6926
- for (const part of parts) {
6927
- const configDeny = matchesConfigDeny(part);
6928
- if (configDeny) {
6929
- console.log(`denied: ${configDeny.message}`);
6930
- process.exitCode = 1;
6931
- return;
6932
- }
6933
- }
6957
+ if (reportDeny(toolName, parts)) return;
6934
6958
  const reasons = [];
6935
6959
  for (const part of parts) {
6936
6960
  const reason = isApprovedRead(part, toolName);
@@ -10285,11 +10309,73 @@ function comment2(path52, line, body, startLine) {
10285
10309
  }
10286
10310
  }
10287
10311
 
10312
+ // src/commands/prs/create.ts
10313
+ import { execSync as execSync30 } from "child_process";
10314
+
10315
+ // src/commands/prs/buildCreateArgs.ts
10316
+ function buildCreateArgs(title, body, options2) {
10317
+ const args = [
10318
+ "gh pr create",
10319
+ `--title ${shellQuote(title)}`,
10320
+ `--body ${shellQuote(body)}`
10321
+ ];
10322
+ const valueFlags = [
10323
+ ["--base", options2.base],
10324
+ ["--head", options2.head],
10325
+ ["--milestone", options2.milestone]
10326
+ ];
10327
+ for (const [flag, value] of valueFlags) {
10328
+ if (value) args.push(`${flag} ${shellQuote(value)}`);
10329
+ }
10330
+ if (options2.draft) args.push("--draft");
10331
+ if (options2.web) args.push("--web");
10332
+ const repeatableFlags = [
10333
+ ["--label", options2.label],
10334
+ ["--assignee", options2.assignee],
10335
+ ["--reviewer", options2.reviewer]
10336
+ ];
10337
+ for (const [flag, values] of repeatableFlags) {
10338
+ for (const value of values ?? []) {
10339
+ args.push(`${flag} ${shellQuote(value)}`);
10340
+ }
10341
+ }
10342
+ return args;
10343
+ }
10344
+
10345
+ // src/commands/prs/validatePrContent.ts
10346
+ function validatePrContent(title, body) {
10347
+ if (title.toLowerCase().includes("claude")) {
10348
+ console.error("Error: PR title must not reference Claude");
10349
+ process.exit(1);
10350
+ }
10351
+ if (body.toLowerCase().includes("claude")) {
10352
+ console.error("Error: PR body must not reference Claude");
10353
+ process.exit(1);
10354
+ }
10355
+ }
10356
+
10357
+ // src/commands/prs/create.ts
10358
+ function create(options2) {
10359
+ if (!options2.title || !options2.body) {
10360
+ console.error(
10361
+ "Usage: assist prs create --title <title> --body <body> [--base <branch>] [--head <branch>] [--draft] [--web] [--label <label>] [--assignee <login>] [--reviewer <handle>] [--milestone <name>]"
10362
+ );
10363
+ process.exit(1);
10364
+ }
10365
+ validatePrContent(options2.title, options2.body);
10366
+ const args = buildCreateArgs(options2.title, options2.body, options2);
10367
+ try {
10368
+ execSync30(args.join(" "), { stdio: "inherit" });
10369
+ } catch (_error) {
10370
+ process.exit(1);
10371
+ }
10372
+ }
10373
+
10288
10374
  // src/commands/prs/fixed.ts
10289
- import { execSync as execSync31 } from "child_process";
10375
+ import { execSync as execSync32 } from "child_process";
10290
10376
 
10291
10377
  // src/commands/prs/resolveCommentWithReply.ts
10292
- import { execSync as execSync30 } from "child_process";
10378
+ import { execSync as execSync31 } from "child_process";
10293
10379
  import { unlinkSync as unlinkSync8, writeFileSync as writeFileSync21 } from "fs";
10294
10380
  import { tmpdir as tmpdir5 } from "os";
10295
10381
  import { join as join32 } from "path";
@@ -10319,7 +10405,7 @@ function deleteCommentsCache(prNumber) {
10319
10405
 
10320
10406
  // src/commands/prs/resolveCommentWithReply.ts
10321
10407
  function replyToComment(org, repo, prNumber, commentId, message) {
10322
- execSync30(
10408
+ execSync31(
10323
10409
  `gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message.replace(/"/g, '\\"')}" -F in_reply_to=${commentId}`,
10324
10410
  { stdio: ["inherit", "pipe", "inherit"] }
10325
10411
  );
@@ -10329,7 +10415,7 @@ function resolveThread(threadId) {
10329
10415
  const queryFile = join32(tmpdir5(), `gh-mutation-${Date.now()}.graphql`);
10330
10416
  writeFileSync21(queryFile, mutation);
10331
10417
  try {
10332
- execSync30(
10418
+ execSync31(
10333
10419
  `gh api graphql -F query=@${queryFile} -f threadId="${threadId}"`,
10334
10420
  { stdio: ["inherit", "pipe", "inherit"] }
10335
10421
  );
@@ -10381,7 +10467,7 @@ function resolveCommentWithReply(commentId, message) {
10381
10467
  // src/commands/prs/fixed.ts
10382
10468
  function verifySha(sha) {
10383
10469
  try {
10384
- return execSync31(`git rev-parse --verify ${sha}`, {
10470
+ return execSync32(`git rev-parse --verify ${sha}`, {
10385
10471
  encoding: "utf-8"
10386
10472
  }).trim();
10387
10473
  } catch {
@@ -10395,7 +10481,7 @@ function fixed(commentId, sha) {
10395
10481
  const { org, repo } = getRepoInfo();
10396
10482
  const repoUrl = `https://github.com/${org}/${repo}`;
10397
10483
  const message = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
10398
- execSync31("git push", { stdio: "inherit" });
10484
+ execSync32("git push", { stdio: "inherit" });
10399
10485
  resolveCommentWithReply(commentId, message);
10400
10486
  } catch (error) {
10401
10487
  if (isGhNotInstalled(error)) {
@@ -10413,7 +10499,7 @@ import { join as join34 } from "path";
10413
10499
  import { stringify } from "yaml";
10414
10500
 
10415
10501
  // src/commands/prs/fetchThreadIds.ts
10416
- import { execSync as execSync32 } from "child_process";
10502
+ import { execSync as execSync33 } from "child_process";
10417
10503
  import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync22 } from "fs";
10418
10504
  import { tmpdir as tmpdir6 } from "os";
10419
10505
  import { join as join33 } from "path";
@@ -10422,7 +10508,7 @@ function fetchThreadIds(org, repo, prNumber) {
10422
10508
  const queryFile = join33(tmpdir6(), `gh-query-${Date.now()}.graphql`);
10423
10509
  writeFileSync22(queryFile, THREAD_QUERY);
10424
10510
  try {
10425
- const result = execSync32(
10511
+ const result = execSync33(
10426
10512
  `gh api graphql -F query=@${queryFile} -F owner="${org}" -F repo="${repo}" -F prNumber=${prNumber}`,
10427
10513
  { encoding: "utf-8" }
10428
10514
  );
@@ -10444,9 +10530,9 @@ function fetchThreadIds(org, repo, prNumber) {
10444
10530
  }
10445
10531
 
10446
10532
  // src/commands/prs/listComments/fetchReviewComments.ts
10447
- import { execSync as execSync33 } from "child_process";
10533
+ import { execSync as execSync34 } from "child_process";
10448
10534
  function fetchJson(endpoint) {
10449
- const result = execSync33(`gh api --paginate ${endpoint}`, {
10535
+ const result = execSync34(`gh api --paginate ${endpoint}`, {
10450
10536
  encoding: "utf-8"
10451
10537
  });
10452
10538
  if (!result.trim()) return [];
@@ -10585,7 +10671,7 @@ async function listComments() {
10585
10671
  }
10586
10672
 
10587
10673
  // src/commands/prs/prs/index.ts
10588
- import { execSync as execSync34 } from "child_process";
10674
+ import { execSync as execSync35 } from "child_process";
10589
10675
 
10590
10676
  // src/commands/prs/prs/displayPaginated/index.ts
10591
10677
  import enquirer9 from "enquirer";
@@ -10692,7 +10778,7 @@ async function prs(options2) {
10692
10778
  const state = options2.open ? "open" : options2.closed ? "closed" : "all";
10693
10779
  try {
10694
10780
  const { org, repo } = getRepoInfo();
10695
- const result = execSync34(
10781
+ const result = execSync35(
10696
10782
  `gh pr list --state ${state} --json number,title,url,author,createdAt,mergedAt,closedAt,state,changedFiles --limit 100 -R ${org}/${repo}`,
10697
10783
  { encoding: "utf-8" }
10698
10784
  );
@@ -10715,7 +10801,7 @@ async function prs(options2) {
10715
10801
  }
10716
10802
 
10717
10803
  // src/commands/prs/wontfix.ts
10718
- import { execSync as execSync35 } from "child_process";
10804
+ import { execSync as execSync36 } from "child_process";
10719
10805
  function validateReason(reason) {
10720
10806
  const lowerReason = reason.toLowerCase();
10721
10807
  if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
@@ -10732,7 +10818,7 @@ function validateShaReferences(reason) {
10732
10818
  const invalidShas = [];
10733
10819
  for (const sha of shas) {
10734
10820
  try {
10735
- execSync35(`git cat-file -t ${sha}`, { stdio: "pipe" });
10821
+ execSync36(`git cat-file -t ${sha}`, { stdio: "pipe" });
10736
10822
  } catch {
10737
10823
  invalidShas.push(sha);
10738
10824
  }
@@ -10759,9 +10845,30 @@ function wontfix(commentId, reason) {
10759
10845
  }
10760
10846
  }
10761
10847
 
10848
+ // src/commands/registerPrsCreate.ts
10849
+ function collect2(value, previous) {
10850
+ return previous.concat([value]);
10851
+ }
10852
+ function registerPrsCreate(prsCommand) {
10853
+ prsCommand.command("create").description(
10854
+ "Create a pull request via gh pr create, validating the title and body first"
10855
+ ).option("-t, --title <title>", "Title for the pull request").option("-b, --body <body>", "Body for the pull request").option("-B, --base <branch>", "Branch into which the pull request merges").option("-H, --head <branch>", "Branch that contains the commits").option("-d, --draft", "Mark the pull request as a draft").option("-w, --web", "Open the browser to create the pull request").option("-l, --label <label>", "Add a label (repeatable)", collect2, []).option(
10856
+ "-a, --assignee <login>",
10857
+ "Assign a person by login (repeatable)",
10858
+ collect2,
10859
+ []
10860
+ ).option(
10861
+ "-r, --reviewer <handle>",
10862
+ "Request a review (repeatable)",
10863
+ collect2,
10864
+ []
10865
+ ).option("-m, --milestone <name>", "Add the pull request to a milestone").action(create);
10866
+ }
10867
+
10762
10868
  // src/commands/registerPrs.ts
10763
10869
  function registerPrs(program2) {
10764
10870
  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);
10871
+ registerPrsCreate(prsCommand);
10765
10872
  prsCommand.command("list-comments").description("List all comments on the current branch's pull request").action(() => {
10766
10873
  listComments().then(printComments2);
10767
10874
  });
@@ -10846,10 +10953,10 @@ import chalk119 from "chalk";
10846
10953
  import Enquirer2 from "enquirer";
10847
10954
 
10848
10955
  // src/commands/ravendb/searchItems.ts
10849
- import { execSync as execSync36 } from "child_process";
10956
+ import { execSync as execSync37 } from "child_process";
10850
10957
  import chalk118 from "chalk";
10851
10958
  function opExec(args) {
10852
- return execSync36(`op ${args}`, {
10959
+ return execSync37(`op ${args}`, {
10853
10960
  encoding: "utf-8",
10854
10961
  stdio: ["pipe", "pipe", "pipe"]
10855
10962
  }).trim();
@@ -11001,7 +11108,7 @@ ${errorText}`
11001
11108
  }
11002
11109
 
11003
11110
  // src/commands/ravendb/resolveOpSecret.ts
11004
- import { execSync as execSync37 } from "child_process";
11111
+ import { execSync as execSync38 } from "child_process";
11005
11112
  import chalk123 from "chalk";
11006
11113
  function resolveOpSecret(reference) {
11007
11114
  if (!reference.startsWith("op://")) {
@@ -11009,7 +11116,7 @@ function resolveOpSecret(reference) {
11009
11116
  process.exit(1);
11010
11117
  }
11011
11118
  try {
11012
- return execSync37(`op read "${reference}"`, {
11119
+ return execSync38(`op read "${reference}"`, {
11013
11120
  encoding: "utf-8",
11014
11121
  stdio: ["pipe", "pipe", "pipe"]
11015
11122
  }).trim();
@@ -11258,7 +11365,7 @@ Refactor check failed:
11258
11365
  }
11259
11366
 
11260
11367
  // src/commands/refactor/check/getViolations/index.ts
11261
- import { execSync as execSync38 } from "child_process";
11368
+ import { execSync as execSync39 } from "child_process";
11262
11369
  import fs19 from "fs";
11263
11370
  import { minimatch as minimatch4 } from "minimatch";
11264
11371
 
@@ -11308,7 +11415,7 @@ function getGitFiles(options2) {
11308
11415
  }
11309
11416
  const files = /* @__PURE__ */ new Set();
11310
11417
  if (options2.staged || options2.modified) {
11311
- const staged = execSync38("git diff --cached --name-only", {
11418
+ const staged = execSync39("git diff --cached --name-only", {
11312
11419
  encoding: "utf-8"
11313
11420
  });
11314
11421
  for (const file of staged.trim().split("\n").filter(Boolean)) {
@@ -11316,7 +11423,7 @@ function getGitFiles(options2) {
11316
11423
  }
11317
11424
  }
11318
11425
  if (options2.unstaged || options2.modified) {
11319
- const unstaged = execSync38("git diff --name-only", { encoding: "utf-8" });
11426
+ const unstaged = execSync39("git diff --name-only", { encoding: "utf-8" });
11320
11427
  for (const file of unstaged.trim().split("\n").filter(Boolean)) {
11321
11428
  files.add(file);
11322
11429
  }
@@ -12866,9 +12973,9 @@ function buildReviewPaths(repoRoot, key) {
12866
12973
  }
12867
12974
 
12868
12975
  // src/commands/review/fetchExistingComments.ts
12869
- import { execSync as execSync39 } from "child_process";
12976
+ import { execSync as execSync40 } from "child_process";
12870
12977
  function fetchRawComments(org, repo, prNumber) {
12871
- const out = execSync39(
12978
+ const out = execSync40(
12872
12979
  `gh api --paginate repos/${org}/${repo}/pulls/${prNumber}/comments`,
12873
12980
  { encoding: "utf-8", maxBuffer: 64 * 1024 * 1024 }
12874
12981
  );
@@ -12899,12 +13006,41 @@ function fetchExistingComments() {
12899
13006
  }
12900
13007
 
12901
13008
  // src/commands/review/gatherContext.ts
13009
+ import { execSync as execSync43 } from "child_process";
13010
+
13011
+ // src/commands/review/fetchPrDiff.ts
12902
13012
  import { execSync as execSync41 } from "child_process";
13013
+ function fetchPrDiff(prNumber, baseSha, headSha) {
13014
+ const { org, repo } = getRepoInfo();
13015
+ try {
13016
+ return execSync41(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
13017
+ encoding: "utf-8",
13018
+ maxBuffer: 256 * 1024 * 1024,
13019
+ stdio: ["ignore", "pipe", "pipe"]
13020
+ });
13021
+ } catch (error) {
13022
+ if (!isDiffTooLarge(error)) throw error;
13023
+ return fetchDiffViaGit(baseSha, headSha);
13024
+ }
13025
+ }
13026
+ function isDiffTooLarge(error) {
13027
+ return error instanceof Error && (error.message.includes("too_large") || error.message.includes("maximum number of files"));
13028
+ }
13029
+ function fetchDiffViaGit(baseSha, headSha) {
13030
+ try {
13031
+ execSync41(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
13032
+ } catch {
13033
+ }
13034
+ return execSync41(`git diff ${baseSha}...${headSha}`, {
13035
+ encoding: "utf-8",
13036
+ maxBuffer: 256 * 1024 * 1024
13037
+ });
13038
+ }
12903
13039
 
12904
13040
  // src/commands/review/fetchPrDiffInfo.ts
12905
- import { execSync as execSync40 } from "child_process";
13041
+ import { execSync as execSync42 } from "child_process";
12906
13042
  function getCurrentBranch2() {
12907
- return execSync40("git rev-parse --abbrev-ref HEAD", {
13043
+ return execSync42("git rev-parse --abbrev-ref HEAD", {
12908
13044
  encoding: "utf-8"
12909
13045
  }).trim();
12910
13046
  }
@@ -12914,7 +13050,7 @@ function fetchPrDiffInfo() {
12914
13050
  const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
12915
13051
  let raw;
12916
13052
  try {
12917
- raw = execSync40(`gh pr view ${branch} --json ${fields} -R ${org}/${repo}`, {
13053
+ raw = execSync42(`gh pr view ${branch} --json ${fields} -R ${org}/${repo}`, {
12918
13054
  encoding: "utf-8",
12919
13055
  stdio: ["ignore", "pipe", "pipe"]
12920
13056
  });
@@ -12938,32 +13074,28 @@ function fetchPrDiffInfo() {
12938
13074
  }
12939
13075
  function fetchPrChangedFiles(prNumber) {
12940
13076
  const { org, repo } = getRepoInfo();
12941
- const out = execSync40(`gh pr diff ${prNumber} --name-only -R ${org}/${repo}`, {
12942
- encoding: "utf-8",
12943
- maxBuffer: 64 * 1024 * 1024
12944
- });
13077
+ const out = execSync42(
13078
+ `gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
13079
+ {
13080
+ encoding: "utf-8",
13081
+ maxBuffer: 64 * 1024 * 1024
13082
+ }
13083
+ );
12945
13084
  return out.trim().split("\n").filter(Boolean);
12946
13085
  }
12947
- function fetchPrDiff(prNumber) {
12948
- const { org, repo } = getRepoInfo();
12949
- return execSync40(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
12950
- encoding: "utf-8",
12951
- maxBuffer: 256 * 1024 * 1024
12952
- });
12953
- }
12954
13086
 
12955
13087
  // src/commands/review/gatherContext.ts
12956
13088
  function gatherContext() {
12957
- const branch = execSync41("git rev-parse --abbrev-ref HEAD", {
13089
+ const branch = execSync43("git rev-parse --abbrev-ref HEAD", {
12958
13090
  encoding: "utf-8"
12959
13091
  }).trim();
12960
- const sha = execSync41("git rev-parse HEAD", { encoding: "utf-8" }).trim();
12961
- const shortSha = execSync41("git rev-parse --short=7 HEAD", {
13092
+ const sha = execSync43("git rev-parse HEAD", { encoding: "utf-8" }).trim();
13093
+ const shortSha = execSync43("git rev-parse --short=7 HEAD", {
12962
13094
  encoding: "utf-8"
12963
13095
  }).trim();
12964
13096
  const prInfo = fetchPrDiffInfo();
12965
13097
  const changedFiles = fetchPrChangedFiles(prInfo.prNumber);
12966
- const diff2 = fetchPrDiff(prInfo.prNumber);
13098
+ const diff2 = fetchPrDiff(prInfo.prNumber, prInfo.baseSha, prInfo.headSha);
12967
13099
  return {
12968
13100
  branch,
12969
13101
  sha,
@@ -14270,11 +14402,11 @@ async function reviewPr(repoRoot, options2) {
14270
14402
  }
14271
14403
 
14272
14404
  // src/commands/review/gatherShaContext.ts
14273
- import { execSync as execSync42 } from "child_process";
14405
+ import { execSync as execSync44 } from "child_process";
14274
14406
  function resolveSha(ref, format2) {
14275
14407
  const flag = format2 === "short" ? "--short=7 " : "";
14276
14408
  try {
14277
- return execSync42(`git rev-parse --verify ${flag}${ref}^{commit}`, {
14409
+ return execSync44(`git rev-parse --verify ${flag}${ref}^{commit}`, {
14278
14410
  encoding: "utf-8",
14279
14411
  stdio: ["ignore", "pipe", "pipe"]
14280
14412
  }).trim();
@@ -14288,11 +14420,11 @@ function gatherShaContext(ref) {
14288
14420
  const shortSha = resolveSha(sha, "short");
14289
14421
  const parentSha = resolveSha(`${sha}^`, "long");
14290
14422
  const range = `${parentSha}..${sha}`;
14291
- const changedFiles = execSync42(`git diff --name-only ${range}`, {
14423
+ const changedFiles = execSync44(`git diff --name-only ${range}`, {
14292
14424
  encoding: "utf-8",
14293
14425
  maxBuffer: 64 * 1024 * 1024
14294
14426
  }).trim().split("\n").filter(Boolean);
14295
- const diff2 = execSync42(`git diff ${range}`, {
14427
+ const diff2 = execSync44(`git diff ${range}`, {
14296
14428
  encoding: "utf-8",
14297
14429
  maxBuffer: 256 * 1024 * 1024
14298
14430
  });
@@ -15727,7 +15859,7 @@ import { mkdirSync as mkdirSync14 } from "fs";
15727
15859
  import { join as join45 } from "path";
15728
15860
 
15729
15861
  // src/commands/voice/checkLockFile.ts
15730
- import { execSync as execSync43 } from "child_process";
15862
+ import { execSync as execSync45 } from "child_process";
15731
15863
  import { existsSync as existsSync44, mkdirSync as mkdirSync13, readFileSync as readFileSync34, writeFileSync as writeFileSync27 } from "fs";
15732
15864
  import { join as join44 } from "path";
15733
15865
  function isProcessAlive2(pid) {
@@ -15756,7 +15888,7 @@ function bootstrapVenv() {
15756
15888
  if (existsSync44(getVenvPython())) return;
15757
15889
  console.log("Setting up Python environment...");
15758
15890
  const pythonDir = getPythonDir();
15759
- execSync43(
15891
+ execSync45(
15760
15892
  `uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
15761
15893
  {
15762
15894
  stdio: "inherit",
@@ -15923,11 +16055,11 @@ import { randomBytes } from "crypto";
15923
16055
  import chalk153 from "chalk";
15924
16056
 
15925
16057
  // src/lib/openBrowser.ts
15926
- import { execSync as execSync44 } from "child_process";
16058
+ import { execSync as execSync46 } from "child_process";
15927
16059
  function tryExec(commands) {
15928
16060
  for (const cmd of commands) {
15929
16061
  try {
15930
- execSync44(cmd);
16062
+ execSync46(cmd);
15931
16063
  return true;
15932
16064
  } catch {
15933
16065
  }
@@ -16269,11 +16401,11 @@ function resolveParams(params, cliArgs) {
16269
16401
  }
16270
16402
 
16271
16403
  // src/commands/run/runPreCommands.ts
16272
- import { execSync as execSync45 } from "child_process";
16404
+ import { execSync as execSync47 } from "child_process";
16273
16405
  function runPreCommands(pre, cwd) {
16274
16406
  for (const cmd of pre) {
16275
16407
  try {
16276
- execSync45(cmd, { stdio: "inherit", cwd });
16408
+ execSync47(cmd, { stdio: "inherit", cwd });
16277
16409
  } catch (err) {
16278
16410
  const code = err && typeof err === "object" && "status" in err ? err.status : 1;
16279
16411
  process.exit(code);
@@ -16536,7 +16668,7 @@ function registerRun(program2) {
16536
16668
  }
16537
16669
 
16538
16670
  // src/commands/screenshot/index.ts
16539
- import { execSync as execSync46 } from "child_process";
16671
+ import { execSync as execSync48 } from "child_process";
16540
16672
  import { existsSync as existsSync49, mkdirSync as mkdirSync17, unlinkSync as unlinkSync15, writeFileSync as writeFileSync30 } from "fs";
16541
16673
  import { tmpdir as tmpdir7 } from "os";
16542
16674
  import { join as join51, resolve as resolve13 } from "path";
@@ -16679,7 +16811,7 @@ function runPowerShellScript(processName, outputPath) {
16679
16811
  const scriptPath = join51(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
16680
16812
  writeFileSync30(scriptPath, captureWindowPs1, "utf-8");
16681
16813
  try {
16682
- execSync46(
16814
+ execSync48(
16683
16815
  `powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
16684
16816
  { stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }
16685
16817
  );
@@ -17067,7 +17199,7 @@ function syncCommands(claudeDir, targetBase) {
17067
17199
  }
17068
17200
 
17069
17201
  // src/commands/update.ts
17070
- import { execSync as execSync47 } from "child_process";
17202
+ import { execSync as execSync49 } from "child_process";
17071
17203
  import * as path51 from "path";
17072
17204
  function isGlobalNpmInstall(dir) {
17073
17205
  try {
@@ -17075,7 +17207,7 @@ function isGlobalNpmInstall(dir) {
17075
17207
  if (resolved.split(path51.sep).includes("node_modules")) {
17076
17208
  return true;
17077
17209
  }
17078
- const globalPrefix = execSync47("npm prefix -g", { stdio: "pipe" }).toString().trim();
17210
+ const globalPrefix = execSync49("npm prefix -g", { stdio: "pipe" }).toString().trim();
17079
17211
  return resolved.toLowerCase().startsWith(path51.resolve(globalPrefix).toLowerCase());
17080
17212
  } catch {
17081
17213
  return false;
@@ -17086,18 +17218,18 @@ async function update2() {
17086
17218
  console.log(`Assist is installed at: ${installDir}`);
17087
17219
  if (isGitRepo(installDir)) {
17088
17220
  console.log("Detected git repo installation, pulling latest...");
17089
- execSync47("git pull", { cwd: installDir, stdio: "inherit" });
17221
+ execSync49("git pull", { cwd: installDir, stdio: "inherit" });
17090
17222
  console.log("Installing dependencies...");
17091
- execSync47("npm i", { cwd: installDir, stdio: "inherit" });
17223
+ execSync49("npm i", { cwd: installDir, stdio: "inherit" });
17092
17224
  console.log("Building...");
17093
- execSync47("npm run build", { cwd: installDir, stdio: "inherit" });
17225
+ execSync49("npm run build", { cwd: installDir, stdio: "inherit" });
17094
17226
  console.log("Syncing commands...");
17095
- execSync47("assist sync", { stdio: "inherit" });
17227
+ execSync49("assist sync", { stdio: "inherit" });
17096
17228
  } else if (isGlobalNpmInstall(installDir)) {
17097
17229
  console.log("Detected global npm installation, updating...");
17098
- execSync47("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
17230
+ execSync49("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
17099
17231
  console.log("Syncing commands...");
17100
- execSync47("assist sync", { stdio: "inherit" });
17232
+ execSync49("assist sync", { stdio: "inherit" });
17101
17233
  } else {
17102
17234
  console.error(
17103
17235
  "Could not determine installation method. Expected a git repo or global npm install."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.238.1",
3
+ "version": "0.239.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {