prq-cli 0.7.0 → 0.9.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/README.md CHANGED
@@ -95,19 +95,21 @@ prq skill --global # install globally
95
95
 
96
96
  ## Pluggable Actions
97
97
 
98
- PRQ doesn't force a workflow. Every action is a configurable shell command template. Override the defaults or add your own in `.prqrc.json`:
98
+ PRQ doesn't force a workflow. Every action is a configurable shell command template — inline commands or scripts. Override the defaults or add your own in `.prqrc.json`.
99
+
100
+ Actions run with full terminal control. When you trigger an action, prq suspends its TUI, the command takes over the screen (interactive tools like Claude Code work as normal), and prq resumes when the command exits.
99
101
 
100
102
  ### Use Claude Code for reviews
101
103
 
102
104
  ```json
103
105
  {
104
106
  "actions": {
105
- "review": "claude -p '/review {url}'"
107
+ "review": "claude '/review {url}'"
106
108
  }
107
109
  }
108
110
  ```
109
111
 
110
- Now `prq review 482` dispatches to Claude Code.
112
+ Now `prq review 482` opens an interactive Claude Code session.
111
113
 
112
114
  ### Use Codex for reviews
113
115
 
@@ -119,6 +121,18 @@ Now `prq review 482` dispatches to Claude Code.
119
121
  }
120
122
  ```
121
123
 
124
+ ### Use a script for complex workflows
125
+
126
+ ```json
127
+ {
128
+ "actions": {
129
+ "review": "./scripts/review.sh {number} {url}"
130
+ }
131
+ }
132
+ ```
133
+
134
+ The script handles its own logic — session management, resuming, branching, whatever you need.
135
+
122
136
  ### Use gh CLI to checkout
123
137
 
124
138
  ```json
@@ -143,9 +157,11 @@ With no config, `prq review` opens the files changed tab and `prq open` opens th
143
157
  | `{number}` | `482` |
144
158
  | `{owner}` | `org` |
145
159
  | `{repo}` | `repo` |
160
+ | `{fullRepo}` | `org/repo` |
146
161
  | `{title}` | `fix: handle edge case` |
147
162
  | `{author}` | `alice` |
148
163
  | `{days}` | `5` |
164
+ | `{category}` | `needs-re-review` |
149
165
 
150
166
  ## Agent & Automation
151
167
 
@@ -196,7 +212,7 @@ Full example:
196
212
  "repos": ["org/repo1", "org/repo2"],
197
213
  "staleDays": 5,
198
214
  "actions": {
199
- "review": "claude -p '/review {url}'",
215
+ "review": "claude '/review {url}'",
200
216
  "checkout": "gh pr checkout {number} --repo {owner}/{repo}",
201
217
  "approve": "gh pr review {number} --repo {owner}/{repo} --approve"
202
218
  }
