@dyyz1993/pi-coding-agent 0.74.24 → 0.74.25

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 (145) hide show
  1. package/dist/core/agent-session.d.ts.map +1 -1
  2. package/dist/core/agent-session.js +3 -0
  3. package/dist/core/agent-session.js.map +1 -1
  4. package/dist/extensions/agent-permissions/index.ts +235 -0
  5. package/dist/extensions/ask-tools/index.ts +115 -0
  6. package/dist/extensions/auto-memory/contract.d.ts +51 -0
  7. package/dist/extensions/auto-memory/contract.d.ts.map +1 -0
  8. package/dist/extensions/auto-memory/contract.js +2 -0
  9. package/dist/extensions/auto-memory/contract.js.map +1 -0
  10. package/dist/extensions/auto-memory/contract.ts +56 -0
  11. package/dist/extensions/auto-memory/index.ts +969 -0
  12. package/dist/extensions/auto-memory/prompts.ts +202 -0
  13. package/dist/extensions/auto-memory/skip-rules.ts +297 -0
  14. package/dist/extensions/auto-memory/utils.ts +208 -0
  15. package/dist/extensions/auto-session-title/index.ts +83 -0
  16. package/dist/extensions/bash-ext/contract.d.ts +79 -0
  17. package/dist/extensions/bash-ext/contract.d.ts.map +1 -0
  18. package/dist/extensions/bash-ext/contract.js +2 -0
  19. package/dist/extensions/bash-ext/contract.js.map +1 -0
  20. package/dist/extensions/bash-ext/contract.ts +69 -0
  21. package/dist/extensions/bash-ext/index.ts +858 -0
  22. package/dist/extensions/claude-hooks-compat/config-loader.ts +49 -0
  23. package/dist/extensions/claude-hooks-compat/handler-runner.ts +377 -0
  24. package/dist/extensions/claude-hooks-compat/if-parser.ts +53 -0
  25. package/dist/extensions/claude-hooks-compat/index.ts +178 -0
  26. package/dist/extensions/claude-hooks-compat/matcher.ts +17 -0
  27. package/dist/extensions/claude-hooks-compat/stdin-builder.ts +27 -0
  28. package/dist/extensions/claude-hooks-compat/types.ts +77 -0
  29. package/dist/extensions/compaction-manager/config.ts +47 -0
  30. package/dist/extensions/compaction-manager/context-fold.ts +63 -0
  31. package/dist/extensions/compaction-manager/index.ts +151 -0
  32. package/dist/extensions/compaction-manager/microcompact.ts +49 -0
  33. package/dist/extensions/compaction-manager/reactive.ts +9 -0
  34. package/dist/extensions/compaction-manager/session-memory.ts +48 -0
  35. package/dist/extensions/coordinator/INTEGRATION.md +376 -0
  36. package/dist/extensions/coordinator/handler.test.ts +277 -0
  37. package/dist/extensions/coordinator/handler.ts +189 -0
  38. package/dist/extensions/coordinator/index.ts +261 -0
  39. package/dist/extensions/coordinator/types.d.ts +100 -0
  40. package/dist/extensions/coordinator/types.d.ts.map +1 -0
  41. package/dist/extensions/coordinator/types.js +2 -0
  42. package/dist/extensions/coordinator/types.js.map +1 -0
  43. package/dist/extensions/coordinator/types.ts +72 -0
  44. package/dist/extensions/file-snapshot/index.ts +131 -0
  45. package/dist/extensions/file-time-guard/README.md +133 -0
  46. package/dist/extensions/file-time-guard/config.ts +13 -0
  47. package/dist/extensions/file-time-guard/index.ts +171 -0
  48. package/dist/extensions/hooks-engine/index.ts +117 -0
  49. package/dist/extensions/lsp/lsp/client/file-tracker.ts +70 -0
  50. package/dist/extensions/lsp/lsp/client/registry.ts +305 -0
  51. package/dist/extensions/lsp/lsp/client/runtime.ts +832 -0
  52. package/dist/extensions/lsp/lsp/config/resolver.ts +573 -0
  53. package/dist/extensions/lsp/lsp/contract.d.ts +101 -0
  54. package/dist/extensions/lsp/lsp/contract.d.ts.map +1 -0
  55. package/dist/extensions/lsp/lsp/contract.js +2 -0
  56. package/dist/extensions/lsp/lsp/contract.js.map +1 -0
  57. package/dist/extensions/lsp/lsp/contract.ts +103 -0
  58. package/dist/extensions/lsp/lsp/hooks/agent-end.ts +169 -0
  59. package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.d.ts +10 -0
  60. package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.d.ts.map +1 -0
  61. package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.js +30 -0
  62. package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.js.map +1 -0
  63. package/dist/extensions/lsp/lsp/hooks/diagnostics-mode.ts +41 -0
  64. package/dist/extensions/lsp/lsp/hooks/writethrough.ts +342 -0
  65. package/dist/extensions/lsp/lsp/index.ts +307 -0
  66. package/dist/extensions/lsp/lsp/lsp.test.ts +684 -0
  67. package/dist/extensions/lsp/lsp/monitoring/server-metrics.ts +176 -0
  68. package/dist/extensions/lsp/lsp/tools/lsp-tool.ts +402 -0
  69. package/dist/extensions/lsp/lsp/utils/dependency-resolver.ts +147 -0
  70. package/dist/extensions/lsp/lsp/utils/diagnostics-wait.ts +41 -0
  71. package/dist/extensions/lsp/lsp/utils/lsp-helpers.d.ts +20 -0
  72. package/dist/extensions/lsp/lsp/utils/lsp-helpers.d.ts.map +1 -0
  73. package/dist/extensions/lsp/lsp/utils/lsp-helpers.js +64 -0
  74. package/dist/extensions/lsp/lsp/utils/lsp-helpers.js.map +1 -0
  75. package/dist/extensions/lsp/lsp/utils/lsp-helpers.ts +76 -0
  76. package/dist/extensions/message-bridge/GUIDE.md +210 -0
  77. package/dist/extensions/message-bridge/index.ts +222 -0
  78. package/dist/extensions/output-guard/index.ts +384 -0
  79. package/dist/extensions/preview/index.ts +278 -0
  80. package/dist/extensions/rules-engine/MATCH_HISTORY_RECONCILIATION.md +111 -0
  81. package/dist/extensions/rules-engine/RULES-ENGINE-GUIDE.md +470 -0
  82. package/dist/extensions/rules-engine/cache.js +232 -0
  83. package/dist/extensions/rules-engine/cache.ts +38 -0
  84. package/dist/extensions/rules-engine/config.js +63 -0
  85. package/dist/extensions/rules-engine/config.ts +70 -0
  86. package/dist/extensions/rules-engine/index.js +1530 -0
  87. package/dist/extensions/rules-engine/index.ts +552 -0
  88. package/dist/extensions/rules-engine/injector.js +68 -0
  89. package/dist/extensions/rules-engine/injector.ts +74 -0
  90. package/dist/extensions/rules-engine/loader.js +179 -0
  91. package/dist/extensions/rules-engine/loader.ts +205 -0
  92. package/dist/extensions/rules-engine/matcher.js +534 -0
  93. package/dist/extensions/rules-engine/matcher.ts +52 -0
  94. package/dist/extensions/rules-engine/types.d.ts +156 -0
  95. package/dist/extensions/rules-engine/types.d.ts.map +1 -0
  96. package/dist/extensions/rules-engine/types.js +2 -0
  97. package/dist/extensions/rules-engine/types.js.map +1 -0
  98. package/dist/extensions/rules-engine/types.ts +169 -0
  99. package/dist/extensions/session-supervisor/checker.ts +116 -0
  100. package/dist/extensions/session-supervisor/config.ts +45 -0
  101. package/dist/extensions/session-supervisor/index.ts +726 -0
  102. package/dist/extensions/session-supervisor/prompts.ts +132 -0
  103. package/dist/extensions/session-supervisor/scheduler.ts +69 -0
  104. package/dist/extensions/session-supervisor/types.ts +215 -0
  105. package/dist/extensions/subagent/README.md +172 -0
  106. package/dist/extensions/subagent/agents/explorer.md +25 -0
  107. package/dist/extensions/subagent/agents/guide.md +27 -0
  108. package/dist/extensions/subagent/agents/planner.md +37 -0
  109. package/dist/extensions/subagent/agents/reviewer.md +35 -0
  110. package/dist/extensions/subagent/agents/scout.md +50 -0
  111. package/dist/extensions/subagent/agents/verification.md +35 -0
  112. package/dist/extensions/subagent/agents/worker.md +24 -0
  113. package/dist/extensions/subagent/agents.ts +25 -0
  114. package/dist/extensions/subagent/index.ts +987 -0
  115. package/dist/extensions/subagent/prompts/implement-and-review.md +10 -0
  116. package/dist/extensions/subagent/prompts/implement.md +10 -0
  117. package/dist/extensions/subagent/prompts/scout-and-plan.md +9 -0
  118. package/dist/extensions/subagent-ext/contract.d.ts +2 -0
  119. package/dist/extensions/subagent-ext/contract.d.ts.map +1 -0
  120. package/dist/extensions/subagent-ext/contract.js +2 -0
  121. package/dist/extensions/subagent-ext/contract.js.map +1 -0
  122. package/dist/extensions/subagent-ext/contract.ts +1 -0
  123. package/dist/extensions/subagent-ext/index.ts +347 -0
  124. package/dist/extensions/subagent-shared/contract.d.ts +25 -0
  125. package/dist/extensions/subagent-shared/contract.d.ts.map +1 -0
  126. package/dist/extensions/subagent-shared/contract.js +2 -0
  127. package/dist/extensions/subagent-shared/contract.js.map +1 -0
  128. package/dist/extensions/subagent-shared/contract.ts +28 -0
  129. package/dist/extensions/subagent-shared/index.ts +4 -0
  130. package/dist/extensions/subagent-shared/render.ts +166 -0
  131. package/dist/extensions/subagent-shared/types.ts +35 -0
  132. package/dist/extensions/subagent-shared/utils.ts +112 -0
  133. package/dist/extensions/subagent-v2/contract.d.ts +2 -0
  134. package/dist/extensions/subagent-v2/contract.d.ts.map +1 -0
  135. package/dist/extensions/subagent-v2/contract.js +2 -0
  136. package/dist/extensions/subagent-v2/contract.js.map +1 -0
  137. package/dist/extensions/subagent-v2/contract.ts +1 -0
  138. package/dist/extensions/subagent-v2/index.ts +599 -0
  139. package/dist/extensions/todo-ext/contract.d.ts +27 -0
  140. package/dist/extensions/todo-ext/contract.d.ts.map +1 -0
  141. package/dist/extensions/todo-ext/contract.js +2 -0
  142. package/dist/extensions/todo-ext/contract.js.map +1 -0
  143. package/dist/extensions/todo-ext/contract.ts +30 -0
  144. package/dist/extensions/todo-ext/index.ts +419 -0
  145. package/package.json +6 -5
