@preapexis/piexis 0.2.4 → 0.2.5
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 +22 -2
- package/apps/server/src/index.ts +97 -0
- package/apps/server/src/session-manager.ts +95 -0
- package/apps/server/src/workspace.ts +39 -0
- package/{tsconfig.json → apps/server/tsconfig.json} +3 -4
- package/apps/web/dist/assets/index-D4n3_lAw.js +10 -0
- package/apps/web/dist/assets/index-igiK0FPY.css +1 -0
- package/apps/web/dist/index.html +3 -0
- package/bin/piexis-web.mjs +24 -0
- package/extensions/webui.ts +45 -0
- package/package.json +38 -3
- package/packages/pi-integration/src/rpc-client.ts +99 -0
- package/packages/shared/src/index.ts +27 -0
- package/themes/midnight.json +81 -0
- package/.github/workflows/publish.yml +0 -35
- package/AGENTS.md +0 -38
- package/scripts/release.mjs +0 -32
- package/skills-lock.json +0 -365
- package/tests/agents.test.mjs +0 -605
- package/tests/ask-question-pty.test.mjs +0 -16
- package/tests/ask-question.test.mjs +0 -269
- package/tests/fixtures/agents-provider.ts +0 -55
- package/tests/fixtures/ask-question-pty.py +0 -91
- package/tests/fixtures/ask-question-terminal.mjs +0 -69
- package/tests/fixtures/recommended-tools-terminal.mjs +0 -103
- package/tests/init-workflow.test.mjs +0 -31
- package/tests/recommended-tools-pty.test.mjs +0 -16
- package/tests/recommended-tools.test.mjs +0 -423
- package/tests/resources.test.mjs +0 -66
- package/tests/skills-routing.test.mjs +0 -54
- package/tests/ui-tip-reload.test.mjs +0 -70
- package/tests/ui.test.mjs +0 -450
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const value = (flag) => { const index = args.indexOf(flag); return index < 0 ? undefined : args[index + 1]; };
|
|
9
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
10
|
+
console.log("Usage: piexis-web [--workspace <directory>] [--port <port>] [--token <token>]");
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
|
+
const workspace = path.resolve(value("--workspace") || process.cwd());
|
|
15
|
+
const port = value("--port") || process.env.PIXIS_PORT || "4317";
|
|
16
|
+
const token = value("--token") || process.env.PIXIS_TOKEN || randomBytes(32).toString("base64url");
|
|
17
|
+
const server = path.join(root, "apps", "server", "src", "index.ts");
|
|
18
|
+
const child = spawn(process.execPath, ["--import", "tsx", server], {
|
|
19
|
+
cwd: workspace,
|
|
20
|
+
env: { ...process.env, PIXIS_WORKSPACE: workspace, PIXIS_PORT: port, PIXIS_TOKEN: token, PIXIS_ORIGIN: `http://127.0.0.1:${port}` },
|
|
21
|
+
stdio: "inherit",
|
|
22
|
+
windowsHide: true
|
|
23
|
+
});
|
|
24
|
+
child.on("exit", (code) => process.exitCode = code ?? 1);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
|
|
8
|
+
async function availablePort(): Promise<number> {
|
|
9
|
+
const server = createServer();
|
|
10
|
+
await new Promise<void>((resolve, reject) => {
|
|
11
|
+
server.once("error", reject);
|
|
12
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
13
|
+
});
|
|
14
|
+
const address = server.address();
|
|
15
|
+
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
16
|
+
if (!address || typeof address === "string") throw new Error("Could not select a local PIXIS WebUI port.");
|
|
17
|
+
return address.port;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Starts the separate local WebUI; Pi's TUI remains usable in this process. */
|
|
21
|
+
export default function webui(pi: ExtensionAPI) {
|
|
22
|
+
const servers = new Map<string, ChildProcess>();
|
|
23
|
+
const launcher = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../bin/piexis-web.mjs");
|
|
24
|
+
|
|
25
|
+
pi.registerCommand("webui", {
|
|
26
|
+
description: "Start the local PIXIS WebUI for this workspace",
|
|
27
|
+
handler: async (_args, ctx) => {
|
|
28
|
+
const running = servers.get(ctx.cwd);
|
|
29
|
+
if (running && running.exitCode === null && !running.killed) {
|
|
30
|
+
ctx.ui.notify("PIXIS WebUI is already running for this workspace.", "info");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const token = randomBytes(32).toString("base64url");
|
|
34
|
+
const port = await availablePort();
|
|
35
|
+
const child = spawn(process.execPath, [launcher, "--workspace", ctx.cwd, "--port", `${port}`, "--token", token], {
|
|
36
|
+
detached: true,
|
|
37
|
+
stdio: "ignore",
|
|
38
|
+
windowsHide: true
|
|
39
|
+
});
|
|
40
|
+
child.unref();
|
|
41
|
+
servers.set(ctx.cwd, child);
|
|
42
|
+
ctx.ui.notify(`PIXIS WebUI started: http://127.0.0.1:${port}/#token=${token}`, "info");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preapexis/piexis",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Extensions, skills, prompts, and themes for pi",
|
|
6
6
|
"repository": {
|
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
"pi-package"
|
|
12
12
|
],
|
|
13
13
|
"license": "MIT",
|
|
14
|
+
"bin": {
|
|
15
|
+
"piexis-web": "./bin/piexis-web.mjs"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"apps/server/",
|
|
19
|
+
"apps/web/dist/",
|
|
20
|
+
"bin/",
|
|
21
|
+
"extensions/",
|
|
22
|
+
"lib/",
|
|
23
|
+
"packages/",
|
|
24
|
+
"prompts/",
|
|
25
|
+
"schemas/",
|
|
26
|
+
"skills/",
|
|
27
|
+
"themes/"
|
|
28
|
+
],
|
|
14
29
|
"engines": {
|
|
15
30
|
"node": ">=22.19.0"
|
|
16
31
|
},
|
|
@@ -21,7 +36,13 @@
|
|
|
21
36
|
},
|
|
22
37
|
"scripts": {
|
|
23
38
|
"test": "node --test tests/*.test.mjs",
|
|
39
|
+
"test:webui": "node --import tsx --test tests/webui/*.test.ts",
|
|
24
40
|
"check": "tsc --noEmit",
|
|
41
|
+
"check:server": "tsc -p apps/server/tsconfig.json --noEmit",
|
|
42
|
+
"check:web": "tsc -p apps/web/tsconfig.json --noEmit",
|
|
43
|
+
"build": "vite build --config apps/web/vite.config.ts",
|
|
44
|
+
"prepack": "npm run build",
|
|
45
|
+
"dev": "node scripts/dev.mjs",
|
|
25
46
|
"release": "node scripts/release.mjs"
|
|
26
47
|
},
|
|
27
48
|
"pi": {
|
|
@@ -29,7 +50,8 @@
|
|
|
29
50
|
"./extensions/ask-question.ts",
|
|
30
51
|
"./extensions/ui.ts",
|
|
31
52
|
"./extensions/recommended-tools.ts",
|
|
32
|
-
"./extensions/agents.ts"
|
|
53
|
+
"./extensions/agents.ts",
|
|
54
|
+
"./extensions/webui.ts"
|
|
33
55
|
],
|
|
34
56
|
"skills": [
|
|
35
57
|
"./skills"
|
|
@@ -43,6 +65,19 @@
|
|
|
43
65
|
},
|
|
44
66
|
"devDependencies": {
|
|
45
67
|
"@types/node": "24.10.0",
|
|
46
|
-
"
|
|
68
|
+
"@types/react": "^19.3.0",
|
|
69
|
+
"@types/react-dom": "^19.3.0",
|
|
70
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
71
|
+
"typescript": "5.9.3",
|
|
72
|
+
"vite": "^8.3.4"
|
|
73
|
+
},
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"@fastify/static": "^10.1.6",
|
|
76
|
+
"@fastify/websocket": "^11.3.4",
|
|
77
|
+
"fastify": "^5.12.5",
|
|
78
|
+
"react": "^19.3.0",
|
|
79
|
+
"react-dom": "^19.3.0",
|
|
80
|
+
"tsx": "^4.23.15",
|
|
81
|
+
"zod": "^4.6.5"
|
|
47
82
|
}
|
|
48
83
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ChildProcess, spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
|
|
5
|
+
export interface RpcResponse {
|
|
6
|
+
type: "response";
|
|
7
|
+
id?: string;
|
|
8
|
+
command: string;
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type RpcRecord = Record<string, unknown>;
|
|
15
|
+
|
|
16
|
+
/** Long-lived, documented Pi RPC client. It treats stdout as strict JSONL. */
|
|
17
|
+
export class PiRpcClient extends EventEmitter {
|
|
18
|
+
#child?: ChildProcess;
|
|
19
|
+
#buffer = "";
|
|
20
|
+
#pending = new Map<string, { resolve: (value: RpcResponse) => void; reject: (reason: Error) => void }>();
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly cwd: string,
|
|
24
|
+
private readonly executable = process.env.PIXIS_PI_BIN || (process.platform === "win32" ? "pi.cmd" : "pi"),
|
|
25
|
+
private readonly executableArgs: string[] = []
|
|
26
|
+
) {
|
|
27
|
+
super();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
start(): void {
|
|
31
|
+
if (this.#child) return;
|
|
32
|
+
const child = spawn(this.executable, [...this.executableArgs, "--mode", "rpc"], { cwd: this.cwd, stdio: ["pipe", "pipe", "pipe"], windowsHide: true });
|
|
33
|
+
this.#child = child;
|
|
34
|
+
child.stdout?.on("data", (chunk: Buffer) => this.#consume(chunk.toString("utf8")));
|
|
35
|
+
child.stderr?.on("data", (chunk: Buffer) => this.emit("diagnostic", chunk.toString("utf8")));
|
|
36
|
+
child.on("error", (error) => this.#fail(error));
|
|
37
|
+
child.on("exit", (code, signal) => this.#fail(new Error(`Pi exited (${code ?? signal ?? "unknown"})`)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async command(command: Record<string, unknown>): Promise<RpcResponse> {
|
|
41
|
+
this.start();
|
|
42
|
+
const id = randomUUID();
|
|
43
|
+
const record = { ...command, id };
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
this.#pending.set(id, { resolve, reject });
|
|
46
|
+
const input = this.#child?.stdin;
|
|
47
|
+
if (!input || input.destroyed) return reject(new Error("Pi RPC stdin is unavailable"));
|
|
48
|
+
input.write(`${JSON.stringify(record)}\n`, "utf8", (error) => {
|
|
49
|
+
if (error) {
|
|
50
|
+
this.#pending.delete(id);
|
|
51
|
+
reject(error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
send(record: Record<string, unknown>): void {
|
|
58
|
+
const input = this.#child?.stdin;
|
|
59
|
+
if (!input || input.destroyed) throw new Error("Pi RPC stdin is unavailable");
|
|
60
|
+
input.write(`${JSON.stringify(record)}\n`, "utf8");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
dispose(): void {
|
|
64
|
+
this.#child?.stdin?.end();
|
|
65
|
+
this.#child = undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#consume(chunk: string): void {
|
|
69
|
+
this.#buffer += chunk;
|
|
70
|
+
let newline: number;
|
|
71
|
+
while ((newline = this.#buffer.indexOf("\n")) >= 0) {
|
|
72
|
+
const line = this.#buffer.slice(0, newline).replace(/\r$/, "");
|
|
73
|
+
this.#buffer = this.#buffer.slice(newline + 1);
|
|
74
|
+
if (!line) continue;
|
|
75
|
+
try {
|
|
76
|
+
const record = JSON.parse(line) as RpcRecord;
|
|
77
|
+
if (record.type === "response" && typeof record.id === "string" && typeof record.command === "string" && typeof record.success === "boolean") {
|
|
78
|
+
const pending = this.#pending.get(record.id);
|
|
79
|
+
if (pending) {
|
|
80
|
+
this.#pending.delete(record.id);
|
|
81
|
+
pending.resolve(record as unknown as RpcResponse);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.emit("record", record);
|
|
86
|
+
} catch {
|
|
87
|
+
this.emit("diagnostic", `Ignoring malformed Pi RPC record: ${line.slice(0, 200)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#fail(error: Error): void {
|
|
93
|
+
if (!this.#child && this.#pending.size === 0) return;
|
|
94
|
+
this.#child = undefined;
|
|
95
|
+
for (const { reject } of this.#pending.values()) reject(error);
|
|
96
|
+
this.#pending.clear();
|
|
97
|
+
this.emit("exit", error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type SessionStatus = "starting" | "ready" | "running" | "waiting" | "cancelling" | "failed" | "stopped";
|
|
2
|
+
|
|
3
|
+
export interface SessionSummary {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
status: SessionStatus;
|
|
7
|
+
model?: string;
|
|
8
|
+
sessionFile?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ClientCommand =
|
|
12
|
+
| { type: "list_sessions" }
|
|
13
|
+
| { type: "subscribe"; sessionId: string }
|
|
14
|
+
| { type: "create_session" }
|
|
15
|
+
| { type: "prompt"; sessionId: string; message: string }
|
|
16
|
+
| { type: "abort"; sessionId: string }
|
|
17
|
+
| { type: "extension_ui_response"; sessionId: string; id: string; value?: string; confirmed?: boolean; cancelled?: boolean }
|
|
18
|
+
| { type: "git_status"; sessionId: string }
|
|
19
|
+
| { type: "git_diff"; sessionId: string; path: string };
|
|
20
|
+
|
|
21
|
+
export type ServerMessage =
|
|
22
|
+
| { type: "snapshot"; sessions: SessionSummary[]; sessionId: string; state: unknown; messages: unknown[] }
|
|
23
|
+
| { type: "session"; session: SessionSummary }
|
|
24
|
+
| { type: "pi_event"; sessionId: string; event: unknown }
|
|
25
|
+
| { type: "extension_ui"; sessionId: string; request: unknown }
|
|
26
|
+
| { type: "workspace"; sessionId: string; action: "git_status" | "git_diff"; data: unknown }
|
|
27
|
+
| { type: "error"; message: string; sessionId?: string };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
3
|
+
"name": "midnight",
|
|
4
|
+
"vars": {
|
|
5
|
+
"cyan": "#67d9ef",
|
|
6
|
+
"violet": "#bb9af7",
|
|
7
|
+
"blue": "#7aa2f7",
|
|
8
|
+
"ink": "#141821",
|
|
9
|
+
"panel": "#1c2230",
|
|
10
|
+
"panelRaised": "#2c3548",
|
|
11
|
+
"text": "#e2e8f0",
|
|
12
|
+
"muted": "#a9b7cc",
|
|
13
|
+
"dim": "#7e8da6",
|
|
14
|
+
"mint": "#88dfb3",
|
|
15
|
+
"rose": "#ff859d",
|
|
16
|
+
"gold": "#f5ce7b"
|
|
17
|
+
},
|
|
18
|
+
"colors": {
|
|
19
|
+
"accent": "cyan",
|
|
20
|
+
"border": "blue",
|
|
21
|
+
"borderAccent": "cyan",
|
|
22
|
+
"borderMuted": "dim",
|
|
23
|
+
"success": "mint",
|
|
24
|
+
"error": "rose",
|
|
25
|
+
"warning": "gold",
|
|
26
|
+
"muted": "muted",
|
|
27
|
+
"dim": "dim",
|
|
28
|
+
"text": "text",
|
|
29
|
+
"thinkingText": "muted",
|
|
30
|
+
"selectedBg": "panelRaised",
|
|
31
|
+
"scrollbarTrack": "panel",
|
|
32
|
+
"scrollbarThumb": "blue",
|
|
33
|
+
"searchMatchBg": "#3c3655",
|
|
34
|
+
"searchMatchText": "text",
|
|
35
|
+
"userMessageBg": "panel",
|
|
36
|
+
"userMessageText": "text",
|
|
37
|
+
"customMessageBg": "#232337",
|
|
38
|
+
"customMessageText": "text",
|
|
39
|
+
"customMessageLabel": "violet",
|
|
40
|
+
"toolPendingBg": "#232b3c",
|
|
41
|
+
"toolSuccessBg": "#1c302b",
|
|
42
|
+
"toolErrorBg": "#37242f",
|
|
43
|
+
"toolTitle": "cyan",
|
|
44
|
+
"toolOutput": "muted",
|
|
45
|
+
"mdHeading": "violet",
|
|
46
|
+
"mdLink": "cyan",
|
|
47
|
+
"mdLinkUrl": "muted",
|
|
48
|
+
"mdCode": "mint",
|
|
49
|
+
"mdCodeBlock": "text",
|
|
50
|
+
"mdCodeBlockBorder": "blue",
|
|
51
|
+
"mdQuote": "muted",
|
|
52
|
+
"mdQuoteBorder": "violet",
|
|
53
|
+
"mdHr": "dim",
|
|
54
|
+
"mdListBullet": "cyan",
|
|
55
|
+
"toolDiffAdded": "mint",
|
|
56
|
+
"toolDiffRemoved": "rose",
|
|
57
|
+
"toolDiffContext": "muted",
|
|
58
|
+
"syntaxComment": "dim",
|
|
59
|
+
"syntaxKeyword": "violet",
|
|
60
|
+
"syntaxFunction": "blue",
|
|
61
|
+
"syntaxVariable": "text",
|
|
62
|
+
"syntaxString": "mint",
|
|
63
|
+
"syntaxNumber": "gold",
|
|
64
|
+
"syntaxType": "cyan",
|
|
65
|
+
"syntaxOperator": "cyan",
|
|
66
|
+
"syntaxPunctuation": "muted",
|
|
67
|
+
"thinkingOff": "dim",
|
|
68
|
+
"thinkingMinimal": "muted",
|
|
69
|
+
"thinkingLow": "blue",
|
|
70
|
+
"thinkingMedium": "cyan",
|
|
71
|
+
"thinkingHigh": "violet",
|
|
72
|
+
"thinkingXhigh": "rose",
|
|
73
|
+
"thinkingMax": "gold",
|
|
74
|
+
"bashMode": "gold"
|
|
75
|
+
},
|
|
76
|
+
"export": {
|
|
77
|
+
"pageBg": "ink",
|
|
78
|
+
"cardBg": "panel",
|
|
79
|
+
"infoBg": "panelRaised"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
name: Publish npm package
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
publish:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
|
|
12
|
-
permissions:
|
|
13
|
-
contents: read
|
|
14
|
-
id-token: write
|
|
15
|
-
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v6
|
|
18
|
-
|
|
19
|
-
- uses: actions/setup-node@v6
|
|
20
|
-
with:
|
|
21
|
-
node-version: 24
|
|
22
|
-
registry-url: https://registry.npmjs.org
|
|
23
|
-
package-manager-cache: false
|
|
24
|
-
|
|
25
|
-
- run: npm ci --ignore-scripts
|
|
26
|
-
- run: npm run check
|
|
27
|
-
- run: npm test
|
|
28
|
-
|
|
29
|
-
- name: Verify version tag
|
|
30
|
-
run: |
|
|
31
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
32
|
-
TAG=${GITHUB_REF_NAME#v}
|
|
33
|
-
test "$VERSION" = "$TAG"
|
|
34
|
-
|
|
35
|
-
- run: npm publish --access public
|
package/AGENTS.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Agents Guide
|
|
2
|
-
|
|
3
|
-
## Project and layout
|
|
4
|
-
|
|
5
|
-
Piexis (`@preapexis/piexis`) is a TypeScript Pi package providing interactive questions, UI extensions, skills, prompts, and themes. Node.js 22.19+ and a current Pi release are required.
|
|
6
|
-
|
|
7
|
-
- `extensions/ask-question.ts`: sequential interactive `AskQuestion` tool; requires the TUI.
|
|
8
|
-
- `extensions/agents.ts`: five agent profiles and sequential subprocess subagents; `lib/agents.ts` owns configuration/routing, and `lib/subagent-runner.ts` owns protocol/cancellation. User/project `agent.json` files configure models and effort, with legacy `agents.json` fallback. Accepted project setup creates a missing `.pi/agent.json` scaffold without overwriting existing configuration.
|
|
9
|
-
- `extensions/ui.ts`: project header, activity footer, Git branch, model, and context usage.
|
|
10
|
-
- `extensions/recommended-tools.ts`: automatic opt-in project setup for Pi's native `grep`, `find`, and `ls` tools.
|
|
11
|
-
- `skills/`: task-specific `SKILL.md` guidance.
|
|
12
|
-
- `prompts/`: bundled `/init`, `/commit`, and `/summary` prompts.
|
|
13
|
-
- `themes/orange.json`: bundled theme.
|
|
14
|
-
- `package.json`: resource manifest and npm scripts; `tsconfig.json`: strict TypeScript configuration.
|
|
15
|
-
|
|
16
|
-
## Development
|
|
17
|
-
|
|
18
|
-
| Command | Purpose |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| `npm ci --ignore-scripts` | Install locked dependencies without lifecycle scripts. |
|
|
21
|
-
| `npm run check` | Type-check with `tsc --noEmit`. |
|
|
22
|
-
| `npm test` | Run `node --test tests/*.test.mjs`. |
|
|
23
|
-
| `pi -e ./extensions/ask-question.ts` | Preview only the question extension. |
|
|
24
|
-
|
|
25
|
-
There are no separate build, lint, or format scripts, and no CI configuration in this checkout.
|
|
26
|
-
|
|
27
|
-
## Conventions and safety
|
|
28
|
-
|
|
29
|
-
- Use ESM and strict TypeScript with NodeNext module resolution and ES2022 targeting. Local imports use explicit `.ts` extensions.
|
|
30
|
-
- Match nearby source style: two-space indentation, double-quoted strings, and semicolons. Extensions export a default registration function receiving `ExtensionAPI`.
|
|
31
|
-
- Preserve interactive cancellation and non-TUI handling. Add or update `node:test`/`node:assert/strict` tests for behavior changes, using disposable fixtures rather than credentials.
|
|
32
|
-
- Preserve unrelated working-tree changes and intentional deletions. Do not commit generated output, secrets, dependency directories, or local configuration. `.pi/` and `.kilo/` are ignored local state, not package resources.
|
|
33
|
-
- Permission modes, approval/classifier logic, plan execution handoffs, and background workers have been removed. Piexis does not enforce tool-access restrictions or provide sandboxing; respect host restrictions and other installed extensions.
|
|
34
|
-
- Do not delete existing user settings, session records, or retained task worktrees as part of routine package changes.
|
|
35
|
-
|
|
36
|
-
## Validation
|
|
37
|
-
|
|
38
|
-
Run `npm run check` and relevant tests for implementation changes. Report unexecuted checks explicitly; skipped or empty runs do not establish coverage. Review the diff for credentials and unrelated changes before finishing.
|
package/scripts/release.mjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
|
|
4
|
-
const bump = process.argv[2] ?? "patch";
|
|
5
|
-
if (!["patch", "minor", "major"].includes(bump)) {
|
|
6
|
-
console.error("Usage: npm run release -- [patch|minor|major]");
|
|
7
|
-
process.exit(1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function run(command, args) {
|
|
11
|
-
const result = spawnSync(command, args, { stdio: "inherit", shell: false });
|
|
12
|
-
if (result.error) throw result.error;
|
|
13
|
-
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const status = spawnSync("git", ["status", "--porcelain"], { encoding: "utf8" });
|
|
17
|
-
if (status.status !== 0) process.exit(status.status ?? 1);
|
|
18
|
-
if (status.stdout.trim()) {
|
|
19
|
-
console.error("Working tree is not clean. Commit or stash changes first.");
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
console.log("Running checks...");
|
|
24
|
-
run("npm", ["run", "check"]);
|
|
25
|
-
run("npm", ["test"]);
|
|
26
|
-
|
|
27
|
-
console.log(`Bumping ${bump} version and creating a Git tag...`);
|
|
28
|
-
run("npm", ["version", bump]);
|
|
29
|
-
|
|
30
|
-
const packageJson = JSON.parse(readFileSync("package.json", "utf8"));
|
|
31
|
-
console.log(`Pushing v${packageJson.version}; GitHub Actions will publish it.`);
|
|
32
|
-
run("git", ["push", "origin", "HEAD", "--follow-tags"]);
|