pi-lens 2.2.9 → 3.0.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/CHANGELOG.md +198 -0
- package/README.md +709 -519
- package/clients/__tests__/file-time.test.js +216 -0
- package/clients/__tests__/file-time.test.ts +276 -0
- package/clients/__tests__/format-service.test.js +245 -0
- package/clients/__tests__/format-service.test.ts +339 -0
- package/clients/__tests__/formatters.test.js +271 -0
- package/clients/__tests__/formatters.test.ts +401 -0
- package/clients/amain-types.js +164 -0
- package/clients/amain-types.ts +165 -0
- package/clients/architect-client.js +56 -12
- package/clients/architect-client.ts +81 -16
- package/clients/ast-grep-client.js +2 -2
- package/clients/ast-grep-client.ts +14 -39
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.js +8 -0
- package/clients/ast-grep-rule-manager.ts +10 -1
- package/clients/ast-grep-types.js +9 -0
- package/clients/ast-grep-types.ts +106 -0
- package/clients/auto-loop.js +10 -0
- package/clients/auto-loop.ts +14 -1
- package/clients/biome-client.js +81 -19
- package/clients/biome-client.ts +103 -22
- package/clients/bus/bus.js +191 -0
- package/clients/bus/bus.ts +251 -0
- package/clients/bus/events.js +214 -0
- package/clients/bus/events.ts +279 -0
- package/clients/bus/index.js +8 -0
- package/clients/bus/index.ts +9 -0
- package/clients/bus/integration.js +158 -0
- package/clients/bus/integration.ts +214 -0
- package/clients/complexity-client.js +13 -7
- package/clients/complexity-client.ts +13 -7
- package/clients/config-validator.js +465 -0
- package/clients/config-validator.ts +558 -0
- package/clients/dependency-checker.js +4 -10
- package/clients/dependency-checker.ts +4 -10
- package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
- package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
- package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
- package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
- package/clients/dispatch/bus-dispatcher.js +177 -0
- package/clients/dispatch/bus-dispatcher.ts +251 -0
- package/clients/dispatch/dispatcher.edge.test.js +82 -0
- package/clients/dispatch/dispatcher.edge.test.ts +100 -0
- package/clients/dispatch/dispatcher.format.test.js +46 -0
- package/clients/dispatch/dispatcher.format.test.ts +58 -0
- package/clients/dispatch/dispatcher.inline.test.js +74 -0
- package/clients/dispatch/dispatcher.inline.test.ts +93 -0
- package/clients/dispatch/dispatcher.js +19 -53
- package/clients/dispatch/dispatcher.ts +20 -67
- package/clients/dispatch/plan.js +9 -4
- package/clients/dispatch/plan.ts +9 -4
- package/clients/dispatch/runners/architect.js +21 -7
- package/clients/dispatch/runners/architect.test.js +138 -0
- package/clients/dispatch/runners/architect.test.ts +162 -0
- package/clients/dispatch/runners/architect.ts +22 -7
- package/clients/dispatch/runners/ast-grep-napi.js +462 -0
- package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
- package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
- package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
- package/clients/dispatch/runners/ast-grep.js +62 -19
- package/clients/dispatch/runners/ast-grep.ts +70 -18
- package/clients/dispatch/runners/biome.js +29 -53
- package/clients/dispatch/runners/biome.ts +29 -63
- package/clients/dispatch/runners/config-validation.js +67 -0
- package/clients/dispatch/runners/config-validation.ts +82 -0
- package/clients/dispatch/runners/go-vet.js +4 -28
- package/clients/dispatch/runners/go-vet.ts +4 -32
- package/clients/dispatch/runners/index.js +30 -10
- package/clients/dispatch/runners/index.ts +30 -10
- package/clients/dispatch/runners/oxlint.js +141 -0
- package/clients/dispatch/runners/oxlint.test.js +230 -0
- package/clients/dispatch/runners/oxlint.test.ts +303 -0
- package/clients/dispatch/runners/oxlint.ts +175 -0
- package/clients/dispatch/runners/pyright.js +40 -70
- package/clients/dispatch/runners/pyright.test.js +16 -2
- package/clients/dispatch/runners/pyright.test.ts +14 -2
- package/clients/dispatch/runners/pyright.ts +48 -91
- package/clients/dispatch/runners/python-slop.js +97 -0
- package/clients/dispatch/runners/python-slop.test.js +203 -0
- package/clients/dispatch/runners/python-slop.test.ts +298 -0
- package/clients/dispatch/runners/python-slop.ts +124 -0
- package/clients/dispatch/runners/ruff.js +18 -71
- package/clients/dispatch/runners/ruff.ts +19 -79
- package/clients/dispatch/runners/rust-clippy.js +28 -32
- package/clients/dispatch/runners/rust-clippy.ts +29 -31
- package/clients/dispatch/runners/scan_codebase.test.js +89 -0
- package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
- package/clients/dispatch/runners/shellcheck.js +147 -0
- package/clients/dispatch/runners/shellcheck.test.js +98 -0
- package/clients/dispatch/runners/shellcheck.test.ts +129 -0
- package/clients/dispatch/runners/shellcheck.ts +188 -0
- package/clients/dispatch/runners/similarity.js +230 -0
- package/clients/dispatch/runners/similarity.ts +339 -0
- package/clients/dispatch/runners/spellcheck.js +106 -0
- package/clients/dispatch/runners/spellcheck.test.js +158 -0
- package/clients/dispatch/runners/spellcheck.test.ts +214 -0
- package/clients/dispatch/runners/spellcheck.ts +136 -0
- package/clients/dispatch/runners/tree-sitter.js +107 -0
- package/clients/dispatch/runners/tree-sitter.ts +135 -0
- package/clients/dispatch/runners/ts-lsp.js +104 -33
- package/clients/dispatch/runners/ts-lsp.ts +120 -38
- package/clients/dispatch/runners/ts-slop.js +113 -0
- package/clients/dispatch/runners/ts-slop.test.js +180 -0
- package/clients/dispatch/runners/ts-slop.test.ts +230 -0
- package/clients/dispatch/runners/ts-slop.ts +142 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
- package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
- package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
- package/clients/dispatch/runners/utils.js +2 -4
- package/clients/dispatch/runners/utils.ts +2 -4
- package/clients/dispatch/types.ts +1 -1
- package/clients/dispatch/utils/format-utils.js +49 -0
- package/clients/dispatch/utils/format-utils.ts +60 -0
- package/clients/dogfood.test.js +201 -0
- package/clients/dogfood.test.ts +269 -0
- package/clients/file-time.js +152 -0
- package/clients/file-time.ts +208 -0
- package/clients/file-utils.js +40 -0
- package/clients/file-utils.ts +44 -0
- package/clients/fix-scanners.js +10 -20
- package/clients/fix-scanners.ts +10 -22
- package/clients/format-service.js +172 -0
- package/clients/format-service.ts +254 -0
- package/clients/formatters.js +435 -0
- package/clients/formatters.ts +508 -0
- package/clients/go-client.js +5 -14
- package/clients/go-client.ts +5 -13
- package/clients/installer/index.js +356 -0
- package/clients/installer/index.ts +426 -0
- package/clients/jscpd-client.js +11 -9
- package/clients/jscpd-client.ts +12 -8
- package/clients/knip-client.js +3 -7
- package/clients/knip-client.ts +3 -6
- package/clients/lsp/__tests__/client.test.js +325 -0
- package/clients/lsp/__tests__/client.test.ts +434 -0
- package/clients/lsp/__tests__/config.test.js +166 -0
- package/clients/lsp/__tests__/config.test.ts +209 -0
- package/clients/lsp/__tests__/error-recovery.test.js +213 -0
- package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
- package/clients/lsp/__tests__/integration.test.js +127 -0
- package/clients/lsp/__tests__/integration.test.ts +160 -0
- package/clients/lsp/__tests__/launch.test.js +260 -0
- package/clients/lsp/__tests__/launch.test.ts +329 -0
- package/clients/lsp/__tests__/server.test.js +259 -0
- package/clients/lsp/__tests__/server.test.ts +332 -0
- package/clients/lsp/__tests__/service.test.js +417 -0
- package/clients/lsp/__tests__/service.test.ts +499 -0
- package/clients/lsp/client.js +235 -0
- package/clients/lsp/client.ts +328 -0
- package/clients/lsp/config.js +115 -0
- package/clients/lsp/config.ts +149 -0
- package/clients/lsp/index.js +222 -0
- package/clients/lsp/index.ts +280 -0
- package/clients/lsp/installer/index.js +391 -0
- package/clients/lsp/interactive-install.js +210 -0
- package/clients/lsp/interactive-install.ts +251 -0
- package/clients/lsp/language.js +170 -0
- package/clients/lsp/language.ts +216 -0
- package/clients/lsp/launch.js +174 -0
- package/clients/lsp/launch.ts +240 -0
- package/clients/lsp/lsp/launch.js +116 -0
- package/clients/lsp/lsp/server.js +532 -0
- package/clients/lsp/lsp-index.js +10 -0
- package/clients/lsp/lsp-index.ts +11 -0
- package/clients/lsp/path-utils.js +48 -0
- package/clients/lsp/path-utils.ts +52 -0
- package/clients/lsp/server.js +615 -0
- package/clients/lsp/server.ts +800 -0
- package/clients/lsp/test-py-spawn/requirements.txt +1 -0
- package/clients/lsp/test-py-spawn/test.py +3 -0
- package/clients/lsp/test-py-svc/requirements.txt +1 -0
- package/clients/lsp/test-py-svc/test.py +3 -0
- package/clients/lsp/test-python-project/requirements.txt +1 -0
- package/clients/lsp/test-python-project/test.py +5 -0
- package/clients/metrics-history.js +2 -2
- package/clients/metrics-history.ts +2 -2
- package/clients/production-readiness.js +522 -0
- package/clients/production-readiness.ts +556 -0
- package/clients/project-index.js +255 -0
- package/clients/project-index.ts +383 -0
- package/clients/project-metadata.js +531 -0
- package/clients/project-metadata.ts +624 -0
- package/clients/ruff-client.js +56 -16
- package/clients/ruff-client.ts +72 -15
- package/clients/runner-tracker.js +152 -0
- package/clients/runner-tracker.ts +213 -0
- package/clients/rust-client.js +4 -11
- package/clients/rust-client.ts +5 -11
- package/clients/safe-spawn.js +96 -0
- package/clients/safe-spawn.ts +128 -0
- package/clients/scan-architectural-debt.js +3 -6
- package/clients/scan-architectural-debt.ts +3 -6
- package/clients/scan-utils.js +5 -20
- package/clients/scan-utils.ts +5 -29
- package/clients/secrets-scanner.js +3 -17
- package/clients/secrets-scanner.ts +4 -20
- package/clients/services/__tests__/effect-integration.test.js +86 -0
- package/clients/services/__tests__/effect-integration.test.ts +111 -0
- package/clients/services/effect-integration.js +194 -0
- package/clients/services/effect-integration.ts +268 -0
- package/clients/services/index.js +7 -0
- package/clients/services/index.ts +8 -0
- package/clients/services/runner-service.js +105 -0
- package/clients/services/runner-service.ts +179 -0
- package/clients/sg-runner.js +87 -13
- package/clients/sg-runner.ts +97 -13
- package/clients/state-matrix.js +160 -0
- package/clients/state-matrix.ts +202 -0
- package/clients/subprocess-client.js +10 -9
- package/clients/subprocess-client.ts +10 -8
- package/clients/test-runner-client.js +3 -7
- package/clients/test-runner-client.ts +3 -6
- package/clients/tool-availability.js +4 -10
- package/clients/tool-availability.ts +4 -9
- package/clients/tree-sitter-client.js +564 -0
- package/clients/tree-sitter-client.ts +797 -0
- package/clients/tree-sitter-query-loader.js +355 -0
- package/clients/tree-sitter-query-loader.ts +425 -0
- package/clients/type-coverage-client.js +3 -7
- package/clients/type-coverage-client.ts +3 -6
- package/clients/typescript-client.codefix.test.js +157 -0
- package/clients/typescript-client.codefix.test.ts +186 -0
- package/clients/typescript-client.js +43 -0
- package/clients/typescript-client.ts +98 -0
- package/commands/booboo.js +799 -219
- package/commands/booboo.ts +1004 -225
- package/commands/clients/ast-grep-client.js +250 -0
- package/commands/clients/ast-grep-parser.js +86 -0
- package/commands/clients/ast-grep-rule-manager.js +91 -0
- package/commands/clients/ast-grep-types.js +9 -0
- package/commands/clients/biome-client.js +380 -0
- package/commands/clients/complexity-client.js +667 -0
- package/commands/clients/file-kinds.js +177 -0
- package/commands/clients/file-utils.js +40 -0
- package/commands/clients/jscpd-client.js +169 -0
- package/commands/clients/knip-client.js +211 -0
- package/commands/clients/ruff-client.js +297 -0
- package/commands/clients/safe-spawn.js +88 -0
- package/commands/clients/scan-utils.js +83 -0
- package/commands/clients/sg-runner.js +190 -0
- package/commands/clients/types.js +11 -0
- package/commands/clients/typescript-client.js +505 -0
- package/commands/fix-from-booboo.js +398 -0
- package/commands/fix-from-booboo.ts +485 -0
- package/commands/fix-simplified.js +618 -0
- package/commands/fix-simplified.ts +768 -0
- package/commands/rate.js +10 -14
- package/commands/rate.ts +9 -16
- package/default-architect.yaml +59 -15
- package/index.ts +342 -429
- package/package.json +16 -3
- package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
- package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
- package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
- package/rules/python-slop-rules/.sgconfig.yml +4 -0
- package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
- package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
- package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
- package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
- package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
- package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
- package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
- package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
- package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
- package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
- package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
- package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
- package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
- package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
- package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
- package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
- package/rules/ts-slop-rules/.sgconfig.yml +4 -0
- package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
- package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
- package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
- package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
- package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
- package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
- package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
- package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
- package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
- package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
- package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
- package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
- package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
- package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
- package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
- package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
- package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
- package/skills/ast-grep/SKILL.md +182 -0
- package/clients/dispatch/runners/secrets.js +0 -109
- package/commands/fix.js +0 -244
- package/commands/fix.ts +0 -373
- package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP Process Launch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles spawning LSP servers via various methods:
|
|
5
|
+
* - Direct binary execution (using absolute paths on Windows)
|
|
6
|
+
* - Node.js scripts (npx/bun)
|
|
7
|
+
* - Package manager execution
|
|
8
|
+
*/
|
|
9
|
+
import { spawn as nodeSpawn, } from "node:child_process";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
// Helper to detect if running on Windows
|
|
12
|
+
const isWindows = process.platform === "win32";
|
|
13
|
+
/**
|
|
14
|
+
* Attach error handler to a spawned process to prevent ENOENT crashes
|
|
15
|
+
* This catches "command not found" errors and other spawn failures
|
|
16
|
+
*/
|
|
17
|
+
function _attachErrorHandler(proc, context) {
|
|
18
|
+
proc.on("error", (err) => {
|
|
19
|
+
// Log the error but don't crash - the caller should handle this gracefully
|
|
20
|
+
console.error(`[lsp] Spawn error for ${context}:`, err.message);
|
|
21
|
+
});
|
|
22
|
+
// Also handle unexpected exit (process crash after successful spawn)
|
|
23
|
+
proc.on("exit", (code, signal) => {
|
|
24
|
+
if (code !== 0 && code !== null) {
|
|
25
|
+
console.error(`[lsp] ${context} exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
proc.on("close", (code, signal) => {
|
|
29
|
+
if (code !== 0 && code !== null) {
|
|
30
|
+
console.error(`[lsp] ${context} closed with code ${code}${signal ? ` (signal: ${signal})` : ""}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Spawn an LSP server process
|
|
36
|
+
*
|
|
37
|
+
* Key fixes for Windows:
|
|
38
|
+
* - Uses absolute paths (relative paths fail in shell mode)
|
|
39
|
+
* - Uses shell: true for .cmd files
|
|
40
|
+
* - Uses windowsHide to prevent console window popup
|
|
41
|
+
*
|
|
42
|
+
* @param command - Command to run (e.g., "typescript-language-server")
|
|
43
|
+
* @param args - Arguments (e.g., ["--stdio"])
|
|
44
|
+
* @param options - Spawn options including cwd, env
|
|
45
|
+
* @returns LSPProcess handle
|
|
46
|
+
*/
|
|
47
|
+
export function launchLSP(command, args = [], options = {}) {
|
|
48
|
+
const cwd = String(options.cwd ?? process.cwd());
|
|
49
|
+
const env = { ...process.env, ...options.env };
|
|
50
|
+
// Resolve command path
|
|
51
|
+
// - If already absolute, use as-is
|
|
52
|
+
// - If it's a simple command (no path separators), let system find it via PATH
|
|
53
|
+
// - Otherwise, resolve relative to cwd
|
|
54
|
+
const resolvedCommand = path.isAbsolute(command)
|
|
55
|
+
? command
|
|
56
|
+
: command.includes(path.sep) || command.includes("/")
|
|
57
|
+
? path.resolve(cwd, command)
|
|
58
|
+
: command; // Let system find it via PATH
|
|
59
|
+
// On Windows with shell: true, we need to quote the command if it has spaces
|
|
60
|
+
const needsShell = isWindows &&
|
|
61
|
+
(resolvedCommand.includes(" ") || resolvedCommand.includes(".cmd"));
|
|
62
|
+
let proc;
|
|
63
|
+
if (needsShell) {
|
|
64
|
+
// Use shell mode with quoted command
|
|
65
|
+
const shellCommand = `"${resolvedCommand}" ${args.map((a) => (a.includes(" ") ? `"${a}"` : a)).join(" ")}`;
|
|
66
|
+
proc = nodeSpawn(shellCommand, [], {
|
|
67
|
+
cwd,
|
|
68
|
+
env,
|
|
69
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
70
|
+
detached: false,
|
|
71
|
+
windowsHide: true,
|
|
72
|
+
shell: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Use normal spawn without shell
|
|
77
|
+
proc = nodeSpawn(resolvedCommand, args, {
|
|
78
|
+
cwd,
|
|
79
|
+
env,
|
|
80
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
81
|
+
detached: false,
|
|
82
|
+
windowsHide: isWindows,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
86
|
+
throw new Error(`Failed to spawn LSP server: ${command}`);
|
|
87
|
+
}
|
|
88
|
+
// Check if process exited immediately (spawn failure - synchronous check)
|
|
89
|
+
if (proc.exitCode !== null || proc.killed) {
|
|
90
|
+
throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). ` +
|
|
91
|
+
`The binary may be missing or corrupted.`);
|
|
92
|
+
}
|
|
93
|
+
// Attach error handler to prevent ENOENT crashes and track later failures
|
|
94
|
+
_attachErrorHandler(proc, command);
|
|
95
|
+
return {
|
|
96
|
+
process: proc,
|
|
97
|
+
stdin: proc.stdin,
|
|
98
|
+
stdout: proc.stdout,
|
|
99
|
+
stderr: proc.stderr,
|
|
100
|
+
pid: proc.pid ?? 0,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Spawn via package manager (npx/bun)
|
|
105
|
+
*/
|
|
106
|
+
export function launchViaPackageManager(packageName, args = [], options = {}) {
|
|
107
|
+
// Prefer bun if available, fall back to npx (use .cmd on Windows)
|
|
108
|
+
const isWin = process.platform === "win32";
|
|
109
|
+
if (process.env.BUN_INSTALL) {
|
|
110
|
+
return launchLSP(isWin ? "bun.exe" : "bun", ["x", packageName, ...args], options);
|
|
111
|
+
}
|
|
112
|
+
// For npx on Windows, use shell mode with the full command string
|
|
113
|
+
if (isWin) {
|
|
114
|
+
const argsStr = args.map((a) => (a.includes(" ") ? `"${a}"` : a)).join(" ");
|
|
115
|
+
const shellCommand = `npx -y ${packageName}${argsStr ? ` ${argsStr}` : ""}`;
|
|
116
|
+
const cwd = String(options.cwd ?? process.cwd());
|
|
117
|
+
const env = { ...process.env, ...options.env };
|
|
118
|
+
const proc = nodeSpawn(shellCommand, [], {
|
|
119
|
+
cwd,
|
|
120
|
+
env,
|
|
121
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
122
|
+
detached: false,
|
|
123
|
+
windowsHide: true,
|
|
124
|
+
shell: true,
|
|
125
|
+
});
|
|
126
|
+
// Attach error handler to prevent ENOENT crashes
|
|
127
|
+
_attachErrorHandler(proc, packageName);
|
|
128
|
+
return {
|
|
129
|
+
process: proc,
|
|
130
|
+
stdin: proc.stdin,
|
|
131
|
+
stdout: proc.stdout,
|
|
132
|
+
stderr: proc.stderr,
|
|
133
|
+
pid: proc.pid ?? 0,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return launchLSP("npx", ["-y", packageName, ...args], options);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Spawn via Node.js directly
|
|
140
|
+
*/
|
|
141
|
+
export function launchViaNode(scriptPath, args = [], options = {}) {
|
|
142
|
+
return launchLSP(process.execPath, [scriptPath, ...args], options);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Spawn via Python module
|
|
146
|
+
*/
|
|
147
|
+
export function launchViaPython(moduleName, args = [], options = {}) {
|
|
148
|
+
// On Windows, prefer 'py' launcher, fall back to 'python'
|
|
149
|
+
const pythonCmd = process.platform === "win32" ? "py" : "python3";
|
|
150
|
+
return launchLSP(pythonCmd, ["-m", moduleName, ...args], options);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Stop an LSP process gracefully
|
|
154
|
+
*/
|
|
155
|
+
export async function stopLSP(handle) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
// Send SIGTERM first
|
|
158
|
+
handle.process.kill("SIGTERM");
|
|
159
|
+
// Force kill after timeout
|
|
160
|
+
const timeout = setTimeout(() => {
|
|
161
|
+
if (!handle.process.killed) {
|
|
162
|
+
handle.process.kill("SIGKILL");
|
|
163
|
+
}
|
|
164
|
+
}, 5000);
|
|
165
|
+
handle.process.on("exit", () => {
|
|
166
|
+
clearTimeout(timeout);
|
|
167
|
+
resolve();
|
|
168
|
+
});
|
|
169
|
+
handle.process.on("error", () => {
|
|
170
|
+
clearTimeout(timeout);
|
|
171
|
+
resolve();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP Process Launch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles spawning LSP servers via various methods:
|
|
5
|
+
* - Direct binary execution (using absolute paths on Windows)
|
|
6
|
+
* - Node.js scripts (npx/bun)
|
|
7
|
+
* - Package manager execution
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
type ChildProcess,
|
|
12
|
+
spawn as nodeSpawn,
|
|
13
|
+
type SpawnOptions,
|
|
14
|
+
} from "node:child_process";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
export interface LSPProcess {
|
|
18
|
+
process: ChildProcess;
|
|
19
|
+
stdin: NodeJS.WritableStream;
|
|
20
|
+
stdout: NodeJS.ReadableStream;
|
|
21
|
+
stderr: NodeJS.ReadableStream;
|
|
22
|
+
pid: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Helper to detect if running on Windows
|
|
26
|
+
const isWindows = process.platform === "win32";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Attach error handler to a spawned process to prevent ENOENT crashes
|
|
30
|
+
* This catches "command not found" errors and other spawn failures
|
|
31
|
+
*/
|
|
32
|
+
function _attachErrorHandler(proc: ChildProcess, context: string): void {
|
|
33
|
+
proc.on("error", (err) => {
|
|
34
|
+
// Log the error but don't crash - the caller should handle this gracefully
|
|
35
|
+
console.error(`[lsp] Spawn error for ${context}:`, err.message);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Also handle unexpected exit (process crash after successful spawn)
|
|
39
|
+
proc.on("exit", (code, signal) => {
|
|
40
|
+
if (code !== 0 && code !== null) {
|
|
41
|
+
console.error(
|
|
42
|
+
`[lsp] ${context} exited with code ${code}${signal ? ` (signal: ${signal})` : ""}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
proc.on("close", (code, signal) => {
|
|
48
|
+
if (code !== 0 && code !== null) {
|
|
49
|
+
console.error(
|
|
50
|
+
`[lsp] ${context} closed with code ${code}${signal ? ` (signal: ${signal})` : ""}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Spawn an LSP server process
|
|
58
|
+
*
|
|
59
|
+
* Key fixes for Windows:
|
|
60
|
+
* - Uses absolute paths (relative paths fail in shell mode)
|
|
61
|
+
* - Uses shell: true for .cmd files
|
|
62
|
+
* - Uses windowsHide to prevent console window popup
|
|
63
|
+
*
|
|
64
|
+
* @param command - Command to run (e.g., "typescript-language-server")
|
|
65
|
+
* @param args - Arguments (e.g., ["--stdio"])
|
|
66
|
+
* @param options - Spawn options including cwd, env
|
|
67
|
+
* @returns LSPProcess handle
|
|
68
|
+
*/
|
|
69
|
+
export function launchLSP(
|
|
70
|
+
command: string,
|
|
71
|
+
args: string[] = [],
|
|
72
|
+
options: SpawnOptions = {},
|
|
73
|
+
): LSPProcess {
|
|
74
|
+
const cwd = String(options.cwd ?? process.cwd());
|
|
75
|
+
const env = { ...process.env, ...options.env };
|
|
76
|
+
|
|
77
|
+
// Resolve command path
|
|
78
|
+
// - If already absolute, use as-is
|
|
79
|
+
// - If it's a simple command (no path separators), let system find it via PATH
|
|
80
|
+
// - Otherwise, resolve relative to cwd
|
|
81
|
+
const resolvedCommand = path.isAbsolute(command)
|
|
82
|
+
? command
|
|
83
|
+
: command.includes(path.sep) || command.includes("/")
|
|
84
|
+
? path.resolve(cwd, command)
|
|
85
|
+
: command; // Let system find it via PATH
|
|
86
|
+
|
|
87
|
+
// On Windows with shell: true, we need to quote the command if it has spaces
|
|
88
|
+
const needsShell =
|
|
89
|
+
isWindows &&
|
|
90
|
+
(resolvedCommand.includes(" ") || resolvedCommand.includes(".cmd"));
|
|
91
|
+
|
|
92
|
+
let proc: ChildProcess;
|
|
93
|
+
|
|
94
|
+
if (needsShell) {
|
|
95
|
+
// Use shell mode with quoted command
|
|
96
|
+
const shellCommand = `"${resolvedCommand}" ${args.map((a) => (a.includes(" ") ? `"${a}"` : a)).join(" ")}`;
|
|
97
|
+
proc = nodeSpawn(shellCommand, [], {
|
|
98
|
+
cwd,
|
|
99
|
+
env,
|
|
100
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
101
|
+
detached: false,
|
|
102
|
+
windowsHide: true,
|
|
103
|
+
shell: true,
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
// Use normal spawn without shell
|
|
107
|
+
proc = nodeSpawn(resolvedCommand, args, {
|
|
108
|
+
cwd,
|
|
109
|
+
env,
|
|
110
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
111
|
+
detached: false,
|
|
112
|
+
windowsHide: isWindows,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
117
|
+
throw new Error(`Failed to spawn LSP server: ${command}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check if process exited immediately (spawn failure - synchronous check)
|
|
121
|
+
if (proc.exitCode !== null || proc.killed) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`LSP server ${command} exited immediately (code: ${proc.exitCode}). ` +
|
|
124
|
+
`The binary may be missing or corrupted.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Attach error handler to prevent ENOENT crashes and track later failures
|
|
129
|
+
_attachErrorHandler(proc, command);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
process: proc,
|
|
133
|
+
stdin: proc.stdin,
|
|
134
|
+
stdout: proc.stdout,
|
|
135
|
+
stderr: proc.stderr,
|
|
136
|
+
pid: proc.pid ?? 0,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Spawn via package manager (npx/bun)
|
|
142
|
+
*/
|
|
143
|
+
export function launchViaPackageManager(
|
|
144
|
+
packageName: string,
|
|
145
|
+
args: string[] = [],
|
|
146
|
+
options: SpawnOptions = {},
|
|
147
|
+
): LSPProcess {
|
|
148
|
+
// Prefer bun if available, fall back to npx (use .cmd on Windows)
|
|
149
|
+
const isWin = process.platform === "win32";
|
|
150
|
+
|
|
151
|
+
if (process.env.BUN_INSTALL) {
|
|
152
|
+
return launchLSP(
|
|
153
|
+
isWin ? "bun.exe" : "bun",
|
|
154
|
+
["x", packageName, ...args],
|
|
155
|
+
options,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// For npx on Windows, use shell mode with the full command string
|
|
160
|
+
if (isWin) {
|
|
161
|
+
const argsStr = args.map((a) => (a.includes(" ") ? `"${a}"` : a)).join(" ");
|
|
162
|
+
const shellCommand = `npx -y ${packageName}${argsStr ? ` ${argsStr}` : ""}`;
|
|
163
|
+
|
|
164
|
+
const cwd = String(options.cwd ?? process.cwd());
|
|
165
|
+
const env = { ...process.env, ...options.env };
|
|
166
|
+
|
|
167
|
+
const proc = nodeSpawn(shellCommand, [], {
|
|
168
|
+
cwd,
|
|
169
|
+
env,
|
|
170
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
171
|
+
detached: false,
|
|
172
|
+
windowsHide: true,
|
|
173
|
+
shell: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Attach error handler to prevent ENOENT crashes
|
|
177
|
+
_attachErrorHandler(proc, packageName);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
process: proc,
|
|
181
|
+
stdin: proc.stdin!,
|
|
182
|
+
stdout: proc.stdout!,
|
|
183
|
+
stderr: proc.stderr!,
|
|
184
|
+
pid: proc.pid ?? 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return launchLSP("npx", ["-y", packageName, ...args], options);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Spawn via Node.js directly
|
|
193
|
+
*/
|
|
194
|
+
export function launchViaNode(
|
|
195
|
+
scriptPath: string,
|
|
196
|
+
args: string[] = [],
|
|
197
|
+
options: SpawnOptions = {},
|
|
198
|
+
): LSPProcess {
|
|
199
|
+
return launchLSP(process.execPath, [scriptPath, ...args], options);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Spawn via Python module
|
|
204
|
+
*/
|
|
205
|
+
export function launchViaPython(
|
|
206
|
+
moduleName: string,
|
|
207
|
+
args: string[] = [],
|
|
208
|
+
options: SpawnOptions = {},
|
|
209
|
+
): LSPProcess {
|
|
210
|
+
// On Windows, prefer 'py' launcher, fall back to 'python'
|
|
211
|
+
const pythonCmd = process.platform === "win32" ? "py" : "python3";
|
|
212
|
+
return launchLSP(pythonCmd, ["-m", moduleName, ...args], options);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Stop an LSP process gracefully
|
|
217
|
+
*/
|
|
218
|
+
export async function stopLSP(handle: LSPProcess): Promise<void> {
|
|
219
|
+
return new Promise((resolve) => {
|
|
220
|
+
// Send SIGTERM first
|
|
221
|
+
handle.process.kill("SIGTERM");
|
|
222
|
+
|
|
223
|
+
// Force kill after timeout
|
|
224
|
+
const timeout = setTimeout(() => {
|
|
225
|
+
if (!handle.process.killed) {
|
|
226
|
+
handle.process.kill("SIGKILL");
|
|
227
|
+
}
|
|
228
|
+
}, 5000);
|
|
229
|
+
|
|
230
|
+
handle.process.on("exit", () => {
|
|
231
|
+
clearTimeout(timeout);
|
|
232
|
+
resolve();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
handle.process.on("error", () => {
|
|
236
|
+
clearTimeout(timeout);
|
|
237
|
+
resolve();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP Process Launch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Handles spawning LSP servers via various methods:
|
|
5
|
+
* - Direct binary execution (using absolute paths on Windows)
|
|
6
|
+
* - Node.js scripts (npx/bun)
|
|
7
|
+
* - Package manager execution
|
|
8
|
+
*/
|
|
9
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
10
|
+
import path from "path";
|
|
11
|
+
// Helper to detect if running on Windows
|
|
12
|
+
const isWindows = process.platform === "win32";
|
|
13
|
+
/**
|
|
14
|
+
* Spawn an LSP server process
|
|
15
|
+
*
|
|
16
|
+
* Key fixes for Windows:
|
|
17
|
+
* - Uses absolute paths (relative paths fail in shell mode)
|
|
18
|
+
* - Uses shell: true for .cmd files
|
|
19
|
+
* - Uses windowsHide to prevent console window popup
|
|
20
|
+
*
|
|
21
|
+
* @param command - Command to run (e.g., "typescript-language-server")
|
|
22
|
+
* @param args - Arguments (e.g., ["--stdio"])
|
|
23
|
+
* @param options - Spawn options including cwd, env
|
|
24
|
+
* @returns LSPProcess handle
|
|
25
|
+
*/
|
|
26
|
+
export function launchLSP(command, args = [], options = {}) {
|
|
27
|
+
const cwd = String(options.cwd ?? process.cwd());
|
|
28
|
+
const env = { ...process.env, ...options.env };
|
|
29
|
+
// Always use absolute path (critical for Windows shell mode)
|
|
30
|
+
const resolvedCommand = path.isAbsolute(command)
|
|
31
|
+
? command
|
|
32
|
+
: path.resolve(cwd, command);
|
|
33
|
+
// On Windows with shell: true, we need to quote the command if it has spaces
|
|
34
|
+
const needsShell = isWindows && (resolvedCommand.includes(" ") || resolvedCommand.includes(".cmd"));
|
|
35
|
+
let proc;
|
|
36
|
+
if (needsShell) {
|
|
37
|
+
// Use shell mode with quoted command
|
|
38
|
+
const shellCommand = `"${resolvedCommand}" ${args.map(a => a.includes(" ") ? `"${a}"` : a).join(" ")}`;
|
|
39
|
+
proc = nodeSpawn(shellCommand, [], {
|
|
40
|
+
cwd,
|
|
41
|
+
env,
|
|
42
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
43
|
+
detached: false,
|
|
44
|
+
windowsHide: true,
|
|
45
|
+
shell: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Use normal spawn without shell
|
|
50
|
+
proc = nodeSpawn(resolvedCommand, args, {
|
|
51
|
+
cwd,
|
|
52
|
+
env,
|
|
53
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
54
|
+
detached: false,
|
|
55
|
+
windowsHide: isWindows,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
59
|
+
throw new Error(`Failed to spawn LSP server: ${command}`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
process: proc,
|
|
63
|
+
stdin: proc.stdin,
|
|
64
|
+
stdout: proc.stdout,
|
|
65
|
+
stderr: proc.stderr,
|
|
66
|
+
pid: proc.pid ?? 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Spawn via package manager (npx/bun)
|
|
71
|
+
*/
|
|
72
|
+
export function launchViaPackageManager(packageName, args = [], options = {}) {
|
|
73
|
+
// Prefer bun if available, fall back to npx (use .cmd on Windows)
|
|
74
|
+
const isWin = process.platform === "win32";
|
|
75
|
+
const manager = process.env.BUN_INSTALL
|
|
76
|
+
? { cmd: isWin ? "bun.exe" : "bun", args: ["x", packageName, ...args] }
|
|
77
|
+
: { cmd: isWin ? "npx.cmd" : "npx", args: ["-y", packageName, ...args] };
|
|
78
|
+
return launchLSP(manager.cmd, manager.args, options);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Spawn via Node.js directly
|
|
82
|
+
*/
|
|
83
|
+
export function launchViaNode(scriptPath, args = [], options = {}) {
|
|
84
|
+
return launchLSP(process.execPath, [scriptPath, ...args], options);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Spawn via Python module
|
|
88
|
+
*/
|
|
89
|
+
export function launchViaPython(moduleName, args = [], options = {}) {
|
|
90
|
+
// On Windows, prefer 'py' launcher, fall back to 'python'
|
|
91
|
+
const pythonCmd = process.platform === "win32" ? "py" : "python3";
|
|
92
|
+
return launchLSP(pythonCmd, ["-m", moduleName, ...args], options);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stop an LSP process gracefully
|
|
96
|
+
*/
|
|
97
|
+
export async function stopLSP(handle) {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
// Send SIGTERM first
|
|
100
|
+
handle.process.kill("SIGTERM");
|
|
101
|
+
// Force kill after timeout
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
if (!handle.process.killed) {
|
|
104
|
+
handle.process.kill("SIGKILL");
|
|
105
|
+
}
|
|
106
|
+
}, 5000);
|
|
107
|
+
handle.process.on("exit", () => {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
resolve();
|
|
110
|
+
});
|
|
111
|
+
handle.process.on("error", () => {
|
|
112
|
+
clearTimeout(timeout);
|
|
113
|
+
resolve();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|