code-frontmatter 0.2.3 → 0.2.5

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/dist/index.d.ts CHANGED
@@ -1,3 +1 @@
1
1
  #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,254 +1,19 @@
1
1
  #!/usr/bin/env node
2
- /*---
3
- intent: "MCP Server 入口:注册 cfm_read、cfm_write、cfm_search、cfm_register_language 四个工具,通过 stdio 与 AI IDE 通信"
4
- role: entry
5
- exports:
6
- - "main: MCP Server 启动函数"
7
- depends_on:
8
- - "@modelcontextprotocol/sdk"
9
- - "./registry.ts"
10
- - "./tools/read.ts"
11
- - "./tools/search.ts"
12
- - "./tools/register.ts"
13
- - "./tools/write.ts"
14
- when_to_load: "修改 MCP 工具注册、Server 配置或启动流程时加载"
15
- ai_notes: "使用 stdio transport,兼容所有 MCP 客户端(Cursor、Claude Desktop 等)"
16
- ---*/
17
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
- import { z } from "zod";
20
- import { loadRegistry } from "./registry.js";
21
- import { scanDirectory } from "./tools/read.js";
22
- import { searchFrontmatter } from "./tools/search.js";
23
- import { registerNewLanguage } from "./tools/register.js";
24
- import { writeFrontmatter } from "./tools/write.js";
25
- /**
26
- * 创建并配置 MCP Server
27
- */
28
- async function main() {
29
- // 初始化语言注册表
30
- await loadRegistry();
31
- // 创建 MCP Server 实例
32
- const server = new McpServer({
33
- name: "code-frontmatter",
34
- version: "0.2.0",
35
- });
36
- // ─── 工具 1: cfm_read ───────────────────────────────────
37
- server.tool("cfm_read", "扫描项目目录中所有代码文件的 CFM 表头,返回结构化索引。这是 AI 建立项目全局认知的第一步:用极低 Token 消耗获取整个项目的文件身份信息。", {
38
- directory: z
39
- .string()
40
- .describe("要扫描的项目根目录的绝对路径"),
41
- cfm_only: z
42
- .boolean()
43
- .optional()
44
- .default(false)
45
- .describe("是否只返回有 CFM 表头的文件(默认 false,返回全部)"),
46
- ignore_dirs: z
47
- .array(z.string())
48
- .optional()
49
- .describe("额外忽略的目录名列表(默认已忽略 node_modules, .git, dist 等)"),
50
- }, async ({ directory, cfm_only, ignore_dirs }) => {
51
- try {
52
- const result = await scanDirectory(directory, {
53
- cfmOnly: cfm_only,
54
- ignoreDirs: ignore_dirs,
55
- });
56
- return {
57
- content: [
58
- {
59
- type: "text",
60
- text: JSON.stringify(result, null, 2),
61
- },
62
- ],
63
- };
64
- }
65
- catch (error) {
66
- return {
67
- content: [
68
- {
69
- type: "text",
70
- text: `扫描失败: ${error.message}`,
71
- },
72
- ],
73
- isError: true,
74
- };
75
- }
76
- });
77
- // ─── 工具 2: cfm_write ──────────────────────────────────
78
- server.tool("cfm_write", "将标准 CFM 表头写入指定代码文件。AI 在完成编码任务后调用此工具补充/更新表头,确保字段名和注释格式严格合规。工具会自动根据文件类型选择正确的注释语法。", {
79
- file: z
80
- .string()
81
- .describe("目标文件的绝对路径"),
82
- intent: z
83
- .string()
84
- .max(200)
85
- .describe("文件的核心用途,简洁明了(必填)"),
86
- role: z
87
- .string()
88
- .describe("文件角色(如 service, component, util, config, page, model, entry, example)"),
89
- exports: z
90
- .array(z.string())
91
- .describe("导出的关键 API / 函数 / 组件(格式: \"名称: 简述\")"),
92
- depends_on: z
93
- .array(z.string())
94
- .optional()
95
- .describe("关键依赖的文件路径列表"),
96
- when_to_load: z
97
- .string()
98
- .optional()
99
- .describe("什么场景下 AI 才需要读取此文件全文"),
100
- mutates_state: z
101
- .boolean()
102
- .optional()
103
- .describe("是否修改外部状态(数据库、全局变量等)"),
104
- side_effects: z
105
- .array(z.string())
106
- .optional()
107
- .describe("副作用描述列表"),
108
- domain: z
109
- .string()
110
- .optional()
111
- .describe("业务领域标签(如 payment, auth, map)"),
112
- ai_notes: z
113
- .string()
114
- .optional()
115
- .describe("给 AI 的特殊注意事项"),
116
- }, async ({ file, intent, role, exports, depends_on, when_to_load, mutates_state, side_effects, domain, ai_notes }) => {
117
- try {
118
- const result = await writeFrontmatter(file, {
119
- intent,
120
- role,
121
- exports,
122
- depends_on,
123
- when_to_load,
124
- mutates_state,
125
- side_effects,
126
- domain,
127
- ai_notes,
128
- });
129
- return {
130
- content: [
131
- {
132
- type: "text",
133
- text: JSON.stringify(result, null, 2),
134
- },
135
- ],
136
- isError: !result.success,
137
- };
138
- }
139
- catch (error) {
140
- return {
141
- content: [
142
- {
143
- type: "text",
144
- text: `写入失败: ${error.message}`,
145
- },
146
- ],
147
- isError: true,
148
- };
149
- }
150
- });
151
- // ─── 工具 3: cfm_search ─────────────────────────────────
152
- server.tool("cfm_search", "在项目中搜索匹配条件的 CFM 表头。支持按关键字(在 intent, exports 等字段中全文搜索)、按角色(role)和按业务领域(domain)过滤,用于精准定位目标文件。", {
153
- directory: z
154
- .string()
155
- .describe("要搜索的项目根目录的绝对路径"),
156
- keyword: z
157
- .string()
158
- .optional()
159
- .describe("关键字搜索,匹配 intent、exports、ai_notes 等字段"),
160
- role: z
161
- .string()
162
- .optional()
163
- .describe("按文件角色过滤(如 service, component, util, config, page)"),
164
- domain: z
165
- .string()
166
- .optional()
167
- .describe("按业务领域过滤(如 payment, auth, map-layer)"),
168
- }, async ({ directory, keyword, role, domain }) => {
169
- // 至少需要一个搜索条件
170
- if (!keyword && !role && !domain) {
171
- return {
172
- content: [
173
- {
174
- type: "text",
175
- text: "请至少提供一个搜索条件(keyword、role 或 domain)",
176
- },
177
- ],
178
- isError: true,
179
- };
180
- }
181
- try {
182
- const result = await searchFrontmatter(directory, {
183
- keyword,
184
- role,
185
- domain,
186
- });
187
- return {
188
- content: [
189
- {
190
- type: "text",
191
- text: JSON.stringify(result, null, 2),
192
- },
193
- ],
194
- };
195
- }
196
- catch (error) {
197
- return {
198
- content: [
199
- {
200
- type: "text",
201
- text: `搜索失败: ${error.message}`,
202
- },
203
- ],
204
- isError: true,
205
- };
206
- }
207
- });
208
- // ─── 工具 4: cfm_register_language ──────────────────────
209
- server.tool("cfm_register_language", "注册新的编程语言注释规则。当遇到 CFM 尚不支持的语言时,通过此工具添加该语言的注释语法,使 cfm_read 能正确提取其表头。注册仅在当前会话有效。", {
210
- name: z
211
- .string()
212
- .describe("语言名称(如 elixir, dart, haskell)"),
213
- comment_start: z
214
- .string()
215
- .describe("CFM 表头的起始标记(如 \"#---\")"),
216
- comment_end: z
217
- .string()
218
- .describe("CFM 表头的结束标记(如 \"#---\")"),
219
- extensions: z
220
- .array(z.string())
221
- .describe("该语言的文件扩展名列表(如 [\".ex\", \".exs\"])"),
222
- line_prefix: z
223
- .string()
224
- .nullable()
225
- .optional()
226
- .default(null)
227
- .describe("行前缀(脚本语言使用,如 \"# \"),C 家族传 null"),
228
- }, async ({ name, comment_start, comment_end, extensions, line_prefix }) => {
229
- const result = registerNewLanguage(name, {
230
- comment_start,
231
- comment_end,
232
- extensions,
233
- line_prefix,
234
- });
235
- return {
236
- content: [
237
- {
238
- type: "text",
239
- text: JSON.stringify(result, null, 2),
240
- },
241
- ],
242
- isError: !result.success,
243
- };
244
- });
245
- // ─── 启动 Server ────────────────────────────────────────
246
- const transport = new StdioServerTransport();
247
- await server.connect(transport);
248
- }
249
- // 启动
250
- main().catch((error) => {
251
- console.error("MCP Server 启动失败:", error);
252
- process.exit(1);
253
- });
254
- //# sourceMappingURL=index.js.map
2
+ import{McpServer as ne}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as re}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as l}from"zod";import{readFile as I}from"fs/promises";import{fileURLToPath as M}from"url";import{dirname as D,join as N}from"path";import{z as c}from"zod";var C=c.object({comment_start:c.string(),comment_end:c.string(),extensions:c.array(c.string()),line_prefix:c.string().nullable()}),ie=c.object({intent:c.string().max(500),role:c.string(),exports:c.array(c.string()),depends_on:c.array(c.string()).optional(),when_to_load:c.string().optional(),side_effects:c.array(c.string()).optional(),mutates_state:c.boolean().optional(),domain:c.string().optional(),ai_notes:c.string().optional(),cfm_version:c.string().optional()}),L=c.object({intent:c.string()}).passthrough(),ae=c.object({file:c.string(),language:c.string().optional(),frontmatter:c.record(c.unknown()).nullable(),warnings:c.array(c.string()).optional()});var x=new Map,w=new Map;async function E(){let s=M(import.meta.url),e=D(s),r=N(e,"..","languages","registry.json"),t=await I(r,"utf-8"),n=JSON.parse(t);for(let[o,a]of Object.entries(n)){let i=C.parse(a);x.set(o,i);for(let f of i.extensions)w.set(f.toLowerCase(),o)}}function _(s){let e=s.toLowerCase(),r=w.get(e);if(!r)return null;let t=x.get(r);return t?{name:r,rule:t}:null}function R(s,e){let r=s.toLowerCase();if(x.has(r))throw new Error(`\u8BED\u8A00 "${s}" \u5DF2\u5B58\u5728\u4E8E\u6CE8\u518C\u8868\u4E2D\u3002\u5982\u9700\u66F4\u65B0\uFF0C\u8BF7\u5148\u79FB\u9664\u65E7\u6CE8\u518C\u3002`);for(let t of e.extensions){let n=w.get(t.toLowerCase());if(n)throw new Error(`\u6269\u5C55\u540D "${t}" \u5DF2\u88AB\u8BED\u8A00 "${n}" \u6CE8\u518C\u3002`)}x.set(r,e);for(let t of e.extensions)w.set(t.toLowerCase(),r)}function $(){let s={};for(let[e,r]of x.entries())s[e]=r;return s}import{readdir as J,stat as Y}from"fs/promises";import{join as P,relative as U,extname as G}from"path";import{extname as W}from"path";import{parse as z}from"yaml";var T=4096;async function F(s,e){let r=W(s),t=_(r);if(!t)return{file:e,language:void 0,frontmatter:null,warnings:["\u672A\u6CE8\u518C\u7684\u6587\u4EF6\u7C7B\u578B\uFF0C\u8DF3\u8FC7\u8868\u5934\u63D0\u53D6"]};let n=[];try{let o=await H(s,T),{comment_start:a,comment_end:i,line_prefix:f}=t.rule,g=B(o,a,i,f);if(!g)return{file:e,language:t.name,frontmatter:null};let m;try{m=z(g)}catch(u){return{file:e,language:t.name,frontmatter:null,warnings:[`YAML \u89E3\u6790\u5931\u8D25: ${u.message}`]}}if(!m||typeof m!="object")return{file:e,language:t.name,frontmatter:null,warnings:["\u8868\u5934\u5185\u5BB9\u4E0D\u662F\u6709\u6548\u7684 YAML \u5BF9\u8C61"]};let p=L.safeParse(m);return p.success||n.push(`Schema \u6821\u9A8C\u8B66\u544A: ${p.error.issues.map(u=>u.message).join("; ")}`),m.intent||n.push("\u7F3A\u5C11\u5FC5\u9009\u5B57\u6BB5: intent"),m.role||n.push("\u7F3A\u5C11\u63A8\u8350\u5B57\u6BB5: role"),m.exports||n.push("\u7F3A\u5C11\u63A8\u8350\u5B57\u6BB5: exports"),{file:e,language:t.name,frontmatter:m,warnings:n.length>0?n:void 0}}catch(o){return{file:e,language:t.name,frontmatter:null,warnings:[`\u6587\u4EF6\u8BFB\u53D6\u5931\u8D25: ${o.message}`]}}}function B(s,e,r,t){let n=s.replace(/\r\n/g,`
3
+ `).replace(/\r/g,`
4
+ `);if(n.startsWith("#!")){let m=n.indexOf(`
5
+ `);m!==-1?n=n.slice(m+1):n=""}let o=n.trimStart();if(!o.startsWith(e))return null;let a=o.slice(e.length),i=a.indexOf(r);if(i===-1)return null;let f=a.slice(0,i);t&&(f=f.split(`
6
+ `).map(p=>{let u=p.trimStart();if(u.startsWith(t))return u.slice(t.length);let h=t.trim();return h&&u.startsWith(h)?u.slice(h.length):p}).join(`
7
+ `));let g=f.trim();return g.length>0?g:null}async function H(s,e){let{open:r}=await import("fs/promises"),t=await r(s,"r");try{let n=Buffer.alloc(e),{bytesRead:o}=await t.read(n,0,e,0);return n.slice(0,o).toString("utf-8")}finally{await t.close()}}var Q=new Set(["node_modules",".git",".svn",".hg","dist","build","out",".next",".nuxt","__pycache__",".pytest_cache","venv",".venv","target","vendor",".idea",".vscode","coverage"]),X=new Set([".DS_Store","Thumbs.db","package-lock.json","yarn.lock","pnpm-lock.yaml"]);async function b(s,e={}){let r=new Set([...Q,...e.ignoreDirs??[]]),t=[];await k(s,s,r,t);let n=e.cfmOnly?t.filter(o=>o.frontmatter!==null):t;return{total_files:t.length,files_with_cfm:t.filter(o=>o.frontmatter!==null).length,files_without_cfm:t.filter(o=>o.frontmatter===null).length,entries:n}}async function k(s,e,r,t){let n=await J(e);for(let o of n){let a=P(e,o);if(X.has(o))continue;let i=await Y(a);if(i.isDirectory()){if(r.has(o))continue;await k(s,a,r,t)}else if(i.isFile()){let f=G(o);if(!_(f))continue;let g=U(s,a).replace(/\\/g,"/"),m=await F(a,g);t.push(m)}}}async function j(s,e){let r=await b(s,{cfmOnly:!1}),t=[];e.keyword&&t.push(`keyword="${e.keyword}"`),e.role&&t.push(`role="${e.role}"`),e.domain&&t.push(`domain="${e.domain}"`);let n=t.join(", "),o=[],a=[];for(let i of r.entries){if(!i.frontmatter&&i.warnings&&i.warnings.length>0){a.push({file:i.file,message:i.warnings.join("; ")});continue}K(i,e)&&o.push(i)}return{query:n,matches:o.length,entries:o,errors:a.length>0?a:void 0}}function K(s,e){let r=s.frontmatter;if(!r||e.role&&String(r.role??"").toLowerCase()!==e.role.toLowerCase()||e.domain&&String(r.domain??"").toLowerCase()!==e.domain.toLowerCase())return!1;if(e.keyword){let t=e.keyword.toLowerCase();if(![String(r.intent??""),String(r.ai_notes??""),String(r.when_to_load??""),String(r.domain??""),...Array.isArray(r.exports)?r.exports.map(String):[],...Array.isArray(r.depends_on)?r.depends_on.map(String):[],...Array.isArray(r.side_effects)?r.side_effects.map(String):[],s.file].join(" ").toLowerCase().includes(t))return!1}return!0}function O(s,e){if(!s||s.trim().length===0)return{success:!1,message:"\u8BED\u8A00\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"};if(!e.comment_start||!e.comment_end)return{success:!1,message:"comment_start \u548C comment_end \u4E0D\u80FD\u4E3A\u7A7A"};if(!e.extensions||e.extensions.length===0)return{success:!1,message:"extensions \u4E0D\u80FD\u4E3A\u7A7A\uFF0C\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u6587\u4EF6\u6269\u5C55\u540D"};let r=e.extensions.map(n=>n.startsWith(".")?n:`.${n}`),t={comment_start:e.comment_start,comment_end:e.comment_end,extensions:r,line_prefix:e.line_prefix??null};try{R(s,t);let n=$();return{success:!0,message:`\u8BED\u8A00 "${s}" \u6CE8\u518C\u6210\u529F\uFF08\u6269\u5C55\u540D: ${r.join(", ")}\uFF09`,total_languages:Object.keys(n).length}}catch(n){return{success:!1,message:n.message}}}import{readFile as V,writeFile as Z}from"fs/promises";import{extname as q}from"path";import{stringify as ee}from"yaml";async function v(s,e){let r=q(s),t=_(r);if(!t)return{success:!1,message:`\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B "${r}"\uFF0C\u8BF7\u5148\u7528 cfm_register_language \u6CE8\u518C\u8BE5\u8BED\u8A00\u3002`};let{comment_start:n,comment_end:o,line_prefix:a}=t.rule,i={};i.intent=e.intent,i.role=e.role,i.exports=e.exports,e.depends_on&&e.depends_on.length>0&&(i.depends_on=e.depends_on),e.when_to_load&&(i.when_to_load=e.when_to_load),e.mutates_state!==void 0&&(i.mutates_state=e.mutates_state),e.side_effects&&e.side_effects.length>0&&(i.side_effects=e.side_effects),e.domain&&(i.domain=e.domain),e.ai_notes&&(i.ai_notes=e.ai_notes);let f=ee(i,{lineWidth:0}).trim(),g;if(a){let A=f.split(`
8
+ `).map(S=>S?`${a}${S}`:a.trim());g=`${n}
9
+ ${A.join(`
10
+ `)}
11
+ ${o}`}else g=`${n}
12
+ ${f}
13
+ ${o}`;let m;try{m=await V(s,"utf-8")}catch{return{success:!1,message:`\u65E0\u6CD5\u8BFB\u53D6\u6587\u4EF6: ${s}`}}let p=m.replace(/\r\n/g,`
14
+ `).replace(/\r/g,`
15
+ `),u=te(p,n,o,g);try{await Z(s,u,"utf-8")}catch{return{success:!1,message:`\u65E0\u6CD5\u5199\u5165\u6587\u4EF6: ${s}`}}return{success:!0,message:`CFM \u8868\u5934\u5DF2\u5199\u5165 ${s}`}}function te(s,e,r,t){let n=s.trimStart(),o=s.slice(0,s.length-n.length),a="",i=n;if(n.startsWith("#!")){let g=n.indexOf(`
16
+ `);g!==-1&&(a=n.slice(0,g+1),i=n.slice(g+1))}let f=i.trimStart();if(f.startsWith(e)){let g=f.slice(e.length),m=g.indexOf(r);if(m!==-1){let u=g.slice(m+r.length).replace(/^\n{0,2}/,`
17
+ `);return a+t+u}}return a+t+`
18
+
19
+ `+i}async function se(){await E();let s=new ne({name:"code-frontmatter",version:"0.2.0"});s.tool("cfm_read","\u626B\u63CF\u9879\u76EE\u76EE\u5F55\u4E2D\u6240\u6709\u4EE3\u7801\u6587\u4EF6\u7684 CFM \u8868\u5934\uFF0C\u8FD4\u56DE\u7ED3\u6784\u5316\u7D22\u5F15\u3002\u8FD9\u662F AI \u5EFA\u7ACB\u9879\u76EE\u5168\u5C40\u8BA4\u77E5\u7684\u7B2C\u4E00\u6B65\uFF1A\u7528\u6781\u4F4E Token \u6D88\u8017\u83B7\u53D6\u6574\u4E2A\u9879\u76EE\u7684\u6587\u4EF6\u8EAB\u4EFD\u4FE1\u606F\u3002",{directory:l.string().describe("\u8981\u626B\u63CF\u7684\u9879\u76EE\u6839\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84"),cfm_only:l.boolean().optional().default(!1).describe("\u662F\u5426\u53EA\u8FD4\u56DE\u6709 CFM \u8868\u5934\u7684\u6587\u4EF6\uFF08\u9ED8\u8BA4 false\uFF0C\u8FD4\u56DE\u5168\u90E8\uFF09"),ignore_dirs:l.array(l.string()).optional().describe("\u989D\u5916\u5FFD\u7565\u7684\u76EE\u5F55\u540D\u5217\u8868\uFF08\u9ED8\u8BA4\u5DF2\u5FFD\u7565 node_modules, .git, dist \u7B49\uFF09")},async({directory:r,cfm_only:t,ignore_dirs:n})=>{try{let o=await b(r,{cfmOnly:t,ignoreDirs:n});return{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){return{content:[{type:"text",text:`\u626B\u63CF\u5931\u8D25: ${o.message}`}],isError:!0}}}),s.tool("cfm_write","\u5C06\u6807\u51C6 CFM \u8868\u5934\u5199\u5165\u6307\u5B9A\u4EE3\u7801\u6587\u4EF6\u3002CRITICAL: \u66F4\u65B0\u8868\u5934\u65F6\u60A8\u6709'\u4E3B\u52A8\u7EF4\u62A4\u4E49\u52A1'\uFF1A1. \u8BFB\u53D6\u65E7\u8868\u5934\u30022. \u4FDD\u7559'\u6C38\u4E45\u6027\u6280\u672F\u7EA6\u675F/\u7ECF\u9A8C\u6559\u8BAD'\u30023. \u4E22\u5F03'\u53D8\u66F4\u65E5\u5FD7/\u5386\u53F2/\u6D41\u7A0B\u4FE1\u606F'\u30024. \u5C06\u5269\u4F59\u5185\u5BB9\u6781\u81F4\u7CBE\u7B80\u3002\u7981\u6B62\u76F2\u76EE\u8986\u76D6\uFF01",{file:l.string().describe("\u76EE\u6807\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84"),intent:l.string().max(300).describe("\u6587\u4EF6\u7684\u6838\u5FC3\u7528\u9014\u3002\u5FC5\u987B\u7CBE\u7B80\u9AD8\u6548\uFF08<50\u5B57\uFF09\uFF0C\u62D2\u7EDD\u5E9F\u8BDD\u3002"),role:l.string().describe("\u6587\u4EF6\u89D2\u8272\uFF08\u5982 service, component, util, config, page, model, entry, example\uFF09"),exports:l.array(l.string()).describe("\u5BFC\u51FA\u7684\u5173\u952E API/\u7EC4\u4EF6\u5217\u8868\u3002\u683C\u5F0F\uFF1A'Name: Brief desc'\u3002\u53EA\u5217\u51FA\u6700\u91CD\u8981\u7684\uFF0C\u62D2\u7EDD\u5197\u957F\u7B7E\u540D\u3002"),depends_on:l.array(l.string()).optional().describe("\u5173\u952E\u4F9D\u8D56\u7684\u6587\u4EF6\u8DEF\u5F84\u5217\u8868"),when_to_load:l.string().optional().describe("\u4EC0\u4E48\u573A\u666F\u4E0B\u624D\u9700\u8981\u8BFB\u53D6\u6B64\u6587\u4EF6\u5168\u6587\u3002\u53EA\u63CF\u8FF0\u5173\u952E\u89E6\u53D1\u6761\u4EF6\uFF0C\u4FDD\u6301\u7B80\u77ED\u3002"),mutates_state:l.boolean().optional().describe("\u662F\u5426\u4FEE\u6539\u5916\u90E8\u72B6\u6001\uFF08\u6570\u636E\u5E93\u3001\u5168\u5C40\u53D8\u91CF\u7B49\uFF09"),side_effects:l.array(l.string()).optional().describe("\u526F\u4F5C\u7528\u63CF\u8FF0\u5217\u8868"),domain:l.string().optional().describe("\u4E1A\u52A1\u9886\u57DF\u6807\u7B7E\uFF08\u5982 payment, auth, map\uFF09"),ai_notes:l.string().optional().describe("\u7ED9 AI \u7684\u5173\u952E\u6280\u672F\u7EA6\u675F\u6216\u8B66\u793A\u3002\u5FC5\u987B\u662F\u6C38\u4E45\u6027\u77E5\u8BC6\uFF08\u5982'\u4F7F\u7528\u6355\u83B7\u9636\u6BB5\u76D1\u542C'\uFF09\u3002\u4E25\u7981\u8BB0\u5F55\u53D8\u66F4\u65E5\u5FD7/\u5386\u53F2/\u4F5C\u8005/\u65E5\u671F\uFF01\u53EA\u4FDD\u7559\u5BF9\u7406\u89E3\u4EE3\u7801\u81F3\u5173\u91CD\u8981\u7684\u4FE1\u606F\uFF0C\u8D8A\u77ED\u8D8A\u597D\u3002")},async({file:r,intent:t,role:n,exports:o,depends_on:a,when_to_load:i,mutates_state:f,side_effects:g,domain:m,ai_notes:p})=>{try{let u=await v(r,{intent:t,role:n,exports:o,depends_on:a,when_to_load:i,mutates_state:f,side_effects:g,domain:m,ai_notes:p});return{content:[{type:"text",text:JSON.stringify(u,null,2)}],isError:!u.success}}catch(u){return{content:[{type:"text",text:`\u5199\u5165\u5931\u8D25: ${u.message}`}],isError:!0}}}),s.tool("cfm_search","\u5728\u9879\u76EE\u4E2D\u641C\u7D22\u5339\u914D\u6761\u4EF6\u7684 CFM \u8868\u5934\u3002\u652F\u6301\u6309\u5173\u952E\u5B57\uFF08\u5728 intent, exports \u7B49\u5B57\u6BB5\u4E2D\u5168\u6587\u641C\u7D22\uFF09\u3001\u6309\u89D2\u8272\uFF08role\uFF09\u548C\u6309\u4E1A\u52A1\u9886\u57DF\uFF08domain\uFF09\u8FC7\u6EE4\uFF0C\u7528\u4E8E\u7CBE\u51C6\u5B9A\u4F4D\u76EE\u6807\u6587\u4EF6\u3002",{directory:l.string().describe("\u8981\u641C\u7D22\u7684\u9879\u76EE\u6839\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84"),keyword:l.string().optional().describe("\u5173\u952E\u5B57\u641C\u7D22\uFF0C\u5339\u914D intent\u3001exports\u3001ai_notes \u7B49\u5B57\u6BB5"),role:l.string().optional().describe("\u6309\u6587\u4EF6\u89D2\u8272\u8FC7\u6EE4\uFF08\u5982 service, component, util, config, page\uFF09"),domain:l.string().optional().describe("\u6309\u4E1A\u52A1\u9886\u57DF\u8FC7\u6EE4\uFF08\u5982 payment, auth, map-layer\uFF09")},async({directory:r,keyword:t,role:n,domain:o})=>{if(!t&&!n&&!o)return{content:[{type:"text",text:"\u8BF7\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u641C\u7D22\u6761\u4EF6\uFF08keyword\u3001role \u6216 domain\uFF09"}],isError:!0};try{let a=await j(r,{keyword:t,role:n,domain:o});return{content:[{type:"text",text:JSON.stringify(a,null,2)}]}}catch(a){return{content:[{type:"text",text:`\u641C\u7D22\u5931\u8D25: ${a.message}`}],isError:!0}}}),s.tool("cfm_register_language","\u6CE8\u518C\u65B0\u7684\u7F16\u7A0B\u8BED\u8A00\u6CE8\u91CA\u89C4\u5219\u3002\u5F53\u9047\u5230 CFM \u5C1A\u4E0D\u652F\u6301\u7684\u8BED\u8A00\u65F6\uFF0C\u901A\u8FC7\u6B64\u5DE5\u5177\u6DFB\u52A0\u8BE5\u8BED\u8A00\u7684\u6CE8\u91CA\u8BED\u6CD5\uFF0C\u4F7F cfm_read \u80FD\u6B63\u786E\u63D0\u53D6\u5176\u8868\u5934\u3002\u6CE8\u518C\u4EC5\u5728\u5F53\u524D\u4F1A\u8BDD\u6709\u6548\u3002",{name:l.string().describe("\u8BED\u8A00\u540D\u79F0\uFF08\u5982 elixir, dart, haskell\uFF09"),comment_start:l.string().describe('CFM \u8868\u5934\u7684\u8D77\u59CB\u6807\u8BB0\uFF08\u5982 "#---"\uFF09'),comment_end:l.string().describe('CFM \u8868\u5934\u7684\u7ED3\u675F\u6807\u8BB0\uFF08\u5982 "#---"\uFF09'),extensions:l.array(l.string()).describe('\u8BE5\u8BED\u8A00\u7684\u6587\u4EF6\u6269\u5C55\u540D\u5217\u8868\uFF08\u5982 [".ex", ".exs"]\uFF09'),line_prefix:l.string().nullable().optional().default(null).describe('\u884C\u524D\u7F00\uFF08\u811A\u672C\u8BED\u8A00\u4F7F\u7528\uFF0C\u5982 "# "\uFF09\uFF0CC \u5BB6\u65CF\u4F20 null')},async({name:r,comment_start:t,comment_end:n,extensions:o,line_prefix:a})=>{let i=O(r,{comment_start:t,comment_end:n,extensions:o,line_prefix:a});return{content:[{type:"text",text:JSON.stringify(i,null,2)}],isError:!i.success}});let e=new re;await s.connect(e)}se().catch(s=>{console.error("MCP Server \u542F\u52A8\u5931\u8D25:",s),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-frontmatter",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "让每一个代码文件拥有自己的身份证,让 AI 不读全文就能理解你的整个项目。An MCP Server that gives every code file a self-describing frontmatter header for AI assistants.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -8,8 +8,8 @@
8
8
  "code-frontmatter": "dist/index.js"
9
9
  },
10
10
  "scripts": {
11
- "build": "tsc",
12
- "dev": "tsc --watch",
11
+ "build": "tsup",
12
+ "dev": "tsup --watch",
13
13
  "start": "node dist/index.js",
14
14
  "prepublishOnly": "npm run build"
15
15
  },
@@ -47,6 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^22.15.3",
50
+ "tsup": "^8.5.1",
50
51
  "typescript": "^5.8.3"
51
52
  }
52
53
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;KAcK;AAEL,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AACH,KAAK,UAAU,IAAI;IACf,WAAW;IACX,MAAM,YAAY,EAAE,CAAC;IAErB,mBAAmB;IACnB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QACzB,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,OAAO;KACnB,CAAC,CAAC;IAEH,yDAAyD;IACzD,MAAM,CAAC,IAAI,CACP,UAAU,EACV,6EAA6E,EAC7E;QACI,SAAS,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,CAAC,gBAAgB,CAAC;QAC/B,QAAQ,EAAE,CAAC;aACN,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,OAAO,CAAC,KAAK,CAAC;aACd,QAAQ,CAAC,iCAAiC,CAAC;QAChD,WAAW,EAAE,CAAC;aACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,QAAQ,EAAE;aACV,QAAQ,CAAC,8CAA8C,CAAC;KAChE,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE;QAC3C,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE;gBAC1C,OAAO,EAAE,QAAQ;gBACjB,UAAU,EAAE,WAAW;aAC1B,CAAC,CAAC;YAEH,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC;iBACJ;aACJ,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,SAAU,KAAe,CAAC,OAAO,EAAE;qBAC5C;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;IAEF,yDAAyD;IACzD,MAAM,CAAC,IAAI,CACP,WAAW,EACX,iFAAiF,EACjF;QACI,IAAI,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,WAAW,CAAC;QAC1B,MAAM,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,CAAC,kBAAkB,CAAC;QACjC,IAAI,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,uEAAuE,CAAC;QACtF,OAAO,EAAE,CAAC;aACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,QAAQ,CAAC,qCAAqC,CAAC;QACpD,UAAU,EAAE,CAAC;aACR,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,QAAQ,EAAE;aACV,QAAQ,CAAC,aAAa,CAAC;QAC5B,YAAY,EAAE,CAAC;aACV,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qBAAqB,CAAC;QACpC,aAAa,EAAE,CAAC;aACX,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,qBAAqB,CAAC;QACpC,YAAY,EAAE,CAAC;aACV,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,QAAQ,EAAE;aACV,QAAQ,CAAC,SAAS,CAAC;QACxB,MAAM,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8BAA8B,CAAC;QAC7C,QAAQ,EAAE,CAAC;aACN,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,cAAc,CAAC;KAChC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC/G,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE;gBACxC,MAAM;gBACN,IAAI;gBACJ,OAAO;gBACP,UAAU;gBACV,YAAY;gBACZ,aAAa;gBACb,YAAY;gBACZ,MAAM;gBACN,QAAQ;aACX,CAAC,CAAC;YAEH,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC;iBACJ;gBACD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO;aAC3B,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,SAAU,KAAe,CAAC,OAAO,EAAE;qBAC5C;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;IAEF,yDAAyD;IACzD,MAAM,CAAC,IAAI,CACP,YAAY,EACZ,6FAA6F,EAC7F;QACI,SAAS,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,CAAC,gBAAgB,CAAC;QAC/B,OAAO,EAAE,CAAC;aACL,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,sCAAsC,CAAC;QACrD,IAAI,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,mDAAmD,CAAC;QAClE,MAAM,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,qCAAqC,CAAC;KACvD,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAC3C,aAAa;QACb,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC/B,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,oCAAoC;qBAC7C;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;QAED,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,SAAS,EAAE;gBAC9C,OAAO;gBACP,IAAI;gBACJ,MAAM;aACT,CAAC,CAAC;YAEH,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC;iBACJ;aACJ,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,SAAU,KAAe,CAAC,OAAO,EAAE;qBAC5C;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAC;QACN,CAAC;IACL,CAAC,CACJ,CAAC;IAEF,yDAAyD;IACzD,MAAM,CAAC,IAAI,CACP,uBAAuB,EACvB,+EAA+E,EAC/E;QACI,IAAI,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CAAC,+BAA+B,CAAC;QAC9C,aAAa,EAAE,CAAC;aACX,MAAM,EAAE;aACR,QAAQ,CAAC,yBAAyB,CAAC;QACxC,WAAW,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,CAAC,yBAAyB,CAAC;QACxC,UAAU,EAAE,CAAC;aACR,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,QAAQ,CAAC,oCAAoC,CAAC;QACnD,WAAW,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,OAAO,CAAC,IAAI,CAAC;aACb,QAAQ,CAAC,iCAAiC,CAAC;KACnD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE;QACpE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,EAAE;YACrC,aAAa;YACb,WAAW;YACX,UAAU;YACV,WAAW;SACd,CAAC,CAAC;QAEH,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxC;aACJ;YACD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO;SAC3B,CAAC;IACN,CAAC,CACJ,CAAC;IAEF,yDAAyD;IACzD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,KAAK;AACL,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
package/dist/parser.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import { type CfmEntry } from "./schema.js";
2
- /**
3
- * 从单个文件中提取 CFM 表头
4
- *
5
- * 提取流程:
6
- * 1. 根据文件扩展名查找语言注释规则
7
- * 2. 在文件开头区域匹配 comment_start / comment_end 标记对
8
- * 3. 提取标记之间的文本,去除 line_prefix(如 "# ")
9
- * 4. 用 YAML 解析器解析,再用 Zod Schema 校验
10
- *
11
- * @param filePath - 文件的绝对路径
12
- * @param relativePath - 用于输出的相对路径
13
- * @returns CFM 条目(含解析数据或 null + 警告信息)
14
- */
15
- export declare function extractFrontmatter(filePath: string, relativePath: string): Promise<CfmEntry>;
16
- //# sourceMappingURL=parser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAcA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5D;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACrB,OAAO,CAAC,QAAQ,CAAC,CAuFnB"}
package/dist/parser.js DELETED
@@ -1,173 +0,0 @@
1
- /*---
2
- intent: "从代码文件中提取 CFM 表头:根据语言规则定位注释标记,去除行前缀,解析 YAML 并校验"
3
- role: service
4
- exports:
5
- - "extractFrontmatter: 从单个文件中提取并解析 CFM 表头"
6
- depends_on: ["./registry.ts", "./schema.ts", "yaml"]
7
- when_to_load: "修改表头提取逻辑或 YAML 解析方式时加载"
8
- ai_notes: "使用简单文本匹配而非 AST 解析,保持零重型依赖的设计原则"
9
- ---*/
10
- import { extname } from "node:path";
11
- import { parse as parseYaml } from "yaml";
12
- import { getLanguageByExtension } from "./registry.js";
13
- import { CfmSchemaLoose } from "./schema.js";
14
- /** 读取文件的最大字节数(只读开头部分以查找表头) */
15
- const MAX_READ_BYTES = 4096;
16
- /**
17
- * 从单个文件中提取 CFM 表头
18
- *
19
- * 提取流程:
20
- * 1. 根据文件扩展名查找语言注释规则
21
- * 2. 在文件开头区域匹配 comment_start / comment_end 标记对
22
- * 3. 提取标记之间的文本,去除 line_prefix(如 "# ")
23
- * 4. 用 YAML 解析器解析,再用 Zod Schema 校验
24
- *
25
- * @param filePath - 文件的绝对路径
26
- * @param relativePath - 用于输出的相对路径
27
- * @returns CFM 条目(含解析数据或 null + 警告信息)
28
- */
29
- export async function extractFrontmatter(filePath, relativePath) {
30
- const ext = extname(filePath);
31
- const lang = getLanguageByExtension(ext);
32
- // 未知语言类型,跳过
33
- if (!lang) {
34
- return {
35
- file: relativePath,
36
- language: undefined,
37
- frontmatter: null,
38
- warnings: ["未注册的文件类型,跳过表头提取"],
39
- };
40
- }
41
- const warnings = [];
42
- try {
43
- // 只读取文件开头部分,节省 I/O
44
- const content = await readFileHead(filePath, MAX_READ_BYTES);
45
- const { comment_start, comment_end, line_prefix } = lang.rule;
46
- // 查找表头区域
47
- const headerContent = extractHeaderContent(content, comment_start, comment_end, line_prefix);
48
- if (!headerContent) {
49
- return {
50
- file: relativePath,
51
- language: lang.name,
52
- frontmatter: null,
53
- };
54
- }
55
- // 解析 YAML
56
- let parsed;
57
- try {
58
- parsed = parseYaml(headerContent);
59
- }
60
- catch (yamlError) {
61
- return {
62
- file: relativePath,
63
- language: lang.name,
64
- frontmatter: null,
65
- warnings: [`YAML 解析失败: ${yamlError.message}`],
66
- };
67
- }
68
- // 如果 YAML 解析结果不是对象(可能是纯文本)
69
- if (!parsed || typeof parsed !== "object") {
70
- return {
71
- file: relativePath,
72
- language: lang.name,
73
- frontmatter: null,
74
- warnings: ["表头内容不是有效的 YAML 对象"],
75
- };
76
- }
77
- // 宽松模式校验
78
- const validation = CfmSchemaLoose.safeParse(parsed);
79
- if (!validation.success) {
80
- warnings.push(`Schema 校验警告: ${validation.error.issues.map((i) => i.message).join("; ")}`);
81
- }
82
- // 检查必选字段缺失
83
- if (!parsed.intent)
84
- warnings.push("缺少必选字段: intent");
85
- if (!parsed.role)
86
- warnings.push("缺少推荐字段: role");
87
- if (!parsed.exports)
88
- warnings.push("缺少推荐字段: exports");
89
- return {
90
- file: relativePath,
91
- language: lang.name,
92
- frontmatter: parsed,
93
- warnings: warnings.length > 0 ? warnings : undefined,
94
- };
95
- }
96
- catch (error) {
97
- return {
98
- file: relativePath,
99
- language: lang.name,
100
- frontmatter: null,
101
- warnings: [`文件读取失败: ${error.message}`],
102
- };
103
- }
104
- }
105
- /**
106
- * 从文件内容中提取表头区域的 YAML 文本
107
- * @returns 清理后的 YAML 字符串,或 null(未找到表头)
108
- */
109
- function extractHeaderContent(content, commentStart, commentEnd, linePrefix) {
110
- // 统一换行符为 \n,兼容 Windows (\r\n) 和 Mac (\r)
111
- let normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
112
- // 去除 Shebang 行(如 #!/usr/bin/env node)
113
- if (normalized.startsWith("#!")) {
114
- const firstNewLine = normalized.indexOf("\n");
115
- if (firstNewLine !== -1) {
116
- normalized = normalized.slice(firstNewLine + 1);
117
- }
118
- else {
119
- // 文件只有 Shebang
120
- normalized = "";
121
- }
122
- }
123
- const trimmedContent = normalized.trimStart();
124
- // 查找起始标记
125
- if (!trimmedContent.startsWith(commentStart)) {
126
- return null;
127
- }
128
- // 查找结束标记(从起始标记之后开始搜索)
129
- const afterStart = trimmedContent.slice(commentStart.length);
130
- const endIndex = afterStart.indexOf(commentEnd);
131
- if (endIndex === -1) {
132
- return null;
133
- }
134
- // 提取标记之间的原始文本
135
- let rawContent = afterStart.slice(0, endIndex);
136
- // 去除每行的 line_prefix(如 Python 的 "# ")
137
- if (linePrefix) {
138
- const lines = rawContent.split("\n");
139
- rawContent = lines
140
- .map((line) => {
141
- const trimmedLine = line.trimStart();
142
- // 尝试完整前缀匹配(如 "# "),然后尝试去尾空格后的匹配(如 "#")
143
- if (trimmedLine.startsWith(linePrefix)) {
144
- return trimmedLine.slice(linePrefix.length);
145
- }
146
- const trimmedPrefix = linePrefix.trim();
147
- if (trimmedPrefix && trimmedLine.startsWith(trimmedPrefix)) {
148
- return trimmedLine.slice(trimmedPrefix.length);
149
- }
150
- return line;
151
- })
152
- .join("\n");
153
- }
154
- const cleaned = rawContent.trim();
155
- return cleaned.length > 0 ? cleaned : null;
156
- }
157
- /**
158
- * 只读取文件的前 N 个字节
159
- * 大文件只需读开头就能找到表头,避免全文读取
160
- */
161
- async function readFileHead(filePath, maxBytes) {
162
- const { open } = await import("node:fs/promises");
163
- const fileHandle = await open(filePath, "r");
164
- try {
165
- const buffer = Buffer.alloc(maxBytes);
166
- const { bytesRead } = await fileHandle.read(buffer, 0, maxBytes, 0);
167
- return buffer.slice(0, bytesRead).toString("utf-8");
168
- }
169
- finally {
170
- await fileHandle.close();
171
- }
172
- }
173
- //# sourceMappingURL=parser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
@@ -1,28 +0,0 @@
1
- import { type LanguageRule } from "./schema.js";
2
- /**
3
- * 从 languages/registry.json 加载内置语言注册表
4
- * 应在 MCP Server 启动时调用一次
5
- */
6
- export declare function loadRegistry(): Promise<void>;
7
- /**
8
- * 根据文件扩展名查找对应的语言注释规则
9
- * @param ext - 文件扩展名,如 ".js", ".py"
10
- * @returns 匹配的语言规则,未找到则返回 null
11
- */
12
- export declare function getLanguageByExtension(ext: string): {
13
- name: string;
14
- rule: LanguageRule;
15
- } | null;
16
- /**
17
- * 运行时动态注册新语言
18
- * @param name - 语言名称
19
- * @param rule - 语言注释规则
20
- * @throws 如果该语言已存在则抛出错误
21
- */
22
- export declare function registerLanguage(name: string, rule: LanguageRule): void;
23
- /**
24
- * 获取所有已注册的语言及其规则
25
- * @returns 语言名称与规则的映射
26
- */
27
- export declare function getAllLanguages(): Record<string, LanguageRule>;
28
- //# sourceMappingURL=registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAeA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAcpE;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBlD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAClC,GAAG,EAAE,MAAM,GACZ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,CAU7C;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAuBvE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM9D"}