rulesync 0.5.0 → 0.9.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.md CHANGED
@@ -11,6 +11,7 @@ A Node.js CLI tool that automatically generates configuration files for various
11
11
  - **Cursor Project Rules** (`.cursor/rules/*.mdc`)
12
12
  - **Cline Rules** (`.clinerules/*.md`)
13
13
  - **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
14
+ - **Roo Code Rules** (`.roo/rules/*.md`)
14
15
 
15
16
  ## Installation
16
17
 
@@ -165,7 +166,7 @@ Define metadata in front matter for each Markdown file:
165
166
  ```markdown
166
167
  ---
167
168
  root: true # or false
168
- targets: ["*"] # or [copilot, cursor, cline, claudecode]
169
+ targets: ["*"] # or [copilot, cursor, cline, claude, roo]
169
170
  description: "TypeScript coding rules"
170
171
  globs: ["**/*.ts", "**/*.tsx"]
171
172
  ---
@@ -197,6 +198,7 @@ Each AI tool handles rule levels differently:
197
198
  | **Cursor** | `ruletype: always` | `ruletype: autoattached` | Detail rules without globs use `ruletype: agentrequested` |
198
199
  | **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
199
200
  | **Cline** | Standard format | Standard format | All rules use plain Markdown format |
201
+ | **Roo Code** | Standard format | Standard format | All rules use plain Markdown format with description header |
200
202
 
201
203
  ### 3. Generate Configuration Files
202
204
 
@@ -209,6 +211,7 @@ rulesync generate --copilot
209
211
  rulesync generate --cursor
210
212
  rulesync generate --cline
211
213
  rulesync generate --claude
214
+ rulesync generate --roo
212
215
 
213
216
  # Clean build (delete existing files first)
214
217
  rulesync generate --delete
@@ -225,7 +228,7 @@ rulesync generate --delete --verbose
225
228
 
226
229
  - `--delete`: Remove all existing generated files before creating new ones
227
230
  - `--verbose`: Show detailed output during generation process
228
- - `--copilot`, `--cursor`, `--cline`, `--claude`: Generate only for specified tools
231
+ - `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: Generate only for specified tools
229
232
 
230
233
  ### 4. Other Commands
231
234
 
