march-cli 0.1.13 → 0.1.15
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/package.json +2 -1
- package/src/agent/command-exec-tool.mjs +53 -10
- package/src/agent/{read-file-tool.mjs → file-tools/read-file-tool.mjs} +2 -2
- package/src/agent/file-tools/read-image-tool.mjs +76 -0
- package/src/agent/runner.mjs +7 -7
- package/src/agent/runtime/remote-runner-client.mjs +1 -1
- package/src/agent/runtime/runner-ipc-target.mjs +2 -3
- package/src/agent/runtime/runner-runtime-host.mjs +1 -0
- package/src/agent/screen-tools/list-windows-tool.mjs +39 -0
- package/src/agent/screen-tools/screen-tool.mjs +49 -0
- package/src/agent/screen-tools/windows-screen.mjs +133 -0
- package/src/agent/session/session-options.mjs +2 -1
- package/src/agent/tools.mjs +14 -7
- package/src/agent/vision-capability.mjs +14 -0
- package/src/cli/args.mjs +8 -0
- package/src/cli/session/pi-session-switch-command.mjs +2 -2
- package/src/cli/tui/status/status-bar.mjs +2 -1
- package/src/config/config-json.mjs +11 -0
- package/src/context/system-core/base.md +2 -1
- package/src/lsp/client.mjs +59 -6
- package/src/lsp/managed-node-server.mjs +5 -0
- package/src/lsp/server-definitions.mjs +188 -0
- package/src/lsp/servers.mjs +11 -134
- package/src/main.mjs +7 -6
- package/src/provider/accept-command.mjs +89 -0
- package/src/provider/command.mjs +21 -0
- package/src/provider/custom-provider.mjs +5 -4
- package/src/provider/share-command.mjs +79 -0
- package/src/provider/share-payload.mjs +52 -0
- package/src/supergrok/tool.mjs +6 -6
|
@@ -26,13 +26,13 @@ export async function resumePiSessionById(id, { runner, sessions, projectMarchDi
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
let result;
|
|
29
|
+
const restoreState = toContextSessionState(sidecar.state);
|
|
29
30
|
try {
|
|
30
|
-
result = await runner.switchPiSession(session.path);
|
|
31
|
+
result = await runner.switchPiSession(session.path, restoreState);
|
|
31
32
|
} catch (err) {
|
|
32
33
|
return [`Error: failed to switch pi session ${session.id}: ${err.message}`];
|
|
33
34
|
}
|
|
34
35
|
if (result?.cancelled) return [`Resume pi session cancelled: ${session.id}`];
|
|
35
|
-
runner.engine.restoreSession(toContextSessionState(sidecar.state), null, { replace: true });
|
|
36
36
|
return [`Resumed pi session: ${session.id}`];
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -75,7 +75,8 @@ export class StatusBar {
|
|
|
75
75
|
const mode = formatModeLabel(parts.mode || DEFAULT_STATUS_TEXT);
|
|
76
76
|
const activity = parts.activity ? statusBar.muted(`${parts.activity} · `) : "";
|
|
77
77
|
const right = [parts.model, parts.thinking].filter(Boolean).join(" • ");
|
|
78
|
-
const
|
|
78
|
+
const paintWidth = inputPaintWidth(innerWidth);
|
|
79
|
+
const line = composeMetaLine({ left: `${activity}${mode}`, right, width: paintWidth, muteLeft: false });
|
|
79
80
|
return ["", `${insetLeft}${line}${insetRight}`];
|
|
80
81
|
}
|
|
81
82
|
}
|
|
@@ -38,6 +38,17 @@ export function upsertProviderProfile({ path = globalConfigJsonPath(), id, type,
|
|
|
38
38
|
return config;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export function upsertSharedProviderProfile({ path = globalConfigJsonPath(), id, provider }) {
|
|
42
|
+
const config = readConfigJson(path);
|
|
43
|
+
const providers = config.providers && typeof config.providers === "object" && !Array.isArray(config.providers)
|
|
44
|
+
? config.providers
|
|
45
|
+
: {};
|
|
46
|
+
providers[id] = provider;
|
|
47
|
+
config.providers = providers;
|
|
48
|
+
writeConfigJson(path, config);
|
|
49
|
+
return config;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
export function upsertModelSelection({ path = globalConfigJsonPath(), provider, model, serviceTier }) {
|
|
42
53
|
const config = readConfigJson(path);
|
|
43
54
|
config.provider = provider;
|
|
@@ -53,7 +53,8 @@ The user primarily asks for software engineering work: fixing bugs, adding behav
|
|
|
53
53
|
</git_contract>
|
|
54
54
|
|
|
55
55
|
<memory_system>
|
|
56
|
-
- [memory_hint source="..."] blocks in recent_chat
|
|
56
|
+
- [memory_hint source="..."] blocks in recent_chat are lightweight recall hints matched from prior thinking output. Treat them as possibly relevant pointers, not as complete facts.
|
|
57
|
+
- If a memory hint may help the current task, use memory_open(id) to read the full memory before relying on it. Ignore hints that are clearly unrelated or too low-value for the task.
|
|
57
58
|
- Use memory_search(query) for full-text search across all memories.
|
|
58
59
|
- To edit an existing memory, use memory_open(id) to get its path, then edit_file with mode="patch" for targeted edits.
|
|
59
60
|
- Use memory_save() to create memories or update whole fields. Before creating a new memory, first search/open related memories and merge updates into an existing memory when they share the same topic, project, or decision thread; prefer modifying the existing memory file over creating a scattered new one. Tags are the primary retrieval key for future recall. Prefer lowercase kebab-case tags like 'march-cli', 'tooling', 'permissions'.
|
package/src/lsp/client.mjs
CHANGED
|
@@ -1,23 +1,76 @@
|
|
|
1
1
|
import { pathToFileURL } from "node:url";
|
|
2
2
|
import { spawnCommand } from "../platform/spawn-command.mjs";
|
|
3
|
-
import { extname } from "node:path";
|
|
3
|
+
import { basename, extname } from "node:path";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
5
|
|
|
6
6
|
const INITIALIZE_TIMEOUT_MS = 15000;
|
|
7
7
|
const TEXT_DOCUMENT_SYNC_INCREMENTAL = 2;
|
|
8
8
|
|
|
9
9
|
const LANGUAGE_IDS = {
|
|
10
|
+
".astro": "astro",
|
|
11
|
+
".bash": "shellscript",
|
|
12
|
+
".c": "c",
|
|
13
|
+
".c++": "cpp",
|
|
14
|
+
".cc": "cpp",
|
|
15
|
+
".cjs": "javascript",
|
|
16
|
+
".cpp": "cpp",
|
|
17
|
+
".css": "css",
|
|
18
|
+
".cts": "typescript",
|
|
19
|
+
".cxx": "cpp",
|
|
20
|
+
".dart": "dart",
|
|
21
|
+
".dockerfile": "dockerfile",
|
|
22
|
+
".go": "go",
|
|
23
|
+
".h": "c",
|
|
24
|
+
".h++": "cpp",
|
|
25
|
+
".hh": "cpp",
|
|
26
|
+
".hpp": "cpp",
|
|
27
|
+
".htm": "html",
|
|
28
|
+
".html": "html",
|
|
29
|
+
".hxx": "cpp",
|
|
10
30
|
".js": "javascript",
|
|
31
|
+
".json": "json",
|
|
32
|
+
".jsonc": "jsonc",
|
|
11
33
|
".jsx": "javascriptreact",
|
|
12
|
-
".
|
|
13
|
-
".
|
|
34
|
+
".ksh": "shellscript",
|
|
35
|
+
".less": "less",
|
|
36
|
+
".lua": "lua",
|
|
37
|
+
".markdown": "markdown",
|
|
38
|
+
".md": "markdown",
|
|
39
|
+
".mdx": "mdx",
|
|
14
40
|
".mjs": "javascript",
|
|
15
|
-
".cjs": "javascript",
|
|
16
41
|
".mts": "typescript",
|
|
17
|
-
".
|
|
42
|
+
".php": "php",
|
|
43
|
+
".prisma": "prisma",
|
|
44
|
+
".py": "python",
|
|
45
|
+
".pyi": "python",
|
|
46
|
+
".rs": "rust",
|
|
47
|
+
".sass": "sass",
|
|
48
|
+
".scss": "scss",
|
|
49
|
+
".sh": "shellscript",
|
|
50
|
+
".svelte": "svelte",
|
|
51
|
+
".tf": "terraform",
|
|
52
|
+
".tfvars": "terraform-vars",
|
|
53
|
+
".toml": "toml",
|
|
54
|
+
".ts": "typescript",
|
|
55
|
+
".tsx": "typescriptreact",
|
|
18
56
|
".vue": "vue",
|
|
57
|
+
".yaml": "yaml",
|
|
58
|
+
".yml": "yaml",
|
|
59
|
+
".zig": "zig",
|
|
60
|
+
".zon": "zig",
|
|
61
|
+
".zsh": "shellscript",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const FILENAME_LANGUAGE_IDS = {
|
|
65
|
+
containerfile: "dockerfile",
|
|
66
|
+
dockerfile: "dockerfile",
|
|
19
67
|
};
|
|
20
68
|
|
|
69
|
+
export function languageIdForPath(path) {
|
|
70
|
+
const name = basename(path).toLowerCase();
|
|
71
|
+
return FILENAME_LANGUAGE_IDS[name] ?? LANGUAGE_IDS[extname(path).toLowerCase()] ?? "plaintext";
|
|
72
|
+
}
|
|
73
|
+
|
|
21
74
|
export class LspClient {
|
|
22
75
|
constructor({ serverId, command, args = [], cwd, initialization = {}, store }) {
|
|
23
76
|
this.serverId = serverId;
|
|
@@ -94,7 +147,7 @@ export class LspClient {
|
|
|
94
147
|
this.#notify("textDocument/didOpen", {
|
|
95
148
|
textDocument: {
|
|
96
149
|
uri,
|
|
97
|
-
languageId:
|
|
150
|
+
languageId: languageIdForPath(path),
|
|
98
151
|
version: 0,
|
|
99
152
|
text,
|
|
100
153
|
},
|
|
@@ -8,6 +8,11 @@ const DEFAULT_CACHE_ROOT = join(homedir(), ".march", "lsp", "node");
|
|
|
8
8
|
const MANAGED_PACKAGES = {
|
|
9
9
|
"typescript-language-server": ["typescript-language-server", "typescript"],
|
|
10
10
|
"vue-language-server": ["@vue/language-server", "typescript"],
|
|
11
|
+
"pyright-langserver": ["pyright"],
|
|
12
|
+
"vscode-json-language-server": ["vscode-langservers-extracted"],
|
|
13
|
+
"vscode-html-language-server": ["vscode-langservers-extracted"],
|
|
14
|
+
"vscode-css-language-server": ["vscode-langservers-extracted"],
|
|
15
|
+
"docker-langserver": ["dockerfile-language-server-nodejs"],
|
|
11
16
|
};
|
|
12
17
|
|
|
13
18
|
const installs = new Map();
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const NODE_ROOT_MARKERS = ["package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock", "package.json"];
|
|
2
|
+
|
|
3
|
+
export function createLspServerDefinitions({ resolveTypeScriptProjectRoot, resolveTypeScriptSdk, resolveTypeScriptServer }) {
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
id: "vue",
|
|
7
|
+
extensions: [".vue"],
|
|
8
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
9
|
+
command: ["vue-language-server"],
|
|
10
|
+
managedCommand: "vue-language-server",
|
|
11
|
+
args: ["--stdio"],
|
|
12
|
+
initialization: ({ root, workspaceRoot }) => {
|
|
13
|
+
const tsdk = resolveTypeScriptSdk({ root, workspaceRoot });
|
|
14
|
+
return tsdk ? { typescript: { tsdk } } : null;
|
|
15
|
+
},
|
|
16
|
+
managedTypeScript: true,
|
|
17
|
+
missingInitialization: "missing project typescript SDK",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "typescript",
|
|
21
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
22
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
23
|
+
projectRoot: resolveTypeScriptProjectRoot,
|
|
24
|
+
command: ["typescript-language-server"],
|
|
25
|
+
managedCommand: "typescript-language-server",
|
|
26
|
+
args: ["--stdio"],
|
|
27
|
+
initialization: ({ root, workspaceRoot }) => {
|
|
28
|
+
const tsserver = resolveTypeScriptServer({ root, workspaceRoot });
|
|
29
|
+
return tsserver ? { tsserver: { path: tsserver } } : null;
|
|
30
|
+
},
|
|
31
|
+
managedTypeScript: true,
|
|
32
|
+
missingInitialization: "missing project typescript/tsserver.js",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "python",
|
|
36
|
+
extensions: [".py", ".pyi"],
|
|
37
|
+
rootMarkers: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json"],
|
|
38
|
+
command: ["pyright-langserver"],
|
|
39
|
+
managedCommand: "pyright-langserver",
|
|
40
|
+
args: ["--stdio"],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "go",
|
|
44
|
+
extensions: [".go"],
|
|
45
|
+
rootMarkers: ["go.work", "go.mod", "go.sum"],
|
|
46
|
+
command: ["gopls"],
|
|
47
|
+
args: [],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "rust",
|
|
51
|
+
extensions: [".rs"],
|
|
52
|
+
rootMarkers: ["Cargo.toml", "Cargo.lock"],
|
|
53
|
+
command: ["rust-analyzer"],
|
|
54
|
+
args: [],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "clangd",
|
|
58
|
+
extensions: [".c", ".cpp", ".cc", ".cxx", ".c++", ".h", ".hpp", ".hh", ".hxx", ".h++"],
|
|
59
|
+
rootMarkers: ["compile_commands.json", "compile_flags.txt", ".clangd"],
|
|
60
|
+
command: ["clangd"],
|
|
61
|
+
args: ["--background-index", "--clang-tidy"],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "svelte",
|
|
65
|
+
extensions: [".svelte"],
|
|
66
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
67
|
+
command: ["svelteserver", "svelte-language-server"],
|
|
68
|
+
args: ["--stdio"],
|
|
69
|
+
initialization: () => ({}),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "astro",
|
|
73
|
+
extensions: [".astro"],
|
|
74
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
75
|
+
command: ["astro-ls", "@astrojs/language-server"],
|
|
76
|
+
args: ["--stdio"],
|
|
77
|
+
initialization: ({ root, workspaceRoot }) => {
|
|
78
|
+
const tsdk = resolveTypeScriptSdk({ root, workspaceRoot });
|
|
79
|
+
return tsdk ? { typescript: { tsdk } } : null;
|
|
80
|
+
},
|
|
81
|
+
missingInitialization: "missing project typescript SDK",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "json",
|
|
85
|
+
extensions: [".json", ".jsonc"],
|
|
86
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
87
|
+
command: ["vscode-json-language-server"],
|
|
88
|
+
managedCommand: "vscode-json-language-server",
|
|
89
|
+
args: ["--stdio"],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "html",
|
|
93
|
+
extensions: [".html", ".htm"],
|
|
94
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
95
|
+
command: ["vscode-html-language-server"],
|
|
96
|
+
managedCommand: "vscode-html-language-server",
|
|
97
|
+
args: ["--stdio"],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "css",
|
|
101
|
+
extensions: [".css", ".scss", ".sass", ".less"],
|
|
102
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
103
|
+
command: ["vscode-css-language-server"],
|
|
104
|
+
managedCommand: "vscode-css-language-server",
|
|
105
|
+
args: ["--stdio"],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "yaml",
|
|
109
|
+
extensions: [".yaml", ".yml"],
|
|
110
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
111
|
+
command: ["yaml-language-server"],
|
|
112
|
+
args: ["--stdio"],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "bash",
|
|
116
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
117
|
+
rootMarkers: [],
|
|
118
|
+
command: ["bash-language-server"],
|
|
119
|
+
args: ["start"],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "lua",
|
|
123
|
+
extensions: [".lua"],
|
|
124
|
+
rootMarkers: [".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml"],
|
|
125
|
+
command: ["lua-language-server"],
|
|
126
|
+
args: [],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "zig",
|
|
130
|
+
extensions: [".zig", ".zon"],
|
|
131
|
+
rootMarkers: ["build.zig"],
|
|
132
|
+
command: ["zls"],
|
|
133
|
+
args: [],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "dart",
|
|
137
|
+
extensions: [".dart"],
|
|
138
|
+
rootMarkers: ["pubspec.yaml", "analysis_options.yaml"],
|
|
139
|
+
command: ["dart"],
|
|
140
|
+
args: ["language-server", "--lsp"],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "php",
|
|
144
|
+
extensions: [".php"],
|
|
145
|
+
rootMarkers: ["composer.json", "composer.lock", ".php-version"],
|
|
146
|
+
command: ["intelephense"],
|
|
147
|
+
args: ["--stdio"],
|
|
148
|
+
initialization: () => ({ telemetry: { enabled: false } }),
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "dockerfile",
|
|
152
|
+
extensions: [".dockerfile"],
|
|
153
|
+
filenames: ["dockerfile", "containerfile"],
|
|
154
|
+
rootMarkers: ["Dockerfile", "docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"],
|
|
155
|
+
command: ["docker-langserver"],
|
|
156
|
+
managedCommand: "docker-langserver",
|
|
157
|
+
args: ["--stdio"],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: "markdown",
|
|
161
|
+
extensions: [".md", ".mdx", ".markdown"],
|
|
162
|
+
rootMarkers: NODE_ROOT_MARKERS,
|
|
163
|
+
command: ["marksman"],
|
|
164
|
+
args: ["server"],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "toml",
|
|
168
|
+
extensions: [".toml"],
|
|
169
|
+
rootMarkers: ["Cargo.toml", "pyproject.toml", "taplo.toml", ".taplo.toml"],
|
|
170
|
+
command: ["taplo"],
|
|
171
|
+
args: ["lsp", "stdio"],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "terraform",
|
|
175
|
+
extensions: [".tf", ".tfvars"],
|
|
176
|
+
rootMarkers: [".terraform", ".terraform.lock.hcl", "terraform.tfstate"],
|
|
177
|
+
command: ["terraform-ls"],
|
|
178
|
+
args: ["serve"],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: "prisma",
|
|
182
|
+
extensions: [".prisma"],
|
|
183
|
+
rootMarkers: ["schema.prisma", "prisma/schema.prisma", "prisma"],
|
|
184
|
+
command: ["prisma"],
|
|
185
|
+
args: ["language-server"],
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
}
|
package/src/lsp/servers.mjs
CHANGED
|
@@ -1,140 +1,11 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { dirname, join, resolve } from "node:path";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { ensureManagedNodeCommand, ensureManagedTypeScript, findManagedTypeScriptSdk, findManagedTypeScriptServer } from "./managed-node-server.mjs";
|
|
5
|
+
import { createLspServerDefinitions } from "./server-definitions.mjs";
|
|
5
6
|
import { resolveTypeScriptProjectRoot } from "./typescript-project-resolver.mjs";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const LSP_SERVERS = [
|
|
10
|
-
{
|
|
11
|
-
id: "vue",
|
|
12
|
-
extensions: [".vue"],
|
|
13
|
-
rootMarkers: NODE_ROOT_MARKERS,
|
|
14
|
-
command: ["vue-language-server"],
|
|
15
|
-
managedCommand: "vue-language-server",
|
|
16
|
-
args: ["--stdio"],
|
|
17
|
-
initialization: ({ root, workspaceRoot }) => {
|
|
18
|
-
const tsdk = resolveTypeScriptSdk({ root, workspaceRoot });
|
|
19
|
-
return tsdk ? { typescript: { tsdk } } : null;
|
|
20
|
-
},
|
|
21
|
-
managedTypeScript: true,
|
|
22
|
-
missingInitialization: "missing project typescript SDK",
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
id: "typescript",
|
|
26
|
-
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
27
|
-
rootMarkers: NODE_ROOT_MARKERS,
|
|
28
|
-
projectRoot: resolveTypeScriptProjectRoot,
|
|
29
|
-
command: ["typescript-language-server"],
|
|
30
|
-
managedCommand: "typescript-language-server",
|
|
31
|
-
args: ["--stdio"],
|
|
32
|
-
initialization: ({ root, workspaceRoot }) => {
|
|
33
|
-
const tsserver = resolveTypeScriptServer({ root, workspaceRoot });
|
|
34
|
-
return tsserver ? { tsserver: { path: tsserver } } : null;
|
|
35
|
-
},
|
|
36
|
-
managedTypeScript: true,
|
|
37
|
-
missingInitialization: "missing project typescript/tsserver.js",
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
id: "python",
|
|
41
|
-
extensions: [".py", ".pyi"],
|
|
42
|
-
rootMarkers: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json"],
|
|
43
|
-
command: ["pyright-langserver"],
|
|
44
|
-
args: ["--stdio"],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "go",
|
|
48
|
-
extensions: [".go"],
|
|
49
|
-
rootMarkers: ["go.work", "go.mod", "go.sum"],
|
|
50
|
-
command: ["gopls"],
|
|
51
|
-
args: [],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: "rust",
|
|
55
|
-
extensions: [".rs"],
|
|
56
|
-
rootMarkers: ["Cargo.toml", "Cargo.lock"],
|
|
57
|
-
command: ["rust-analyzer"],
|
|
58
|
-
args: [],
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
id: "clangd",
|
|
62
|
-
extensions: [".c", ".cpp", ".cc", ".cxx", ".c++", ".h", ".hpp", ".hh", ".hxx", ".h++"],
|
|
63
|
-
rootMarkers: ["compile_commands.json", "compile_flags.txt", ".clangd"],
|
|
64
|
-
command: ["clangd"],
|
|
65
|
-
args: ["--background-index", "--clang-tidy"],
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
id: "svelte",
|
|
69
|
-
extensions: [".svelte"],
|
|
70
|
-
rootMarkers: NODE_ROOT_MARKERS,
|
|
71
|
-
command: ["svelteserver", "svelte-language-server"],
|
|
72
|
-
args: ["--stdio"],
|
|
73
|
-
initialization: () => ({}),
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: "astro",
|
|
77
|
-
extensions: [".astro"],
|
|
78
|
-
rootMarkers: NODE_ROOT_MARKERS,
|
|
79
|
-
command: ["astro-ls", "@astrojs/language-server"],
|
|
80
|
-
args: ["--stdio"],
|
|
81
|
-
initialization: ({ root, workspaceRoot }) => {
|
|
82
|
-
const tsdk = resolveTypeScriptSdk({ root, workspaceRoot });
|
|
83
|
-
return tsdk ? { typescript: { tsdk } } : null;
|
|
84
|
-
},
|
|
85
|
-
missingInitialization: "missing project typescript SDK",
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: "yaml",
|
|
89
|
-
extensions: [".yaml", ".yml"],
|
|
90
|
-
rootMarkers: NODE_ROOT_MARKERS,
|
|
91
|
-
command: ["yaml-language-server"],
|
|
92
|
-
args: ["--stdio"],
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: "bash",
|
|
96
|
-
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
97
|
-
rootMarkers: [],
|
|
98
|
-
command: ["bash-language-server"],
|
|
99
|
-
args: ["start"],
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: "lua",
|
|
103
|
-
extensions: [".lua"],
|
|
104
|
-
rootMarkers: [".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml"],
|
|
105
|
-
command: ["lua-language-server"],
|
|
106
|
-
args: [],
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: "zig",
|
|
110
|
-
extensions: [".zig", ".zon"],
|
|
111
|
-
rootMarkers: ["build.zig"],
|
|
112
|
-
command: ["zls"],
|
|
113
|
-
args: [],
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
id: "dart",
|
|
117
|
-
extensions: [".dart"],
|
|
118
|
-
rootMarkers: ["pubspec.yaml", "analysis_options.yaml"],
|
|
119
|
-
command: ["dart"],
|
|
120
|
-
args: ["language-server", "--lsp"],
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
id: "php",
|
|
124
|
-
extensions: [".php"],
|
|
125
|
-
rootMarkers: ["composer.json", "composer.lock", ".php-version"],
|
|
126
|
-
command: ["intelephense"],
|
|
127
|
-
args: ["--stdio"],
|
|
128
|
-
initialization: () => ({ telemetry: { enabled: false } }),
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: "prisma",
|
|
132
|
-
extensions: [".prisma"],
|
|
133
|
-
rootMarkers: ["schema.prisma", "prisma/schema.prisma", "prisma"],
|
|
134
|
-
command: ["prisma"],
|
|
135
|
-
args: ["language-server"],
|
|
136
|
-
},
|
|
137
|
-
];
|
|
8
|
+
const LSP_SERVERS = createLspServerDefinitions({ resolveTypeScriptProjectRoot, resolveTypeScriptSdk, resolveTypeScriptServer });
|
|
138
9
|
|
|
139
10
|
export async function resolveLspServer({ filePath, workspaceRoot, onEvent = null } = {}) {
|
|
140
11
|
const result = await resolveLspServerStatus({ filePath, workspaceRoot, onEvent });
|
|
@@ -143,7 +14,7 @@ export async function resolveLspServer({ filePath, workspaceRoot, onEvent = null
|
|
|
143
14
|
|
|
144
15
|
export async function resolveLspServerStatus({ filePath, workspaceRoot, onEvent = null } = {}) {
|
|
145
16
|
const ext = extensionOf(filePath);
|
|
146
|
-
const def = LSP_SERVERS.find((server) => server
|
|
17
|
+
const def = LSP_SERVERS.find((server) => matchesServer(server, filePath, ext));
|
|
147
18
|
if (!def) return { status: "unsupported", extension: ext };
|
|
148
19
|
|
|
149
20
|
const root = resolveServerRoot(def, { filePath, workspaceRoot });
|
|
@@ -165,7 +36,13 @@ export async function resolveLspServerStatus({ filePath, workspaceRoot, onEvent
|
|
|
165
36
|
}
|
|
166
37
|
|
|
167
38
|
export function listLspServerDefinitions() {
|
|
168
|
-
return LSP_SERVERS.map(({ id, extensions, rootMarkers, command, args }) => ({ id, extensions, rootMarkers, command, args }));
|
|
39
|
+
return LSP_SERVERS.map(({ id, extensions, filenames, rootMarkers, command, args }) => ({ id, extensions, filenames, rootMarkers, command, args }));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function matchesServer(server, filePath, ext) {
|
|
43
|
+
if (server.extensions.includes(ext)) return true;
|
|
44
|
+
const name = basename(filePath).toLowerCase();
|
|
45
|
+
return server.filenames?.includes(name) ?? false;
|
|
169
46
|
}
|
|
170
47
|
|
|
171
48
|
async function resolveCommand(def, { root, workspaceRoot, onEvent }) {
|
package/src/main.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import { createWebToolsFromConfig } from "./web/tools.mjs";
|
|
|
30
30
|
import { createModelContextDumper } from "./debug/model-context-dumper.mjs";
|
|
31
31
|
import { createLogger, installProcessLogHandlers } from "./debug/logger.mjs";
|
|
32
32
|
import { defaultProfilePaths, ensureProfileFiles } from "./context/profiles.mjs";
|
|
33
|
-
import {
|
|
33
|
+
import { runProviderCommand } from "./provider/command.mjs";
|
|
34
34
|
import { runWebSearchConfigCommand } from "./web/config-command.mjs";
|
|
35
35
|
import { createDesktopTurnNotifier } from "./notification/desktop-notifier.mjs";
|
|
36
36
|
import { registerSuperGrokOAuthProvider } from "./supergrok/oauth-provider.mjs";
|
|
@@ -60,11 +60,12 @@ export async function run(argv) {
|
|
|
60
60
|
return 1;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
if (args.command?.name === "provider") {
|
|
64
|
+
return await runProviderCommand(args);
|
|
65
|
+
}
|
|
66
|
+
if (args.command?.name === "websearch") {
|
|
67
|
+
if (args.providerConfig) return await runWebSearchConfigCommand({ homeDir: homedir() });
|
|
68
|
+
process.stderr.write("Usage: march websearch --config\n");
|
|
68
69
|
return 1;
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { globalConfigJsonPath, readConfigJson, upsertSharedProviderProfile } from "../config/config-json.mjs";
|
|
2
|
+
import { selectWithKeyboard } from "../cli/input/select-with-keyboard.mjs";
|
|
3
|
+
import { hasApiKey, parseProviderShareToken } from "./share-payload.mjs";
|
|
4
|
+
|
|
5
|
+
export async function runProviderAcceptCommand({
|
|
6
|
+
homeDir,
|
|
7
|
+
token,
|
|
8
|
+
input = process.stdin,
|
|
9
|
+
output = process.stdout,
|
|
10
|
+
select = selectWithKeyboard,
|
|
11
|
+
} = {}) {
|
|
12
|
+
if (!token) {
|
|
13
|
+
output.write("Usage: march provider accept <march-provider-v1-token>\n");
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let payload;
|
|
18
|
+
try {
|
|
19
|
+
payload = parseProviderShareToken(token);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
output.write(`Invalid provider share token: ${error.message}\n`);
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
output.write(formatImportPreview(payload));
|
|
26
|
+
const path = globalConfigJsonPath(homeDir);
|
|
27
|
+
const config = readConfigJson(path);
|
|
28
|
+
const providers = config.providers && typeof config.providers === "object" && !Array.isArray(config.providers) ? config.providers : {};
|
|
29
|
+
if (providers[payload.providerId]) {
|
|
30
|
+
const action = await select({
|
|
31
|
+
input,
|
|
32
|
+
output,
|
|
33
|
+
message: `Provider "${payload.providerId}" already exists`,
|
|
34
|
+
items: [
|
|
35
|
+
{ label: "Overwrite existing provider", value: "overwrite" },
|
|
36
|
+
{ label: "Cancel", value: "cancel" },
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
if (action !== "overwrite") {
|
|
40
|
+
output.write("Provider import cancelled.\n");
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
const action = await select({
|
|
45
|
+
input,
|
|
46
|
+
output,
|
|
47
|
+
message: "Import provider?",
|
|
48
|
+
items: [
|
|
49
|
+
{ label: "Import", value: "import" },
|
|
50
|
+
{ label: "Cancel", value: "cancel" },
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
if (action !== "import") {
|
|
54
|
+
output.write("Provider import cancelled.\n");
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
upsertSharedProviderProfile({ path, id: payload.providerId, provider: payload.provider });
|
|
60
|
+
output.write(`Imported provider: ${payload.providerId}\n`);
|
|
61
|
+
output.write(`Config: ${path}\n`);
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatImportPreview(payload) {
|
|
66
|
+
const provider = payload.provider;
|
|
67
|
+
const models = Array.isArray(provider.models) ? provider.models : [];
|
|
68
|
+
const lines = [
|
|
69
|
+
"Provider to import:",
|
|
70
|
+
` Id: ${payload.providerId}`,
|
|
71
|
+
` Name: ${typeof provider.name === "string" && provider.name ? provider.name : "-"}`,
|
|
72
|
+
` Type: ${provider.type}`,
|
|
73
|
+
];
|
|
74
|
+
if (typeof provider.baseUrl === "string" && provider.baseUrl) lines.push(` Base URL: ${provider.baseUrl}`);
|
|
75
|
+
if (typeof provider.api === "string" && provider.api) lines.push(` API: ${provider.api}`);
|
|
76
|
+
if (models.length) {
|
|
77
|
+
lines.push(` Models: ${models.length}`);
|
|
78
|
+
for (const model of models.slice(0, 5)) lines.push(` - ${formatModel(model)}`);
|
|
79
|
+
if (models.length > 5) lines.push(` ... ${models.length - 5} more`);
|
|
80
|
+
}
|
|
81
|
+
lines.push(` API key: ${hasApiKey(provider) ? "included" : "not included"}`);
|
|
82
|
+
lines.push("");
|
|
83
|
+
return `${lines.join("\n")}\n`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatModel(model) {
|
|
87
|
+
if (model && typeof model === "object" && !Array.isArray(model)) return model.name || model.id || "<unnamed>";
|
|
88
|
+
return String(model);
|
|
89
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { runProviderConfigCommand } from "./config-command.mjs";
|
|
3
|
+
import { runProviderShareCommand } from "./share-command.mjs";
|
|
4
|
+
import { runProviderAcceptCommand } from "./accept-command.mjs";
|
|
5
|
+
|
|
6
|
+
export async function runProviderCommand(args, { homeDir = homedir(), stderr = process.stderr } = {}) {
|
|
7
|
+
if (args.providerConfig) return await runProviderConfigCommand({ homeDir });
|
|
8
|
+
if (args.command.args[0] === "share") {
|
|
9
|
+
return await runProviderShareCommand({
|
|
10
|
+
homeDir,
|
|
11
|
+
providerId: args.command.args[1],
|
|
12
|
+
includeKey: args.includeKey,
|
|
13
|
+
profileOnly: args.profileOnly,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (args.command.args[0] === "accept") {
|
|
17
|
+
return await runProviderAcceptCommand({ homeDir, token: args.command.args[1] });
|
|
18
|
+
}
|
|
19
|
+
stderr.write("Usage: march provider --config | march provider share [id] | march provider accept <token>\n");
|
|
20
|
+
return 1;
|
|
21
|
+
}
|