-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrules-migrator.ts
More file actions
136 lines (113 loc) · 4.12 KB
/
rules-migrator.ts
File metadata and controls
136 lines (113 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// kilocode_change - new file
import * as fs from "fs/promises"
import * as path from "path"
import os from "os"
export namespace RulesMigrator {
// Only support .kilocoderules (no migration for .roorules or .clinerules)
const LEGACY_RULE_FILE = ".kilocoderules"
// Directory-based rules
const KILOCODE_RULES_DIR = ".kilocode/rules"
const GLOBAL_RULES_DIR = path.join(os.homedir(), ".kilocode", "rules")
// Known modes for mode-specific rule discovery
const KNOWN_MODES = ["code", "architect", "ask", "debug", "orchestrator"]
export interface RuleFile {
path: string
source: "global" | "project" | "legacy"
mode?: string // e.g., "code", "architect" - undefined means applies to all modes
}
export interface MigrationResult {
instructions: string[]
warnings: string[]
}
async function exists(filepath: string): Promise<boolean> {
return Bun.file(filepath).exists()
}
async function isDirectory(filepath: string): Promise<boolean> {
try {
const stat = await fs.stat(filepath)
return stat.isDirectory()
} catch {
return false
}
}
async function findMarkdownFiles(dir: string): Promise<string[]> {
try {
const entries = await fs.readdir(dir, { withFileTypes: true })
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path.join(dir, e.name))
} catch {
return []
}
}
export async function discoverRules(projectDir: string): Promise<RuleFile[]> {
const rules: RuleFile[] = []
// 1. Global rules directory (~/.kilocode/rules/*.md)
if (await isDirectory(GLOBAL_RULES_DIR)) {
const files = await findMarkdownFiles(GLOBAL_RULES_DIR)
for (const file of files) {
rules.push({ path: file, source: "global" })
}
}
// 2. Project .kilocode/rules/ directory
const projectRulesDir = path.join(projectDir, KILOCODE_RULES_DIR)
if (await isDirectory(projectRulesDir)) {
const files = await findMarkdownFiles(projectRulesDir)
for (const file of files) {
rules.push({ path: file, source: "project" })
}
}
// 3. Legacy .kilocoderules file (only kilocode, not roo/cline)
const legacyFile = path.join(projectDir, LEGACY_RULE_FILE)
if (await exists(legacyFile)) {
rules.push({ path: legacyFile, source: "legacy" })
}
// 4. Mode-specific rules
for (const mode of KNOWN_MODES) {
// Mode-specific directory (.kilocode/rules-{mode}/*.md)
const modeDir = path.join(projectDir, `.kilocode/rules-${mode}`)
if (await isDirectory(modeDir)) {
const files = await findMarkdownFiles(modeDir)
for (const file of files) {
rules.push({ path: file, source: "project", mode })
}
}
// Legacy mode-specific file (.kilocoderules-{mode})
const legacyModeFile = path.join(projectDir, `.kilocoderules-${mode}`)
if (await exists(legacyModeFile)) {
rules.push({ path: legacyModeFile, source: "legacy", mode })
}
}
return rules
}
export async function migrate(options: {
projectDir: string
includeGlobal?: boolean
includeModeSpecific?: boolean
}): Promise<MigrationResult> {
const warnings: string[] = []
const instructions: string[] = []
const includeGlobal = options.includeGlobal ?? true
const includeModeSpecific = options.includeModeSpecific ?? true
const rules = await discoverRules(options.projectDir)
for (const rule of rules) {
// Skip global if not requested
if (rule.source === "global" && !includeGlobal) {
continue
}
// Skip mode-specific if not requested
if (rule.mode && !includeModeSpecific) {
warnings.push(`Mode-specific rule '${path.basename(rule.path)}' skipped (mode: ${rule.mode})`)
continue
}
// Add to instructions array
instructions.push(rule.path)
// Warn about legacy files
if (rule.source === "legacy") {
warnings.push(
`Legacy rule file '${path.basename(rule.path)}' found. ` +
`Consider migrating to .kilocode/rules/ directory.`,
)
}
}
return { instructions, warnings }
}
}