@@ -291,7 +294,7 @@ This project follows TypeScript-first development with clean architecture princi
291
294
  ```markdown
292
295
  ---
293
296
  root: false
294
- targets: ["copilot", "cursor"]
297
+ targets: ["copilot", "cursor", "roo"]
295
298
  description: "TypeScript coding standards"
296
299
  globs: ["**/*.ts", "**/*.tsx"]
297
300
  ---
@@ -311,6 +314,7 @@ globs: ["**/*.ts", "**/*.tsx"]
311
314
  | **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `ruletype: always`<br>Non-root: `ruletype: autoattached`<br>Non-root without globs: `ruletype: agentrequested` |
312
315
  | **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
313
316
  | **Claude Code** | `./CLAUDE.md` (root)<br>`.claude/memories/*.md` (non-root) | Plain Markdown | Root goes to CLAUDE.md<br>Non-root go to separate memory files<br>CLAUDE.md includes `@filename` references |
317
+ | **Roo Code** | `.roo/rules/*.md` | Plain Markdown | Both levels use same format with description header |
314
318
 
315
319
  ## Validation
316
320
 
package/dist/index.js CHANGED
@@ -51,8 +51,10 @@ async function generateClaudeConfig(rules, config) {
51
51
  function generateClaudeMarkdown(rootRules, detailRules) {
52
52
  const lines = [];
53
53
  if (detailRules.length > 0) {
54
+ lines.push("Please also reference the following documents as needed:");
55
+ lines.push("");
54
56
  for (const rule of detailRules) {
55
- lines.push(`@${rule.filename}`);
57
+ lines.push(`@.claude/memories/${rule.filename}.md`);
56
58
  }
57
59
  lines.push("");
58
60
  }
@@ -87,10 +89,6 @@ function formatRuleForClaude(rule) {
87
89
  }
88
90
  function generateMemoryFile(rule) {
89
91
  const lines = [];
90
- lines.push("Please also refer to the following files as needed:");
91
- lines.push("");
92
- lines.push("---");
93
- lines.push("");
94
92
  lines.push(`# ${rule.filename}`);
95
93
  lines.push("");
96
94
  if (rule.frontmatter.description) {
@@ -158,7 +156,6 @@ function generateCopilotMarkdown(rule) {
158
156
  lines.push('applyTo: "**"');
159
157
  }
160
158
  lines.push("---");
161
- lines.push("");
162
159
  lines.push(rule.content);
163
160
  return lines.join("\n");
164
161
  }
@@ -195,6 +192,28 @@ function generateCursorMarkdown(rule) {
195
192
  }
196
193
  lines.push(`ruletype: ${ruletype}`);
197
194
  lines.push("---");
195
+ lines.push(rule.content);
196
+ return lines.join("\n");
197
+ }
198
+
199
+ // src/generators/roo.ts
200
+ var import_node_path5 = require("path");
201
+ async function generateRooConfig(rules, config) {
202
+ const outputs = [];
203
+ for (const rule of rules) {
204
+ const content = generateRooMarkdown(rule);
205
+ const filepath = (0, import_node_path5.join)(config.outputPaths.roo, `${rule.filename}.md`);
206
+ outputs.push({
207
+ tool: "roo",
208
+ filepath,
209
+ content
210
+ });
211
+ }
212
+ return outputs;
213
+ }
214
+ function generateRooMarkdown(rule) {
215
+ const lines = [];
216
+ lines.push(`# ${rule.frontmatter.description}`);
198
217
  lines.push("");
199
218
  lines.push(rule.content);
200
219
  return lines.join("\n");
@@ -208,10 +227,11 @@ function getDefaultConfig() {
208
227
  copilot: ".github/instructions",
209
228
  cursor: ".cursor/rules",
210
229
  cline: ".clinerules",
211
- claude: "."
230
+ claude: ".",
231
+ roo: ".roo/rules"
212
232
  },
213
233
  watchEnabled: false,
214
- defaultTargets: ["copilot", "cursor", "cline", "claude"]
234
+ defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
215
235
  };
216
236
  }
217
237
  function resolveTargets(targets, config) {
@@ -223,7 +243,7 @@ function resolveTargets(targets, config) {
223
243
 
224
244
  // src/utils/file.ts
225
245
  var import_promises = require("fs/promises");
226
- var import_node_path5 = require("path");
246
+ var import_node_path6 = require("path");
227
247
  async function ensureDir(dirPath) {
228
248
  try {
229
249
  await (0, import_promises.stat)(dirPath);
@@ -235,13 +255,13 @@ async function readFileContent(filepath) {
235
255
  return (0, import_promises.readFile)(filepath, "utf-8");
236
256
  }
237
257
  async function writeFileContent(filepath, content) {
238
- await ensureDir((0, import_node_path5.dirname)(filepath));
258
+ await ensureDir((0, import_node_path6.dirname)(filepath));
239
259
  await (0, import_promises.writeFile)(filepath, content, "utf-8");
240
260
  }
241
261
  async function findFiles(dir, extension = ".md") {
242
262
  try {
243
263
  const files = await (0, import_promises.readdir)(dir);
244
- return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path5.join)(dir, file));
264
+ return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path6.join)(dir, file));
245
265
  } catch {
246
266
  return [];
247
267
  }
@@ -255,6 +275,11 @@ async function fileExists(filepath) {
255
275
  }
256
276
  }
257
277
  async function removeDirectory(dirPath) {
278
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
279
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
280
+ console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
281
+ return;
282
+ }
258
283
  try {
259
284
  if (await fileExists(dirPath)) {
260
285
  await (0, import_promises.rm)(dirPath, { recursive: true, force: true });
@@ -263,6 +288,25 @@ async function removeDirectory(dirPath) {
263
288
  console.warn(`Failed to remove directory ${dirPath}:`, error);
264
289
  }
265
290
  }
291
+ async function removeFile(filepath) {
292
+ try {
293
+ if (await fileExists(filepath)) {
294
+ await (0, import_promises.rm)(filepath);
295
+ }
296
+ } catch (error) {
297
+ console.warn(`Failed to remove file ${filepath}:`, error);
298
+ }
299
+ }
300
+ async function removeClaudeGeneratedFiles() {
301
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
302
+ for (const fileOrDir of filesToRemove) {
303
+ if (fileOrDir.endsWith("/memories")) {
304
+ await removeDirectory(fileOrDir);
305
+ } else {
306
+ await removeFile(fileOrDir);
307
+ }
308
+ }
309
+ }
266
310
 
267
311
  // src/core/generator.ts
268
312
  async function generateConfigurations(rules, config, targetTools) {
@@ -297,6 +341,8 @@ async function generateForTool(tool, rules, config) {
297
341
  return generateClineConfig(rules, config);
298
342
  case "claude":
299
343
  return await generateClaudeConfig(rules, config);
344
+ case "roo":
345
+ return generateRooConfig(rules, config);
300
346
  default:
301
347
  console.warn(`Unknown tool: ${tool}`);
302
348
  return null;
@@ -304,7 +350,7 @@ async function generateForTool(tool, rules, config) {
304
350
  }
305
351
 
306
352
  // src/core/parser.ts
307
- var import_node_path6 = require("path");
353
+ var import_node_path7 = require("path");
308
354
  var import_gray_matter = __toESM(require("gray-matter"));
309
355
  async function parseRulesFromDirectory(aiRulesDir) {
310
356
  const ruleFiles = await findFiles(aiRulesDir);
@@ -324,7 +370,7 @@ async function parseRuleFile(filepath) {
324
370
  const parsed = (0, import_gray_matter.default)(content);
325
371
  validateFrontmatter(parsed.data, filepath);
326
372
  const frontmatter = parsed.data;
327
- const filename = (0, import_node_path6.basename)(filepath, ".md");
373
+ const filename = (0, import_node_path7.basename)(filepath, ".md");
328
374
  return {
329
375
  frontmatter,
330
376
  content: parsed.content,
@@ -452,7 +498,10 @@ async function generateCommand(options = {}) {
452
498
  deleteTasks.push(removeDirectory(config.outputPaths.cline));
453
499
  break;
454
500
  case "claude":
455
- deleteTasks.push(removeDirectory(config.outputPaths.claude));
501
+ deleteTasks.push(removeClaudeGeneratedFiles());
502
+ break;
503
+ case "roo":
504
+ deleteTasks.push(removeDirectory(config.outputPaths.roo));
456
505
  break;
457
506
  }
458
507
  }
@@ -480,15 +529,18 @@ async function generateCommand(options = {}) {
480
529
 
481
530
  // src/cli/commands/gitignore.ts
482
531
  var import_node_fs = require("fs");
483
- var import_node_path7 = require("path");
532
+ var import_node_path8 = require("path");
484
533
  var gitignoreCommand = async () => {
485
- const gitignorePath = (0, import_node_path7.join)(process.cwd(), ".gitignore");
534
+ const gitignorePath = (0, import_node_path8.join)(process.cwd(), ".gitignore");
486
535
  const rulesFilesToIgnore = [
487
536
  "# Generated by rulesync - AI tool configuration files",
537
+ ".github/copilot-instructions.md",
488
538
  ".github/instructions/",
489
539
  ".cursor/rules/",
490
540
  ".clinerules/",
491
- "CLAUDE.md"
541
+ "CLAUDE.md",
542
+ ".claude/memories/",
543
+ ".roo/rules/"
492
544
  ];
493
545
  let gitignoreContent = "";
494
546
  if ((0, import_node_fs.existsSync)(gitignorePath)) {
@@ -519,7 +571,7 @@ ${linesToAdd.join("\n")}
519
571
  };
520
572
 
521
573
  // src/cli/commands/init.ts
522
- var import_node_path8 = require("path");
574
+ var import_node_path9 = require("path");
523
575
  async function initCommand() {
524
576
  const aiRulesDir = ".rulesync";
525
577
  console.log("Initializing rulesync...");
@@ -609,7 +661,7 @@ globs: ["src/**/*.ts"]
609
661
  }