@@ -0,0 +1,179 @@
1
+ // packages/coding-agent/extensions/rules-engine/loader.ts
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ function splitComma(val) {
5
+ const result = [];
6
+ let depth = 0;
7
+ let current = "";
8
+ for (const ch of val) {
9
+ if (ch === "{" || ch === "(" || ch === "[") depth++;
10
+ else if (ch === "}" || ch === ")" || ch === "]") depth--;
11
+ if (ch === "," && depth === 0) {
12
+ const trimmed2 = current.trim().replace(/^["']|["']$/g, "");
13
+ if (trimmed2) result.push(trimmed2);
14
+ current = "";
15
+ } else {
16
+ current += ch;
17
+ }
18
+ }
19
+ const trimmed = current.trim().replace(/^["']|["']$/g, "");
20
+ if (trimmed) result.push(trimmed);
21
+ return result;
22
+ }
23
+ function parseFrontmatter(content) {
24
+ const frontmatterRegex = /^---\r?\n([\s\S]*?)\n?---\r?\n([\s\S]*)$/;
25
+ const match = content.match(frontmatterRegex);
26
+ if (!match) {
27
+ return { data: {}, body: content };
28
+ }
29
+ const [, frontmatterStr, body] = match;
30
+ const data = {};
31
+ const lines = frontmatterStr.split("\n");
32
+ let i = 0;
33
+ while (i < lines.length) {
34
+ const line = lines[i];
35
+ const colonIndex = line.indexOf(":");
36
+ if (colonIndex === -1) {
37
+ i++;
38
+ continue;
39
+ }
40
+ const rawKey = line.slice(0, colonIndex).trim();
41
+ let value = line.slice(colonIndex + 1).trim();
42
+ if (value === "" || value === "null" || value === "undefined") {
43
+ const listItems = [];
44
+ let j = i + 1;
45
+ while (j < lines.length) {
46
+ const subLine = lines[j];
47
+ if (subLine.match(/^\s*-\s+/)) {
48
+ listItems.push(
49
+ subLine.replace(/^\s*-\s+/, "").trim().replace(/^["']|["']$/g, "")
50
+ );
51
+ j++;
52
+ } else if (subLine.trim() === "" || subLine.match(/^\s+/)) {
53
+ j++;
54
+ } else {
55
+ break;
56
+ }
57
+ }
58
+ if (listItems.length > 0) {
59
+ const camelKey2 = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
60
+ data[camelKey2] = listItems;
61
+ i = j;
62
+ continue;
63
+ }
64
+ value = null;
65
+ } else if (value.startsWith("[") && value.endsWith("]")) {
66
+ try {
67
+ value = JSON.parse(value.replace(/'/g, '"'));
68
+ } catch (err) {
69
+ console.debug("[rules-engine] JSON array parse failed:", err instanceof Error ? err.message : err);
70
+ value = value.slice(1, -1).split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
71
+ }
72
+ } else if (value.startsWith('"') && value.endsWith('"')) {
73
+ value = value.slice(1, -1);
74
+ } else if (value.startsWith("'") && value.endsWith("'")) {
75
+ value = value.slice(1, -1);
76
+ }
77
+ const camelKey = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
78
+ if ((camelKey === "paths" || camelKey === "globs") && typeof value === "string") {
79
+ data[camelKey] = splitComma(value);
80
+ } else {
81
+ data[camelKey] = value;
82
+ }
83
+ i++;
84
+ }
85
+ return { data, body: body.trim() };
86
+ }
87
+ function extractTitle(body) {
88
+ for (const line of body.split("\n")) {
89
+ const trimmed = line.trim();
90
+ if (trimmed) {
91
+ return trimmed.replace(/^#+\s*/, "").replace(/\*\*/g, "");
92
+ }
93
+ }
94
+ return "Untitled Rule";
95
+ }
96
+ function parsePaths(raw) {
97
+ if (!raw) return [];
98
+ if (typeof raw === "string") {
99
+ return raw.split(",").map((g) => g.trim()).filter(Boolean);
100
+ }
101
+ if (Array.isArray(raw)) return raw;
102
+ return [];
103
+ }
104
+ function parseRuleFile(filePath, content) {
105
+ const { data, body } = parseFrontmatter(content);
106
+ const rawGlobs = data.globs ?? data.paths;
107
+ const globs = parsePaths(rawGlobs);
108
+ const rawPaths = data.paths;
109
+ const paths = parsePaths(rawPaths);
110
+ const isUnconditional = globs.length === 0 || globs.length === 1 && globs[0] === "**";
111
+ const frontmatter = {};
112
+ if (rawGlobs) {
113
+ frontmatter.globs = globs;
114
+ if (rawPaths) {
115
+ frontmatter.paths = paths;
116
+ } else {
117
+ frontmatter.paths = globs;
118
+ }
119
+ }
120
+ if (data.description && typeof data.description === "string") frontmatter.description = data.description;
121
+ if (data.severity && typeof data.severity === "string")
122
+ frontmatter.severity = data.severity;
123
+ if (data.allowedTools)
124
+ frontmatter.allowedTools = typeof data.allowedTools === "string" ? [data.allowedTools] : data.allowedTools;
125
+ if (data.whenToUse && typeof data.whenToUse === "string") frontmatter.whenToUse = data.whenToUse;
126
+ if (data.notifyOnMatch !== void 0)
127
+ frontmatter.notifyOnMatch = data.notifyOnMatch === "true" || data.notifyOnMatch === true;
128
+ if (data.skipInPrompt !== void 0)
129
+ frontmatter.skipInPrompt = data.skipInPrompt === "true" || data.skipInPrompt === true;
130
+ return {
131
+ name: path.basename(filePath, path.extname(filePath)),
132
+ filePath,
133
+ title: extractTitle(body),
134
+ content: body.trim(),
135
+ scope: "project",
136
+ source: "",
137
+ frontmatter,
138
+ isUnconditional
139
+ };
140
+ }
141
+ function scanDir(dir, files = []) {
142
+ if (!fs.existsSync(dir)) return files;
143
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
144
+ for (const entry of entries) {
145
+ const fullPath = path.join(dir, entry.name);
146
+ if (entry.isDirectory()) {
147
+ scanDir(fullPath, files);
148
+ } else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
149
+ files.push(fullPath);
150
+ }
151
+ }
152
+ return files;
153
+ }
154
+ function loadRules(rulesDir) {
155
+ const rules = [];
156
+ const unconditional = [];
157
+ const conditional = [];
158
+ if (!fs.existsSync(rulesDir)) {
159
+ return { rules, unconditional, conditional, loadedAt: Date.now() };
160
+ }
161
+ const files = scanDir(rulesDir);
162
+ for (const filePath of files) {
163
+ const content = fs.readFileSync(filePath, "utf-8");
164
+ const rule = parseRuleFile(filePath, content);
165
+ rules.push(rule);
166
+ if (rule.isUnconditional) {
167
+ unconditional.push(rule);
168
+ } else {
169
+ conditional.push(rule);
170
+ }
171
+ }
172
+ return { rules, unconditional, conditional, loadedAt: Date.now() };
173
+ }
174
+ export {
175
+ loadRules,
176
+ parseFrontmatter,
177
+ parseRuleFile
178
+ };
179
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibG9hZGVyLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgKiBhcyBmcyBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgdHlwZSB7IFBhcnNlZFJ1bGUsIFJ1bGVDYWNoZSwgUnVsZUZyb250bWF0dGVyIH0gZnJvbSBcIi4vdHlwZXMuanNcIjtcblxuZnVuY3Rpb24gc3BsaXRDb21tYSh2YWw6IHN0cmluZyk6IHN0cmluZ1tdIHtcblx0Y29uc3QgcmVzdWx0OiBzdHJpbmdbXSA9IFtdO1xuXHRsZXQgZGVwdGggPSAwO1xuXHRsZXQgY3VycmVudCA9IFwiXCI7XG5cdGZvciAoY29uc3QgY2ggb2YgdmFsKSB7XG5cdFx0aWYgKGNoID09PSBcIntcIiB8fCBjaCA9PT0gXCIoXCIgfHwgY2ggPT09IFwiW1wiKSBkZXB0aCsrO1xuXHRcdGVsc2UgaWYgKGNoID09PSBcIn1cIiB8fCBjaCA9PT0gXCIpXCIgfHwgY2ggPT09IFwiXVwiKSBkZXB0aC0tO1xuXG5cdFx0aWYgKGNoID09PSBcIixcIiAmJiBkZXB0aCA9PT0gMCkge1xuXHRcdFx0Y29uc3QgdHJpbW1lZCA9IGN1cnJlbnQudHJpbSgpLnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgXCJcIik7XG5cdFx0XHRpZiAodHJpbW1lZCkgcmVzdWx0LnB1c2godHJpbW1lZCk7XG5cdFx0XHRjdXJyZW50ID0gXCJcIjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Y3VycmVudCArPSBjaDtcblx0XHR9XG5cdH1cblx0Y29uc3QgdHJpbW1lZCA9IGN1cnJlbnQudHJpbSgpLnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgXCJcIik7XG5cdGlmICh0cmltbWVkKSByZXN1bHQucHVzaCh0cmltbWVkKTtcblx0cmV0dXJuIHJlc3VsdDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlRnJvbnRtYXR0ZXIoY29udGVudDogc3RyaW5nKTogeyBkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjsgYm9keTogc3RyaW5nIH0ge1xuXHRjb25zdCBmcm9udG1hdHRlclJlZ2V4ID0gL14tLS1cXHI/XFxuKFtcXHNcXFNdKj8pXFxuPy0tLVxccj9cXG4oW1xcc1xcU10qKSQvO1xuXHRjb25zdCBtYXRjaCA9IGNvbnRlbnQubWF0Y2goZnJvbnRtYXR0ZXJSZWdleCk7XG5cblx0aWYgKCFtYXRjaCkge1xuXHRcdHJldHVybiB7IGRhdGE6IHt9LCBib2R5OiBjb250ZW50IH07XG5cdH1cblxuXHRjb25zdCBbLCBmcm9udG1hdHRlclN0ciwgYm9keV0gPSBtYXRjaDtcblx0Y29uc3QgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcblxuXHRjb25zdCBsaW5lcyA9IGZyb250bWF0dGVyU3RyLnNwbGl0KFwiXFxuXCIpO1xuXHRsZXQgaSA9IDA7XG5cdHdoaWxlIChpIDwgbGluZXMubGVuZ3RoKSB7XG5cdFx0Y29uc3QgbGluZSA9IGxpbmVzW2ldO1xuXHRcdGNvbnN0IGNvbG9uSW5kZXggPSBsaW5lLmluZGV4T2YoXCI6XCIpO1xuXHRcdGlmIChjb2xvbkluZGV4ID09PSAtMSkge1xuXHRcdFx0aSsrO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXG5cdFx0Y29uc3QgcmF3S2V5ID0gbGluZS5zbGljZSgwLCBjb2xvbkluZGV4KS50cmltKCk7XG5cdFx0bGV0IHZhbHVlOiBzdHJpbmcgfCBzdHJpbmdbXSB8IG51bGwgPSBsaW5lLnNsaWNlKGNvbG9uSW5kZXggKyAxKS50cmltKCk7XG5cblx0XHRpZiAodmFsdWUgPT09IFwiXCIgfHwgdmFsdWUgPT09IFwibnVsbFwiIHx8IHZhbHVlID09PSBcInVuZGVmaW5lZFwiKSB7XG5cdFx0XHRjb25zdCBsaXN0SXRlbXM6IHN0cmluZ1tdID0gW107XG5cdFx0XHRsZXQgaiA9IGkgKyAxO1xuXHRcdFx0d2hpbGUgKGogPCBsaW5lcy5sZW5ndGgpIHtcblx0XHRcdFx0Y29uc3Qgc3ViTGluZSA9IGxpbmVzW2pdO1xuXHRcdFx0XHRpZiAoc3ViTGluZS5tYXRjaCgvXlxccyotXFxzKy8pKSB7XG5cdFx0XHRcdFx0bGlzdEl0ZW1zLnB1c2goXG5cdFx0XHRcdFx0XHRzdWJMaW5lXG5cdFx0XHRcdFx0XHRcdC5yZXBsYWNlKC9eXFxzKi1cXHMrLywgXCJcIilcblx0XHRcdFx0XHRcdFx0LnRyaW0oKVxuXHRcdFx0XHRcdFx0XHQucmVwbGFjZSgvXltcIiddfFtcIiddJC9nLCBcIlwiKSxcblx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdGorKztcblx0XHRcdFx0fSBlbHNlIGlmIChzdWJMaW5lLnRyaW0oKSA9PT0gXCJcIiB8fCBzdWJMaW5lLm1hdGNoKC9eXFxzKy8pKSB7XG5cdFx0XHRcdFx0aisrO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRpZiAobGlzdEl0ZW1zLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0Y29uc3QgY2FtZWxLZXkgPSByYXdLZXkucmVwbGFjZSgvLShbYS16XSkvZywgKF8sIGxldHRlcikgPT4gbGV0dGVyLnRvVXBwZXJDYXNlKCkpO1xuXHRcdFx0XHRkYXRhW2NhbWVsS2V5XSA9IGxpc3RJdGVtcztcblx0XHRcdFx0aSA9IGo7XG5cdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0fVxuXHRcdFx0dmFsdWUgPSBudWxsO1xuXHRcdH0gZWxzZSBpZiAodmFsdWUuc3RhcnRzV2l0aChcIltcIikgJiYgdmFsdWUuZW5kc1dpdGgoXCJdXCIpKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHR2YWx1ZSA9IEpTT04ucGFyc2UodmFsdWUucmVwbGFjZSgvJy9nLCAnXCInKSk7XG5cdFx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdFx0Y29uc29sZS5kZWJ1ZyhcIltydWxlcy1lbmdpbmVdIEpTT04gYXJyYXkgcGFyc2UgZmFpbGVkOlwiLCBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogZXJyKTtcblx0XHRcdFx0dmFsdWUgPSAodmFsdWUgYXMgc3RyaW5nKVxuXHRcdFx0XHRcdC5zbGljZSgxLCAtMSlcblx0XHRcdFx0XHQuc3BsaXQoXCIsXCIpXG5cdFx0XHRcdFx0Lm1hcCgodjogc3RyaW5nKSA9PiB2LnRyaW0oKS5yZXBsYWNlKC9eW1wiJ118W1wiJ10kL2csIFwiXCIpKTtcblx0XHRcdH1cblx0XHR9IGVsc2UgaWYgKHZhbHVlLnN0YXJ0c1dpdGgoJ1wiJykgJiYgdmFsdWUuZW5kc1dpdGgoJ1wiJykpIHtcblx0XHRcdHZhbHVlID0gdmFsdWUuc2xpY2UoMSwgLTEpO1xuXHRcdH0gZWxzZSBpZiAodmFsdWUuc3RhcnRzV2l0aChcIidcIikgJiYgdmFsdWUuZW5kc1dpdGgoXCInXCIpKSB7XG5cdFx0XHR2YWx1ZSA9IHZhbHVlLnNsaWNlKDEsIC0xKTtcblx0XHR9XG5cblx0XHRjb25zdCBjYW1lbEtleSA9IHJhd0tleS5yZXBsYWNlKC8tKFthLXpdKS9nLCAoXywgbGV0dGVyKSA9PiBsZXR0ZXIudG9VcHBlckNhc2UoKSk7XG5cblx0XHRpZiAoKGNhbWVsS2V5ID09PSBcInBhdGhzXCIgfHwgY2FtZWxLZXkgPT09IFwiZ2xvYnNcIikgJiYgdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiKSB7XG5cdFx0XHRkYXRhW2NhbWVsS2V5XSA9IHNwbGl0Q29tbWEodmFsdWUpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRkYXRhW2NhbWVsS2V5XSA9IHZhbHVlO1xuXHRcdH1cblx0XHRpKys7XG5cdH1cblxuXHRyZXR1cm4geyBkYXRhLCBib2R5OiBib2R5LnRyaW0oKSB9O1xufVxuXG5mdW5jdGlvbiBleHRyYWN0VGl0bGUoYm9keTogc3RyaW5nKTogc3RyaW5nIHtcblx0Zm9yIChjb25zdCBsaW5lIG9mIGJvZHkuc3BsaXQoXCJcXG5cIikpIHtcblx0XHRjb25zdCB0cmltbWVkID0gbGluZS50cmltKCk7XG5cdFx0aWYgKHRyaW1tZWQpIHtcblx0XHRcdHJldHVybiB0cmltbWVkLnJlcGxhY2UoL14jK1xccyovLCBcIlwiKS5yZXBsYWNlKC9cXCpcXCovZywgXCJcIik7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBcIlVudGl0bGVkIFJ1bGVcIjtcbn1cblxuZnVuY3Rpb24gcGFyc2VQYXRocyhyYXc6IHVua25vd24pOiBzdHJpbmdbXSB7XG5cdGlmICghcmF3KSByZXR1cm4gW107XG5cdGlmICh0eXBlb2YgcmF3ID09PSBcInN0cmluZ1wiKSB7XG5cdFx0cmV0dXJuIHJhd1xuXHRcdFx0LnNwbGl0KFwiLFwiKVxuXHRcdFx0Lm1hcCgoZykgPT4gZy50cmltKCkpXG5cdFx0XHQuZmlsdGVyKEJvb2xlYW4pO1xuXHR9XG5cdGlmIChBcnJheS5pc0FycmF5KHJhdykpIHJldHVybiByYXcgYXMgc3RyaW5nW107XG5cdHJldHVybiBbXTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlUnVsZUZpbGUoZmlsZVBhdGg6IHN0cmluZywgY29udGVudDogc3RyaW5nKTogUGFyc2VkUnVsZSB7XG5cdGNvbnN0IHsgZGF0YSwgYm9keSB9ID0gcGFyc2VGcm9udG1hdHRlcihjb250ZW50KTtcblx0Y29uc3QgcmF3R2xvYnMgPSBkYXRhLmdsb2JzID8/IGRhdGEucGF0aHM7XG5cdGNvbnN0IGdsb2JzID0gcGFyc2VQYXRocyhyYXdHbG9icyk7XG5cdGNvbnN0IHJhd1BhdGhzID0gZGF0YS5wYXRocztcblx0Y29uc3QgcGF0aHMgPSBwYXJzZVBhdGhzKHJhd1BhdGhzKTtcblx0Y29uc3QgaXNVbmNvbmRpdGlvbmFsID0gZ2xvYnMubGVuZ3RoID09PSAwIHx8IChnbG9icy5sZW5ndGggPT09IDEgJiYgZ2xvYnNbMF0gPT09IFwiKipcIik7XG5cblx0Y29uc3QgZnJvbnRtYXR0ZXI6IFJ1bGVGcm9udG1hdHRlciA9IHt9O1xuXHRpZiAocmF3R2xvYnMpIHtcblx0XHRmcm9udG1hdHRlci5nbG9icyA9IGdsb2JzO1xuXHRcdGlmIChyYXdQYXRocykge1xuXHRcdFx0ZnJvbnRtYXR0ZXIucGF0aHMgPSBwYXRocztcblx0XHR9IGVsc2Uge1xuXHRcdFx0ZnJvbnRtYXR0ZXIucGF0aHMgPSBnbG9icztcblx0XHR9XG5cdH1cblx0aWYgKGRhdGEuZGVzY3JpcHRpb24gJiYgdHlwZW9mIGRhdGEuZGVzY3JpcHRpb24gPT09IFwic3RyaW5nXCIpIGZyb250bWF0dGVyLmRlc2NyaXB0aW9uID0gZGF0YS5kZXNjcmlwdGlvbjtcblx0aWYgKGRhdGEuc2V2ZXJpdHkgJiYgdHlwZW9mIGRhdGEuc2V2ZXJpdHkgPT09IFwic3RyaW5nXCIpXG5cdFx0ZnJvbnRtYXR0ZXIuc2V2ZXJpdHkgPSBkYXRhLnNldmVyaXR5IGFzIFBhcnNlZFJ1bGVbXCJmcm9udG1hdHRlclwiXVtcInNldmVyaXR5XCJdO1xuXHRpZiAoZGF0YS5hbGxvd2VkVG9vbHMpXG5cdFx0ZnJvbnRtYXR0ZXIuYWxsb3dlZFRvb2xzID1cblx0XHRcdHR5cGVvZiBkYXRhLmFsbG93ZWRUb29scyA9PT0gXCJzdHJpbmdcIiA/IFtkYXRhLmFsbG93ZWRUb29sc10gOiAoZGF0YS5hbGxvd2VkVG9vbHMgYXMgc3RyaW5nW10pO1xuXHRpZiAoZGF0YS53aGVuVG9Vc2UgJiYgdHlwZW9mIGRhdGEud2hlblRvVXNlID09PSBcInN0cmluZ1wiKSBmcm9udG1hdHRlci53aGVuVG9Vc2UgPSBkYXRhLndoZW5Ub1VzZTtcblx0aWYgKGRhdGEubm90aWZ5T25NYXRjaCAhPT0gdW5kZWZpbmVkKVxuXHRcdGZyb250bWF0dGVyLm5vdGlmeU9uTWF0Y2ggPSBkYXRhLm5vdGlmeU9uTWF0Y2ggPT09IFwidHJ1ZVwiIHx8IGRhdGEubm90aWZ5T25NYXRjaCA9PT0gdHJ1ZTtcblx0aWYgKGRhdGEuc2tpcEluUHJvbXB0ICE9PSB1bmRlZmluZWQpXG5cdFx0ZnJvbnRtYXR0ZXIuc2tpcEluUHJvbXB0ID0gZGF0YS5za2lwSW5Qcm9tcHQgPT09IFwidHJ1ZVwiIHx8IGRhdGEuc2tpcEluUHJvbXB0ID09PSB0cnVlO1xuXG5cdHJldHVybiB7XG5cdFx0bmFtZTogcGF0aC5iYXNlbmFtZShmaWxlUGF0aCwgcGF0aC5leHRuYW1lKGZpbGVQYXRoKSksXG5cdFx0ZmlsZVBhdGgsXG5cdFx0dGl0bGU6IGV4dHJhY3RUaXRsZShib2R5KSxcblx0XHRjb250ZW50OiBib2R5LnRyaW0oKSxcblx0XHRzY29wZTogXCJwcm9qZWN0XCIsXG5cdFx0c291cmNlOiBcIlwiLFxuXHRcdGZyb250bWF0dGVyLFxuXHRcdGlzVW5jb25kaXRpb25hbCxcblx0fTtcbn1cblxuZnVuY3Rpb24gc2NhbkRpcihkaXI6IHN0cmluZywgZmlsZXM6IHN0cmluZ1tdID0gW10pOiBzdHJpbmdbXSB7XG5cdGlmICghZnMuZXhpc3RzU3luYyhkaXIpKSByZXR1cm4gZmlsZXM7XG5cdGNvbnN0IGVudHJpZXMgPSBmcy5yZWFkZGlyU3luYyhkaXIsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KTtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG5cdFx0Y29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4oZGlyLCBlbnRyeS5uYW1lKTtcblx0XHRpZiAoZW50cnkuaXNEaXJlY3RvcnkoKSkge1xuXHRcdFx0c2NhbkRpcihmdWxsUGF0aCwgZmlsZXMpO1xuXHRcdH0gZWxzZSBpZiAoZW50cnkuaXNGaWxlKCkgJiYgKGVudHJ5Lm5hbWUuZW5kc1dpdGgoXCIubWRcIikgfHwgZW50cnkubmFtZS5lbmRzV2l0aChcIi5tZGNcIikpKSB7XG5cdFx0XHRmaWxlcy5wdXNoKGZ1bGxQYXRoKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIGZpbGVzO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbG9hZFJ1bGVzKHJ1bGVzRGlyOiBzdHJpbmcpOiBSdWxlQ2FjaGUge1xuXHRjb25zdCBydWxlczogUGFyc2VkUnVsZVtdID0gW107XG5cdGNvbnN0IHVuY29uZGl0aW9uYWw6IFBhcnNlZFJ1bGVbXSA9IFtdO1xuXHRjb25zdCBjb25kaXRpb25hbDogUGFyc2VkUnVsZVtdID0gW107XG5cblx0aWYgKCFmcy5leGlzdHNTeW5jKHJ1bGVzRGlyKSkge1xuXHRcdHJldHVybiB7IHJ1bGVzLCB1bmNvbmRpdGlvbmFsLCBjb25kaXRpb25hbCwgbG9hZGVkQXQ6IERhdGUubm93KCkgfTtcblx0fVxuXG5cdGNvbnN0IGZpbGVzID0gc2NhbkRpcihydWxlc0Rpcik7XG5cblx0Zm9yIChjb25zdCBmaWxlUGF0aCBvZiBmaWxlcykge1xuXHRcdGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMoZmlsZVBhdGgsIFwidXRmLThcIik7XG5cdFx0Y29uc3QgcnVsZSA9IHBhcnNlUnVsZUZpbGUoZmlsZVBhdGgsIGNvbnRlbnQpO1xuXHRcdHJ1bGVzLnB1c2gocnVsZSk7XG5cdFx0aWYgKHJ1bGUuaXNVbmNvbmRpdGlvbmFsKSB7XG5cdFx0XHR1bmNvbmRpdGlvbmFsLnB1c2gocnVsZSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbmRpdGlvbmFsLnB1c2gocnVsZSk7XG5cdFx0fVxuXHR9XG5cblx0cmV0dXJuIHsgcnVsZXMsIHVuY29uZGl0aW9uYWwsIGNvbmRpdGlvbmFsLCBsb2FkZWRBdDogRGF0ZS5ub3coKSB9O1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFBLFlBQVksUUFBUTtBQUNwQixZQUFZLFVBQVU7QUFHdEIsU0FBUyxXQUFXLEtBQXVCO0FBQzFDLFFBQU0sU0FBbUIsQ0FBQztBQUMxQixNQUFJLFFBQVE7QUFDWixNQUFJLFVBQVU7QUFDZCxhQUFXLE1BQU0sS0FBSztBQUNyQixRQUFJLE9BQU8sT0FBTyxPQUFPLE9BQU8sT0FBTyxJQUFLO0FBQUEsYUFDbkMsT0FBTyxPQUFPLE9BQU8sT0FBTyxPQUFPLElBQUs7QUFFakQsUUFBSSxPQUFPLE9BQU8sVUFBVSxHQUFHO0FBQzlCLFlBQU1BLFdBQVUsUUFBUSxLQUFLLEVBQUUsUUFBUSxnQkFBZ0IsRUFBRTtBQUN6RCxVQUFJQSxTQUFTLFFBQU8sS0FBS0EsUUFBTztBQUNoQyxnQkFBVTtBQUFBLElBQ1gsT0FBTztBQUNOLGlCQUFXO0FBQUEsSUFDWjtBQUFBLEVBQ0Q7QUFDQSxRQUFNLFVBQVUsUUFBUSxLQUFL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LEtBQUssaUJBQWlCLFdBQVcsQ0FBQyxLQUFLLFlBQVksSUFBSyxLQUFLO0FBQ3RFLE1BQUksS0FBSyxhQUFhLE9BQU8sS0FBSyxjQUFjLFNBQVUsYUFBWSxZQUFZLEtBQUs7QUFDdkYsTUFBSSxLQUFLLGtCQUFrQjtBQUMxQixnQkFBWSxnQkFBZ0IsS0FBSyxrQkFBa0IsVUFBVSxLQUFLLGtCQUFrQjtBQUNyRixNQUFJLEtBQUssaUJBQWlCO0FBQ3pCLGdCQUFZLGVBQWUsS0FBSyxpQkFBaUIsVUFBVSxLQUFLLGlCQUFpQjtBQUVsRixTQUFPO0FBQUEsSUFDTixNQUFXLGNBQVMsVUFBZSxhQUFRLFFBQVEsQ0FBQztBQUFBLElBQ3BEO0FBQUEsSUFDQSxPQUFPLGFBQWEsSUFBSTtBQUFBLElBQ3hCLFNBQVMsS0FBSyxLQUFLO0FBQUEsSUFDbkIsT0FBTztBQUFBLElBQ1AsUUFBUTtBQUFBLElBQ1I7QUFBQSxJQUNBO0FBQUEsRUFDRDtBQUNEO0FBRUEsU0FBUyxRQUFRLEtBQWEsUUFBa0IsQ0FBQyxHQUFhO0FBQzdELE1BQUksQ0FBSSxjQUFXLEdBQUcsRUFBRyxRQUFPO0FBQ2hDLFFBQU0sVUFBYSxlQUFZLEtBQUssRUFBRSxlQUFlLEtBQUssQ0FBQztBQUMzRCxhQUFXLFNBQVMsU0FBUztBQUM1QixVQUFNLFdBQWdCLFVBQUssS0FBSyxNQUFNLElBQUk7QUFDMUMsUUFBSSxNQUFNLFlBQVksR0FBRztBQUN4QixjQUFRLFVBQVUsS0FBSztBQUFBLElBQ3hCLFdBQVcsTUFBTSxPQUFPLE1BQU0sTUFBTSxLQUFLLFNBQVMsS0FBSyxLQUFLLE1BQU0sS0FBSyxTQUFTLE1BQU0sSUFBSTtBQUN6RixZQUFNLEtBQUssUUFBUTtBQUFBLElBQ3BCO0FBQUEsRUFDRDtBQUNBLFNBQU87QUFDUjtBQUVPLFNBQVMsVUFBVSxVQUE2QjtBQUN0RCxRQUFNLFFBQXNCLENBQUM7QUFDN0IsUUFBTSxnQkFBOEIsQ0FBQztBQUNyQyxRQUFNLGNBQTRCLENBQUM7QUFFbkMsTUFBSSxDQUFJLGNBQVcsUUFBUSxHQUFHO0FBQzdCLFdBQU8sRUFBRSxPQUFPLGVBQWUsYUFBYSxVQUFVLEtBQUssSUFBSSxFQUFFO0FBQUEsRUFDbEU7QUFFQSxRQUFNLFFBQVEsUUFBUSxRQUFRO0FBRTlCLGFBQVcsWUFBWSxPQUFPO0FBQzdCLFVBQU0sVUFBYSxnQkFBYSxVQUFVLE9BQU87QUFDakQsVUFBTSxPQUFPLGNBQWMsVUFBVSxPQUFPO0FBQzVDLFVBQU0sS0FBSyxJQUFJO0FBQ2YsUUFBSSxLQUFLLGlCQUFpQjtBQUN6QixvQkFBYyxLQUFLLElBQUk7QUFBQSxJQUN4QixPQUFPO0FBQ04sa0JBQVksS0FBSyxJQUFJO0FBQUEsSUFDdEI7QUFBQSxFQUNEO0FBRUEsU0FBTyxFQUFFLE9BQU8sZUFBZSxhQUFhLFVBQVUsS0FBSyxJQUFJLEVBQUU7QUFDbEU7IiwKICAibmFtZXMiOiBbInRyaW1tZWQiLCAiY2FtZWxLZXkiXQp9Cg==
@@ -0,0 +1,205 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { ParsedRule, RuleCache, RuleFrontmatter } from "./types.js";
4
+
5
+ function splitComma(val: string): string[] {
6
+ const result: string[] = [];
7
+ let depth = 0;
8
+ let current = "";
9
+ for (const ch of val) {
10
+ if (ch === "{" || ch === "(" || ch === "[") depth++;
11
+ else if (ch === "}" || ch === ")" || ch === "]") depth--;
12
+
13
+ if (ch === "," && depth === 0) {
14
+ const trimmed = current.trim().replace(/^["']|["']$/g, "");
15
+ if (trimmed) result.push(trimmed);
16
+ current = "";
17
+ } else {
18
+ current += ch;
19
+ }
20
+ }
21
+ const trimmed = current.trim().replace(/^["']|["']$/g, "");
22
+ if (trimmed) result.push(trimmed);
23
+ return result;
24
+ }
25
+
26
+ export function parseFrontmatter(content: string): { data: Record<string, unknown>; body: string } {
27
+ const frontmatterRegex = /^---\r?\n([\s\S]*?)\n?---\r?\n([\s\S]*)$/;
28
+ const match = content.match(frontmatterRegex);
29
+
30
+ if (!match) {
31
+ return { data: {}, body: content };
32
+ }
33
+
34
+ const [, frontmatterStr, body] = match;
35
+ const data: Record<string, unknown> = {};
36
+
37
+ const lines = frontmatterStr.split("\n");
38
+ let i = 0;
39
+ while (i < lines.length) {
40
+ const line = lines[i];
41
+ const colonIndex = line.indexOf(":");
42
+ if (colonIndex === -1) {
43
+ i++;
44
+ continue;
45
+ }
46
+
47
+ const rawKey = line.slice(0, colonIndex).trim();
48
+ let value: string | string[] | null = line.slice(colonIndex + 1).trim();
49
+
50
+ if (value === "" || value === "null" || value === "undefined") {
51
+ const listItems: string[] = [];
52
+ let j = i + 1;
53
+ while (j < lines.length) {
54
+ const subLine = lines[j];
55
+ if (subLine.match(/^\s*-\s+/)) {
56
+ listItems.push(
57
+ subLine
58
+ .replace(/^\s*-\s+/, "")
59
+ .trim()
60
+ .replace(/^["']|["']$/g, ""),
61
+ );
62
+ j++;
63
+ } else if (subLine.trim() === "" || subLine.match(/^\s+/)) {
64
+ j++;
65
+ } else {
66
+ break;
67
+ }
68
+ }
69
+ if (listItems.length > 0) {
70
+ const camelKey = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
71
+ data[camelKey] = listItems;
72
+ i = j;
73
+ continue;
74
+ }
75
+ value = null;
76
+ } else if (value.startsWith("[") && value.endsWith("]")) {
77
+ try {
78
+ value = JSON.parse(value.replace(/'/g, '"'));
79
+ } catch (err) {
80
+ console.debug("[rules-engine] JSON array parse failed:", err instanceof Error ? err.message : err);
81
+ value = (value as string)
82
+ .slice(1, -1)
83
+ .split(",")
84
+ .map((v: string) => v.trim().replace(/^["']|["']$/g, ""));
85
+ }
86
+ } else if (value.startsWith('"') && value.endsWith('"')) {
87
+ value = value.slice(1, -1);
88
+ } else if (value.startsWith("'") && value.endsWith("'")) {
89
+ value = value.slice(1, -1);
90
+ }
91
+
92
+ const camelKey = rawKey.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
93
+
94
+ if ((camelKey === "paths" || camelKey === "globs") && typeof value === "string") {
95
+ data[camelKey] = splitComma(value);
96
+ } else {
97
+ data[camelKey] = value;
98
+ }
99
+ i++;
100
+ }
101
+
102
+ return { data, body: body.trim() };
103
+ }
104
+
105
+ function extractTitle(body: string): string {
106
+ for (const line of body.split("\n")) {
107
+ const trimmed = line.trim();
108
+ if (trimmed) {
109
+ return trimmed.replace(/^#+\s*/, "").replace(/\*\*/g, "");
110
+ }
111
+ }
112
+ return "Untitled Rule";
113
+ }
114
+
115
+ function parsePaths(raw: unknown): string[] {
116
+ if (!raw) return [];
117
+ if (typeof raw === "string") {
118
+ return raw
119
+ .split(",")
120
+ .map((g) => g.trim())
121
+ .filter(Boolean);
122
+ }
123
+ if (Array.isArray(raw)) return raw as string[];
124
+ return [];
125
+ }
126
+
127
+ export function parseRuleFile(filePath: string, content: string): ParsedRule {
128
+ const { data, body } = parseFrontmatter(content);
129
+ const rawGlobs = data.globs ?? data.paths;
130
+ const globs = parsePaths(rawGlobs);
131
+ const rawPaths = data.paths;
132
+ const paths = parsePaths(rawPaths);
133
+ const isUnconditional = globs.length === 0 || (globs.length === 1 && globs[0] === "**");
134
+
135
+ const frontmatter: RuleFrontmatter = {};
136
+ if (rawGlobs) {
137
+ frontmatter.globs = globs;
138
+ if (rawPaths) {
139
+ frontmatter.paths = paths;
140
+ } else {
141
+ frontmatter.paths = globs;
142
+ }
143
+ }
144
+ if (data.description && typeof data.description === "string") frontmatter.description = data.description;
145
+ if (data.severity && typeof data.severity === "string")
146
+ frontmatter.severity = data.severity as ParsedRule["frontmatter"]["severity"];
147
+ if (data.allowedTools)
148
+ frontmatter.allowedTools =
149
+ typeof data.allowedTools === "string" ? [data.allowedTools] : (data.allowedTools as string[]);
150
+ if (data.whenToUse && typeof data.whenToUse === "string") frontmatter.whenToUse = data.whenToUse;
151
+ if (data.notifyOnMatch !== undefined)
152
+ frontmatter.notifyOnMatch = data.notifyOnMatch === "true" || data.notifyOnMatch === true;
153
+ if (data.skipInPrompt !== undefined)
154
+ frontmatter.skipInPrompt = data.skipInPrompt === "true" || data.skipInPrompt === true;
155
+
156
+ return {
157
+ name: path.basename(filePath, path.extname(filePath)),
158
+ filePath,
159
+ title: extractTitle(body),
160
+ content: body.trim(),
161
+ scope: "project",
162
+ source: "",
163
+ frontmatter,
164
+ isUnconditional,
165
+ };
166
+ }
167
+
168
+ function scanDir(dir: string, files: string[] = []): string[] {
169
+ if (!fs.existsSync(dir)) return files;
170
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
171
+ for (const entry of entries) {
172
+ const fullPath = path.join(dir, entry.name);
173
+ if (entry.isDirectory()) {
174
+ scanDir(fullPath, files);
175
+ } else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
176
+ files.push(fullPath);
177
+ }
178
+ }
179
+ return files;
180
+ }
181
+
182
+ export function loadRules(rulesDir: string): RuleCache {
183
+ const rules: ParsedRule[] = [];
184
+ const unconditional: ParsedRule[] = [];
185
+ const conditional: ParsedRule[] = [];
186
+
187
+ if (!fs.existsSync(rulesDir)) {
188
+ return { rules, unconditional, conditional, loadedAt: Date.now() };
189
+ }
190
+
191
+ const files = scanDir(rulesDir);
192
+
193
+ for (const filePath of files) {
194
+ const content = fs.readFileSync(filePath, "utf-8");
195
+ const rule = parseRuleFile(filePath, content);
196
+ rules.push(rule);
197
+ if (rule.isUnconditional) {
198
+ unconditional.push(rule);
199
+ } else {
200
+ conditional.push(rule);
201
+ }
202
+ }
203
+
204
+ return { rules, unconditional, conditional, loadedAt: Date.now() };
205
+ }