zigsm 1.4.0 → 1.4.2
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 +24 -6
- package/dist/mcp.js +1 -1
- package/dist/std.js +222 -4
- package/dist/tools.js +12 -1
- package/dist/voyage.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ It uses the same approach as Zig's official autodoc (ziglang.org) by reading STD
|
|
|
12
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
13
|
|
|
14
14
|
> [!TIP]
|
|
15
|
-
> Add `use
|
|
15
|
+
> Add `use zsm` to your prompt if you want to explicitly instruct the LLM to use zsm tools. Otherwise, LLM will automatically decide when to utilize MCP tools based on the context of your questions.
|
|
16
16
|
|
|
17
17
|
<p align="center" width="100%">
|
|
18
18
|
<img src="https://raw.githubusercontent.com/zig-wasm/.github/refs/heads/main/static/readme_mcp_1.gif" width="49%" />
|
|
@@ -82,7 +82,7 @@ When using `--doc-source remote`, documentation is fetched from ziglang.org and
|
|
|
82
82
|
|
|
83
83
|
Optional environment variables:
|
|
84
84
|
- `VOYAGE_API_KEY` - Enables embeddings for doc search when set.
|
|
85
|
-
- `VOYAGE_MODEL` - Embedding model override (default: `voyage-3
|
|
85
|
+
- `VOYAGE_MODEL` - Embedding model override (default: `voyage-code-3`).
|
|
86
86
|
|
|
87
87
|
Embeddings are cached per Zig version, doc source, and model in the same cache directory used for docs.
|
|
88
88
|
|
|
@@ -117,13 +117,19 @@ Add the JSON configuration below to your MCP settings file.
|
|
|
117
117
|
|
|
118
118
|
### JSON Configuration Template
|
|
119
119
|
|
|
120
|
+
To enable semantic search with Voyage embeddings, include an `env` block like shown below.
|
|
121
|
+
|
|
120
122
|
**Node.js:**
|
|
121
123
|
```json
|
|
122
124
|
{
|
|
123
125
|
"mcpServers": {
|
|
124
126
|
"zsm": {
|
|
125
127
|
"command": "npx",
|
|
126
|
-
"args": ["-y", "zigsm@latest"]
|
|
128
|
+
"args": ["-y", "zigsm@latest"],
|
|
129
|
+
"env": {
|
|
130
|
+
"VOYAGE_API_KEY": "your_voyage_api_key",
|
|
131
|
+
"VOYAGE_MODEL": "voyage-code-3"
|
|
132
|
+
}
|
|
127
133
|
}
|
|
128
134
|
}
|
|
129
135
|
}
|
|
@@ -135,7 +141,11 @@ Add the JSON configuration below to your MCP settings file.
|
|
|
135
141
|
"mcpServers": {
|
|
136
142
|
"zsm": {
|
|
137
143
|
"command": "bunx",
|
|
138
|
-
"args": ["zigsm@latest"]
|
|
144
|
+
"args": ["zigsm@latest"],
|
|
145
|
+
"env": {
|
|
146
|
+
"VOYAGE_API_KEY": "your_voyage_api_key",
|
|
147
|
+
"VOYAGE_MODEL": "voyage-code-3"
|
|
148
|
+
}
|
|
139
149
|
}
|
|
140
150
|
}
|
|
141
151
|
}
|
|
@@ -161,7 +171,11 @@ claude mcp add zsm -- bunx zigsm@latest --doc-source remote --version 0.14.1
|
|
|
161
171
|
"mcpServers": {
|
|
162
172
|
"zsm": {
|
|
163
173
|
"command": "npx",
|
|
164
|
-
"args": ["-y", "zigsm@latest", "--doc-source", "remote", "--version", "master"]
|
|
174
|
+
"args": ["-y", "zigsm@latest", "--doc-source", "remote", "--version", "master"],
|
|
175
|
+
"env": {
|
|
176
|
+
"VOYAGE_API_KEY": "your_voyage_api_key",
|
|
177
|
+
"VOYAGE_MODEL": "voyage-code-3"
|
|
178
|
+
}
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
}
|
|
@@ -173,7 +187,11 @@ claude mcp add zsm -- bunx zigsm@latest --doc-source remote --version 0.14.1
|
|
|
173
187
|
"mcpServers": {
|
|
174
188
|
"zsm": {
|
|
175
189
|
"command": "bunx",
|
|
176
|
-
"args": ["zigsm@latest", "--doc-source", "remote", "--version", "0.14.1"]
|
|
190
|
+
"args": ["zigsm@latest", "--doc-source", "remote", "--version", "0.14.1"],
|
|
191
|
+
"env": {
|
|
192
|
+
"VOYAGE_API_KEY": "your_voyage_api_key",
|
|
193
|
+
"VOYAGE_MODEL": "voyage-code-3"
|
|
194
|
+
}
|
|
177
195
|
}
|
|
178
196
|
}
|
|
179
197
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -103,7 +103,7 @@ async function main() {
|
|
|
103
103
|
const builtinFunctions = await ensureDocs(options.version, options.updatePolicy, true, options.docSource);
|
|
104
104
|
const stdSources = await downloadSourcesTar(options.version, true, false, options.docSource);
|
|
105
105
|
const mcpServer = new McpServer({
|
|
106
|
-
name: "
|
|
106
|
+
name: "ZSM",
|
|
107
107
|
description: "Retrieves up-to-date documentation for the Zig programming language standard library and builtin functions.",
|
|
108
108
|
version: options.version,
|
|
109
109
|
});
|
package/dist/std.js
CHANGED
|
@@ -741,6 +741,38 @@ function setInputString(s) {
|
|
|
741
741
|
wasmArray.set(jsArray);
|
|
742
742
|
}
|
|
743
743
|
let inMemoryEmbeddingCache = null;
|
|
744
|
+
let cachedSourceTextIndex = null;
|
|
745
|
+
let cachedSourceTextRef = null;
|
|
746
|
+
function countWordHits(text, words) {
|
|
747
|
+
let hits = 0;
|
|
748
|
+
for (const word of words) {
|
|
749
|
+
if (text.includes(word))
|
|
750
|
+
hits++;
|
|
751
|
+
}
|
|
752
|
+
return hits;
|
|
753
|
+
}
|
|
754
|
+
function scoreDeclMatch(decl, queryLower, queryWords) {
|
|
755
|
+
const nameLower = decl.name.toLowerCase();
|
|
756
|
+
const pathLower = decl.path.toLowerCase();
|
|
757
|
+
let score = 0;
|
|
758
|
+
if (nameLower === queryLower)
|
|
759
|
+
score += 1000;
|
|
760
|
+
else if (nameLower.startsWith(queryLower))
|
|
761
|
+
score += 700;
|
|
762
|
+
else if (nameLower.includes(queryLower))
|
|
763
|
+
score += 450;
|
|
764
|
+
if (pathLower.includes(queryLower))
|
|
765
|
+
score += 150;
|
|
766
|
+
if (score === 0 && queryWords.length > 0) {
|
|
767
|
+
const searchable = `${nameLower} ${decl.preview.toLowerCase()} ${pathLower}`;
|
|
768
|
+
const wordHits = countWordHits(searchable, queryWords);
|
|
769
|
+
if (wordHits === queryWords.length)
|
|
770
|
+
score += 300;
|
|
771
|
+
else if (wordHits > 0)
|
|
772
|
+
score += wordHits * 80;
|
|
773
|
+
}
|
|
774
|
+
return score;
|
|
775
|
+
}
|
|
744
776
|
async function initWasmRuntime(wasmPath, stdSources) {
|
|
745
777
|
const fs = await import("node:fs");
|
|
746
778
|
const wasmBytes = typeof wasmPath === "string" ? fs.readFileSync(wasmPath) : wasmPath;
|
|
@@ -813,6 +845,169 @@ function stripMarkdown(input) {
|
|
|
813
845
|
.replace(/\s+/g, " ")
|
|
814
846
|
.trim();
|
|
815
847
|
}
|
|
848
|
+
function parseTarOctal(raw) {
|
|
849
|
+
const cleaned = raw.replace(/\0/g, "").trim();
|
|
850
|
+
if (!cleaned)
|
|
851
|
+
return 0;
|
|
852
|
+
const parsed = Number.parseInt(cleaned, 8);
|
|
853
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
854
|
+
}
|
|
855
|
+
function parseSourcesTar(stdSources) {
|
|
856
|
+
const files = [];
|
|
857
|
+
let offset = 0;
|
|
858
|
+
while (offset + 512 <= stdSources.length) {
|
|
859
|
+
const header = stdSources.subarray(offset, offset + 512);
|
|
860
|
+
let emptyHeader = true;
|
|
861
|
+
for (let i = 0; i < 512; i++) {
|
|
862
|
+
if (header[i] !== 0) {
|
|
863
|
+
emptyHeader = false;
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (emptyHeader)
|
|
868
|
+
break;
|
|
869
|
+
const name = text_decoder.decode(header.subarray(0, 100)).replace(/\0.*$/, "").trim();
|
|
870
|
+
const sizeRaw = text_decoder
|
|
871
|
+
.decode(header.subarray(124, 136))
|
|
872
|
+
.replace(/\0.*$/, "")
|
|
873
|
+
.trim();
|
|
874
|
+
const prefix = text_decoder
|
|
875
|
+
.decode(header.subarray(345, 500))
|
|
876
|
+
.replace(/\0.*$/, "")
|
|
877
|
+
.trim();
|
|
878
|
+
const size = parseTarOctal(sizeRaw);
|
|
879
|
+
const fullPath = prefix.length > 0 ? `${prefix}/${name}` : name;
|
|
880
|
+
const contentStart = offset + 512;
|
|
881
|
+
const contentEnd = contentStart + size;
|
|
882
|
+
if (size > 0 && contentEnd <= stdSources.length && fullPath.endsWith(".zig")) {
|
|
883
|
+
const text = text_decoder.decode(stdSources.subarray(contentStart, contentEnd));
|
|
884
|
+
files.push({ path: fullPath, text, lowerText: text.toLowerCase() });
|
|
885
|
+
}
|
|
886
|
+
const paddedSize = Math.ceil(size / 512) * 512;
|
|
887
|
+
offset = contentStart + paddedSize;
|
|
888
|
+
}
|
|
889
|
+
return files;
|
|
890
|
+
}
|
|
891
|
+
function buildSourceTextIndex(stdSources) {
|
|
892
|
+
if (cachedSourceTextIndex && cachedSourceTextRef === stdSources) {
|
|
893
|
+
return cachedSourceTextIndex;
|
|
894
|
+
}
|
|
895
|
+
const files = parseSourcesTar(stdSources);
|
|
896
|
+
const decls = [];
|
|
897
|
+
const declRegex = /^\s*(?:pub\s+)?(?:const|var|fn)\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
898
|
+
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
899
|
+
const file = files[fileIndex];
|
|
900
|
+
const lines = file.text.split("\n");
|
|
901
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
902
|
+
const line = lines[lineIndex];
|
|
903
|
+
const match = line.match(declRegex);
|
|
904
|
+
if (!match)
|
|
905
|
+
continue;
|
|
906
|
+
decls.push({
|
|
907
|
+
name: match[1],
|
|
908
|
+
path: file.path,
|
|
909
|
+
line: lineIndex + 1,
|
|
910
|
+
preview: line.trim(),
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
cachedSourceTextRef = stdSources;
|
|
915
|
+
cachedSourceTextIndex = { files, decls };
|
|
916
|
+
return cachedSourceTextIndex;
|
|
917
|
+
}
|
|
918
|
+
function fallbackSearchStdLib(stdSources, query, limit) {
|
|
919
|
+
const index = buildSourceTextIndex(stdSources);
|
|
920
|
+
const queryLower = query.toLowerCase().trim();
|
|
921
|
+
const queryWords = queryLower.split(/\s+/).filter(Boolean);
|
|
922
|
+
const declScored = index.decls
|
|
923
|
+
.map((decl) => ({ decl, score: scoreDeclMatch(decl, queryLower, queryWords) }))
|
|
924
|
+
.filter((item) => item.score > 0)
|
|
925
|
+
.sort((a, b) => b.score - a.score);
|
|
926
|
+
const contentMatches = [];
|
|
927
|
+
if (declScored.length < limit) {
|
|
928
|
+
for (const file of index.files) {
|
|
929
|
+
const lines = file.text.split("\n");
|
|
930
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
931
|
+
const wordHits = countWordHits(lines[lineIndex].toLowerCase(), queryWords);
|
|
932
|
+
if (wordHits === queryWords.length) {
|
|
933
|
+
contentMatches.push({
|
|
934
|
+
path: file.path,
|
|
935
|
+
line: lineIndex + 1,
|
|
936
|
+
preview: lines[lineIndex].trim().slice(0, 120),
|
|
937
|
+
score: 200 + wordHits * 30,
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
if (contentMatches.length >= limit * 3)
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
943
|
+
if (contentMatches.length >= limit * 3)
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
contentMatches.sort((a, b) => b.score - a.score);
|
|
947
|
+
}
|
|
948
|
+
let markdown = `# Search Results\n\nQuery: "${query}"\n\n`;
|
|
949
|
+
markdown += `_Fallback mode: text index (parser-incompatible Zig version)_\n\n`;
|
|
950
|
+
if (declScored.length === 0 && contentMatches.length === 0) {
|
|
951
|
+
markdown += "No results found.";
|
|
952
|
+
return markdown;
|
|
953
|
+
}
|
|
954
|
+
if (declScored.length > 0) {
|
|
955
|
+
const limited = declScored.slice(0, limit);
|
|
956
|
+
markdown += `Found ${declScored.length} declaration matches (showing ${limited.length}):\n\n`;
|
|
957
|
+
for (let i = 0; i < limited.length; i++) {
|
|
958
|
+
const entry = limited[i].decl;
|
|
959
|
+
markdown += `- std.${entry.name} (${entry.path}:${entry.line})\n`;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (contentMatches.length > 0 && declScored.length < limit) {
|
|
963
|
+
const remaining = limit - declScored.length;
|
|
964
|
+
const limited = contentMatches.slice(0, remaining);
|
|
965
|
+
markdown += `\nContent matches:\n\n`;
|
|
966
|
+
for (let i = 0; i < limited.length; i++) {
|
|
967
|
+
const entry = limited[i];
|
|
968
|
+
markdown += `- ${entry.path}:${entry.line} — \`${entry.preview}\`\n`;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return markdown;
|
|
972
|
+
}
|
|
973
|
+
function fallbackGetStdLibItem(stdSources, name, getSourceFile) {
|
|
974
|
+
const index = buildSourceTextIndex(stdSources);
|
|
975
|
+
if (getSourceFile) {
|
|
976
|
+
const normalized = name.replace(/^src\//, "");
|
|
977
|
+
const matchedFile = index.files.find((file) => file.path === normalized) ||
|
|
978
|
+
index.files.find((file) => file.path.endsWith(`/${normalized}`)) ||
|
|
979
|
+
index.files.find((file) => file.path.endsWith(name));
|
|
980
|
+
if (!matchedFile) {
|
|
981
|
+
return `# Error\n\nCould not find source file for "${name}" in fallback mode.`;
|
|
982
|
+
}
|
|
983
|
+
return `# ${matchedFile.path}\n\n${matchedFile.text}`;
|
|
984
|
+
}
|
|
985
|
+
const target = name.split(".").pop()?.trim() || name.trim();
|
|
986
|
+
const targetLower = target.toLowerCase();
|
|
987
|
+
const ranked = index.decls
|
|
988
|
+
.map((decl) => ({ decl, score: scoreDeclMatch(decl, targetLower, [targetLower]) }))
|
|
989
|
+
.filter((item) => item.score > 0)
|
|
990
|
+
.sort((a, b) => b.score - a.score);
|
|
991
|
+
if (ranked.length === 0) {
|
|
992
|
+
return `# Error\n\nDeclaration "${name}" not found (fallback mode).`;
|
|
993
|
+
}
|
|
994
|
+
const best = ranked[0].decl;
|
|
995
|
+
const file = index.files.find((entry) => entry.path === best.path);
|
|
996
|
+
if (!file) {
|
|
997
|
+
return `# Error\n\nDeclaration "${name}" matched, but source file could not be loaded.`;
|
|
998
|
+
}
|
|
999
|
+
const lines = file.text.split("\n");
|
|
1000
|
+
const startLine = Math.max(1, best.line - 20);
|
|
1001
|
+
const endLine = Math.min(lines.length, best.line + 80);
|
|
1002
|
+
const snippet = lines.slice(startLine - 1, endLine).join("\n");
|
|
1003
|
+
let markdown = `# ${name}\n\n`;
|
|
1004
|
+
markdown += `_Fallback mode: text index (parser-incompatible Zig version)_\n\n`;
|
|
1005
|
+
markdown += `Match: ${best.path}:${best.line}\n\n`;
|
|
1006
|
+
markdown += "```zig\n";
|
|
1007
|
+
markdown += snippet;
|
|
1008
|
+
markdown += "\n```\n";
|
|
1009
|
+
return markdown;
|
|
1010
|
+
}
|
|
816
1011
|
function buildDeclEmbeddingText(decl) {
|
|
817
1012
|
const category = wasm_exports.categorize_decl(decl, 0);
|
|
818
1013
|
const fqn = fullyQualifiedName(decl);
|
|
@@ -832,6 +1027,14 @@ function buildDeclEmbeddingText(decl) {
|
|
|
832
1027
|
const text = stripMarkdown(`${fqn}\n${name}\n${proto}\n${typeInfo}\n${docs}\n${source}`).slice(0, 4000);
|
|
833
1028
|
return { decl, fqn, text };
|
|
834
1029
|
}
|
|
1030
|
+
function tryBuildDeclEmbeddingText(decl) {
|
|
1031
|
+
try {
|
|
1032
|
+
return buildDeclEmbeddingText(decl);
|
|
1033
|
+
}
|
|
1034
|
+
catch {
|
|
1035
|
+
return null;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
835
1038
|
async function getEmbeddingCachePath(zigVersion, docSource, model) {
|
|
836
1039
|
const envPathsMod = await import("env-paths");
|
|
837
1040
|
const path = await import("node:path");
|
|
@@ -891,9 +1094,14 @@ async function getOrBuildEmbeddingCache(wasmPath, stdSources, options) {
|
|
|
891
1094
|
inMemoryEmbeddingCache = existing;
|
|
892
1095
|
return existing;
|
|
893
1096
|
}
|
|
894
|
-
await initWasmRuntime(wasmPath, stdSources);
|
|
895
1097
|
const decls = collectDeclsForEmbeddings();
|
|
896
|
-
const docs =
|
|
1098
|
+
const docs = [];
|
|
1099
|
+
for (let i = 0; i < decls.length; i++) {
|
|
1100
|
+
const doc = tryBuildDeclEmbeddingText(decls[i]);
|
|
1101
|
+
if (doc && doc.text.length > 0) {
|
|
1102
|
+
docs.push(doc);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
897
1105
|
if (docs.length === 0) {
|
|
898
1106
|
return null;
|
|
899
1107
|
}
|
|
@@ -929,7 +1137,12 @@ function buildHybridRanking(lexicalDecls, semanticDecls, maxResults) {
|
|
|
929
1137
|
.map(([decl]) => decl);
|
|
930
1138
|
}
|
|
931
1139
|
export async function searchStdLib(wasmPath, stdSources, query, limit = 20, options = {}) {
|
|
932
|
-
|
|
1140
|
+
try {
|
|
1141
|
+
await initWasmRuntime(wasmPath, stdSources);
|
|
1142
|
+
}
|
|
1143
|
+
catch {
|
|
1144
|
+
return fallbackSearchStdLib(stdSources, query, limit);
|
|
1145
|
+
}
|
|
933
1146
|
const ignoreCase = query.toLowerCase() === query;
|
|
934
1147
|
const lexicalResults = Array.from(executeQuery(query, ignoreCase));
|
|
935
1148
|
let mergedResults = lexicalResults;
|
|
@@ -968,7 +1181,12 @@ export async function searchStdLib(wasmPath, stdSources, query, limit = 20, opti
|
|
|
968
1181
|
return markdown;
|
|
969
1182
|
}
|
|
970
1183
|
export async function getStdLibItem(wasmPath, stdSources, name, getSourceFile = false) {
|
|
971
|
-
|
|
1184
|
+
try {
|
|
1185
|
+
await initWasmRuntime(wasmPath, stdSources);
|
|
1186
|
+
}
|
|
1187
|
+
catch {
|
|
1188
|
+
return fallbackGetStdLibItem(stdSources, name, getSourceFile);
|
|
1189
|
+
}
|
|
972
1190
|
const exports = wasm_exports;
|
|
973
1191
|
const decl_index = findDecl(name);
|
|
974
1192
|
if (decl_index === null) {
|
package/dist/tools.js
CHANGED
|
@@ -67,15 +67,26 @@ function getBuiltinFunctionTool(builtinFunctions) {
|
|
|
67
67
|
],
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
+
const queryBare = queryLower.replace(/@/g, "");
|
|
71
|
+
const queryWords = queryBare.split(/\s+/).filter(Boolean);
|
|
70
72
|
const scoredFunctions = builtinFunctions.map((fn) => {
|
|
71
73
|
const funcLower = fn.func.toLowerCase();
|
|
74
|
+
const funcBare = funcLower.replace(/^@/, "");
|
|
72
75
|
let score = 0;
|
|
73
|
-
if (funcLower === queryLower)
|
|
76
|
+
if (funcLower === queryLower || funcBare === queryBare)
|
|
74
77
|
score += 1000;
|
|
75
78
|
else if (funcLower.startsWith(queryLower))
|
|
76
79
|
score += 500;
|
|
77
80
|
else if (funcLower.includes(queryLower))
|
|
78
81
|
score += 300;
|
|
82
|
+
if (score === 0 && queryWords.length > 0) {
|
|
83
|
+
const searchable = `${funcBare} ${fn.docs.toLowerCase()} ${fn.signature.toLowerCase()}`;
|
|
84
|
+
const wordHits = queryWords.filter((w) => searchable.includes(w)).length;
|
|
85
|
+
if (wordHits === queryWords.length)
|
|
86
|
+
score += 200;
|
|
87
|
+
else if (wordHits > 0)
|
|
88
|
+
score += wordHits * 60;
|
|
89
|
+
}
|
|
79
90
|
if (score > 0)
|
|
80
91
|
score += Math.max(0, 50 - fn.func.length);
|
|
81
92
|
return { ...fn, score };
|
package/dist/voyage.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zigsm",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"zsm": "dist/mcp.js"
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
"description": "MCP server that provides up-to-date documentation for the Zig programming language standard library and builtin functions.",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/
|
|
27
|
+
"url": "git+https://github.com/pokeylooted/zsm.git"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"mcp",
|
|
31
31
|
"modelcontextprotocol",
|
|
32
32
|
"zig",
|
|
33
33
|
"ziglang",
|
|
34
|
-
"
|
|
34
|
+
"zsm",
|
|
35
35
|
"cli"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|