@xynogen/pix-optimizer 1.1.16 → 1.1.17
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 +1 -1
- package/package.json +1 -1
- package/src/capability.test.ts +42 -0
- package/src/capability.ts +16 -0
- package/src/index.ts +1 -1
- package/src/json.ts +4 -5
- package/src/rtk.test.ts +31 -1
- package/src/rtk.ts +9 -17
- package/src/status.ts +3 -3
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ Switch the style live from the **`/optimizer` overlay** — a fifth `icons` row
|
|
|
44
44
|
at the bottom cycles `nerd → unicode → ascii` with `←→`; the choice persists to
|
|
45
45
|
`~/.pi/agent/optimizer.json`.
|
|
46
46
|
|
|
47
|
-
Icons follow the **global** `pix-pretty` mode — set via `/
|
|
47
|
+
Icons follow the **global** `pix-pretty` mode — set via `/pix` or `PRETTY_ICONS`
|
|
48
48
|
env var. The optimizer no longer has its own toggle.
|
|
49
49
|
|
|
50
50
|
## Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-optimizer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.17",
|
|
4
4
|
"description": "Performance optimization suite for Pi Coding Agent - caveman mode + RTK tool rewriting + jq/TOON JSON compression + ponytail lazy-dev mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it, mock } from "bun:test";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { canExecute } from "./capability.ts";
|
|
4
|
+
|
|
5
|
+
function executor(impl: ExtensionAPI["exec"]): Pick<ExtensionAPI, "exec"> {
|
|
6
|
+
return { exec: impl };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe("canExecute", () => {
|
|
10
|
+
it("runs the requested capability probe without a shell", async () => {
|
|
11
|
+
const exec = mock(async () => ({
|
|
12
|
+
stdout: "jq-1.7",
|
|
13
|
+
stderr: "",
|
|
14
|
+
code: 0,
|
|
15
|
+
killed: false,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
expect(await canExecute(executor(exec), "jq", ["--version"])).toBe(true);
|
|
19
|
+
expect(exec).toHaveBeenCalledWith("jq", ["--version"], { timeout: 3000 });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("supports a custom timeout", async () => {
|
|
23
|
+
const exec = mock(async () => ({ stdout: "2.3.0", stderr: "", code: 0, killed: false }));
|
|
24
|
+
|
|
25
|
+
expect(await canExecute(executor(exec), "toon", ["--version"], 1000)).toBe(true);
|
|
26
|
+
expect(exec).toHaveBeenCalledWith("toon", ["--version"], { timeout: 1000 });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("returns false for a non-zero exit", async () => {
|
|
30
|
+
const exec = mock(async () => ({ stdout: "", stderr: "failed", code: 1, killed: false }));
|
|
31
|
+
|
|
32
|
+
expect(await canExecute(executor(exec), "jq", ["--version"])).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("returns false when the executable cannot be spawned", async () => {
|
|
36
|
+
const exec = mock(async () => {
|
|
37
|
+
throw new Error("ENOENT");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(await canExecute(executor(exec), "toon", ["--version"])).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/** Test whether a command can perform a harmless operation through Pi's executor. */
|
|
4
|
+
export async function canExecute(
|
|
5
|
+
pi: Pick<ExtensionAPI, "exec">,
|
|
6
|
+
command: string,
|
|
7
|
+
args: string[],
|
|
8
|
+
timeout = 3000,
|
|
9
|
+
): Promise<boolean> {
|
|
10
|
+
try {
|
|
11
|
+
const result = await pi.exec(command, args, { timeout });
|
|
12
|
+
return result.code === 0;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { type OptimizerHandle, OptimizerStatus, type OptimizerTool } from "./sta
|
|
|
23
23
|
import { filterModelWarnings } from "./tool-result-filter.ts";
|
|
24
24
|
|
|
25
25
|
export default function optimizer(pi: ExtensionAPI) {
|
|
26
|
-
// Icons follow the global pix-pretty mode (set via /
|
|
26
|
+
// Icons follow the global pix-pretty mode (set via /pix); no local state.
|
|
27
27
|
const status = new OptimizerStatus();
|
|
28
28
|
|
|
29
29
|
// Each module registers its own lifecycle hooks and returns a handle the
|
package/src/json.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ExtensionCommandContext,
|
|
23
23
|
ExtensionContext,
|
|
24
24
|
} from "@earendil-works/pi-coding-agent";
|
|
25
|
+
import { canExecute } from "./capability.ts";
|
|
25
26
|
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
26
27
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
27
28
|
|
|
@@ -241,12 +242,10 @@ export function json(pi: ExtensionAPI, status: OptimizerStatus): OptimizerHandle
|
|
|
241
242
|
|
|
242
243
|
// Probe once on first JSON-relevant prompt.
|
|
243
244
|
if (jqAvailable === null || toonAvailable === null) {
|
|
244
|
-
|
|
245
|
-
pi
|
|
246
|
-
pi
|
|
245
|
+
[jqAvailable, toonAvailable] = await Promise.all([
|
|
246
|
+
canExecute(pi, "jq", ["--version"]),
|
|
247
|
+
canExecute(pi, "toon", ["--version"]),
|
|
247
248
|
]);
|
|
248
|
-
jqAvailable = jqRes.code === 0;
|
|
249
|
-
toonAvailable = toonRes.code === 0;
|
|
250
249
|
if (!jqAvailable)
|
|
251
250
|
ctx.ui.notify(
|
|
252
251
|
"jq not found — JSON/TOON guidance disabled. Install: sudo apt install jq or brew install jq",
|
package/src/rtk.test.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
1
|
+
import { describe, expect, it, mock } from "bun:test";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
3
|
import {
|
|
3
4
|
applyRtkRewrite,
|
|
4
5
|
type BashCallEvent,
|
|
5
6
|
buildSudoBlockReason,
|
|
6
7
|
detectSudoSegments,
|
|
8
|
+
probeRtkAvailability,
|
|
7
9
|
rewriteChain,
|
|
8
10
|
splitChain,
|
|
9
11
|
} from "./rtk.ts";
|
|
@@ -13,6 +15,34 @@ function bashEvent(command: string): BashCallEvent {
|
|
|
13
15
|
return { toolName: "bash", input: { command } };
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
describe("probeRtkAvailability", () => {
|
|
19
|
+
it("probes rtk directly and accepts a successful version check", async () => {
|
|
20
|
+
const exec = mock(async () => ({
|
|
21
|
+
stdout: "rtk 0.1.0",
|
|
22
|
+
stderr: "",
|
|
23
|
+
code: 0,
|
|
24
|
+
killed: false,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
expect(await probeRtkAvailability({ exec } as Pick<ExtensionAPI, "exec">)).toBe(true);
|
|
28
|
+
expect(exec).toHaveBeenCalledWith("rtk", ["--version"], { timeout: 3000 });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("rejects an unsuccessful version check", async () => {
|
|
32
|
+
const exec = mock(async () => ({ stdout: "", stderr: "missing", code: 1, killed: false }));
|
|
33
|
+
|
|
34
|
+
expect(await probeRtkAvailability({ exec } as Pick<ExtensionAPI, "exec">)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("treats a spawn failure as unavailable", async () => {
|
|
38
|
+
const exec = mock(async () => {
|
|
39
|
+
throw new Error("ENOENT");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(await probeRtkAvailability({ exec } as Pick<ExtensionAPI, "exec">)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
16
46
|
describe("splitChain", () => {
|
|
17
47
|
it("returns single segment for plain command", () => {
|
|
18
48
|
expect(splitChain("git status")).toEqual(["git status"]);
|
package/src/rtk.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ExtensionCommandContext,
|
|
12
12
|
ExtensionContext,
|
|
13
13
|
} from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { canExecute } from "./capability.ts";
|
|
14
15
|
import { loadOptValue, saveOptValue } from "./persist.ts";
|
|
15
16
|
import type { OptimizerHandle, OptimizerStatus } from "./status.ts";
|
|
16
17
|
|
|
@@ -136,7 +137,11 @@ const RTK_COMMANDS = new Set([
|
|
|
136
137
|
interface RtkStatus {
|
|
137
138
|
available: boolean;
|
|
138
139
|
checkedAt: number;
|
|
139
|
-
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Probe the command we actually use instead of relying on a platform-specific locator. */
|
|
143
|
+
export function probeRtkAvailability(pi: Pick<ExtensionAPI, "exec">): Promise<boolean> {
|
|
144
|
+
return canExecute(pi, "rtk", ["--version"]);
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
/**
|
|
@@ -242,25 +247,12 @@ export function rtk(pi: ExtensionAPI, status: OptimizerStatus): OptimizerHandle
|
|
|
242
247
|
return rtkStatus;
|
|
243
248
|
}
|
|
244
249
|
|
|
245
|
-
|
|
246
|
-
const result = await pi.exec("which", ["rtk"], { timeout: 1000 });
|
|
247
|
-
if (result.code === 0 && result.stdout?.trim()) {
|
|
248
|
-
rtkStatus = {
|
|
249
|
-
available: true,
|
|
250
|
-
checkedAt: Date.now(),
|
|
251
|
-
path: result.stdout.trim(),
|
|
252
|
-
};
|
|
253
|
-
warnedMissing = false;
|
|
254
|
-
return rtkStatus;
|
|
255
|
-
}
|
|
256
|
-
} catch (_error) {
|
|
257
|
-
// which command failed
|
|
258
|
-
}
|
|
259
|
-
|
|
250
|
+
const available = await probeRtkAvailability(pi);
|
|
260
251
|
rtkStatus = {
|
|
261
|
-
available
|
|
252
|
+
available,
|
|
262
253
|
checkedAt: Date.now(),
|
|
263
254
|
};
|
|
255
|
+
if (available) warnedMissing = false;
|
|
264
256
|
return rtkStatus;
|
|
265
257
|
};
|
|
266
258
|
|
package/src/status.ts
CHANGED
|
@@ -45,7 +45,7 @@ export type OptimizerTool = "caveman" | "rtk" | "toon" | "ponytail";
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Icons come from the shared pix-pretty catalog and follow the ONE global icon
|
|
48
|
-
* mode (set via /
|
|
48
|
+
* mode (set via /pix). The optimizer no longer owns a mode: each tool maps
|
|
49
49
|
* to a semantic catalog key, and the cell renders whatever the active mode
|
|
50
50
|
* resolves — nerd / unicode / ascii — with no per-package knob.
|
|
51
51
|
*/
|
|
@@ -76,7 +76,7 @@ export type Colorize = (color: ThemeColor, text: string) => string;
|
|
|
76
76
|
* Build the colored status string for a set of tool states. ALL tool icons are
|
|
77
77
|
* always shown, in TOOL_ORDER; each is accent-colored when its tool is enabled
|
|
78
78
|
* and dim when disabled. Glyphs resolve against the active global icon mode
|
|
79
|
-
* (see /
|
|
79
|
+
* (see /pix). `color` applies the theme color (e.g. theme.fg).
|
|
80
80
|
*
|
|
81
81
|
* Pure + exported for tests (pass a tagging colorizer to assert per-icon color).
|
|
82
82
|
* A trailing space separates the cell from the next status segment.
|
|
@@ -102,7 +102,7 @@ export class OptimizerStatus {
|
|
|
102
102
|
|
|
103
103
|
constructor() {
|
|
104
104
|
// The cell is a pushed status (setStatus), so it does NOT auto-refresh
|
|
105
|
-
// when /
|
|
105
|
+
// when /pix flips the global icon mode. Repaint it on mode change
|
|
106
106
|
// using the last-seen ctx (no-op until the cell has painted once).
|
|
107
107
|
onIconModeChange(() => {
|
|
108
108
|
if (this.lastCtx) this.paint(this.lastCtx);
|