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.
- package/LICENSE +21 -0
- package/README.md +180 -0
- package/dist/docs.js +176 -0
- package/dist/extract-builtin-functions.js +155 -0
- package/dist/index.html +47 -0
- package/dist/local-std-server.js +155 -0
- package/dist/main.wasm +0 -0
- package/dist/mcp.js +125 -0
- package/dist/std.js +1006 -0
- package/dist/tools.js +225 -0
- package/dist/voyage.js +88 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Andrey Ryapov, "Zig and WebAssembly" contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# ZSM
|
|
2
|
+
|
|
3
|
+
ZSM - fork of **Zig Docs MCP**
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> This fork keeps the original behavior and documentation flow nearly unchanged. The main addition vs upstream is addition of embeddings for better semantic retrieval in search tools.
|
|
7
|
+
|
|
8
|
+
Model Context Protocol (MCP) server that provides up-to-date documentation for the Zig programming language standard library and builtin functions.
|
|
9
|
+
|
|
10
|
+
It uses the same approach as Zig's official autodoc (ziglang.org) by reading STD lib source files directly through a WASM module. However instead of returning HTML, it outputs Markdown which significantly reduces token usage.
|
|
11
|
+
|
|
12
|
+
By default, the server uses your locally installed Zig compiler to serve documentation, ensuring you always get docs that match your actual Zig version. It can also fetch documentation from ziglang.org if needed.
|
|
13
|
+
|
|
14
|
+
> [!TIP]
|
|
15
|
+
> Add `use zigdocs` to your prompt if you want to explicitly instruct the LLM to use Zig docs tools. Otherwise, LLM will automatically decide when to utilize MCP tools based on the context of your questions.
|
|
16
|
+
|
|
17
|
+
<p align="center" width="100%">
|
|
18
|
+
<img src="https://raw.githubusercontent.com/zig-wasm/.github/refs/heads/main/static/readme_mcp_1.gif" width="49%" />
|
|
19
|
+
<img src="https://raw.githubusercontent.com/zig-wasm/.github/refs/heads/main/static/readme_mcp_2.gif" width="49%" />
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
## Tools
|
|
23
|
+
|
|
24
|
+
- **`list_builtin_functions`** - Lists all available Zig builtin functions. Builtin functions are provided by the compiler and are prefixed with '@'. The comptime keyword on a parameter means that the parameter must be known at compile time. Use this to discover what functions are available, then use 'get_builtin_function' to get detailed documentation.
|
|
25
|
+
- **`get_builtin_function`** - Search for Zig builtin functions by name and get their documentation, signatures, and usage information. Returns all matching functions ranked by relevance.
|
|
26
|
+
- **`search_std_lib`** - Search the Zig standard library for declarations by name. Returns a list of matching items with their fully qualified names. Use this to discover available types, functions, and constants in the standard library.
|
|
27
|
+
- **`get_std_lib_item`** - Get detailed documentation for a specific standard library item by its fully qualified name (e.g., "std.ArrayList.init"). Returns comprehensive documentation including function signatures, parameters, errors, examples, and source code. Set `get_source_file: true` to retrieve the entire source file where the item is implemented.
|
|
28
|
+
|
|
29
|
+
When `VOYAGE_API_KEY` is set, search tools automatically use hybrid ranking (lexical + embeddings) and fall back to lexical-only results if embeddings are unavailable.
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
The CLI provides flexible options for version control and update management:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Start MCP server
|
|
37
|
+
zsm --doc-source local
|
|
38
|
+
|
|
39
|
+
# Use specific Zig version from ziglang.org instead of local Zig
|
|
40
|
+
zsm --doc-source remote --version 0.14.1
|
|
41
|
+
|
|
42
|
+
# Enable automatic daily updates
|
|
43
|
+
zsm --doc-source remote --update-policy daily
|
|
44
|
+
|
|
45
|
+
# Update documentation without starting MCP server (only for remote)
|
|
46
|
+
zsm update --version 0.15.1
|
|
47
|
+
|
|
48
|
+
# Start local web server to view documentation
|
|
49
|
+
zsm view --version 0.15.1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Version options `--version`**:
|
|
53
|
+
- `master` (default) - Latest development version from Zig's master branch
|
|
54
|
+
- `0.14.1`, `0.14.0`, etc. - Specific Zig release versions
|
|
55
|
+
|
|
56
|
+
**Update policies `--update-policy`**:
|
|
57
|
+
- `manual` (default) - No automatic updates, manual control only
|
|
58
|
+
- `daily` - Check for documentation updates once per day
|
|
59
|
+
- `startup` - Update documentation every time the server starts
|
|
60
|
+
|
|
61
|
+
**Documentation sources `--doc-source`**:
|
|
62
|
+
- `local` (default) - Use your locally installed Zig compiler's documentation server (`zig std`)
|
|
63
|
+
- `remote` - Download documentation from ziglang.org
|
|
64
|
+
|
|
65
|
+
## Documentation Sources
|
|
66
|
+
|
|
67
|
+
### Local Mode (Default)
|
|
68
|
+
|
|
69
|
+
The server automatically uses your local Zig installation to serve documentation via `zig std`. This ensures:
|
|
70
|
+
- Documentation always matches your installed Zig version
|
|
71
|
+
- No network requests needed for standard library docs
|
|
72
|
+
- Faster response times
|
|
73
|
+
|
|
74
|
+
### Remote Mode
|
|
75
|
+
|
|
76
|
+
When using `--doc-source remote`, documentation is fetched from ziglang.org and cached in platform-specific directories:
|
|
77
|
+
- Linux: `~/.cache/zigsm/`
|
|
78
|
+
- macOS: `~/Library/Caches/zigsm/`
|
|
79
|
+
- Windows: `%LOCALAPPDATA%\zigsm\`
|
|
80
|
+
|
|
81
|
+
## Embeddings (Voyage)
|
|
82
|
+
|
|
83
|
+
Optional environment variables:
|
|
84
|
+
- `VOYAGE_API_KEY` - Enables embeddings for doc search when set.
|
|
85
|
+
- `VOYAGE_MODEL` - Embedding model override (default: `voyage-3-lite`).
|
|
86
|
+
|
|
87
|
+
Embeddings are cached per Zig version, doc source, and model in the same cache directory used for docs.
|
|
88
|
+
|
|
89
|
+
## Installation
|
|
90
|
+
|
|
91
|
+
The installation examples below use the local documentation source by default. In local mode, docs are served by your installed Zig via `zig std`, requiring no network and matching your actual Zig version. This is the recommended setup for most users. For downloading docs from ziglang.org instead, see Remote Documentation (Optional) below.
|
|
92
|
+
|
|
93
|
+
### Claude Code
|
|
94
|
+
Using npx (Node.js)
|
|
95
|
+
```bash
|
|
96
|
+
claude mcp add zsm -- npx -y zigsm@latest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Using bunx (Bun)
|
|
100
|
+
```bash
|
|
101
|
+
claude mcp add zsm -- bunx zigsm@latest
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Roo Code
|
|
105
|
+
|
|
106
|
+
1. Click the **MCP** button in Roo Code
|
|
107
|
+
2. Select **"Edit Global MCP"** or **"Edit Project MCP"**
|
|
108
|
+
3. Add the configuration from the JSON template below
|
|
109
|
+
|
|
110
|
+
### Augment Code
|
|
111
|
+
|
|
112
|
+
Navigate to **Settings → MCP Servers → Add Server** and use the JSON template below.
|
|
113
|
+
|
|
114
|
+
### Claude Desktop
|
|
115
|
+
|
|
116
|
+
Add the JSON configuration below to your MCP settings file.
|
|
117
|
+
|
|
118
|
+
### JSON Configuration Template
|
|
119
|
+
|
|
120
|
+
**Node.js:**
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"mcpServers": {
|
|
124
|
+
"zsm": {
|
|
125
|
+
"command": "npx",
|
|
126
|
+
"args": ["-y", "zigsm@latest"]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Bun:**
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"zsm": {
|
|
137
|
+
"command": "bunx",
|
|
138
|
+
"args": ["zigsm@latest"]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Remote Documentation (Optional)
|
|
145
|
+
|
|
146
|
+
If you prefer downloading documentation from ziglang.org instead of using your local Zig, enable remote mode explicitly and choose a version:
|
|
147
|
+
|
|
148
|
+
Using npx (Node.js)
|
|
149
|
+
```bash
|
|
150
|
+
claude mcp add zsm -- npx -y zigsm@latest --doc-source remote --version master
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Using bunx (Bun)
|
|
154
|
+
```bash
|
|
155
|
+
claude mcp add zsm -- bunx zigsm@latest --doc-source remote --version 0.14.1
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Node.js (remote):**
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"zsm": {
|
|
163
|
+
"command": "npx",
|
|
164
|
+
"args": ["-y", "zigsm@latest", "--doc-source", "remote", "--version", "master"]
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Bun (remote):**
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"mcpServers": {
|
|
174
|
+
"zsm": {
|
|
175
|
+
"command": "bunx",
|
|
176
|
+
"args": ["zigsm@latest", "--doc-source", "remote", "--version", "0.14.1"]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
package/dist/docs.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as http from "node:http";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import envPaths from "env-paths";
|
|
6
|
+
import extractBuiltinFunctions from "./extract-builtin-functions.js";
|
|
7
|
+
import { getLocalStdSources, getZigVersion } from "./local-std-server.js";
|
|
8
|
+
export async function ensureDocs(zigVersion, updatePolicy = "manual", isMcpMode = true, docSource = "local") {
|
|
9
|
+
const paths = envPaths("zigsm", { suffix: "" });
|
|
10
|
+
const metadataPath = path.join(paths.cache, zigVersion, "metadata.json");
|
|
11
|
+
let shouldUpdate = false;
|
|
12
|
+
if (updatePolicy === "startup") {
|
|
13
|
+
shouldUpdate = true;
|
|
14
|
+
}
|
|
15
|
+
else if (updatePolicy === "daily") {
|
|
16
|
+
if (!fs.existsSync(metadataPath)) {
|
|
17
|
+
shouldUpdate = true;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
try {
|
|
21
|
+
const content = fs.readFileSync(metadataPath, "utf8");
|
|
22
|
+
const metadata = JSON.parse(content);
|
|
23
|
+
const dayInMs = 24 * 60 * 60 * 1000;
|
|
24
|
+
shouldUpdate = Date.now() - metadata.lastUpdate >= dayInMs;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
shouldUpdate = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (shouldUpdate) {
|
|
32
|
+
try {
|
|
33
|
+
if (!isMcpMode)
|
|
34
|
+
console.log(`Updating documentation for Zig version: ${zigVersion}`);
|
|
35
|
+
const builtinFunctions = await extractBuiltinFunctions(zigVersion, isMcpMode, true);
|
|
36
|
+
await downloadSourcesTar(zigVersion, isMcpMode, true, docSource);
|
|
37
|
+
const dir = path.dirname(metadataPath);
|
|
38
|
+
if (!fs.existsSync(dir)) {
|
|
39
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
fs.writeFileSync(metadataPath, JSON.stringify({
|
|
42
|
+
lastUpdate: Date.now(),
|
|
43
|
+
version: zigVersion,
|
|
44
|
+
}, null, 2));
|
|
45
|
+
if (!isMcpMode)
|
|
46
|
+
console.log(`Successfully updated documentation for Zig version: ${zigVersion}`);
|
|
47
|
+
return builtinFunctions;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (error instanceof Error && error.message.includes("404")) {
|
|
51
|
+
console.error(`Error: Zig version '${zigVersion}' not found on ziglang.org. Please check the version number.`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.error(`Error updating documentation for version ${zigVersion}:`, error);
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return await extractBuiltinFunctions(zigVersion, isMcpMode);
|
|
60
|
+
}
|
|
61
|
+
export async function downloadSourcesTar(zigVersion, isMcpMode = false, forceUpdate = false, docSource = "local") {
|
|
62
|
+
if (docSource === "local") {
|
|
63
|
+
try {
|
|
64
|
+
if (!isMcpMode)
|
|
65
|
+
console.log("Using local Zig std server for documentation");
|
|
66
|
+
const localVersion = getZigVersion();
|
|
67
|
+
if (!isMcpMode)
|
|
68
|
+
console.log(`Local Zig version: ${localVersion}`);
|
|
69
|
+
return await getLocalStdSources();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!isMcpMode) {
|
|
73
|
+
console.log(`Failed to use local Zig std server: ${error}`);
|
|
74
|
+
console.log("Falling back to remote documentation");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const paths = envPaths("zigsm", { suffix: "" });
|
|
79
|
+
const versionCacheDir = path.join(paths.cache, zigVersion);
|
|
80
|
+
const sourcesPath = path.join(versionCacheDir, "sources.tar");
|
|
81
|
+
if (fs.existsSync(sourcesPath) && !forceUpdate) {
|
|
82
|
+
if (!isMcpMode)
|
|
83
|
+
console.log(`Using cached sources.tar from ${sourcesPath}`);
|
|
84
|
+
return new Uint8Array(fs.readFileSync(sourcesPath));
|
|
85
|
+
}
|
|
86
|
+
const url = `https://ziglang.org/documentation/${zigVersion}/std/sources.tar`;
|
|
87
|
+
if (!isMcpMode)
|
|
88
|
+
console.log(`Downloading sources.tar from: ${url}`);
|
|
89
|
+
const response = await fetch(url);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`Failed to download sources.tar from ${url}: ${response.status} ${response.statusText}`);
|
|
92
|
+
}
|
|
93
|
+
const buffer = await response.arrayBuffer();
|
|
94
|
+
const uint8Array = new Uint8Array(buffer);
|
|
95
|
+
if (!fs.existsSync(versionCacheDir)) {
|
|
96
|
+
fs.mkdirSync(versionCacheDir, { recursive: true });
|
|
97
|
+
}
|
|
98
|
+
fs.writeFileSync(sourcesPath, uint8Array);
|
|
99
|
+
if (!isMcpMode)
|
|
100
|
+
console.log(`Downloaded sources.tar to ${sourcesPath}`);
|
|
101
|
+
return uint8Array;
|
|
102
|
+
}
|
|
103
|
+
async function downloadSourcesTarPath(zigVersion) {
|
|
104
|
+
const paths = envPaths("zigsm", { suffix: "" });
|
|
105
|
+
const versionCacheDir = path.join(paths.cache, zigVersion);
|
|
106
|
+
const sourcesPath = path.join(versionCacheDir, "sources.tar");
|
|
107
|
+
if (fs.existsSync(sourcesPath)) {
|
|
108
|
+
console.log(`Using cached sources.tar from ${sourcesPath}`);
|
|
109
|
+
return sourcesPath;
|
|
110
|
+
}
|
|
111
|
+
await downloadSourcesTar(zigVersion, false);
|
|
112
|
+
return sourcesPath;
|
|
113
|
+
}
|
|
114
|
+
export async function startViewServer(zigVersion) {
|
|
115
|
+
try {
|
|
116
|
+
const sourcesPath = await downloadSourcesTarPath(zigVersion);
|
|
117
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
118
|
+
const wasmPath = path.join(currentDir, "main.wasm");
|
|
119
|
+
const indexPath = path.join(currentDir, "index.html");
|
|
120
|
+
const stdJsPath = path.join(currentDir, "std.js");
|
|
121
|
+
const port = 8080;
|
|
122
|
+
const server = http.createServer((req, res) => {
|
|
123
|
+
let filePath;
|
|
124
|
+
const url = req.url || "/";
|
|
125
|
+
if (url === "/" || url === "/index.html") {
|
|
126
|
+
filePath = indexPath;
|
|
127
|
+
}
|
|
128
|
+
else if (url === "/std.js") {
|
|
129
|
+
filePath = stdJsPath;
|
|
130
|
+
}
|
|
131
|
+
else if (url === "/main.wasm") {
|
|
132
|
+
filePath = wasmPath;
|
|
133
|
+
}
|
|
134
|
+
else if (url === "/sources.tar") {
|
|
135
|
+
filePath = sourcesPath;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
res.writeHead(404);
|
|
139
|
+
res.end("File not found");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (!fs.existsSync(filePath)) {
|
|
143
|
+
res.writeHead(404);
|
|
144
|
+
res.end("File not found");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
148
|
+
const contentTypes = {
|
|
149
|
+
".html": "text/html",
|
|
150
|
+
".js": "text/javascript",
|
|
151
|
+
".css": "text/css",
|
|
152
|
+
".wasm": "application/wasm",
|
|
153
|
+
".tar": "application/x-tar",
|
|
154
|
+
};
|
|
155
|
+
const contentType = contentTypes[ext] || "application/octet-stream";
|
|
156
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
157
|
+
fs.createReadStream(filePath).pipe(res);
|
|
158
|
+
});
|
|
159
|
+
server.listen(port, () => {
|
|
160
|
+
const url = `http://localhost:${port}`;
|
|
161
|
+
console.log(`Server started at ${url}`);
|
|
162
|
+
console.log(`Serving Zig ${zigVersion} documentation`);
|
|
163
|
+
console.log("Press Ctrl+C to stop the server");
|
|
164
|
+
});
|
|
165
|
+
process.on("SIGINT", () => {
|
|
166
|
+
console.log("\nShutting down server...");
|
|
167
|
+
server.close(() => {
|
|
168
|
+
process.exit(0);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error("Error starting view server:", error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as cheerio from "cheerio";
|
|
4
|
+
import envPaths from "env-paths";
|
|
5
|
+
async function extractBuiltinFunctions(zigVersion, isMcpMode = true, forceUpdate = false) {
|
|
6
|
+
const paths = envPaths("zigsm", { suffix: "" });
|
|
7
|
+
const versionCacheDir = path.join(paths.cache, zigVersion);
|
|
8
|
+
const outputPath = path.join(versionCacheDir, "builtin-functions.json");
|
|
9
|
+
if (fs.existsSync(outputPath) && !forceUpdate) {
|
|
10
|
+
if (!isMcpMode)
|
|
11
|
+
console.log(`Using cached builtin functions from ${outputPath}`);
|
|
12
|
+
try {
|
|
13
|
+
const content = fs.readFileSync(outputPath, "utf8");
|
|
14
|
+
return JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.error(`Error reading cached file, re-extracting:`, error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const url = `https://ziglang.org/documentation/${zigVersion}/`;
|
|
21
|
+
if (!isMcpMode)
|
|
22
|
+
console.log(`Downloading HTML from: ${url}`);
|
|
23
|
+
const response = await fetch(url);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Failed to download HTML from ${url}: ${response.status} ${response.statusText}`);
|
|
26
|
+
}
|
|
27
|
+
const html = await response.text();
|
|
28
|
+
const $ = cheerio.load(html);
|
|
29
|
+
const builtins = [];
|
|
30
|
+
// Find "Builtin Functions" section
|
|
31
|
+
const builtinFunctionsSection = $('h2[id="Builtin-Functions"]');
|
|
32
|
+
if (builtinFunctionsSection.length === 0) {
|
|
33
|
+
throw new Error("Could not find Builtin Functions section in HTML");
|
|
34
|
+
}
|
|
35
|
+
let current = builtinFunctionsSection.next();
|
|
36
|
+
while (current.length && current.prop("tagName")?.toLowerCase() !== "h2") {
|
|
37
|
+
if (current.is("h3[id]")) {
|
|
38
|
+
const h3 = current;
|
|
39
|
+
const func = h3.find("a").first().text();
|
|
40
|
+
if (func.startsWith("@")) {
|
|
41
|
+
const signature = h3.next("pre").text().trim();
|
|
42
|
+
const descriptionParts = [];
|
|
43
|
+
const seeAlsoLinks = [];
|
|
44
|
+
let descCurrent = h3.next("pre").next();
|
|
45
|
+
while (descCurrent.length &&
|
|
46
|
+
!["h2", "h3"].includes(descCurrent.prop("tagName")?.toLowerCase() || "")) {
|
|
47
|
+
if (descCurrent.is("p")) {
|
|
48
|
+
const pHtml = descCurrent.html() || "";
|
|
49
|
+
const $p = cheerio.load(pHtml, {
|
|
50
|
+
xml: { xmlMode: false, decodeEntities: false },
|
|
51
|
+
});
|
|
52
|
+
$p("a").each((_, a) => {
|
|
53
|
+
const link = $p(a);
|
|
54
|
+
const href = link.attr("href") || "";
|
|
55
|
+
const text = link.text();
|
|
56
|
+
let markdownLink;
|
|
57
|
+
if (href.startsWith("#")) {
|
|
58
|
+
markdownLink = `[${text}](https://ziglang.org/documentation/${zigVersion}/${href})`;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
markdownLink = `[${text}](${href})`;
|
|
62
|
+
}
|
|
63
|
+
link.replaceWith(markdownLink);
|
|
64
|
+
});
|
|
65
|
+
$p("code").each((_, code) => {
|
|
66
|
+
const el = $p(code);
|
|
67
|
+
el.replaceWith(`\`${el.text()}\``);
|
|
68
|
+
});
|
|
69
|
+
const pText = $p.root().text();
|
|
70
|
+
descriptionParts.push(pText.replace(/\s+/g, " ").trim());
|
|
71
|
+
}
|
|
72
|
+
else if (descCurrent.is("ul")) {
|
|
73
|
+
// Convert each <li> to Markdown, handling <a> and <code> tags
|
|
74
|
+
descCurrent.children("li").each((_, li) => {
|
|
75
|
+
const liHtml = $(li).html() || "";
|
|
76
|
+
const $li = cheerio.load(liHtml, {
|
|
77
|
+
xml: { xmlMode: false, decodeEntities: false },
|
|
78
|
+
});
|
|
79
|
+
$li("a").each((_, a) => {
|
|
80
|
+
const link = $li(a);
|
|
81
|
+
const href = link.attr("href") || "";
|
|
82
|
+
const text = link.text();
|
|
83
|
+
let markdownLink;
|
|
84
|
+
if (href.startsWith("#")) {
|
|
85
|
+
markdownLink = `[${text}](https://ziglang.org/documentation/${zigVersion}/${href})`;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
markdownLink = `[${text}](${href})`;
|
|
89
|
+
}
|
|
90
|
+
link.replaceWith(markdownLink);
|
|
91
|
+
});
|
|
92
|
+
$li("code").each((_, code) => {
|
|
93
|
+
const el = $li(code);
|
|
94
|
+
el.replaceWith(`\`${el.text()}\``);
|
|
95
|
+
});
|
|
96
|
+
const liText = $li.root().text().replace(/\s+/g, " ").trim();
|
|
97
|
+
if (liText.length > 0) {
|
|
98
|
+
descriptionParts.push(`* ${liText}`);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else if (descCurrent.is("figure")) {
|
|
103
|
+
// Extract <figcaption> and <pre> content
|
|
104
|
+
const figcaption = descCurrent.find("figcaption").first().text().trim();
|
|
105
|
+
const pre = descCurrent.find("pre").first();
|
|
106
|
+
const code = pre.text();
|
|
107
|
+
let lang = "";
|
|
108
|
+
let label = "";
|
|
109
|
+
if (figcaption) {
|
|
110
|
+
label = `**${figcaption}**\n`;
|
|
111
|
+
if (figcaption.endsWith(".zig")) {
|
|
112
|
+
lang = "zig";
|
|
113
|
+
}
|
|
114
|
+
else if (figcaption.toLowerCase().includes("shell")) {
|
|
115
|
+
lang = "sh";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (code) {
|
|
119
|
+
// Format as Markdown code block
|
|
120
|
+
const codeBlock = `${label}\n\`\`\`${lang}\n${code.trim()}\n\`\`\``;
|
|
121
|
+
descriptionParts.push(codeBlock.trim());
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
descCurrent = descCurrent.next();
|
|
125
|
+
}
|
|
126
|
+
// Join doc blocks with a single newline and collapse multiple newlines
|
|
127
|
+
let docs = descriptionParts.join("\n");
|
|
128
|
+
docs = docs.replace(/\n{2,}/g, "\n").replace(/\n+$/g, "");
|
|
129
|
+
if (docs.toLowerCase().endsWith("see also:")) {
|
|
130
|
+
docs = docs.slice(0, -"see also:".length).trim();
|
|
131
|
+
}
|
|
132
|
+
if (seeAlsoLinks.length > 0) {
|
|
133
|
+
if (docs.length > 0) {
|
|
134
|
+
docs += "\n";
|
|
135
|
+
}
|
|
136
|
+
docs += `See also:\n* ${seeAlsoLinks.join("\n* ")}`;
|
|
137
|
+
}
|
|
138
|
+
builtins.push({
|
|
139
|
+
func,
|
|
140
|
+
signature,
|
|
141
|
+
docs,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
current = current.next();
|
|
146
|
+
}
|
|
147
|
+
if (!fs.existsSync(versionCacheDir)) {
|
|
148
|
+
fs.mkdirSync(versionCacheDir, { recursive: true });
|
|
149
|
+
}
|
|
150
|
+
fs.writeFileSync(outputPath, JSON.stringify(builtins, null, 2));
|
|
151
|
+
if (!isMcpMode)
|
|
152
|
+
console.log(`Extracted ${builtins.length} builtin functions to ${outputPath}`);
|
|
153
|
+
return builtins;
|
|
154
|
+
}
|
|
155
|
+
export default extractBuiltinFunctions;
|
package/dist/index.html
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Zig Documentation</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: monospace;
|
|
10
|
+
font-size: 14px;
|
|
11
|
+
margin: 20px;
|
|
12
|
+
}
|
|
13
|
+
#search {
|
|
14
|
+
width: 100%;
|
|
15
|
+
padding: 10px;
|
|
16
|
+
margin-bottom: 20px;
|
|
17
|
+
border: 1px solid #555;
|
|
18
|
+
font-family: monospace;
|
|
19
|
+
}
|
|
20
|
+
#content {
|
|
21
|
+
white-space: pre-wrap;
|
|
22
|
+
line-height: 1.4;
|
|
23
|
+
}
|
|
24
|
+
.hidden {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
27
|
+
#errors {
|
|
28
|
+
background: #400;
|
|
29
|
+
padding: 10px;
|
|
30
|
+
margin: 10px 0;
|
|
31
|
+
border: 1px solid #600;
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="Search documentation...">
|
|
37
|
+
<div id="content"></div>
|
|
38
|
+
<div id="errors" class="hidden">
|
|
39
|
+
<h1>Errors</h1>
|
|
40
|
+
<pre id="errorsText"></pre>
|
|
41
|
+
</div>
|
|
42
|
+
<script type="module">
|
|
43
|
+
import { startDocsViewer } from './std.js';
|
|
44
|
+
startDocsViewer();
|
|
45
|
+
</script>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|