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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/docs/assets/banner.svg +179 -0
  4. package/package.json +60 -0
  5. package/src/core/agents/prr-master.agent.yaml +80 -0
  6. package/src/core/module.yaml +19 -0
  7. package/src/core/tasks/help.md +37 -0
  8. package/src/core/tasks/workflow.xml +22 -0
  9. package/src/core/workflows/party-mode/steps/step-01-load-reviewers.md +68 -0
  10. package/src/core/workflows/party-mode/steps/step-02-discussion.md +125 -0
  11. package/src/core/workflows/party-mode/workflow.md +35 -0
  12. package/src/prr/agents/architecture-reviewer.agent.yaml +45 -0
  13. package/src/prr/agents/general-reviewer.agent.yaml +48 -0
  14. package/src/prr/agents/performance-reviewer.agent.yaml +45 -0
  15. package/src/prr/agents/security-reviewer.agent.yaml +43 -0
  16. package/src/prr/data/review-types.csv +39 -0
  17. package/src/prr/module.yaml +38 -0
  18. package/src/prr/workflows/0-setup/collect-project-context/steps/step-01-scan-configs.md +106 -0
  19. package/src/prr/workflows/0-setup/collect-project-context/steps/step-02-extract-rules.md +131 -0
  20. package/src/prr/workflows/0-setup/collect-project-context/steps/step-03-ask-context.md +194 -0
  21. package/src/prr/workflows/0-setup/collect-project-context/steps/step-04-save-context.md +161 -0
  22. package/src/prr/workflows/0-setup/collect-project-context/workflow.md +58 -0
  23. package/src/prr/workflows/1-discover/select-pr/steps/step-01-fetch.md +68 -0
  24. package/src/prr/workflows/1-discover/select-pr/steps/step-02-list-branches.md +95 -0
  25. package/src/prr/workflows/1-discover/select-pr/steps/step-03-select.md +127 -0
  26. package/src/prr/workflows/1-discover/select-pr/steps/step-04-load-diff.md +79 -0
  27. package/src/prr/workflows/1-discover/select-pr/steps/step-05-confirm.md +76 -0
  28. package/src/prr/workflows/1-discover/select-pr/workflow.md +36 -0
  29. package/src/prr/workflows/2-analyze/describe-pr/steps/step-01-load-context.md +37 -0
  30. package/src/prr/workflows/2-analyze/describe-pr/steps/step-02-classify.md +50 -0
  31. package/src/prr/workflows/2-analyze/describe-pr/steps/step-03-walkthrough.md +41 -0
  32. package/src/prr/workflows/2-analyze/describe-pr/steps/step-04-output.md +50 -0
  33. package/src/prr/workflows/2-analyze/describe-pr/templates/pr-description.template.md +51 -0
  34. package/src/prr/workflows/2-analyze/describe-pr/workflow.md +28 -0
  35. package/src/prr/workflows/3-review/architecture-review/checklist.md +22 -0
  36. package/src/prr/workflows/3-review/architecture-review/instructions.xml +68 -0
  37. package/src/prr/workflows/3-review/architecture-review/workflow.yaml +18 -0
  38. package/src/prr/workflows/3-review/general-review/checklist.md +23 -0
  39. package/src/prr/workflows/3-review/general-review/instructions.xml +68 -0
  40. package/src/prr/workflows/3-review/general-review/workflow.yaml +18 -0
  41. package/src/prr/workflows/3-review/performance-review/checklist.md +22 -0
  42. package/src/prr/workflows/3-review/performance-review/instructions.xml +68 -0
  43. package/src/prr/workflows/3-review/performance-review/workflow.yaml +18 -0
  44. package/src/prr/workflows/3-review/security-review/checklist.md +25 -0
  45. package/src/prr/workflows/3-review/security-review/data/owasp-checklist.csv +19 -0
  46. package/src/prr/workflows/3-review/security-review/instructions.xml +70 -0
  47. package/src/prr/workflows/3-review/security-review/workflow.yaml +19 -0
  48. package/src/prr/workflows/4-improve/improve-code/checklist.md +18 -0
  49. package/src/prr/workflows/4-improve/improve-code/instructions.xml +59 -0
  50. package/src/prr/workflows/4-improve/improve-code/workflow.yaml +18 -0
  51. package/src/prr/workflows/5-ask/ask-code/steps/step-01-load-context.md +37 -0
  52. package/src/prr/workflows/5-ask/ask-code/steps/step-02-answer.md +36 -0
  53. package/src/prr/workflows/5-ask/ask-code/workflow.md +31 -0
  54. package/src/prr/workflows/6-report/generate-report/steps/step-01-collect.md +42 -0
  55. package/src/prr/workflows/6-report/generate-report/steps/step-02-organize.md +38 -0
  56. package/src/prr/workflows/6-report/generate-report/steps/step-03-write.md +44 -0
  57. package/src/prr/workflows/6-report/generate-report/templates/review-report.template.md +78 -0
  58. package/src/prr/workflows/6-report/generate-report/workflow.md +26 -0
  59. package/src/prr/workflows/6-report/post-comments/steps/step-01-format.md +166 -0
  60. package/src/prr/workflows/6-report/post-comments/steps/step-02-post.md +97 -0
  61. package/src/prr/workflows/6-report/post-comments/workflow.md +45 -0
  62. package/src/prr/workflows/quick/workflow.md +244 -0
  63. package/tools/cli/commands/install.js +66 -0
  64. package/tools/cli/commands/status.js +36 -0
  65. package/tools/cli/commands/uninstall.js +38 -0
  66. package/tools/cli/installers/lib/core/config-collector.js +47 -0
  67. package/tools/cli/installers/lib/core/detector.js +46 -0
  68. package/tools/cli/installers/lib/core/installer.js +162 -0
  69. package/tools/cli/installers/lib/core/manifest-generator.js +172 -0
  70. package/tools/cli/installers/lib/core/manifest.js +62 -0
  71. package/tools/cli/installers/lib/ide/_base-ide.js +36 -0
  72. package/tools/cli/installers/lib/ide/_config-driven.js +167 -0
  73. package/tools/cli/installers/lib/ide/manager.js +97 -0
  74. package/tools/cli/installers/lib/ide/platform-codes.yaml +76 -0
  75. package/tools/cli/installers/lib/ide/shared/path-utils.js +11 -0
  76. package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +16 -0
  77. package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +7 -0
  78. package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +5 -0
  79. package/tools/cli/lib/agent/compiler.js +123 -0
  80. package/tools/cli/lib/agent/template-engine.js +73 -0
  81. package/tools/cli/lib/cli-utils.js +32 -0
  82. package/tools/cli/lib/prompts.js +15 -0
  83. package/tools/cli/lib/ui.js +132 -0
  84. package/tools/cli/lib/xml-utils.js +24 -0
  85. package/tools/cli/prr-cli.js +36 -0
  86. package/tools/prr-npx-wrapper.js +6 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Agent Compiler — transforms agent YAML to compiled XML/Markdown format
