pi-formatter 1.0.0 → 1.0.2
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 +17 -19
- package/extensions/formatter/config.ts +3 -3
- package/extensions/index.ts +46 -28
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
A [pi](https://pi.dev) extension that auto-formats files after `write` and
|
|
4
4
|
`edit` tool calls.
|
|
5
5
|
|
|
6
|
-
By default, formatting runs once per
|
|
7
|
-
|
|
6
|
+
By default, formatting runs once per prompt — after the agent finishes all its
|
|
7
|
+
work and yields control back to you. You can also format after each individual
|
|
8
|
+
tool call or defer formatting until the session shuts down.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
the old default, set `"formatMode": "tool"`.
|
|
10
|
+
To format after every individual edit instead, set `"formatMode": "tool"`.
|
|
11
11
|
|
|
12
12
|
## 📦 Install
|
|
13
13
|
|
|
@@ -22,19 +22,17 @@ best-effort post-processing. Formatter failures never block tool results.
|
|
|
22
22
|
|
|
23
23
|
Formatting modes:
|
|
24
24
|
|
|
25
|
-
- `tool`: format immediately after each successful `write` or `edit` tool
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
keeping files formatted throughout the run.
|
|
25
|
+
- `tool`: format immediately after each successful `write` or `edit` tool call.
|
|
26
|
+
Use this mode when you want files on disk to stay formatted after every edit,
|
|
27
|
+
even while the agent is still working.
|
|
28
|
+
- `prompt`: collect all files touched during the agent run and format them once
|
|
29
|
+
when the agent finishes and yields control back to you. This is the default.
|
|
30
|
+
Use this mode to avoid mid-run formatter interruptions while still getting
|
|
31
|
+
clean files after each response.
|
|
33
32
|
- `session`: collect files touched during the current session and format them
|
|
34
|
-
once at
|
|
35
|
-
Use this mode when you want the fewest
|
|
36
|
-
|
|
37
|
-
runs stay pending until the session ends or changes.
|
|
33
|
+
once at session shutdown, reload, or switch.
|
|
34
|
+
Use this mode when you want the fewest interruptions and are okay with
|
|
35
|
+
formatting only when the session ends.
|
|
38
36
|
|
|
39
37
|
Supported file types:
|
|
40
38
|
|
|
@@ -61,14 +59,14 @@ folder (default: `~/.pi/agent`, overridable via `PI_CODING_AGENT_DIR`):
|
|
|
61
59
|
|
|
62
60
|
```json
|
|
63
61
|
{
|
|
64
|
-
"formatMode": "
|
|
62
|
+
"formatMode": "prompt",
|
|
65
63
|
"commandTimeoutMs": 10000,
|
|
66
64
|
"hideCallSummariesInTui": false
|
|
67
65
|
}
|
|
68
66
|
```
|
|
69
67
|
|
|
70
|
-
- `formatMode`: formatting strategy (`"tool"` | `"
|
|
71
|
-
default: `"
|
|
68
|
+
- `formatMode`: formatting strategy (`"tool"` | `"prompt"` | `"session"`,
|
|
69
|
+
default: `"prompt"`). Use `"tool"` to format after every individual edit.
|
|
72
70
|
- `commandTimeoutMs`: timeout (ms) per formatter command (default: `10000`)
|
|
73
71
|
- `hideCallSummariesInTui`: hide formatter pass/fail summaries in the TUI
|
|
74
72
|
(default: `false`)
|
|
@@ -2,11 +2,11 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
|
|
5
|
-
export type FormatMode = "tool" | "
|
|
5
|
+
export type FormatMode = "tool" | "prompt" | "session";
|
|
6
6
|
|
|
7
7
|
const DEFAULT_COMMAND_TIMEOUT_MS = 10_000;
|
|
8
8
|
const DEFAULT_HIDE_CALL_SUMMARIES_IN_TUI = false;
|
|
9
|
-
const DEFAULT_FORMAT_MODE: FormatMode = "
|
|
9
|
+
export const DEFAULT_FORMAT_MODE: FormatMode = "prompt";
|
|
10
10
|
const FORMATTER_CONFIG_FILE = "formatter.json";
|
|
11
11
|
|
|
12
12
|
export type FormatterConfigSnapshot = {
|
|
@@ -65,7 +65,7 @@ function parseBooleanValue(value: unknown, defaultValue: boolean): boolean {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function parseFormatMode(value: unknown, defaultValue: FormatMode): FormatMode {
|
|
68
|
-
if (value === "tool" || value === "
|
|
68
|
+
if (value === "tool" || value === "prompt" || value === "session") {
|
|
69
69
|
return value;
|
|
70
70
|
}
|
|
71
71
|
|
package/extensions/index.ts
CHANGED
|
@@ -79,9 +79,9 @@ function getFormatterSettingItems(
|
|
|
79
79
|
id: "formatMode",
|
|
80
80
|
label: "Format mode",
|
|
81
81
|
description:
|
|
82
|
-
"Choose whether formatting runs after each successful write/edit tool call, once after each
|
|
82
|
+
"Choose whether formatting runs after each successful write/edit tool call, once after each prompt completes, or once when the session shuts down.",
|
|
83
83
|
currentValue: config.formatMode,
|
|
84
|
-
values: ["tool", "
|
|
84
|
+
values: ["tool", "prompt", "session"],
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
id: "commandTimeoutMs",
|
|
@@ -112,7 +112,7 @@ type FormatterContext = {
|
|
|
112
112
|
export default function (pi: ExtensionAPI) {
|
|
113
113
|
let formatterConfig = loadFormatterConfig();
|
|
114
114
|
const formatQueueByPath = new Map<string, Promise<void>>();
|
|
115
|
-
const
|
|
115
|
+
const pendingPromptPaths = new Set<string>();
|
|
116
116
|
const pendingSessionPaths = new Set<string>();
|
|
117
117
|
|
|
118
118
|
const enqueueFormat = async (
|
|
@@ -135,12 +135,27 @@ export default function (pi: ExtensionAPI) {
|
|
|
135
135
|
await next;
|
|
136
136
|
};
|
|
137
137
|
|
|
138
|
+
const emitSummaryMessages = (
|
|
139
|
+
messages: string[],
|
|
140
|
+
ctx: FormatterContext,
|
|
141
|
+
): void => {
|
|
142
|
+
if (
|
|
143
|
+
!ctx.hasUI ||
|
|
144
|
+
formatterConfig.hideCallSummariesInTui ||
|
|
145
|
+
messages.length === 0
|
|
146
|
+
) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
ctx.ui.notify(messages.join("\n"), "info");
|
|
151
|
+
};
|
|
152
|
+
|
|
138
153
|
const formatResolvedPath = async (
|
|
139
154
|
filePath: string,
|
|
140
155
|
ctx: FormatterContext,
|
|
141
|
-
): Promise<
|
|
156
|
+
): Promise<string[]> => {
|
|
142
157
|
if (!(await pathExists(filePath))) {
|
|
143
|
-
return;
|
|
158
|
+
return [];
|
|
144
159
|
}
|
|
145
160
|
|
|
146
161
|
const showSummaries = !formatterConfig.hideCallSummariesInTui && ctx.hasUI;
|
|
@@ -155,6 +170,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
155
170
|
console.warn(normalizedMessage);
|
|
156
171
|
};
|
|
157
172
|
|
|
173
|
+
let summaryMessages: string[] = [];
|
|
174
|
+
|
|
158
175
|
await enqueueFormat(filePath, async () => {
|
|
159
176
|
const summaries: FormatCallSummary[] = [];
|
|
160
177
|
const summaryReporter = showSummaries
|
|
@@ -188,28 +205,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
188
205
|
}
|
|
189
206
|
|
|
190
207
|
const fileLabel = getRelativePathOrAbsolute(filePath, ctx.cwd);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
208
|
+
summaryMessages = summaries.map((summary) =>
|
|
209
|
+
formatCallSummary(summary, fileLabel),
|
|
210
|
+
);
|
|
195
211
|
});
|
|
212
|
+
|
|
213
|
+
return summaryMessages;
|
|
196
214
|
};
|
|
197
215
|
|
|
198
216
|
const flushPaths = async (
|
|
199
217
|
paths: Set<string>,
|
|
200
218
|
ctx: FormatterContext,
|
|
201
|
-
): Promise<
|
|
219
|
+
): Promise<string[]> => {
|
|
202
220
|
const batch = [...paths];
|
|
203
221
|
paths.clear();
|
|
204
222
|
|
|
223
|
+
const summaryMessages: string[] = [];
|
|
224
|
+
|
|
205
225
|
for (const filePath of batch) {
|
|
206
|
-
await formatResolvedPath(filePath, ctx);
|
|
226
|
+
summaryMessages.push(...(await formatResolvedPath(filePath, ctx)));
|
|
207
227
|
}
|
|
228
|
+
|
|
229
|
+
return summaryMessages;
|
|
208
230
|
};
|
|
209
231
|
|
|
210
232
|
const flushPendingPaths = async (ctx: FormatterContext): Promise<void> => {
|
|
211
|
-
|
|
212
|
-
|
|
233
|
+
const summaryMessages = [
|
|
234
|
+
...(await flushPaths(pendingPromptPaths, ctx)),
|
|
235
|
+
...(await flushPaths(pendingSessionPaths, ctx)),
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
emitSummaryMessages(summaryMessages, ctx);
|
|
213
239
|
};
|
|
214
240
|
|
|
215
241
|
const reloadFormatterConfig = () => {
|
|
@@ -231,36 +257,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
231
257
|
}
|
|
232
258
|
|
|
233
259
|
if (formatterConfig.formatMode === "tool") {
|
|
234
|
-
await formatResolvedPath(filePath, ctx);
|
|
260
|
+
emitSummaryMessages(await formatResolvedPath(filePath, ctx), ctx);
|
|
235
261
|
return;
|
|
236
262
|
}
|
|
237
263
|
|
|
238
|
-
if (formatterConfig.formatMode === "
|
|
239
|
-
|
|
264
|
+
if (formatterConfig.formatMode === "prompt") {
|
|
265
|
+
pendingPromptPaths.add(filePath);
|
|
240
266
|
return;
|
|
241
267
|
}
|
|
242
268
|
|
|
243
269
|
pendingSessionPaths.add(filePath);
|
|
244
270
|
});
|
|
245
271
|
|
|
246
|
-
pi.on("turn_end", async (_event, ctx) => {
|
|
247
|
-
if (pendingTurnPaths.size === 0) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
await flushPaths(pendingTurnPaths, ctx);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
272
|
pi.on("agent_end", async (_event, ctx) => {
|
|
255
|
-
if (
|
|
273
|
+
if (pendingPromptPaths.size === 0) {
|
|
256
274
|
return;
|
|
257
275
|
}
|
|
258
276
|
|
|
259
|
-
await flushPaths(
|
|
277
|
+
emitSummaryMessages(await flushPaths(pendingPromptPaths, ctx), ctx);
|
|
260
278
|
});
|
|
261
279
|
|
|
262
280
|
pi.on("session_switch", async (_event, ctx) => {
|
|
263
|
-
if (
|
|
281
|
+
if (pendingPromptPaths.size === 0 && pendingSessionPaths.size === 0) {
|
|
264
282
|
return;
|
|
265
283
|
}
|
|
266
284
|
|
|
@@ -268,7 +286,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
268
286
|
});
|
|
269
287
|
|
|
270
288
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
271
|
-
if (
|
|
289
|
+
if (pendingPromptPaths.size === 0 && pendingSessionPaths.size === 0) {
|
|
272
290
|
return;
|
|
273
291
|
}
|
|
274
292
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-formatter",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Pi extension that auto-formats files
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Pi extension that auto-formats files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"extensions",
|