rulesync 0.16.0 → 0.18.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/dist/index.js +122 -38
- package/dist/index.mjs +122 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -368,14 +368,20 @@ var import_gray_matter = __toESM(require("gray-matter"));
|
|
|
368
368
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
369
369
|
const ruleFiles = await findFiles(aiRulesDir);
|
|
370
370
|
const rules = [];
|
|
371
|
+
const errors = [];
|
|
371
372
|
for (const filepath of ruleFiles) {
|
|
372
373
|
try {
|
|
373
374
|
const rule = await parseRuleFile(filepath);
|
|
374
375
|
rules.push(rule);
|
|
375
376
|
} catch (error) {
|
|
376
|
-
|
|
377
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
378
|
+
errors.push(`Failed to parse rule file ${filepath}: ${errorMessage}`);
|
|
377
379
|
}
|
|
378
380
|
}
|
|
381
|
+
if (errors.length > 0) {
|
|
382
|
+
throw new Error(`Validation errors found:
|
|
383
|
+
${errors.join("\n")}`);
|
|
384
|
+
}
|
|
379
385
|
return rules;
|
|
380
386
|
}
|
|
381
387
|
async function parseRuleFile(filepath) {
|
|
@@ -393,14 +399,36 @@ async function parseRuleFile(filepath) {
|
|
|
393
399
|
}
|
|
394
400
|
function validateFrontmatter(data, filepath) {
|
|
395
401
|
if (!data || typeof data !== "object") {
|
|
396
|
-
|
|
402
|
+
if (!data) {
|
|
403
|
+
throw new Error(
|
|
404
|
+
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
throw new Error(`Invalid frontmatter in ${filepath}: frontmatter must be a valid YAML object`);
|
|
397
408
|
}
|
|
398
409
|
const obj = data;
|
|
410
|
+
if (Object.keys(obj).length === 0) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
if (obj.root === void 0) {
|
|
416
|
+
throw new Error(`Missing required field "root" in ${filepath}: must be true or false`);
|
|
417
|
+
}
|
|
399
418
|
if (typeof obj.root !== "boolean") {
|
|
400
|
-
throw new Error(
|
|
419
|
+
throw new Error(
|
|
420
|
+
`Invalid "root" field in ${filepath}: must be a boolean (true or false), got ${typeof obj.root}`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (obj.targets === void 0) {
|
|
424
|
+
throw new Error(
|
|
425
|
+
`Missing required field "targets" in ${filepath}: must be an array like ["*"] or ["copilot", "cursor"]`
|
|
426
|
+
);
|
|
401
427
|
}
|
|
402
428
|
if (!Array.isArray(obj.targets)) {
|
|
403
|
-
throw new Error(
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
|
|
431
|
+
);
|
|
404
432
|
}
|
|
405
433
|
const validTargets = ["copilot", "cursor", "cline", "claude", "roo", "*"];
|
|
406
434
|
for (const target of obj.targets) {
|
|
@@ -410,15 +438,31 @@ function validateFrontmatter(data, filepath) {
|
|
|
410
438
|
);
|
|
411
439
|
}
|
|
412
440
|
}
|
|
441
|
+
if (obj.description === void 0) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
413
446
|
if (!obj.description || typeof obj.description !== "string") {
|
|
414
|
-
throw new Error(
|
|
447
|
+
throw new Error(
|
|
448
|
+
`Invalid "description" field in ${filepath}: must be a non-empty string, got ${typeof obj.description}`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
if (obj.globs === void 0) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Missing required field "globs" in ${filepath}: must be an array of file patterns like ["**/*.ts"]`
|
|
454
|
+
);
|
|
415
455
|
}
|
|
416
456
|
if (!Array.isArray(obj.globs)) {
|
|
417
|
-
throw new Error(
|
|
457
|
+
throw new Error(
|
|
458
|
+
`Invalid "globs" field in ${filepath}: must be an array, got ${typeof obj.globs}`
|
|
459
|
+
);
|
|
418
460
|
}
|
|
419
461
|
for (const glob of obj.globs) {
|
|
420
462
|
if (typeof glob !== "string") {
|
|
421
|
-
throw new Error(
|
|
463
|
+
throw new Error(
|
|
464
|
+
`Invalid glob pattern in ${filepath}: all globs must be strings, got ${typeof glob}`
|
|
465
|
+
);
|
|
422
466
|
}
|
|
423
467
|
}
|
|
424
468
|
}
|
|
@@ -622,15 +666,15 @@ async function initCommand() {
|
|
|
622
666
|
async function createSampleFiles(aiRulesDir) {
|
|
623
667
|
const sampleFiles = [
|
|
624
668
|
{
|
|
625
|
-
filename: "
|
|
669
|
+
filename: "overview.md",
|
|
626
670
|
content: `---
|
|
627
671
|
root: true
|
|
628
672
|
targets: ["*"]
|
|
629
|
-
description: "
|
|
673
|
+
description: "Project overview and general development guidelines"
|
|
630
674
|
globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
631
675
|
---
|
|
632
676
|
|
|
633
|
-
#
|
|
677
|
+
# Project Overview
|
|
634
678
|
|
|
635
679
|
## General Guidelines
|
|
636
680
|
|
|
@@ -646,54 +690,94 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
|
646
690
|
- Use semicolons
|
|
647
691
|
- Use double quotes for strings
|
|
648
692
|
- Use trailing commas in multi-line objects and arrays
|
|
693
|
+
|
|
694
|
+
## Architecture Principles
|
|
695
|
+
|
|
696
|
+
- Organize code by feature, not by file type
|
|
697
|
+
- Keep related files close together
|
|
698
|
+
- Use dependency injection for better testability
|
|
699
|
+
- Implement proper error handling
|
|
700
|
+
- Follow single responsibility principle
|
|
649
701
|
`
|
|
650
702
|
},
|
|
651
703
|
{
|
|
652
|
-
filename: "
|
|
704
|
+
filename: "frontend.md",
|
|
653
705
|
content: `---
|
|
654
706
|
root: false
|
|
655
707
|
targets: ["*"]
|
|
656
|
-
description: "
|
|
657
|
-
globs: ["**/*.
|
|
708
|
+
description: "Frontend development rules and best practices"
|
|
709
|
+
globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
|
|
658
710
|
---
|
|
659
711
|
|
|
660
|
-
#
|
|
712
|
+
# Frontend Development Rules
|
|
713
|
+
|
|
714
|
+
## React Components
|
|
661
715
|
|
|
662
|
-
|
|
663
|
-
-
|
|
664
|
-
- Use
|
|
665
|
-
-
|
|
716
|
+
- Use functional components with hooks
|
|
717
|
+
- Follow PascalCase naming for components
|
|
718
|
+
- Use TypeScript interfaces for props
|
|
719
|
+
- Implement proper error boundaries
|
|
666
720
|
|
|
667
|
-
##
|
|
668
|
-
- Use kebab-case for file names
|
|
669
|
-
- Use PascalCase for component files
|
|
670
|
-
- Use lowercase for directory names
|
|
721
|
+
## Styling
|
|
671
722
|
|
|
672
|
-
|
|
673
|
-
-
|
|
674
|
-
-
|
|
723
|
+
- Use CSS modules or styled-components
|
|
724
|
+
- Follow BEM methodology for CSS classes
|
|
725
|
+
- Prefer flexbox and grid for layouts
|
|
726
|
+
- Use semantic HTML elements
|
|
727
|
+
|
|
728
|
+
## State Management
|
|
729
|
+
|
|
730
|
+
- Use React hooks for local state
|
|
731
|
+
- Consider Redux or Zustand for global state
|
|
732
|
+
- Avoid prop drilling with context API
|
|
733
|
+
- Keep state as close to where it's used as possible
|
|
734
|
+
|
|
735
|
+
## Performance
|
|
736
|
+
|
|
737
|
+
- Use React.memo for expensive components
|
|
738
|
+
- Implement lazy loading for routes
|
|
739
|
+
- Optimize images and assets
|
|
740
|
+
- Use proper key props in lists
|
|
675
741
|
`
|
|
676
742
|
},
|
|
677
743
|
{
|
|
678
|
-
filename: "
|
|
744
|
+
filename: "backend.md",
|
|
679
745
|
content: `---
|
|
680
746
|
root: false
|
|
681
|
-
targets: ["
|
|
682
|
-
description: "
|
|
683
|
-
globs: ["src/**/*.ts"]
|
|
747
|
+
targets: ["*"]
|
|
748
|
+
description: "Backend development rules and API guidelines"
|
|
749
|
+
globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
684
750
|
---
|
|
685
751
|
|
|
686
|
-
#
|
|
752
|
+
# Backend Development Rules
|
|
687
753
|
|
|
688
|
-
##
|
|
689
|
-
- Organize code by feature, not by file type
|
|
690
|
-
- Keep related files close together
|
|
691
|
-
- Use index files for clean imports
|
|
754
|
+
## API Design
|
|
692
755
|
|
|
693
|
-
|
|
694
|
-
- Use
|
|
695
|
-
- Implement proper error handling
|
|
696
|
-
-
|
|
756
|
+
- Follow RESTful conventions
|
|
757
|
+
- Use consistent HTTP status codes
|
|
758
|
+
- Implement proper error handling with meaningful messages
|
|
759
|
+
- Use API versioning when necessary
|
|
760
|
+
|
|
761
|
+
## Database
|
|
762
|
+
|
|
763
|
+
- Use proper indexing for performance
|
|
764
|
+
- Implement database migrations
|
|
765
|
+
- Follow naming conventions for tables and columns
|
|
766
|
+
- Use transactions for data consistency
|
|
767
|
+
|
|
768
|
+
## Security
|
|
769
|
+
|
|
770
|
+
- Validate all input data
|
|
771
|
+
- Use proper authentication and authorization
|
|
772
|
+
- Implement rate limiting
|
|
773
|
+
- Sanitize database queries to prevent SQL injection
|
|
774
|
+
|
|
775
|
+
## Code Organization
|
|
776
|
+
|
|
777
|
+
- Use service layer pattern
|
|
778
|
+
- Implement proper logging
|
|
779
|
+
- Use environment variables for configuration
|
|
780
|
+
- Write comprehensive tests for business logic
|
|
697
781
|
`
|
|
698
782
|
}
|
|
699
783
|
];
|
package/dist/index.mjs
CHANGED
|
@@ -345,14 +345,20 @@ import matter from "gray-matter";
|
|
|
345
345
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
346
346
|
const ruleFiles = await findFiles(aiRulesDir);
|
|
347
347
|
const rules = [];
|
|
348
|
+
const errors = [];
|
|
348
349
|
for (const filepath of ruleFiles) {
|
|
349
350
|
try {
|
|
350
351
|
const rule = await parseRuleFile(filepath);
|
|
351
352
|
rules.push(rule);
|
|
352
353
|
} catch (error) {
|
|
353
|
-
|
|
354
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
355
|
+
errors.push(`Failed to parse rule file ${filepath}: ${errorMessage}`);
|
|
354
356
|
}
|
|
355
357
|
}
|
|
358
|
+
if (errors.length > 0) {
|
|
359
|
+
throw new Error(`Validation errors found:
|
|
360
|
+
${errors.join("\n")}`);
|
|
361
|
+
}
|
|
356
362
|
return rules;
|
|
357
363
|
}
|
|
358
364
|
async function parseRuleFile(filepath) {
|
|
@@ -370,14 +376,36 @@ async function parseRuleFile(filepath) {
|
|
|
370
376
|
}
|
|
371
377
|
function validateFrontmatter(data, filepath) {
|
|
372
378
|
if (!data || typeof data !== "object") {
|
|
373
|
-
|
|
379
|
+
if (!data) {
|
|
380
|
+
throw new Error(
|
|
381
|
+
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
throw new Error(`Invalid frontmatter in ${filepath}: frontmatter must be a valid YAML object`);
|
|
374
385
|
}
|
|
375
386
|
const obj = data;
|
|
387
|
+
if (Object.keys(obj).length === 0) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
if (obj.root === void 0) {
|
|
393
|
+
throw new Error(`Missing required field "root" in ${filepath}: must be true or false`);
|
|
394
|
+
}
|
|
376
395
|
if (typeof obj.root !== "boolean") {
|
|
377
|
-
throw new Error(
|
|
396
|
+
throw new Error(
|
|
397
|
+
`Invalid "root" field in ${filepath}: must be a boolean (true or false), got ${typeof obj.root}`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
if (obj.targets === void 0) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`Missing required field "targets" in ${filepath}: must be an array like ["*"] or ["copilot", "cursor"]`
|
|
403
|
+
);
|
|
378
404
|
}
|
|
379
405
|
if (!Array.isArray(obj.targets)) {
|
|
380
|
-
throw new Error(
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
|
|
408
|
+
);
|
|
381
409
|
}
|
|
382
410
|
const validTargets = ["copilot", "cursor", "cline", "claude", "roo", "*"];
|
|
383
411
|
for (const target of obj.targets) {
|
|
@@ -387,15 +415,31 @@ function validateFrontmatter(data, filepath) {
|
|
|
387
415
|
);
|
|
388
416
|
}
|
|
389
417
|
}
|
|
418
|
+
if (obj.description === void 0) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
390
423
|
if (!obj.description || typeof obj.description !== "string") {
|
|
391
|
-
throw new Error(
|
|
424
|
+
throw new Error(
|
|
425
|
+
`Invalid "description" field in ${filepath}: must be a non-empty string, got ${typeof obj.description}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
if (obj.globs === void 0) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`Missing required field "globs" in ${filepath}: must be an array of file patterns like ["**/*.ts"]`
|
|
431
|
+
);
|
|
392
432
|
}
|
|
393
433
|
if (!Array.isArray(obj.globs)) {
|
|
394
|
-
throw new Error(
|
|
434
|
+
throw new Error(
|
|
435
|
+
`Invalid "globs" field in ${filepath}: must be an array, got ${typeof obj.globs}`
|
|
436
|
+
);
|
|
395
437
|
}
|
|
396
438
|
for (const glob of obj.globs) {
|
|
397
439
|
if (typeof glob !== "string") {
|
|
398
|
-
throw new Error(
|
|
440
|
+
throw new Error(
|
|
441
|
+
`Invalid glob pattern in ${filepath}: all globs must be strings, got ${typeof glob}`
|
|
442
|
+
);
|
|
399
443
|
}
|
|
400
444
|
}
|
|
401
445
|
}
|
|
@@ -599,15 +643,15 @@ async function initCommand() {
|
|
|
599
643
|
async function createSampleFiles(aiRulesDir) {
|
|
600
644
|
const sampleFiles = [
|
|
601
645
|
{
|
|
602
|
-
filename: "
|
|
646
|
+
filename: "overview.md",
|
|
603
647
|
content: `---
|
|
604
648
|
root: true
|
|
605
649
|
targets: ["*"]
|
|
606
|
-
description: "
|
|
650
|
+
description: "Project overview and general development guidelines"
|
|
607
651
|
globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
608
652
|
---
|
|
609
653
|
|
|
610
|
-
#
|
|
654
|
+
# Project Overview
|
|
611
655
|
|
|
612
656
|
## General Guidelines
|
|
613
657
|
|
|
@@ -623,54 +667,94 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
|
623
667
|
- Use semicolons
|
|
624
668
|
- Use double quotes for strings
|
|
625
669
|
- Use trailing commas in multi-line objects and arrays
|
|
670
|
+
|
|
671
|
+
## Architecture Principles
|
|
672
|
+
|
|
673
|
+
- Organize code by feature, not by file type
|
|
674
|
+
- Keep related files close together
|
|
675
|
+
- Use dependency injection for better testability
|
|
676
|
+
- Implement proper error handling
|
|
677
|
+
- Follow single responsibility principle
|
|
626
678
|
`
|
|
627
679
|
},
|
|
628
680
|
{
|
|
629
|
-
filename: "
|
|
681
|
+
filename: "frontend.md",
|
|
630
682
|
content: `---
|
|
631
683
|
root: false
|
|
632
684
|
targets: ["*"]
|
|
633
|
-
description: "
|
|
634
|
-
globs: ["**/*.
|
|
685
|
+
description: "Frontend development rules and best practices"
|
|
686
|
+
globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
|
|
635
687
|
---
|
|
636
688
|
|
|
637
|
-
#
|
|
689
|
+
# Frontend Development Rules
|
|
690
|
+
|
|
691
|
+
## React Components
|
|
638
692
|
|
|
639
|
-
|
|
640
|
-
-
|
|
641
|
-
- Use
|
|
642
|
-
-
|
|
693
|
+
- Use functional components with hooks
|
|
694
|
+
- Follow PascalCase naming for components
|
|
695
|
+
- Use TypeScript interfaces for props
|
|
696
|
+
- Implement proper error boundaries
|
|
643
697
|
|
|
644
|
-
##
|
|
645
|
-
- Use kebab-case for file names
|
|
646
|
-
- Use PascalCase for component files
|
|
647
|
-
- Use lowercase for directory names
|
|
698
|
+
## Styling
|
|
648
699
|
|
|
649
|
-
|
|
650
|
-
-
|
|
651
|
-
-
|
|
700
|
+
- Use CSS modules or styled-components
|
|
701
|
+
- Follow BEM methodology for CSS classes
|
|
702
|
+
- Prefer flexbox and grid for layouts
|
|
703
|
+
- Use semantic HTML elements
|
|
704
|
+
|
|
705
|
+
## State Management
|
|
706
|
+
|
|
707
|
+
- Use React hooks for local state
|
|
708
|
+
- Consider Redux or Zustand for global state
|
|
709
|
+
- Avoid prop drilling with context API
|
|
710
|
+
- Keep state as close to where it's used as possible
|
|
711
|
+
|
|
712
|
+
## Performance
|
|
713
|
+
|
|
714
|
+
- Use React.memo for expensive components
|
|
715
|
+
- Implement lazy loading for routes
|
|
716
|
+
- Optimize images and assets
|
|
717
|
+
- Use proper key props in lists
|
|
652
718
|
`
|
|
653
719
|
},
|
|
654
720
|
{
|
|
655
|
-
filename: "
|
|
721
|
+
filename: "backend.md",
|
|
656
722
|
content: `---
|
|
657
723
|
root: false
|
|
658
|
-
targets: ["
|
|
659
|
-
description: "
|
|
660
|
-
globs: ["src/**/*.ts"]
|
|
724
|
+
targets: ["*"]
|
|
725
|
+
description: "Backend development rules and API guidelines"
|
|
726
|
+
globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
661
727
|
---
|
|
662
728
|
|
|
663
|
-
#
|
|
729
|
+
# Backend Development Rules
|
|
664
730
|
|
|
665
|
-
##
|
|
666
|
-
- Organize code by feature, not by file type
|
|
667
|
-
- Keep related files close together
|
|
668
|
-
- Use index files for clean imports
|
|
731
|
+
## API Design
|
|
669
732
|
|
|
670
|
-
|
|
671
|
-
- Use
|
|
672
|
-
- Implement proper error handling
|
|
673
|
-
-
|
|
733
|
+
- Follow RESTful conventions
|
|
734
|
+
- Use consistent HTTP status codes
|
|
735
|
+
- Implement proper error handling with meaningful messages
|
|
736
|
+
- Use API versioning when necessary
|
|
737
|
+
|
|
738
|
+
## Database
|
|
739
|
+
|
|
740
|
+
- Use proper indexing for performance
|
|
741
|
+
- Implement database migrations
|
|
742
|
+
- Follow naming conventions for tables and columns
|
|
743
|
+
- Use transactions for data consistency
|
|
744
|
+
|
|
745
|
+
## Security
|
|
746
|
+
|
|
747
|
+
- Validate all input data
|
|
748
|
+
- Use proper authentication and authorization
|
|
749
|
+
- Implement rate limiting
|
|
750
|
+
- Sanitize database queries to prevent SQL injection
|
|
751
|
+
|
|
752
|
+
## Code Organization
|
|
753
|
+
|
|
754
|
+
- Use service layer pattern
|
|
755
|
+
- Implement proper logging
|
|
756
|
+
- Use environment variables for configuration
|
|
757
|
+
- Write comprehensive tests for business logic
|
|
674
758
|
`
|
|
675
759
|
}
|
|
676
760
|
];
|
package/package.json
CHANGED