prr-kit 1.0.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/LICENSE +21 -0
- package/README.md +226 -0
- package/docs/assets/banner.svg +179 -0
- package/package.json +60 -0
- package/src/core/agents/prr-master.agent.yaml +80 -0
- package/src/core/module.yaml +19 -0
- package/src/core/tasks/help.md +37 -0
- package/src/core/tasks/workflow.xml +22 -0
- package/src/core/workflows/party-mode/steps/step-01-load-reviewers.md +68 -0
- package/src/core/workflows/party-mode/steps/step-02-discussion.md +125 -0
- package/src/core/workflows/party-mode/workflow.md +35 -0
- package/src/prr/agents/architecture-reviewer.agent.yaml +45 -0
- package/src/prr/agents/general-reviewer.agent.yaml +48 -0
- package/src/prr/agents/performance-reviewer.agent.yaml +45 -0
- package/src/prr/agents/security-reviewer.agent.yaml +43 -0
- package/src/prr/data/review-types.csv +39 -0
- package/src/prr/module.yaml +38 -0
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-01-scan-configs.md +106 -0
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-02-extract-rules.md +131 -0
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-03-ask-context.md +194 -0
- package/src/prr/workflows/0-setup/collect-project-context/steps/step-04-save-context.md +161 -0
- package/src/prr/workflows/0-setup/collect-project-context/workflow.md +58 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-01-fetch.md +68 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-02-list-branches.md +95 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-03-select.md +127 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-04-load-diff.md +79 -0
- package/src/prr/workflows/1-discover/select-pr/steps/step-05-confirm.md +76 -0
- package/src/prr/workflows/1-discover/select-pr/workflow.md +36 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-01-load-context.md +37 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-02-classify.md +50 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-03-walkthrough.md +41 -0
- package/src/prr/workflows/2-analyze/describe-pr/steps/step-04-output.md +50 -0
- package/src/prr/workflows/2-analyze/describe-pr/templates/pr-description.template.md +51 -0
- package/src/prr/workflows/2-analyze/describe-pr/workflow.md +28 -0
- package/src/prr/workflows/3-review/architecture-review/checklist.md +22 -0
- package/src/prr/workflows/3-review/architecture-review/instructions.xml +68 -0
- package/src/prr/workflows/3-review/architecture-review/workflow.yaml +18 -0
- package/src/prr/workflows/3-review/general-review/checklist.md +23 -0
- package/src/prr/workflows/3-review/general-review/instructions.xml +68 -0
- package/src/prr/workflows/3-review/general-review/workflow.yaml +18 -0
- package/src/prr/workflows/3-review/performance-review/checklist.md +22 -0
- package/src/prr/workflows/3-review/performance-review/instructions.xml +68 -0
- package/src/prr/workflows/3-review/performance-review/workflow.yaml +18 -0
- package/src/prr/workflows/3-review/security-review/checklist.md +25 -0
- package/src/prr/workflows/3-review/security-review/data/owasp-checklist.csv +19 -0
- package/src/prr/workflows/3-review/security-review/instructions.xml +70 -0
- package/src/prr/workflows/3-review/security-review/workflow.yaml +19 -0
- package/src/prr/workflows/4-improve/improve-code/checklist.md +18 -0
- package/src/prr/workflows/4-improve/improve-code/instructions.xml +59 -0
- package/src/prr/workflows/4-improve/improve-code/workflow.yaml +18 -0
- package/src/prr/workflows/5-ask/ask-code/steps/step-01-load-context.md +37 -0
- package/src/prr/workflows/5-ask/ask-code/steps/step-02-answer.md +36 -0
- package/src/prr/workflows/5-ask/ask-code/workflow.md +31 -0
- package/src/prr/workflows/6-report/generate-report/steps/step-01-collect.md +42 -0
- package/src/prr/workflows/6-report/generate-report/steps/step-02-organize.md +38 -0
- package/src/prr/workflows/6-report/generate-report/steps/step-03-write.md +44 -0
- package/src/prr/workflows/6-report/generate-report/templates/review-report.template.md +78 -0
- package/src/prr/workflows/6-report/generate-report/workflow.md +26 -0
- package/src/prr/workflows/6-report/post-comments/steps/step-01-format.md +166 -0
- package/src/prr/workflows/6-report/post-comments/steps/step-02-post.md +97 -0
- package/src/prr/workflows/6-report/post-comments/workflow.md +45 -0
- package/src/prr/workflows/quick/workflow.md +244 -0
- package/tools/cli/commands/install.js +66 -0
- package/tools/cli/commands/status.js +36 -0
- package/tools/cli/commands/uninstall.js +38 -0
- package/tools/cli/installers/lib/core/config-collector.js +47 -0
- package/tools/cli/installers/lib/core/detector.js +46 -0
- package/tools/cli/installers/lib/core/installer.js +162 -0
- package/tools/cli/installers/lib/core/manifest-generator.js +172 -0
- package/tools/cli/installers/lib/core/manifest.js +62 -0
- package/tools/cli/installers/lib/ide/_base-ide.js +36 -0
- package/tools/cli/installers/lib/ide/_config-driven.js +167 -0
- package/tools/cli/installers/lib/ide/manager.js +97 -0
- package/tools/cli/installers/lib/ide/platform-codes.yaml +76 -0
- package/tools/cli/installers/lib/ide/shared/path-utils.js +11 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +16 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +7 -0
- package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +5 -0
- package/tools/cli/lib/agent/compiler.js +123 -0
- package/tools/cli/lib/agent/template-engine.js +73 -0
- package/tools/cli/lib/cli-utils.js +32 -0
- package/tools/cli/lib/prompts.js +15 -0
- package/tools/cli/lib/ui.js +132 -0
- package/tools/cli/lib/xml-utils.js +24 -0
- package/tools/cli/prr-cli.js +36 -0
- package/tools/prr-npx-wrapper.js +6 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: quick
|
|
3
|
+
description: "Full PR review pipeline in one command: select → describe → review → report"
|
|
4
|
+
main_config: '{project-root}/_prr/prr/config.yaml'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Quick Review — Full Pipeline
|
|
8
|
+
|
|
9
|
+
**Goal:** Run the complete PR review pipeline end-to-end with minimal interruptions.
|
|
10
|
+
Only pause for user input when selecting the branch. Everything else runs automatically.
|
|
11
|
+
|
|
12
|
+
## INITIALIZATION
|
|
13
|
+
|
|
14
|
+
Load config from {main_config}: `user_name`, `communication_language`, `target_repo`, `review_output`, `project_context`.
|
|
15
|
+
|
|
16
|
+
Set `date` = today's date (YYYY-MM-DD).
|
|
17
|
+
|
|
18
|
+
If `project_context` file exists at `{review_output}/project-context.yaml`, load it silently.
|
|
19
|
+
If it does not exist, notify: "⚠️ No project context found — run [CP] Collect Project Context first for better reviews. Continuing without it."
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## PHASE 1 — SELECT PR
|
|
24
|
+
*Execute steps 1a–1b automatically. Pause only at 1c.*
|
|
25
|
+
|
|
26
|
+
### 1a. Fetch latest
|
|
27
|
+
```bash
|
|
28
|
+
git -C {target_repo} fetch origin --prune
|
|
29
|
+
```
|
|
30
|
+
Show: `✓ Fetched latest from remote`
|
|
31
|
+
|
|
32
|
+
### 1b. List open PRs (primary) + recent branches (secondary)
|
|
33
|
+
|
|
34
|
+
**Primary — GitHub PRs** (if `{github_repo}` is configured):
|
|
35
|
+
```bash
|
|
36
|
+
gh pr list --repo {github_repo} --state open \
|
|
37
|
+
--json number,title,headRefName,baseRefName,author,createdAt,isDraft --limit 20
|
|
38
|
+
```
|
|
39
|
+
Display as a table: `#N | title | head → base | author | age`
|
|
40
|
+
|
|
41
|
+
**Secondary — recent branches** (always):
|
|
42
|
+
```bash
|
|
43
|
+
git -C {target_repo} branch -r --sort=-committerdate \
|
|
44
|
+
--format="%(refname:short) | %(objectname:short) | %(committerdate:relative) | %(contents:subject)" \
|
|
45
|
+
| head -15
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 1c. Select PR ← **ONLY USER INPUT IN THIS WORKFLOW**
|
|
49
|
+
|
|
50
|
+
**If `{github_repo}` is configured** — ask:
|
|
51
|
+
> Select a PR to review:
|
|
52
|
+
> Enter PR number (e.g. `44`) or branch name (e.g. `feature/my-feature`):
|
|
53
|
+
|
|
54
|
+
Wait for response.
|
|
55
|
+
|
|
56
|
+
**If PR number entered:**
|
|
57
|
+
```bash
|
|
58
|
+
gh pr view {pr_number} --repo {github_repo} \
|
|
59
|
+
--json number,title,headRefName,baseRefName,author,headRefOid
|
|
60
|
+
```
|
|
61
|
+
Set `target_branch` = `headRefName`, `base_branch` = `baseRefName` ← **exact from GitHub, not assumed**.
|
|
62
|
+
Set `pr_head_sha` = `headRefOid`.
|
|
63
|
+
|
|
64
|
+
**If branch name entered:**
|
|
65
|
+
Check if a PR exists for it via `gh pr list --head {branch}`.
|
|
66
|
+
If yes: use PR's `baseRefName`. If no: detect `origin/main` or `origin/master`.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
**If `{github_repo}` is NOT configured** — ask two separate questions:
|
|
71
|
+
|
|
72
|
+
First, display EXACTLY:
|
|
73
|
+
```
|
|
74
|
+
🎯 Head branch (the branch to review)?
|
|
75
|
+
|
|
76
|
+
You can:
|
|
77
|
+
• Enter a number from the list (e.g., 1)
|
|
78
|
+
• Type the branch name directly (e.g., feature/my-feature)
|
|
79
|
+
```
|
|
80
|
+
Wait for response. Set `target_branch` = input.
|
|
81
|
+
|
|
82
|
+
Then display EXACTLY:
|
|
83
|
+
```
|
|
84
|
+
🎯 Base branch (what to diff against)?
|
|
85
|
+
|
|
86
|
+
You can:
|
|
87
|
+
• Press Enter to use the default [main]
|
|
88
|
+
• Type the branch name directly (e.g., develop)
|
|
89
|
+
```
|
|
90
|
+
Wait for response. If empty → detect `origin/main` or `origin/master`.
|
|
91
|
+
Set `base_branch` = input or detected default.
|
|
92
|
+
Set `diff_range` = `{base_branch}...{target_branch}`.
|
|
93
|
+
|
|
94
|
+
### 1d. Load diff
|
|
95
|
+
```bash
|
|
96
|
+
# If PR number available (preferred):
|
|
97
|
+
gh pr diff {pr_number} --repo {github_repo}
|
|
98
|
+
|
|
99
|
+
# Otherwise:
|
|
100
|
+
git -C {target_repo} diff {base_branch}...{target_branch} --stat
|
|
101
|
+
git -C {target_repo} diff {base_branch}...{target_branch}
|
|
102
|
+
```
|
|
103
|
+
Store diff in memory. Count files changed, lines added/removed.
|
|
104
|
+
|
|
105
|
+
### 1e. Save PR context
|
|
106
|
+
Write `{review_output}/current-pr-context.yaml`:
|
|
107
|
+
```yaml
|
|
108
|
+
target_branch: "{target_branch}"
|
|
109
|
+
base_branch: "{base_branch}"
|
|
110
|
+
date: "{date}"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Show summary:
|
|
114
|
+
```
|
|
115
|
+
✓ PR selected: {target_branch}
|
|
116
|
+
Files changed: X | +Y / -Z lines
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## PHASE 2 — DESCRIBE PR
|
|
122
|
+
*Execute automatically, no user input.*
|
|
123
|
+
|
|
124
|
+
### 2a. Classify PR type
|
|
125
|
+
Analyze the diff and classify as one of: `bugfix` | `feature` | `refactor` | `docs` | `test` | `chore` | `hotfix`
|
|
126
|
+
|
|
127
|
+
### 2b. Generate walkthrough
|
|
128
|
+
For each changed file, write a 1-2 sentence summary of what changed and why.
|
|
129
|
+
Group by: new files | modified files | deleted files | renamed files.
|
|
130
|
+
|
|
131
|
+
### 2c. Output description
|
|
132
|
+
Print to screen:
|
|
133
|
+
```
|
|
134
|
+
## PR Description
|
|
135
|
+
|
|
136
|
+
**Type:** {pr_type}
|
|
137
|
+
**Branch:** {target_branch}
|
|
138
|
+
**Summary:** {2-3 sentence overall summary}
|
|
139
|
+
|
|
140
|
+
### Files Changed
|
|
141
|
+
{walkthrough table}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## PHASE 3 — REVIEW
|
|
147
|
+
*Execute all review types automatically, one by one.*
|
|
148
|
+
|
|
149
|
+
For each review, read the corresponding instructions file and apply it to `{pr_diff}`.
|
|
150
|
+
|
|
151
|
+
### 3a. General Review
|
|
152
|
+
Load and follow: `{project-root}/_prr/prr/workflows/3-review/general-review/instructions.xml`
|
|
153
|
+
|
|
154
|
+
Collect findings as `{general_findings}`.
|
|
155
|
+
Print section header: `## 👁️ General Review`
|
|
156
|
+
|
|
157
|
+
### 3b. Security Review
|
|
158
|
+
Load and follow: `{project-root}/_prr/prr/workflows/3-review/security-review/instructions.xml`
|
|
159
|
+
|
|
160
|
+
Collect findings as `{security_findings}`.
|
|
161
|
+
Print section header: `## 🔒 Security Review`
|
|
162
|
+
|
|
163
|
+
### 3c. Performance Review
|
|
164
|
+
Load and follow: `{project-root}/_prr/prr/workflows/3-review/performance-review/instructions.xml`
|
|
165
|
+
|
|
166
|
+
Collect findings as `{performance_findings}`.
|
|
167
|
+
Print section header: `## ⚡ Performance Review`
|
|
168
|
+
|
|
169
|
+
### 3d. Architecture Review
|
|
170
|
+
Load and follow: `{project-root}/_prr/prr/workflows/3-review/architecture-review/instructions.xml`
|
|
171
|
+
|
|
172
|
+
Collect findings as `{architecture_findings}`.
|
|
173
|
+
Print section header: `## 🏗️ Architecture Review`
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## PHASE 4 — GENERATE REPORT
|
|
178
|
+
*Execute automatically.*
|
|
179
|
+
|
|
180
|
+
Compile all findings from phases 3a–3d.
|
|
181
|
+
|
|
182
|
+
Sort by severity: 🔴 Blockers first → 🟡 Warnings → 🟢 Suggestions → 📌 Questions.
|
|
183
|
+
|
|
184
|
+
Count totals:
|
|
185
|
+
- `{blocker_count}` = number of 🔴 findings
|
|
186
|
+
- `{warning_count}` = number of 🟡 findings
|
|
187
|
+
- `{suggestion_count}` = number of 🟢 findings
|
|
188
|
+
|
|
189
|
+
Generate report filename: `review-{target_branch_slug}-{date}.md`
|
|
190
|
+
where `{target_branch_slug}` = branch name with `/` replaced by `-`.
|
|
191
|
+
|
|
192
|
+
Write report to: `{review_output}/review-{target_branch_slug}-{date}.md`
|
|
193
|
+
|
|
194
|
+
Report format:
|
|
195
|
+
```markdown
|
|
196
|
+
# PR Review: {target_branch}
|
|
197
|
+
**Date:** {date} | **Reviewer:** AI Review Framework
|
|
198
|
+
**Type:** {pr_type} | **Files:** X | **Lines:** +Y/-Z
|
|
199
|
+
|
|
200
|
+
## Executive Summary
|
|
201
|
+
{2-3 sentence overall quality assessment}
|
|
202
|
+
|
|
203
|
+
**Totals:** 🔴 {blocker_count} blockers | 🟡 {warning_count} warnings | 🟢 {suggestion_count} suggestions
|
|
204
|
+
|
|
205
|
+
## Blockers 🔴
|
|
206
|
+
{all blocker findings}
|
|
207
|
+
|
|
208
|
+
## Warnings 🟡
|
|
209
|
+
{all warning findings}
|
|
210
|
+
|
|
211
|
+
## Suggestions 🟢
|
|
212
|
+
{all suggestion findings}
|
|
213
|
+
|
|
214
|
+
## Questions 📌
|
|
215
|
+
{all questions}
|
|
216
|
+
|
|
217
|
+
## Files Reviewed
|
|
218
|
+
{file list}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## PHASE 5 — DONE
|
|
224
|
+
|
|
225
|
+
Print completion summary:
|
|
226
|
+
```
|
|
227
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
228
|
+
✅ Quick Review Complete
|
|
229
|
+
|
|
230
|
+
Branch: {target_branch}
|
|
231
|
+
Report: {review_output}/review-{target_branch_slug}-{date}.md
|
|
232
|
+
|
|
233
|
+
🔴 Blockers: {blocker_count}
|
|
234
|
+
🟡 Warnings: {warning_count}
|
|
235
|
+
🟢 Suggestions: {suggestion_count}
|
|
236
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Then ask:
|
|
240
|
+
> Post these findings as inline comments to GitHub? (requires `gh` CLI and `github_repo` configured)
|
|
241
|
+
> Type **PC** to post, or **Enter** to finish.
|
|
242
|
+
|
|
243
|
+
If user types `PC`, load and follow: `{project-root}/_prr/prr/workflows/6-report/post-comments/workflow.md`
|
|
244
|
+
Otherwise, end session.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const { Installer } = require('../installers/lib/core/installer');
|
|
2
|
+
const { UI } = require('../lib/ui');
|
|
3
|
+
const prompts = require('../lib/prompts');
|
|
4
|
+
|
|
5
|
+
const installer = new Installer();
|
|
6
|
+
const ui = new UI();
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
command: 'install',
|
|
10
|
+
description: 'Install PR Review agents and workflows into your project',
|
|
11
|
+
options: [
|
|
12
|
+
['-d, --debug', 'Enable debug output for manifest generation'],
|
|
13
|
+
['--directory <path>', 'Installation directory (default: current directory)'],
|
|
14
|
+
['--modules <modules>', 'Comma-separated list of module IDs to install (e.g., "prr")'],
|
|
15
|
+
['--tools <tools>', 'Comma-separated list of IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip.'],
|
|
16
|
+
['--action <type>', 'Action type: install, update, or quick-update'],
|
|
17
|
+
['--user-name <name>', 'Name for agents to use'],
|
|
18
|
+
['--communication-language <lang>', 'Language for agent communication (default: English)'],
|
|
19
|
+
['--output-folder <path>', 'Output folder relative to project root (default: _prr-output)'],
|
|
20
|
+
['--target-repo <path>', 'Path to git repository to review (default: .)'],
|
|
21
|
+
['--github-repo <owner/repo>', 'GitHub repository for posting comments (optional)'],
|
|
22
|
+
['-y, --yes', 'Accept all defaults and skip prompts'],
|
|
23
|
+
],
|
|
24
|
+
action: async (options) => {
|
|
25
|
+
try {
|
|
26
|
+
if (options.debug) {
|
|
27
|
+
process.env.PRR_DEBUG_MANIFEST = 'true';
|
|
28
|
+
await prompts.log.info('Debug mode enabled');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const config = await ui.promptInstall(options);
|
|
32
|
+
|
|
33
|
+
if (config.actionType === 'cancel') {
|
|
34
|
+
await prompts.log.warn('Installation cancelled.');
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (config.actionType === 'quick-update') {
|
|
39
|
+
const result = await installer.quickUpdate(config);
|
|
40
|
+
await prompts.log.success('Quick update complete!');
|
|
41
|
+
await prompts.log.info(`Updated ${result.moduleCount} modules (${result.modules.join(', ')})`);
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await installer.install(config);
|
|
46
|
+
|
|
47
|
+
if (result && result.cancelled) {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (result && result.success) {
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error.fullMessage) {
|
|
56
|
+
await prompts.log.error(error.fullMessage);
|
|
57
|
+
} else {
|
|
58
|
+
await prompts.log.error(`Installation failed: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
if (error.stack && process.env.PRR_DEBUG_MANIFEST) {
|
|
61
|
+
await prompts.log.message(error.stack);
|
|
62
|
+
}
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { Installer } = require('../installers/lib/core/installer');
|
|
2
|
+
const prompts = require('../lib/prompts');
|
|
3
|
+
|
|
4
|
+
const installer = new Installer();
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
command: 'status',
|
|
8
|
+
description: 'Show current PR Review installation status',
|
|
9
|
+
options: [
|
|
10
|
+
['--directory <path>', 'Project directory (default: current directory)'],
|
|
11
|
+
],
|
|
12
|
+
action: async (options) => {
|
|
13
|
+
try {
|
|
14
|
+
const projectDir = options.directory
|
|
15
|
+
? require('node:path').resolve(options.directory)
|
|
16
|
+
: process.cwd();
|
|
17
|
+
|
|
18
|
+
const status = await installer.status(projectDir);
|
|
19
|
+
|
|
20
|
+
if (!status.installed) {
|
|
21
|
+
await prompts.log.warn('PR Review is not installed in this directory.');
|
|
22
|
+
await prompts.log.info('Run `npx pr-review install` to get started.');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await prompts.log.success(`PR Review v${status.version} installed`);
|
|
27
|
+
await prompts.log.info(`Modules: ${status.modules.join(', ')}`);
|
|
28
|
+
await prompts.log.info(`IDEs configured: ${status.ides.join(', ') || 'none'}`);
|
|
29
|
+
await prompts.log.info(`Install date: ${status.installDate}`);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await prompts.log.error(`Status check failed: ${error.message}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { Installer } = require('../installers/lib/core/installer');
|
|
2
|
+
const prompts = require('../lib/prompts');
|
|
3
|
+
|
|
4
|
+
const installer = new Installer();
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
command: 'uninstall',
|
|
8
|
+
description: 'Remove PR Review installation from a project',
|
|
9
|
+
options: [
|
|
10
|
+
['--directory <path>', 'Project directory (default: current directory)'],
|
|
11
|
+
['-y, --yes', 'Skip confirmation prompt'],
|
|
12
|
+
],
|
|
13
|
+
action: async (options) => {
|
|
14
|
+
try {
|
|
15
|
+
const projectDir = options.directory
|
|
16
|
+
? require('node:path').resolve(options.directory)
|
|
17
|
+
: process.cwd();
|
|
18
|
+
|
|
19
|
+
if (!options.yes) {
|
|
20
|
+
const { confirm } = require('@clack/prompts');
|
|
21
|
+
const ok = await confirm({
|
|
22
|
+
message: `Remove PR Review from ${projectDir}?`,
|
|
23
|
+
});
|
|
24
|
+
if (!ok) {
|
|
25
|
+
await prompts.log.warn('Uninstall cancelled.');
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
await installer.uninstall(projectDir);
|
|
31
|
+
await prompts.log.success('PR Review uninstalled successfully.');
|
|
32
|
+
process.exit(0);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
await prompts.log.error(`Uninstall failed: ${error.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Collector — writes config.yaml files for each module
|
|
3
|
+
*/
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const yaml = require('yaml');
|
|
7
|
+
|
|
8
|
+
class ConfigCollector {
|
|
9
|
+
/**
|
|
10
|
+
* Write module config files based on user answers
|
|
11
|
+
* @param {string} prrDir - _prr directory path
|
|
12
|
+
* @param {Object} config - User-provided config values
|
|
13
|
+
*/
|
|
14
|
+
async writeConfigs(prrDir, config) {
|
|
15
|
+
// Core config
|
|
16
|
+
const coreConfigPath = path.join(prrDir, 'core', 'config.yaml');
|
|
17
|
+
await fs.ensureDir(path.dirname(coreConfigPath));
|
|
18
|
+
|
|
19
|
+
const coreConfig = {
|
|
20
|
+
user_name: config.userName || 'Reviewer',
|
|
21
|
+
communication_language: config.communicationLanguage || 'English',
|
|
22
|
+
output_folder: config.outputFolder || '_prr-output',
|
|
23
|
+
};
|
|
24
|
+
await fs.writeFile(coreConfigPath, yaml.stringify(coreConfig, { indent: 2 }), 'utf8');
|
|
25
|
+
|
|
26
|
+
// PRR module config
|
|
27
|
+
if (config.selectedModules && config.selectedModules.includes('prr')) {
|
|
28
|
+
const prrConfigPath = path.join(prrDir, 'prr', 'config.yaml');
|
|
29
|
+
await fs.ensureDir(path.dirname(prrConfigPath));
|
|
30
|
+
|
|
31
|
+
const outputFolderAbs = path.join(config.projectDir, config.outputFolder || '_prr-output');
|
|
32
|
+
const reviewOutput = path.join(outputFolderAbs, 'reviews').replaceAll('\\', '/');
|
|
33
|
+
|
|
34
|
+
const prrConfig = {
|
|
35
|
+
...coreConfig,
|
|
36
|
+
project_name: config.projectName || path.basename(config.projectDir),
|
|
37
|
+
target_repo: config.targetRepo || '.',
|
|
38
|
+
platform: config.platform || 'auto',
|
|
39
|
+
platform_repo: config.platformRepo || config.githubRepo || '',
|
|
40
|
+
review_output: reviewOutput,
|
|
41
|
+
};
|
|
42
|
+
await fs.writeFile(prrConfigPath, yaml.stringify(prrConfig, { indent: 2 }), 'utf8');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { ConfigCollector };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detector — detects existing PR Review installations
|
|
3
|
+
*/
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const yaml = require('yaml');
|
|
7
|
+
|
|
8
|
+
const PRR_FOLDER_NAME = '_prr';
|
|
9
|
+
|
|
10
|
+
class Detector {
|
|
11
|
+
/**
|
|
12
|
+
* Check if PR Review is installed in the given directory
|
|
13
|
+
* @param {string} projectDir
|
|
14
|
+
* @returns {Object} { installed, version, modules, prrDir }
|
|
15
|
+
*/
|
|
16
|
+
async detect(projectDir) {
|
|
17
|
+
const prrDir = path.join(projectDir, PRR_FOLDER_NAME);
|
|
18
|
+
|
|
19
|
+
if (!(await fs.pathExists(prrDir))) {
|
|
20
|
+
return { installed: false, prrDir };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const manifestPath = path.join(prrDir, '_config', 'manifest.yaml');
|
|
24
|
+
if (!(await fs.pathExists(manifestPath))) {
|
|
25
|
+
return { installed: false, prrDir };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const content = await fs.readFile(manifestPath, 'utf8');
|
|
30
|
+
const data = yaml.parse(content);
|
|
31
|
+
const modules = (data.modules || []).map((m) => (typeof m === 'string' ? m : m.name));
|
|
32
|
+
return {
|
|
33
|
+
installed: true,
|
|
34
|
+
version: data.installation?.version || 'unknown',
|
|
35
|
+
installDate: data.installation?.installDate,
|
|
36
|
+
modules,
|
|
37
|
+
ides: data.ides || [],
|
|
38
|
+
prrDir,
|
|
39
|
+
};
|
|
40
|
+
} catch {
|
|
41
|
+
return { installed: false, prrDir };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { Detector, PRR_FOLDER_NAME };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer — main installation orchestrator
|
|
3
|
+
* Main installation orchestrator for PR Review Framework
|
|
4
|
+
*/
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const { Manifest } = require('./manifest');
|
|
8
|
+
const { ManifestGenerator } = require('./manifest-generator');
|
|
9
|
+
const { ConfigCollector } = require('./config-collector');
|
|
10
|
+
const { Detector, PRR_FOLDER_NAME } = require('./detector');
|
|
11
|
+
const { IdeManager } = require('../ide/manager');
|
|
12
|
+
const { compileAgent } = require('../../../lib/agent/compiler');
|
|
13
|
+
const prompts = require('../../../lib/prompts');
|
|
14
|
+
const packageJson = require('../../../../../package.json');
|
|
15
|
+
|
|
16
|
+
const SRC_DIR = path.join(__dirname, '..', '..', '..', '..', '..', 'src');
|
|
17
|
+
const TEXT_EXTENSIONS = new Set(['.md', '.yaml', '.yml', '.txt', '.json', '.js', '.csv', '.xml', '.toml']);
|
|
18
|
+
|
|
19
|
+
class Installer {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.manifest = new Manifest();
|
|
22
|
+
this.manifestGenerator = new ManifestGenerator();
|
|
23
|
+
this.configCollector = new ConfigCollector();
|
|
24
|
+
this.detector = new Detector();
|
|
25
|
+
this.ideManager = new IdeManager();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async install(config) {
|
|
29
|
+
const { projectDir, selectedModules = ['prr'], selectedIdes = [], actionType = 'install' } = config;
|
|
30
|
+
|
|
31
|
+
await this.ideManager.ensureInitialized();
|
|
32
|
+
|
|
33
|
+
const prrDir = path.join(projectDir, PRR_FOLDER_NAME);
|
|
34
|
+
await fs.ensureDir(prrDir);
|
|
35
|
+
|
|
36
|
+
await prompts.log.step(`Installing PR Review to ${projectDir}...`);
|
|
37
|
+
|
|
38
|
+
// Copy module files from src/
|
|
39
|
+
const modulesToInstall = ['core', ...selectedModules.filter((m) => m !== 'core')];
|
|
40
|
+
for (const mod of modulesToInstall) {
|
|
41
|
+
const srcModDir = path.join(SRC_DIR, mod === 'core' ? 'core' : mod);
|
|
42
|
+
const dstModDir = path.join(prrDir, mod);
|
|
43
|
+
|
|
44
|
+
if (!(await fs.pathExists(srcModDir))) {
|
|
45
|
+
await prompts.log.warn(`Module '${mod}' not found in src/ — skipping`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await fs.remove(dstModDir);
|
|
50
|
+
await this.copyModuleFiles(srcModDir, dstModDir);
|
|
51
|
+
await prompts.log.info(` ✓ Module '${mod}' installed`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Compile agents (YAML → XML/MD)
|
|
55
|
+
await this.compileModuleAgents(prrDir, modulesToInstall);
|
|
56
|
+
|
|
57
|
+
// Write config files
|
|
58
|
+
await this.configCollector.writeConfigs(prrDir, config);
|
|
59
|
+
await prompts.log.info(' ✓ Configuration written');
|
|
60
|
+
|
|
61
|
+
// Create output directories
|
|
62
|
+
const outputFolderAbs = path.join(projectDir, config.outputFolder || '_prr-output');
|
|
63
|
+
await fs.ensureDir(path.join(outputFolderAbs, 'reviews'));
|
|
64
|
+
await prompts.log.info(' ✓ Output directories created');
|
|
65
|
+
|
|
66
|
+
// Generate manifests
|
|
67
|
+
const cfgDir = path.join(prrDir, '_config');
|
|
68
|
+
await fs.ensureDir(cfgDir);
|
|
69
|
+
await this.manifestGenerator.generateManifests(prrDir, selectedModules);
|
|
70
|
+
await this.manifest.create(prrDir, {
|
|
71
|
+
version: packageJson.version,
|
|
72
|
+
modules: modulesToInstall,
|
|
73
|
+
ides: selectedIdes,
|
|
74
|
+
});
|
|
75
|
+
await prompts.log.info(' ✓ Manifests generated');
|
|
76
|
+
|
|
77
|
+
// Configure IDEs
|
|
78
|
+
for (const ide of selectedIdes) {
|
|
79
|
+
const result = await this.ideManager.setup(ide, projectDir, prrDir, { selectedModules: modulesToInstall });
|
|
80
|
+
if (result.success) {
|
|
81
|
+
await prompts.log.info(` ✓ ${ide} configured (${result.detail || 'done'})`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await prompts.log.success('PR Review installed successfully!');
|
|
86
|
+
await prompts.log.info('Open your AI IDE and use the reviewer agents to start reviewing PRs.');
|
|
87
|
+
|
|
88
|
+
return { success: true };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async copyModuleFiles(srcDir, dstDir) {
|
|
92
|
+
await fs.ensureDir(dstDir);
|
|
93
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
94
|
+
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
97
|
+
const dstPath = path.join(dstDir, entry.name);
|
|
98
|
+
|
|
99
|
+
if (entry.isDirectory()) {
|
|
100
|
+
await this.copyModuleFiles(srcPath, dstPath);
|
|
101
|
+
} else {
|
|
102
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
103
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
104
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
105
|
+
await fs.ensureDir(path.dirname(dstPath));
|
|
106
|
+
await fs.writeFile(dstPath, content, 'utf8');
|
|
107
|
+
} else {
|
|
108
|
+
await fs.copy(srcPath, dstPath, { overwrite: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async compileModuleAgents(prrDir, modules) {
|
|
115
|
+
for (const mod of modules) {
|
|
116
|
+
const agentsYamlDir = path.join(prrDir, mod, 'agents');
|
|
117
|
+
if (!(await fs.pathExists(agentsYamlDir))) continue;
|
|
118
|
+
|
|
119
|
+
const files = await fs.readdir(agentsYamlDir);
|
|
120
|
+
for (const file of files) {
|
|
121
|
+
if (!file.endsWith('.agent.yaml')) continue;
|
|
122
|
+
|
|
123
|
+
const yamlPath = path.join(agentsYamlDir, file);
|
|
124
|
+
const baseName = file.replace('.agent.yaml', '');
|
|
125
|
+
const outputPath = path.join(agentsYamlDir, `${baseName}.md`);
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const yamlContent = await fs.readFile(yamlPath, 'utf8');
|
|
129
|
+
const { xml } = await compileAgent(yamlContent, {}, baseName, `_prr/${mod}/agents/${baseName}.md`);
|
|
130
|
+
await fs.writeFile(outputPath, xml, 'utf8');
|
|
131
|
+
} catch (err) {
|
|
132
|
+
await prompts.log.warn(` Warning: Could not compile agent ${file}: ${err.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async uninstall(projectDir) {
|
|
139
|
+
const prrDir = path.join(projectDir, PRR_FOLDER_NAME);
|
|
140
|
+
if (await fs.pathExists(prrDir)) {
|
|
141
|
+
await fs.remove(prrDir);
|
|
142
|
+
}
|
|
143
|
+
// Also clean IDE configurations
|
|
144
|
+
await this.ideManager.ensureInitialized();
|
|
145
|
+
await this.ideManager.cleanup(projectDir);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async quickUpdate(config) {
|
|
149
|
+
const { projectDir } = config;
|
|
150
|
+
const existing = await this.detector.detect(projectDir);
|
|
151
|
+
if (!existing.installed) {
|
|
152
|
+
throw new Error('PR Review is not installed in this directory.');
|
|
153
|
+
}
|
|
154
|
+
return this.install({ ...config, selectedModules: existing.modules, selectedIdes: existing.ides });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async status(projectDir) {
|
|
158
|
+
return this.detector.detect(projectDir);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = { Installer };
|