@dsh-stack/lsp 0.0.0-stage → 0.1.1

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 CHANGED
@@ -1,3 +1,41 @@
1
- # Temporary Holding Version
1
+ # lsp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness (`dsh`) plugin: owns the LSP capability on the web profile.
4
+
5
+ The harness ships the LSP service definition (`ctx.lsp`), a generic stdio
6
+ provider (`dsh-lsp-stdio`), and a model-facing `lsp` tool (`dsh-tool-lsp`), but
7
+ composes none of them by default. This plugin mounts the trio and feeds the
8
+ stdio provider the server table from the `lsp` settings section, so the
9
+ agent's `goToDefinition` / `findReferences` / `goToImplementation` / `hover`
10
+ queries work out of the box.
11
+
12
+ ## Settings
13
+
14
+ `lsp:` section of `settings.yaml`:
15
+
16
+ ```yaml
17
+ lsp:
18
+ servers:
19
+ typescript:
20
+ command: typescript-language-server
21
+ extensionToLanguage: { ".ts": typescript, ".tsx": typescriptreact }
22
+ args: [--stdio]
23
+ ```
24
+
25
+ Server entries follow `LspLocalServerConfig` from `@deepseek-ai/dsh-lsp-stdio`.
26
+ Changes apply on the next boot (mounts are boot-time, not hot-reloaded).
27
+
28
+ ## CLI
29
+
30
+ ```
31
+ dsh lsp list
32
+ dsh lsp servers add <id> <command> [--ext=.ts=typescript] [--args=a b c]
33
+ dsh lsp servers remove <id>
34
+ ```
35
+
36
+ ## Layout
37
+
38
+ - `src/settings.ts` — the `lsp` settings namespace and server entry schema.
39
+ - `src/index.ts` — plugin: settings section + mounting the LSP trio.
40
+ - `bin/lsp.mjs` — the `dsh lsp` CLI.
41
+ - `check-plugin.mjs` — boot-verify harness (`npm test`).
package/bin/lsp.mjs ADDED
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ // jscpd:ignore-start -- per-package CLI entrypoint boilerplate (arg parsing, help text), duplicated by design across sibling CLI packages
3
+ /**
4
+ * The `dsh lsp` command: the owner surface over the lsp server table. It
5
+ * reads/writes the `lsp` section of `settings.yaml` under the same agent
6
+ * home the harness boots (DSH_HOME); the plugin mounts those servers on the
7
+ * next boot.
8
+ *
9
+ * Verbs:
10
+ * list — installed LSP servers (id, command, extensions)
11
+ * servers add <id> <command> — add a stdio server (--ext=.ts=typescript, --args=...)
12
+ * servers remove <id> — delete a server entry
13
+ */
14
+
15
+ import { homedir } from "node:os";
16
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
17
+ import { dirname, join, resolve } from "node:path";
18
+
19
+ const home = resolve(process.env.DSH_HOME ?? join(homedir(), ".agents"));
20
+ const settingsPath = join(home, "settings.yaml");
21
+
22
+ /** readSection implementation. */
23
+ function* readSection(text, section) {
24
+ let inSection = false;
25
+ for (const line of text.split("\n")) {
26
+ if (new RegExp(`^${section}\\s*:`).test(line)) {
27
+ inSection = true;
28
+ continue;
29
+ }
30
+ if (inSection && !/^\s/.test(line)) break;
31
+ if (!inSection) continue;
32
+ const match = line.match(/^\s+([A-Za-z_][A-Za-z0-9_-]*)\s*:\s*(.*)$/);
33
+ if (match) {
34
+ yield [match[1], match[2].trim().replace(/^(['"])(.*)\1$/, "$2")];
35
+ }
36
+ }
37
+ }
38
+
39
+ /** parseJsonValue implementation. */
40
+ function parseJsonValue(raw) {
41
+ if (raw === "" || raw === "null") return null;
42
+ if (raw[0] === "{" || raw[0] === "[") {
43
+ try {
44
+ return JSON.parse(raw);
45
+ } catch {
46
+ /* fall through */
47
+ }
48
+ }
49
+ if (raw === "true") return true;
50
+ if (raw === "false") return false;
51
+ const num = Number(raw);
52
+ if (raw !== "" && Number.isFinite(num)) return num;
53
+ return raw;
54
+ }
55
+
56
+ /** readServers implementation. */
57
+ async function readServers() {
58
+ try {
59
+ const text = await readFile(settingsPath, "utf8");
60
+ const section = {};
61
+ for (const [key, value] of readSection(text, "lsp")) {
62
+ section[key] = parseJsonValue(value);
63
+ }
64
+ return section;
65
+ } catch {
66
+ /* no document yet */
67
+ }
68
+ return {};
69
+ }
70
+
71
+ /** writeServers implementation. */
72
+ async function writeServers(servers) {
73
+ let text = "";
74
+ try {
75
+ text = await readFile(settingsPath, "utf8");
76
+ } catch {
77
+ /* new file */
78
+ }
79
+ const pattern = /^lsp[^\n]*\n(?:[ \t][^\n]*\n)*/m;
80
+ const section = `lsp:\n servers: ${JSON.stringify(servers.servers ?? {})}\n`;
81
+ const without = text.replace(pattern, "");
82
+ const rest = without.replace(/\n{3,}/g, "\n\n").trim();
83
+ const out = `${rest}${rest.endsWith("\n") ? "" : "\n"}${section}`;
84
+ await mkdir(dirname(settingsPath), { recursive: true });
85
+ await writeFile(settingsPath, out, "utf8");
86
+ }
87
+
88
+ /** printHelp implementation. */
89
+ function printHelp() {
90
+ process.stdout.write(`usage: dsh lsp <verb> [args]
91
+
92
+ verbs:
93
+ list
94
+ servers add <id> <command> [--ext=.ts=typescript] [--args=a b c]
95
+ servers remove <id>
96
+ `);
97
+ }
98
+
99
+ /** main implementation. */
100
+ async function main() {
101
+ const verb = process.argv[2];
102
+ const argv = process.argv.slice(3);
103
+
104
+ if (verb === "list") {
105
+ const servers = await readServers();
106
+ const entries = Object.entries(servers.servers ?? {});
107
+ if (entries.length === 0) {
108
+ process.stdout.write("no LSP servers configured\n");
109
+ return;
110
+ }
111
+ for (const [id, cfg] of entries) {
112
+ const ext = Object.keys(cfg.extensionToLanguage ?? {}).join(",") || "—";
113
+ process.stdout.write(`${id} ${cfg.command} ${ext}\n`);
114
+ }
115
+ return;
116
+ }
117
+
118
+ if (verb === "servers" && argv[0] === "add") {
119
+ const id = argv[1];
120
+ const command = argv[2];
121
+ if (!id || !command) {
122
+ process.stderr.write("dsh lsp servers add: missing id or command\n");
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ const extensionToLanguage = {};
127
+ const args = [];
128
+ for (const arg of argv.slice(3)) {
129
+ if (arg.startsWith("--ext=")) {
130
+ const spec = arg.slice(6);
131
+ const [ext, lang] = spec.split("=");
132
+ if (ext && lang) extensionToLanguage[ext] = lang;
133
+ } else if (arg.startsWith("--args=")) {
134
+ args.push(...arg.slice(7).split(" ").filter(Boolean));
135
+ }
136
+ }
137
+ const servers = await readServers();
138
+ const table = servers.servers ?? {};
139
+ table[id] = { command, extensionToLanguage, ...(args.length > 0 ? { args } : {}) };
140
+ await writeServers({ ...servers, servers: table });
141
+ process.stdout.write(`added LSP server ${id} -> ${command}\n`);
142
+ return;
143
+ }
144
+
145
+ if (verb === "servers" && argv[0] === "remove") {
146
+ const id = argv[1];
147
+ if (!id) {
148
+ process.stderr.write("dsh lsp servers remove: missing id\n");
149
+ process.exitCode = 1;
150
+ return;
151
+ }
152
+ const servers = await readServers();
153
+ const table = servers.servers ?? {};
154
+ if (table[id] === undefined) {
155
+ process.stderr.write(`dsh lsp servers remove: no server "${id}"\n`);
156
+ process.exitCode = 1;
157
+ return;
158
+ }
159
+ delete table[id];
160
+ await writeServers({ ...servers, servers: table });
161
+ process.stdout.write(`removed LSP server ${id}\n`);
162
+ return;
163
+ }
164
+
165
+ printHelp();
166
+ }
167
+
168
+ main().catch((err) => {
169
+ process.stderr.write(`dsh lsp: ${err.message}\n`);
170
+ process.exitCode = 1;
171
+ });
172
+
173
+ // jscpd:ignore-end
package/lib/index.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `lsp`: owns the LSP capability on the web profile. The harness ships the
3
+ * LSP service definition (`ctx.lsp`), a generic stdio provider
4
+ * (`dsh-lsp-stdio`), and a model-facing `lsp` tool (`dsh-tool-lsp`) — but
5
+ * composes none of them by default, and its LSP operation set is a closed
6
+ * compile-time union (no formatting: that is `formatters`' job). This
7
+ * plugin mounts the trio through `ctx.plugin` and feeds the stdio provider
8
+ * the server table this plugin's own Config carries, so the agent's
9
+ * `goToDefinition`/`findReferences`/`goToImplementation`/`hover` queries work
10
+ * out of the box. The `dsh lsp` CLI (bin/lsp.mjs) manages the table; changes
11
+ * apply on the next boot (mounts are boot-time, not hot-reloaded).
12
+ * @module lsp
13
+ */
14
+ import type { Context } from "@deepseek-ai/cordis";
15
+ import { LspConfig } from "./settings.js";
16
+ export type * from "./settings.js";
17
+ export declare const name = "lsp";
18
+ export declare const inject: string[];
19
+ /** Re-exported bare, not as `z<LspConfig>` — see `settings.ts`. */
20
+ export declare const Config: import("@deepseek-ai/schemastery").default<import("./settings.js").LspServerInput, LspConfig, "plain">;
21
+ /**
22
+ * Mount the LSP service definition, the stdio provider (only when at least one
23
+ * server is configured — `lsp-stdio` refuses an empty table), and the
24
+ * model-facing tool.
25
+ *
26
+ * Since 0.2.0 there is no separate settings section to read: this plugin's
27
+ * `Config` is the `lsp` form the settings service projects, under this entry's
28
+ * own id as its namespace, so `config.servers` is the one server table — entry
29
+ * values and profile edits alike arrive through it. It is a live reference the
30
+ * Loader commits settings writes into, so it is read through `installedServers`
31
+ * at mount rather than snapshotted. The mounts stay boot-time, so a table edited
32
+ * after boot takes effect on the next boot, the same contract the `dsh lsp` CLI
33
+ * documents.
34
+ *
35
+ * @param ctx - the plugin context.
36
+ * @param config - the plugin's live configuration, carrying the LSP server table.
37
+ */
38
+ export declare function apply(ctx: Context, config: LspConfig): void;
package/lib/index.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `lsp`: owns the LSP capability on the web profile. The harness ships the
3
+ * LSP service definition (`ctx.lsp`), a generic stdio provider
4
+ * (`dsh-lsp-stdio`), and a model-facing `lsp` tool (`dsh-tool-lsp`) — but
5
+ * composes none of them by default, and its LSP operation set is a closed
6
+ * compile-time union (no formatting: that is `formatters`' job). This
7
+ * plugin mounts the trio through `ctx.plugin` and feeds the stdio provider
8
+ * the server table this plugin's own Config carries, so the agent's
9
+ * `goToDefinition`/`findReferences`/`goToImplementation`/`hover` queries work
10
+ * out of the box. The `dsh lsp` CLI (bin/lsp.mjs) manages the table; changes
11
+ * apply on the next boot (mounts are boot-time, not hot-reloaded).
12
+ * @module lsp
13
+ */
14
+ import { declareCustomSettingsPage } from "@dsh-stack/plugin-kit";
15
+ import Lsp from "@deepseek-ai/dsh-lsp";
16
+ import * as LspStdio from "@deepseek-ai/dsh-lsp-stdio";
17
+ import * as ToolLsp from "@deepseek-ai/dsh-tool-lsp";
18
+ import { installedServers, LspConfig } from "./settings.js";
19
+ export const name = "lsp";
20
+ export const inject = [];
21
+ /** Re-exported bare, not as `z<LspConfig>` — see `settings.ts`. */
22
+ export const Config = LspConfig;
23
+ /**
24
+ * Mount the LSP service definition, the stdio provider (only when at least one
25
+ * server is configured — `lsp-stdio` refuses an empty table), and the
26
+ * model-facing tool.
27
+ *
28
+ * Since 0.2.0 there is no separate settings section to read: this plugin's
29
+ * `Config` is the `lsp` form the settings service projects, under this entry's
30
+ * own id as its namespace, so `config.servers` is the one server table — entry
31
+ * values and profile edits alike arrive through it. It is a live reference the
32
+ * Loader commits settings writes into, so it is read through `installedServers`
33
+ * at mount rather than snapshotted. The mounts stay boot-time, so a table edited
34
+ * after boot takes effect on the next boot, the same contract the `dsh lsp` CLI
35
+ * documents.
36
+ *
37
+ * @param ctx - the plugin context.
38
+ * @param config - the plugin's live configuration, carrying the LSP server table.
39
+ */
40
+ export function apply(ctx, config) {
41
+ declareCustomSettingsPage(ctx);
42
+ ctx.inject(["settings"], async () => {
43
+ const servers = installedServers(config);
44
+ if (ctx.get("lsp") === undefined) {
45
+ await ctx.plugin(Lsp);
46
+ }
47
+ if (Object.keys(servers).length > 0) {
48
+ await ctx.plugin(LspStdio, { servers });
49
+ await ctx.plugin(ToolLsp, {});
50
+ ctx.logger.info(`lsp: mounted ${Object.keys(servers).length} LSP server(s)`);
51
+ }
52
+ else {
53
+ ctx.logger.warn("lsp: no LSP servers configured — run `dsh lsp servers add <id> <command>`");
54
+ }
55
+ });
56
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * lsp settings: the `lsp` entry owns the LSP server table. The plugin mounts
3
+ * the harness LSP capability (service definition, the stdio provider with this
4
+ * table, and the model-facing `lsp` tool) on the web profile, so
5
+ * `goToDefinition`/`findReferences`/`goToImplementation`/`hover` work for the
6
+ * agent without touching the pristine harness.
7
+ *
8
+ * Since 0.2.0 there is no second section schema to register: this plugin's own
9
+ * `Config` is the form the settings service projects, and its namespace is this
10
+ * entry's own id. So one schema, `LspConfig`, carries both roles. Which of its
11
+ * fields that form shows is decided by volatility: the server table is the
12
+ * user's choice — which language servers exist and how to launch them — so it is
13
+ * declared `.volatile()`, and it is the whole of the table rather than just the
14
+ * list of ids. The settings service only recurses into `object` schemas when it
15
+ * projects a form, so a volatile marker nested inside the table's entries would
16
+ * leave `servers` itself unprojected; marking the dictionary is what makes the
17
+ * form exist, and it is what lets the user edit a server's command rather than
18
+ * only add and remove ids.
19
+ * @module lsp/settings
20
+ */
21
+ import type { Volatile } from "@deepseek-ai/cordis";
22
+ import z from "@deepseek-ai/schemastery";
23
+ import type { LspLocalServerConfig } from "@deepseek-ai/dsh-lsp-stdio";
24
+ /** Namespace of the LSP server table — this plugin's own Loader entry id. */
25
+ export declare const NS: import("@dsh-stack/plugin-kit").SettingsNamespace;
26
+ /** One stdio language server the plugin mounts through `dsh-lsp-stdio`. */
27
+ export type LspServerEntry = LspLocalServerConfig;
28
+ export declare const LspServerEntry: z<LspServerEntry>;
29
+ /** The plugin's configuration — and, since 0.2.0, its own settings form. */
30
+ export interface LspConfig {
31
+ /**
32
+ * Lowercase leading-dot extension → installed stdio server. Volatile because
33
+ * the answer is machine-local: which language servers exist, under what ids and
34
+ * with what commands, differs per workstation, so no deployment can pin one
35
+ * correct table and the user owns it. It is volatile as one whole-dictionary
36
+ * node — not a marker per table entry — because the settings service only
37
+ * recurses into `object` schemas when it projects a form, and because a form
38
+ * write can only address a path lying under a volatile node: a reference per
39
+ * entry could be neither declared nor edited, and marking only the ids would
40
+ * leave the command a client cannot correct.
41
+ */
42
+ servers: Volatile<Record<string, LspServerEntry>>;
43
+ }
44
+ /** What the server-table schema accepts before validation. */
45
+ export type LspServerInput = {
46
+ servers: Record<string, LspServerEntry>;
47
+ };
48
+ /**
49
+ * Not annotated `z<LspConfig>`: a volatile field's output type is `Volatile<T>`,
50
+ * which `z<T>`'s invariant position cannot hold. The three-parameter schema
51
+ * type is the annotation that can, and it is required rather than cosmetic —
52
+ * left to inference, the nested `Volatile` inside this dictionary has no name
53
+ * the declaration emitter can write (`TS2742`), and `themes`/`formatters` get
54
+ * away with a bare `z.object({...})` only because their volatile fields are
55
+ * scalar. `harness`'s own `llm-pi-ai` uses the same bare shape and builds with
56
+ * a different declaration emitter.
57
+ */
58
+ export declare const LspConfig: z<LspServerInput, LspConfig, "plain">;
59
+ /**
60
+ * The servers currently installed for this entry, in the mutable shape
61
+ * `dsh-lsp-stdio` declares.
62
+ *
63
+ * Reads the volatile field at call time, so a settings write is visible to the
64
+ * next read rather than to a value captured at boot. `args` is copied because a
65
+ * volatile snapshot is deeply frozen and the stdio provider's own Config declares
66
+ * a mutable `string[]` there; the plain properties and index signatures need no
67
+ * such copy, as TypeScript does not compare them for `readonly`.
68
+ *
69
+ * @param config - the plugin's projected Config.
70
+ * @returns the installed server table, empty when none is configured.
71
+ */
72
+ export declare function installedServers(config: LspConfig | undefined): Record<string, LspServerEntry>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * lsp settings: the `lsp` entry owns the LSP server table. The plugin mounts
3
+ * the harness LSP capability (service definition, the stdio provider with this
4
+ * table, and the model-facing `lsp` tool) on the web profile, so
5
+ * `goToDefinition`/`findReferences`/`goToImplementation`/`hover` work for the
6
+ * agent without touching the pristine harness.
7
+ *
8
+ * Since 0.2.0 there is no second section schema to register: this plugin's own
9
+ * `Config` is the form the settings service projects, and its namespace is this
10
+ * entry's own id. So one schema, `LspConfig`, carries both roles. Which of its
11
+ * fields that form shows is decided by volatility: the server table is the
12
+ * user's choice — which language servers exist and how to launch them — so it is
13
+ * declared `.volatile()`, and it is the whole of the table rather than just the
14
+ * list of ids. The settings service only recurses into `object` schemas when it
15
+ * projects a form, so a volatile marker nested inside the table's entries would
16
+ * leave `servers` itself unprojected; marking the dictionary is what makes the
17
+ * form exist, and it is what lets the user edit a server's command rather than
18
+ * only add and remove ids.
19
+ * @module lsp/settings
20
+ */
21
+ import z from "@deepseek-ai/schemastery";
22
+ import { settingsNamespace } from "@dsh-stack/plugin-kit";
23
+ /** Namespace of the LSP server table — this plugin's own Loader entry id. */
24
+ export const NS = settingsNamespace("lsp");
25
+ export const LspServerEntry = z.object({
26
+ command: z.string().required(),
27
+ extensionToLanguage: z.dict(String).required(),
28
+ args: z.array(String).default([]),
29
+ env: z.dict(String).default({}),
30
+ initializationOptions: z.any().default(null),
31
+ configuration: z.any().default(null),
32
+ maxMessageBytes: z.number().default(16_000_000),
33
+ maxStderrBytes: z.number().default(1_000_000),
34
+ maxDocumentBytes: z.number().default(4_000_000),
35
+ shutdownTimeoutMs: z.number().default(5_000),
36
+ killGraceMs: z.number().default(2_000),
37
+ });
38
+ /**
39
+ * Not annotated `z<LspConfig>`: a volatile field's output type is `Volatile<T>`,
40
+ * which `z<T>`'s invariant position cannot hold. The three-parameter schema
41
+ * type is the annotation that can, and it is required rather than cosmetic —
42
+ * left to inference, the nested `Volatile` inside this dictionary has no name
43
+ * the declaration emitter can write (`TS2742`), and `themes`/`formatters` get
44
+ * away with a bare `z.object({...})` only because their volatile fields are
45
+ * scalar. `harness`'s own `llm-pi-ai` uses the same bare shape and builds with
46
+ * a different declaration emitter.
47
+ */
48
+ export const LspConfig = z.object({
49
+ servers: z.dict(LspServerEntry).default({}).volatile(),
50
+ });
51
+ /**
52
+ * The servers currently installed for this entry, in the mutable shape
53
+ * `dsh-lsp-stdio` declares.
54
+ *
55
+ * Reads the volatile field at call time, so a settings write is visible to the
56
+ * next read rather than to a value captured at boot. `args` is copied because a
57
+ * volatile snapshot is deeply frozen and the stdio provider's own Config declares
58
+ * a mutable `string[]` there; the plain properties and index signatures need no
59
+ * such copy, as TypeScript does not compare them for `readonly`.
60
+ *
61
+ * @param config - the plugin's projected Config.
62
+ * @returns the installed server table, empty when none is configured.
63
+ */
64
+ export function installedServers(config) {
65
+ return Object.fromEntries(Object.entries(config?.servers?.get() ?? {}).map(([id, entry]) => [
66
+ id,
67
+ { ...entry, args: [...(entry.args ?? [])] },
68
+ ]));
69
+ }
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@dsh-stack/lsp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "LSP server configuration + dsh lsp CLI for the DeepSeek Harness LSP capability seam",
4
+ "version": "0.1.1",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./src/*": "./src/*",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "bin",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "node check-plugin.mjs"
25
+ },
26
+ "peerDependencies": {
27
+ "@deepseek-ai/cordis": "^4.0.1",
28
+ "@deepseek-ai/dsh-lsp": "0.2.0-rc.2",
29
+ "@deepseek-ai/dsh-lsp-stdio": "0.2.0-rc.2",
30
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
31
+ "@deepseek-ai/dsh-tool-lsp": "0.2.0-rc.2",
32
+ "@deepseek-ai/schemastery": "^3.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@dsh-stack/plugin-kit": "0.1.1"
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.9.0",
39
+ "@types/node": "^22.0.0",
40
+ "@deepseek-ai/cordis": "^4.0.1",
41
+ "@deepseek-ai/dsh-lsp": "0.2.0-rc.2",
42
+ "@deepseek-ai/dsh-lsp-stdio": "0.2.0-rc.2",
43
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
44
+ "@deepseek-ai/dsh-tool-lsp": "0.2.0-rc.2",
45
+ "@deepseek-ai/schemastery": "^3.0.0"
46
+ },
47
+ "stack": {
48
+ "kind": "plugin",
49
+ "id": "stack.dev.lsp"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/marius-patrik/dsh-stack.git",
57
+ "directory": "plugins/lsp"
58
+ }
59
+ }