3
+ * Compiles agent YAML files to XML/Markdown launcher format
4
+ */
5
+
6
+ const yaml = require('yaml');
7
+ const { processAgentYaml, extractInstallConfig, stripInstallConfig, getDefaultValues } = require('./template-engine');
8
+ const { escapeXml } = require('../xml-utils');
9
+
10
+ function buildFrontmatter(metadata, agentName) {
11
+ const name = agentName || metadata.name || 'agent';
12
+ const description = metadata.title || 'PR Review Agent';
13
+ const noLauncher = metadata.no_launcher ? '\nno-launcher: true' : '';
14
+ return `---\nname: "${name.replaceAll('-', ' ')}"\ndescription: "${description}"${noLauncher}\n---\n\nYou must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.\n\n`;
15
+ }
16
+
17
+ function buildPersonaXml(persona) {
18
+ if (!persona) return '';
19
+ let xml = ' <persona>\n';
20
+ if (persona.role) xml += ` <role>${escapeXml(persona.role.trim().replaceAll(/\s+/g, ' '))}</role>\n`;
21
+ if (persona.identity) xml += ` <identity>${escapeXml(persona.identity.trim().replaceAll(/\s+/g, ' '))}</identity>\n`;
22
+ if (persona.communication_style) xml += ` <communication_style>${escapeXml(persona.communication_style.trim().replaceAll(/\s+/g, ' '))}</communication_style>\n`;
23
+ if (persona.principles) {
24
+ const text = Array.isArray(persona.principles)
25
+ ? persona.principles.join(' ')
26
+ : persona.principles.trim().replaceAll(/\n+/g, ' ');
27
+ xml += ` <principles>${escapeXml(text)}</principles>\n`;
28
+ }
29
+ xml += ' </persona>\n';
30
+ return xml;
31
+ }
32
+
33
+ function buildMemoriesXml(memories) {
34
+ if (!memories || memories.length === 0) return '';
35
+ let xml = ' <memories>\n';
36
+ for (const m of memories) xml += ` <memory>${escapeXml(String(m))}</memory>\n`;
37
+ xml += ' </memories>\n';
38
+ return xml;
39
+ }
40
+
41
+ function buildMenuXml(menuItems) {
42
+ let xml = ' <menu>\n';
43
+ xml += ` <item cmd="MH or fuzzy match on menu or help">[MH] Redisplay Menu Help</item>\n`;
44
+ xml += ` <item cmd="CH or fuzzy match on chat">[CH] Chat with the Reviewer about anything</item>\n`;
45
+
46
+ if (menuItems && menuItems.length > 0) {
47
+ for (const item of menuItems) {
48
+ if (!item.trigger) continue;
49
+ const attrs = [`cmd="${item.trigger}"`];
50
+ if (item.workflow) attrs.push(`workflow="${item.workflow}"`);
51
+ if (item.exec) attrs.push(`exec="${item.exec}"`);
52
+ if (item.action) attrs.push(`action="${item.action}"`);
53
+ xml += ` <item ${attrs.join(' ')}>${escapeXml(item.description || '')}</item>\n`;
54
+ }
55
+ }
56
+
57
+ xml += ` <item cmd="PM or fuzzy match on party-mode" exec="{project-root}/_prr/core/workflows/party-mode/workflow.md">[PM] Start Party Mode (multi-reviewer discussion)</item>\n`;
58
+ xml += ` <item cmd="DA or fuzzy match on exit, leave, goodbye or dismiss agent">[DA] Dismiss Reviewer</item>\n`;
59
+ xml += ' </menu>\n';
60
+ return xml;
61
+ }
62
+
63
+ function buildActivationXml(meta) {
64
+ return ` <activation>
65
+ <step>Greet the user by name if known, introduce yourself as ${meta.name || 'PR Reviewer'} (${meta.title || 'Code Reviewer'})</step>
66
+ <step>Display your menu using numbered list format</step>
67
+ <step>Wait for user to select an option</step>
68
+ <step>Load and execute the corresponding workflow or task</step>
69
+ </activation>\n`;
70
+ }
71
+
72
+ async function compileToXml(agentYaml, agentName = '') {
73
+ const agent = agentYaml.agent;
74
+ const meta = agent.metadata;
75
+
76
+ let xml = buildFrontmatter(meta, agentName || meta.name);
77
+ xml += '```xml\n';
78
+
79
+ const agentAttrs = [
80
+ `id="${meta.id || ''}"`,
81
+ `name="${meta.name || ''}"`,
82
+ `title="${meta.title || ''}"`,
83
+ `icon="${meta.icon || '🔍'}"`,
84
+ ];
85
+ if (meta.capabilities) agentAttrs.push(`capabilities="${escapeXml(meta.capabilities)}"`);
86
+
87
+ xml += `<agent ${agentAttrs.join(' ')}>\n`;
88
+ xml += buildActivationXml(meta);
89
+
90
+ if (agent.critical_actions && agent.critical_actions.length > 0) {
91
+ xml += ' <critical_actions>\n';
92
+ for (const action of agent.critical_actions) {
93
+ xml += ` <action>${escapeXml(action)}</action>\n`;
94
+ }
95
+ xml += ' </critical_actions>\n';
96
+ }
97
+
98
+ xml += buildPersonaXml(agent.persona);
99
+ if (agent.memories && agent.memories.length > 0) xml += buildMemoriesXml(agent.memories);
100
+ xml += buildMenuXml(agent.menu || []);
101
+ xml += '</agent>\n```\n';
102
+
103
+ return xml;
104
+ }
105
+
106
+ async function compileAgent(yamlContent, answers = {}, agentName = '', targetPath = '') {
107
+ let agentYaml = yaml.parse(yamlContent);
108
+
109
+ const installConfig = extractInstallConfig(agentYaml);
110
+ let finalAnswers = answers;
111
+ if (installConfig) {
112
+ const defaults = getDefaultValues(installConfig);
113
+ finalAnswers = { ...defaults, ...answers };
114
+ }
115
+
116
+ const processedYaml = processAgentYaml(agentYaml, finalAnswers);
117
+ const cleanYaml = stripInstallConfig(processedYaml);
118
+ const xml = await compileToXml(cleanYaml, agentName, targetPath);
119
+
120
+ return { xml, metadata: cleanYaml.agent.metadata, processedYaml: cleanYaml };
121
+ }
122
+
123
+ module.exports = { compileAgent, compileToXml, buildFrontmatter, buildPersonaXml, buildMenuXml, escapeXml };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Template Engine — processes agent YAML with variable substitution
3
+ * Handles variable substitution for agent YAML configuration
4
+ */
5
+
6
+ /**
7
+ * Recursively replace {variable} placeholders in strings/objects/arrays
8
+ * @param {any} value - Value to process
9
+ * @param {Object} vars - Variable map
10
+ * @returns {any} Processed value
11
+ */
12
+ function replacePlaceholders(value, vars) {
13
+ if (typeof value === 'string') {
14
+ return value.replaceAll(/\{(\w+)\}/g, (match, key) => {
15
+ return Object.prototype.hasOwnProperty.call(vars, key) ? vars[key] : match;
16
+ });
17
+ }
18
+ if (Array.isArray(value)) {
19
+ return value.map((item) => replacePlaceholders(item, vars));
20
+ }
21
+ if (value && typeof value === 'object') {
22
+ const result = {};
23
+ for (const [k, v] of Object.entries(value)) {
24
+ result[k] = replacePlaceholders(v, vars);
25
+ }
26
+ return result;
27
+ }
28
+ return value;
29
+ }
30
+
31
+ /**
32
+ * Extract install_config section from agent YAML
33
+ */
34
+ function extractInstallConfig(agentYaml) {
35
+ return agentYaml.install_config || null;
36
+ }
37
+
38
+ /**
39
+ * Strip install_config from processed YAML
40
+ */
41
+ function stripInstallConfig(agentYaml) {
42
+ const { install_config, ...rest } = agentYaml;
43
+ return rest;
44
+ }
45
+
46
+ /**
47
+ * Get default values from install_config questions
48
+ */
49
+ function getDefaultValues(installConfig) {
50
+ const defaults = {};
51
+ if (!installConfig || !installConfig.questions) return defaults;
52
+ for (const q of installConfig.questions) {
53
+ if (q.id && q.default !== undefined) {
54
+ defaults[q.id] = q.default;
55
+ }
56
+ }
57
+ return defaults;
58
+ }
59
+
60
+ /**
61
+ * Process agent YAML with variable substitution
62
+ */
63
+ function processAgentYaml(agentYaml, answers = {}) {
64
+ return replacePlaceholders(agentYaml, answers);
65
+ }
66
+
67
+ module.exports = {
68
+ processAgentYaml,
69
+ extractInstallConfig,
70
+ stripInstallConfig,
71
+ getDefaultValues,
72
+ replacePlaceholders,
73
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * CLI utilities — common helper functions
3
+ */
4
+ const path = require('node:path');
5
+
6
+ /**
7
+ * Resolve a directory path: use provided path or cwd
8
+ */
9
+ function resolveDirectory(dir) {
10
+ if (!dir) return process.cwd();
11
+ return path.resolve(dir);
12
+ }
13
+
14
+ /**
15
+ * Parse comma-separated string into array of trimmed non-empty values
16
+ */
17
+ function parseList(str) {
18
+ if (!str) return [];
19
+ return str
20
+ .split(',')
21
+ .map((s) => s.trim())
22
+ .filter(Boolean);
23
+ }
24
+
25
+ /**
26
+ * Get OS username as default fallback
27
+ */
28
+ function getSystemUsername() {
29
+ return process.env.USERNAME || process.env.USER || 'Reviewer';
30
+ }
31
+
32
+ module.exports = { resolveDirectory, parseList, getSystemUsername };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Prompts — wrapper around @clack/prompts for consistent CLI output
3
+ */
4
+ const clack = require('@clack/prompts');
5
+
6
+ const log = {
7
+ info: async (msg) => clack.log.info(msg),
8
+ success: async (msg) => clack.log.success(msg),
9
+ warn: async (msg) => clack.log.warn(msg),
10
+ error: async (msg) => clack.log.error(msg),
11
+ message: async (msg) => clack.log.message(msg),
12
+ step: async (msg) => clack.log.step(msg),
13
+ };
14
+
15
+ module.exports = { ...clack, log };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * UI — Interactive prompts for installation
3
+ * Interactive CLI UI for PR Review installer
4
+ */
5
+ const clack = require('@clack/prompts');
6
+ const path = require('node:path');
7
+ const { parseList, resolveDirectory, getSystemUsername } = require('./cli-utils');
8
+ const { Detector } = require('../installers/lib/core/detector');
9
+ const { IdeManager } = require('../installers/lib/ide/manager');
10
+
11
+ const detector = new Detector();
12
+ const ideManager = new IdeManager();
13
+
14
+ class UI {
15
+ /**
16
+ * Prompt user through the full install configuration
17
+ * @param {Object} cliOptions - Parsed CLI options
18
+ * @returns {Object} config object for Installer
19
+ */
20
+ async promptInstall(cliOptions) {
21
+ await ideManager.ensureInitialized();
22
+
23
+ clack.intro('PR Review — AI-driven Code Review Framework');
24
+
25
+ const projectDir = resolveDirectory(cliOptions.directory);
26
+ const dirName = path.basename(projectDir);
27
+
28
+ // Check for existing installation
29
+ const existing = await detector.detect(projectDir);
30
+ let actionType = cliOptions.action || null;
31
+
32
+ if (existing.installed && !actionType) {
33
+ if (cliOptions.yes) {
34
+ actionType = 'update';
35
+ } else {
36
+ actionType = await clack.select({
37
+ message: `PR Review v${existing.version} is already installed. What would you like to do?`,
38
+ options: [
39
+ { value: 'update', label: 'Full Update — reinstall all modules' },
40
+ { value: 'quick-update', label: 'Quick Update — refresh files, keep settings' },
41
+ { value: 'cancel', label: 'Cancel' },
42
+ ],
43
+ });
44
+ if (clack.isCancel(actionType)) return { actionType: 'cancel' };
45
+ }
46
+ } else if (!actionType) {
47
+ actionType = 'install';
48
+ }
49
+
50
+ if (actionType === 'cancel') return { actionType: 'cancel' };
51
+ if (actionType === 'quick-update') {
52
+ return { actionType: 'quick-update', projectDir, existing };
53
+ }
54
+
55
+ // Collect core config
56
+ const userName = cliOptions.userName
57
+ || (!cliOptions.yes && await this._ask('What should reviewers call you?', getSystemUsername()))
58
+ || getSystemUsername();
59
+
60
+ const communicationLanguage = cliOptions.communicationLanguage
61
+ || (!cliOptions.yes && await this._ask('Language for reviewer agents?', 'English'))
62
+ || 'English';
63
+
64
+ const outputFolder = cliOptions.outputFolder
65
+ || (!cliOptions.yes && await this._ask('Output folder (relative to project root)?', '_prr-output'))
66
+ || '_prr-output';
67
+
68
+ // PRR module config
69
+ const targetRepo = cliOptions.targetRepo
70
+ || (!cliOptions.yes && await this._ask('Path to the git repo to review?', '.'))
71
+ || '.';
72
+
73
+ const githubRepo = cliOptions.githubRepo
74
+ || (!cliOptions.yes && await this._ask('GitHub repo for posting comments? (owner/repo, blank to skip)', ''))
75
+ || '';
76
+
77
+ // Modules (default: prr)
78
+ const modulesInput = cliOptions.modules
79
+ ? parseList(cliOptions.modules)
80
+ : ['prr'];
81
+
82
+ // IDEs
83
+ let selectedIdes = cliOptions.tools === 'none'
84
+ ? []
85
+ : cliOptions.tools
86
+ ? parseList(cliOptions.tools)
87
+ : null;
88
+
89
+ if (selectedIdes === null && !cliOptions.yes) {
90
+ const availableIdes = ideManager.getAvailableIdes();
91
+ const choices = await clack.multiselect({
92
+ message: 'Which IDEs should PR Review be configured for?',
93
+ options: availableIdes.map((ide) => ({
94
+ value: ide.value,
95
+ label: ide.name,
96
+ hint: ide.preferred ? 'recommended' : undefined,
97
+ })),
98
+ required: false,
99
+ });
100
+ selectedIdes = clack.isCancel(choices) ? [] : choices;
101
+ } else if (selectedIdes === null) {
102
+ selectedIdes = ideManager.getPreferredIdes().map((ide) => ide.value);
103
+ }
104
+
105
+ clack.outro('Configuration complete — starting installation...');
106
+
107
+ return {
108
+ actionType,
109
+ projectDir,
110
+ userName,
111
+ communicationLanguage,
112
+ outputFolder,
113
+ targetRepo,
114
+ githubRepo,
115
+ selectedModules: modulesInput,
116
+ selectedIdes,
117
+ existing,
118
+ };
119
+ }
120
+
121
+ async _ask(message, defaultValue) {
122
+ const result = await clack.text({
123
+ message,
124
+ placeholder: defaultValue,
125
+ defaultValue,
126
+ });
127
+ if (clack.isCancel(result)) return defaultValue;
128
+ return result || defaultValue;
129
+ }
130
+ }
131
+
132
+ module.exports = { UI };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * XML utilities — escape/unescape for agent XML generation
3
+ */
4
+ function escapeXml(str) {
5
+ if (typeof str !== 'string') return String(str ?? '');
6
+ return str
7
+ .replaceAll('&', '&amp;')
8
+ .replaceAll('<', '&lt;')
9
+ .replaceAll('>', '&gt;')
10
+ .replaceAll('"', '&quot;')
11
+ .replaceAll("'", '&apos;');
12
+ }
13
+
14
+ function unescapeXml(str) {
15
+ if (typeof str !== 'string') return str;
16
+ return str
17
+ .replaceAll('&apos;', "'")
18
+ .replaceAll('&quot;', '"')
19
+ .replaceAll('&gt;', '>')
20
+ .replaceAll('&lt;', '<')
21
+ .replaceAll('&amp;', '&');
22
+ }
23
+
24
+ module.exports = { escapeXml, unescapeXml };
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PR Review CLI — Main entry point
4
+ * Usage: node tools/cli/prr-cli.js <command> [options]
5
+ */
6
+
7
+ const { program } = require('commander');
8
+ const path = require('node:path');
9
+ const fs = require('node:fs');
10
+
11
+ const packageJson = require('../../package.json');
12
+
13
+ // Dynamically load all command modules from commands/
14
+ const commandsPath = path.join(__dirname, 'commands');
15
+ const commandFiles = fs.readdirSync(commandsPath).filter((f) => f.endsWith('.js'));
16
+
17
+ for (const file of commandFiles) {
18
+ const cmd = require(path.join(commandsPath, file));
19
+
20
+ const command = program
21
+ .command(cmd.command)
22
+ .description(cmd.description);
23
+
24
+ for (const option of cmd.options || []) {
25
+ command.option(...option);
26
+ }
27
+
28
+ command.action(cmd.action);
29
+ }
30
+
31
+ program
32
+ .name('pr-review')
33
+ .version(packageJson.version)
34
+ .description('PR Review Framework — AI-driven code review agent system');
35
+
36
+ program.parse(process.argv);
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PR Review NPX wrapper
4
+ * Entry point for `npx pr-review` usage
5
+ */
6
+ require('./cli/prr-cli.js');