()
| 87 | * Scan .continue/rules/ directories for markdown rule files and return the rules with metadata that should be always-applied |
| 88 | */ |
| 89 | export function loadMarkdownRulesWithMetadata(): RuleObject[] { |
| 90 | const cwd = process.cwd(); |
| 91 | const rulesDirs = [ |
| 92 | path.join(cwd, ".continue", "rules"), |
| 93 | path.join(env.continueHome, "rules"), |
| 94 | ]; |
| 95 | |
| 96 | const rules: RuleObject[] = []; |
| 97 | |
| 98 | for (const dir of rulesDirs) { |
| 99 | if (!fs.existsSync(dir)) continue; |
| 100 | |
| 101 | let files: string[]; |
| 102 | try { |
| 103 | files = fs.readdirSync(dir, { recursive: true }) as string[]; |
| 104 | } catch { |
| 105 | continue; |
| 106 | } |
| 107 | |
| 108 | for (const file of files) { |
| 109 | if (!String(file).endsWith(".md")) continue; |
| 110 | |
| 111 | const filePath = path.join(dir, String(file)); |
| 112 | try { |
| 113 | const stat = fs.statSync(filePath); |
| 114 | if (!stat.isFile()) continue; |
| 115 | } catch { |
| 116 | continue; |
| 117 | } |
| 118 | |
| 119 | try { |
| 120 | const content = fs.readFileSync(filePath, "utf-8"); |
| 121 | const { frontmatter, markdown } = parseMarkdownRule(content); |
| 122 | |
| 123 | if (frontmatter.invokable) continue; |
| 124 | |
| 125 | const isAlwaysApply = |
| 126 | frontmatter.alwaysApply === true || |
| 127 | (frontmatter.alwaysApply === undefined && |
| 128 | !frontmatter.globs && |
| 129 | !frontmatter.regex); |
| 130 | |
| 131 | if (isAlwaysApply && markdown.trim()) { |
| 132 | const ruleName = |
| 133 | frontmatter.name || getRuleNameFromPath(String(file)); |
| 134 | rules.push({ |
| 135 | name: ruleName, |
| 136 | rule: markdown, |
| 137 | description: frontmatter.description, |
| 138 | globs: frontmatter.globs, |
| 139 | regex: frontmatter.regex, |
| 140 | alwaysApply: true, |
| 141 | sourceFile: filePath, |
| 142 | }); |
| 143 | } |
| 144 | } catch { |
| 145 | // Skip files that can't be read or parsed |
| 146 | } |
no test coverage detected