codex-skills-mcp 1.0.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.
Files changed (46) hide show
  1. package/README.md +221 -0
  2. package/dist/config.d.ts +22 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +95 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/index.d.ts +20 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +160 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/loader/index.d.ts +68 -0
  11. package/dist/loader/index.d.ts.map +1 -0
  12. package/dist/loader/index.js +246 -0
  13. package/dist/loader/index.js.map +1 -0
  14. package/dist/remote/github.d.ts +14 -0
  15. package/dist/remote/github.d.ts.map +1 -0
  16. package/dist/remote/github.js +90 -0
  17. package/dist/remote/github.js.map +1 -0
  18. package/dist/search/index.d.ts +45 -0
  19. package/dist/search/index.d.ts.map +1 -0
  20. package/dist/search/index.js +149 -0
  21. package/dist/search/index.js.map +1 -0
  22. package/dist/tools/list-categories.d.ts +4 -0
  23. package/dist/tools/list-categories.d.ts.map +1 -0
  24. package/dist/tools/list-categories.js +18 -0
  25. package/dist/tools/list-categories.js.map +1 -0
  26. package/dist/tools/list-skill-files.d.ts +5 -0
  27. package/dist/tools/list-skill-files.d.ts.map +1 -0
  28. package/dist/tools/list-skill-files.js +61 -0
  29. package/dist/tools/list-skill-files.js.map +1 -0
  30. package/dist/tools/load-skill-file.d.ts +5 -0
  31. package/dist/tools/load-skill-file.d.ts.map +1 -0
  32. package/dist/tools/load-skill-file.js +41 -0
  33. package/dist/tools/load-skill-file.js.map +1 -0
  34. package/dist/tools/plan-workflow.d.ts +4 -0
  35. package/dist/tools/plan-workflow.d.ts.map +1 -0
  36. package/dist/tools/plan-workflow.js +53 -0
  37. package/dist/tools/plan-workflow.js.map +1 -0
  38. package/dist/tools/read-skill.d.ts +5 -0
  39. package/dist/tools/read-skill.d.ts.map +1 -0
  40. package/dist/tools/read-skill.js +100 -0
  41. package/dist/tools/read-skill.js.map +1 -0
  42. package/dist/tools/search-skills.d.ts +4 -0
  43. package/dist/tools/search-skills.d.ts.map +1 -0
  44. package/dist/tools/search-skills.js +32 -0
  45. package/dist/tools/search-skills.js.map +1 -0
  46. package/package.json +39 -0
