@nielpattin/pi-simplify 0.2.1
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 +50 -0
- package/dist/git-diff.d.ts +3 -0
- package/dist/git-diff.js +50 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/prompt-builder.d.ts +2 -0
- package/dist/prompt-builder.js +26 -0
- package/dist/simplify-command.d.ts +5 -0
- package/dist/simplify-command.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +0 -0
- package/package.json +73 -0
- package/src/git-diff.ts +59 -0
- package/src/index.ts +9 -0
- package/src/prompt-builder.ts +29 -0
- package/src/simplify-command.ts +42 -0
- package/src/types.ts +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# pi-simplify
|
|
2
|
+
|
|
3
|
+
A [Pi](https://github.com/nicholasgasior/pi-coding-agent) extension that reviews recently changed code for clarity, consistency, and maintainability improvements.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pi install npm:pi-simplify
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Review all uncommitted changes
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/simplify
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Review only staged changes
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/simplify --staged
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Review specific files
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
/simplify src/foo.ts src/bar.ts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Diff against a specific branch
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
/simplify --ref=main
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## What it does
|
|
38
|
+
|
|
39
|
+
When invoked, `/simplify` detects changed files (via `git diff`) and instructs the agent to review them with these principles:
|
|
40
|
+
|
|
41
|
+
- **Preserve functionality**: never change what the code does
|
|
42
|
+
- **Apply project standards**: follow conventions from CLAUDE.md / AGENTS.md
|
|
43
|
+
- **Enhance clarity**: reduce complexity, eliminate redundancy, improve naming
|
|
44
|
+
- **Maintain balance**: avoid over-simplification
|
|
45
|
+
|
|
46
|
+
The agent reads each file, applies improvements one at a time, runs tests to verify nothing breaks, and summarizes the changes.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/dist/git-diff.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const STATUS_MAP = {
|
|
2
|
+
M: "modified",
|
|
3
|
+
A: "added",
|
|
4
|
+
R: "renamed",
|
|
5
|
+
C: "copied",
|
|
6
|
+
};
|
|
7
|
+
function parseDiffOutput(stdout) {
|
|
8
|
+
const files = [];
|
|
9
|
+
for (const line of stdout.split("\n")) {
|
|
10
|
+
if (!line.trim())
|
|
11
|
+
continue;
|
|
12
|
+
const parts = line.split("\t");
|
|
13
|
+
const statusCode = parts[0]?.[0];
|
|
14
|
+
if (!statusCode)
|
|
15
|
+
continue;
|
|
16
|
+
const status = STATUS_MAP[statusCode];
|
|
17
|
+
if (!status)
|
|
18
|
+
continue;
|
|
19
|
+
// Renamed (R100\told\tnew) and copied (C100\told\tnew) have two paths; use the new one.
|
|
20
|
+
const path = status === "renamed" || status === "copied" ? parts[2] : parts[1];
|
|
21
|
+
if (path) {
|
|
22
|
+
files.push({ path, status });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return files;
|
|
26
|
+
}
|
|
27
|
+
export async function getChangedFiles(pi, cwd, options) {
|
|
28
|
+
if (options.files.length > 0) {
|
|
29
|
+
return options.files.map((path) => ({ path, status: "modified" }));
|
|
30
|
+
}
|
|
31
|
+
const args = ["diff", "--name-status"];
|
|
32
|
+
if (options.staged) {
|
|
33
|
+
args.push("--cached");
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
args.push(options.ref);
|
|
37
|
+
}
|
|
38
|
+
const result = await pi.exec("git", args, { cwd });
|
|
39
|
+
if (result.code === 0) {
|
|
40
|
+
const files = parseDiffOutput(result.stdout);
|
|
41
|
+
if (files.length > 0)
|
|
42
|
+
return files;
|
|
43
|
+
}
|
|
44
|
+
// Fallback: diff against previous commit
|
|
45
|
+
const fallback = await pi.exec("git", ["diff", "--name-status", "HEAD~1"], { cwd });
|
|
46
|
+
if (fallback.code === 0) {
|
|
47
|
+
return parseDiffOutput(fallback.stdout);
|
|
48
|
+
}
|
|
49
|
+
return [];
|
|
50
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { handleSimplifyCommand, COMMAND_NAME } from "./simplify-command.js";
|
|
2
|
+
export default function (pi) {
|
|
3
|
+
pi.registerCommand(COMMAND_NAME, {
|
|
4
|
+
description: "Review recently changed files for clarity, consistency, and maintainability improvements",
|
|
5
|
+
handler: (args, ctx) => handleSimplifyCommand(args, ctx, pi),
|
|
6
|
+
});
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function buildSimplifyPrompt(files) {
|
|
2
|
+
const fileList = files.map((f) => `- ${f.path} (${f.status})`).join("\n");
|
|
3
|
+
return `Review the following recently changed files and apply simplification improvements.
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
- **Preserve functionality**: Never change what the code does. All existing tests must continue to pass.
|
|
8
|
+
- **Apply project standards**: Follow any conventions from CLAUDE.md or AGENTS.md in this project.
|
|
9
|
+
- **Enhance clarity**: Reduce unnecessary complexity and nesting, eliminate redundant code and abstractions, improve variable and function names, consolidate related logic, remove unnecessary comments that describe obvious code. Avoid nested ternary operators: prefer switch statements or if/else chains for multiple conditions.
|
|
10
|
+
- **Maintain balance**: Do not over-simplify. Avoid overly clever solutions that are hard to understand. Do not combine too many concerns into single functions. Do not remove helpful abstractions. Prioritize readability over fewer lines.
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
Only review and modify these files:
|
|
15
|
+
${fileList}
|
|
16
|
+
|
|
17
|
+
## Process
|
|
18
|
+
|
|
19
|
+
1. Read each file listed above
|
|
20
|
+
2. Identify concrete improvements (dead code, unclear names, redundant logic, inconsistent patterns)
|
|
21
|
+
3. Apply changes one file at a time
|
|
22
|
+
4. After all changes, run existing tests to verify nothing is broken
|
|
23
|
+
5. Summarize what you changed and why
|
|
24
|
+
|
|
25
|
+
Do NOT add new features, change public APIs, or refactor code outside the listed files.`;
|
|
26
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { SimplifyOptions } from "./types.js";
|
|
3
|
+
export declare const COMMAND_NAME = "simplify";
|
|
4
|
+
export declare function parseArgs(args: string): SimplifyOptions;
|
|
5
|
+
export declare function handleSimplifyCommand(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getChangedFiles } from "./git-diff.js";
|
|
2
|
+
import { buildSimplifyPrompt } from "./prompt-builder.js";
|
|
3
|
+
export const COMMAND_NAME = "simplify";
|
|
4
|
+
export function parseArgs(args) {
|
|
5
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
6
|
+
const files = [];
|
|
7
|
+
let ref = "HEAD";
|
|
8
|
+
let staged = false;
|
|
9
|
+
for (const token of tokens) {
|
|
10
|
+
if (token === "--staged") {
|
|
11
|
+
staged = true;
|
|
12
|
+
}
|
|
13
|
+
else if (token.startsWith("--ref=")) {
|
|
14
|
+
ref = token.slice("--ref=".length);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
files.push(token);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { files, ref, staged };
|
|
21
|
+
}
|
|
22
|
+
export async function handleSimplifyCommand(args, ctx, pi) {
|
|
23
|
+
const options = parseArgs(args);
|
|
24
|
+
const files = await getChangedFiles(pi, ctx.cwd, options);
|
|
25
|
+
if (files.length === 0) {
|
|
26
|
+
ctx.ui.notify("No changed files found. Specify file paths or make some changes first.", "info");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const prompt = buildSimplifyPrompt(files);
|
|
30
|
+
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
31
|
+
}
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nielpattin/pi-simplify",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "A Pi extension that reviews recently changed code for clarity, consistency, and maintainability.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"ai-agent",
|
|
8
|
+
"code-review",
|
|
9
|
+
"coding-assistant",
|
|
10
|
+
"developer-tools",
|
|
11
|
+
"llm",
|
|
12
|
+
"pi-coding-agent",
|
|
13
|
+
"pi-extension",
|
|
14
|
+
"pi-package",
|
|
15
|
+
"refactor",
|
|
16
|
+
"simplify"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/nielpattin/pi-packages/tree/main/pi-simplify#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/nielpattin/pi-packages/issues"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Matt Devy",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/nielpattin/pi-packages.git",
|
|
27
|
+
"directory": "pi-simplify"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src",
|
|
35
|
+
"!src/**/*.test.ts",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"type": "module",
|
|
39
|
+
"main": "./dist/index.js",
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"scripts": {
|
|
42
|
+
"clean": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.build.tsbuildinfo',{force:true});\"",
|
|
43
|
+
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"lint": "eslint src/",
|
|
47
|
+
"check": "vitest run && eslint src/ && tsc --noEmit",
|
|
48
|
+
"prepublishOnly": "npm run build && npm run check",
|
|
49
|
+
"prepack": "node -e \"const fs=require('node:fs');if(!fs.existsSync('dist')){console.error('Error: dist/ missing. Run npm run build first.');process.exit(1);}\""
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^25.7.0",
|
|
53
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
54
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
55
|
+
"eslint": "^10.3.0",
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"vitest": "^4.1.6"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@earendil-works/pi-ai": "^0.74.0",
|
|
61
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
62
|
+
"@earendil-works/pi-tui": "^0.74.0",
|
|
63
|
+
"@sinclair/typebox": "^0.34.0"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18"
|
|
67
|
+
},
|
|
68
|
+
"pi": {
|
|
69
|
+
"extensions": [
|
|
70
|
+
"src/index.ts"
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/git-diff.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ChangedFile, SimplifyOptions } from "./types.js";
|
|
3
|
+
|
|
4
|
+
const STATUS_MAP: Record<string, ChangedFile["status"]> = {
|
|
5
|
+
M: "modified",
|
|
6
|
+
A: "added",
|
|
7
|
+
R: "renamed",
|
|
8
|
+
C: "copied",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function parseDiffOutput(stdout: string): ChangedFile[] {
|
|
12
|
+
const files: ChangedFile[] = [];
|
|
13
|
+
|
|
14
|
+
for (const line of stdout.split("\n")) {
|
|
15
|
+
if (!line.trim()) continue;
|
|
16
|
+
|
|
17
|
+
const parts = line.split("\t");
|
|
18
|
+
const statusCode = parts[0]?.[0];
|
|
19
|
+
if (!statusCode) continue;
|
|
20
|
+
|
|
21
|
+
const status = STATUS_MAP[statusCode];
|
|
22
|
+
if (!status) continue;
|
|
23
|
+
|
|
24
|
+
// Renamed (R100\told\tnew) and copied (C100\told\tnew) have two paths; use the new one.
|
|
25
|
+
const path = status === "renamed" || status === "copied" ? parts[2] : parts[1];
|
|
26
|
+
if (path) {
|
|
27
|
+
files.push({ path, status });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return files;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function getChangedFiles(pi: ExtensionAPI, cwd: string, options: SimplifyOptions): Promise<ChangedFile[]> {
|
|
35
|
+
if (options.files.length > 0) {
|
|
36
|
+
return options.files.map((path) => ({ path, status: "modified" as const }));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const args = ["diff", "--name-status"];
|
|
40
|
+
if (options.staged) {
|
|
41
|
+
args.push("--cached");
|
|
42
|
+
} else {
|
|
43
|
+
args.push(options.ref);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const result = await pi.exec("git", args, { cwd });
|
|
47
|
+
if (result.code === 0) {
|
|
48
|
+
const files = parseDiffOutput(result.stdout);
|
|
49
|
+
if (files.length > 0) return files;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Fallback: diff against previous commit
|
|
53
|
+
const fallback = await pi.exec("git", ["diff", "--name-status", "HEAD~1"], { cwd });
|
|
54
|
+
if (fallback.code === 0) {
|
|
55
|
+
return parseDiffOutput(fallback.stdout);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return [];
|
|
59
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { handleSimplifyCommand, COMMAND_NAME } from "./simplify-command.js";
|
|
3
|
+
|
|
4
|
+
export default function (pi: ExtensionAPI): void {
|
|
5
|
+
pi.registerCommand(COMMAND_NAME, {
|
|
6
|
+
description: "Review recently changed files for clarity, consistency, and maintainability improvements",
|
|
7
|
+
handler: (args: string, ctx: ExtensionCommandContext) => handleSimplifyCommand(args, ctx, pi),
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChangedFile } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function buildSimplifyPrompt(files: readonly ChangedFile[]): string {
|
|
4
|
+
const fileList = files.map((f) => `- ${f.path} (${f.status})`).join("\n");
|
|
5
|
+
|
|
6
|
+
return `Review the following recently changed files and apply simplification improvements.
|
|
7
|
+
|
|
8
|
+
## Principles
|
|
9
|
+
|
|
10
|
+
- **Preserve functionality**: Never change what the code does. All existing tests must continue to pass.
|
|
11
|
+
- **Apply project standards**: Follow any conventions from CLAUDE.md or AGENTS.md in this project.
|
|
12
|
+
- **Enhance clarity**: Reduce unnecessary complexity and nesting, eliminate redundant code and abstractions, improve variable and function names, consolidate related logic, remove unnecessary comments that describe obvious code. Avoid nested ternary operators: prefer switch statements or if/else chains for multiple conditions.
|
|
13
|
+
- **Maintain balance**: Do not over-simplify. Avoid overly clever solutions that are hard to understand. Do not combine too many concerns into single functions. Do not remove helpful abstractions. Prioritize readability over fewer lines.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
Only review and modify these files:
|
|
18
|
+
${fileList}
|
|
19
|
+
|
|
20
|
+
## Process
|
|
21
|
+
|
|
22
|
+
1. Read each file listed above
|
|
23
|
+
2. Identify concrete improvements (dead code, unclear names, redundant logic, inconsistent patterns)
|
|
24
|
+
3. Apply changes one file at a time
|
|
25
|
+
4. After all changes, run existing tests to verify nothing is broken
|
|
26
|
+
5. Summarize what you changed and why
|
|
27
|
+
|
|
28
|
+
Do NOT add new features, change public APIs, or refactor code outside the listed files.`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { getChangedFiles } from "./git-diff.js";
|
|
3
|
+
import { buildSimplifyPrompt } from "./prompt-builder.js";
|
|
4
|
+
import type { SimplifyOptions } from "./types.js";
|
|
5
|
+
|
|
6
|
+
export const COMMAND_NAME = "simplify";
|
|
7
|
+
|
|
8
|
+
export function parseArgs(args: string): SimplifyOptions {
|
|
9
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
10
|
+
const files: string[] = [];
|
|
11
|
+
let ref = "HEAD";
|
|
12
|
+
let staged = false;
|
|
13
|
+
|
|
14
|
+
for (const token of tokens) {
|
|
15
|
+
if (token === "--staged") {
|
|
16
|
+
staged = true;
|
|
17
|
+
} else if (token.startsWith("--ref=")) {
|
|
18
|
+
ref = token.slice("--ref=".length);
|
|
19
|
+
} else {
|
|
20
|
+
files.push(token);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { files, ref, staged };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function handleSimplifyCommand(
|
|
28
|
+
args: string,
|
|
29
|
+
ctx: ExtensionCommandContext,
|
|
30
|
+
pi: ExtensionAPI,
|
|
31
|
+
): Promise<void> {
|
|
32
|
+
const options = parseArgs(args);
|
|
33
|
+
const files = await getChangedFiles(pi, ctx.cwd, options);
|
|
34
|
+
|
|
35
|
+
if (files.length === 0) {
|
|
36
|
+
ctx.ui.notify("No changed files found. Specify file paths or make some changes first.", "info");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const prompt = buildSimplifyPrompt(files);
|
|
41
|
+
pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
42
|
+
}
|
package/src/types.ts
ADDED