toolfactory 0.1.1 → 0.2.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 +31 -3
- package/dist/bindings/python.js +1 -1
- package/dist/commands.d.ts +1 -1
- package/dist/distribution/native.d.ts +50 -0
- package/dist/distribution/native.js +414 -0
- package/dist/distribution/native.test.d.ts +1 -0
- package/dist/distribution/native.test.js +127 -0
- package/dist/distribution/system.d.ts +57 -0
- package/dist/distribution/system.js +637 -0
- package/dist/distribution/system.test.d.ts +1 -0
- package/dist/distribution/system.test.js +116 -0
- package/dist/e2e.test.js +7 -1
- package/dist/hosts/openclaw.js +6 -2
- package/dist/hosts/vscode.d.ts +1 -0
- package/dist/hosts/vscode.js +67 -0
- package/dist/model.d.ts +63 -2
- package/dist/model.js +18 -1
- package/dist/ops.d.ts +52 -0
- package/dist/project/apply.d.ts +1 -1
- package/dist/project/apply.js +194 -17
- package/dist/project/apply.test.js +103 -0
- package/dist/project/gate.d.ts +17 -2
- package/dist/project/gate.js +102 -23
- package/dist/project/history.d.ts +30 -1
- package/dist/project/lock.d.ts +5 -0
- package/dist/project/lock.js +9 -0
- package/dist/surfaces/agent-plugins.js +5 -4
- package/dist/surfaces/agents.js +27 -3
- package/dist/surfaces/agents.test.js +12 -0
- package/dist/surfaces/claude.js +8 -3
- package/dist/surfaces/codex.js +9 -4
- package/dist/surfaces/codex.test.js +16 -6
- package/dist/surfaces/cursor.js +2 -2
- package/dist/surfaces/distribution.test.js +121 -7
- package/dist/surfaces/dsh.js +29 -7
- package/dist/surfaces/dsh.test.js +11 -6
- package/dist/surfaces/gemini.js +2 -2
- package/dist/surfaces/gemini.test.js +4 -4
- package/dist/surfaces/hermes-native.js +43 -12
- package/dist/surfaces/hermes-native.test.js +53 -11
- package/dist/surfaces/mcp-registry.js +59 -7
- package/dist/surfaces/mcp-registry.test.js +23 -0
- package/dist/surfaces/mcpb.d.ts +1 -0
- package/dist/surfaces/mcpb.js +36 -23
- package/dist/surfaces/mcpb.test.js +31 -3
- package/dist/surfaces/npm.js +45 -14
- package/dist/surfaces/openclaw-native.js +10 -8
- package/dist/surfaces/openclaw-native.test.js +30 -0
- package/dist/surfaces/pypi.js +27 -3
- package/dist/surfaces/readme.d.ts +16 -0
- package/dist/surfaces/readme.js +17 -7
- package/dist/surfaces/registry.js +9 -3
- package/dist/surfaces/vscode-extension.d.ts +14 -0
- package/dist/surfaces/vscode-extension.js +312 -0
- package/dist/surfaces/vscode-extension.test.d.ts +1 -0
- package/dist/surfaces/vscode-extension.test.js +81 -0
- package/dist/surfaces/vscode-scaffold.d.ts +43 -0
- package/dist/surfaces/vscode-scaffold.js +43 -0
- package/dist/surfaces/workflows.js +57 -11
- package/dist/surfaces/workflows.test.js +71 -3
- package/dist/toolfactory/cli.js +1 -1
- package/dist/toolfactory/mcp.js +1 -1
- package/package.json +2 -1
- package/src/bindings/python.ts +1 -1
- package/src/distribution/native.test.ts +161 -0
- package/src/distribution/native.ts +460 -0
- package/src/distribution/system.test.ts +136 -0
- package/src/distribution/system.ts +688 -0
- package/src/e2e.test.ts +7 -1
- package/src/hosts/openclaw.ts +4 -2
- package/src/hosts/vscode.ts +79 -0
- package/src/model.ts +26 -2
- package/src/project/apply.test.ts +120 -0
- package/src/project/apply.ts +257 -15
- package/src/project/gate.ts +131 -24
- package/src/project/lock.ts +12 -0
- package/src/surfaces/agent-plugins.ts +5 -4
- package/src/surfaces/agents.test.ts +16 -0
- package/src/surfaces/agents.ts +38 -3
- package/src/surfaces/claude.ts +7 -3
- package/src/surfaces/codex.test.ts +15 -5
- package/src/surfaces/codex.ts +12 -6
- package/src/surfaces/cursor.ts +2 -10
- package/src/surfaces/distribution.test.ts +141 -6
- package/src/surfaces/dsh.test.ts +11 -5
- package/src/surfaces/dsh.ts +30 -7
- package/src/surfaces/gemini.test.ts +3 -3
- package/src/surfaces/gemini.ts +1 -2
- package/src/surfaces/hermes-native.test.ts +61 -10
- package/src/surfaces/hermes-native.ts +46 -13
- package/src/surfaces/mcp-registry.test.ts +28 -0
- package/src/surfaces/mcp-registry.ts +66 -8
- package/src/surfaces/mcpb.test.ts +32 -5
- package/src/surfaces/mcpb.ts +44 -23
- package/src/surfaces/npm.ts +47 -15
- package/src/surfaces/openclaw-native.test.ts +37 -0
- package/src/surfaces/openclaw-native.ts +49 -47
- package/src/surfaces/pypi.ts +27 -3
- package/src/surfaces/readme.ts +21 -6
- package/src/surfaces/registry.ts +9 -3
- package/src/surfaces/vscode-extension.test.ts +89 -0
- package/src/surfaces/vscode-extension.ts +345 -0
- package/src/surfaces/vscode-scaffold.ts +55 -0
- package/src/surfaces/workflows.test.ts +86 -3
- package/src/surfaces/workflows.ts +57 -11
- package/src/toolfactory/cli.ts +1 -1
- package/src/toolfactory/mcp.ts +1 -1
- package/web/dist/assets/{index-CCXhQKK3.css → index-BuT2czb2.css} +1 -1
- package/web/dist/assets/{index-D1PwZ3qS.js → index-CVm6m4s0.js} +1 -1
- package/web/dist/index.html +2 -2
package/src/surfaces/readme.ts
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* `cursor.com/en/install-mcp`), both computed from the exact same `{command, args, env}` launch
|
|
15
15
|
* the plain-text line renders — a badge can never point somewhere the text line does not.
|
|
16
16
|
*/
|
|
17
|
+
|
|
18
|
+
import { nativeInstallLines } from "../distribution/native.js";
|
|
19
|
+
import { systemInstallLines } from "../distribution/system.js";
|
|
17
20
|
import { githubSlug } from "../hosts/github.js";
|
|
18
21
|
import { projectName } from "../identity/name.js";
|
|
19
22
|
import type { Project, Surface } from "../model.js";
|
|
@@ -22,6 +25,7 @@ import { HOST_DIR as DSH_HOST_DIR, dshTarball } from "./dsh.js";
|
|
|
22
25
|
import { pluginDir as hermesPluginDir } from "./hermes-native.js";
|
|
23
26
|
import { HOST_DIR as OPENCLAW_HOST_DIR } from "./openclaw-native.js";
|
|
24
27
|
import { envName, has, npmName, pypiName, requiredConfig } from "./shared.js";
|
|
28
|
+
import { vsixName } from "./vscode-extension.js";
|
|
25
29
|
|
|
26
30
|
export const README_PATH = "README.md";
|
|
27
31
|
export const INSTALL_BEGIN = "<!-- tf:install -->";
|
|
@@ -83,6 +87,12 @@ function installLines(project: Project): string[] {
|
|
|
83
87
|
const { identity } = project;
|
|
84
88
|
const slug = githubSlug(identity.repository);
|
|
85
89
|
const lines: string[] = [];
|
|
90
|
+
lines.push(...nativeInstallLines(project).map((line) => `- **Native package** — ${line}`));
|
|
91
|
+
lines.push(...systemInstallLines(project).map((line) => `- **System package** — ${line}`));
|
|
92
|
+
if (has(project, "vscode-extension"))
|
|
93
|
+
lines.push(
|
|
94
|
+
`- **VS Code extension** — download \`${vsixName(project)}\` from the release, then \`code --install-extension ${vsixName(project)}\``,
|
|
95
|
+
);
|
|
86
96
|
if (has(project, "skill") && slug) {
|
|
87
97
|
lines.push(`- **Agent Skill** — \`npx skills add ${slug}\``);
|
|
88
98
|
}
|
|
@@ -142,8 +152,8 @@ function installLines(project: Project): string[] {
|
|
|
142
152
|
}
|
|
143
153
|
if (has(project, "browser-extension")) {
|
|
144
154
|
// Three channels, in the order a reader needs them: build-and-load from a checkout, the
|
|
145
|
-
// release assets (
|
|
146
|
-
//
|
|
155
|
+
// release assets (the signed Firefox XPI depends on configured signing credentials), and
|
|
156
|
+
// store listings. Pairing is the last step of each.
|
|
147
157
|
const zips = (["chrome", "firefox", "edge"] as const).map(
|
|
148
158
|
(browser) => `\`${zipName(project, browser)}\``,
|
|
149
159
|
);
|
|
@@ -152,9 +162,8 @@ function installLines(project: Project): string[] {
|
|
|
152
162
|
`- **Browser extension** — from a checkout: \`npm --prefix ${BROWSER_HOST_DIR} install && npm --prefix ${BROWSER_HOST_DIR} exec --no -- wxt build\`,`,
|
|
153
163
|
` then \`chrome://extensions\` → developer mode → Load unpacked → \`${BROWSER_HOST_DIR}/.output/chrome-mv3\``,
|
|
154
164
|
` (Firefox: \`npm --prefix ${BROWSER_HOST_DIR} exec --no -- web-ext run\`). Each GitHub Release attaches the`,
|
|
155
|
-
` store uploads ${zips.join(", ")},
|
|
156
|
-
"
|
|
157
|
-
" extensions; the Chrome Web Store, Firefox Add-ons and Edge Add-ons listings appear once the release's",
|
|
165
|
+
` store uploads ${zips.join(", ")}. When Firefox signing credentials are configured, it also attaches a`,
|
|
166
|
+
" Mozilla-signed `.xpi`; the Chrome Web Store, Firefox Add-ons and Edge Add-ons listings appear once the release's",
|
|
158
167
|
` submit step has each store's credentials. Then pair it: \`${mcpCommand(project)} --http --pair\``,
|
|
159
168
|
" prints the `<url>#<token>` the extension's options page accepts.",
|
|
160
169
|
].join("\n"),
|
|
@@ -169,7 +178,13 @@ function installLines(project: Project): string[] {
|
|
|
169
178
|
" returns the URL.",
|
|
170
179
|
);
|
|
171
180
|
}
|
|
172
|
-
if (has(project, "npm"))
|
|
181
|
+
if (has(project, "npm")) {
|
|
182
|
+
const pythonBridge =
|
|
183
|
+
project.tool.binding === "python"
|
|
184
|
+
? `; requires \`uv\`, and \`${identity.name}\` delegates to \`uvx --from ${pypiName(project)}==${identity.version} ${identity.name}\``
|
|
185
|
+
: "";
|
|
186
|
+
lines.push(`- **npm package** — \`npm install ${npmName(project)}\`${pythonBridge}`);
|
|
187
|
+
}
|
|
173
188
|
if (has(project, "pypi")) lines.push(`- **PyPI package** — \`uv add ${pypiName(project)}\``);
|
|
174
189
|
return lines;
|
|
175
190
|
}
|
package/src/surfaces/registry.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { nativePackageSurfaces } from "../distribution/native.js";
|
|
2
|
+
import { systemPackageSurfaces } from "../distribution/system.js";
|
|
1
3
|
import type { Surface, SurfaceId } from "../model.js";
|
|
2
4
|
import { surface as agentPlugins } from "./agent-plugins.js";
|
|
3
5
|
import { surface as agents } from "./agents.js";
|
|
@@ -17,10 +19,13 @@ import { surface as openclawNative } from "./openclaw-native.js";
|
|
|
17
19
|
import { surface as pypi } from "./pypi.js";
|
|
18
20
|
import { surface as readme } from "./readme.js";
|
|
19
21
|
import { surface as skill } from "./skill.js";
|
|
22
|
+
import { surface as vscodeExtension } from "./vscode-extension.js";
|
|
20
23
|
import { surface as web } from "./web.js";
|
|
21
24
|
import { surface as workflows } from "./workflows.js";
|
|
22
25
|
|
|
23
26
|
const registry: Partial<Record<SurfaceId, Surface>> = {
|
|
27
|
+
...Object.fromEntries(nativePackageSurfaces.map((surface) => [surface.id, surface])),
|
|
28
|
+
...Object.fromEntries(systemPackageSurfaces.map((surface) => [surface.id, surface])),
|
|
24
29
|
"agent-plugins": agentPlugins,
|
|
25
30
|
agents,
|
|
26
31
|
"browser-extension": { ...browserExtension, requires: ["mcp"] },
|
|
@@ -38,9 +43,9 @@ const registry: Partial<Record<SurfaceId, Surface>> = {
|
|
|
38
43
|
cli,
|
|
39
44
|
mcp,
|
|
40
45
|
"mcp-registry": mcpRegistry,
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
mcpb: { ...mcpb, requires: ["mcp"
|
|
46
|
+
// TypeScript bundles stage their npm tarball; Python `uv` bundles stage the PyPI project's
|
|
47
|
+
// source. The projector enforces that binding-specific distribution dependency.
|
|
48
|
+
mcpb: { ...mcpb, requires: ["mcp"] },
|
|
44
49
|
npm,
|
|
45
50
|
skill,
|
|
46
51
|
"hermes-native": hermesNative,
|
|
@@ -48,6 +53,7 @@ const registry: Partial<Record<SurfaceId, Surface>> = {
|
|
|
48
53
|
pypi,
|
|
49
54
|
readme,
|
|
50
55
|
web: { ...web, requires: ["mcp"] },
|
|
56
|
+
"vscode-extension": { ...vscodeExtension, requires: ["mcp", "cli"] },
|
|
51
57
|
workflows,
|
|
52
58
|
};
|
|
53
59
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, expect, it } from "vitest";
|
|
5
|
+
import { type Project, ToolConfigSchema } from "../model.js";
|
|
6
|
+
import { apply, check } from "../project/apply.js";
|
|
7
|
+
import { packageSteps, registries } from "../project/gate.js";
|
|
8
|
+
import { surface } from "./vscode-extension.js";
|
|
9
|
+
|
|
10
|
+
const roots: string[] = [];
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function fixture(): Project {
|
|
16
|
+
const root = mkdtempSync(join(tmpdir(), "tf-vscode-"));
|
|
17
|
+
roots.push(root);
|
|
18
|
+
return {
|
|
19
|
+
root,
|
|
20
|
+
tool: ToolConfigSchema.parse({
|
|
21
|
+
schemaVersion: 1,
|
|
22
|
+
identity: "plugin.json",
|
|
23
|
+
binding: "typescript",
|
|
24
|
+
surfaces: ["mcp", "cli", "npm", "vscode-extension"],
|
|
25
|
+
vscode: { publisher: "octo" },
|
|
26
|
+
}),
|
|
27
|
+
identity: { name: "hello.tool", version: "1.0.0", repository: "https://github.com/octo/hello" },
|
|
28
|
+
identityExtra: {},
|
|
29
|
+
operations: [
|
|
30
|
+
{ name: "echo", inputSchema: { type: "object" }, requires: [] },
|
|
31
|
+
{ name: "native", inputSchema: { type: "object" }, requires: ["model"] },
|
|
32
|
+
],
|
|
33
|
+
toolfactoryVersion: "0.1.2",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it("keeps native contributions, activation, build settings and command details across operation changes", () => {
|
|
38
|
+
const project = fixture();
|
|
39
|
+
apply(project.root, surface.plan(project), "0.1.2");
|
|
40
|
+
const path = join(project.root, "hosts/vscode/package.json");
|
|
41
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
42
|
+
pkg.contributes.commands[0].icon = "$(beaker)";
|
|
43
|
+
pkg.contributes.commands.push({ command: "hello.tool.custom", title: "Custom native command" });
|
|
44
|
+
pkg.contributes.views = { explorer: [{ id: "native-view", name: "Native view" }] };
|
|
45
|
+
pkg.scripts.custom = "node custom.js";
|
|
46
|
+
writeFileSync(path, JSON.stringify(pkg));
|
|
47
|
+
const entry = join(project.root, "hosts/vscode/src/extension.ts");
|
|
48
|
+
writeFileSync(entry, `${readFileSync(entry, "utf8")}\nexport const authored = true;\n`);
|
|
49
|
+
expect(check(project.root, surface.plan(project), "0.1.2")).toEqual([]);
|
|
50
|
+
const echo = project.operations[0];
|
|
51
|
+
if (!echo) throw new Error("fixture must contain echo");
|
|
52
|
+
echo.title = "New title";
|
|
53
|
+
apply(project.root, surface.plan(project), "0.1.2");
|
|
54
|
+
const rebuilt = JSON.parse(readFileSync(path, "utf8"));
|
|
55
|
+
expect(rebuilt.contributes.commands).toContainEqual({
|
|
56
|
+
command: "hello-tool.echo",
|
|
57
|
+
title: "New title",
|
|
58
|
+
category: "Hello Tool",
|
|
59
|
+
icon: "$(beaker)",
|
|
60
|
+
});
|
|
61
|
+
expect(rebuilt.contributes.commands).toContainEqual({
|
|
62
|
+
command: "hello.tool.custom",
|
|
63
|
+
title: "Custom native command",
|
|
64
|
+
});
|
|
65
|
+
expect(rebuilt.contributes.commands).not.toContainEqual(
|
|
66
|
+
expect.objectContaining({ command: "hello-tool.native" }),
|
|
67
|
+
);
|
|
68
|
+
expect(rebuilt.contributes.views).toEqual(pkg.contributes.views);
|
|
69
|
+
expect(rebuilt.scripts.custom).toBe("node custom.js");
|
|
70
|
+
expect(readFileSync(entry, "utf8")).toContain("export const authored = true;");
|
|
71
|
+
expect(check(project.root, surface.plan(project), "0.1.2")).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("packages a VSIX independently of registry credentials and permits each registry to be deselected", () => {
|
|
75
|
+
const project = fixture();
|
|
76
|
+
expect(packageSteps(project).some((step) => step.run.includes("hello-tool-1.0.0.vsix"))).toBe(
|
|
77
|
+
true,
|
|
78
|
+
);
|
|
79
|
+
expect(registries(project).map((row) => row.id)).toEqual(
|
|
80
|
+
expect.arrayContaining(["vscode-marketplace", "open-vsx"]),
|
|
81
|
+
);
|
|
82
|
+
project.tool.vscode = { publisher: "octo", marketplace: false, openvsx: true };
|
|
83
|
+
expect(registries(project).map((row) => row.id)).not.toContain("vscode-marketplace");
|
|
84
|
+
project.tool.binding = "python";
|
|
85
|
+
expect(() => surface.plan(project)).toThrow("requires pypi");
|
|
86
|
+
project.tool.surfaces = ["mcp", "cli", "pypi", "vscode-extension"];
|
|
87
|
+
const runtime = surface.plan(project).find((file) => file.path.endsWith("/generated.ts"));
|
|
88
|
+
expect(runtime?.kind === "file" && runtime.content).toContain('"command":"uvx"');
|
|
89
|
+
});
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/** A native VS Code extension. The author owns activation and all native contributions. */
|
|
2
|
+
import { execArgv } from "node:process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { githubOwner, projectName } from "../identity/name.js";
|
|
6
|
+
import { isPortable, type Project, type Surface } from "../model.js";
|
|
7
|
+
import {
|
|
8
|
+
configProperties,
|
|
9
|
+
dataDirEnvName,
|
|
10
|
+
envName,
|
|
11
|
+
has,
|
|
12
|
+
isSensitive,
|
|
13
|
+
npmName,
|
|
14
|
+
pypiName,
|
|
15
|
+
rootEnvName,
|
|
16
|
+
} from "./shared.js";
|
|
17
|
+
import {
|
|
18
|
+
ESBUILD,
|
|
19
|
+
ESLINT,
|
|
20
|
+
GITIGNORE,
|
|
21
|
+
IGNORE,
|
|
22
|
+
LAUNCH,
|
|
23
|
+
TASKS,
|
|
24
|
+
VSCODE_SCAFFOLD,
|
|
25
|
+
} from "./vscode-scaffold.js";
|
|
26
|
+
|
|
27
|
+
export const HOST_DIR = "hosts/vscode";
|
|
28
|
+
export const vscodeConfigSchema = z.object({
|
|
29
|
+
publisher: z
|
|
30
|
+
.string()
|
|
31
|
+
.regex(/^[a-zA-Z0-9][a-zA-Z0-9-]*$/)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe(
|
|
34
|
+
"Your registered Visual Studio Marketplace/Open VSX publisher; repository owner is the local packaging default.",
|
|
35
|
+
),
|
|
36
|
+
marketplace: z.boolean().default(true),
|
|
37
|
+
openvsx: z.boolean().default(true),
|
|
38
|
+
});
|
|
39
|
+
export const VSCE_VERSION = "3.9.2";
|
|
40
|
+
export const OVSX_VERSION = "1.1.1";
|
|
41
|
+
|
|
42
|
+
export function extensionName(project: Project): string {
|
|
43
|
+
return project.identity.name.replaceAll(".", "-");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function publisher(project: Project): string {
|
|
47
|
+
return (
|
|
48
|
+
project.tool.vscode?.publisher ??
|
|
49
|
+
githubOwner(project.identity.repository) ??
|
|
50
|
+
extensionName(project)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function vsixName(project: Project): string {
|
|
55
|
+
return `${extensionName(project)}-${project.identity.version ?? "0.0.0"}.vsix`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function operations(project: Project) {
|
|
59
|
+
return project.operations.filter(isPortable);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function runtime(project: Project): string {
|
|
63
|
+
const name = extensionName(project);
|
|
64
|
+
const python = project.tool.binding === "python";
|
|
65
|
+
const packageName = python ? pypiName(project) : npmName(project);
|
|
66
|
+
const version = project.identity.version ?? "0.0.0";
|
|
67
|
+
const defaults = python
|
|
68
|
+
? { command: "uvx", args: ["--from", `${packageName}==${version}`, project.identity.name] }
|
|
69
|
+
: { command: "npx", args: ["--yes", `${packageName}@${version}`] };
|
|
70
|
+
const config = Object.entries(configProperties(project)).map(([key, property]) => ({
|
|
71
|
+
key,
|
|
72
|
+
env: envName(key),
|
|
73
|
+
secret: isSensitive(property),
|
|
74
|
+
}));
|
|
75
|
+
return `// Generated by toolfactory. Native extension code belongs in extension.ts.
|
|
76
|
+
import { mkdir } from 'node:fs/promises';
|
|
77
|
+
import { Client } from '@modelcontextprotocol/client';
|
|
78
|
+
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
|
|
79
|
+
import * as vscode from 'vscode';
|
|
80
|
+
|
|
81
|
+
const namespace = ${JSON.stringify(name)};
|
|
82
|
+
const defaults = ${JSON.stringify(defaults)};
|
|
83
|
+
const config: Array<{key: string; env: string; secret: boolean}> = ${JSON.stringify(config)};
|
|
84
|
+
const operations: Array<{name: string; title: string}> = ${JSON.stringify(operations(project).map((op) => ({ name: op.name, title: op.title ?? op.name })))};
|
|
85
|
+
|
|
86
|
+
function launch(context: vscode.ExtensionContext) {
|
|
87
|
+
const settings = vscode.workspace.getConfiguration(namespace);
|
|
88
|
+
const root = vscode.Uri.joinPath(context.extensionUri, '../..').fsPath;
|
|
89
|
+
const development = context.extensionMode !== vscode.ExtensionMode.Production;
|
|
90
|
+
const local = ${python ? `{ command: 'uv', args: ['run', '--quiet', '--directory', root, 'python', '-m', '${projectName.pythonPackage(project.identity.name)}.toolfactory.cli'] }` : `{ command: 'node', args: ['--import', 'tsx', root + '/src/toolfactory/cli.ts'] }`};
|
|
91
|
+
const selected = development ? local : defaults;
|
|
92
|
+
const command = settings.get<string>('runtime.command');
|
|
93
|
+
return {
|
|
94
|
+
command: command || selected.command,
|
|
95
|
+
args: command ? settings.get<string[]>('runtime.args', []) : selected.args,
|
|
96
|
+
cwd: development ? root : undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function environment(context: vscode.ExtensionContext): Promise<Record<string, string>> {
|
|
101
|
+
const settings = vscode.workspace.getConfiguration(namespace);
|
|
102
|
+
const env: Record<string, string> = {};
|
|
103
|
+
for (const property of config) {
|
|
104
|
+
const value = (property.secret ? await context.secrets.get(property.env) : settings.get('config.' + property.key)) ?? process.env[property.env];
|
|
105
|
+
if (value !== undefined) env[property.env] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
106
|
+
}
|
|
107
|
+
await mkdir(context.globalStorageUri.fsPath, { recursive: true });
|
|
108
|
+
env[${JSON.stringify(dataDirEnvName(project))}] = context.globalStorageUri.fsPath;
|
|
109
|
+
const folder = vscode.workspace.workspaceFolders?.find(folder => folder.uri.scheme === 'file');
|
|
110
|
+
if (folder) env[${JSON.stringify(rootEnvName(project))}] = folder.uri.fsPath;
|
|
111
|
+
return env;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function registerGenerated(context: vscode.ExtensionContext): void {
|
|
115
|
+
const changes = new vscode.EventEmitter<void>();
|
|
116
|
+
context.subscriptions.push(changes,
|
|
117
|
+
vscode.workspace.onDidChangeConfiguration(event => { if (event.affectsConfiguration(namespace)) changes.fire(); }),
|
|
118
|
+
context.secrets.onDidChange(() => changes.fire()),
|
|
119
|
+
vscode.lm.registerMcpServerDefinitionProvider(namespace, {
|
|
120
|
+
onDidChangeMcpServerDefinitions: changes.event,
|
|
121
|
+
provideMcpServerDefinitions() {
|
|
122
|
+
const selected = launch(context);
|
|
123
|
+
const server = new vscode.McpStdioServerDefinition(
|
|
124
|
+
${JSON.stringify(projectName.display(project.identity.name))}, selected.command,
|
|
125
|
+
[...selected.args, 'mcp'], {}, ${JSON.stringify(version)},
|
|
126
|
+
);
|
|
127
|
+
server.cwd = selected.cwd ? vscode.Uri.file(selected.cwd) : undefined;
|
|
128
|
+
return [server];
|
|
129
|
+
},
|
|
130
|
+
async resolveMcpServerDefinition(server) {
|
|
131
|
+
if (!vscode.workspace.isTrusted) throw new Error('Trust this workspace before starting the tool runtime.');
|
|
132
|
+
if (server instanceof vscode.McpStdioServerDefinition) server.env = { ...server.env, ...await environment(context) };
|
|
133
|
+
return server;
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
${
|
|
138
|
+
config.some((property) => property.secret)
|
|
139
|
+
? `context.subscriptions.push(vscode.commands.registerCommand(namespace + '.configureSecrets', async () => {
|
|
140
|
+
const chosen = await vscode.window.showQuickPick(config.filter(property => property.secret).map(property => property.env), { title: 'Configure credentials' });
|
|
141
|
+
if (!chosen) return;
|
|
142
|
+
const value = await vscode.window.showInputBox({ title: chosen, password: true, prompt: 'Stored in VS Code SecretStorage. Leave empty to remove.' });
|
|
143
|
+
if (value === undefined) return;
|
|
144
|
+
if (value) await context.secrets.store(chosen, value); else await context.secrets.delete(chosen);
|
|
145
|
+
}));`
|
|
146
|
+
: ""
|
|
147
|
+
}
|
|
148
|
+
for (const operation of operations) {
|
|
149
|
+
context.subscriptions.push(vscode.commands.registerCommand(namespace + '.' + operation.name, async (args?: Record<string, unknown>) => {
|
|
150
|
+
if (!vscode.workspace.isTrusted) throw new Error('Trust this workspace before running tools.');
|
|
151
|
+
const interactive = args === undefined;
|
|
152
|
+
if (interactive) {
|
|
153
|
+
const input = await vscode.window.showInputBox({ title: operation.title, prompt: 'Operation arguments (JSON object)', value: '{}',
|
|
154
|
+
validateInput(value) { try { const parsed = JSON.parse(value); return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? undefined : 'Enter a JSON object'; } catch { return 'Enter a JSON object'; } },
|
|
155
|
+
});
|
|
156
|
+
if (input === undefined) return;
|
|
157
|
+
args = JSON.parse(input) as Record<string, unknown>;
|
|
158
|
+
}
|
|
159
|
+
const selected = launch(context);
|
|
160
|
+
const client = new Client({ name: namespace + '-vscode', version: ${JSON.stringify(version)} });
|
|
161
|
+
try {
|
|
162
|
+
await client.connect(new StdioClientTransport({ command: selected.command, args: [...selected.args, 'mcp'],
|
|
163
|
+
cwd: selected.cwd, env: await environment(context),
|
|
164
|
+
}));
|
|
165
|
+
const response = await client.callTool({ name: operation.name, arguments: args });
|
|
166
|
+
if (response.isError) throw new Error(JSON.stringify(response.content));
|
|
167
|
+
const result = response.structuredContent ?? response.content;
|
|
168
|
+
if (interactive) await vscode.window.showTextDocument(await vscode.workspace.openTextDocument({ language: 'json', content: JSON.stringify(result, null, 2) }));
|
|
169
|
+
return result;
|
|
170
|
+
} finally {
|
|
171
|
+
await client.close();
|
|
172
|
+
}
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function packagePatch(project: Project): Record<string, unknown> {
|
|
180
|
+
const name = extensionName(project);
|
|
181
|
+
const properties = Object.fromEntries(
|
|
182
|
+
Object.entries(configProperties(project))
|
|
183
|
+
.filter(([, property]) => !isSensitive(property))
|
|
184
|
+
.map(([key, property]) => {
|
|
185
|
+
const { "x-toolfactory": _metadata, ...schema } = property;
|
|
186
|
+
return [`${name}.config.${key}`, schema];
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
const commands = operations(project).map((op) => ({
|
|
190
|
+
command: `${name}.${op.name}`,
|
|
191
|
+
title: op.title ?? op.name,
|
|
192
|
+
category: projectName.display(project.identity.name),
|
|
193
|
+
}));
|
|
194
|
+
if (Object.values(configProperties(project)).some(isSensitive))
|
|
195
|
+
commands.push({
|
|
196
|
+
command: `${name}.configureSecrets`,
|
|
197
|
+
title: "Configure credentials",
|
|
198
|
+
category: projectName.display(project.identity.name),
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
version: project.identity.version ?? "0.0.0",
|
|
203
|
+
displayName: projectName.display(project.identity.name),
|
|
204
|
+
description: project.identity.description ?? `${project.identity.name} tools for VS Code`,
|
|
205
|
+
publisher: publisher(project),
|
|
206
|
+
license: project.identity.license ?? "UNLICENSED",
|
|
207
|
+
...(project.identity.repository
|
|
208
|
+
? { repository: { type: "git", url: project.identity.repository } }
|
|
209
|
+
: {}),
|
|
210
|
+
engines: VSCODE_SCAFFOLD.engines,
|
|
211
|
+
main: "./dist/extension.js",
|
|
212
|
+
capabilities: { untrustedWorkspaces: { supported: false } },
|
|
213
|
+
contributes: {
|
|
214
|
+
commands,
|
|
215
|
+
mcpServerDefinitionProviders: [
|
|
216
|
+
{ id: name, label: projectName.display(project.identity.name) },
|
|
217
|
+
],
|
|
218
|
+
configuration: {
|
|
219
|
+
title: projectName.display(project.identity.name),
|
|
220
|
+
properties: {
|
|
221
|
+
...properties,
|
|
222
|
+
[`${name}.runtime.command`]: {
|
|
223
|
+
type: "string",
|
|
224
|
+
scope: "machine",
|
|
225
|
+
description: "Override the tool runtime executable.",
|
|
226
|
+
},
|
|
227
|
+
[`${name}.runtime.args`]: {
|
|
228
|
+
type: "array",
|
|
229
|
+
items: { type: "string" },
|
|
230
|
+
scope: "machine",
|
|
231
|
+
description: "Arguments for runtime.command, before the mcp subcommand.",
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
scripts: { ...VSCODE_SCAFFOLD.scripts, vsix: "vsce package --out extension.vsix" },
|
|
237
|
+
dependencies: { "@modelcontextprotocol/client": "^2.0.0" },
|
|
238
|
+
devDependencies: { ...VSCODE_SCAFFOLD.devDependencies, "@vscode/vsce": `^${VSCE_VERSION}` },
|
|
239
|
+
overrides: VSCODE_SCAFFOLD.overrides,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export const surface: Surface = {
|
|
244
|
+
id: "vscode-extension",
|
|
245
|
+
plan(project) {
|
|
246
|
+
const registry = project.tool.binding === "python" ? "pypi" : "npm";
|
|
247
|
+
if (!has(project, registry))
|
|
248
|
+
throw new Error(`vscode-extension requires ${registry} for its installed runtime.`);
|
|
249
|
+
const entry = `import * as vscode from 'vscode';\nimport { registerGenerated } from './generated';\n`;
|
|
250
|
+
const probe = operations(project).find((op) => project.tool.tests.examples[op.name]);
|
|
251
|
+
return [
|
|
252
|
+
{
|
|
253
|
+
kind: "merge",
|
|
254
|
+
path: `${HOST_DIR}/package.json`,
|
|
255
|
+
format: "json",
|
|
256
|
+
patch: packagePatch(project),
|
|
257
|
+
keyedArrays: {
|
|
258
|
+
"contributes.commands": "command",
|
|
259
|
+
"contributes.mcpServerDefinitionProviders": "id",
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{ kind: "file", path: `${HOST_DIR}/src/generated.ts`, content: runtime(project) },
|
|
263
|
+
{
|
|
264
|
+
kind: "region",
|
|
265
|
+
path: `${HOST_DIR}/src/extension.ts`,
|
|
266
|
+
regions: [{ begin: "// tf:imports", end: "// /tf:imports", content: `\n${entry}` }],
|
|
267
|
+
template: `// tf:imports\n// /tf:imports\n\n// This is your extension entry. Use the complete VS Code API here.\nexport function activate(context: vscode.ExtensionContext) {\n registerGenerated(context);\n}\n\nexport function deactivate() {}\n`,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
kind: "merge",
|
|
271
|
+
path: `${HOST_DIR}/tsconfig.json`,
|
|
272
|
+
format: "json",
|
|
273
|
+
patch: {
|
|
274
|
+
compilerOptions: {
|
|
275
|
+
module: "Node16",
|
|
276
|
+
target: "ES2022",
|
|
277
|
+
lib: ["ES2022"],
|
|
278
|
+
types: ["node", "mocha"],
|
|
279
|
+
sourceMap: true,
|
|
280
|
+
rootDir: "src",
|
|
281
|
+
strict: true,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
...[
|
|
286
|
+
["esbuild.js", ESBUILD],
|
|
287
|
+
["eslint.config.mjs", ESLINT],
|
|
288
|
+
[".vscode/launch.json", LAUNCH],
|
|
289
|
+
[".vscode/tasks.json", TASKS],
|
|
290
|
+
[".vscodeignore", IGNORE],
|
|
291
|
+
[".gitignore", GITIGNORE],
|
|
292
|
+
].map(([path, content]) => ({
|
|
293
|
+
kind: "region" as const,
|
|
294
|
+
path: `${HOST_DIR}/${path}`,
|
|
295
|
+
regions: [
|
|
296
|
+
{
|
|
297
|
+
begin: path?.endsWith("ignore") ? "# tf:scaffold" : "// tf:scaffold",
|
|
298
|
+
end: path?.endsWith("ignore") ? "# /tf:scaffold" : "// /tf:scaffold",
|
|
299
|
+
content: "\n",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
template: `${path?.endsWith("ignore") ? "# tf:scaffold\n# /tf:scaffold" : "// tf:scaffold\n// /tf:scaffold"}\n${content}`,
|
|
303
|
+
})),
|
|
304
|
+
{
|
|
305
|
+
kind: "file",
|
|
306
|
+
path: `${HOST_DIR}/.vscode-test.mjs`,
|
|
307
|
+
content: `import { defineConfig } from '@vscode/test-cli';\nexport default defineConfig({ files: 'out/test/**/*.test.js', launchArgs: ['--disable-workspace-trust', '--skip-welcome', '--skip-release-notes', '--no-sandbox'] });\n`,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
kind: "file",
|
|
311
|
+
path: `${HOST_DIR}/src/test/generated.test.ts`,
|
|
312
|
+
content: `import * as assert from 'node:assert/strict';\nimport * as vscode from 'vscode';\nsuite('Generated extension', () => {\n test('activates and registers the operations', async () => {\n const extension = vscode.extensions.getExtension(${JSON.stringify(`${publisher(project)}.${extensionName(project)}`)});\n assert.ok(extension);\n await extension.activate();\n const commands = await vscode.commands.getCommands(true);\n for (const command of ${JSON.stringify(operations(project).map((op) => `${extensionName(project)}.${op.name}`))}) assert.ok(commands.includes(command), command);\n ${probe ? `await vscode.commands.executeCommand(${JSON.stringify(`${extensionName(project)}.${probe.name}`)}, ${JSON.stringify(project.tool.tests.examples[probe.name])});` : ""}\n });\n});\n`,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
kind: "file",
|
|
316
|
+
path: `${HOST_DIR}/README.md`,
|
|
317
|
+
content: `# ${projectName.display(project.identity.name)} for VS Code\n\n${project.identity.description ?? ""}\n\nInstall the VSIX with \`code --install-extension ${vsixName(project)}\`. The extension provides an MCP server and commands for each portable operation. ${registry === "npm" ? "Node.js/npm" : "uv"} must be on PATH; installed extensions use the matching published ${registry} version.\n\nDevelop with \`npm install\`, then open this directory in VS Code and press F5. Development and tests use the checkout's kernel. \`npm test\` runs a real Extension Development Host; \`npm run vsix\` creates the installable package.\n\nEdit \`src/extension.ts\` outside its import markers and add native files freely. Use all VS Code APIs: language-model tools, chat participants, views, webviews, commands, editors and language services. Add native contribution points and additional commands directly to \`package.json\`; generated command/provider entries are maintained by identity, preserving your entries. Build scripts, debugger settings and ignore rules are editable scaffold files. Use \`adopt\` only when replacing a generated integration itself.\n\nConfiguration is in VS Code Settings; credentials use SecretStorage through the Configure credentials command when declared. The runtime command/arguments can be overridden in machine settings.\n\nPublishers must be registered separately with Visual Studio Marketplace and Open VSX. Releases attach a VSIX even when no publishing credentials are configured.\n`,
|
|
318
|
+
},
|
|
319
|
+
];
|
|
320
|
+
},
|
|
321
|
+
validate(project) {
|
|
322
|
+
const cwd = `${project.root}/${HOST_DIR}`;
|
|
323
|
+
const drift = fileURLToPath(new URL("../hosts/vscode.js", import.meta.url));
|
|
324
|
+
return [
|
|
325
|
+
{
|
|
326
|
+
label: "VS Code upstream scaffold",
|
|
327
|
+
command: "node",
|
|
328
|
+
args: [...execArgv, drift, project.root],
|
|
329
|
+
},
|
|
330
|
+
{ label: "VS Code dependencies", command: "npm", args: ["install"], cwd },
|
|
331
|
+
{ label: "VS Code VSIX", command: "npm", args: ["run", "vsix"], cwd },
|
|
332
|
+
{
|
|
333
|
+
label: "VS Code Extension Development Host",
|
|
334
|
+
command: process.platform === "linux" ? "xvfb-run" : "npm",
|
|
335
|
+
args: process.platform === "linux" ? ["-a", "npm", "test"] : ["test"],
|
|
336
|
+
cwd,
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
},
|
|
340
|
+
verdict(operation) {
|
|
341
|
+
return isPortable(operation)
|
|
342
|
+
? { kind: "bridged", reason: "bridged:vscode-mcp" }
|
|
343
|
+
: { kind: "excluded", reason: "excluded:implement-in-hosts" };
|
|
344
|
+
},
|
|
345
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Captured by running generator-code@1.12.0 (TypeScript/esbuild); hosts/vscode.ts verifies it. */
|
|
2
|
+
|
|
3
|
+
export const VSCODE_GENERATOR = "1.12.0";
|
|
4
|
+
export const GITIGNORE = "out\ndist\nnode_modules\n.vscode-test/\n*.vsix\n";
|
|
5
|
+
|
|
6
|
+
export const VSCODE_SCAFFOLD = {
|
|
7
|
+
engines: {
|
|
8
|
+
vscode: "^1.136.0",
|
|
9
|
+
},
|
|
10
|
+
scripts: {
|
|
11
|
+
"vscode:prepublish": "npm run package",
|
|
12
|
+
compile: "npm run check-types && npm run lint && node esbuild.js",
|
|
13
|
+
watch: "npm-run-all -p watch:*",
|
|
14
|
+
"watch:esbuild": "node esbuild.js --watch",
|
|
15
|
+
"watch:tsc": "tsc --noEmit --watch --project tsconfig.json",
|
|
16
|
+
package: "npm run check-types && npm run lint && node esbuild.js --production",
|
|
17
|
+
"compile-tests": "tsc -p . --outDir out",
|
|
18
|
+
"watch-tests": "tsc -p . -w --outDir out",
|
|
19
|
+
pretest: "npm run compile-tests && npm run compile && npm run lint",
|
|
20
|
+
"check-types": "tsc --noEmit",
|
|
21
|
+
lint: "eslint src",
|
|
22
|
+
test: "vscode-test",
|
|
23
|
+
},
|
|
24
|
+
devDependencies: {
|
|
25
|
+
"@types/vscode": "^1.136.0",
|
|
26
|
+
"@types/mocha": "^10.0.10",
|
|
27
|
+
"@types/node": "24.x",
|
|
28
|
+
"typescript-eslint": "^8.61.1",
|
|
29
|
+
eslint: "^10.5.0",
|
|
30
|
+
esbuild: "^0.28.1",
|
|
31
|
+
"npm-run-all": "^4.1.5",
|
|
32
|
+
typescript: "^6.0.3",
|
|
33
|
+
"@vscode/test-cli": "^0.0.15",
|
|
34
|
+
"@vscode/test-electron": "^3.0.0",
|
|
35
|
+
},
|
|
36
|
+
overrides: {
|
|
37
|
+
diff: "^8.0.4",
|
|
38
|
+
"serialize-javascript": "^7.0.6",
|
|
39
|
+
},
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
export const ESBUILD =
|
|
43
|
+
"const esbuild = require(\"esbuild\");\n\nconst production = process.argv.includes('--production');\nconst watch = process.argv.includes('--watch');\n\n/**\n * @type {import('esbuild').Plugin}\n */\nconst esbuildProblemMatcherPlugin = {\n\tname: 'esbuild-problem-matcher',\n\n\tsetup(build) {\n\t\tbuild.onStart(() => {\n\t\t\tconsole.log('[watch] build started');\n\t\t});\n\t\tbuild.onEnd((result) => {\n\t\t\tresult.errors.forEach(({ text, location }) => {\n\t\t\t\tconsole.error(`\u2718 [ERROR] ${text}`);\n\t\t\t\tconsole.error(` ${location.file}:${location.line}:${location.column}:`);\n\t\t\t});\n\t\t\tconsole.log('[watch] build finished');\n\t\t});\n\t},\n};\n\nasync function main() {\n\tconst ctx = await esbuild.context({\n\t\tentryPoints: [\n\t\t\t'src/extension.ts'\n\t\t],\n\t\tbundle: true,\n\t\tformat: 'cjs',\n\t\tminify: production,\n\t\tsourcemap: !production,\n\t\tsourcesContent: false,\n\t\tplatform: 'node',\n\t\toutfile: 'dist/extension.js',\n\t\texternal: ['vscode'],\n\t\tlogLevel: 'silent',\n\t\tplugins: [\n\t\t\t/* add to the end of plugins array */\n\t\t\tesbuildProblemMatcherPlugin,\n\t\t],\n\t});\n\tif (watch) {\n\t\tawait ctx.watch();\n\t} else {\n\t\tawait ctx.rebuild();\n\t\tawait ctx.dispose();\n\t}\n}\n\nmain().catch(e => {\n\tconsole.error(e);\n\tprocess.exit(1);\n});\n";
|
|
44
|
+
|
|
45
|
+
export const ESLINT =
|
|
46
|
+
'import typescriptEslint from "typescript-eslint";\n\nexport default [{\n files: ["**/*.ts"],\n}, {\n plugins: {\n "@typescript-eslint": typescriptEslint.plugin,\n },\n\n languageOptions: {\n parser: typescriptEslint.parser,\n ecmaVersion: 2022,\n sourceType: "module",\n },\n\n rules: {\n "@typescript-eslint/naming-convention": ["warn", {\n selector: "import",\n format: ["camelCase", "PascalCase"],\n }],\n\n curly: "warn",\n eqeqeq: "warn",\n "no-throw-literal": "warn",\n semi: "warn",\n },\n}];';
|
|
47
|
+
|
|
48
|
+
export const IGNORE =
|
|
49
|
+
".vscode/**\n.vscode-test/**\nout/**\nnode_modules/**\nsrc/**\n.gitignore\n.yarnrc\nesbuild.js\nvsc-extension-quickstart.md\n**/tsconfig.json\n**/eslint.config.mjs\n**/*.map\n**/*.ts\n**/.vscode-test.*\n";
|
|
50
|
+
|
|
51
|
+
export const LAUNCH =
|
|
52
|
+
'// A launch configuration that compiles the extension and then opens it inside a new window\n// Use IntelliSense to learn about possible attributes.\n// Hover to view descriptions of existing attributes.\n// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n{\n\t"version": "0.2.0",\n\t"configurations": [\n\t\t{\n\t\t\t"name": "Run Extension",\n\t\t\t"type": "extensionHost",\n\t\t\t"request": "launch",\n\t\t\t"args": [\n\t\t\t\t"--extensionDevelopmentPath=${workspaceFolder}"\n\t\t\t],\n\t\t\t"outFiles": [\n\t\t\t\t"${workspaceFolder}/dist/**/*.js"\n\t\t\t],\n\t\t\t"preLaunchTask": "${defaultBuildTask}"\n\t\t}\n\t]\n}\n';
|
|
53
|
+
|
|
54
|
+
export const TASKS =
|
|
55
|
+
'// See https://go.microsoft.com/fwlink/?LinkId=733558\n// for the documentation about the tasks.json format\n{\n\t"version": "2.0.0",\n\t"tasks": [\n\t\t{\n "label": "watch",\n "dependsOn": [\n "npm: watch:tsc",\n "npm: watch:esbuild"\n ],\n "presentation": {\n "reveal": "never"\n },\n "group": {\n "kind": "build",\n "isDefault": true\n }\n },\n {\n "type": "npm",\n "script": "watch:esbuild",\n "group": "build",\n "problemMatcher": "$esbuild-watch",\n "isBackground": true,\n "label": "npm: watch:esbuild",\n "presentation": {\n "group": "watch",\n "reveal": "never"\n }\n },\n\t\t{\n "type": "npm",\n "script": "watch:tsc",\n "group": "build",\n "problemMatcher": "$tsc-watch",\n "isBackground": true,\n "label": "npm: watch:tsc",\n "presentation": {\n "group": "watch",\n "reveal": "never"\n }\n },\n\t\t{\n\t\t\t"type": "npm",\n\t\t\t"script": "watch-tests",\n\t\t\t"problemMatcher": "$tsc-watch",\n\t\t\t"isBackground": true,\n\t\t\t"presentation": {\n\t\t\t\t"reveal": "never",\n\t\t\t\t"group": "watchers"\n\t\t\t},\n\t\t\t"group": "build"\n\t\t},\n\t\t{\n\t\t\t"label": "tasks: watch-tests",\n\t\t\t"dependsOn": [\n\t\t\t\t"npm: watch",\n\t\t\t\t"npm: watch-tests"\n\t\t\t],\n\t\t\t"problemMatcher": []\n\t\t}\n\t]\n}\n';
|