@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 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
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import type { ChangedFile, SimplifyOptions } from "./types.js";
3
+ export declare function getChangedFiles(pi: ExtensionAPI, cwd: string, options: SimplifyOptions): Promise<ChangedFile[]>;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export default function (pi: ExtensionAPI): void;
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,2 @@
1
+ import type { ChangedFile } from "./types.js";
2
+ export declare function buildSimplifyPrompt(files: readonly ChangedFile[]): string;
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ export interface ChangedFile {
2
+ readonly path: string;
3
+ readonly status: "modified" | "added" | "renamed" | "copied";
4
+ }
5
+ export interface SimplifyOptions {
6
+ readonly files: readonly string[];
7
+ readonly ref: string;
8
+ readonly staged: boolean;
9
+ }
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
+ }
@@ -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
@@ -0,0 +1,10 @@
1
+ export interface ChangedFile {
2
+ readonly path: string;
3
+ readonly status: "modified" | "added" | "renamed" | "copied";
4
+ }
5
+
6
+ export interface SimplifyOptions {
7
+ readonly files: readonly string[];
8
+ readonly ref: string;
9
+ readonly staged: boolean;
10
+ }