@mrclrchtr/supi-extras 1.8.0 → 1.9.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
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# @mrclrchtr/supi-extras
|
|
2
4
|
|
|
3
5
|
Adds a bundle of small quality-of-life features to the [pi coding agent](https://github.com/earendil-works/pi).
|
|
@@ -14,7 +16,7 @@ For local development:
|
|
|
14
16
|
pi install ./packages/supi-extras
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+

|
|
18
20
|
|
|
19
21
|
## What you get
|
|
20
22
|
|
|
@@ -1,29 +1,20 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# @mrclrchtr/supi-core
|
|
2
4
|
|
|
3
5
|
Shared infrastructure for SuPi extensions.
|
|
4
6
|
|
|
5
|
-
This
|
|
7
|
+
This is a **pure library** — it does not register any pi commands or tools. The `/supi-settings` command is now available through `@mrclrchtr/supi-settings`.
|
|
6
8
|
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
|
-
### As a dependency for another extension
|
|
10
|
-
|
|
11
11
|
```bash
|
|
12
12
|
pnpm add @mrclrchtr/supi-core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
### As a pi package
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
pi install npm:@mrclrchtr/supi-core
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Installing it as a pi package adds the minimal `/supi-settings` extension surface.
|
|
22
|
-
|
|
23
15
|
## Package surfaces
|
|
24
16
|
|
|
25
17
|
- `@mrclrchtr/supi-core/api` — reusable helpers for other packages and extensions
|
|
26
|
-
- `@mrclrchtr/supi-core/extension` — minimal pi extension that registers `/supi-settings`
|
|
27
18
|
|
|
28
19
|
## What you get from the API
|
|
29
20
|
|
|
@@ -101,7 +92,6 @@ const message = wrapExtensionContext("my-extension", "hello", {
|
|
|
101
92
|
## Source
|
|
102
93
|
|
|
103
94
|
- `src/api.ts` — exported library surface
|
|
104
|
-
- `src/extension.ts` — minimal `/supi-settings` entrypoint
|
|
105
95
|
- `src/config.ts` — shared config loading and writing
|
|
106
96
|
- `src/config-settings.ts` — config-backed settings registration helper
|
|
107
97
|
- `src/settings-ui.ts` — shared settings overlay
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
"./config": "./src/config.ts",
|
|
41
41
|
"./context": "./src/context.ts",
|
|
42
42
|
"./debug": "./src/debug-registry.ts",
|
|
43
|
-
"./extension": "./src/extension.ts",
|
|
44
43
|
"./package.json": "./package.json",
|
|
45
44
|
"./path": "./src/path.ts",
|
|
46
45
|
"./project": "./src/project.ts",
|
|
@@ -50,10 +49,5 @@
|
|
|
50
49
|
"./terminal": "./src/terminal.ts",
|
|
51
50
|
"./tool-framework": "./src/tool-framework.ts",
|
|
52
51
|
"./types": "./src/types.ts"
|
|
53
|
-
},
|
|
54
|
-
"pi": {
|
|
55
|
-
"extensions": [
|
|
56
|
-
"./src/extension.ts"
|
|
57
|
-
]
|
|
58
52
|
}
|
|
59
53
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-extras",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "SuPi extras — command aliases, skill shorthand, tab spinner, /supi-stash prompt stash with TUI overlay, and other small utilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"clipboardy": "^5.3.1",
|
|
24
|
-
"@mrclrchtr/supi-core": "1.
|
|
24
|
+
"@mrclrchtr/supi-core": "1.9.1"
|
|
25
25
|
},
|
|
26
26
|
"bundledDependencies": [
|
|
27
27
|
"@mrclrchtr/supi-core"
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"pi": {
|
|
43
43
|
"extensions": [
|
|
44
|
-
"./src/extension.ts"
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
"./src/extension.ts"
|
|
45
|
+
],
|
|
46
|
+
"image": "https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-extras/assets/logo.png"
|
|
47
47
|
},
|
|
48
48
|
"exports": {
|
|
49
49
|
"./api": "./src/api.ts",
|
package/src/tab-spinner.ts
CHANGED
|
@@ -15,6 +15,18 @@ import { formatTitle, signalDone } from "@mrclrchtr/supi-core/terminal";
|
|
|
15
15
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
16
16
|
const AGENT_END_SETTLE_MS = 200;
|
|
17
17
|
|
|
18
|
+
/** Icons shown in the terminal title when a review phase completes. */
|
|
19
|
+
const BRIEF_DONE_ICON = "\u{1F4CB}"; // 📋 clipboard
|
|
20
|
+
const REVIEW_DONE_ICON = "\u{1F50D}"; // 🔍 magnifying glass
|
|
21
|
+
const REVIEW_FAILED_ICON = "\u2717"; // ✗ ballot x
|
|
22
|
+
|
|
23
|
+
type ReviewTitleState =
|
|
24
|
+
| { kind: "none" }
|
|
25
|
+
| { kind: "brief-done" }
|
|
26
|
+
| { kind: "brief-failed" }
|
|
27
|
+
| { kind: "review-done" }
|
|
28
|
+
| { kind: "review-failed" };
|
|
29
|
+
|
|
18
30
|
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: spinner state and event wiring are intentionally colocated
|
|
19
31
|
export default function tabSpinner(pi: ExtensionAPI) {
|
|
20
32
|
let timer: ReturnType<typeof setInterval> | null = null;
|
|
@@ -24,6 +36,7 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
24
36
|
let hasActiveAgent = false;
|
|
25
37
|
let pendingAgentEnd = false;
|
|
26
38
|
let askUserActive = 0;
|
|
39
|
+
let reviewTitleState: ReviewTitleState = { kind: "none" };
|
|
27
40
|
let currentCtx: ExtensionContext | undefined;
|
|
28
41
|
let cachedSessionName: string | undefined;
|
|
29
42
|
let cachedCwd: string | undefined;
|
|
@@ -34,6 +47,39 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
34
47
|
return formatTitle(getSessionNameSafe(), cachedCwd);
|
|
35
48
|
}
|
|
36
49
|
|
|
50
|
+
/** Set the terminal title to the review-phase icon if one is active. */
|
|
51
|
+
function applyReviewTitle(): boolean {
|
|
52
|
+
if (timer) return false; // spinner is active, don't override
|
|
53
|
+
|
|
54
|
+
const baseTitle = title();
|
|
55
|
+
const icon = reviewTitleIcon();
|
|
56
|
+
if (!icon) return false;
|
|
57
|
+
|
|
58
|
+
safelySetTitle(`${icon} ${baseTitle}`);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Return the appropriate review title icon, or empty string when idle. */
|
|
63
|
+
function reviewTitleIcon(): string {
|
|
64
|
+
switch (reviewTitleState.kind) {
|
|
65
|
+
case "brief-done":
|
|
66
|
+
return BRIEF_DONE_ICON;
|
|
67
|
+
case "brief-failed":
|
|
68
|
+
return REVIEW_FAILED_ICON;
|
|
69
|
+
case "review-done":
|
|
70
|
+
return REVIEW_DONE_ICON;
|
|
71
|
+
case "review-failed":
|
|
72
|
+
return REVIEW_FAILED_ICON;
|
|
73
|
+
case "none":
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Clear any active review title state and let normal title logic resume. */
|
|
79
|
+
function clearReviewTitle() {
|
|
80
|
+
reviewTitleState = { kind: "none" };
|
|
81
|
+
}
|
|
82
|
+
|
|
37
83
|
function clearPendingAgentEnd() {
|
|
38
84
|
pendingAgentEnd = false;
|
|
39
85
|
if (pendingAgentEndTimer) {
|
|
@@ -85,24 +131,24 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
85
131
|
}
|
|
86
132
|
}
|
|
87
133
|
|
|
88
|
-
/** Restore the base title
|
|
134
|
+
/** Restore the base title, or show the review icon if one is active. */
|
|
89
135
|
function stop() {
|
|
90
136
|
clearPendingAgentEnd();
|
|
91
137
|
clearSpinnerTimer();
|
|
92
138
|
frame = 0;
|
|
93
|
-
|
|
94
|
-
safelySetTitle(
|
|
139
|
+
if (applyReviewTitle()) return;
|
|
140
|
+
safelySetTitle(title());
|
|
95
141
|
}
|
|
96
142
|
|
|
97
|
-
/** Show the ✓ done symbol
|
|
143
|
+
/** Show the ✓ done symbol, or the review icon if one is active. */
|
|
98
144
|
function showDone() {
|
|
99
145
|
clearPendingAgentEnd();
|
|
100
146
|
clearSpinnerTimer();
|
|
101
147
|
frame = 0;
|
|
102
|
-
const baseTitle = title();
|
|
103
148
|
if (!currentCtx) return;
|
|
149
|
+
if (applyReviewTitle()) return;
|
|
104
150
|
try {
|
|
105
|
-
signalDone(currentCtx,
|
|
151
|
+
signalDone(currentCtx, title());
|
|
106
152
|
} catch {
|
|
107
153
|
handleStaleContext();
|
|
108
154
|
}
|
|
@@ -172,6 +218,7 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
172
218
|
});
|
|
173
219
|
|
|
174
220
|
pi.on("agent_start", async (_event, ctx) => {
|
|
221
|
+
clearReviewTitle();
|
|
175
222
|
if (pendingAgentEnd) {
|
|
176
223
|
resumePendingAgent(ctx);
|
|
177
224
|
return;
|
|
@@ -191,6 +238,7 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
191
238
|
|
|
192
239
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
193
240
|
unregisterEvents();
|
|
241
|
+
clearReviewTitle();
|
|
194
242
|
activeCount = 0;
|
|
195
243
|
rememberContext(ctx);
|
|
196
244
|
stop();
|
|
@@ -217,4 +265,22 @@ export default function tabSpinner(pi: ExtensionAPI) {
|
|
|
217
265
|
if (askUserActive === 0 && activeCount > 0) start();
|
|
218
266
|
}),
|
|
219
267
|
);
|
|
268
|
+
|
|
269
|
+
unregisterBusHandlers.push(
|
|
270
|
+
pi.events.on("supi:review:brief-done", (payload: unknown) => {
|
|
271
|
+
const data = payload as { kind: string };
|
|
272
|
+
reviewTitleState =
|
|
273
|
+
data.kind === "success" ? { kind: "brief-done" } : { kind: "brief-failed" };
|
|
274
|
+
applyReviewTitle();
|
|
275
|
+
}),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
unregisterBusHandlers.push(
|
|
279
|
+
pi.events.on("supi:review:review-done", (payload: unknown) => {
|
|
280
|
+
const data = payload as { kind: string };
|
|
281
|
+
reviewTitleState =
|
|
282
|
+
data.kind === "success" ? { kind: "review-done" } : { kind: "review-failed" };
|
|
283
|
+
applyReviewTitle();
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
220
286
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { registerSettingsCommand as default } from "./settings/settings-command.ts";
|