rulesync 0.54.0 → 0.56.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.
- package/README.ja.md +67 -5
- package/README.md +77 -6
- package/dist/{augmentcode-MJYD2Y4S.js → augmentcode-HIZIQG2W.js} +2 -2
- package/dist/chunk-7E4U4YAB.js +17 -0
- package/dist/{chunk-D7XQ4OHK.js → chunk-7UBF4OLN.js} +1 -1
- package/dist/{chunk-VI6SBYFB.js → chunk-AUUSMVCT.js} +2 -1
- package/dist/chunk-J3TBR5EP.js +292 -0
- package/dist/{chunk-OXKDEZJK.js → chunk-KUGTKMNW.js} +1 -1
- package/dist/{chunk-QVPD6ENS.js → chunk-LXTA7DBA.js} +1 -1
- package/dist/chunk-OA473EXZ.js +17 -0
- package/dist/{chunk-BEPSWIZC.js → chunk-PCATT4UZ.js} +1 -1
- package/dist/chunk-VKNCBVZF.js +17 -0
- package/dist/chunk-VNT6AHHO.js +17 -0
- package/dist/chunk-W2WU253H.js +17 -0
- package/dist/chunk-WAX2UANS.js +61 -0
- package/dist/{chunk-ORNO5MOO.js → chunk-YTU3SCQO.js} +1 -1
- package/dist/{claudecode-CKGUHLRR.js → claudecode-VVI2PTKI.js} +3 -3
- package/dist/{cline-Z5C656VR.js → cline-BJLFSLEB.js} +3 -3
- package/dist/{codexcli-VFUJKSIJ.js → codexcli-LKWQB3V3.js} +3 -3
- package/dist/{copilot-4WQS5TA7.js → copilot-MOR3HHJX.js} +2 -2
- package/dist/{cursor-HOB2F2V2.js → cursor-2BVUO64T.js} +3 -2
- package/dist/{geminicli-XTMQTIU2.js → geminicli-5YFMKRFL.js} +3 -2
- package/dist/index.cjs +898 -493
- package/dist/index.js +669 -337
- package/dist/{junie-AN6CR7DD.js → junie-5TDJPUXX.js} +3 -2
- package/dist/{kiro-PTUZOHQ2.js → kiro-YDHXY2MA.js} +2 -2
- package/dist/{roo-WOMS36KU.js → roo-L3QTTIPO.js} +2 -2
- package/dist/windsurf-PXDRIQ76.js +10 -0
- package/package.json +1 -1
- package/dist/chunk-3PHMFVXP.js +0 -66
- package/dist/chunk-OY6BYYIX.js +0 -63
- package/dist/chunk-PPAQWVXX.js +0 -94
- package/dist/chunk-TJKD6LEW.js +0 -90
- package/dist/chunk-UHANRG2O.js +0 -54
- package/dist/chunk-UZCJNUXO.js +0 -67
package/dist/index.js
CHANGED
|
@@ -1,42 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateJunieMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VNT6AHHO.js";
|
|
5
5
|
import {
|
|
6
6
|
generateKiroMcp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LXTA7DBA.js";
|
|
8
8
|
import {
|
|
9
9
|
generateRooMcp
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PCATT4UZ.js";
|
|
11
|
+
import {
|
|
12
|
+
generateWindsurfMcp
|
|
13
|
+
} from "./chunk-7E4U4YAB.js";
|
|
11
14
|
import {
|
|
12
15
|
generateAugmentcodeMcp
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YTU3SCQO.js";
|
|
14
17
|
import {
|
|
15
18
|
generateClaudeMcp
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VKNCBVZF.js";
|
|
17
20
|
import {
|
|
18
21
|
generateClineMcp
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-W2WU253H.js";
|
|
20
23
|
import {
|
|
21
24
|
generateCodexMcp
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-PPAQWVXX.js";
|
|
25
|
+
} from "./chunk-7UBF4OLN.js";
|
|
24
26
|
import {
|
|
25
27
|
generateCopilotMcp
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-KUGTKMNW.js";
|
|
27
29
|
import {
|
|
28
30
|
generateCursorMcp
|
|
29
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-OA473EXZ.js";
|
|
30
32
|
import {
|
|
31
33
|
generateGeminiCliMcp
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-WAX2UANS.js";
|
|
35
|
+
import "./chunk-J3TBR5EP.js";
|
|
33
36
|
import {
|
|
34
37
|
ALL_TOOL_TARGETS,
|
|
35
38
|
RulesyncTargetsSchema,
|
|
36
39
|
ToolTargetSchema,
|
|
37
40
|
ToolTargetsSchema,
|
|
38
41
|
isToolTarget
|
|
39
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-AUUSMVCT.js";
|
|
40
43
|
|
|
41
44
|
// src/cli/index.ts
|
|
42
45
|
import { Command } from "commander";
|
|
@@ -60,7 +63,8 @@ function getDefaultConfig() {
|
|
|
60
63
|
roo: ".roo/rules",
|
|
61
64
|
geminicli: ".gemini/memories",
|
|
62
65
|
kiro: ".kiro/steering",
|
|
63
|
-
junie: "."
|
|
66
|
+
junie: ".",
|
|
67
|
+
windsurf: "."
|
|
64
68
|
},
|
|
65
69
|
watchEnabled: false,
|
|
66
70
|
defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
|
|
@@ -147,7 +151,8 @@ var OutputPathsSchema = z3.object({
|
|
|
147
151
|
roo: z3.optional(z3.string()),
|
|
148
152
|
geminicli: z3.optional(z3.string()),
|
|
149
153
|
kiro: z3.optional(z3.string()),
|
|
150
|
-
junie: z3.optional(z3.string())
|
|
154
|
+
junie: z3.optional(z3.string()),
|
|
155
|
+
windsurf: z3.optional(z3.string())
|
|
151
156
|
});
|
|
152
157
|
var ConfigOptionsSchema = z3.object({
|
|
153
158
|
aiRulesDir: z3.optional(z3.string()),
|
|
@@ -227,6 +232,8 @@ var RuleFrontmatterSchema = z5.object({
|
|
|
227
232
|
description: z5.string(),
|
|
228
233
|
globs: z5.array(z5.string()),
|
|
229
234
|
cursorRuleType: z5.optional(z5.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
235
|
+
windsurfActivationMode: z5.optional(z5.enum(["always", "manual", "model-decision", "glob"])),
|
|
236
|
+
windsurfOutputFormat: z5.optional(z5.enum(["single-file", "directory"])),
|
|
230
237
|
tags: z5.optional(z5.array(z5.string()))
|
|
231
238
|
});
|
|
232
239
|
var ParsedRuleSchema = z5.object({
|
|
@@ -435,6 +442,36 @@ function mergeWithCliOptions(config, cliOptions) {
|
|
|
435
442
|
return merged;
|
|
436
443
|
}
|
|
437
444
|
|
|
445
|
+
// src/utils/error.ts
|
|
446
|
+
function getErrorMessage(error) {
|
|
447
|
+
return error instanceof Error ? error.message : String(error);
|
|
448
|
+
}
|
|
449
|
+
function formatErrorWithContext(error, context) {
|
|
450
|
+
const errorMessage = getErrorMessage(error);
|
|
451
|
+
return `${context}: ${errorMessage}`;
|
|
452
|
+
}
|
|
453
|
+
function createErrorResult(error, context) {
|
|
454
|
+
const errorMessage = context ? formatErrorWithContext(error, context) : getErrorMessage(error);
|
|
455
|
+
return {
|
|
456
|
+
success: false,
|
|
457
|
+
error: errorMessage
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function createSuccessResult(result) {
|
|
461
|
+
return {
|
|
462
|
+
success: true,
|
|
463
|
+
result
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
async function safeAsyncOperation(operation, errorContext) {
|
|
467
|
+
try {
|
|
468
|
+
const result = await operation();
|
|
469
|
+
return createSuccessResult(result);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
return createErrorResult(error, errorContext);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
438
475
|
// src/utils/file.ts
|
|
439
476
|
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
440
477
|
import { dirname, join as join2 } from "path";
|
|
@@ -445,6 +482,9 @@ async function ensureDir(dirPath) {
|
|
|
445
482
|
await mkdir2(dirPath, { recursive: true });
|
|
446
483
|
}
|
|
447
484
|
}
|
|
485
|
+
function resolvePath(relativePath, baseDir) {
|
|
486
|
+
return baseDir ? join2(baseDir, relativePath) : relativePath;
|
|
487
|
+
}
|
|
448
488
|
async function readFileContent(filepath) {
|
|
449
489
|
return readFile(filepath, "utf-8");
|
|
450
490
|
}
|
|
@@ -735,7 +775,7 @@ export default config;
|
|
|
735
775
|
}
|
|
736
776
|
|
|
737
777
|
// src/cli/commands/generate.ts
|
|
738
|
-
import { join as
|
|
778
|
+
import { join as join11 } from "path";
|
|
739
779
|
|
|
740
780
|
// src/generators/ignore/shared-factory.ts
|
|
741
781
|
import { join as join3 } from "path";
|
|
@@ -1295,6 +1335,131 @@ var ignoreConfigs = {
|
|
|
1295
1335
|
],
|
|
1296
1336
|
includeCommonPatterns: false,
|
|
1297
1337
|
projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
|
|
1338
|
+
},
|
|
1339
|
+
windsurf: {
|
|
1340
|
+
tool: "windsurf",
|
|
1341
|
+
filename: ".codeiumignore",
|
|
1342
|
+
header: [
|
|
1343
|
+
"# Generated by rulesync - Windsurf AI Code Editor ignore file",
|
|
1344
|
+
"# This file controls which files are excluded from Cascade AI analysis and context",
|
|
1345
|
+
"# Uses same syntax as .gitignore patterns",
|
|
1346
|
+
"# Note: Git-ignored files are automatically excluded by Windsurf"
|
|
1347
|
+
],
|
|
1348
|
+
corePatterns: [
|
|
1349
|
+
"# \u2500\u2500\u2500\u2500\u2500 Security & Credentials (Critical) \u2500\u2500\u2500\u2500\u2500",
|
|
1350
|
+
"# Environment files",
|
|
1351
|
+
".env*",
|
|
1352
|
+
"!.env.example",
|
|
1353
|
+
"",
|
|
1354
|
+
"# Private keys and certificates",
|
|
1355
|
+
"*.pem",
|
|
1356
|
+
"*.key",
|
|
1357
|
+
"*.crt",
|
|
1358
|
+
"*.p12",
|
|
1359
|
+
"*.pfx",
|
|
1360
|
+
"*.der",
|
|
1361
|
+
"",
|
|
1362
|
+
"# SSH keys",
|
|
1363
|
+
"id_rsa*",
|
|
1364
|
+
"id_dsa*",
|
|
1365
|
+
"*.ppk",
|
|
1366
|
+
"",
|
|
1367
|
+
"# API keys and tokens",
|
|
1368
|
+
"**/apikeys/",
|
|
1369
|
+
"**/*_token*",
|
|
1370
|
+
"**/*_secret*",
|
|
1371
|
+
"**/*api_key*",
|
|
1372
|
+
"",
|
|
1373
|
+
"# Cloud provider credentials",
|
|
1374
|
+
"aws-credentials.json",
|
|
1375
|
+
"gcp-service-account*.json",
|
|
1376
|
+
"azure-credentials.json",
|
|
1377
|
+
"",
|
|
1378
|
+
"# \u2500\u2500\u2500\u2500\u2500 Database & Configuration Files \u2500\u2500\u2500\u2500\u2500",
|
|
1379
|
+
"# Database files",
|
|
1380
|
+
"*.db",
|
|
1381
|
+
"*.sqlite",
|
|
1382
|
+
"*.sqlite3",
|
|
1383
|
+
"",
|
|
1384
|
+
"# Configuration files with secrets",
|
|
1385
|
+
"config/secrets/",
|
|
1386
|
+
"**/database.yml",
|
|
1387
|
+
"",
|
|
1388
|
+
"# \u2500\u2500\u2500\u2500\u2500 Build Artifacts & Dependencies \u2500\u2500\u2500\u2500\u2500",
|
|
1389
|
+
"# Build outputs",
|
|
1390
|
+
"dist/",
|
|
1391
|
+
"build/",
|
|
1392
|
+
"out/",
|
|
1393
|
+
"target/",
|
|
1394
|
+
"",
|
|
1395
|
+
"# Dependencies (already auto-excluded but reinforced)",
|
|
1396
|
+
"node_modules/",
|
|
1397
|
+
".pnpm-store/",
|
|
1398
|
+
".yarn/",
|
|
1399
|
+
"vendor/",
|
|
1400
|
+
"",
|
|
1401
|
+
"# \u2500\u2500\u2500\u2500\u2500 Cache & Temporary Files \u2500\u2500\u2500\u2500\u2500",
|
|
1402
|
+
"# Cache directories",
|
|
1403
|
+
".cache/",
|
|
1404
|
+
".parcel-cache/",
|
|
1405
|
+
".next/cache/",
|
|
1406
|
+
"",
|
|
1407
|
+
"# Temporary files",
|
|
1408
|
+
"*.tmp",
|
|
1409
|
+
"*.swp",
|
|
1410
|
+
"*.swo",
|
|
1411
|
+
"*~",
|
|
1412
|
+
"",
|
|
1413
|
+
"# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
|
|
1414
|
+
"# Data files",
|
|
1415
|
+
"*.csv",
|
|
1416
|
+
"*.xlsx",
|
|
1417
|
+
"*.json",
|
|
1418
|
+
"data/",
|
|
1419
|
+
"datasets/",
|
|
1420
|
+
"",
|
|
1421
|
+
"# Media files",
|
|
1422
|
+
"*.mp4",
|
|
1423
|
+
"*.avi",
|
|
1424
|
+
"*.mov",
|
|
1425
|
+
"*.png",
|
|
1426
|
+
"*.jpg",
|
|
1427
|
+
"*.jpeg",
|
|
1428
|
+
"*.gif",
|
|
1429
|
+
"",
|
|
1430
|
+
"# Archives",
|
|
1431
|
+
"*.zip",
|
|
1432
|
+
"*.tar.gz",
|
|
1433
|
+
"*.rar",
|
|
1434
|
+
"",
|
|
1435
|
+
"# \u2500\u2500\u2500\u2500\u2500 IDE & Editor Files \u2500\u2500\u2500\u2500\u2500",
|
|
1436
|
+
"# IDE settings (personal)",
|
|
1437
|
+
".vscode/settings.json",
|
|
1438
|
+
".idea/",
|
|
1439
|
+
"",
|
|
1440
|
+
"# Editor temporary files",
|
|
1441
|
+
"*.swp",
|
|
1442
|
+
"*.swo",
|
|
1443
|
+
"",
|
|
1444
|
+
"# \u2500\u2500\u2500\u2500\u2500 Test Coverage & Logs \u2500\u2500\u2500\u2500\u2500",
|
|
1445
|
+
"# Test coverage reports",
|
|
1446
|
+
"coverage/",
|
|
1447
|
+
".nyc_output/",
|
|
1448
|
+
"",
|
|
1449
|
+
"# Logs",
|
|
1450
|
+
"*.log",
|
|
1451
|
+
"",
|
|
1452
|
+
"# \u2500\u2500\u2500\u2500\u2500 Re-include Important Files \u2500\u2500\u2500\u2500\u2500",
|
|
1453
|
+
"# Allow configuration examples",
|
|
1454
|
+
"!.env.example",
|
|
1455
|
+
"!config/*.example.*",
|
|
1456
|
+
"",
|
|
1457
|
+
"# Allow documentation",
|
|
1458
|
+
"!docs/**/*.md",
|
|
1459
|
+
"!README.md"
|
|
1460
|
+
],
|
|
1461
|
+
includeCommonPatterns: false,
|
|
1462
|
+
projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
|
|
1298
1463
|
}
|
|
1299
1464
|
};
|
|
1300
1465
|
|
|
@@ -1313,6 +1478,11 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
|
1313
1478
|
return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
|
|
1314
1479
|
}
|
|
1315
1480
|
|
|
1481
|
+
// src/generators/ignore/windsurf.ts
|
|
1482
|
+
function generateWindsurfIgnore(rules, config, baseDir) {
|
|
1483
|
+
return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1316
1486
|
// src/generators/rules/augmentcode.ts
|
|
1317
1487
|
import { join as join6 } from "path";
|
|
1318
1488
|
|
|
@@ -1372,7 +1542,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
1372
1542
|
|
|
1373
1543
|
// src/generators/rules/shared-helpers.ts
|
|
1374
1544
|
function resolveOutputDir(config, tool, baseDir) {
|
|
1375
|
-
return
|
|
1545
|
+
return resolvePath(config.outputPaths[tool], baseDir);
|
|
1376
1546
|
}
|
|
1377
1547
|
function createOutputsArray() {
|
|
1378
1548
|
return [];
|
|
@@ -1399,7 +1569,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1399
1569
|
}
|
|
1400
1570
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1401
1571
|
if (ignorePatterns.patterns.length > 0) {
|
|
1402
|
-
const ignorePath =
|
|
1572
|
+
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1403
1573
|
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1404
1574
|
outputs.push({
|
|
1405
1575
|
tool: generatorConfig.tool,
|
|
@@ -1417,7 +1587,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1417
1587
|
if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
|
|
1418
1588
|
for (const rule of detailRules) {
|
|
1419
1589
|
const content = generatorConfig.generateDetailContent(rule);
|
|
1420
|
-
const filepath =
|
|
1590
|
+
const filepath = resolvePath(
|
|
1591
|
+
join5(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
1592
|
+
baseDir
|
|
1593
|
+
);
|
|
1421
1594
|
outputs.push({
|
|
1422
1595
|
tool: generatorConfig.tool,
|
|
1423
1596
|
filepath,
|
|
@@ -1427,7 +1600,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1427
1600
|
}
|
|
1428
1601
|
if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
|
|
1429
1602
|
const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
|
|
1430
|
-
const rootFilepath =
|
|
1603
|
+
const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
|
|
1431
1604
|
outputs.push({
|
|
1432
1605
|
tool: generatorConfig.tool,
|
|
1433
1606
|
filepath: rootFilepath,
|
|
@@ -1436,7 +1609,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1436
1609
|
}
|
|
1437
1610
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1438
1611
|
if (ignorePatterns.patterns.length > 0) {
|
|
1439
|
-
const ignorePath =
|
|
1612
|
+
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1440
1613
|
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1441
1614
|
outputs.push({
|
|
1442
1615
|
tool: generatorConfig.tool,
|
|
@@ -1466,16 +1639,6 @@ function generateIgnoreFile2(patterns, tool) {
|
|
|
1466
1639
|
lines.push(...patterns);
|
|
1467
1640
|
return lines.join("\n");
|
|
1468
1641
|
}
|
|
1469
|
-
async function generateComplexRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
1470
|
-
const unifiedConfig = {
|
|
1471
|
-
tool: generatorConfig.tool,
|
|
1472
|
-
fileExtension: generatorConfig.fileExtension,
|
|
1473
|
-
ignoreFileName: generatorConfig.ignoreFileName,
|
|
1474
|
-
generateContent: generatorConfig.generateContent,
|
|
1475
|
-
pathResolver: generatorConfig.getOutputPath
|
|
1476
|
-
};
|
|
1477
|
-
return generateRulesConfig(rules, config, unifiedConfig, baseDir);
|
|
1478
|
-
}
|
|
1479
1642
|
|
|
1480
1643
|
// src/generators/rules/augmentcode.ts
|
|
1481
1644
|
async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
@@ -1541,32 +1704,24 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
1541
1704
|
// src/generators/rules/claudecode.ts
|
|
1542
1705
|
import { join as join7 } from "path";
|
|
1543
1706
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
1544
|
-
const
|
|
1545
|
-
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
1546
|
-
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
1547
|
-
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
1548
|
-
const claudeOutputDir = baseDir ? join7(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
1549
|
-
outputs.push({
|
|
1707
|
+
const generatorConfig = {
|
|
1550
1708
|
tool: "claudecode",
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
1566
|
-
}
|
|
1567
|
-
return outputs;
|
|
1709
|
+
fileExtension: ".md",
|
|
1710
|
+
ignoreFileName: ".aiignore",
|
|
1711
|
+
generateContent: generateMemoryFile,
|
|
1712
|
+
generateRootContent: generateClaudeMarkdown,
|
|
1713
|
+
rootFilePath: "CLAUDE.md",
|
|
1714
|
+
generateDetailContent: generateMemoryFile,
|
|
1715
|
+
detailSubDir: ".claude/memories",
|
|
1716
|
+
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
1717
|
+
const settingsPath = resolvePath(join7(".claude", "settings.json"), baseDir2);
|
|
1718
|
+
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
1719
|
+
return [];
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
1568
1723
|
}
|
|
1569
|
-
function generateClaudeMarkdown(
|
|
1724
|
+
function generateClaudeMarkdown(rootRule, detailRules) {
|
|
1570
1725
|
const lines = [];
|
|
1571
1726
|
if (detailRules.length > 0) {
|
|
1572
1727
|
lines.push("Please also reference the following documents as needed:");
|
|
@@ -1580,11 +1735,9 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
1580
1735
|
}
|
|
1581
1736
|
lines.push("");
|
|
1582
1737
|
}
|
|
1583
|
-
if (
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
lines.push("");
|
|
1587
|
-
}
|
|
1738
|
+
if (rootRule) {
|
|
1739
|
+
lines.push(rootRule.content);
|
|
1740
|
+
lines.push("");
|
|
1588
1741
|
}
|
|
1589
1742
|
return lines.join("\n");
|
|
1590
1743
|
}
|
|
@@ -1624,134 +1777,8 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
1624
1777
|
console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
|
|
1625
1778
|
}
|
|
1626
1779
|
|
|
1627
|
-
// src/generators/rules/
|
|
1628
|
-
async function generateClineConfig(rules, config, baseDir) {
|
|
1629
|
-
return generateRulesConfig(
|
|
1630
|
-
rules,
|
|
1631
|
-
config,
|
|
1632
|
-
{
|
|
1633
|
-
tool: "cline",
|
|
1634
|
-
fileExtension: ".md",
|
|
1635
|
-
ignoreFileName: ".clineignore",
|
|
1636
|
-
generateContent: (rule) => rule.content.trim()
|
|
1637
|
-
},
|
|
1638
|
-
baseDir
|
|
1639
|
-
);
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
// src/generators/rules/codexcli.ts
|
|
1643
|
-
async function generateCodexConfig(rules, config, baseDir) {
|
|
1644
|
-
const generatorConfig = {
|
|
1645
|
-
tool: "codexcli",
|
|
1646
|
-
fileExtension: ".md",
|
|
1647
|
-
ignoreFileName: ".codexignore",
|
|
1648
|
-
generateContent: generateCodexInstructionsMarkdown,
|
|
1649
|
-
pathResolver: (rule, outputDir) => {
|
|
1650
|
-
if (rule.frontmatter.root === true) {
|
|
1651
|
-
return `${outputDir}/codex.md`;
|
|
1652
|
-
}
|
|
1653
|
-
return `${outputDir}/${rule.filename}.md`;
|
|
1654
|
-
}
|
|
1655
|
-
};
|
|
1656
|
-
return generateRulesConfig(rules, config, generatorConfig, baseDir);
|
|
1657
|
-
}
|
|
1658
|
-
function generateCodexInstructionsMarkdown(rule) {
|
|
1659
|
-
const lines = [];
|
|
1660
|
-
if (rule.frontmatter.root === false && rule.frontmatter.description && rule.frontmatter.description !== "Main instructions" && !rule.frontmatter.description.includes("Project-level Codex CLI instructions")) {
|
|
1661
|
-
lines.push(`<!-- ${rule.frontmatter.description} -->`);
|
|
1662
|
-
if (rule.content.trim()) {
|
|
1663
|
-
lines.push("");
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
const content = rule.content.trim();
|
|
1667
|
-
if (content) {
|
|
1668
|
-
lines.push(content);
|
|
1669
|
-
}
|
|
1670
|
-
return lines.join("\n");
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
// src/generators/rules/copilot.ts
|
|
1780
|
+
// src/generators/rules/generator-registry.ts
|
|
1674
1781
|
import { join as join8 } from "path";
|
|
1675
|
-
async function generateCopilotConfig(rules, config, baseDir) {
|
|
1676
|
-
return generateComplexRulesConfig(
|
|
1677
|
-
rules,
|
|
1678
|
-
config,
|
|
1679
|
-
{
|
|
1680
|
-
tool: "copilot",
|
|
1681
|
-
fileExtension: ".instructions.md",
|
|
1682
|
-
ignoreFileName: ".copilotignore",
|
|
1683
|
-
generateContent: generateCopilotMarkdown,
|
|
1684
|
-
getOutputPath: (rule, outputDir) => {
|
|
1685
|
-
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
1686
|
-
return join8(outputDir, `${baseFilename}.instructions.md`);
|
|
1687
|
-
}
|
|
1688
|
-
},
|
|
1689
|
-
baseDir
|
|
1690
|
-
);
|
|
1691
|
-
}
|
|
1692
|
-
function generateCopilotMarkdown(rule) {
|
|
1693
|
-
const lines = [];
|
|
1694
|
-
lines.push("---");
|
|
1695
|
-
lines.push(`description: "${rule.frontmatter.description}"`);
|
|
1696
|
-
if (rule.frontmatter.globs.length > 0) {
|
|
1697
|
-
lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
|
|
1698
|
-
} else {
|
|
1699
|
-
lines.push('applyTo: "**"');
|
|
1700
|
-
}
|
|
1701
|
-
lines.push("---");
|
|
1702
|
-
lines.push(rule.content);
|
|
1703
|
-
return lines.join("\n");
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
// src/generators/rules/cursor.ts
|
|
1707
|
-
import { join as join9 } from "path";
|
|
1708
|
-
async function generateCursorConfig(rules, config, baseDir) {
|
|
1709
|
-
return generateComplexRulesConfig(
|
|
1710
|
-
rules,
|
|
1711
|
-
config,
|
|
1712
|
-
{
|
|
1713
|
-
tool: "cursor",
|
|
1714
|
-
fileExtension: ".mdc",
|
|
1715
|
-
ignoreFileName: ".cursorignore",
|
|
1716
|
-
generateContent: generateCursorMarkdown,
|
|
1717
|
-
getOutputPath: (rule, outputDir) => {
|
|
1718
|
-
return join9(outputDir, `${rule.filename}.mdc`);
|
|
1719
|
-
}
|
|
1720
|
-
},
|
|
1721
|
-
baseDir
|
|
1722
|
-
);
|
|
1723
|
-
}
|
|
1724
|
-
function generateCursorMarkdown(rule) {
|
|
1725
|
-
const lines = [];
|
|
1726
|
-
const ruleType = determineCursorRuleType(rule.frontmatter);
|
|
1727
|
-
lines.push("---");
|
|
1728
|
-
switch (ruleType) {
|
|
1729
|
-
case "always":
|
|
1730
|
-
lines.push("description:");
|
|
1731
|
-
lines.push("globs:");
|
|
1732
|
-
lines.push("alwaysApply: true");
|
|
1733
|
-
break;
|
|
1734
|
-
case "manual":
|
|
1735
|
-
lines.push("description:");
|
|
1736
|
-
lines.push("globs:");
|
|
1737
|
-
lines.push("alwaysApply: false");
|
|
1738
|
-
break;
|
|
1739
|
-
case "specificFiles":
|
|
1740
|
-
lines.push("description:");
|
|
1741
|
-
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
1742
|
-
lines.push("alwaysApply: false");
|
|
1743
|
-
break;
|
|
1744
|
-
case "intelligently":
|
|
1745
|
-
lines.push(`description: ${rule.frontmatter.description}`);
|
|
1746
|
-
lines.push("globs:");
|
|
1747
|
-
lines.push("alwaysApply: false");
|
|
1748
|
-
break;
|
|
1749
|
-
}
|
|
1750
|
-
lines.push("---");
|
|
1751
|
-
lines.push("");
|
|
1752
|
-
lines.push(rule.content);
|
|
1753
|
-
return lines.join("\n");
|
|
1754
|
-
}
|
|
1755
1782
|
function determineCursorRuleType(frontmatter) {
|
|
1756
1783
|
if (frontmatter.cursorRuleType) {
|
|
1757
1784
|
return frontmatter.cursorRuleType;
|
|
@@ -1773,6 +1800,293 @@ function determineCursorRuleType(frontmatter) {
|
|
|
1773
1800
|
}
|
|
1774
1801
|
return "intelligently";
|
|
1775
1802
|
}
|
|
1803
|
+
var GENERATOR_REGISTRY = {
|
|
1804
|
+
// Simple generators - generate one file per rule
|
|
1805
|
+
cline: {
|
|
1806
|
+
type: "simple",
|
|
1807
|
+
tool: "cline",
|
|
1808
|
+
fileExtension: ".md",
|
|
1809
|
+
ignoreFileName: ".clineignore",
|
|
1810
|
+
generateContent: (rule) => rule.content.trim()
|
|
1811
|
+
},
|
|
1812
|
+
roo: {
|
|
1813
|
+
type: "simple",
|
|
1814
|
+
tool: "roo",
|
|
1815
|
+
fileExtension: ".md",
|
|
1816
|
+
ignoreFileName: ".rooignore",
|
|
1817
|
+
generateContent: (rule) => rule.content.trim()
|
|
1818
|
+
},
|
|
1819
|
+
kiro: {
|
|
1820
|
+
type: "simple",
|
|
1821
|
+
tool: "kiro",
|
|
1822
|
+
fileExtension: ".md",
|
|
1823
|
+
ignoreFileName: ".kiroignore",
|
|
1824
|
+
generateContent: (rule) => rule.content.trim()
|
|
1825
|
+
},
|
|
1826
|
+
augmentcode: {
|
|
1827
|
+
type: "simple",
|
|
1828
|
+
tool: "augmentcode",
|
|
1829
|
+
fileExtension: ".md",
|
|
1830
|
+
ignoreFileName: ".aiignore",
|
|
1831
|
+
generateContent: (rule) => rule.content.trim()
|
|
1832
|
+
},
|
|
1833
|
+
"augmentcode-legacy": {
|
|
1834
|
+
type: "simple",
|
|
1835
|
+
tool: "augmentcode-legacy",
|
|
1836
|
+
fileExtension: ".md",
|
|
1837
|
+
ignoreFileName: ".aiignore",
|
|
1838
|
+
generateContent: (rule) => rule.content.trim()
|
|
1839
|
+
},
|
|
1840
|
+
// Complex generators with custom content formatting
|
|
1841
|
+
copilot: {
|
|
1842
|
+
type: "simple",
|
|
1843
|
+
tool: "copilot",
|
|
1844
|
+
fileExtension: ".instructions.md",
|
|
1845
|
+
ignoreFileName: ".copilotignore",
|
|
1846
|
+
generateContent: (rule) => {
|
|
1847
|
+
const lines = [];
|
|
1848
|
+
lines.push("---");
|
|
1849
|
+
lines.push(`description: "${rule.frontmatter.description}"`);
|
|
1850
|
+
if (rule.frontmatter.globs.length > 0) {
|
|
1851
|
+
lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
|
|
1852
|
+
} else {
|
|
1853
|
+
lines.push('applyTo: "**"');
|
|
1854
|
+
}
|
|
1855
|
+
lines.push("---");
|
|
1856
|
+
lines.push(rule.content);
|
|
1857
|
+
return lines.join("\n");
|
|
1858
|
+
},
|
|
1859
|
+
pathResolver: (rule, outputDir) => {
|
|
1860
|
+
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
1861
|
+
return join8(outputDir, `${baseFilename}.instructions.md`);
|
|
1862
|
+
}
|
|
1863
|
+
},
|
|
1864
|
+
cursor: {
|
|
1865
|
+
type: "simple",
|
|
1866
|
+
tool: "cursor",
|
|
1867
|
+
fileExtension: ".mdc",
|
|
1868
|
+
ignoreFileName: ".cursorignore",
|
|
1869
|
+
generateContent: (rule) => {
|
|
1870
|
+
const lines = [];
|
|
1871
|
+
const ruleType = determineCursorRuleType(rule.frontmatter);
|
|
1872
|
+
lines.push("---");
|
|
1873
|
+
switch (ruleType) {
|
|
1874
|
+
case "always":
|
|
1875
|
+
lines.push("description:");
|
|
1876
|
+
lines.push("globs:");
|
|
1877
|
+
lines.push("alwaysApply: true");
|
|
1878
|
+
break;
|
|
1879
|
+
case "manual":
|
|
1880
|
+
lines.push("description:");
|
|
1881
|
+
lines.push("globs:");
|
|
1882
|
+
lines.push("alwaysApply: false");
|
|
1883
|
+
break;
|
|
1884
|
+
case "specificFiles":
|
|
1885
|
+
lines.push("description:");
|
|
1886
|
+
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
1887
|
+
lines.push("alwaysApply: false");
|
|
1888
|
+
break;
|
|
1889
|
+
case "intelligently":
|
|
1890
|
+
lines.push(`description: ${rule.frontmatter.description}`);
|
|
1891
|
+
lines.push("globs:");
|
|
1892
|
+
lines.push("alwaysApply: false");
|
|
1893
|
+
break;
|
|
1894
|
+
}
|
|
1895
|
+
lines.push("---");
|
|
1896
|
+
lines.push("");
|
|
1897
|
+
lines.push(rule.content);
|
|
1898
|
+
return lines.join("\n");
|
|
1899
|
+
},
|
|
1900
|
+
pathResolver: (rule, outputDir) => {
|
|
1901
|
+
return join8(outputDir, `${rule.filename}.mdc`);
|
|
1902
|
+
}
|
|
1903
|
+
},
|
|
1904
|
+
codexcli: {
|
|
1905
|
+
type: "simple",
|
|
1906
|
+
tool: "codexcli",
|
|
1907
|
+
fileExtension: ".md",
|
|
1908
|
+
ignoreFileName: ".codexignore",
|
|
1909
|
+
generateContent: (rule) => rule.content.trim()
|
|
1910
|
+
},
|
|
1911
|
+
windsurf: {
|
|
1912
|
+
type: "simple",
|
|
1913
|
+
tool: "windsurf",
|
|
1914
|
+
fileExtension: ".md",
|
|
1915
|
+
ignoreFileName: ".codeiumignore",
|
|
1916
|
+
generateContent: (rule) => {
|
|
1917
|
+
const lines = [];
|
|
1918
|
+
const activationMode = rule.frontmatter.windsurfActivationMode;
|
|
1919
|
+
const globPattern = rule.frontmatter.globs?.[0];
|
|
1920
|
+
if (activationMode || globPattern) {
|
|
1921
|
+
lines.push("---");
|
|
1922
|
+
if (activationMode) {
|
|
1923
|
+
lines.push(`activation: ${activationMode}`);
|
|
1924
|
+
}
|
|
1925
|
+
if (globPattern && activationMode === "glob") {
|
|
1926
|
+
lines.push(`pattern: "${globPattern}"`);
|
|
1927
|
+
}
|
|
1928
|
+
lines.push("---");
|
|
1929
|
+
lines.push("");
|
|
1930
|
+
}
|
|
1931
|
+
lines.push(rule.content.trim());
|
|
1932
|
+
return lines.join("\n");
|
|
1933
|
+
},
|
|
1934
|
+
pathResolver: (rule, outputDir) => {
|
|
1935
|
+
const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
|
|
1936
|
+
if (outputFormat === "single-file") {
|
|
1937
|
+
return join8(outputDir, ".windsurf-rules");
|
|
1938
|
+
} else {
|
|
1939
|
+
const rulesDir = join8(outputDir, ".windsurf", "rules");
|
|
1940
|
+
return join8(rulesDir, `${rule.filename}.md`);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
// Complex generators with root + detail pattern
|
|
1945
|
+
claudecode: {
|
|
1946
|
+
type: "complex",
|
|
1947
|
+
tool: "claudecode",
|
|
1948
|
+
fileExtension: ".md",
|
|
1949
|
+
ignoreFileName: ".aiignore",
|
|
1950
|
+
generateContent: (rule) => {
|
|
1951
|
+
const lines = [];
|
|
1952
|
+
if (rule.frontmatter.description) {
|
|
1953
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1954
|
+
`);
|
|
1955
|
+
}
|
|
1956
|
+
lines.push(rule.content.trim());
|
|
1957
|
+
return lines.join("\n");
|
|
1958
|
+
}
|
|
1959
|
+
// NOTE: Claude Code specific logic is handled in the actual generator file
|
|
1960
|
+
// due to complex settings.json manipulation requirements
|
|
1961
|
+
},
|
|
1962
|
+
geminicli: {
|
|
1963
|
+
type: "complex",
|
|
1964
|
+
tool: "geminicli",
|
|
1965
|
+
fileExtension: ".md",
|
|
1966
|
+
ignoreFileName: ".aiexclude",
|
|
1967
|
+
generateContent: (rule) => {
|
|
1968
|
+
const lines = [];
|
|
1969
|
+
if (rule.frontmatter.description) {
|
|
1970
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1971
|
+
`);
|
|
1972
|
+
}
|
|
1973
|
+
lines.push(rule.content.trim());
|
|
1974
|
+
return lines.join("\n");
|
|
1975
|
+
}
|
|
1976
|
+
// Complex generation handled by existing generator
|
|
1977
|
+
},
|
|
1978
|
+
junie: {
|
|
1979
|
+
type: "complex",
|
|
1980
|
+
tool: "junie",
|
|
1981
|
+
fileExtension: ".md",
|
|
1982
|
+
ignoreFileName: ".aiignore",
|
|
1983
|
+
generateContent: (rule) => {
|
|
1984
|
+
const lines = [];
|
|
1985
|
+
if (rule.frontmatter.description) {
|
|
1986
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1987
|
+
`);
|
|
1988
|
+
}
|
|
1989
|
+
lines.push(rule.content.trim());
|
|
1990
|
+
return lines.join("\n");
|
|
1991
|
+
}
|
|
1992
|
+
// Complex generation handled by existing generator
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
async function generateFromRegistry(tool, rules, config, baseDir) {
|
|
1996
|
+
const generatorConfig = GENERATOR_REGISTRY[tool];
|
|
1997
|
+
if (!generatorConfig) {
|
|
1998
|
+
throw new Error(`No generator configuration found for tool: ${tool}`);
|
|
1999
|
+
}
|
|
2000
|
+
if (generatorConfig.type === "simple") {
|
|
2001
|
+
const ruleConfig = {
|
|
2002
|
+
tool: generatorConfig.tool,
|
|
2003
|
+
fileExtension: generatorConfig.fileExtension,
|
|
2004
|
+
ignoreFileName: generatorConfig.ignoreFileName,
|
|
2005
|
+
generateContent: generatorConfig.generateContent,
|
|
2006
|
+
...generatorConfig.pathResolver && { pathResolver: generatorConfig.pathResolver }
|
|
2007
|
+
};
|
|
2008
|
+
return generateRulesConfig(rules, config, ruleConfig, baseDir);
|
|
2009
|
+
} else {
|
|
2010
|
+
const enhancedConfig = {
|
|
2011
|
+
tool: generatorConfig.tool,
|
|
2012
|
+
fileExtension: generatorConfig.fileExtension,
|
|
2013
|
+
ignoreFileName: generatorConfig.ignoreFileName,
|
|
2014
|
+
generateContent: generatorConfig.generateContent,
|
|
2015
|
+
...generatorConfig.generateRootContent && {
|
|
2016
|
+
generateRootContent: generatorConfig.generateRootContent
|
|
2017
|
+
},
|
|
2018
|
+
...generatorConfig.rootFilePath && { rootFilePath: generatorConfig.rootFilePath },
|
|
2019
|
+
...generatorConfig.generateDetailContent && {
|
|
2020
|
+
generateDetailContent: generatorConfig.generateDetailContent
|
|
2021
|
+
},
|
|
2022
|
+
...generatorConfig.detailSubDir && { detailSubDir: generatorConfig.detailSubDir },
|
|
2023
|
+
...generatorConfig.updateAdditionalConfig && {
|
|
2024
|
+
updateAdditionalConfig: generatorConfig.updateAdditionalConfig
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
return generateComplexRules(rules, config, enhancedConfig, baseDir);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// src/generators/rules/cline.ts
|
|
2032
|
+
async function generateClineConfig(rules, config, baseDir) {
|
|
2033
|
+
return generateFromRegistry("cline", rules, config, baseDir);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/generators/rules/codexcli.ts
|
|
2037
|
+
async function generateCodexConfig(rules, config, baseDir) {
|
|
2038
|
+
const outputs = [];
|
|
2039
|
+
if (rules.length === 0) {
|
|
2040
|
+
return outputs;
|
|
2041
|
+
}
|
|
2042
|
+
const sortedRules = [...rules].sort((a, b) => {
|
|
2043
|
+
if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
|
|
2044
|
+
if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
|
|
2045
|
+
return 0;
|
|
2046
|
+
});
|
|
2047
|
+
const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
|
|
2048
|
+
if (concatenatedContent.trim()) {
|
|
2049
|
+
const outputDir = resolveOutputDir(config, "codexcli", baseDir);
|
|
2050
|
+
const filepath = `${outputDir}/codex.md`;
|
|
2051
|
+
outputs.push({
|
|
2052
|
+
tool: "codexcli",
|
|
2053
|
+
filepath,
|
|
2054
|
+
content: concatenatedContent
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
2058
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
2059
|
+
const ignorePath = resolvePath(".codexignore", baseDir);
|
|
2060
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
|
|
2061
|
+
outputs.push({
|
|
2062
|
+
tool: "codexcli",
|
|
2063
|
+
filepath: ignorePath,
|
|
2064
|
+
content: ignoreContent
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
return outputs;
|
|
2068
|
+
}
|
|
2069
|
+
function generateConcatenatedCodexContent(rules) {
|
|
2070
|
+
const sections = [];
|
|
2071
|
+
for (const rule of rules) {
|
|
2072
|
+
const content = rule.content.trim();
|
|
2073
|
+
if (!content) {
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2076
|
+
sections.push(content);
|
|
2077
|
+
}
|
|
2078
|
+
return sections.join("\n\n---\n\n");
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// src/generators/rules/copilot.ts
|
|
2082
|
+
async function generateCopilotConfig(rules, config, baseDir) {
|
|
2083
|
+
return generateFromRegistry("copilot", rules, config, baseDir);
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
// src/generators/rules/cursor.ts
|
|
2087
|
+
async function generateCursorConfig(rules, config, baseDir) {
|
|
2088
|
+
return generateFromRegistry("cursor", rules, config, baseDir);
|
|
2089
|
+
}
|
|
1776
2090
|
|
|
1777
2091
|
// src/generators/rules/geminicli.ts
|
|
1778
2092
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
@@ -1844,38 +2158,18 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
|
1844
2158
|
}
|
|
1845
2159
|
|
|
1846
2160
|
// src/generators/rules/kiro.ts
|
|
1847
|
-
import { join as join10 } from "path";
|
|
1848
2161
|
async function generateKiroConfig(rules, config, baseDir) {
|
|
1849
|
-
|
|
1850
|
-
for (const rule of rules) {
|
|
1851
|
-
const content = generateKiroMarkdown(rule);
|
|
1852
|
-
const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
|
|
1853
|
-
const filepath = join10(outputDir, `${rule.filename}.md`);
|
|
1854
|
-
outputs.push({
|
|
1855
|
-
tool: "kiro",
|
|
1856
|
-
filepath,
|
|
1857
|
-
content
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
return outputs;
|
|
1861
|
-
}
|
|
1862
|
-
function generateKiroMarkdown(rule) {
|
|
1863
|
-
return rule.content.trim();
|
|
2162
|
+
return generateFromRegistry("kiro", rules, config, baseDir);
|
|
1864
2163
|
}
|
|
1865
2164
|
|
|
1866
2165
|
// src/generators/rules/roo.ts
|
|
1867
2166
|
async function generateRooConfig(rules, config, baseDir) {
|
|
1868
|
-
return
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
ignoreFileName: ".rooignore",
|
|
1875
|
-
generateContent: (rule) => rule.content.trim()
|
|
1876
|
-
},
|
|
1877
|
-
baseDir
|
|
1878
|
-
);
|
|
2167
|
+
return generateFromRegistry("roo", rules, config, baseDir);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
// src/generators/rules/windsurf.ts
|
|
2171
|
+
async function generateWindsurfConfig(rules, config, baseDir) {
|
|
2172
|
+
return generateFromRegistry("windsurf", rules, config, baseDir);
|
|
1879
2173
|
}
|
|
1880
2174
|
|
|
1881
2175
|
// src/core/generator.ts
|
|
@@ -1950,6 +2244,11 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
1950
2244
|
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
1951
2245
|
return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
|
|
1952
2246
|
}
|
|
2247
|
+
case "windsurf": {
|
|
2248
|
+
const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
|
|
2249
|
+
const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
|
|
2250
|
+
return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
|
|
2251
|
+
}
|
|
1953
2252
|
default:
|
|
1954
2253
|
console.warn(`Unknown tool: ${tool}`);
|
|
1955
2254
|
return null;
|
|
@@ -2152,6 +2451,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
|
|
|
2152
2451
|
tool: "roo-project",
|
|
2153
2452
|
path: path4.join(targetRoot, ".roo", "mcp.json"),
|
|
2154
2453
|
generate: () => generateRooMcp(config)
|
|
2454
|
+
},
|
|
2455
|
+
{
|
|
2456
|
+
tool: "windsurf-project",
|
|
2457
|
+
path: path4.join(targetRoot, "mcp_config.json"),
|
|
2458
|
+
generate: () => generateWindsurfMcp(config)
|
|
2155
2459
|
}
|
|
2156
2460
|
];
|
|
2157
2461
|
const filteredGenerators = targetTools ? generators.filter((g) => {
|
|
@@ -2168,7 +2472,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
|
|
|
2168
2472
|
try {
|
|
2169
2473
|
const content = generator.generate();
|
|
2170
2474
|
const parsed = JSON.parse(content);
|
|
2171
|
-
if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
2475
|
+
if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo") || generator.tool.includes("windsurf")) {
|
|
2172
2476
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
2173
2477
|
results.push({
|
|
2174
2478
|
tool: generator.tool,
|
|
@@ -2280,12 +2584,12 @@ async function generateCommand(options = {}) {
|
|
|
2280
2584
|
for (const tool of targetTools) {
|
|
2281
2585
|
switch (tool) {
|
|
2282
2586
|
case "augmentcode":
|
|
2283
|
-
deleteTasks.push(removeDirectory(
|
|
2284
|
-
deleteTasks.push(removeDirectory(
|
|
2587
|
+
deleteTasks.push(removeDirectory(join11(".augment", "rules")));
|
|
2588
|
+
deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
|
|
2285
2589
|
break;
|
|
2286
2590
|
case "augmentcode-legacy":
|
|
2287
2591
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2288
|
-
deleteTasks.push(removeDirectory(
|
|
2592
|
+
deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
|
|
2289
2593
|
break;
|
|
2290
2594
|
case "copilot":
|
|
2291
2595
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
@@ -2308,6 +2612,9 @@ async function generateCommand(options = {}) {
|
|
|
2308
2612
|
case "kiro":
|
|
2309
2613
|
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
2310
2614
|
break;
|
|
2615
|
+
case "windsurf":
|
|
2616
|
+
deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
|
|
2617
|
+
break;
|
|
2311
2618
|
}
|
|
2312
2619
|
}
|
|
2313
2620
|
await Promise.all(deleteTasks);
|
|
@@ -2380,9 +2687,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
2380
2687
|
|
|
2381
2688
|
// src/cli/commands/gitignore.ts
|
|
2382
2689
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2383
|
-
import { join as
|
|
2690
|
+
import { join as join12 } from "path";
|
|
2384
2691
|
var gitignoreCommand = async () => {
|
|
2385
|
-
const gitignorePath =
|
|
2692
|
+
const gitignorePath = join12(process.cwd(), ".gitignore");
|
|
2386
2693
|
const rulesFilesToIgnore = [
|
|
2387
2694
|
"# Generated by rulesync - AI tool configuration files",
|
|
2388
2695
|
"**/.github/copilot-instructions.md",
|
|
@@ -2446,11 +2753,11 @@ ${linesToAdd.join("\n")}
|
|
|
2446
2753
|
};
|
|
2447
2754
|
|
|
2448
2755
|
// src/core/importer.ts
|
|
2449
|
-
import { join as
|
|
2450
|
-
import
|
|
2756
|
+
import { join as join19 } from "path";
|
|
2757
|
+
import matter6 from "gray-matter";
|
|
2451
2758
|
|
|
2452
2759
|
// src/parsers/augmentcode.ts
|
|
2453
|
-
import { basename as basename2, join as
|
|
2760
|
+
import { basename as basename2, join as join13 } from "path";
|
|
2454
2761
|
import matter2 from "gray-matter";
|
|
2455
2762
|
|
|
2456
2763
|
// src/utils/parser-helpers.ts
|
|
@@ -2472,36 +2779,63 @@ function addRules(result, rules) {
|
|
|
2472
2779
|
}
|
|
2473
2780
|
result.rules.push(...rules);
|
|
2474
2781
|
}
|
|
2475
|
-
function handleParseError(error, context) {
|
|
2476
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2477
|
-
return `${context}: ${errorMessage}`;
|
|
2478
|
-
}
|
|
2479
2782
|
async function safeReadFile(operation, errorContext) {
|
|
2480
2783
|
try {
|
|
2481
2784
|
const result = await operation();
|
|
2482
|
-
return
|
|
2785
|
+
return createSuccessResult(result);
|
|
2483
2786
|
} catch (error) {
|
|
2484
|
-
return
|
|
2485
|
-
success: false,
|
|
2486
|
-
error: handleParseError(error, errorContext)
|
|
2487
|
-
};
|
|
2787
|
+
return createErrorResult(error, errorContext);
|
|
2488
2788
|
}
|
|
2489
2789
|
}
|
|
2490
2790
|
|
|
2491
2791
|
// src/parsers/augmentcode.ts
|
|
2492
2792
|
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
2793
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
2794
|
+
rulesDir: ".augment/rules",
|
|
2795
|
+
targetName: "augmentcode",
|
|
2796
|
+
filenamePrefix: "augmentcode"
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
2800
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
2801
|
+
legacyFilePath: ".augment-guidelines",
|
|
2802
|
+
targetName: "augmentcode-legacy",
|
|
2803
|
+
filenamePrefix: "augmentcode-legacy"
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
2493
2807
|
const result = createParseResult();
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2808
|
+
if (config.rulesDir) {
|
|
2809
|
+
const rulesDir = join13(baseDir, config.rulesDir);
|
|
2810
|
+
if (await fileExists(rulesDir)) {
|
|
2811
|
+
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
2812
|
+
addRules(result, rulesResult.rules);
|
|
2813
|
+
result.errors.push(...rulesResult.errors);
|
|
2814
|
+
} else {
|
|
2815
|
+
addError(
|
|
2816
|
+
result,
|
|
2817
|
+
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
if (config.legacyFilePath) {
|
|
2822
|
+
const legacyPath = join13(baseDir, config.legacyFilePath);
|
|
2823
|
+
if (await fileExists(legacyPath)) {
|
|
2824
|
+
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
2825
|
+
if (legacyResult.rule) {
|
|
2826
|
+
addRule(result, legacyResult.rule);
|
|
2827
|
+
}
|
|
2828
|
+
result.errors.push(...legacyResult.errors);
|
|
2829
|
+
} else {
|
|
2830
|
+
addError(
|
|
2831
|
+
result,
|
|
2832
|
+
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2501
2835
|
}
|
|
2502
2836
|
return { rules: result.rules || [], errors: result.errors };
|
|
2503
2837
|
}
|
|
2504
|
-
async function parseAugmentRules(rulesDir) {
|
|
2838
|
+
async function parseAugmentRules(rulesDir, config) {
|
|
2505
2839
|
const rules = [];
|
|
2506
2840
|
const errors = [];
|
|
2507
2841
|
try {
|
|
@@ -2509,7 +2843,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2509
2843
|
const files = await readdir2(rulesDir);
|
|
2510
2844
|
for (const file of files) {
|
|
2511
2845
|
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
2512
|
-
const filePath =
|
|
2846
|
+
const filePath = join13(rulesDir, file);
|
|
2513
2847
|
try {
|
|
2514
2848
|
const rawContent = await readFileContent(filePath);
|
|
2515
2849
|
const parsed = matter2(rawContent);
|
|
@@ -2521,7 +2855,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2521
2855
|
const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
2522
2856
|
const frontmatter = {
|
|
2523
2857
|
root: isRoot,
|
|
2524
|
-
targets: [
|
|
2858
|
+
targets: [config.targetName],
|
|
2525
2859
|
description,
|
|
2526
2860
|
globs: ["**/*"],
|
|
2527
2861
|
// AugmentCode doesn't use specific globs in the same way
|
|
@@ -2530,7 +2864,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2530
2864
|
rules.push({
|
|
2531
2865
|
frontmatter,
|
|
2532
2866
|
content: parsed.content.trim(),
|
|
2533
|
-
filename:
|
|
2867
|
+
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
2534
2868
|
filepath: filePath
|
|
2535
2869
|
});
|
|
2536
2870
|
} catch (error) {
|
|
@@ -2541,50 +2875,33 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2541
2875
|
}
|
|
2542
2876
|
} catch (error) {
|
|
2543
2877
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2544
|
-
errors.push(`Failed to read .
|
|
2878
|
+
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
2545
2879
|
}
|
|
2546
2880
|
return { rules, errors };
|
|
2547
2881
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
if (content.trim()) {
|
|
2572
|
-
const frontmatter = {
|
|
2573
|
-
root: true,
|
|
2574
|
-
// Legacy guidelines become root rules
|
|
2575
|
-
targets: ["augmentcode-legacy"],
|
|
2576
|
-
description: "Legacy AugmentCode guidelines",
|
|
2577
|
-
globs: ["**/*"]
|
|
2578
|
-
};
|
|
2579
|
-
return {
|
|
2580
|
-
frontmatter,
|
|
2581
|
-
content: content.trim(),
|
|
2582
|
-
filename: "augmentcode-legacy-guidelines",
|
|
2583
|
-
filepath: guidelinesPath
|
|
2584
|
-
};
|
|
2585
|
-
}
|
|
2586
|
-
return null;
|
|
2587
|
-
}, "Failed to parse .augment-guidelines");
|
|
2882
|
+
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
2883
|
+
const parseResult = await safeReadFile(
|
|
2884
|
+
async () => {
|
|
2885
|
+
const content = await readFileContent(guidelinesPath);
|
|
2886
|
+
if (content.trim()) {
|
|
2887
|
+
const frontmatter = {
|
|
2888
|
+
root: true,
|
|
2889
|
+
// Legacy guidelines become root rules
|
|
2890
|
+
targets: [config.targetName],
|
|
2891
|
+
description: "Legacy AugmentCode guidelines",
|
|
2892
|
+
globs: ["**/*"]
|
|
2893
|
+
};
|
|
2894
|
+
return {
|
|
2895
|
+
frontmatter,
|
|
2896
|
+
content: content.trim(),
|
|
2897
|
+
filename: `${config.filenamePrefix}-guidelines`,
|
|
2898
|
+
filepath: guidelinesPath
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
return null;
|
|
2902
|
+
},
|
|
2903
|
+
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
2904
|
+
);
|
|
2588
2905
|
if (parseResult.success) {
|
|
2589
2906
|
return { rule: parseResult.result || null, errors: [] };
|
|
2590
2907
|
} else {
|
|
@@ -2593,33 +2910,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
|
|
|
2593
2910
|
}
|
|
2594
2911
|
|
|
2595
2912
|
// src/parsers/shared-helpers.ts
|
|
2596
|
-
import { basename as basename3, join as
|
|
2913
|
+
import { basename as basename3, join as join14 } from "path";
|
|
2597
2914
|
import matter3 from "gray-matter";
|
|
2598
2915
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
2599
2916
|
const errors = [];
|
|
2600
2917
|
const rules = [];
|
|
2601
2918
|
if (config.mainFile) {
|
|
2602
|
-
const
|
|
2919
|
+
const mainFile = config.mainFile;
|
|
2920
|
+
const mainFilePath = resolvePath(mainFile.path, baseDir);
|
|
2603
2921
|
if (await fileExists(mainFilePath)) {
|
|
2604
|
-
|
|
2922
|
+
const result = await safeAsyncOperation(async () => {
|
|
2605
2923
|
const rawContent = await readFileContent(mainFilePath);
|
|
2606
2924
|
let content;
|
|
2607
2925
|
let frontmatter;
|
|
2608
|
-
if (
|
|
2926
|
+
if (mainFile.useFrontmatter) {
|
|
2609
2927
|
const parsed = matter3(rawContent);
|
|
2610
2928
|
content = parsed.content.trim();
|
|
2929
|
+
const parsedFrontmatter = parsed.data;
|
|
2611
2930
|
frontmatter = {
|
|
2612
|
-
root: false,
|
|
2931
|
+
root: mainFile.isRoot ?? false,
|
|
2613
2932
|
targets: [config.tool],
|
|
2614
|
-
description:
|
|
2615
|
-
globs: ["**/*"]
|
|
2933
|
+
description: parsedFrontmatter.description || mainFile.description,
|
|
2934
|
+
globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
|
|
2935
|
+
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
2616
2936
|
};
|
|
2617
2937
|
} else {
|
|
2618
2938
|
content = rawContent.trim();
|
|
2619
2939
|
frontmatter = {
|
|
2620
|
-
root: false,
|
|
2940
|
+
root: mainFile.isRoot ?? false,
|
|
2621
2941
|
targets: [config.tool],
|
|
2622
|
-
description:
|
|
2942
|
+
description: mainFile.description,
|
|
2623
2943
|
globs: ["**/*"]
|
|
2624
2944
|
};
|
|
2625
2945
|
}
|
|
@@ -2627,43 +2947,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2627
2947
|
rules.push({
|
|
2628
2948
|
frontmatter,
|
|
2629
2949
|
content,
|
|
2630
|
-
filename: "instructions",
|
|
2950
|
+
filename: mainFile.filenameOverride || "instructions",
|
|
2631
2951
|
filepath: mainFilePath
|
|
2632
2952
|
});
|
|
2633
2953
|
}
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
errors.push(
|
|
2954
|
+
}, `Failed to parse ${mainFile.path}`);
|
|
2955
|
+
if (!result.success) {
|
|
2956
|
+
errors.push(result.error);
|
|
2637
2957
|
}
|
|
2638
2958
|
}
|
|
2639
2959
|
}
|
|
2640
2960
|
if (config.directories) {
|
|
2641
2961
|
for (const dirConfig of config.directories) {
|
|
2642
|
-
const dirPath =
|
|
2962
|
+
const dirPath = resolvePath(dirConfig.directory, baseDir);
|
|
2643
2963
|
if (await fileExists(dirPath)) {
|
|
2644
|
-
|
|
2964
|
+
const result = await safeAsyncOperation(async () => {
|
|
2645
2965
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2646
2966
|
const files = await readdir2(dirPath);
|
|
2647
2967
|
for (const file of files) {
|
|
2648
2968
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
2649
|
-
const filePath =
|
|
2650
|
-
|
|
2969
|
+
const filePath = join14(dirPath, file);
|
|
2970
|
+
const fileResult = await safeAsyncOperation(async () => {
|
|
2651
2971
|
const rawContent = await readFileContent(filePath);
|
|
2652
2972
|
let content;
|
|
2973
|
+
let frontmatter;
|
|
2974
|
+
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
2653
2975
|
if (dirConfig.filePattern === ".instructions.md") {
|
|
2654
2976
|
const parsed = matter3(rawContent);
|
|
2655
2977
|
content = parsed.content.trim();
|
|
2978
|
+
const parsedFrontmatter = parsed.data;
|
|
2979
|
+
frontmatter = {
|
|
2980
|
+
root: false,
|
|
2981
|
+
targets: [config.tool],
|
|
2982
|
+
description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
|
|
2983
|
+
globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
|
|
2984
|
+
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
2985
|
+
};
|
|
2656
2986
|
} else {
|
|
2657
2987
|
content = rawContent.trim();
|
|
2658
|
-
|
|
2659
|
-
if (content) {
|
|
2660
|
-
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
2661
|
-
const frontmatter = {
|
|
2988
|
+
frontmatter = {
|
|
2662
2989
|
root: false,
|
|
2663
2990
|
targets: [config.tool],
|
|
2664
2991
|
description: `${dirConfig.description}: ${filename}`,
|
|
2665
2992
|
globs: ["**/*"]
|
|
2666
2993
|
};
|
|
2994
|
+
}
|
|
2995
|
+
if (content) {
|
|
2667
2996
|
rules.push({
|
|
2668
2997
|
frontmatter,
|
|
2669
2998
|
content,
|
|
@@ -2671,15 +3000,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2671
3000
|
filepath: filePath
|
|
2672
3001
|
});
|
|
2673
3002
|
}
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
errors.push(
|
|
3003
|
+
}, `Failed to parse ${filePath}`);
|
|
3004
|
+
if (!fileResult.success) {
|
|
3005
|
+
errors.push(fileResult.error);
|
|
2677
3006
|
}
|
|
2678
3007
|
}
|
|
2679
3008
|
}
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
errors.push(
|
|
3009
|
+
}, `Failed to parse ${dirConfig.directory} files`);
|
|
3010
|
+
if (!result.success) {
|
|
3011
|
+
errors.push(result.error);
|
|
2683
3012
|
}
|
|
2684
3013
|
}
|
|
2685
3014
|
}
|
|
@@ -2694,7 +3023,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2694
3023
|
const rules = [];
|
|
2695
3024
|
let ignorePatterns;
|
|
2696
3025
|
let mcpServers;
|
|
2697
|
-
const mainFilePath =
|
|
3026
|
+
const mainFilePath = resolvePath(config.mainFileName, baseDir);
|
|
2698
3027
|
if (!await fileExists(mainFilePath)) {
|
|
2699
3028
|
errors.push(`${config.mainFileName} file not found`);
|
|
2700
3029
|
return { rules, errors };
|
|
@@ -2705,12 +3034,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2705
3034
|
if (mainRule) {
|
|
2706
3035
|
rules.push(mainRule);
|
|
2707
3036
|
}
|
|
2708
|
-
const memoryDir =
|
|
3037
|
+
const memoryDir = resolvePath(config.memoryDirPath, baseDir);
|
|
2709
3038
|
if (await fileExists(memoryDir)) {
|
|
2710
3039
|
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
2711
3040
|
rules.push(...memoryRules);
|
|
2712
3041
|
}
|
|
2713
|
-
const settingsPath =
|
|
3042
|
+
const settingsPath = resolvePath(config.settingsPath, baseDir);
|
|
2714
3043
|
if (await fileExists(settingsPath)) {
|
|
2715
3044
|
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
2716
3045
|
if (settingsResult.ignorePatterns) {
|
|
@@ -2722,7 +3051,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2722
3051
|
errors.push(...settingsResult.errors);
|
|
2723
3052
|
}
|
|
2724
3053
|
if (config.additionalIgnoreFile) {
|
|
2725
|
-
const additionalIgnorePath =
|
|
3054
|
+
const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
|
|
2726
3055
|
if (await fileExists(additionalIgnorePath)) {
|
|
2727
3056
|
const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
|
|
2728
3057
|
if (additionalPatterns.length > 0) {
|
|
@@ -2731,8 +3060,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2731
3060
|
}
|
|
2732
3061
|
}
|
|
2733
3062
|
} catch (error) {
|
|
2734
|
-
|
|
2735
|
-
errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
|
|
3063
|
+
errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
|
|
2736
3064
|
}
|
|
2737
3065
|
return {
|
|
2738
3066
|
rules,
|
|
@@ -2776,7 +3104,7 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
2776
3104
|
const files = await readdir2(memoryDir);
|
|
2777
3105
|
for (const file of files) {
|
|
2778
3106
|
if (file.endsWith(".md")) {
|
|
2779
|
-
const filePath =
|
|
3107
|
+
const filePath = join14(memoryDir, file);
|
|
2780
3108
|
const content = await readFileContent(filePath);
|
|
2781
3109
|
if (content.trim()) {
|
|
2782
3110
|
const filename = basename3(file, ".md");
|
|
@@ -2828,8 +3156,7 @@ async function parseSettingsFile(settingsPath, tool) {
|
|
|
2828
3156
|
mcpServers = parseResult.data.mcpServers;
|
|
2829
3157
|
}
|
|
2830
3158
|
} catch (error) {
|
|
2831
|
-
|
|
2832
|
-
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
3159
|
+
errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
|
|
2833
3160
|
}
|
|
2834
3161
|
return {
|
|
2835
3162
|
errors,
|
|
@@ -2872,7 +3199,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
2872
3199
|
}
|
|
2873
3200
|
|
|
2874
3201
|
// src/parsers/codexcli.ts
|
|
2875
|
-
import { join as
|
|
3202
|
+
import { join as join15 } from "path";
|
|
2876
3203
|
|
|
2877
3204
|
// src/parsers/copilot.ts
|
|
2878
3205
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -2895,7 +3222,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
2895
3222
|
}
|
|
2896
3223
|
|
|
2897
3224
|
// src/parsers/cursor.ts
|
|
2898
|
-
import { basename as basename4, join as
|
|
3225
|
+
import { basename as basename4, join as join16 } from "path";
|
|
2899
3226
|
import matter4 from "gray-matter";
|
|
2900
3227
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
2901
3228
|
import { z as z6 } from "zod/mini";
|
|
@@ -3020,7 +3347,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3020
3347
|
const rules = [];
|
|
3021
3348
|
let ignorePatterns;
|
|
3022
3349
|
let mcpServers;
|
|
3023
|
-
const cursorFilePath =
|
|
3350
|
+
const cursorFilePath = join16(baseDir, ".cursorrules");
|
|
3024
3351
|
if (await fileExists(cursorFilePath)) {
|
|
3025
3352
|
try {
|
|
3026
3353
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -3041,14 +3368,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3041
3368
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
3042
3369
|
}
|
|
3043
3370
|
}
|
|
3044
|
-
const cursorRulesDir =
|
|
3371
|
+
const cursorRulesDir = join16(baseDir, ".cursor", "rules");
|
|
3045
3372
|
if (await fileExists(cursorRulesDir)) {
|
|
3046
3373
|
try {
|
|
3047
3374
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
3048
3375
|
const files = await readdir2(cursorRulesDir);
|
|
3049
3376
|
for (const file of files) {
|
|
3050
3377
|
if (file.endsWith(".mdc")) {
|
|
3051
|
-
const filePath =
|
|
3378
|
+
const filePath = join16(cursorRulesDir, file);
|
|
3052
3379
|
try {
|
|
3053
3380
|
const rawContent = await readFileContent(filePath);
|
|
3054
3381
|
const parsed = matter4(rawContent, customMatterOptions);
|
|
@@ -3077,7 +3404,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3077
3404
|
if (rules.length === 0) {
|
|
3078
3405
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
3079
3406
|
}
|
|
3080
|
-
const cursorIgnorePath =
|
|
3407
|
+
const cursorIgnorePath = join16(baseDir, ".cursorignore");
|
|
3081
3408
|
if (await fileExists(cursorIgnorePath)) {
|
|
3082
3409
|
try {
|
|
3083
3410
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -3090,7 +3417,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3090
3417
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
3091
3418
|
}
|
|
3092
3419
|
}
|
|
3093
|
-
const cursorMcpPath =
|
|
3420
|
+
const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
|
|
3094
3421
|
if (await fileExists(cursorMcpPath)) {
|
|
3095
3422
|
try {
|
|
3096
3423
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -3139,11 +3466,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
3139
3466
|
}
|
|
3140
3467
|
|
|
3141
3468
|
// src/parsers/junie.ts
|
|
3142
|
-
import { join as
|
|
3469
|
+
import { join as join17 } from "path";
|
|
3143
3470
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
3144
3471
|
const errors = [];
|
|
3145
3472
|
const rules = [];
|
|
3146
|
-
const guidelinesPath =
|
|
3473
|
+
const guidelinesPath = join17(baseDir, ".junie", "guidelines.md");
|
|
3147
3474
|
if (!await fileExists(guidelinesPath)) {
|
|
3148
3475
|
errors.push(".junie/guidelines.md file not found");
|
|
3149
3476
|
return { rules, errors };
|
|
@@ -3194,6 +3521,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
3194
3521
|
});
|
|
3195
3522
|
}
|
|
3196
3523
|
|
|
3524
|
+
// src/parsers/windsurf.ts
|
|
3525
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
3526
|
+
import { join as join18 } from "path";
|
|
3527
|
+
import matter5 from "gray-matter";
|
|
3528
|
+
|
|
3197
3529
|
// src/core/importer.ts
|
|
3198
3530
|
async function importConfiguration(options) {
|
|
3199
3531
|
const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
|
|
@@ -3278,7 +3610,7 @@ async function importConfiguration(options) {
|
|
|
3278
3610
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
3279
3611
|
return { success: false, rulesCreated: 0, errors };
|
|
3280
3612
|
}
|
|
3281
|
-
const rulesDirPath =
|
|
3613
|
+
const rulesDirPath = join19(baseDir, rulesDir);
|
|
3282
3614
|
try {
|
|
3283
3615
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3284
3616
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -3292,7 +3624,7 @@ async function importConfiguration(options) {
|
|
|
3292
3624
|
try {
|
|
3293
3625
|
const baseFilename = rule.filename;
|
|
3294
3626
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
3295
|
-
const filePath =
|
|
3627
|
+
const filePath = join19(rulesDirPath, `${filename}.md`);
|
|
3296
3628
|
const content = generateRuleFileContent(rule);
|
|
3297
3629
|
await writeFileContent(filePath, content);
|
|
3298
3630
|
rulesCreated++;
|
|
@@ -3307,7 +3639,7 @@ async function importConfiguration(options) {
|
|
|
3307
3639
|
let ignoreFileCreated = false;
|
|
3308
3640
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
3309
3641
|
try {
|
|
3310
|
-
const rulesyncignorePath =
|
|
3642
|
+
const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
|
|
3311
3643
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
3312
3644
|
`;
|
|
3313
3645
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -3323,7 +3655,7 @@ async function importConfiguration(options) {
|
|
|
3323
3655
|
let mcpFileCreated = false;
|
|
3324
3656
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
3325
3657
|
try {
|
|
3326
|
-
const mcpPath =
|
|
3658
|
+
const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
|
|
3327
3659
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
3328
3660
|
`;
|
|
3329
3661
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -3345,13 +3677,13 @@ async function importConfiguration(options) {
|
|
|
3345
3677
|
};
|
|
3346
3678
|
}
|
|
3347
3679
|
function generateRuleFileContent(rule) {
|
|
3348
|
-
const frontmatter =
|
|
3680
|
+
const frontmatter = matter6.stringify("", rule.frontmatter);
|
|
3349
3681
|
return frontmatter + rule.content;
|
|
3350
3682
|
}
|
|
3351
3683
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
3352
3684
|
let filename = baseFilename;
|
|
3353
3685
|
let counter = 1;
|
|
3354
|
-
while (await fileExists(
|
|
3686
|
+
while (await fileExists(join19(rulesDir, `${filename}.md`))) {
|
|
3355
3687
|
filename = `${baseFilename}-${counter}`;
|
|
3356
3688
|
counter++;
|
|
3357
3689
|
}
|
|
@@ -3418,7 +3750,7 @@ async function importCommand(options = {}) {
|
|
|
3418
3750
|
}
|
|
3419
3751
|
|
|
3420
3752
|
// src/cli/commands/init.ts
|
|
3421
|
-
import { join as
|
|
3753
|
+
import { join as join20 } from "path";
|
|
3422
3754
|
async function initCommand() {
|
|
3423
3755
|
const aiRulesDir = ".rulesync";
|
|
3424
3756
|
console.log("Initializing rulesync...");
|
|
@@ -3465,7 +3797,7 @@ globs: ["**/*"]
|
|
|
3465
3797
|
- Follow single responsibility principle
|
|
3466
3798
|
`
|
|
3467
3799
|
};
|
|
3468
|
-
const filepath =
|
|
3800
|
+
const filepath = join20(aiRulesDir, sampleFile.filename);
|
|
3469
3801
|
if (!await fileExists(filepath)) {
|
|
3470
3802
|
await writeFileContent(filepath, sampleFile.content);
|
|
3471
3803
|
console.log(`Created ${filepath}`);
|
|
@@ -3609,7 +3941,7 @@ async function watchCommand() {
|
|
|
3609
3941
|
|
|
3610
3942
|
// src/cli/index.ts
|
|
3611
3943
|
var program = new Command();
|
|
3612
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
3944
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.56.0");
|
|
3613
3945
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
3614
3946
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
3615
3947
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|