package/dist/bin/prq.js CHANGED
@@ -6424,7 +6424,7 @@ function getAction(name, config) {
6424
6424
  function listActions(config) {
6425
6425
  return { ...DEFAULT_ACTIONS, ...config.actions };
6426
6426
  }
6427
- function buildContext(pr) {
6427
+ function buildContext(pr, category = "") {
6428
6428
  const days = Math.floor((Date.now() - new Date(pr.updatedAt || Date.now()).getTime()) / 86400000);
6429
6429
  return {
6430
6430
  url: pr.url,
@@ -6434,7 +6434,8 @@ function buildContext(pr) {
6434
6434
  fullRepo: `${pr.owner}/${pr.repo}`,
6435
6435
  title: pr.title,
6436
6436
  author: pr.author,
6437
- days
6437
+ days,
6438
+ category
6438
6439
  };
6439
6440
  }
6440
6441
  function interpolate(template, context) {
@@ -6481,98 +6482,62 @@ Available actions: ${available}`);
6481
6482
  // src/commands/skill.ts
6482
6483
  var SKILL_CONTENT = `---
6483
6484
  name: prq
6484
- description: PR review queue manager. Use when the user wants to check their PR review queue, review PRs, nudge stale PRs, or manage code review workflow. Triggers on mentions of "review queue", "PRs waiting", "stale PRs", "what needs review", or "prq".
6485
+ description: PR review queue manager. Use when the user wants to check their PR review queue, review PRs, nudge stale PRs, open PRs, or manage code review workflow. Triggers on mentions of "review queue", "PRs waiting", "stale PRs", "what needs review", "open PR", "nudge", or "prq".
6485
6486
  ---
6486
6487
 
6487
- # PRQ — PR Review Queue
6488
+ # PRQ CLI
6488
6489
 
6489
- You have access to the \`prq\` CLI tool installed on this machine. Use it to help the user manage their code review queue.
6490
+ \`prq\` is a CLI tool for managing code review queues. It's installed on this machine.
6490
6491
 
6491
- ## Step 1: Get the queue
6492
-
6493
- Run this command to get the user's current review queue:
6492
+ ## Commands
6494
6493
 
6495
6494
  \`\`\`bash
6495
+ # Show the review queue (JSON for parsing)
6496
6496
  prq status --json
6497
- \`\`\`
6498
6497
 
6499
- This returns a JSON object with categorized PRs:
6500
- - **needs-re-review** — PRs where the user left a review but new commits were pushed after
6501
- - **requested** — PRs where the user is a requested reviewer
6502
- - **stale** — PRs with no activity for N days
6503
- - **waiting-on-others** — PRs the user authored that are waiting on someone else
6498
+ # Open a PR in the browser
6499
+ prq open <number>
6500
+ prq open <owner/repo#number>
6504
6501
 
6505
- ## Step 2: Present the queue
6502
+ # Open files changed tab for review
6503
+ prq review <number>
6506
6504
 
6507
- Show the results in a clear, scannable format grouped by category. For each PR, show:
6508
- - The category symbol (◆ needs re-review, ● requested, ○ stale, ◇ waiting)
6509
- - The repo and PR number
6510
- - The title
6511
- - The detail (e.g., "new commits since your review 2d ago")
6505
+ # Post a nudge comment on a stale PR
6506
+ prq nudge <number> --yes
6507
+ prq nudge <number> --message "Custom message" --yes
6512
6508
 
6513
- Then ask what the user wants to do.
6509
+ # Run any configured action
6510
+ prq run <action> <number>
6511
+ \`\`\`
6514
6512
 
6515
- ## Step 3: Act on PRs
6513
+ ## JSON output
6516
6514
 
6517
- When the user asks to act on a PR, check the \`.prqrc.json\` file in the current directory (or \`~/.config/prq/config.json\`) for custom actions:
6515
+ \`prq status --json\` returns:
6518
6516
 
6519
6517
  \`\`\`json
6520
6518
  {
6521
- "actions": {
6522
- "review": "/review {url}",
6523
- "nudge": "shell:prq nudge {number} --yes"
6524
- }
6519
+ "user": "caio-pizzol",
6520
+ "prs": [
6521
+ {
6522
+ "category": "needs-re-review",
6523
+ "repo": "org/repo",
6524
+ "number": 2439,
6525
+ "title": "fix: something",
6526
+ "author": "alice",
6527
+ "url": "https://github.com/org/repo/pull/2439",
6528
+ "detail": "New commits since your review 1d ago"
6529
+ }
6530
+ ]
6525
6531
  }
6526
6532
  \`\`\`
6527
6533
 
6528
- ### Action resolution
6529
-
6530
- For each action template:
6531
- - **Starts with \`/\`** — it's a Claude Code skill. Invoke it by running the skill with the interpolated value. For example, \`/review https://github.com/org/repo/pull/123\`
6532
- - **Starts with \`shell:\`** — it's a shell command. Run it with the Bash tool. For example, \`prq nudge 123 --yes\`
6533
- - **Otherwise** — treat it as a prompt. Send it as a message.
6534
-
6535
- ### Template variables
6536
-
6537
- Replace these in the action template:
6538
- - \`{url}\` — full PR URL
6539
- - \`{number}\` — PR number
6540
- - \`{owner}\` — repo owner
6541
- - \`{repo}\` — repo name
6542
- - \`{title}\` — PR title
6543
- - \`{author}\` — PR author
6544
-
6545
- ### Default actions (if no config found)
6546
-
6547
- If no actions are configured, use these defaults:
6548
- - **review** — invoke \`/review {url}\` if the /review skill exists, otherwise run \`prq review {number}\` to open files changed in browser
6549
- - **nudge** — run \`prq nudge {number} --yes\`
6550
- - **open** — run \`prq open {number}\`
6551
-
6552
- ## Step 4: Batch operations
6553
-
6554
- If the user says things like "review all needs-re-review PRs" or "nudge all stale PRs":
6555
-
6556
- 1. Filter the queue JSON by the requested category
6557
- 2. Confirm the list with the user: "I'll review these 3 PRs: #2439, #2380, #2352. Proceed?"
6558
- 3. Execute the action on each PR sequentially
6559
-
6560
- ## Examples
6561
-
6562
- **User:** "check my review queue"
6563
- → Run \`prq status --json\`, present results, ask what to do
6564
-
6565
- **User:** "review 2439"
6566
- → Look up action for "review", interpolate with PR data, execute
6567
-
6568
- **User:** "nudge all stale PRs"
6569
- → Filter stale PRs from queue, confirm, run nudge on each
6534
+ Categories: \`needs-re-review\`, \`requested\`, \`stale\`, \`waiting-on-others\`.
6570
6535
 
6571
- **User:** "what PRs are waiting on me?"
6572
- → Run \`prq status --json\`, show only needs-re-review and requested categories
6536
+ ## Usage
6573
6537
 
6574
- **User:** "/prq" with no context
6575
- Run \`prq status --json\`, present full queue, ask what to do
6538
+ - When the user asks about their review queue, run \`prq status --json\` and present the results grouped by category.
6539
+ - When the user asks to open, review, or nudge a PR, use the corresponding \`prq\` command.
6540
+ - For batch operations ("nudge all stale PRs"), filter the JSON output and confirm with the user before acting.
6576
6541
  `;
6577
6542
  function skillCommand(global) {
6578
6543
  if (global) {
@@ -6797,14 +6762,34 @@ function render(state) {
6797
6762
  process.stdout.write(lines.join(`
6798
6763
  `));
6799
6764
  }
6800
- async function runAction(actionName, template, pr, state) {
6801
- const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
6802
- state.message = source_default.dim(`running ${actionName} on ${pr.repo}#${pr.number}...`);
6765
+ function suspend() {
6766
+ process.stdin.setRawMode(false);
6767
+ process.stdin.pause();
6768
+ process.stdin.removeAllListeners("data");
6769
+ process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
6770
+ }
6771
+ function resume(state, onData) {
6772
+ process.stdin.setRawMode(true);
6773
+ process.stdin.resume();
6774
+ process.stdin.setEncoding("utf8");
6775
+ process.stdout.write("\x1B[?25l");
6776
+ process.stdin.on("data", onData);
6803
6777
  render(state);
6778
+ }
6779
+ async function runAction(actionName, template, pr, state, onData) {
6780
+ const context = buildContext(toResolvedPR(pr), pr.category);
6781
+ const cmd = interpolate(template, context);
6782
+ suspend();
6783
+ process.stdout.write(source_default.dim(`
6784
+ running ${actionName} on ${pr.repo}#${pr.number}...
6785
+
6786
+ `));
6804
6787
  try {
6805
6788
  await executeCommand(cmd);
6789
+ resume(state, onData);
6806
6790
  return source_default.green(`${actionName}: ${pr.repo}#${pr.number}`);
6807
6791
  } catch {
6792
+ resume(state, onData);
6808
6793
  return source_default.red(`${actionName} failed`);
6809
6794
  }
6810
6795
  }
@@ -6828,33 +6813,22 @@ async function interactiveMode(result, config) {
6828
6813
  `);
6829
6814
  return;
6830
6815
  }
6831
- process.stdin.setRawMode(true);
6832
- process.stdin.resume();
6833
- process.stdin.setEncoding("utf8");
6834
- process.stdout.write("\x1B[?25l");
6835
- render(state);
6836
6816
  return new Promise((resolve) => {
6837
- const cleanup = () => {
6838
- process.stdin.setRawMode(false);
6839
- process.stdin.pause();
6840
- process.stdin.removeAllListeners("data");
6841
- process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
6842
- };
6843
- process.stdin.on("data", async (key) => {
6817
+ const onData = async (key) => {
6844
6818
  const pr = result.prs[state.selectedIndex];
6845
6819
  if (state.actionMenu) {
6846
6820
  if (key === "q" || key === "\x1B" || key === "a") {
6847
6821
  state.actionMenu = null;
6848
6822
  state.message = "";
6849
6823
  } else if (key === "\x03") {
6850
- cleanup();
6824
+ suspend();
6851
6825
  resolve();
6852
6826
  return;
6853
6827
  } else {
6854
6828
  const idx = parseInt(key, 10);
6855
6829
  if (idx >= 1 && idx <= state.actionMenu.length) {
6856
6830
  const action = state.actionMenu[idx - 1];
6857
- state.message = await runAction(action.name, action.template, pr, state);
6831
+ state.message = await runAction(action.name, action.template, pr, state, onData);
6858
6832
  state.actionMenu = null;
6859
6833
  }
6860
6834
  }
@@ -6864,7 +6838,7 @@ async function interactiveMode(result, config) {
6864
6838
  switch (key) {
6865
6839
  case "q":
6866
6840
  case "\x03":
6867
- cleanup();
6841
+ suspend();
6868
6842
  resolve();
6869
6843
  return;
6870
6844
  case "\x1B[A":
@@ -6878,21 +6852,21 @@ async function interactiveMode(result, config) {
6878
6852
  case "o": {
6879
6853
  const template = allActions.open;
6880
6854
  if (template) {
6881
- state.message = await runAction("open", template, pr, state);
6855
+ state.message = await runAction("open", template, pr, state, onData);
6882
6856
  }
6883
6857
  break;
6884
6858
  }
6885
6859
  case "r": {
6886
6860
  const template = allActions.review;
6887
6861
  if (template) {
6888
- state.message = await runAction("review", template, pr, state);
6862
+ state.message = await runAction("review", template, pr, state, onData);
6889
6863
  }
6890
6864
  break;
6891
6865
  }
6892
6866
  case "n": {
6893
6867
  const template = allActions.nudge;
6894
6868
  if (template) {
6895
- state.message = await runAction("nudge", template, pr, state);
6869
+ state.message = await runAction("nudge", template, pr, state, onData);
6896
6870
  }
6897
6871
  break;
6898
6872
  }
@@ -6920,7 +6894,8 @@ async function interactiveMode(result, config) {
6920
6894
  break;
6921
6895
  }
6922
6896
  render(state);
6923
- });
6897
+ };
6898
+ resume(state, onData);
6924
6899
  });
6925
6900
  }
6926
6901
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prq-cli",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "PR Queue — see what code reviews need your attention",
5
5
  "type": "module",
6
6
  "bin": {