prsmith 1.0.2 → 1.1.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/CHANGELOG.md +27 -0
- package/README.md +37 -14
- package/bin/cli.js +33 -5
- package/package.json +4 -2
- package/src/config.js +28 -0
- package/src/formatter.js +4 -3
- package/src/prompts.js +39 -14
- package/tests/formatter.test.js +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.1.0] - 2026-06-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Clipboard Integration**: Automatically copies the generated markdown to your clipboard (`clipboardy`).
|
|
9
|
+
- **Editor Prompts**: The `issue` and `fix` interactive prompts now intelligently launch your default system `$EDITOR` (e.g. VS Code, vim) for a seamless multi-line typing experience.
|
|
10
|
+
- **CLI Arguments**: You can completely bypass the interactive mode by supplying flags: `-s`, `-t`, `-i`, `-f`.
|
|
11
|
+
- **File Output**: Use the `-o` or `--out` flag to directly write the markdown to a file (e.g. `prsmith --out comment.md`).
|
|
12
|
+
- **Update Notifier**: The CLI now checks for updates in the background and notifies you if a newer version is available on NPM.
|
|
13
|
+
- **Custom Configs**: Support for `.prsmith.json` files in your home directory (`~/`) or project directory. Define your own custom severities and intro templates!
|
|
14
|
+
|
|
15
|
+
## [1.0.2] - 2026-05-31
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Integrated `commander` to provide robust native support for CLI flags (`-h`, `--help`, `-v`, `--version`).
|
|
19
|
+
- Graceful termination handling: Intercepting `ExitPromptError` to display a friendly message when users abort the interactive prompt (e.g., via `Ctrl+C`) instead of throwing stack traces.
|
|
20
|
+
- Custom author branding and detailed bio added to the README footer.
|
|
21
|
+
|
|
22
|
+
## [1.0.1] - 2026-05-31
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Re-configured Git Hooks (Husky) to ensure `npm test` and `npm run lint` run reliably on pre-commit.
|
|
26
|
+
- Resolved invalid path structures in `package.json` for the executable `bin` field.
|
|
27
|
+
- Fixed a bug in `inquirer` by migrating legacy `"list"` prompt types to `"select"` to prevent CLI crashes.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- Massively overhauled `README.md` for NPM compatibility: Replaced unsupported Mermaid diagrams with elegant ASCII flowcharts, incorporated GitHub/NPM badges, and structured sections with collapsible `<details>` HTML tags for premium rendering on the NPM website.
|
|
31
|
+
|
|
5
32
|
## [1.0.0] - 2026-05-31
|
|
6
33
|
|
|
7
34
|
### Added
|
package/README.md
CHANGED
|
@@ -45,30 +45,53 @@ Install globally to use it anywhere on your machine.
|
|
|
45
45
|
npm install -g prsmith
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
## Features
|
|
48
|
+
## ✨ Advanced Features (v1.1.0)
|
|
49
49
|
|
|
50
|
-
- **Interactive Prompts:**
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
53
|
-
- **
|
|
50
|
+
- **Interactive Editor Prompts:** For multi-line issues, PRSmith temporarily opens your system's default `$EDITOR` (like VS Code or Vim) so you can type formatted text seamlessly.
|
|
51
|
+
- **Auto-Clipboard:** No need to manually highlight terminal output! The generated markdown is automatically copied to your clipboard.
|
|
52
|
+
- **Bypass Prompts:** Skip the interactive flow completely by using CLI arguments (perfect for scripting).
|
|
53
|
+
- **File Output:** Directly write the output to a `.md` file using the `--out` flag.
|
|
54
|
+
- **Custom Templates:** Create a `.prsmith.json` file in your project or home directory to define your own severity levels and custom intro messages.
|
|
54
55
|
|
|
55
|
-
## Usage
|
|
56
|
+
## 💻 Usage
|
|
56
57
|
|
|
57
|
-
###
|
|
58
|
+
### Interactive Mode
|
|
58
59
|
The primary way to use PRSmith is via its interactive mode. Simply run:
|
|
59
60
|
|
|
60
61
|
```bash
|
|
61
62
|
prsmith
|
|
62
63
|
```
|
|
63
|
-
This will launch the interactive prompt
|
|
64
|
+
This will launch the interactive prompt. When asked to describe the issue or fix, your default terminal editor will launch, allowing you to write multi-line markdown!
|
|
64
65
|
|
|
65
|
-
###
|
|
66
|
+
### Non-Interactive Flags
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
You can bypass the prompts entirely:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
prsmith -s Critical -t "Memory Leak" -i "Connection left open." -f "Add a finally block." -o review.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
| Flag | Full | Description |
|
|
75
|
+
| :--- | :--- | :--- |
|
|
76
|
+
| `-s` | `--severity` | Severity level (e.g., Critical, Minor) |
|
|
77
|
+
| `-t` | `--title` | The review title |
|
|
78
|
+
| `-i` | `--issue` | Description of the problem |
|
|
79
|
+
| `-f` | `--fix` | Suggested solution |
|
|
80
|
+
| `-o` | `--out` | Save generated markdown to a specific file |
|
|
81
|
+
|
|
82
|
+
### Configuration (`.prsmith.json`)
|
|
83
|
+
|
|
84
|
+
You can define custom templates by creating a `.prsmith.json` in your home directory (`~/`) or current working directory:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"templates": {
|
|
89
|
+
"Nitpick": "This is just a tiny nitpick, no pressure to fix.",
|
|
90
|
+
"Security": "CRITICAL SECURITY VULNERABILITY DETECTED."
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
If you pass `-s Nitpick` (or select it in the CLI), PRSmith will automatically use your custom text!
|
|
72
95
|
|
|
73
96
|
<details>
|
|
74
97
|
<summary><b>Click to view an Example Interaction</b></summary>
|
package/bin/cli.js
CHANGED
|
@@ -1,27 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
3
6
|
import chalk from "chalk";
|
|
4
7
|
import { program } from "commander";
|
|
8
|
+
import clipboardy from "clipboardy";
|
|
9
|
+
import updateNotifier from "update-notifier";
|
|
5
10
|
import { getReviewData } from "../src/prompts.js";
|
|
6
11
|
import { generateMarkdown } from "../src/formatter.js";
|
|
12
|
+
import { loadConfig } from "../src/config.js";
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
const pkg = JSON.parse(
|
|
17
|
+
fs.readFileSync(path.join(__dirname, "../package.json"), "utf8")
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
updateNotifier({ pkg }).notify();
|
|
7
21
|
|
|
8
22
|
program
|
|
9
23
|
.name("prsmith")
|
|
10
24
|
.description("Forge professional pull request review comments directly from the terminal.")
|
|
11
|
-
.version(
|
|
25
|
+
.version(pkg.version, "-v, --version", "Output the current version")
|
|
12
26
|
.helpOption("-h, --help", "Display help for command")
|
|
13
|
-
.
|
|
27
|
+
.option("-s, --severity <level>", "Severity of the issue (e.g. Critical, Major, Minor, Suggestion)")
|
|
28
|
+
.option("-t, --title <title>", "Title of the review")
|
|
29
|
+
.option("-i, --issue <issue>", "Description of the issue")
|
|
30
|
+
.option("-f, --fix <fix>", "Suggested fix")
|
|
31
|
+
.option("-o, --out <file>", "Save the output to a markdown file")
|
|
32
|
+
.action(async (options) => {
|
|
14
33
|
try {
|
|
15
34
|
console.log(
|
|
16
35
|
chalk.cyan("\nPRSmith - Professional PR Review Comment Generator\n")
|
|
17
36
|
);
|
|
18
37
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const markdown = generateMarkdown(data);
|
|
38
|
+
const config = loadConfig();
|
|
39
|
+
const data = await getReviewData(options, config);
|
|
40
|
+
const markdown = generateMarkdown(data, config);
|
|
22
41
|
|
|
23
42
|
console.log(chalk.green("\nGenerated Comment:\n"));
|
|
24
43
|
console.log(markdown);
|
|
44
|
+
|
|
45
|
+
clipboardy.writeSync(markdown);
|
|
46
|
+
console.log(chalk.green("✅ Comment copied to clipboard!\n"));
|
|
47
|
+
|
|
48
|
+
if (options.out) {
|
|
49
|
+
const outPath = path.resolve(process.cwd(), options.out);
|
|
50
|
+
fs.writeFileSync(outPath, markdown, "utf8");
|
|
51
|
+
console.log(chalk.green(`✅ Comment saved to ${outPath}\n`));
|
|
52
|
+
}
|
|
25
53
|
} catch (error) {
|
|
26
54
|
if (error.name === "ExitPromptError") {
|
|
27
55
|
console.log(chalk.yellow("\nYou have quit in b/w :) \n"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prsmith",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Forge professional pull request review comments from simple prompts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pull-request",
|
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"chalk": "^5.6.2",
|
|
28
|
+
"clipboardy": "^5.3.1",
|
|
28
29
|
"commander": "^15.0.0",
|
|
29
|
-
"inquirer": "^14.0.1"
|
|
30
|
+
"inquirer": "^14.0.1",
|
|
31
|
+
"update-notifier": "^7.3.1"
|
|
30
32
|
},
|
|
31
33
|
"devDependencies": {
|
|
32
34
|
"@eslint/js": "^10.0.1",
|
package/src/config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
export function loadConfig() {
|
|
6
|
+
const configPaths = [
|
|
7
|
+
path.join(os.homedir(), '.prsmith.json'),
|
|
8
|
+
path.join(process.cwd(), '.prsmith.json')
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
let mergedConfig = { templates: {} };
|
|
12
|
+
|
|
13
|
+
for (const configPath of configPaths) {
|
|
14
|
+
if (fs.existsSync(configPath)) {
|
|
15
|
+
try {
|
|
16
|
+
const fileContent = fs.readFileSync(configPath, 'utf-8');
|
|
17
|
+
const parsed = JSON.parse(fileContent);
|
|
18
|
+
if (parsed.templates) {
|
|
19
|
+
mergedConfig.templates = { ...mergedConfig.templates, ...parsed.templates };
|
|
20
|
+
}
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.warn(`Warning: Could not parse config at ${configPath} - ${err.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return mergedConfig;
|
|
28
|
+
}
|
package/src/formatter.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { templates } from "./templates.js";
|
|
1
|
+
import { templates as defaultTemplates } from "./templates.js";
|
|
2
2
|
|
|
3
|
-
export function generateMarkdown(data) {
|
|
3
|
+
export function generateMarkdown(data, config = {}) {
|
|
4
|
+
const mergedTemplates = { ...defaultTemplates, ...(config.templates || {}) };
|
|
4
5
|
const intro =
|
|
5
|
-
|
|
6
|
+
mergedTemplates[data.severity] ||
|
|
6
7
|
"The current implementation requires attention.";
|
|
7
8
|
|
|
8
9
|
return `### ${data.severity}: ${data.title}
|
package/src/prompts.js
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
import inquirer from "inquirer";
|
|
2
2
|
|
|
3
|
-
export async function getReviewData() {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export async function getReviewData(options = {}, config = {}) {
|
|
4
|
+
const defaultSeverities = ["Critical", "Major", "Minor", "Suggestion"];
|
|
5
|
+
let severities = [...defaultSeverities];
|
|
6
|
+
if (config.templates) {
|
|
7
|
+
for (const key of Object.keys(config.templates)) {
|
|
8
|
+
if (!severities.includes(key)) {
|
|
9
|
+
severities.push(key);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const questions = [];
|
|
15
|
+
|
|
16
|
+
if (!options.severity) {
|
|
17
|
+
questions.push({
|
|
6
18
|
type: "select",
|
|
7
19
|
name: "severity",
|
|
8
20
|
message: "Select severity:",
|
|
9
|
-
choices:
|
|
10
|
-
}
|
|
11
|
-
|
|
21
|
+
choices: severities,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!options.title) {
|
|
26
|
+
questions.push({
|
|
12
27
|
type: "input",
|
|
13
28
|
name: "title",
|
|
14
29
|
message: "Review title:",
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!options.issue) {
|
|
34
|
+
questions.push({
|
|
35
|
+
type: "editor",
|
|
18
36
|
name: "issue",
|
|
19
37
|
message: "Describe the issue:",
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!options.fix) {
|
|
42
|
+
questions.push({
|
|
43
|
+
type: "editor",
|
|
23
44
|
name: "fix",
|
|
24
45
|
message: "Suggested fix:",
|
|
25
|
-
}
|
|
26
|
-
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const answers = questions.length > 0 ? await inquirer.prompt(questions) : {};
|
|
50
|
+
|
|
51
|
+
return { ...options, ...answers };
|
|
27
52
|
}
|
package/tests/formatter.test.js
CHANGED
|
@@ -29,4 +29,23 @@ describe("formatter.js", () => {
|
|
|
29
29
|
const markdown = generateMarkdown(data);
|
|
30
30
|
expect(markdown).toContain("The current implementation requires attention.");
|
|
31
31
|
});
|
|
32
|
+
|
|
33
|
+
it("should use custom templates when passed via config", () => {
|
|
34
|
+
const data = {
|
|
35
|
+
severity: "Nitpick",
|
|
36
|
+
title: "Formatting",
|
|
37
|
+
issue: "Indentation is off.",
|
|
38
|
+
fix: "Fix indentation."
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const config = {
|
|
42
|
+
templates: {
|
|
43
|
+
Nitpick: "This is a tiny nitpick."
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const markdown = generateMarkdown(data, config);
|
|
48
|
+
expect(markdown).toContain("### Nitpick: Formatting");
|
|
49
|
+
expect(markdown).toContain("This is a tiny nitpick.");
|
|
50
|
+
});
|
|
32
51
|
});
|