@@ -0,0 +1,246 @@
1
+ import { readFileSync, readdirSync, statSync, existsSync } from "node:fs";
2
+ import { resolve, join, relative, extname } from "node:path";
3
+ import { ensureSkillFetched } from "../remote/github.js";
4
+ // Files/dirs to skip when scanning structure
5
+ const SKIP_NAMES = new Set([
6
+ "node_modules", ".git", ".github", "__pycache__",
7
+ ".DS_Store", ".env", ".venv", "venv",
8
+ "dist", "build", ".next", ".cache",
9
+ ]);
10
+ // Max file size to return via load_skill_file (500KB)
11
+ const MAX_FILE_SIZE = 500 * 1024;
12
+ /**
13
+ * Skill loader — reads SKILL.md, scans structure, detects dependencies.
14
+ */
15
+ export class SkillLoader {
16
+ config;
17
+ manifest;
18
+ skillsDir;
19
+ constructor(config, manifest) {
20
+ this.config = config;
21
+ this.manifest = manifest;
22
+ this.skillsDir = config.skillsDir;
23
+ }
24
+ /**
25
+ * Resolve the absolute filesystem path for a skill.
26
+ */
27
+ resolveSkillPath(entry) {
28
+ // relative_path starts with "./" and is relative to the manifest dir
29
+ const relPath = entry.relative_path.replace(/^\.\//, "");
30
+ return resolve(this.skillsDir, relPath);
31
+ }
32
+ /**
33
+ * Read a skill's SKILL.md + structure + dependencies.
34
+ */
35
+ async readSkill(entry) {
36
+ if (this.config.isRemote) {
37
+ await ensureSkillFetched(this.config, entry);
38
+ }
39
+ const skillPath = this.resolveSkillPath(entry);
40
+ // Read SKILL.md
41
+ let instructions = "";
42
+ const skillMdPath = join(skillPath, "SKILL.md");
43
+ if (existsSync(skillMdPath)) {
44
+ instructions = readFileSync(skillMdPath, "utf-8");
45
+ }
46
+ else {
47
+ instructions = `[No SKILL.md found for ${entry.name}]\n\nDescription: ${entry.description}`;
48
+ }
49
+ // Scan structure
50
+ const structure = this.scanStructure(skillPath, entry);
51
+ // Detect dependencies
52
+ const dependencies = this.detectDependencies(skillPath);
53
+ return { instructions, structure, dependencies };
54
+ }
55
+ /**
56
+ * Scan the file tree of a skill (max depth 2 by default).
57
+ */
58
+ scanStructure(dirPath, entry, maxDepth = 2) {
59
+ const files = this.scanDirectory(dirPath, 0, maxDepth);
60
+ // Find sub-skills: look for nested SKILL.md files
61
+ const subSkills = [];
62
+ this.findSubSkills(dirPath, dirPath, subSkills);
63
+ return { files, sub_skills: subSkills };
64
+ }
65
+ scanDirectory(dirPath, currentDepth, maxDepth) {
66
+ if (!existsSync(dirPath))
67
+ return [];
68
+ const nodes = [];
69
+ try {
70
+ const entries = readdirSync(dirPath, { withFileTypes: true });
71
+ for (const entry of entries) {
72
+ if (SKIP_NAMES.has(entry.name))
73
+ continue;
74
+ if (entry.name.startsWith(".") && entry.name !== ".env.example")
75
+ continue;
76
+ const fullPath = join(dirPath, entry.name);
77
+ if (entry.isDirectory()) {
78
+ const node = {
79
+ name: entry.name,
80
+ type: "directory",
81
+ };
82
+ if (currentDepth < maxDepth) {
83
+ node.children = this.scanDirectory(fullPath, currentDepth + 1, maxDepth);
84
+ }
85
+ nodes.push(node);
86
+ }
87
+ else if (entry.isFile()) {
88
+ try {
89
+ const stat = statSync(fullPath);
90
+ nodes.push({
91
+ name: entry.name,
92
+ type: "file",
93
+ size_bytes: stat.size,
94
+ });
95
+ }
96
+ catch {
97
+ nodes.push({ name: entry.name, type: "file" });
98
+ }
99
+ }
100
+ }
101
+ }
102
+ catch {
103
+ // Permission denied or other errors
104
+ }
105
+ return nodes;
106
+ }
107
+ /**
108
+ * Recursively find sub-skill SKILL.md files (not the root one).
109
+ */
110
+ findSubSkills(rootPath, currentPath, results, depth = 0) {
111
+ if (depth > 4)
112
+ return;
113
+ try {
114
+ const entries = readdirSync(currentPath, { withFileTypes: true });
115
+ for (const entry of entries) {
116
+ if (!entry.isDirectory())
117
+ continue;
118
+ if (SKIP_NAMES.has(entry.name))
119
+ continue;
120
+ const subDir = join(currentPath, entry.name);
121
+ const subSkillMd = join(subDir, "SKILL.md");
122
+ if (existsSync(subSkillMd)) {
123
+ // Extract name from frontmatter or use folder name
124
+ const content = readFileSync(subSkillMd, "utf-8");
125
+ const nameMatch = content.match(/^---[\s\S]*?name:\s*(.+?)$/m);
126
+ const descMatch = content.match(/^---[\s\S]*?description:\s*(.+?)$/m);
127
+ results.push({
128
+ name: nameMatch ? nameMatch[1].trim() : entry.name,
129
+ path: relative(rootPath, subDir),
130
+ description: descMatch ? descMatch[1].trim() : "",
131
+ });
132
+ }
133
+ // Recurse deeper
134
+ this.findSubSkills(rootPath, subDir, results, depth + 1);
135
+ }
136
+ }
137
+ catch {
138
+ // Permission denied
139
+ }
140
+ }
141
+ /**
142
+ * Detect language and package manager from project files.
143
+ */
144
+ detectDependencies(skillPath) {
145
+ // Python
146
+ if (existsSync(join(skillPath, "requirements.txt"))) {
147
+ return {
148
+ language: "python",
149
+ package_manager: "pip",
150
+ setup_command: "pip install -r requirements.txt",
151
+ };
152
+ }
153
+ if (existsSync(join(skillPath, "pyproject.toml"))) {
154
+ if (existsSync(join(skillPath, "uv.lock"))) {
155
+ return {
156
+ language: "python",
157
+ package_manager: "uv",
158
+ setup_command: "uv sync",
159
+ };
160
+ }
161
+ return {
162
+ language: "python",
163
+ package_manager: "pip",
164
+ setup_command: "pip install .",
165
+ };
166
+ }
167
+ // Node.js
168
+ if (existsSync(join(skillPath, "package.json"))) {
169
+ return {
170
+ language: "typescript/javascript",
171
+ package_manager: "npm",
172
+ setup_command: "npm install",
173
+ };
174
+ }
175
+ // Go
176
+ if (existsSync(join(skillPath, "go.mod"))) {
177
+ return {
178
+ language: "go",
179
+ package_manager: "go mod",
180
+ setup_command: "go mod download",
181
+ };
182
+ }
183
+ // Pure skill (no runtime dependencies)
184
+ return { language: "markdown-only" };
185
+ }
186
+ /**
187
+ * Load a specific file from within a skill directory.
188
+ */
189
+ async loadSkillFile(entry, filePath) {
190
+ if (this.config.isRemote) {
191
+ await ensureSkillFetched(this.config, entry);
192
+ }
193
+ const skillPath = this.resolveSkillPath(entry);
194
+ const fullPath = resolve(skillPath, filePath);
195
+ // Security: ensure the resolved path is within the skill directory
196
+ if (!fullPath.startsWith(skillPath)) {
197
+ throw new Error("Path traversal detected: file must be within skill directory");
198
+ }
199
+ if (!existsSync(fullPath)) {
200
+ throw new Error(`File not found: ${filePath}`);
201
+ }
202
+ const stat = statSync(fullPath);
203
+ if (stat.isDirectory()) {
204
+ throw new Error(`Path is a directory, not a file: ${filePath}`);
205
+ }
206
+ if (stat.size > MAX_FILE_SIZE) {
207
+ throw new Error(`File too large (${Math.round(stat.size / 1024)}KB). Max: ${MAX_FILE_SIZE / 1024}KB`);
208
+ }
209
+ // Check if binary
210
+ const ext = extname(fullPath).toLowerCase();
211
+ const binaryExts = new Set([
212
+ ".png", ".jpg", ".jpeg", ".gif", ".ico", ".webp",
213
+ ".woff", ".woff2", ".ttf", ".eot",
214
+ ".zip", ".7z", ".tar", ".gz",
215
+ ".exe", ".dll", ".so", ".dylib",
216
+ ".pdf", ".doc", ".docx",
217
+ ]);
218
+ if (binaryExts.has(ext)) {
219
+ return {
220
+ content: `[Binary file: ${filePath} (${stat.size} bytes)]`,
221
+ size_bytes: stat.size,
222
+ };
223
+ }
224
+ const content = readFileSync(fullPath, "utf-8");
225
+ return { content, size_bytes: stat.size };
226
+ }
227
+ /**
228
+ * List files in a subdirectory of a skill.
229
+ */
230
+ async listSkillFiles(entry, subPath = "", maxDepth = 2) {
231
+ if (this.config.isRemote) {
232
+ await ensureSkillFetched(this.config, entry);
233
+ }
234
+ const skillPath = this.resolveSkillPath(entry);
235
+ const targetPath = subPath ? resolve(skillPath, subPath) : skillPath;
236
+ // Security check
237
+ if (!targetPath.startsWith(skillPath)) {
238
+ throw new Error("Path traversal detected");
239
+ }
240
+ if (!existsSync(targetPath)) {
241
+ throw new Error(`Path not found: ${subPath || "."}`);
242
+ }
243
+ return this.scanDirectory(targetPath, 0, maxDepth);
244
+ }
245
+ }
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loader/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Config, ManifestEntry } from "../config.js";
2
+ /**
3
+ * Initialize remote caching by fetching the repository tree
4
+ */
5
+ export declare function initRemote(config: Config): Promise<void>;
6
+ /**
7
+ * Download the manifest file
8
+ */
9
+ export declare function fetchManifest(config: Config): Promise<void>;
10
+ /**
11
+ * Ensure a skill's directory is downloaded and cached
12
+ */
13
+ export declare function ensureSkillFetched(config: Config, entry: ManifestEntry): Promise<void>;
14
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/remote/github.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAiC1D;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB9D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD5F"}
@@ -0,0 +1,90 @@
1
+ import { writeFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ /**
4
+ * Fetch with optional auth token
5
+ */
6
+ async function fetchWithAuth(url, token) {
7
+ const headers = {};
8
+ if (token) {
9
+ headers["Authorization"] = `Bearer ${token}`;
10
+ }
11
+ const res = await fetch(url, { headers });
12
+ if (!res.ok) {
13
+ throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
14
+ }
15
+ return res;
16
+ }
17
+ /**
18
+ * Initialize remote caching by fetching the repository tree
19
+ */
20
+ export async function initRemote(config) {
21
+ const treePath = resolve(config.skillsDir, "tree.json");
22
+ if (existsSync(treePath)) {
23
+ return; // Already initialized in this run
24
+ }
25
+ const url = `https://api.github.com/repos/${config.githubRepo}/git/trees/${config.githubBranch}?recursive=1`;
26
+ console.error(`[codex-skills-mcp] Fetching repo tree from ${url}...`);
27
+ const res = await fetchWithAuth(url, config.githubToken);
28
+ const data = (await res.json());
29
+ if (data.truncated) {
30
+ console.error("[codex-skills-mcp] Warning: GitHub tree is truncated, some files may be missing.");
31
+ }
32
+ writeFileSync(treePath, JSON.stringify(data.tree, null, 2), "utf-8");
33
+ console.error(`[codex-skills-mcp] Cached ${data.tree.length} tree items.`);
34
+ }
35
+ /**
36
+ * Download the manifest file
37
+ */
38
+ export async function fetchManifest(config) {
39
+ if (existsSync(config.manifestPath))
40
+ return;
41
+ const rawUrl = `https://raw.githubusercontent.com/${config.githubRepo}/${config.githubBranch}/${config.githubPath}/skills_manifest.json`;
42
+ console.error(`[codex-skills-mcp] Fetching manifest from ${rawUrl}...`);
43
+ const res = await fetchWithAuth(rawUrl, config.githubToken);
44
+ const text = await res.text();
45
+ writeFileSync(config.manifestPath, text, "utf-8");
46
+ }
47
+ /**
48
+ * Ensure a skill's directory is downloaded and cached
49
+ */
50
+ export async function ensureSkillFetched(config, entry) {
51
+ const relPath = entry.relative_path.replace(/^\.\//, "");
52
+ const skillLocalPath = resolve(config.skillsDir, relPath);
53
+ if (existsSync(skillLocalPath)) {
54
+ return; // Already fetched
55
+ }
56
+ const treePath = resolve(config.skillsDir, "tree.json");
57
+ if (!existsSync(treePath)) {
58
+ throw new Error("Tree cache not found, run initRemote first.");
59
+ }
60
+ const tree = JSON.parse(readFileSync(treePath, "utf-8"));
61
+ // The prefix in github is githubPath + relPath
62
+ // e.g. "codex-skills/01_代码工程与架构/planning-with-files"
63
+ let prefix = config.githubPath ? `${config.githubPath}/${relPath}` : relPath;
64
+ if (prefix.startsWith("/"))
65
+ prefix = prefix.slice(1);
66
+ if (prefix.startsWith("./"))
67
+ prefix = prefix.slice(2);
68
+ const filesToFetch = tree.filter((item) => item.type === "blob" && item.path.startsWith(prefix + "/"));
69
+ if (filesToFetch.length === 0) {
70
+ console.error(`[codex-skills-mcp] Warning: No files found in remote tree for prefix ${prefix}`);
71
+ // Create an empty dir to prevent repeated failures
72
+ mkdirSync(skillLocalPath, { recursive: true });
73
+ return;
74
+ }
75
+ console.error(`[codex-skills-mcp] Fetching ${filesToFetch.length} files for skill: ${entry.name}...`);
76
+ for (const file of filesToFetch) {
77
+ const rawUrl = `https://raw.githubusercontent.com/${config.githubRepo}/${config.githubBranch}/${file.path}`;
78
+ // Determine local path by stripping githubPath prefix
79
+ let relativeFilePath = file.path;
80
+ if (config.githubPath && relativeFilePath.startsWith(config.githubPath + "/")) {
81
+ relativeFilePath = relativeFilePath.substring(config.githubPath.length + 1);
82
+ }
83
+ const localFilePath = resolve(config.skillsDir, relativeFilePath);
84
+ mkdirSync(dirname(localFilePath), { recursive: true });
85
+ const res = await fetchWithAuth(rawUrl, config.githubToken);
86
+ const buffer = await res.arrayBuffer();
87
+ writeFileSync(localFilePath, Buffer.from(buffer));
88
+ }
89
+ }
90
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/remote/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { ManifestEntry } from "../config.js";
2
+ interface SearchResult {
3
+ name: string;
4
+ description: string;
5
+ category: string;
6
+ has_sub_skills: boolean;
7
+ score: number;
8
+ }
9
+ /**
10
+ * Lightweight keyword search engine with TF-IDF-like scoring.
11
+ * No external dependencies — pure string matching with Leading Words boosting.
12
+ */
13
+ export declare class SkillSearchEngine {
14
+ private index;
15
+ private categories;
16
+ constructor(manifest: ManifestEntry[]);
17
+ private buildIndex;
18
+ /**
19
+ * Extract Leading Words from description.
20
+ * Format: "...。Leading Words: word1, word2, word3"
21
+ */
22
+ private extractLeadingWords;
23
+ private tokenize;
24
+ /**
25
+ * Search skills by query string.
26
+ * Scoring: exact name match > Leading Words match > description token match
27
+ */
28
+ search(query: string, options?: {
29
+ category?: string;
30
+ limit?: number;
31
+ }): SearchResult[];
32
+ /**
33
+ * Get all categories with skill counts.
34
+ */
35
+ getCategories(): {
36
+ name: string;
37
+ skill_count: number;
38
+ }[];
39
+ /**
40
+ * Find a skill entry by exact name.
41
+ */
42
+ findByName(name: string): ManifestEntry | undefined;
43
+ }
44
+ export {};
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AASD;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,UAAU,CAAkC;gBAExC,QAAQ,EAAE,aAAa,EAAE;IAIrC,OAAO,CAAC,UAAU;IAiClB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,QAAQ;IA2BhB;;;OAGG;IACH,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAClD,YAAY,EAAE;IAiEjB;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE;IAMxD;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;CAMpD"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Lightweight keyword search engine with TF-IDF-like scoring.
3
+ * No external dependencies — pure string matching with Leading Words boosting.
4
+ */
5
+ export class SkillSearchEngine {
6
+ index = [];
7
+ categories = new Map();
8
+ constructor(manifest) {
9
+ this.buildIndex(manifest);
10
+ }
11
+ buildIndex(manifest) {
12
+ const categoryCounts = new Map();
13
+ for (const entry of manifest) {
14
+ // Skip the router entry itself
15
+ if (entry.name === "00_codex_skills")
16
+ continue;
17
+ // Extract Leading Words from description
18
+ const leadingWords = this.extractLeadingWords(entry.description);
19
+ // Tokenize all searchable fields
20
+ const allText = [
21
+ entry.name,
22
+ entry.description,
23
+ entry.category,
24
+ entry.folder,
25
+ ].join(" ");
26
+ const tokens = this.tokenize(allText);
27
+ // Detect if this is a sub-skill (path depth > 2 segments)
28
+ const pathSegments = entry.relative_path.split("/").filter(Boolean);
29
+ const isSubSkill = pathSegments.length > 3;
30
+ this.index.push({ entry, tokens, leadingWords, isSubSkill });
31
+ // Count categories
32
+ const count = categoryCounts.get(entry.category) || 0;
33
+ categoryCounts.set(entry.category, count + 1);
34
+ }
35
+ this.categories = categoryCounts;
36
+ }
37
+ /**
38
+ * Extract Leading Words from description.
39
+ * Format: "...。Leading Words: word1, word2, word3"
40
+ */
41
+ extractLeadingWords(description) {
42
+ const match = description.match(/Leading\s*Words?\s*[::]\s*(.+)$/i);
43
+ if (!match)
44
+ return [];
45
+ return match[1]
46
+ .split(/[,,、]/)
47
+ .map((w) => w.trim().toLowerCase())
48
+ .filter(Boolean);
49
+ }
50
+ tokenize(text) {
51
+ const raw = text
52
+ .toLowerCase()
53
+ .replace(/[^\w\u4e00-\u9fff\s-]/g, " ")
54
+ .split(/\s+/)
55
+ .filter((t) => t.length > 1);
56
+ // Generate CJK bigrams for better Chinese matching
57
+ const result = [...raw];
58
+ for (const token of raw) {
59
+ const cjkChars = token.match(/[\u4e00-\u9fff]/g);
60
+ if (cjkChars && cjkChars.length >= 2) {
61
+ // Generate bigrams: "前端性能" → ["前端", "端性", "性能"]
62
+ const cjkStr = cjkChars.join("");
63
+ for (let i = 0; i < cjkStr.length - 1; i++) {
64
+ result.push(cjkStr.substring(i, i + 2));
65
+ }
66
+ // Also add individual chars for broader matching
67
+ for (const c of cjkChars) {
68
+ result.push(c);
69
+ }
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ /**
75
+ * Search skills by query string.
76
+ * Scoring: exact name match > Leading Words match > description token match
77
+ */
78
+ search(query, options = {}) {
79
+ const { category, limit = 10 } = options;
80
+ const queryTokens = this.tokenize(query);
81
+ if (queryTokens.length === 0)
82
+ return [];
83
+ const results = [];
84
+ for (const item of this.index) {
85
+ // Category filter
86
+ if (category && item.entry.category !== category)
87
+ continue;
88
+ let score = 0;
89
+ // 1. Exact name match (highest priority)
90
+ const nameLower = item.entry.name.toLowerCase();
91
+ for (const qt of queryTokens) {
92
+ if (nameLower === qt || nameLower.includes(qt)) {
93
+ score += 10;
94
+ }
95
+ }
96
+ // 2. Leading Words match (high priority)
97
+ for (const qt of queryTokens) {
98
+ for (const lw of item.leadingWords) {
99
+ if (lw.includes(qt) || qt.includes(lw)) {
100
+ score += 5;
101
+ }
102
+ }
103
+ }
104
+ // 3. General token match
105
+ for (const qt of queryTokens) {
106
+ let tokenHits = 0;
107
+ for (const token of item.tokens) {
108
+ if (token.includes(qt) || qt.includes(token)) {
109
+ tokenHits++;
110
+ }
111
+ }
112
+ // Normalize by token count to avoid long descriptions always winning
113
+ score += tokenHits > 0 ? 1 + Math.log(tokenHits) : 0;
114
+ }
115
+ // 4. Sub-skills get a slight penalty to prefer top-level skills
116
+ if (item.isSubSkill) {
117
+ score *= 0.7;
118
+ }
119
+ if (score > 0) {
120
+ results.push({
121
+ name: item.entry.name,
122
+ description: item.entry.description,
123
+ category: item.entry.category,
124
+ has_sub_skills: false, // Will be enriched by loader
125
+ score: Math.round(score * 100) / 100,
126
+ });
127
+ }
128
+ }
129
+ // Sort by score descending
130
+ results.sort((a, b) => b.score - a.score);
131
+ return results.slice(0, limit);
132
+ }
133
+ /**
134
+ * Get all categories with skill counts.
135
+ */
136
+ getCategories() {
137
+ return Array.from(this.categories.entries())
138
+ .map(([name, skill_count]) => ({ name, skill_count }))
139
+ .sort((a, b) => a.name.localeCompare(b.name));
140
+ }
141
+ /**
142
+ * Find a skill entry by exact name.
143
+ */
144
+ findByName(name) {
145
+ const nameLower = name.toLowerCase();
146
+ return this.index.find((item) => item.entry.name.toLowerCase() === nameLower)?.entry;
147
+ }
148
+ }
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/search/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SkillSearchEngine } from "../search/index.js";
3
+ export declare function registerListCategories(server: McpServer, searchEngine: SkillSearchEngine): void;
4
+ //# sourceMappingURL=list-categories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-categories.d.ts","sourceRoot":"","sources":["../../src/tools/list-categories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,iBAAiB,GAC9B,IAAI,CAwBN"}
@@ -0,0 +1,18 @@
1
+ export function registerListCategories(server, searchEngine) {
2
+ server.tool("list_categories", "List all skill categories in the codex-skills library with their skill counts. Use this to get an overview of available domains.", {}, async () => {
3
+ const categories = searchEngine.getCategories();
4
+ const formatted = categories
5
+ .map((c) => `- **${c.name}**: ${c.skill_count} skills`)
6
+ .join("\n");
7
+ const total = categories.reduce((sum, c) => sum + c.skill_count, 0);
8
+ return {
9
+ content: [
10
+ {
11
+ type: "text",
12
+ text: `Codex-Skills Library: ${total} skills across ${categories.length} categories\n\n${formatted}\n\nUse search_skills(query) to find skills, or search_skills(query, category) to filter by category.`,
13
+ },
14
+ ],
15
+ };
16
+ });
17
+ }
18
+ //# sourceMappingURL=list-categories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-categories.js","sourceRoot":"","sources":["../../src/tools/list-categories.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,sBAAsB,CACpC,MAAiB,EACjB,YAA+B;IAE/B,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,kIAAkI,EAClI,EAAE,EACF,KAAK,IAAI,EAAE;QACT,MAAM,UAAU,GAAG,YAAY,CAAC,aAAa,EAAE,CAAC;QAEhD,MAAM,SAAS,GAAG,UAAU;aACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,WAAW,SAAS,CAAC;aACtD,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAEpE,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,yBAAyB,KAAK,kBAAkB,UAAU,CAAC,MAAM,kBAAkB,SAAS,uGAAuG;iBAC1M;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { SkillSearchEngine } from "../search/index.js";
3
+ import type { SkillLoader } from "../loader/index.js";
4
+ export declare function registerListSkillFiles(server: McpServer, searchEngine: SkillSearchEngine, loader: SkillLoader): void;
5
+ //# sourceMappingURL=list-skill-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-skill-files.d.ts","sourceRoot":"","sources":["../../src/tools/list-skill-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,iBAAiB,EAC/B,MAAM,EAAE,WAAW,GAClB,IAAI,CA8CN"}