prq-cli 0.3.0 → 0.5.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 +42 -8
- package/dist/bin/prq.js +264 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,11 +17,11 @@ Requires [GitHub CLI](https://cli.github.com/) (`gh`) to be authenticated.
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
# Set up config for the current project
|
|
21
|
-
prq init
|
|
22
|
-
|
|
23
20
|
# See your review queue
|
|
24
21
|
prq
|
|
22
|
+
|
|
23
|
+
# Interactive mode — navigate with arrow keys, act with shortcuts
|
|
24
|
+
prq -i
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Commands
|
|
@@ -40,11 +40,23 @@ prq # all repos you have access to
|
|
|
40
40
|
prq status --repos org/repo1 org/repo2 # specific repos
|
|
41
41
|
prq status --stale-days 7 # custom stale threshold
|
|
42
42
|
prq status --json # machine-readable output
|
|
43
|
+
prq status -i # interactive mode
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
### Interactive Mode
|
|
47
|
+
|
|
48
|
+
Run `prq -i` to navigate your queue with keyboard shortcuts:
|
|
49
|
+
|
|
50
|
+
- **↑↓** navigate between PRs
|
|
51
|
+
- **r** review — open files changed tab
|
|
52
|
+
- **o** open — open PR in browser
|
|
53
|
+
- **n** nudge — post a comment
|
|
54
|
+
- **c** copy URL to clipboard
|
|
55
|
+
- **q** quit
|
|
56
|
+
|
|
45
57
|
### `prq open <identifier>`
|
|
46
58
|
|
|
47
|
-
Open a PR in the browser.
|
|
59
|
+
Open a PR in the browser.
|
|
48
60
|
|
|
49
61
|
```bash
|
|
50
62
|
prq open 482 # searches your queue for PR #482
|
|
@@ -71,14 +83,18 @@ prq nudge 482 --yes # skip confirmation
|
|
|
71
83
|
prq nudge 482 --message "Any updates?" # custom message
|
|
72
84
|
```
|
|
73
85
|
|
|
74
|
-
### `prq
|
|
86
|
+
### `prq run <action> <identifier>`
|
|
75
87
|
|
|
76
|
-
|
|
88
|
+
Run a custom action defined in your config.
|
|
77
89
|
|
|
78
90
|
```bash
|
|
79
|
-
prq
|
|
91
|
+
prq run checkout 482
|
|
80
92
|
```
|
|
81
93
|
|
|
94
|
+
### `prq init`
|
|
95
|
+
|
|
96
|
+
Creates a `.prqrc.json` config file in the current directory.
|
|
97
|
+
|
|
82
98
|
## Configuration
|
|
83
99
|
|
|
84
100
|
Config is loaded in this order (later overrides earlier):
|
|
@@ -92,10 +108,28 @@ Example `.prqrc.json`:
|
|
|
92
108
|
```json
|
|
93
109
|
{
|
|
94
110
|
"repos": ["org/repo1", "org/repo2"],
|
|
95
|
-
"staleDays": 5
|
|
111
|
+
"staleDays": 5,
|
|
112
|
+
"actions": {
|
|
113
|
+
"review": "claude -p '/review {url}'",
|
|
114
|
+
"checkout": "gh pr checkout {number} --repo {owner}/{repo}"
|
|
115
|
+
}
|
|
96
116
|
}
|
|
97
117
|
```
|
|
98
118
|
|
|
119
|
+
### Custom Actions
|
|
120
|
+
|
|
121
|
+
Actions are shell command templates with variables:
|
|
122
|
+
|
|
123
|
+
- `{url}` — full PR URL
|
|
124
|
+
- `{number}` — PR number
|
|
125
|
+
- `{owner}` — repo owner
|
|
126
|
+
- `{repo}` — repo name
|
|
127
|
+
- `{title}` — PR title
|
|
128
|
+
- `{author}` — PR author
|
|
129
|
+
- `{days}` — days since last activity
|
|
130
|
+
|
|
131
|
+
Default actions (`open`, `review`, `nudge`) can be overridden. Custom actions are available via `prq run <action>` and in interactive mode.
|
|
132
|
+
|
|
99
133
|
## License
|
|
100
134
|
|
|
101
135
|
MIT
|
package/dist/bin/prq.js
CHANGED
|
@@ -6411,40 +6411,71 @@ async function nudgeCommand(identifier, config, options) {
|
|
|
6411
6411
|
console.log(source_default.green(` Comment posted on ${label}`));
|
|
6412
6412
|
}
|
|
6413
6413
|
|
|
6414
|
-
// src/
|
|
6414
|
+
// src/actions.ts
|
|
6415
6415
|
import { spawn } from "node:child_process";
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6416
|
+
var DEFAULT_ACTIONS = {
|
|
6417
|
+
open: "open {url}",
|
|
6418
|
+
review: "open {url}/files",
|
|
6419
|
+
nudge: "gh pr comment {number} --repo {owner}/{repo} --body 'Hey @{author}, is this PR still active? No activity for {days} days.'"
|
|
6420
|
+
};
|
|
6421
|
+
function getAction(name, config) {
|
|
6422
|
+
return config.actions[name] ?? DEFAULT_ACTIONS[name];
|
|
6423
|
+
}
|
|
6424
|
+
function listActions(config) {
|
|
6425
|
+
return { ...DEFAULT_ACTIONS, ...config.actions };
|
|
6426
|
+
}
|
|
6427
|
+
function buildContext(pr) {
|
|
6428
|
+
const days = Math.floor((Date.now() - new Date(pr.updatedAt || Date.now()).getTime()) / 86400000);
|
|
6429
|
+
return {
|
|
6430
|
+
url: pr.url,
|
|
6431
|
+
number: pr.number,
|
|
6432
|
+
owner: pr.owner,
|
|
6433
|
+
repo: pr.repo,
|
|
6434
|
+
fullRepo: `${pr.owner}/${pr.repo}`,
|
|
6435
|
+
title: pr.title,
|
|
6436
|
+
author: pr.author,
|
|
6437
|
+
days
|
|
6438
|
+
};
|
|
6439
|
+
}
|
|
6440
|
+
function interpolate(template, context) {
|
|
6441
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
6442
|
+
if (key in context) {
|
|
6443
|
+
return String(context[key]);
|
|
6422
6444
|
}
|
|
6423
|
-
|
|
6424
|
-
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
6425
|
-
child.unref();
|
|
6426
|
-
child.on("error", reject);
|
|
6427
|
-
child.on("close", () => resolve());
|
|
6445
|
+
return match;
|
|
6428
6446
|
});
|
|
6429
6447
|
}
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6448
|
+
function executeCommand(command) {
|
|
6449
|
+
return new Promise((resolve, reject) => {
|
|
6450
|
+
const child = spawn(command, {
|
|
6451
|
+
shell: true,
|
|
6452
|
+
stdio: "inherit"
|
|
6453
|
+
});
|
|
6454
|
+
child.on("error", reject);
|
|
6455
|
+
child.on("close", (code) => {
|
|
6456
|
+
if (code === 0)
|
|
6457
|
+
resolve();
|
|
6458
|
+
else
|
|
6459
|
+
reject(new Error(`Command exited with code ${code}`));
|
|
6460
|
+
});
|
|
6461
|
+
});
|
|
6438
6462
|
}
|
|
6439
6463
|
|
|
6440
|
-
// src/commands/
|
|
6441
|
-
async function
|
|
6464
|
+
// src/commands/run.ts
|
|
6465
|
+
async function runCommand(action, identifier, config) {
|
|
6466
|
+
const template = getAction(action, config);
|
|
6467
|
+
if (!template) {
|
|
6468
|
+
const available = Object.keys(listActions(config)).join(", ");
|
|
6469
|
+
throw new Error(`Unknown action: "${action}"
|
|
6470
|
+
Available actions: ${available}`);
|
|
6471
|
+
}
|
|
6442
6472
|
const pr = await resolveIdentifier(identifier, config);
|
|
6473
|
+
const context = buildContext(pr);
|
|
6474
|
+
const command = interpolate(template, context);
|
|
6443
6475
|
const label = `${pr.owner}/${pr.repo}#${pr.number}`;
|
|
6444
|
-
|
|
6445
|
-
process.stderr.write(source_default.dim(`Opening review for ${label}...
|
|
6476
|
+
process.stderr.write(source_default.dim(`${label} → ${action}: ${command}
|
|
6446
6477
|
`));
|
|
6447
|
-
await
|
|
6478
|
+
await executeCommand(command);
|
|
6448
6479
|
}
|
|
6449
6480
|
|
|
6450
6481
|
// src/categorize.ts
|
|
@@ -6552,7 +6583,7 @@ function categorize(reviewedPRs, requestedPRs, authoredPRs, staleDays) {
|
|
|
6552
6583
|
return results;
|
|
6553
6584
|
}
|
|
6554
6585
|
|
|
6555
|
-
// src/
|
|
6586
|
+
// src/interactive.ts
|
|
6556
6587
|
var CATEGORY_CONFIG = {
|
|
6557
6588
|
"needs-re-review": {
|
|
6558
6589
|
icon: "◆",
|
|
@@ -6573,6 +6604,198 @@ var CATEGORY_ORDER = [
|
|
|
6573
6604
|
"stale",
|
|
6574
6605
|
"waiting-on-others"
|
|
6575
6606
|
];
|
|
6607
|
+
function toResolvedPR(pr) {
|
|
6608
|
+
const [owner, repo] = pr.repo.split("/");
|
|
6609
|
+
return {
|
|
6610
|
+
owner,
|
|
6611
|
+
repo,
|
|
6612
|
+
number: pr.number,
|
|
6613
|
+
url: pr.url,
|
|
6614
|
+
title: pr.title,
|
|
6615
|
+
author: pr.author,
|
|
6616
|
+
updatedAt: pr.updatedAt
|
|
6617
|
+
};
|
|
6618
|
+
}
|
|
6619
|
+
function render(result, selectedIndex, message) {
|
|
6620
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
6621
|
+
const lines = [];
|
|
6622
|
+
lines.push(source_default.bold(` PRQ Status for ${result.user}`));
|
|
6623
|
+
lines.push(source_default.dim(` ${"─".repeat(50)}`));
|
|
6624
|
+
const grouped = new Map;
|
|
6625
|
+
for (const pr of result.prs) {
|
|
6626
|
+
const list = grouped.get(pr.category) ?? [];
|
|
6627
|
+
list.push(pr);
|
|
6628
|
+
grouped.set(pr.category, list);
|
|
6629
|
+
}
|
|
6630
|
+
let flatIndex = 0;
|
|
6631
|
+
for (const category of CATEGORY_ORDER) {
|
|
6632
|
+
const prs = grouped.get(category);
|
|
6633
|
+
if (!prs || prs.length === 0)
|
|
6634
|
+
continue;
|
|
6635
|
+
const config = CATEGORY_CONFIG[category];
|
|
6636
|
+
lines.push("");
|
|
6637
|
+
lines.push(` ${config.color(`${config.icon} ${config.label}`)} ${source_default.dim(`(${prs.length})`)}`);
|
|
6638
|
+
for (const pr of prs) {
|
|
6639
|
+
const isSelected = flatIndex === selectedIndex;
|
|
6640
|
+
const arrow = isSelected ? source_default.yellow("›") : " ";
|
|
6641
|
+
const draft = pr.isDraft ? source_default.dim(" [draft]") : "";
|
|
6642
|
+
const maxTitle = 50;
|
|
6643
|
+
const title = pr.title.length > maxTitle ? `${pr.title.slice(0, maxTitle - 3)}...` : pr.title;
|
|
6644
|
+
if (isSelected) {
|
|
6645
|
+
const ref = source_default.white(`#${pr.number}`);
|
|
6646
|
+
lines.push(` ${arrow} ${ref} ${source_default.white(title)}${draft}`);
|
|
6647
|
+
lines.push(` ${source_default.dim("↳")} ${source_default.dim(pr.detail)}`);
|
|
6648
|
+
} else {
|
|
6649
|
+
const ref = source_default.dim(`#${pr.number}`);
|
|
6650
|
+
lines.push(` ${arrow} ${ref} ${source_default.dim(title)}${draft}`);
|
|
6651
|
+
lines.push(` ${source_default.dim("↳")} ${source_default.dim(pr.detail)}`);
|
|
6652
|
+
}
|
|
6653
|
+
flatIndex++;
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
if (result.prs.length === 0) {
|
|
6657
|
+
lines.push("");
|
|
6658
|
+
lines.push(source_default.green(" All clear! No PRs need your attention."));
|
|
6659
|
+
}
|
|
6660
|
+
lines.push("");
|
|
6661
|
+
lines.push(source_default.dim(` ${"─".repeat(50)}`));
|
|
6662
|
+
lines.push("");
|
|
6663
|
+
lines.push(` ${source_default.dim("↑↓")} navigate ${source_default.white("r")} review ${source_default.white("o")} open ${source_default.white("n")} nudge ${source_default.white("c")} copy url ${source_default.white("q")} quit`);
|
|
6664
|
+
if (message) {
|
|
6665
|
+
lines.push("");
|
|
6666
|
+
lines.push(` ${message}`);
|
|
6667
|
+
}
|
|
6668
|
+
process.stdout.write(lines.join(`
|
|
6669
|
+
`));
|
|
6670
|
+
}
|
|
6671
|
+
async function interactiveMode(result, config) {
|
|
6672
|
+
if (result.prs.length === 0) {
|
|
6673
|
+
console.log(source_default.green(`
|
|
6674
|
+
All clear! No PRs need your attention.
|
|
6675
|
+
`));
|
|
6676
|
+
return;
|
|
6677
|
+
}
|
|
6678
|
+
let selectedIndex = 0;
|
|
6679
|
+
let message = "";
|
|
6680
|
+
const total = result.prs.length;
|
|
6681
|
+
if (!process.stdin.isTTY) {
|
|
6682
|
+
process.stdout.write(`Interactive mode requires a terminal. Use prq status instead.
|
|
6683
|
+
`);
|
|
6684
|
+
return;
|
|
6685
|
+
}
|
|
6686
|
+
process.stdin.setRawMode(true);
|
|
6687
|
+
process.stdin.resume();
|
|
6688
|
+
process.stdin.setEncoding("utf8");
|
|
6689
|
+
process.stdout.write("\x1B[?25l");
|
|
6690
|
+
render(result, selectedIndex, message);
|
|
6691
|
+
return new Promise((resolve) => {
|
|
6692
|
+
const cleanup = () => {
|
|
6693
|
+
process.stdin.setRawMode(false);
|
|
6694
|
+
process.stdin.pause();
|
|
6695
|
+
process.stdin.removeAllListeners("data");
|
|
6696
|
+
process.stdout.write("\x1B[?25h\x1B[2J\x1B[H");
|
|
6697
|
+
};
|
|
6698
|
+
process.stdin.on("data", async (key) => {
|
|
6699
|
+
const pr = result.prs[selectedIndex];
|
|
6700
|
+
switch (key) {
|
|
6701
|
+
case "q":
|
|
6702
|
+
case "\x03":
|
|
6703
|
+
cleanup();
|
|
6704
|
+
resolve();
|
|
6705
|
+
return;
|
|
6706
|
+
case "\x1B[A":
|
|
6707
|
+
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
6708
|
+
message = "";
|
|
6709
|
+
break;
|
|
6710
|
+
case "\x1B[B":
|
|
6711
|
+
selectedIndex = Math.min(total - 1, selectedIndex + 1);
|
|
6712
|
+
message = "";
|
|
6713
|
+
break;
|
|
6714
|
+
case "o": {
|
|
6715
|
+
const template = getAction("open", config);
|
|
6716
|
+
if (template) {
|
|
6717
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6718
|
+
message = source_default.dim(`opening ${pr.repo}#${pr.number}...`);
|
|
6719
|
+
render(result, selectedIndex, message);
|
|
6720
|
+
try {
|
|
6721
|
+
await executeCommand(cmd);
|
|
6722
|
+
message = source_default.green(`opened ${pr.repo}#${pr.number}`);
|
|
6723
|
+
} catch {
|
|
6724
|
+
message = source_default.red("failed to open");
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
break;
|
|
6728
|
+
}
|
|
6729
|
+
case "r": {
|
|
6730
|
+
const template = getAction("review", config);
|
|
6731
|
+
if (template) {
|
|
6732
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6733
|
+
message = source_default.dim(`opening review for ${pr.repo}#${pr.number}...`);
|
|
6734
|
+
render(result, selectedIndex, message);
|
|
6735
|
+
try {
|
|
6736
|
+
await executeCommand(cmd);
|
|
6737
|
+
message = source_default.green(`opened review for ${pr.repo}#${pr.number}`);
|
|
6738
|
+
} catch {
|
|
6739
|
+
message = source_default.red("failed to open review");
|
|
6740
|
+
}
|
|
6741
|
+
}
|
|
6742
|
+
break;
|
|
6743
|
+
}
|
|
6744
|
+
case "n": {
|
|
6745
|
+
const template = getAction("nudge", config);
|
|
6746
|
+
if (template) {
|
|
6747
|
+
const cmd = interpolate(template, buildContext(toResolvedPR(pr)));
|
|
6748
|
+
message = source_default.dim(`nudging ${pr.repo}#${pr.number}...`);
|
|
6749
|
+
render(result, selectedIndex, message);
|
|
6750
|
+
try {
|
|
6751
|
+
await executeCommand(cmd);
|
|
6752
|
+
message = source_default.green(`nudged ${pr.repo}#${pr.number}`);
|
|
6753
|
+
} catch {
|
|
6754
|
+
message = source_default.red("failed to nudge");
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
break;
|
|
6758
|
+
}
|
|
6759
|
+
case "c": {
|
|
6760
|
+
const url = pr.url;
|
|
6761
|
+
try {
|
|
6762
|
+
const proc = process.platform === "darwin" ? "pbcopy" : process.platform === "linux" ? "xclip -selection clipboard" : "clip";
|
|
6763
|
+
await executeCommand(`echo "${url}" | ${proc}`);
|
|
6764
|
+
message = source_default.green("url copied");
|
|
6765
|
+
} catch {
|
|
6766
|
+
message = source_default.dim(url);
|
|
6767
|
+
}
|
|
6768
|
+
break;
|
|
6769
|
+
}
|
|
6770
|
+
default:
|
|
6771
|
+
break;
|
|
6772
|
+
}
|
|
6773
|
+
render(result, selectedIndex, message);
|
|
6774
|
+
});
|
|
6775
|
+
});
|
|
6776
|
+
}
|
|
6777
|
+
|
|
6778
|
+
// src/output.ts
|
|
6779
|
+
var CATEGORY_CONFIG2 = {
|
|
6780
|
+
"needs-re-review": {
|
|
6781
|
+
icon: "◆",
|
|
6782
|
+
label: "Needs Re-review",
|
|
6783
|
+
color: source_default.yellow
|
|
6784
|
+
},
|
|
6785
|
+
requested: { icon: "●", label: "Requested Reviews", color: source_default.green },
|
|
6786
|
+
stale: { icon: "○", label: "Stale", color: source_default.red },
|
|
6787
|
+
"waiting-on-others": {
|
|
6788
|
+
icon: "◇",
|
|
6789
|
+
label: "Your PRs Waiting",
|
|
6790
|
+
color: source_default.dim
|
|
6791
|
+
}
|
|
6792
|
+
};
|
|
6793
|
+
var CATEGORY_ORDER2 = [
|
|
6794
|
+
"needs-re-review",
|
|
6795
|
+
"requested",
|
|
6796
|
+
"stale",
|
|
6797
|
+
"waiting-on-others"
|
|
6798
|
+
];
|
|
6576
6799
|
function formatPR(pr) {
|
|
6577
6800
|
const draft = pr.isDraft ? source_default.dim(" [draft]") : "";
|
|
6578
6801
|
const prRef = source_default.cyan(`${pr.repo}#${pr.number}`);
|
|
@@ -6596,12 +6819,12 @@ function formatStatus(result) {
|
|
|
6596
6819
|
grouped.set(pr.category, list);
|
|
6597
6820
|
}
|
|
6598
6821
|
let hasContent = false;
|
|
6599
|
-
for (const category of
|
|
6822
|
+
for (const category of CATEGORY_ORDER2) {
|
|
6600
6823
|
const prs = grouped.get(category);
|
|
6601
6824
|
if (!prs || prs.length === 0)
|
|
6602
6825
|
continue;
|
|
6603
6826
|
hasContent = true;
|
|
6604
|
-
const config =
|
|
6827
|
+
const config = CATEGORY_CONFIG2[category];
|
|
6605
6828
|
lines.push("");
|
|
6606
6829
|
lines.push(` ${config.color(`${config.icon} ${config.label}`)} ${source_default.dim(`(${prs.length})`)}`);
|
|
6607
6830
|
for (const pr of prs) {
|
|
@@ -6625,7 +6848,7 @@ function formatStatus(result) {
|
|
|
6625
6848
|
}
|
|
6626
6849
|
|
|
6627
6850
|
// src/commands/status.ts
|
|
6628
|
-
async function statusCommand(config, json) {
|
|
6851
|
+
async function statusCommand(config, json, interactive) {
|
|
6629
6852
|
const user = config.user ?? await getAuthenticatedUser();
|
|
6630
6853
|
process.stderr.write(`Fetching PRs for ${user}...
|
|
6631
6854
|
`);
|
|
@@ -6646,6 +6869,8 @@ async function statusCommand(config, json) {
|
|
|
6646
6869
|
if (json) {
|
|
6647
6870
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
6648
6871
|
`);
|
|
6872
|
+
} else if (interactive && process.stdin.isTTY) {
|
|
6873
|
+
await interactiveMode(result, config);
|
|
6649
6874
|
} else {
|
|
6650
6875
|
process.stdout.write(formatStatus(result));
|
|
6651
6876
|
}
|
|
@@ -10632,7 +10857,8 @@ var NEVER = INVALID;
|
|
|
10632
10857
|
var configSchema = exports_external.object({
|
|
10633
10858
|
repos: exports_external.array(exports_external.string()).default([]),
|
|
10634
10859
|
staleDays: exports_external.number().default(3),
|
|
10635
|
-
user: exports_external.string().optional()
|
|
10860
|
+
user: exports_external.string().optional(),
|
|
10861
|
+
actions: exports_external.record(exports_external.string()).default({})
|
|
10636
10862
|
});
|
|
10637
10863
|
function loadConfig(cliOverrides) {
|
|
10638
10864
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
@@ -10667,20 +10893,20 @@ function getVersion() {
|
|
|
10667
10893
|
function createCLI() {
|
|
10668
10894
|
const program2 = new Command;
|
|
10669
10895
|
program2.name("prq").description("PR Queue — see what code reviews need your attention").version(getVersion());
|
|
10670
|
-
program2.command("status", { isDefault: true }).description("Show PRs needing your attention").option("-r, --repos <repos...>", "Filter to specific repos (owner/name)").option("-s, --stale-days <days>", "Days of inactivity to consider stale", "3").option("--json", "Output as JSON").action(async (opts) => {
|
|
10896
|
+
program2.command("status", { isDefault: true }).description("Show PRs needing your attention").option("-r, --repos <repos...>", "Filter to specific repos (owner/name)").option("-s, --stale-days <days>", "Days of inactivity to consider stale", "3").option("--json", "Output as JSON").option("-i, --interactive", "Interactive mode with keyboard shortcuts").action(async (opts) => {
|
|
10671
10897
|
const config = loadConfig({
|
|
10672
10898
|
repos: opts.repos,
|
|
10673
10899
|
staleDays: opts.staleDays ? parseInt(opts.staleDays, 10) : undefined
|
|
10674
10900
|
});
|
|
10675
|
-
await statusCommand(config, opts.json ?? false);
|
|
10901
|
+
await statusCommand(config, opts.json ?? false, opts.interactive ?? false);
|
|
10676
10902
|
});
|
|
10677
10903
|
program2.command("open <identifier>").description("Open a PR in the browser").action(async (identifier) => {
|
|
10678
10904
|
const config = loadConfig({});
|
|
10679
|
-
await
|
|
10905
|
+
await runCommand("open", identifier, config);
|
|
10680
10906
|
});
|
|
10681
10907
|
program2.command("review <identifier>").description("Open PR files changed tab for review").action(async (identifier) => {
|
|
10682
10908
|
const config = loadConfig({});
|
|
10683
|
-
await
|
|
10909
|
+
await runCommand("review", identifier, config);
|
|
10684
10910
|
});
|
|
10685
10911
|
program2.command("nudge <identifier>").description("Post a nudge comment on a PR").option("-m, --message <msg>", "Custom nudge message").option("-y, --yes", "Skip confirmation").action(async (identifier, opts) => {
|
|
10686
10912
|
const config = loadConfig({});
|
|
@@ -10689,6 +10915,10 @@ function createCLI() {
|
|
|
10689
10915
|
yes: opts.yes ?? false
|
|
10690
10916
|
});
|
|
10691
10917
|
});
|
|
10918
|
+
program2.command("run <action> <identifier>").description("Run a custom action on a PR").action(async (action, identifier) => {
|
|
10919
|
+
const config = loadConfig({});
|
|
10920
|
+
await runCommand(action, identifier, config);
|
|
10921
|
+
});
|
|
10692
10922
|
program2.command("init").description("Create config file interactively").action(async () => {
|
|
10693
10923
|
await initCommand();
|
|
10694
10924
|
});
|