gsd-pi 2.8.2 → 2.9.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 +2 -1
- package/dist/cli.js +5 -0
- package/dist/loader.js +1 -1
- package/dist/update-check.d.ts +24 -0
- package/dist/update-check.js +93 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts +13 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +2 -2
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +80 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +80 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/packages/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/packages/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/packages/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/src/resources/extensions/ask-user-questions.ts +42 -2
- package/src/resources/extensions/bg-shell/index.ts +34 -37
- package/src/resources/extensions/browser-tools/core.d.ts +205 -0
- package/src/resources/extensions/browser-tools/index.ts +2 -2
- package/src/resources/extensions/browser-tools/refs.ts +1 -1
- package/src/resources/extensions/browser-tools/tools/session.ts +1 -1
- package/src/resources/extensions/context7/index.ts +2 -2
- package/src/resources/extensions/get-secrets-from-user.ts +3 -2
- package/src/resources/extensions/google-search/index.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +126 -12
- package/src/resources/extensions/gsd/commands.ts +218 -3
- package/src/resources/extensions/gsd/doctor.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +163 -13
- package/src/resources/extensions/gsd/guided-flow.ts +19 -9
- package/src/resources/extensions/gsd/index.ts +17 -7
- package/src/resources/extensions/gsd/preferences.ts +1 -1
- package/src/resources/extensions/gsd/tests/git-service.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +10 -10
- package/src/resources/extensions/gsd/tests/next-milestone-id.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +352 -0
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/worktree.ts +20 -1
- package/src/resources/extensions/mac-tools/index.ts +1 -1
- package/src/resources/extensions/search-the-web/command-search-provider.ts +1 -1
- package/src/resources/extensions/search-the-web/format.ts +1 -1
- package/src/resources/extensions/search-the-web/index.ts +5 -5
- package/src/resources/extensions/search-the-web/native-search.ts +5 -6
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +7 -7
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +11 -11
- package/src/resources/extensions/search-the-web/tool-search.ts +10 -10
- package/src/resources/extensions/shared/interview-ui.ts +2 -2
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import YAML from "yaml";
|
|
7
|
+
import { globSync } from "glob";
|
|
8
|
+
import { CONFIG_DIR_NAME } from "../../config.js";
|
|
9
|
+
import { isRecord } from "./helpers.js";
|
|
10
|
+
import type { ServerConfig } from "./types.js";
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const DEFAULTS = require("./defaults.json") as Record<string, Partial<ServerConfig>>;
|
|
14
|
+
|
|
15
|
+
export interface LspConfig {
|
|
16
|
+
servers: Record<string, ServerConfig>;
|
|
17
|
+
/** Idle timeout in milliseconds. If set, LSP clients will be shutdown after this period of inactivity. Disabled by default. */
|
|
18
|
+
idleTimeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Default Server Configuration Loading
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
const PID_TOKEN = "$PID";
|
|
26
|
+
|
|
27
|
+
interface NormalizedConfig {
|
|
28
|
+
servers: Record<string, Partial<ServerConfig>>;
|
|
29
|
+
idleTimeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseConfigContent(content: string, filePath: string): unknown {
|
|
33
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
34
|
+
if (extension === ".yaml" || extension === ".yml") {
|
|
35
|
+
return YAML.parse(content) as unknown;
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(content) as unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeConfig(value: unknown): NormalizedConfig | null {
|
|
41
|
+
if (!isRecord(value)) return null;
|
|
42
|
+
|
|
43
|
+
const idleTimeoutMs = typeof value.idleTimeoutMs === "number" ? value.idleTimeoutMs : undefined;
|
|
44
|
+
const rawServers = value.servers;
|
|
45
|
+
|
|
46
|
+
if (isRecord(rawServers)) {
|
|
47
|
+
return { servers: rawServers as Record<string, Partial<ServerConfig>>, idleTimeoutMs };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const servers = Object.fromEntries(Object.entries(value).filter(([key]) => key !== "idleTimeoutMs")) as Record<
|
|
51
|
+
string,
|
|
52
|
+
Partial<ServerConfig>
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
return { servers, idleTimeoutMs };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function normalizeStringArray(value: unknown): string[] | null {
|
|
59
|
+
if (!Array.isArray(value)) return null;
|
|
60
|
+
const items = value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
|
|
61
|
+
return items.length > 0 ? items : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeServerConfig(name: string, config: Partial<ServerConfig>): ServerConfig | null {
|
|
65
|
+
const command = typeof config.command === "string" && config.command.length > 0 ? config.command : null;
|
|
66
|
+
const fileTypes = normalizeStringArray(config.fileTypes);
|
|
67
|
+
const rootMarkers = normalizeStringArray(config.rootMarkers);
|
|
68
|
+
|
|
69
|
+
if (!command || !fileTypes || !rootMarkers) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const args = Array.isArray(config.args)
|
|
74
|
+
? config.args.filter((entry): entry is string => typeof entry === "string")
|
|
75
|
+
: undefined;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
...config,
|
|
79
|
+
command,
|
|
80
|
+
args,
|
|
81
|
+
fileTypes,
|
|
82
|
+
rootMarkers,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function readConfigFile(filePath: string): NormalizedConfig | null {
|
|
87
|
+
try {
|
|
88
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
89
|
+
const parsed = parseConfigContent(content, filePath);
|
|
90
|
+
return normalizeConfig(parsed);
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function coerceServerConfigs(servers: Record<string, Partial<ServerConfig>>): Record<string, ServerConfig> {
|
|
97
|
+
const result: Record<string, ServerConfig> = {};
|
|
98
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
99
|
+
const normalized = normalizeServerConfig(name, config);
|
|
100
|
+
if (normalized) {
|
|
101
|
+
result[name] = normalized;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function mergeServers(
|
|
108
|
+
base: Record<string, ServerConfig>,
|
|
109
|
+
overrides: Record<string, Partial<ServerConfig>>,
|
|
110
|
+
): Record<string, ServerConfig> {
|
|
111
|
+
const merged: Record<string, ServerConfig> = { ...base };
|
|
112
|
+
for (const [name, config] of Object.entries(overrides)) {
|
|
113
|
+
if (merged[name]) {
|
|
114
|
+
const candidate = { ...merged[name], ...config };
|
|
115
|
+
const normalized = normalizeServerConfig(name, candidate);
|
|
116
|
+
if (normalized) {
|
|
117
|
+
merged[name] = normalized;
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
const normalized = normalizeServerConfig(name, config);
|
|
121
|
+
if (normalized) {
|
|
122
|
+
merged[name] = normalized;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return merged;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function applyRuntimeDefaults(servers: Record<string, ServerConfig>): Record<string, ServerConfig> {
|
|
130
|
+
const updated: Record<string, ServerConfig> = { ...servers };
|
|
131
|
+
|
|
132
|
+
if (updated.omnisharp?.args) {
|
|
133
|
+
const args = updated.omnisharp.args.map((arg: string) => (arg === PID_TOKEN ? String(process.pid) : arg));
|
|
134
|
+
updated.omnisharp = { ...updated.omnisharp, args };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return updated;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// =============================================================================
|
|
141
|
+
// Configuration Loading
|
|
142
|
+
// =============================================================================
|
|
143
|
+
|
|
144
|
+
export function hasRootMarkers(cwd: string, markers: string[]): boolean {
|
|
145
|
+
for (const marker of markers) {
|
|
146
|
+
if (marker.includes("*")) {
|
|
147
|
+
try {
|
|
148
|
+
const matches = globSync(marker, { cwd, nodir: false });
|
|
149
|
+
if (matches.length > 0) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
} catch {
|
|
153
|
+
// Failed to resolve glob root marker
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const filePath = path.join(cwd, marker);
|
|
158
|
+
if (fs.existsSync(filePath)) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// =============================================================================
|
|
166
|
+
// Local Binary Resolution
|
|
167
|
+
// =============================================================================
|
|
168
|
+
|
|
169
|
+
const LOCAL_BIN_PATHS: Array<{ markers: string[]; binDir: string }> = [
|
|
170
|
+
{ markers: ["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"], binDir: "node_modules/.bin" },
|
|
171
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: ".venv/bin" },
|
|
172
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: "venv/bin" },
|
|
173
|
+
{ markers: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"], binDir: ".env/bin" },
|
|
174
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDir: "vendor/bundle/bin" },
|
|
175
|
+
{ markers: ["Gemfile", "Gemfile.lock"], binDir: "bin" },
|
|
176
|
+
{ markers: ["go.mod", "go.sum"], binDir: "bin" },
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
function which(command: string): string | null {
|
|
180
|
+
const result = spawnSync("which", [command], { encoding: "utf-8" });
|
|
181
|
+
if (result.status !== 0) return null;
|
|
182
|
+
return result.stdout.trim() || null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function resolveCommand(command: string, cwd: string): string | null {
|
|
186
|
+
for (const { markers, binDir } of LOCAL_BIN_PATHS) {
|
|
187
|
+
if (hasRootMarkers(cwd, markers)) {
|
|
188
|
+
const localPath = path.join(cwd, binDir, command);
|
|
189
|
+
if (fs.existsSync(localPath)) {
|
|
190
|
+
return localPath;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return which(command);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Configuration file search paths (in priority order).
|
|
200
|
+
*/
|
|
201
|
+
function getConfigPaths(cwd: string): string[] {
|
|
202
|
+
const filenames = ["lsp.json", ".lsp.json", "lsp.yaml", ".lsp.yaml", "lsp.yml", ".lsp.yml"];
|
|
203
|
+
const paths: string[] = [];
|
|
204
|
+
|
|
205
|
+
// Project root files (highest priority)
|
|
206
|
+
for (const filename of filenames) {
|
|
207
|
+
paths.push(path.join(cwd, filename));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Project config directory
|
|
211
|
+
const projectConfigDir = path.join(cwd, CONFIG_DIR_NAME);
|
|
212
|
+
for (const filename of filenames) {
|
|
213
|
+
paths.push(path.join(projectConfigDir, filename));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// User config directory
|
|
217
|
+
const userConfigDir = path.join(os.homedir(), CONFIG_DIR_NAME, "agent");
|
|
218
|
+
for (const filename of filenames) {
|
|
219
|
+
paths.push(path.join(userConfigDir, filename));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// User home root files (lowest priority fallback)
|
|
223
|
+
for (const filename of filenames) {
|
|
224
|
+
paths.push(path.join(os.homedir(), filename));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return paths;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Load LSP configuration.
|
|
232
|
+
*
|
|
233
|
+
* Priority (highest to lowest):
|
|
234
|
+
* 1. Project root: lsp.json/.lsp.json/lsp.yml/.lsp.yml/lsp.yaml/.lsp.yaml
|
|
235
|
+
* 2. Project config dir: {CONFIG_DIR_NAME}/lsp.* (+ hidden variants)
|
|
236
|
+
* 3. User config dir: ~/{CONFIG_DIR_NAME}/agent/lsp.* (+ hidden variants)
|
|
237
|
+
* 4. User home root: ~/lsp.*, ~/.lsp.*
|
|
238
|
+
* 5. Auto-detect from project markers + available binaries
|
|
239
|
+
*/
|
|
240
|
+
export function loadConfig(cwd: string): LspConfig {
|
|
241
|
+
let mergedServers = coerceServerConfigs(DEFAULTS);
|
|
242
|
+
|
|
243
|
+
const configPaths = getConfigPaths(cwd).reverse();
|
|
244
|
+
let hasOverrides = false;
|
|
245
|
+
|
|
246
|
+
let idleTimeoutMs: number | undefined;
|
|
247
|
+
for (const configPath of configPaths) {
|
|
248
|
+
const parsed = readConfigFile(configPath);
|
|
249
|
+
if (!parsed) continue;
|
|
250
|
+
const hasServerOverrides = Object.keys(parsed.servers).length > 0;
|
|
251
|
+
if (hasServerOverrides) {
|
|
252
|
+
hasOverrides = true;
|
|
253
|
+
mergedServers = mergeServers(mergedServers, parsed.servers);
|
|
254
|
+
}
|
|
255
|
+
if (parsed.idleTimeoutMs !== undefined) {
|
|
256
|
+
idleTimeoutMs = parsed.idleTimeoutMs;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!hasOverrides) {
|
|
261
|
+
const detected: Record<string, ServerConfig> = {};
|
|
262
|
+
const defaultsWithRuntime = applyRuntimeDefaults(mergedServers);
|
|
263
|
+
|
|
264
|
+
for (const [name, config] of Object.entries(defaultsWithRuntime)) {
|
|
265
|
+
if (!hasRootMarkers(cwd, config.rootMarkers)) continue;
|
|
266
|
+
const resolved = resolveCommand(config.command, cwd);
|
|
267
|
+
if (!resolved) continue;
|
|
268
|
+
detected[name] = { ...config, resolvedCommand: resolved };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { servers: detected, idleTimeoutMs };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const mergedWithRuntime = applyRuntimeDefaults(mergedServers);
|
|
275
|
+
const available: Record<string, ServerConfig> = {};
|
|
276
|
+
|
|
277
|
+
for (const [name, config] of Object.entries(mergedWithRuntime)) {
|
|
278
|
+
if (config.disabled) continue;
|
|
279
|
+
const resolved = resolveCommand(config.command, cwd);
|
|
280
|
+
if (!resolved) continue;
|
|
281
|
+
available[name] = { ...config, resolvedCommand: resolved };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { servers: available, idleTimeoutMs };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// =============================================================================
|
|
288
|
+
// Server Selection
|
|
289
|
+
// =============================================================================
|
|
290
|
+
|
|
291
|
+
export function getServersForFile(config: LspConfig, filePath: string): Array<[string, ServerConfig]> {
|
|
292
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
293
|
+
const fileName = path.basename(filePath).toLowerCase();
|
|
294
|
+
const matches: Array<[string, ServerConfig]> = [];
|
|
295
|
+
|
|
296
|
+
for (const [name, serverConfig] of Object.entries(config.servers)) {
|
|
297
|
+
const supportsFile = serverConfig.fileTypes.some(fileType => {
|
|
298
|
+
const normalized = fileType.toLowerCase();
|
|
299
|
+
return normalized === ext || normalized === fileName;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
if (supportsFile) {
|
|
303
|
+
matches.push([name, serverConfig]);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Sort: primary servers (non-linters) first, then linters
|
|
308
|
+
return matches.sort((a, b) => {
|
|
309
|
+
const aIsLinter = a[1].isLinter ? 1 : 0;
|
|
310
|
+
const bIsLinter = b[1].isLinter ? 1 : 0;
|
|
311
|
+
return aIsLinter - bIsLinter;
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export function getServerForFile(config: LspConfig, filePath: string): [string, ServerConfig] | null {
|
|
316
|
+
const servers = getServersForFile(config, filePath);
|
|
317
|
+
return servers.length > 0 ? servers[0] : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function hasCapability(
|
|
321
|
+
config: ServerConfig,
|
|
322
|
+
capability: keyof NonNullable<ServerConfig["capabilities"]>,
|
|
323
|
+
): boolean {
|
|
324
|
+
return config.capabilities?.[capability] === true;
|
|
325
|
+
}
|