pi-x-ide 1.2.0 → 1.4.0
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 +57 -4
- package/README.zh.md +57 -4
- package/dist/package.json +11 -4
- package/dist/src/pi/index.js +14 -3
- package/dist/src/pi/index.js.map +1 -1
- package/dist/src/pi/install.js +13 -8
- package/dist/src/pi/install.js.map +1 -1
- package/dist/src/pi/state.d.ts +3 -0
- package/dist/src/pi/state.js.map +1 -1
- package/dist/src/pi/zed.d.ts +27 -0
- package/dist/src/pi/zed.js +441 -0
- package/dist/src/pi/zed.js.map +1 -0
- package/dist/src/shared/config-options.d.ts +89 -0
- package/dist/src/shared/config-options.js +88 -0
- package/dist/src/shared/config-options.js.map +1 -0
- package/dist/src/shared/config.d.ts +7 -0
- package/dist/src/shared/config.js +51 -0
- package/dist/src/shared/config.js.map +1 -0
- package/dist/src/shared/paths.js +4 -3
- package/dist/src/shared/paths.js.map +1 -1
- package/dist/test/install.test.js +13 -0
- package/dist/test/install.test.js.map +1 -1
- package/dist/test/shared.test.js +27 -0
- package/dist/test/shared.test.js.map +1 -1
- package/dist/test/zed.test.d.ts +1 -0
- package/dist/test/zed.test.js +590 -0
- package/dist/test/zed.test.js.map +1 -0
- package/package.json +11 -4
- package/schemas/config.json +120 -0
- package/src/pi/index.ts +19 -3
- package/src/pi/install.ts +13 -8
- package/src/pi/state.ts +3 -0
- package/src/pi/zed.ts +515 -0
- package/src/shared/config-options.ts +100 -0
- package/src/shared/config.ts +54 -0
- package/src/shared/paths.ts +4 -3
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-x-ide",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Pi extension package for IDE selection context integration.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
|
+
"schemas",
|
|
7
8
|
"src"
|
|
8
9
|
],
|
|
9
10
|
"keywords": [
|
|
10
11
|
"pi-package",
|
|
11
12
|
"pi",
|
|
12
13
|
"ide",
|
|
13
|
-
"vscode"
|
|
14
|
+
"vscode",
|
|
15
|
+
"zed"
|
|
14
16
|
],
|
|
15
17
|
"license": "Apache-2.0",
|
|
16
18
|
"repository": {
|
|
@@ -21,6 +23,9 @@
|
|
|
21
23
|
"url": "https://github.com/balaenis/pi-x-ide/issues"
|
|
22
24
|
},
|
|
23
25
|
"homepage": "https://github.com/balaenis/pi-x-ide#readme",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=26"
|
|
28
|
+
},
|
|
24
29
|
"type": "commonjs",
|
|
25
30
|
"main": "./dist/src/pi/index.js",
|
|
26
31
|
"dependencies": {
|
|
@@ -32,7 +37,7 @@
|
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"@earendil-works/pi-coding-agent": "^0.79.0",
|
|
34
39
|
"@eslint/js": "^10.0.1",
|
|
35
|
-
"@types/node": "^
|
|
40
|
+
"@types/node": "^25.9.2",
|
|
36
41
|
"@types/ws": "^8.5.13",
|
|
37
42
|
"@vscode/vsce": "^3.2.2",
|
|
38
43
|
"esbuild": "^0.27.0",
|
|
@@ -51,7 +56,9 @@
|
|
|
51
56
|
"scripts": {
|
|
52
57
|
"build": "tsc -p tsconfig.json && pnpm --filter './vscode' compile",
|
|
53
58
|
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm --filter './vscode' typecheck",
|
|
54
|
-
"
|
|
59
|
+
"generate:config-schema": "node scripts/generate-config-schema.cjs",
|
|
60
|
+
"check:config-schema": "node scripts/generate-config-schema.cjs --check",
|
|
61
|
+
"test": "pnpm check:config-schema && pnpm build && node --test dist/test/*.test.js",
|
|
55
62
|
"lint": "eslint .",
|
|
56
63
|
"lint:fix": "eslint . --fix",
|
|
57
64
|
"format": "prettier --write .",
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/balaenis/pi-x-ide/schemas/config.json",
|
|
4
|
+
"title": "Pi config.json",
|
|
5
|
+
"description": "Schema for Pi-side configuration read from ~/.pi/config.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": true,
|
|
8
|
+
"properties": {
|
|
9
|
+
"env": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"description": "Pi-side environment variables. Real environment variables override these values.",
|
|
12
|
+
"additionalProperties": {
|
|
13
|
+
"type": [
|
|
14
|
+
"string",
|
|
15
|
+
"number",
|
|
16
|
+
"boolean"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"properties": {
|
|
20
|
+
"PI_X_IDE_LOCK_DIR": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Directory containing Pi x IDE lock files. Defaults to ~/.pi/pi-x-ide/lock."
|
|
23
|
+
},
|
|
24
|
+
"PI_X_IDE_AUTO_INSTALL": {
|
|
25
|
+
"type": [
|
|
26
|
+
"string",
|
|
27
|
+
"number",
|
|
28
|
+
"boolean"
|
|
29
|
+
],
|
|
30
|
+
"description": "Controls VS Code-family extension auto-install. Values 0, false, and off disable it."
|
|
31
|
+
},
|
|
32
|
+
"PI_X_IDE_ZED_DB": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Override path to Zed's SQLite database."
|
|
35
|
+
},
|
|
36
|
+
"TERM_PROGRAM": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Terminal program marker used to detect VS Code, Cursor, Windsurf, or Zed."
|
|
39
|
+
},
|
|
40
|
+
"VSCODE_CWD": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "VS Code-family cwd marker and IDE path hint."
|
|
43
|
+
},
|
|
44
|
+
"VSCODE_PID": {
|
|
45
|
+
"type": [
|
|
46
|
+
"string",
|
|
47
|
+
"number"
|
|
48
|
+
],
|
|
49
|
+
"description": "VS Code-family process marker."
|
|
50
|
+
},
|
|
51
|
+
"VSCODE_IPC_HOOK_CLI": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "VS Code-family IPC marker and IDE path hint."
|
|
54
|
+
},
|
|
55
|
+
"VSCODE_GIT_IPC_HANDLE": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "VS Code-family Git IPC marker and IDE path hint."
|
|
58
|
+
},
|
|
59
|
+
"ZED_TERM": {
|
|
60
|
+
"type": [
|
|
61
|
+
"string",
|
|
62
|
+
"boolean"
|
|
63
|
+
],
|
|
64
|
+
"description": "Zed terminal marker. Pi x IDE detects Zed when this is true."
|
|
65
|
+
},
|
|
66
|
+
"WSL_DISTRO_NAME": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "WSL distribution name used for WSL path normalization and Zed database discovery."
|
|
69
|
+
},
|
|
70
|
+
"WSL_INTEROP": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "WSL interop marker used for WSL path normalization and Zed database discovery."
|
|
73
|
+
},
|
|
74
|
+
"LOCALAPPDATA": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"description": "Windows local application data directory used to find Zed's database."
|
|
77
|
+
},
|
|
78
|
+
"USERPROFILE": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "Windows user profile directory used to find Zed's database when LOCALAPPDATA is unavailable."
|
|
81
|
+
},
|
|
82
|
+
"PATH": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "Executable search path used to find code, cursor, and windsurf CLIs."
|
|
85
|
+
},
|
|
86
|
+
"Path": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": "Windows-style executable search path used to find code, cursor, and windsurf CLIs."
|
|
89
|
+
},
|
|
90
|
+
"path": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"description": "Lowercase executable search path used to find code, cursor, and windsurf CLIs."
|
|
93
|
+
},
|
|
94
|
+
"PATHEXT": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"description": "Windows executable extensions used when searching for IDE CLIs."
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"patternProperties": {
|
|
100
|
+
"^(CURSOR|WINDSURF|CODEIUM).*": {
|
|
101
|
+
"type": [
|
|
102
|
+
"string",
|
|
103
|
+
"number",
|
|
104
|
+
"boolean"
|
|
105
|
+
],
|
|
106
|
+
"description": "IDE-specific marker used to detect Cursor or Windsurf terminals."
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"examples": [
|
|
112
|
+
{
|
|
113
|
+
"env": {
|
|
114
|
+
"PI_X_IDE_LOCK_DIR": "/home/user/.pi/pi-x-ide/lock",
|
|
115
|
+
"PI_X_IDE_AUTO_INSTALL": "0",
|
|
116
|
+
"PI_X_IDE_ZED_DB": "/home/user/.local/share/zed/db/0-stable/db.sqlite"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
package/src/pi/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { registerIdeCommand } from "./commands";
|
|
|
17
17
|
import { clearLatestSelection, registerContextHandlers, setLatestSelection } from "./context";
|
|
18
18
|
import { createRuntime, type PiIdeRuntime } from "./state";
|
|
19
19
|
import { clearIdeUi, updateIdeUi } from "./ui";
|
|
20
|
+
import { startZedPolling, stopZedPolling } from "./zed";
|
|
20
21
|
|
|
21
22
|
const RECONNECT_DELAY_MS = 2_000;
|
|
22
23
|
const INSTALL_RECONNECT_RETRY_MS = 1_500;
|
|
@@ -28,7 +29,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
28
29
|
registerContextHandlers(pi, runtime);
|
|
29
30
|
registerIdeCommand(pi, runtime, {
|
|
30
31
|
refreshCandidates: (ctx) => refreshCandidates(runtime, ctx),
|
|
31
|
-
connectAuto: (ctx) =>
|
|
32
|
+
connectAuto: (ctx) => connectAutoWithZedFallback(runtime, ctx),
|
|
32
33
|
connectCandidate: (candidate, ctx) => connectCandidate(runtime, candidate, ctx),
|
|
33
34
|
disconnect: (ctx, disabled) => disconnect(runtime, ctx, disabled),
|
|
34
35
|
installExtension: (ctx) => installExtension(runtime, ctx),
|
|
@@ -39,18 +40,20 @@ export default function (pi: ExtensionAPI): void {
|
|
|
39
40
|
const generation = runtime.sessionGeneration;
|
|
40
41
|
runtime.ctx = ctx;
|
|
41
42
|
runtime.cwd = ctx.cwd;
|
|
43
|
+
stopZedPolling(runtime);
|
|
42
44
|
if (!runtime.enabled) {
|
|
43
45
|
runtime.connectionStatus = "disabled";
|
|
44
46
|
updateIdeUi(runtime, ctx);
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
void maybeAutoInstallAndReconnect(runtime, ctx, generation);
|
|
48
|
-
await
|
|
50
|
+
await connectAutoWithZedFallback(runtime, ctx, generation);
|
|
49
51
|
});
|
|
50
52
|
|
|
51
53
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
52
54
|
runtime.sessionGeneration += 1;
|
|
53
55
|
runtime.ctx = ctx;
|
|
56
|
+
stopZedPolling(runtime);
|
|
54
57
|
if (runtime.reconnectTimer) clearTimeout(runtime.reconnectTimer);
|
|
55
58
|
runtime.reconnectTimer = undefined;
|
|
56
59
|
runtime.connection?.disconnect();
|
|
@@ -228,6 +231,17 @@ async function refreshCandidates(
|
|
|
228
231
|
return runtime.candidates;
|
|
229
232
|
}
|
|
230
233
|
|
|
234
|
+
async function connectAutoWithZedFallback(
|
|
235
|
+
runtime: PiIdeRuntime,
|
|
236
|
+
ctx: ExtensionContext | ExtensionCommandContext,
|
|
237
|
+
generation = runtime.sessionGeneration,
|
|
238
|
+
): Promise<void> {
|
|
239
|
+
await connectAuto(runtime, ctx);
|
|
240
|
+
if (runtime.connectionStatus !== "connected") {
|
|
241
|
+
startZedPolling(runtime, ctx, { generation });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
231
245
|
async function connectAuto(runtime: PiIdeRuntime, ctx: ExtensionContext | ExtensionCommandContext): Promise<void> {
|
|
232
246
|
runtime.enabled = true;
|
|
233
247
|
const candidates = await refreshCandidates(runtime, ctx);
|
|
@@ -258,6 +272,7 @@ async function connectCandidate(
|
|
|
258
272
|
runtime.ctx = ctx;
|
|
259
273
|
runtime.cwd = ctx.cwd;
|
|
260
274
|
runtime.enabled = true;
|
|
275
|
+
stopZedPolling(runtime);
|
|
261
276
|
if (runtime.reconnectTimer) clearTimeout(runtime.reconnectTimer);
|
|
262
277
|
runtime.reconnectTimer = undefined;
|
|
263
278
|
|
|
@@ -346,6 +361,7 @@ function isCurrentConnection(runtime: PiIdeRuntime, connection: IdeConnection |
|
|
|
346
361
|
|
|
347
362
|
function disconnect(runtime: PiIdeRuntime, ctx: ExtensionContext | ExtensionCommandContext, disabled = false): void {
|
|
348
363
|
runtime.ctx = ctx;
|
|
364
|
+
stopZedPolling(runtime);
|
|
349
365
|
if (runtime.reconnectTimer) clearTimeout(runtime.reconnectTimer);
|
|
350
366
|
runtime.reconnectTimer = undefined;
|
|
351
367
|
const connection = runtime.connection;
|
|
@@ -370,7 +386,7 @@ function scheduleReconnect(runtime: PiIdeRuntime): void {
|
|
|
370
386
|
runtime.reconnectTimer = undefined;
|
|
371
387
|
const ctx = runtime.ctx;
|
|
372
388
|
if (!ctx || !runtime.enabled) return;
|
|
373
|
-
|
|
389
|
+
connectAutoWithZedFallback(runtime, ctx).catch((error: unknown) => {
|
|
374
390
|
runtime.connectionStatus = "error";
|
|
375
391
|
runtime.connectionMessage = error instanceof Error ? error.message : String(error);
|
|
376
392
|
updateIdeUi(runtime);
|
package/src/pi/install.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { access } from "node:fs/promises";
|
|
|
3
3
|
import { delimiter, isAbsolute, join } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import packageJson from "../../package.json";
|
|
6
|
+
import { resolvePiConfigEnv } from "../shared/config";
|
|
6
7
|
import type { PiIdeRuntime } from "./state";
|
|
7
8
|
|
|
8
9
|
const execFileAsync = promisify(execFile);
|
|
@@ -61,7 +62,8 @@ export const SUPPORTED_IDE_CLI_PROFILES: IdeCliProfile[] = [
|
|
|
61
62
|
];
|
|
62
63
|
|
|
63
64
|
export function isAutoInstallEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
64
|
-
const
|
|
65
|
+
const configuredEnv = resolvePiConfigEnv(env);
|
|
66
|
+
const value = configuredEnv[PI_X_IDE_AUTO_INSTALL_ENV];
|
|
65
67
|
if (value === undefined) return true;
|
|
66
68
|
return !["0", "false", "off"].includes(value.trim().toLowerCase());
|
|
67
69
|
}
|
|
@@ -96,10 +98,11 @@ export function compareExtensionVersions(installed: string | undefined, target:
|
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
export function inferCurrentIdeFromEnv(env: NodeJS.ProcessEnv = process.env): SupportedIdeId | undefined {
|
|
101
|
+
const configuredEnv = resolvePiConfigEnv(env);
|
|
99
102
|
const matches = new Set<SupportedIdeId>();
|
|
100
|
-
const hasWindsurfMarker = hasWindsurfEnvMarker(
|
|
101
|
-
const hasCursorMarker = hasCursorEnvMarker(
|
|
102
|
-
const hasVscodeMarker = hasVscodeEnvMarker(
|
|
103
|
+
const hasWindsurfMarker = hasWindsurfEnvMarker(configuredEnv);
|
|
104
|
+
const hasCursorMarker = hasCursorEnvMarker(configuredEnv);
|
|
105
|
+
const hasVscodeMarker = hasVscodeEnvMarker(configuredEnv);
|
|
103
106
|
|
|
104
107
|
if (hasWindsurfMarker) matches.add("windsurf");
|
|
105
108
|
if (hasCursorMarker) matches.add("cursor");
|
|
@@ -146,10 +149,11 @@ export async function findExecutable(
|
|
|
146
149
|
command: string,
|
|
147
150
|
env: NodeJS.ProcessEnv = process.env,
|
|
148
151
|
): Promise<string | undefined> {
|
|
149
|
-
const
|
|
152
|
+
const configuredEnv = resolvePiConfigEnv(env);
|
|
153
|
+
const pathEnv = configuredEnv.PATH ?? configuredEnv.Path ?? configuredEnv.path;
|
|
150
154
|
if (!pathEnv) return undefined;
|
|
151
155
|
|
|
152
|
-
const extensions = process.platform === "win32" ? parsePathExt(
|
|
156
|
+
const extensions = process.platform === "win32" ? parsePathExt(configuredEnv) : [""];
|
|
153
157
|
const candidates = isAbsolute(command)
|
|
154
158
|
? [command]
|
|
155
159
|
: pathEnv
|
|
@@ -184,7 +188,7 @@ export async function runCli(
|
|
|
184
188
|
export async function discoverInstallCandidates(
|
|
185
189
|
options: DiscoverInstallCandidatesOptions = {},
|
|
186
190
|
): Promise<IdeInstallCandidate[]> {
|
|
187
|
-
const env = options.env ?? process.env;
|
|
191
|
+
const env = resolvePiConfigEnv(options.env ?? process.env);
|
|
188
192
|
const currentIde = inferCurrentIdeFromEnv(env);
|
|
189
193
|
const includeLowConfidence = options.includeLowConfidence ?? false;
|
|
190
194
|
const timeoutMs = options.timeoutMs ?? 15_000;
|
|
@@ -228,7 +232,8 @@ export function selectAutoInstallCandidate(
|
|
|
228
232
|
candidates: IdeInstallCandidate[],
|
|
229
233
|
env: NodeJS.ProcessEnv = process.env,
|
|
230
234
|
): IdeInstallCandidate | undefined {
|
|
231
|
-
const
|
|
235
|
+
const configuredEnv = resolvePiConfigEnv(env);
|
|
236
|
+
const currentIde = inferCurrentIdeFromEnv(configuredEnv);
|
|
232
237
|
const highConfidence = candidates.filter(
|
|
233
238
|
(candidate) => candidate.confidence === "current-terminal" && (!currentIde || candidate.id === currentIde),
|
|
234
239
|
);
|
package/src/pi/state.ts
CHANGED
|
@@ -17,6 +17,9 @@ export interface PiIdeRuntime {
|
|
|
17
17
|
attachState: AttachState;
|
|
18
18
|
turnSelection?: EditorSelectionSnapshot;
|
|
19
19
|
reconnectTimer?: NodeJS.Timeout;
|
|
20
|
+
zedPollTimer?: NodeJS.Timeout;
|
|
21
|
+
zedPollSelectionKey?: string;
|
|
22
|
+
zedPollWalMtimeMs?: number;
|
|
20
23
|
installingIdeIds: Set<string>;
|
|
21
24
|
sessionGeneration: number;
|
|
22
25
|
}
|