code-frontmatter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * 语言注释规则 Schema
4
+ * 定义某种编程语言的 CFM 注释语法
5
+ */
6
+ export declare const LanguageRuleSchema: z.ZodObject<{
7
+ /** 注释起始标记,如 "/*---" */
8
+ comment_start: z.ZodString;
9
+ /** 注释结束标记,如 "---*​/" */
10
+ comment_end: z.ZodString;
11
+ /** 该语言的文件扩展名列表 */
12
+ extensions: z.ZodArray<z.ZodString, "many">;
13
+ /** 行前缀(脚本语言使用),如 "# ",C 家族为 null */
14
+ line_prefix: z.ZodNullable<z.ZodString>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ comment_start: string;
17
+ comment_end: string;
18
+ extensions: string[];
19
+ line_prefix: string | null;
20
+ }, {
21
+ comment_start: string;
22
+ comment_end: string;
23
+ extensions: string[];
24
+ line_prefix: string | null;
25
+ }>;
26
+ export type LanguageRule = z.infer<typeof LanguageRuleSchema>;
27
+ /**
28
+ * CFM 表头严格 Schema(含全部必选字段)
29
+ * 用于校验完整规范的表头
30
+ */
31
+ export declare const CfmSchema: z.ZodObject<{
32
+ /** 文件的核心用途和业务价值 */
33
+ intent: z.ZodString;
34
+ /** 文件的角色类型 */
35
+ role: z.ZodString;
36
+ /** 暴露的关键 API / 函数 / 组件 */
37
+ exports: z.ZodArray<z.ZodString, "many">;
38
+ /** 关键依赖 */
39
+ depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
40
+ /** 什么场景下才需要读取此文件全文 */
41
+ when_to_load: z.ZodOptional<z.ZodString>;
42
+ /** 副作用描述 */
43
+ side_effects: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
44
+ /** 是否修改外部状态 */
45
+ mutates_state: z.ZodOptional<z.ZodBoolean>;
46
+ /** 业务领域标签 */
47
+ domain: z.ZodOptional<z.ZodString>;
48
+ /** 给 AI 的特殊注意事项 */
49
+ ai_notes: z.ZodOptional<z.ZodString>;
50
+ /** 表头规范版本号 */
51
+ cfm_version: z.ZodOptional<z.ZodString>;
52
+ }, "strip", z.ZodTypeAny, {
53
+ intent: string;
54
+ role: string;
55
+ exports: string[];
56
+ depends_on?: string[] | undefined;
57
+ when_to_load?: string | undefined;
58
+ side_effects?: string[] | undefined;
59
+ mutates_state?: boolean | undefined;
60
+ domain?: string | undefined;
61
+ ai_notes?: string | undefined;
62
+ cfm_version?: string | undefined;
63
+ }, {
64
+ intent: string;
65
+ role: string;
66
+ exports: string[];
67
+ depends_on?: string[] | undefined;
68
+ when_to_load?: string | undefined;
69
+ side_effects?: string[] | undefined;
70
+ mutates_state?: boolean | undefined;
71
+ domain?: string | undefined;
72
+ ai_notes?: string | undefined;
73
+ cfm_version?: string | undefined;
74
+ }>;
75
+ export type CfmData = z.infer<typeof CfmSchema>;
76
+ /**
77
+ * CFM 表头宽松 Schema(只要求 intent 字段)
78
+ * 用于渐进式表头补充场景——已有表头但尚不完整的文件
79
+ */
80
+ export declare const CfmSchemaLoose: z.ZodObject<{
81
+ intent: z.ZodString;
82
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
83
+ intent: z.ZodString;
84
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
85
+ intent: z.ZodString;
86
+ }, z.ZodTypeAny, "passthrough">>;
87
+ /**
88
+ * 单个文件的 CFM 条目
89
+ * 包含文件路径和解析出的元数据
90
+ */
91
+ export declare const CfmEntrySchema: z.ZodObject<{
92
+ /** 文件相对路径 */
93
+ file: z.ZodString;
94
+ /** 检测到的编程语言 */
95
+ language: z.ZodOptional<z.ZodString>;
96
+ /** 解析出的 CFM 表头数据 */
97
+ frontmatter: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
98
+ /** 校验警告信息 */
99
+ warnings: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
100
+ }, "strip", z.ZodTypeAny, {
101
+ file: string;
102
+ frontmatter: Record<string, unknown> | null;
103
+ language?: string | undefined;
104
+ warnings?: string[] | undefined;
105
+ }, {
106
+ file: string;
107
+ frontmatter: Record<string, unknown> | null;
108
+ language?: string | undefined;
109
+ warnings?: string[] | undefined;
110
+ }>;
111
+ export type CfmEntry = z.infer<typeof CfmEntrySchema>;
112
+ /**
113
+ * cfm_scan 的返回结果
114
+ */
115
+ export interface ScanResult {
116
+ /** 扫描到的总文件数 */
117
+ total_files: number;
118
+ /** 有 CFM 表头的文件数 */
119
+ files_with_cfm: number;
120
+ /** 无 CFM 表头的文件数 */
121
+ files_without_cfm: number;
122
+ /** 所有文件的 CFM 条目 */
123
+ entries: CfmEntry[];
124
+ }
125
+ /**
126
+ * cfm_search 的返回结果
127
+ */
128
+ export interface SearchResult {
129
+ /** 搜索查询 */
130
+ query: string;
131
+ /** 匹配的文件数 */
132
+ matches: number;
133
+ /** 匹配的 CFM 条目 */
134
+ entries: CfmEntry[];
135
+ }
136
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,kBAAkB;IAC3B,uBAAuB;;IAEvB,wBAAwB;;IAExB,kBAAkB;;IAElB,oCAAoC;;;;;;;;;;;;EAEtC,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,SAAS;IAElB,mBAAmB;;IAEnB,cAAc;;IAEd,0BAA0B;;IAI1B,WAAW;;IAEX,sBAAsB;;IAItB,YAAY;;IAEZ,eAAe;;IAEf,aAAa;;IAEb,mBAAmB;;IAEnB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;EAEhB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;gCAIT,CAAC;AAEnB;;;GAGG;AACH,eAAO,MAAM,cAAc;IACvB,aAAa;;IAEb,eAAe;;IAEf,oBAAoB;;IAEpB,aAAa;;;;;;;;;;;;EAEf,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,eAAe;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB;IACnB,OAAO,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,OAAO,EAAE,QAAQ,EAAE,CAAC;CACvB"}
package/dist/schema.js ADDED
@@ -0,0 +1,79 @@
1
+ /*---
2
+ intent: "定义 CFM 表头的 Zod Schema,用于校验从代码文件中提取的 YAML 元数据"
3
+ role: model
4
+ exports:
5
+ - "CfmSchema: CFM 表头的严格校验 Schema"
6
+ - "CfmSchemaLoose: 宽松模式 Schema(只要求 intent)"
7
+ - "CfmEntry: 单个文件的 CFM 条目类型(含文件路径和解析结果)"
8
+ - "LanguageRule: 语言注释规则类型定义"
9
+ depends_on: ["zod"]
10
+ when_to_load: "修改 CFM 字段定义或校验逻辑时加载"
11
+ ---*/
12
+ import { z } from "zod";
13
+ /**
14
+ * 语言注释规则 Schema
15
+ * 定义某种编程语言的 CFM 注释语法
16
+ */
17
+ export const LanguageRuleSchema = z.object({
18
+ /** 注释起始标记,如 "/*---" */
19
+ comment_start: z.string(),
20
+ /** 注释结束标记,如 "---*​/" */
21
+ comment_end: z.string(),
22
+ /** 该语言的文件扩展名列表 */
23
+ extensions: z.array(z.string()),
24
+ /** 行前缀(脚本语言使用),如 "# ",C 家族为 null */
25
+ line_prefix: z.string().nullable(),
26
+ });
27
+ /**
28
+ * CFM 表头严格 Schema(含全部必选字段)
29
+ * 用于校验完整规范的表头
30
+ */
31
+ export const CfmSchema = z.object({
32
+ // ---- 必选字段 ----
33
+ /** 文件的核心用途和业务价值 */
34
+ intent: z.string().max(500),
35
+ /** 文件的角色类型 */
36
+ role: z.string(),
37
+ /** 暴露的关键 API / 函数 / 组件 */
38
+ exports: z.array(z.string()),
39
+ // ---- 推荐字段 ----
40
+ /** 关键依赖 */
41
+ depends_on: z.array(z.string()).optional(),
42
+ /** 什么场景下才需要读取此文件全文 */
43
+ when_to_load: z.string().optional(),
44
+ // ---- 可选字段 ----
45
+ /** 副作用描述 */
46
+ side_effects: z.array(z.string()).optional(),
47
+ /** 是否修改外部状态 */
48
+ mutates_state: z.boolean().optional(),
49
+ /** 业务领域标签 */
50
+ domain: z.string().optional(),
51
+ /** 给 AI 的特殊注意事项 */
52
+ ai_notes: z.string().optional(),
53
+ /** 表头规范版本号 */
54
+ cfm_version: z.string().optional(),
55
+ });
56
+ /**
57
+ * CFM 表头宽松 Schema(只要求 intent 字段)
58
+ * 用于渐进式表头补充场景——已有表头但尚不完整的文件
59
+ */
60
+ export const CfmSchemaLoose = z
61
+ .object({
62
+ intent: z.string(),
63
+ })
64
+ .passthrough();
65
+ /**
66
+ * 单个文件的 CFM 条目
67
+ * 包含文件路径和解析出的元数据
68
+ */
69
+ export const CfmEntrySchema = z.object({
70
+ /** 文件相对路径 */
71
+ file: z.string(),
72
+ /** 检测到的编程语言 */
73
+ language: z.string().optional(),
74
+ /** 解析出的 CFM 表头数据 */
75
+ frontmatter: z.record(z.unknown()).nullable(),
76
+ /** 校验警告信息 */
77
+ warnings: z.array(z.string()).optional(),
78
+ });
79
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;KAUK;AAEL,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,uBAAuB;IACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,wBAAwB;IACxB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,kBAAkB;IAClB,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/B,oCAAoC;IACpC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,iBAAiB;IACjB,mBAAmB;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3B,cAAc;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,0BAA0B;IAC1B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAE5B,iBAAiB;IACjB,WAAW;IACX,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC1C,sBAAsB;IACtB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEnC,iBAAiB;IACjB,YAAY;IACZ,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC5C,eAAe;IACf,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACrC,aAAa;IACb,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,mBAAmB;IACnB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,cAAc;IACd,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC1B,MAAM,CAAC;IACJ,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC;KACD,WAAW,EAAE,CAAC;AAEnB;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,aAAa;IACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,eAAe;IACf,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,oBAAoB;IACpB,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC7C,aAAa;IACb,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 注册新语言的返回结果
3
+ */
4
+ export interface RegisterResult {
5
+ /** 是否注册成功 */
6
+ success: boolean;
7
+ /** 结果消息 */
8
+ message: string;
9
+ /** 注册后的总语言数 */
10
+ total_languages?: number;
11
+ }
12
+ /**
13
+ * 校验输入参数并注册新语言
14
+ *
15
+ * @param name - 语言名称(如 "elixir")
16
+ * @param config - 注释规则配置
17
+ * @returns 注册结果
18
+ */
19
+ export declare function registerNewLanguage(name: string, config: {
20
+ comment_start: string;
21
+ comment_end: string;
22
+ extensions: string[];
23
+ line_prefix?: string | null;
24
+ }): RegisterResult;
25
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,aAAa;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IACJ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,GACF,cAAc,CAwDhB"}
@@ -0,0 +1,69 @@
1
+ /*---
2
+ intent: "实现 cfm_register_language 工具:允许用户在运行时注册新的编程语言注释规则"
3
+ role: service
4
+ exports:
5
+ - "registerNewLanguage: 校验并注册新语言到语言注册表"
6
+ depends_on: ["../registry.ts", "../schema.ts"]
7
+ when_to_load: "修改语言注册接口或校验逻辑时加载"
8
+ ---*/
9
+ import { registerLanguage, getAllLanguages } from "../registry.js";
10
+ /**
11
+ * 校验输入参数并注册新语言
12
+ *
13
+ * @param name - 语言名称(如 "elixir")
14
+ * @param config - 注释规则配置
15
+ * @returns 注册结果
16
+ */
17
+ export function registerNewLanguage(name, config) {
18
+ // 参数校验
19
+ if (!name || name.trim().length === 0) {
20
+ return {
21
+ success: false,
22
+ message: "语言名称不能为空",
23
+ };
24
+ }
25
+ if (!config.comment_start || !config.comment_end) {
26
+ return {
27
+ success: false,
28
+ message: "comment_start 和 comment_end 不能为空",
29
+ };
30
+ }
31
+ if (!config.extensions || config.extensions.length === 0) {
32
+ return {
33
+ success: false,
34
+ message: "extensions 不能为空,至少需要一个文件扩展名",
35
+ };
36
+ }
37
+ // 校验扩展名格式
38
+ for (const ext of config.extensions) {
39
+ if (!ext.startsWith(".")) {
40
+ return {
41
+ success: false,
42
+ message: `扩展名 "${ext}" 格式错误,必须以 "." 开头(如 ".ex")`,
43
+ };
44
+ }
45
+ }
46
+ // 构造语言规则
47
+ const rule = {
48
+ comment_start: config.comment_start,
49
+ comment_end: config.comment_end,
50
+ extensions: config.extensions,
51
+ line_prefix: config.line_prefix ?? null,
52
+ };
53
+ try {
54
+ registerLanguage(name, rule);
55
+ const allLanguages = getAllLanguages();
56
+ return {
57
+ success: true,
58
+ message: `语言 "${name}" 注册成功(扩展名: ${config.extensions.join(", ")})`,
59
+ total_languages: Object.keys(allLanguages).length,
60
+ };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ success: false,
65
+ message: error.message,
66
+ };
67
+ }
68
+ }
69
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AAAA;;;;;;;KAOK;AAEL,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAenE;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAC/B,IAAY,EACZ,MAKC;IAED,OAAO;IACP,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO;YACH,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,UAAU;SACtB,CAAC;IACN,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO;YACH,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,kCAAkC;SAC9C,CAAC;IACN,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,OAAO;YACH,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,6BAA6B;SACzC,CAAC;IACN,CAAC;IAED,UAAU;IACV,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO;gBACH,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,QAAQ,GAAG,4BAA4B;aACnD,CAAC;QACN,CAAC;IACL,CAAC;IAED,SAAS;IACT,MAAM,IAAI,GAAiB;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;KAC1C,CAAC;IAEF,IAAI,CAAC;QACD,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAE7B,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,OAAO;YACH,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,OAAO,IAAI,eAAe,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAClE,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM;SACpD,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO;YACH,OAAO,EAAE,KAAK;YACd,OAAO,EAAG,KAAe,CAAC,OAAO;SACpC,CAAC;IACN,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ScanResult } from "../schema.js";
2
+ /**
3
+ * 扫描目录中所有代码文件的 CFM 表头
4
+ *
5
+ * @param directory - 要扫描的根目录(绝对路径)
6
+ * @param options - 扫描选项
7
+ * @returns 扫描结果汇总(含所有文件的 CFM 条目)
8
+ */
9
+ export declare function scanDirectory(directory: string, options?: {
10
+ /** 自定义忽略目录 */
11
+ ignoreDirs?: string[];
12
+ /** 是否只返回有 CFM 表头的文件 */
13
+ cfmOnly?: boolean;
14
+ }): Promise<ScanResult>;
15
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAY,UAAU,EAAE,MAAM,cAAc,CAAC;AAiCzD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACL,cAAc;IACd,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,uBAAuB;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CAChB,GACP,OAAO,CAAC,UAAU,CAAC,CAsBrB"}
@@ -0,0 +1,97 @@
1
+ /*---
2
+ intent: "实现 cfm_scan 工具:递归扫描目录下所有代码文件,提取 CFM 表头,返回汇总索引"
3
+ role: service
4
+ exports:
5
+ - "scanDirectory: 扫描指定目录,返回所有文件的 CFM 表头索引"
6
+ depends_on: ["../parser.ts", "../registry.ts", "../schema.ts"]
7
+ when_to_load: "修改文件扫描逻辑、过滤规则或结果格式时加载"
8
+ ai_notes: "默认跳过 node_modules, .git, dist 等常见非源码目录"
9
+ ---*/
10
+ import { readdir, stat } from "node:fs/promises";
11
+ import { join, relative, extname } from "node:path";
12
+ import { extractFrontmatter } from "../parser.js";
13
+ import { getLanguageByExtension } from "../registry.js";
14
+ /** 默认跳过的目录名 */
15
+ const DEFAULT_IGNORE_DIRS = new Set([
16
+ "node_modules",
17
+ ".git",
18
+ ".svn",
19
+ ".hg",
20
+ "dist",
21
+ "build",
22
+ "out",
23
+ ".next",
24
+ ".nuxt",
25
+ "__pycache__",
26
+ ".pytest_cache",
27
+ "venv",
28
+ ".venv",
29
+ "target",
30
+ "vendor",
31
+ ".idea",
32
+ ".vscode",
33
+ "coverage",
34
+ ]);
35
+ /** 默认跳过的文件名 */
36
+ const DEFAULT_IGNORE_FILES = new Set([
37
+ ".DS_Store",
38
+ "Thumbs.db",
39
+ "package-lock.json",
40
+ "yarn.lock",
41
+ "pnpm-lock.yaml",
42
+ ]);
43
+ /**
44
+ * 扫描目录中所有代码文件的 CFM 表头
45
+ *
46
+ * @param directory - 要扫描的根目录(绝对路径)
47
+ * @param options - 扫描选项
48
+ * @returns 扫描结果汇总(含所有文件的 CFM 条目)
49
+ */
50
+ export async function scanDirectory(directory, options = {}) {
51
+ const ignoreDirs = new Set([
52
+ ...DEFAULT_IGNORE_DIRS,
53
+ ...(options.ignoreDirs ?? []),
54
+ ]);
55
+ const entries = [];
56
+ // 递归遍历
57
+ await walkDirectory(directory, directory, ignoreDirs, entries);
58
+ // 按选项过滤
59
+ const filteredEntries = options.cfmOnly
60
+ ? entries.filter((e) => e.frontmatter !== null)
61
+ : entries;
62
+ return {
63
+ total_files: entries.length,
64
+ files_with_cfm: entries.filter((e) => e.frontmatter !== null).length,
65
+ files_without_cfm: entries.filter((e) => e.frontmatter === null).length,
66
+ entries: filteredEntries,
67
+ };
68
+ }
69
+ /**
70
+ * 递归遍历目录
71
+ */
72
+ async function walkDirectory(rootDir, currentDir, ignoreDirs, entries) {
73
+ const items = await readdir(currentDir);
74
+ for (const item of items) {
75
+ const fullPath = join(currentDir, item);
76
+ // 跳过被忽略的文件
77
+ if (DEFAULT_IGNORE_FILES.has(item))
78
+ continue;
79
+ const itemStat = await stat(fullPath);
80
+ if (itemStat.isDirectory()) {
81
+ // 跳过被忽略的目录
82
+ if (ignoreDirs.has(item))
83
+ continue;
84
+ await walkDirectory(rootDir, fullPath, ignoreDirs, entries);
85
+ }
86
+ else if (itemStat.isFile()) {
87
+ const ext = extname(item);
88
+ // 只处理已注册语言的文件
89
+ if (!getLanguageByExtension(ext))
90
+ continue;
91
+ const relativePath = relative(rootDir, fullPath).replace(/\\/g, "/");
92
+ const entry = await extractFrontmatter(fullPath, relativePath);
93
+ entries.push(entry);
94
+ }
95
+ }
96
+ }
97
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { SearchResult } from "../schema.js";
2
+ /**
3
+ * 在项目目录中搜索匹配条件的 CFM 表头
4
+ *
5
+ * 支持三种搜索维度:
6
+ * - keyword: 在 intent、exports、ai_notes 等文本字段中全文搜索
7
+ * - role: 精确匹配文件角色(如 "service", "component")
8
+ * - domain: 精确匹配业务领域标签
9
+ *
10
+ * @param directory - 扫描的根目录
11
+ * @param query - 搜索条件
12
+ * @returns 搜索结果
13
+ */
14
+ export declare function searchFrontmatter(directory: string, query: {
15
+ /** 关键字搜索(在 intent、exports、ai_notes 等字段中匹配) */
16
+ keyword?: string;
17
+ /** 按角色过滤 */
18
+ role?: string;
19
+ /** 按业务领域过滤 */
20
+ domain?: string;
21
+ }): Promise<SearchResult>;
22
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAY,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3D;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACnC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IACH,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,GACF,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
@@ -0,0 +1,84 @@
1
+ /*---
2
+ intent: "实现 cfm_search 工具:在已扫描的 CFM 索引中按关键字、角色、领域等条件搜索匹配文件"
3
+ role: service
4
+ exports:
5
+ - "searchFrontmatter: 搜索匹配条件的 CFM 条目"
6
+ depends_on: ["./scan.ts", "../schema.ts"]
7
+ when_to_load: "修改搜索逻辑或过滤条件时加载"
8
+ ---*/
9
+ import { scanDirectory } from "./scan.js";
10
+ /**
11
+ * 在项目目录中搜索匹配条件的 CFM 表头
12
+ *
13
+ * 支持三种搜索维度:
14
+ * - keyword: 在 intent、exports、ai_notes 等文本字段中全文搜索
15
+ * - role: 精确匹配文件角色(如 "service", "component")
16
+ * - domain: 精确匹配业务领域标签
17
+ *
18
+ * @param directory - 扫描的根目录
19
+ * @param query - 搜索条件
20
+ * @returns 搜索结果
21
+ */
22
+ export async function searchFrontmatter(directory, query) {
23
+ // 先获取全量扫描结果
24
+ const scanResult = await scanDirectory(directory, { cfmOnly: true });
25
+ // 拼接查询描述
26
+ const queryParts = [];
27
+ if (query.keyword)
28
+ queryParts.push(`keyword="${query.keyword}"`);
29
+ if (query.role)
30
+ queryParts.push(`role="${query.role}"`);
31
+ if (query.domain)
32
+ queryParts.push(`domain="${query.domain}"`);
33
+ const queryDescription = queryParts.join(", ");
34
+ // 过滤匹配的条目
35
+ const matched = scanResult.entries.filter((entry) => matchesQuery(entry, query));
36
+ return {
37
+ query: queryDescription,
38
+ matches: matched.length,
39
+ entries: matched,
40
+ };
41
+ }
42
+ /**
43
+ * 判断某个 CFM 条目是否匹配查询条件
44
+ * 所有提供的条件都必须满足(AND 逻辑)
45
+ */
46
+ function matchesQuery(entry, query) {
47
+ const fm = entry.frontmatter;
48
+ if (!fm)
49
+ return false;
50
+ // 角色精确匹配
51
+ if (query.role) {
52
+ if (String(fm.role ?? "").toLowerCase() !== query.role.toLowerCase()) {
53
+ return false;
54
+ }
55
+ }
56
+ // 领域精确匹配
57
+ if (query.domain) {
58
+ if (String(fm.domain ?? "").toLowerCase() !== query.domain.toLowerCase()) {
59
+ return false;
60
+ }
61
+ }
62
+ // 关键字全文搜索
63
+ if (query.keyword) {
64
+ const kw = query.keyword.toLowerCase();
65
+ const searchFields = [
66
+ String(fm.intent ?? ""),
67
+ String(fm.ai_notes ?? ""),
68
+ String(fm.when_to_load ?? ""),
69
+ String(fm.domain ?? ""),
70
+ // 展开数组字段
71
+ ...(Array.isArray(fm.exports) ? fm.exports.map(String) : []),
72
+ ...(Array.isArray(fm.depends_on) ? fm.depends_on.map(String) : []),
73
+ ...(Array.isArray(fm.side_effects) ? fm.side_effects.map(String) : []),
74
+ // 文件路径也纳入搜索
75
+ entry.file,
76
+ ];
77
+ const haystack = searchFields.join(" ").toLowerCase();
78
+ if (!haystack.includes(kw)) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}