610
662
  ];
611
663
  for (const file of sampleFiles) {
612
- const filepath = (0, import_node_path8.join)(aiRulesDir, file.filename);
664
+ const filepath = (0, import_node_path9.join)(aiRulesDir, file.filename);
613
665
  if (!await fileExists(filepath)) {
614
666
  await writeFileContent(filepath, file.content);
615
667
  console.log(`Created ${filepath}`);
package/dist/index.mjs CHANGED
@@ -28,8 +28,10 @@ async function generateClaudeConfig(rules, config) {
28
28
  function generateClaudeMarkdown(rootRules, detailRules) {
29
29
  const lines = [];
30
30
  if (detailRules.length > 0) {
31
+ lines.push("Please also reference the following documents as needed:");
32
+ lines.push("");
31
33
  for (const rule of detailRules) {
32
- lines.push(`@${rule.filename}`);
34
+ lines.push(`@.claude/memories/${rule.filename}.md`);
33
35
  }
34
36
  lines.push("");
35
37
  }
@@ -64,10 +66,6 @@ function formatRuleForClaude(rule) {
64
66
  }
65
67
  function generateMemoryFile(rule) {
66
68
  const lines = [];
67
- lines.push("Please also refer to the following files as needed:");
68
- lines.push("");
69
- lines.push("---");
70
- lines.push("");
71
69
  lines.push(`# ${rule.filename}`);
72
70
  lines.push("");
73
71
  if (rule.frontmatter.description) {
@@ -135,7 +133,6 @@ function generateCopilotMarkdown(rule) {
135
133
  lines.push('applyTo: "**"');
136
134
  }
137
135
  lines.push("---");
138
- lines.push("");
139
136
  lines.push(rule.content);
140
137
  return lines.join("\n");
141
138
  }
@@ -172,6 +169,28 @@ function generateCursorMarkdown(rule) {
172
169
  }
173
170
  lines.push(`ruletype: ${ruletype}`);
174
171
  lines.push("---");
172
+ lines.push(rule.content);
173
+ return lines.join("\n");
174
+ }
175
+
176
+ // src/generators/roo.ts
177
+ import { join as join5 } from "path";
178
+ async function generateRooConfig(rules, config) {
179
+ const outputs = [];
180
+ for (const rule of rules) {
181
+ const content = generateRooMarkdown(rule);
182
+ const filepath = join5(config.outputPaths.roo, `${rule.filename}.md`);
183
+ outputs.push({
184
+ tool: "roo",
185
+ filepath,
186
+ content
187
+ });
188
+ }
189
+ return outputs;
190
+ }
191
+ function generateRooMarkdown(rule) {
192
+ const lines = [];
193
+ lines.push(`# ${rule.frontmatter.description}`);
175
194
  lines.push("");
176
195
  lines.push(rule.content);
177
196
  return lines.join("\n");
@@ -185,10 +204,11 @@ function getDefaultConfig() {
185
204
  copilot: ".github/instructions",
186
205
  cursor: ".cursor/rules",
187
206
  cline: ".clinerules",
188
- claude: "."
207
+ claude: ".",
208
+ roo: ".roo/rules"
189
209
  },
190
210
  watchEnabled: false,
191
- defaultTargets: ["copilot", "cursor", "cline", "claude"]
211
+ defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
192
212
  };
193
213
  }
194
214
  function resolveTargets(targets, config) {
@@ -200,7 +220,7 @@ function resolveTargets(targets, config) {
200
220
 
201
221
  // src/utils/file.ts
202
222
  import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
203
- import { dirname, join as join5 } from "path";
223
+ import { dirname, join as join6 } from "path";
204
224
  async function ensureDir(dirPath) {
205
225
  try {
206
226
  await stat(dirPath);
@@ -218,7 +238,7 @@ async function writeFileContent(filepath, content) {
218
238
  async function findFiles(dir, extension = ".md") {
219
239
  try {
220
240
  const files = await readdir(dir);
221
- return files.filter((file) => file.endsWith(extension)).map((file) => join5(dir, file));
241
+ return files.filter((file) => file.endsWith(extension)).map((file) => join6(dir, file));
222
242
  } catch {
223
243
  return [];
224
244
  }
@@ -232,6 +252,11 @@ async function fileExists(filepath) {
232
252
  }
233
253
  }
234
254
  async function removeDirectory(dirPath) {
255
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
256
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
257
+ console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
258
+ return;
259
+ }
235
260
  try {
236
261
  if (await fileExists(dirPath)) {
237
262
  await rm(dirPath, { recursive: true, force: true });
@@ -240,6 +265,25 @@ async function removeDirectory(dirPath) {
240
265
  console.warn(`Failed to remove directory ${dirPath}:`, error);
241
266
  }
242
267
  }
268
+ async function removeFile(filepath) {
269
+ try {
270
+ if (await fileExists(filepath)) {
271
+ await rm(filepath);
272
+ }
273
+ } catch (error) {
274
+ console.warn(`Failed to remove file ${filepath}:`, error);
275
+ }
276
+ }
277
+ async function removeClaudeGeneratedFiles() {
278
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
279
+ for (const fileOrDir of filesToRemove) {
280
+ if (fileOrDir.endsWith("/memories")) {
281
+ await removeDirectory(fileOrDir);
282
+ } else {
283
+ await removeFile(fileOrDir);
284
+ }
285
+ }
286
+ }
243
287
 
244
288
  // src/core/generator.ts
245
289
  async function generateConfigurations(rules, config, targetTools) {
@@ -274,6 +318,8 @@ async function generateForTool(tool, rules, config) {
274
318
  return generateClineConfig(rules, config);
275
319
  case "claude":
276
320
  return await generateClaudeConfig(rules, config);
321
+ case "roo":
322
+ return generateRooConfig(rules, config);
277
323
  default:
278
324
  console.warn(`Unknown tool: ${tool}`);
279
325
  return null;
@@ -429,7 +475,10 @@ async function generateCommand(options = {}) {
429
475
  deleteTasks.push(removeDirectory(config.outputPaths.cline));
430
476
  break;
431
477
  case "claude":
432
- deleteTasks.push(removeDirectory(config.outputPaths.claude));
478
+ deleteTasks.push(removeClaudeGeneratedFiles());
479
+ break;
480
+ case "roo":
481
+ deleteTasks.push(removeDirectory(config.outputPaths.roo));
433
482
  break;
434
483
  }
435
484
  }
@@ -457,15 +506,18 @@ async function generateCommand(options = {}) {
457
506
 
458
507
  // src/cli/commands/gitignore.ts
459
508
  import { existsSync, readFileSync, writeFileSync } from "fs";
460
- import { join as join6 } from "path";
509
+ import { join as join7 } from "path";
461
510
  var gitignoreCommand = async () => {
462
- const gitignorePath = join6(process.cwd(), ".gitignore");
511
+ const gitignorePath = join7(process.cwd(), ".gitignore");
463
512
  const rulesFilesToIgnore = [
464
513
  "# Generated by rulesync - AI tool configuration files",
514
+ ".github/copilot-instructions.md",
465
515
  ".github/instructions/",
466
516
  ".cursor/rules/",
467
517
  ".clinerules/",
468
- "CLAUDE.md"
518
+ "CLAUDE.md",
519
+ ".claude/memories/",
520
+ ".roo/rules/"
469
521
  ];
470
522
  let gitignoreContent = "";
471
523
  if (existsSync(gitignorePath)) {
@@ -496,7 +548,7 @@ ${linesToAdd.join("\n")}
496
548
  };
497
549
 
498
550
  // src/cli/commands/init.ts
499
- import { join as join7 } from "path";
551
+ import { join as join8 } from "path";
500
552
  async function initCommand() {
501
553
  const aiRulesDir = ".rulesync";
502
554
  console.log("Initializing rulesync...");
@@ -586,7 +638,7 @@ globs: ["src/**/*.ts"]
586
638
  }
587
639
  ];
588
640
  for (const file of sampleFiles) {
589
- const filepath = join7(aiRulesDir, file.filename);
641
+ const filepath = join8(aiRulesDir, file.filename);
590
642
  if (!await fileExists(filepath)) {
591
643
  await writeFileContent(filepath, file.content);
592
644
  console.log(`Created ${filepath}`);
package/package.json CHANGED
@@ -1,71 +1,72 @@
1
1
  {
2
- "name": "rulesync",
3
- "version": "0.5.0",
4
- "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "bin": {
9
- "rulesync": "dist/index.js"
10
- },
11
- "files": [
12
- "dist"
13
- ],
14
- "engines": {
15
- "node": ">=20.0.0"
16
- },
17
- "keywords": [
18
- "ai",
19
- "rules",
20
- "cli",
21
- "copilot",
22
- "cursor",
23
- "cline",
24
- "configuration",
25
- "development"
26
- ],
27
- "author": "dyoshikawa",
28
- "license": "MIT",
29
- "repository": {
30
- "type": "git",
31
- "url": "https://github.com/dyoshikawa/rulesync.git"
32
- },
33
- "bugs": {
34
- "url": "https://github.com/dyoshikawa/rulesync/issues"
35
- },
36
- "homepage": "https://github.com/dyoshikawa/rulesync#readme",
37
- "publishConfig": {
38
- "access": "public"
39
- },
40
- "packageManager": "pnpm@7.33.7",
41
- "devDependencies": {
42
- "@biomejs/biome": "2.0.0",
43
- "@secretlint/secretlint-rule-preset-recommend": "10.1.0",
44
- "@tsconfig/node24": "24.0.1",
45
- "@types/node": "24.0.3",
46
- "@vitest/coverage-v8": "3.2.4",
47
- "secretlint": "10.1.0",
48
- "tsup": "8.5.0",
49
- "tsx": "4.20.3",
50
- "typescript": "5.8.3",
51
- "vitest": "3.2.4"
52
- },
53
- "dependencies": {
54
- "chokidar": "4.0.3",
55
- "commander": "14.0.0",
56
- "gray-matter": "4.0.3",
57
- "marked": "15.0.12"
58
- },
59
- "scripts": {
60
- "dev": "tsx src/cli/index.ts",
61
- "build": "tsup src/cli/index.ts --format cjs,esm --dts --clean",
62
- "lint": "biome lint src/",
63
- "format": "biome format --write src/",
64
- "format:check": "biome format src/",
65
- "check": "biome check src/",
66
- "secretlint": "secretlint \"**/*\"",
67
- "test": "vitest",
68
- "test:watch": "vitest --watch",
69
- "test:coverage": "vitest --coverage"
70
- }
71
- }
2
+ "name": "rulesync",
3
+ "version": "0.9.0",
4
+ "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "rulesync": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "dev": "tsx src/cli/index.ts",
16
+ "build": "tsup src/cli/index.ts --format cjs,esm --dts --clean",
17
+ "lint": "biome lint src/",
18
+ "format": "biome format --write src/",
19
+ "format:check": "biome format src/",
20
+ "check": "biome check src/",
21
+ "secretlint": "secretlint \"**/*\"",
22
+ "test": "vitest",
23
+ "test:watch": "vitest --watch",
24
+ "test:coverage": "vitest --coverage",
25
+ "prepublishOnly": "pnpm build"
26
+ },
27
+ "engines": {
28
+ "node": ">=20.0.0"
29
+ },
30
+ "keywords": [
31
+ "ai",
32
+ "rules",
33
+ "cli",
34
+ "copilot",
35
+ "cursor",
36
+ "cline",
37
+ "configuration",
38
+ "development"
39
+ ],
40
+ "author": "dyoshikawa",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/dyoshikawa/rulesync.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/dyoshikawa/rulesync/issues"
48
+ },
49
+ "homepage": "https://github.com/dyoshikawa/rulesync#readme",
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "packageManager": "pnpm@7.33.7",
54
+ "devDependencies": {
55
+ "@biomejs/biome": "2.0.0",
56
+ "@secretlint/secretlint-rule-preset-recommend": "10.1.0",
57
+ "@tsconfig/node24": "24.0.1",
58
+ "@types/node": "24.0.3",
59
+ "@vitest/coverage-v8": "3.2.4",
60
+ "secretlint": "10.1.0",
61
+ "tsup": "8.5.0",
62
+ "tsx": "4.20.3",
63
+ "typescript": "5.8.3",
64
+ "vitest": "3.2.4"
65
+ },
66
+ "dependencies": {
67
+ "chokidar": "4.0.3",
68
+ "commander": "14.0.0",
69
+ "gray-matter": "4.0.3",
70
+ "marked": "15.0.12"
71
+ }
72
+ }