zigsm 1.4.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.
@@ -0,0 +1,155 @@
1
+ import * as child_process from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import * as http from "node:http";
4
+ import * as path from "node:path";
5
+ let activeServer = null;
6
+ function findZigExecutable() {
7
+ const zigPath = process.env.ZIG_PATH || "zig";
8
+ try {
9
+ const result = child_process.execSync(`${zigPath} version`, { encoding: "utf8" });
10
+ if (result.includes("dev") || /\d+\.\d+\.\d+/.test(result)) {
11
+ return zigPath;
12
+ }
13
+ }
14
+ catch {
15
+ // Continue to fallback
16
+ }
17
+ const commonPaths = [
18
+ "/usr/local/bin/zig",
19
+ "/usr/bin/zig",
20
+ "/opt/homebrew/bin/zig",
21
+ "/opt/zig/zig",
22
+ path.join(process.env.HOME || "", ".local/bin/zig"),
23
+ ];
24
+ for (const p of commonPaths) {
25
+ if (fs.existsSync(p)) {
26
+ try {
27
+ child_process.execSync(`${p} version`, { encoding: "utf8" });
28
+ return p;
29
+ }
30
+ catch {
31
+ // Continue checking
32
+ }
33
+ }
34
+ }
35
+ return "zig";
36
+ }
37
+ export function getZigVersion() {
38
+ const zigPath = findZigExecutable();
39
+ try {
40
+ const result = child_process.execSync(`${zigPath} version`, { encoding: "utf8" });
41
+ return result.trim();
42
+ }
43
+ catch (error) {
44
+ throw new Error(`Failed to get Zig version: ${error}`);
45
+ }
46
+ }
47
+ export async function startLocalStdServer() {
48
+ if (activeServer) {
49
+ return activeServer;
50
+ }
51
+ const zigPath = findZigExecutable();
52
+ return new Promise((resolve, reject) => {
53
+ const stdProcess = child_process.spawn(zigPath, ["std", "--no-open-browser"], {
54
+ stdio: ["ignore", "pipe", "pipe"],
55
+ });
56
+ let output = "";
57
+ let errorOutput = "";
58
+ let resolved = false;
59
+ const timeout = setTimeout(() => {
60
+ if (!resolved) {
61
+ stdProcess.kill();
62
+ reject(new Error("Timeout waiting for Zig std server to start"));
63
+ }
64
+ }, 10000);
65
+ stdProcess.stdout?.on("data", (data) => {
66
+ output += data.toString();
67
+ // Match patterns like "http://127.0.0.1:43695/"
68
+ const match = output.match(/(http:\/\/[0-9.]+:[0-9]+)/);
69
+ if (match && !resolved) {
70
+ resolved = true;
71
+ clearTimeout(timeout);
72
+ const baseUrl = match[1];
73
+ const port = parseInt(baseUrl.split(":").pop() || "43695");
74
+ activeServer = {
75
+ process: stdProcess,
76
+ port,
77
+ baseUrl,
78
+ };
79
+ resolve(activeServer);
80
+ }
81
+ });
82
+ stdProcess.stderr?.on("data", (data) => {
83
+ errorOutput += data.toString();
84
+ });
85
+ stdProcess.on("error", (error) => {
86
+ clearTimeout(timeout);
87
+ if (!resolved) {
88
+ resolved = true;
89
+ reject(new Error(`Failed to start Zig std server: ${error.message}`));
90
+ }
91
+ });
92
+ stdProcess.on("exit", (code) => {
93
+ clearTimeout(timeout);
94
+ if (!resolved) {
95
+ resolved = true;
96
+ reject(new Error(`Zig std server exited with code ${code}: ${errorOutput}`));
97
+ }
98
+ activeServer = null;
99
+ });
100
+ });
101
+ }
102
+ export function stopLocalStdServer() {
103
+ if (activeServer) {
104
+ activeServer.process.kill();
105
+ activeServer = null;
106
+ }
107
+ }
108
+ export async function fetchFromLocalServer(path) {
109
+ const server = await startLocalStdServer();
110
+ const url = `${server.baseUrl}${path}`;
111
+ return new Promise((resolve, reject) => {
112
+ http.get(url, (res) => {
113
+ let data = "";
114
+ res.on("data", (chunk) => {
115
+ data += chunk;
116
+ });
117
+ res.on("end", () => {
118
+ if (res.statusCode === 200) {
119
+ resolve(data);
120
+ }
121
+ else {
122
+ reject(new Error(`HTTP ${res.statusCode}: ${data}`));
123
+ }
124
+ });
125
+ }).on("error", (error) => {
126
+ reject(error);
127
+ });
128
+ });
129
+ }
130
+ export async function getLocalStdSources() {
131
+ const server = await startLocalStdServer();
132
+ const url = `${server.baseUrl}/sources.tar`;
133
+ return new Promise((resolve, reject) => {
134
+ http.get(url, (res) => {
135
+ const chunks = [];
136
+ res.on("data", (chunk) => {
137
+ chunks.push(chunk);
138
+ });
139
+ res.on("end", () => {
140
+ if (res.statusCode === 200) {
141
+ const buffer = Buffer.concat(chunks);
142
+ resolve(new Uint8Array(buffer));
143
+ }
144
+ else {
145
+ reject(new Error(`Failed to fetch sources.tar: HTTP ${res.statusCode}`));
146
+ }
147
+ });
148
+ }).on("error", (error) => {
149
+ reject(error);
150
+ });
151
+ });
152
+ }
153
+ process.on("exit", stopLocalStdServer);
154
+ process.on("SIGINT", stopLocalStdServer);
155
+ process.on("SIGTERM", stopLocalStdServer);
package/dist/main.wasm ADDED
Binary file
package/dist/mcp.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { downloadSourcesTar, ensureDocs, startViewServer, } from "./docs.js";
5
+ import { registerAllTools } from "./tools.js";
6
+ function parseArgs(args) {
7
+ const options = {
8
+ version: "master",
9
+ updatePolicy: "manual",
10
+ docSource: "local",
11
+ };
12
+ let versionExplicit = false;
13
+ let docSourceExplicit = false;
14
+ for (let i = 0; i < args.length; i++) {
15
+ const arg = args[i];
16
+ if (arg === "update") {
17
+ options.command = "update";
18
+ }
19
+ else if (arg === "view") {
20
+ options.command = "view";
21
+ }
22
+ else if (arg === "--version" && i + 1 < args.length) {
23
+ options.version = args[++i];
24
+ versionExplicit = true;
25
+ }
26
+ else if (arg === "--update-policy" && i + 1 < args.length) {
27
+ const policy = args[++i];
28
+ if (policy === "manual" || policy === "daily" || policy === "startup") {
29
+ options.updatePolicy = policy;
30
+ }
31
+ else {
32
+ console.error(`Invalid update policy: ${policy}. Must be one of: manual, daily, startup`);
33
+ process.exit(1);
34
+ }
35
+ }
36
+ else if (arg === "--doc-source" && i + 1 < args.length) {
37
+ const source = args[++i];
38
+ if (source === "local" || source === "remote") {
39
+ options.docSource = source;
40
+ docSourceExplicit = true;
41
+ }
42
+ else {
43
+ console.error(`Invalid doc source: ${source}. Must be one of: local, remote`);
44
+ process.exit(1);
45
+ }
46
+ }
47
+ else if (arg === "--help" || arg === "-h") {
48
+ printHelp();
49
+ process.exit(0);
50
+ }
51
+ }
52
+ // If user explicitly provided --version but not --doc-source,
53
+ // prefer remote docs since versioned docs come from ziglang.org
54
+ if (versionExplicit && !docSourceExplicit) {
55
+ options.docSource = "remote";
56
+ }
57
+ return options;
58
+ }
59
+ function printHelp() {
60
+ console.log(`Usage: zsm [options] [command]
61
+
62
+ Commands:
63
+ update Update documentation without starting MCP server
64
+ view Start local web server to view documentation
65
+
66
+ Options:
67
+ --version <version> Zig version to use (default: master)
68
+ Examples: master, 0.13.0, 0.14.1
69
+ --update-policy <policy> Update policy (default: manual)
70
+ Options: manual, daily, startup
71
+ --doc-source <source> Documentation source (default: local)
72
+ Options: local (use local Zig), remote (download from ziglang.org)
73
+ -h, --help Show this help message
74
+
75
+ Examples:
76
+ zsm # Start MCP server with master version (local)
77
+ zsm --version 0.14.1 # Start with specific version (remote)
78
+ zsm --update-policy daily # Auto-update daily on startup
79
+ zsm update --version 0.14.1 # Update docs to specific version
80
+ zsm view --version master # View documentation for specific version`);
81
+ }
82
+ async function main() {
83
+ const args = process.argv.slice(2);
84
+ const options = parseArgs(args);
85
+ if (options.command === "update") {
86
+ try {
87
+ await ensureDocs(options.version, "startup", false, options.docSource);
88
+ process.exit(0);
89
+ }
90
+ catch {
91
+ process.exit(1);
92
+ }
93
+ }
94
+ if (options.command === "view") {
95
+ try {
96
+ await startViewServer(options.version);
97
+ return;
98
+ }
99
+ catch {
100
+ process.exit(1);
101
+ }
102
+ }
103
+ const builtinFunctions = await ensureDocs(options.version, options.updatePolicy, true, options.docSource);
104
+ const stdSources = await downloadSourcesTar(options.version, true, false, options.docSource);
105
+ const mcpServer = new McpServer({
106
+ name: "ZigDocs",
107
+ description: "Retrieves up-to-date documentation for the Zig programming language standard library and builtin functions.",
108
+ version: options.version,
109
+ });
110
+ await registerAllTools(mcpServer, builtinFunctions, stdSources, {
111
+ zigVersion: options.version,
112
+ docSource: options.docSource,
113
+ });
114
+ const transport = new StdioServerTransport();
115
+ await mcpServer.connect(transport);
116
+ }
117
+ main().catch((error) => {
118
+ if (error instanceof Error) {
119
+ console.error(`Error: ${error.message}`);
120
+ }
121
+ else {
122
+ console.error("An unexpected error occurred");
123
+ }
124
+ process.exit(1);
125
+ });