@geminixiang/pi-simplify 0.0.7 → 0.0.8
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/.pi/skills/release/SKILL.md +9 -9
- package/CHANGELOG.md +11 -0
- package/README.md +11 -1
- package/package.json +1 -1
- package/prompt.ts +2 -2
- package/workflow.ts +144 -7
|
@@ -11,15 +11,15 @@ Repo defaults:
|
|
|
11
11
|
- remote: `origin`
|
|
12
12
|
- repo: `geminixiang/pi-simplify`
|
|
13
13
|
- npm package: `@geminixiang/pi-simplify`
|
|
14
|
-
- use `package.json` version
|
|
14
|
+
- use `package.json` version for npm/package files, and `v<version>` for the git tag and GitHub release title
|
|
15
15
|
- versions with `-alpha.`, `-beta.`, or `-rc.` are prereleases by default
|
|
16
16
|
|
|
17
17
|
## Version rules
|
|
18
18
|
|
|
19
19
|
Examples:
|
|
20
20
|
|
|
21
|
-
- stable: `0.2.1`, `0.3.0`, `1.0.0`
|
|
22
|
-
- prerelease: `0.2.0-beta.8`, `0.3.0-rc.1`, `1.0.0-alpha.2`
|
|
21
|
+
- stable package version: `0.2.1`, `0.3.0`, `1.0.0` → release tag/title: `v0.2.1`, `v0.3.0`, `v1.0.0`
|
|
22
|
+
- prerelease package version: `0.2.0-beta.8`, `0.3.0-rc.1`, `1.0.0-alpha.2` → release tag/title: `v0.2.0-beta.8`, `v0.3.0-rc.1`, `v1.0.0-alpha.2`
|
|
23
23
|
|
|
24
24
|
Guidance:
|
|
25
25
|
|
|
@@ -100,17 +100,17 @@ Write concise notes focused on user-visible changes, usually with:
|
|
|
100
100
|
- `### Docs and maintenance`
|
|
101
101
|
- `### Verification`
|
|
102
102
|
|
|
103
|
-
Write notes to `/tmp/pi-simplify-release-<version>.md`. Keep them consistent with the CHANGELOG entry from step 3.
|
|
103
|
+
Write notes to `/tmp/pi-simplify-release-<version>.md`. Keep them consistent with the CHANGELOG entry from step 3. Use `v<previous-version>...v<version>` in compare links.
|
|
104
104
|
|
|
105
105
|
### 6. Create or update release
|
|
106
106
|
|
|
107
107
|
Prerelease:
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
|
-
gh release create <version> \
|
|
110
|
+
gh release create v<version> \
|
|
111
111
|
--repo geminixiang/pi-simplify \
|
|
112
112
|
--target main \
|
|
113
|
-
--title <version> \
|
|
113
|
+
--title v<version> \
|
|
114
114
|
--notes-file /tmp/pi-simplify-release-<version>.md \
|
|
115
115
|
--prerelease
|
|
116
116
|
```
|
|
@@ -118,14 +118,14 @@ gh release create <version> \
|
|
|
118
118
|
Stable:
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
|
-
gh release create <version> \
|
|
121
|
+
gh release create v<version> \
|
|
122
122
|
--repo geminixiang/pi-simplify \
|
|
123
123
|
--target main \
|
|
124
|
-
--title <version> \
|
|
124
|
+
--title v<version> \
|
|
125
125
|
--notes-file /tmp/pi-simplify-release-<version>.md
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
If it already exists, use `gh release edit <version> ...` and keep prerelease/stable intent consistent.
|
|
128
|
+
If it already exists, use `gh release edit v<version> ...` and keep prerelease/stable intent consistent.
|
|
129
129
|
|
|
130
130
|
## Report back
|
|
131
131
|
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.0.8] - 2026-05-25
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Support simplifying specific folders and files as snapshot reviews.
|
|
14
|
+
- Add a preset selector for choosing uncommitted changes or folder snapshot mode.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Keep release tags and GitHub release titles v-prefixed.
|
|
19
|
+
|
|
9
20
|
## [0.0.7] - 2026-05-25
|
|
10
21
|
|
|
11
22
|
### Changed
|
package/README.md
CHANGED
|
@@ -30,12 +30,22 @@ pi install npm:@geminixiang/pi-simplify
|
|
|
30
30
|
/simplify
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Opens a preset selector. Choose uncommitted changes or a folder snapshot.
|
|
34
|
+
|
|
35
|
+
Uncommitted mode analyzes all git changes and presents cleanup candidates:
|
|
34
36
|
|
|
35
37
|
- **Safe** (green) - auto-selected, will be deleted
|
|
36
38
|
- **Confirm** (yellow) - delete after user confirms
|
|
37
39
|
- **Review** (orange) - user should review first
|
|
38
40
|
|
|
41
|
+
### Simplify Folders
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/simplify folder src docs
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Analyzes the specified folders/files as a snapshot (not a diff), even when there are no local git changes.
|
|
48
|
+
|
|
39
49
|
### With Focus
|
|
40
50
|
|
|
41
51
|
```
|
package/package.json
CHANGED
package/prompt.ts
CHANGED
|
@@ -50,8 +50,8 @@ Before flagging, **search the codebase** (utility directories, shared modules, f
|
|
|
50
50
|
|
|
51
51
|
## How to Analyze
|
|
52
52
|
|
|
53
|
-
1.
|
|
54
|
-
2. For each change, classify as **Essential** / **Residual** / **Legacy** (don't flag legacy code unless the diff touches it).
|
|
53
|
+
1. Unless the user explicitly asks for a folder/snapshot review, run \`git diff\` to see what changed. For folder/snapshot review, read the files directly under the requested paths and ignore files outside that scope.
|
|
54
|
+
2. For each change or in-scope snapshot finding, classify as **Essential** / **Residual** / **Legacy** (don't flag legacy code unless the diff touches it; in folder/snapshot mode, flag only in-scope code that is clearly removable or simplifiable).
|
|
55
55
|
3. For each finding:
|
|
56
56
|
- Identify exact file and line(s)
|
|
57
57
|
- Assign **category** (reuse / quality / efficiency) and **risk**
|
package/workflow.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineTool,
|
|
3
|
+
DynamicBorder,
|
|
3
4
|
type ExtensionAPI,
|
|
4
5
|
type ExtensionCommandContext,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { Container, SelectList, Text, type SelectItem } from "@earendil-works/pi-tui";
|
|
6
8
|
import { Type } from "typebox";
|
|
7
9
|
|
|
8
10
|
import { SIMPLIFY_PROMPT } from "./prompt.js";
|
|
@@ -26,6 +28,58 @@ const actionSchema = Type.Union([
|
|
|
26
28
|
Type.Literal("parallelize"),
|
|
27
29
|
]);
|
|
28
30
|
|
|
31
|
+
type SimplifyTarget = { type: "uncommitted" } | { type: "folder"; paths: string[] };
|
|
32
|
+
type ParsedArgs = {
|
|
33
|
+
target: SimplifyTarget | null;
|
|
34
|
+
additionalFocus?: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const SIMPLIFY_PRESETS = [
|
|
39
|
+
{ value: "uncommitted", label: "Simplify uncommitted changes", description: "" },
|
|
40
|
+
{ value: "folder", label: "Simplify a folder (or more)", description: "(snapshot, not diff)" },
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
type SimplifyPresetValue = (typeof SIMPLIFY_PRESETS)[number]["value"];
|
|
44
|
+
|
|
45
|
+
function parseSimplifyPaths(value: string): string[] {
|
|
46
|
+
return value
|
|
47
|
+
.split(/\s+/)
|
|
48
|
+
.map((item) => item.trim())
|
|
49
|
+
.filter((item) => item.length > 0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function parseArgs(args: string): ParsedArgs {
|
|
53
|
+
const trimmed = args.trim();
|
|
54
|
+
if (!trimmed) return { target: null };
|
|
55
|
+
|
|
56
|
+
const [subcommand, ...rest] = trimmed.split(/\s+/);
|
|
57
|
+
if (subcommand?.toLowerCase() !== "folder") {
|
|
58
|
+
return { target: { type: "uncommitted" }, additionalFocus: trimmed };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const paths = parseSimplifyPaths(rest.join(" "));
|
|
62
|
+
if (paths.length === 0)
|
|
63
|
+
return { target: null, error: "Usage: /simplify folder <path> [path...]" };
|
|
64
|
+
return { target: { type: "folder", paths } };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function hasUncommittedChanges(pi: ExtensionAPI): Promise<boolean> {
|
|
68
|
+
const { stdout, code } = await pi.exec("git", ["status", "--porcelain"]);
|
|
69
|
+
return code === 0 && stdout.trim().length > 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildSimplifyPrompt(target: SimplifyTarget, additionalFocus?: string): string {
|
|
73
|
+
let fullPrompt = SIMPLIFY_PROMPT;
|
|
74
|
+
|
|
75
|
+
if (target.type === "folder") {
|
|
76
|
+
fullPrompt += `\n\n## Scope Override: Folder Snapshot\n\nReview only the code in these repository-relative paths: ${target.paths.join(", ")}\n\nThis is a snapshot review, not a git diff review. Read files directly under these paths instead of relying on \`git diff\`. Only return candidates whose \`file\` is inside one of these paths.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (additionalFocus?.trim()) fullPrompt += `\n\n## Additional Focus\n\n${additionalFocus.trim()}`;
|
|
80
|
+
return fullPrompt;
|
|
81
|
+
}
|
|
82
|
+
|
|
29
83
|
function registerSimplifyCandidatesTool(
|
|
30
84
|
pi: ExtensionAPI,
|
|
31
85
|
state: {
|
|
@@ -107,6 +161,75 @@ function registerSimplifyCandidatesTool(
|
|
|
107
161
|
);
|
|
108
162
|
}
|
|
109
163
|
|
|
164
|
+
async function showFolderInput(ctx: ExtensionCommandContext): Promise<SimplifyTarget | null> {
|
|
165
|
+
const result = await ctx.ui.editor(
|
|
166
|
+
"Enter folders/files to simplify (space-separated or one per line):",
|
|
167
|
+
".",
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if (!result?.trim()) return null;
|
|
171
|
+
const paths = parseSimplifyPaths(result);
|
|
172
|
+
if (paths.length === 0) return null;
|
|
173
|
+
|
|
174
|
+
return { type: "folder", paths };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function showSimplifyTargetSelector(
|
|
178
|
+
ctx: ExtensionCommandContext,
|
|
179
|
+
pi: ExtensionAPI,
|
|
180
|
+
): Promise<SimplifyTarget | null> {
|
|
181
|
+
const presetItems: SelectItem[] = SIMPLIFY_PRESETS.map((preset) => ({
|
|
182
|
+
value: preset.value,
|
|
183
|
+
label: preset.label,
|
|
184
|
+
description: preset.description,
|
|
185
|
+
}));
|
|
186
|
+
const smartDefault = (await hasUncommittedChanges(pi)) ? "uncommitted" : "folder";
|
|
187
|
+
const smartDefaultIndex = presetItems.findIndex((item) => item.value === smartDefault);
|
|
188
|
+
|
|
189
|
+
while (true) {
|
|
190
|
+
const result = await ctx.ui.custom<SimplifyPresetValue | null>((tui, theme, _kb, done) => {
|
|
191
|
+
const container = new Container();
|
|
192
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
193
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("Select a simplify preset"))));
|
|
194
|
+
|
|
195
|
+
const selectList = new SelectList(presetItems, Math.min(presetItems.length, 10), {
|
|
196
|
+
selectedPrefix: (text) => theme.fg("accent", text),
|
|
197
|
+
selectedText: (text) => theme.fg("accent", text),
|
|
198
|
+
description: (text) => theme.fg("muted", text),
|
|
199
|
+
scrollInfo: (text) => theme.fg("dim", text),
|
|
200
|
+
noMatch: (text) => theme.fg("warning", text),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (smartDefaultIndex >= 0) selectList.setSelectedIndex(smartDefaultIndex);
|
|
204
|
+
selectList.onSelect = (item) => done(item.value as SimplifyPresetValue);
|
|
205
|
+
selectList.onCancel = () => done(null);
|
|
206
|
+
|
|
207
|
+
container.addChild(selectList);
|
|
208
|
+
container.addChild(new Text(theme.fg("dim", "Press enter to confirm or esc to cancel")));
|
|
209
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
render(width: number) {
|
|
213
|
+
return container.render(width);
|
|
214
|
+
},
|
|
215
|
+
invalidate() {
|
|
216
|
+
container.invalidate();
|
|
217
|
+
},
|
|
218
|
+
handleInput(data: string) {
|
|
219
|
+
selectList.handleInput(data);
|
|
220
|
+
tui.requestRender();
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!result) return null;
|
|
226
|
+
if (result === "uncommitted") return { type: "uncommitted" };
|
|
227
|
+
|
|
228
|
+
const target = await showFolderInput(ctx);
|
|
229
|
+
if (target) return target;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
110
233
|
async function applyFindings(
|
|
111
234
|
ctx: ExtensionCommandContext,
|
|
112
235
|
selected: SimplifyResult[],
|
|
@@ -167,7 +290,8 @@ export function registerSimplifyWorkflow(pi: ExtensionAPI) {
|
|
|
167
290
|
});
|
|
168
291
|
|
|
169
292
|
pi.registerCommand("simplify", {
|
|
170
|
-
description:
|
|
293
|
+
description:
|
|
294
|
+
"Review changed code or folders for reuse, quality, and efficiency, then apply fixes",
|
|
171
295
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
172
296
|
if (!ctx.hasUI) {
|
|
173
297
|
ctx.ui.notify("Simplify requires interactive mode", "error");
|
|
@@ -188,16 +312,29 @@ export function registerSimplifyWorkflow(pi: ExtensionAPI) {
|
|
|
188
312
|
return;
|
|
189
313
|
}
|
|
190
314
|
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
193
|
-
ctx.ui.notify(
|
|
315
|
+
const parsed = parseArgs(args);
|
|
316
|
+
if (parsed.error) {
|
|
317
|
+
ctx.ui.notify(parsed.error, "error");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let target = parsed.target;
|
|
322
|
+
if (!target) target = await showSimplifyTargetSelector(ctx, pi);
|
|
323
|
+
if (!target) {
|
|
324
|
+
ctx.ui.notify("Simplify cancelled", "info");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (target.type === "uncommitted" && !(await hasUncommittedChanges(pi))) {
|
|
329
|
+
ctx.ui.notify("No git changes found. Choose folder mode to simplify a snapshot.", "info");
|
|
194
330
|
return;
|
|
195
331
|
}
|
|
196
332
|
|
|
197
|
-
|
|
333
|
+
const targetHint =
|
|
334
|
+
target.type === "folder" ? `folders: ${target.paths.join(", ")}` : "uncommitted changes";
|
|
335
|
+
ctx.ui.notify(`Analyzing ${targetHint} for review findings...`, "info");
|
|
198
336
|
|
|
199
|
-
|
|
200
|
-
if (args.trim()) fullPrompt += `\n\n## Additional Focus\n\n${args.trim()}`;
|
|
337
|
+
const fullPrompt = buildSimplifyPrompt(target, parsed.additionalFocus);
|
|
201
338
|
|
|
202
339
|
latestToolCandidates = null;
|
|
203
340
|
const turnStarted = new Promise<void>((r) => {
|