@vibe-agent-toolkit/resources 0.1.2 → 0.1.4
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 +255 -17
- package/dist/collection-matcher.d.ts +63 -0
- package/dist/collection-matcher.d.ts.map +1 -0
- package/dist/collection-matcher.js +127 -0
- package/dist/collection-matcher.js.map +1 -0
- package/dist/config-parser.d.ts +63 -0
- package/dist/config-parser.d.ts.map +1 -0
- package/dist/config-parser.js +113 -0
- package/dist/config-parser.js.map +1 -0
- package/dist/frontmatter-validator.d.ts +50 -0
- package/dist/frontmatter-validator.d.ts.map +1 -0
- package/dist/frontmatter-validator.js +238 -0
- package/dist/frontmatter-validator.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/link-parser.d.ts +2 -0
- package/dist/link-parser.d.ts.map +1 -1
- package/dist/link-parser.js +41 -0
- package/dist/link-parser.js.map +1 -1
- package/dist/link-validator.d.ts +25 -3
- package/dist/link-validator.d.ts.map +1 -1
- package/dist/link-validator.js +52 -40
- package/dist/link-validator.js.map +1 -1
- package/dist/multi-schema-validator.d.ts +42 -0
- package/dist/multi-schema-validator.d.ts.map +1 -0
- package/dist/multi-schema-validator.js +107 -0
- package/dist/multi-schema-validator.js.map +1 -0
- package/dist/pattern-expander.d.ts +63 -0
- package/dist/pattern-expander.d.ts.map +1 -0
- package/dist/pattern-expander.js +93 -0
- package/dist/pattern-expander.js.map +1 -0
- package/dist/resource-registry.d.ts +104 -8
- package/dist/resource-registry.d.ts.map +1 -1
- package/dist/resource-registry.js +230 -30
- package/dist/resource-registry.js.map +1 -1
- package/dist/schema-assignment.d.ts +49 -0
- package/dist/schema-assignment.d.ts.map +1 -0
- package/dist/schema-assignment.js +95 -0
- package/dist/schema-assignment.js.map +1 -0
- package/dist/schemas/project-config.d.ts +254 -0
- package/dist/schemas/project-config.d.ts.map +1 -0
- package/dist/schemas/project-config.js +57 -0
- package/dist/schemas/project-config.js.map +1 -0
- package/dist/schemas/resource-metadata.d.ts +10 -1
- package/dist/schemas/resource-metadata.d.ts.map +1 -1
- package/dist/schemas/resource-metadata.js +7 -1
- package/dist/schemas/resource-metadata.js.map +1 -1
- package/dist/schemas/validation-result.d.ts +9 -24
- package/dist/schemas/validation-result.d.ts.map +1 -1
- package/dist/schemas/validation-result.js +11 -18
- package/dist/schemas/validation-result.js.map +1 -1
- package/dist/types/resource-parser.d.ts +53 -0
- package/dist/types/resource-parser.d.ts.map +1 -0
- package/dist/types/resource-parser.js +233 -0
- package/dist/types/resource-parser.js.map +1 -0
- package/dist/types/resource-path-utils.d.ts +43 -0
- package/dist/types/resource-path-utils.d.ts.map +1 -0
- package/dist/types/resource-path-utils.js +89 -0
- package/dist/types/resource-path-utils.js.map +1 -0
- package/dist/types/resources.d.ts +140 -0
- package/dist/types/resources.d.ts.map +1 -0
- package/dist/types/resources.js +58 -0
- package/dist/types/resources.js.map +1 -0
- package/dist/types.d.ts +14 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +18 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +39 -0
- package/dist/utils.js.map +1 -1
- package/package.json +5 -2
- package/src/collection-matcher.ts +148 -0
- package/src/config-parser.ts +125 -0
- package/src/frontmatter-validator.ts +279 -0
- package/src/index.ts +10 -2
- package/src/link-parser.ts +50 -0
- package/src/link-validator.ts +70 -43
- package/src/multi-schema-validator.ts +128 -0
- package/src/pattern-expander.ts +100 -0
- package/src/resource-registry.ts +347 -34
- package/src/schema-assignment.ts +119 -0
- package/src/schemas/project-config.ts +71 -0
- package/src/schemas/resource-metadata.ts +7 -1
- package/src/schemas/validation-result.ts +11 -21
- package/src/types/resource-parser.ts +302 -0
- package/src/types/resource-path-utils.ts +102 -0
- package/src/types/resources.ts +211 -0
- package/src/types.ts +89 -1
- package/src/utils.ts +43 -0
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ Markdown resource parsing, validation, and link integrity checking for AI agent
|
|
|
6
6
|
|
|
7
7
|
- **Parse markdown files** - Extract links, headings, and metadata using unified/remark
|
|
8
8
|
- **Validate link integrity** - Check local file links, anchor links, and detect broken references
|
|
9
|
+
- **Frontmatter support** - Parse YAML frontmatter, optionally validate against JSON Schemas
|
|
9
10
|
- **Track resource collections** - Manage multiple markdown files with automatic ID generation
|
|
10
11
|
- **Resolve cross-references** - Link resources together and track dependencies
|
|
11
12
|
- **Query capabilities** - Find resources by path, ID, or glob patterns with lazy evaluation
|
|
@@ -62,9 +63,6 @@ Main class for managing collections of markdown resources.
|
|
|
62
63
|
new ResourceRegistry(options?: ResourceRegistryOptions)
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
**Options:**
|
|
66
|
-
- `validateOnAdd?: boolean` - Validate resources immediately when added (default: `false`)
|
|
67
|
-
|
|
68
66
|
#### Methods
|
|
69
67
|
|
|
70
68
|
##### addResource(filePath: string): Promise<ResourceMetadata>
|
|
@@ -570,6 +568,260 @@ type ValidationSeverity = 'error' | 'warning' | 'info';
|
|
|
570
568
|
- `warning` - Non-critical issue that should be addressed (e.g., questionable link format)
|
|
571
569
|
- `info` - Informational message (e.g., external URL not validated)
|
|
572
570
|
|
|
571
|
+
## Frontmatter Support
|
|
572
|
+
|
|
573
|
+
The resources package parses YAML frontmatter from markdown files and stores it in `ResourceMetadata.frontmatter`. You can optionally validate frontmatter against JSON Schemas.
|
|
574
|
+
|
|
575
|
+
### Basic Frontmatter Parsing
|
|
576
|
+
|
|
577
|
+
Frontmatter is automatically parsed when resources are added:
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
import { ResourceRegistry } from '@vibe-agent-toolkit/resources';
|
|
581
|
+
|
|
582
|
+
const registry = new ResourceRegistry();
|
|
583
|
+
await registry.addResource('./docs/guide.md');
|
|
584
|
+
|
|
585
|
+
const resource = registry.getResource('./docs/guide.md');
|
|
586
|
+
console.log('Frontmatter:', resource.frontmatter);
|
|
587
|
+
// { title: 'User Guide', category: 'tutorial', tags: ['api', 'getting-started'] }
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**Supported format**: YAML frontmatter between `---` delimiters at the start of the file:
|
|
591
|
+
|
|
592
|
+
```markdown
|
|
593
|
+
---
|
|
594
|
+
title: User Guide
|
|
595
|
+
category: tutorial
|
|
596
|
+
tags:
|
|
597
|
+
- api
|
|
598
|
+
- getting-started
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
# Content starts here
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
### Frontmatter Validation
|
|
605
|
+
|
|
606
|
+
Validate frontmatter against JSON Schema to enforce required fields and data types:
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
import { FrontmatterValidator } from '@vibe-agent-toolkit/resources';
|
|
610
|
+
|
|
611
|
+
// Create validator with JSON Schema
|
|
612
|
+
const validator = new FrontmatterValidator({
|
|
613
|
+
type: 'object',
|
|
614
|
+
required: ['title', 'description'],
|
|
615
|
+
properties: {
|
|
616
|
+
title: { type: 'string', minLength: 1 },
|
|
617
|
+
description: { type: 'string' },
|
|
618
|
+
category: { enum: ['guide', 'reference', 'tutorial', 'api'] },
|
|
619
|
+
tags: { type: 'array', items: { type: 'string' } }
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
// Validate a resource
|
|
624
|
+
const resource = registry.getResource('./docs/guide.md');
|
|
625
|
+
const result = validator.validate(resource);
|
|
626
|
+
|
|
627
|
+
if (!result.valid) {
|
|
628
|
+
console.error('Validation errors:', result.errors);
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Schema Design Patterns
|
|
633
|
+
|
|
634
|
+
#### Pattern 1: Required Fields, Allow Extras
|
|
635
|
+
|
|
636
|
+
Most projects have files (README.md, etc.) without frontmatter. Use `required` for must-have fields but allow custom fields:
|
|
637
|
+
|
|
638
|
+
```json
|
|
639
|
+
{
|
|
640
|
+
"type": "object",
|
|
641
|
+
"required": ["title", "description"],
|
|
642
|
+
"additionalProperties": true,
|
|
643
|
+
"properties": {
|
|
644
|
+
"title": { "type": "string", "minLength": 1 },
|
|
645
|
+
"description": { "type": "string" },
|
|
646
|
+
"category": { "enum": ["guide", "reference", "tutorial", "api"] }
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Behavior**:
|
|
652
|
+
- Files without frontmatter: **Error** (missing required fields)
|
|
653
|
+
- Files with partial frontmatter: **Error** (missing required fields)
|
|
654
|
+
- Files with complete frontmatter: **Valid**
|
|
655
|
+
- Extra fields allowed: **Yes**
|
|
656
|
+
|
|
657
|
+
#### Pattern 2: Optional Fields Only
|
|
658
|
+
|
|
659
|
+
For projects where frontmatter is optional:
|
|
660
|
+
|
|
661
|
+
```json
|
|
662
|
+
{
|
|
663
|
+
"type": "object",
|
|
664
|
+
"additionalProperties": true,
|
|
665
|
+
"properties": {
|
|
666
|
+
"title": { "type": "string" },
|
|
667
|
+
"description": { "type": "string" },
|
|
668
|
+
"category": { "enum": ["guide", "reference", "tutorial", "api"] }
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**Behavior**:
|
|
674
|
+
- Files without frontmatter: **Valid** (all fields optional)
|
|
675
|
+
- Files with frontmatter: **Validated** (fields must match schema types)
|
|
676
|
+
- Extra fields allowed: **Yes**
|
|
677
|
+
|
|
678
|
+
#### Pattern 3: Strict Schema
|
|
679
|
+
|
|
680
|
+
For knowledge bases where all metadata is required:
|
|
681
|
+
|
|
682
|
+
```json
|
|
683
|
+
{
|
|
684
|
+
"type": "object",
|
|
685
|
+
"required": ["title", "description", "category", "keywords"],
|
|
686
|
+
"additionalProperties": false,
|
|
687
|
+
"properties": {
|
|
688
|
+
"title": { "type": "string", "minLength": 1 },
|
|
689
|
+
"description": { "type": "string" },
|
|
690
|
+
"category": { "enum": ["guide", "reference", "tutorial", "api"] },
|
|
691
|
+
"keywords": { "type": "array", "items": { "type": "string" } },
|
|
692
|
+
"source_url": { "type": "string", "format": "uri" }
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Behavior**:
|
|
698
|
+
- Files without frontmatter: **Error** (missing required fields)
|
|
699
|
+
- Extra fields: **Error** (`additionalProperties: false`)
|
|
700
|
+
- All fields must match types: **Yes**
|
|
701
|
+
|
|
702
|
+
### CLI Usage
|
|
703
|
+
|
|
704
|
+
The `vat resources validate` command supports frontmatter validation:
|
|
705
|
+
|
|
706
|
+
```bash
|
|
707
|
+
# Parse frontmatter, report YAML errors only
|
|
708
|
+
vat resources validate docs/
|
|
709
|
+
|
|
710
|
+
# Validate against JSON Schema
|
|
711
|
+
vat resources validate docs/ --frontmatter-schema schema.json
|
|
712
|
+
|
|
713
|
+
# Example output with schema validation
|
|
714
|
+
vat resources validate docs/ --frontmatter-schema schema.json
|
|
715
|
+
# Resources validated: 42
|
|
716
|
+
# Links validated: 156
|
|
717
|
+
# Frontmatter errors:
|
|
718
|
+
# docs/guide.md: Missing required property 'description'
|
|
719
|
+
# docs/api.md: Property 'category' must be one of: guide, reference, tutorial, api
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### Validation Result
|
|
723
|
+
|
|
724
|
+
Frontmatter validation results are included in `ValidationResult`:
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
interface ValidationResult {
|
|
728
|
+
// ... existing fields
|
|
729
|
+
frontmatterValidation?: {
|
|
730
|
+
valid: boolean;
|
|
731
|
+
errors: Array<{
|
|
732
|
+
resourcePath: string;
|
|
733
|
+
message: string;
|
|
734
|
+
field?: string;
|
|
735
|
+
}>;
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Example Schemas
|
|
741
|
+
|
|
742
|
+
#### Knowledge Base Schema
|
|
743
|
+
|
|
744
|
+
```json
|
|
745
|
+
{
|
|
746
|
+
"type": "object",
|
|
747
|
+
"required": ["title", "description"],
|
|
748
|
+
"additionalProperties": true,
|
|
749
|
+
"properties": {
|
|
750
|
+
"title": { "type": "string", "minLength": 1 },
|
|
751
|
+
"description": { "type": "string" },
|
|
752
|
+
"category": { "enum": ["guide", "reference", "tutorial", "api"] },
|
|
753
|
+
"keywords": { "type": "array", "items": { "type": "string" } },
|
|
754
|
+
"source_url": { "type": "string", "format": "uri" },
|
|
755
|
+
"author": { "type": "string" },
|
|
756
|
+
"last_updated": { "type": "string", "format": "date" }
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
#### Blog Post Schema
|
|
762
|
+
|
|
763
|
+
```json
|
|
764
|
+
{
|
|
765
|
+
"type": "object",
|
|
766
|
+
"required": ["title", "date", "author"],
|
|
767
|
+
"additionalProperties": false,
|
|
768
|
+
"properties": {
|
|
769
|
+
"title": { "type": "string", "minLength": 1 },
|
|
770
|
+
"date": { "type": "string", "format": "date" },
|
|
771
|
+
"author": { "type": "string" },
|
|
772
|
+
"tags": { "type": "array", "items": { "type": "string" } },
|
|
773
|
+
"excerpt": { "type": "string" },
|
|
774
|
+
"featured": { "type": "boolean" }
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### API Documentation Schema
|
|
780
|
+
|
|
781
|
+
```json
|
|
782
|
+
{
|
|
783
|
+
"type": "object",
|
|
784
|
+
"required": ["title", "api_version"],
|
|
785
|
+
"additionalProperties": true,
|
|
786
|
+
"properties": {
|
|
787
|
+
"title": { "type": "string", "minLength": 1 },
|
|
788
|
+
"api_version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
|
|
789
|
+
"endpoint": { "type": "string" },
|
|
790
|
+
"method": { "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"] },
|
|
791
|
+
"deprecated": { "type": "boolean" }
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Error Handling
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
const validator = new FrontmatterValidator(schema);
|
|
800
|
+
|
|
801
|
+
for (const resource of registry.getAllResources()) {
|
|
802
|
+
const result = validator.validate(resource);
|
|
803
|
+
|
|
804
|
+
if (!result.valid) {
|
|
805
|
+
console.error(`\n${resource.filePath}:`);
|
|
806
|
+
for (const error of result.errors) {
|
|
807
|
+
console.error(` - ${error.message}`);
|
|
808
|
+
if (error.field) {
|
|
809
|
+
console.error(` Field: ${error.field}`);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Common Validation Errors
|
|
817
|
+
|
|
818
|
+
- **Missing required property**: Field specified in `required` array is missing
|
|
819
|
+
- **Invalid type**: Field value doesn't match the type (e.g., number instead of string)
|
|
820
|
+
- **Invalid enum value**: Field value is not in the allowed enum values
|
|
821
|
+
- **Invalid format**: String doesn't match the format constraint (e.g., "uri", "date")
|
|
822
|
+
- **Additional property not allowed**: Extra field present when `additionalProperties: false`
|
|
823
|
+
- **YAML parsing error**: Invalid YAML syntax in frontmatter
|
|
824
|
+
|
|
573
825
|
## Schemas
|
|
574
826
|
|
|
575
827
|
All types are backed by Zod schemas for runtime validation. You can import schemas for advanced use cases:
|
|
@@ -651,20 +903,6 @@ for (const [path, issues] of issuesByResource) {
|
|
|
651
903
|
}
|
|
652
904
|
```
|
|
653
905
|
|
|
654
|
-
### Validate on Add
|
|
655
|
-
|
|
656
|
-
Enable strict validation mode to fail fast on broken links:
|
|
657
|
-
|
|
658
|
-
```typescript
|
|
659
|
-
const registry = new ResourceRegistry({ validateOnAdd: true });
|
|
660
|
-
|
|
661
|
-
try {
|
|
662
|
-
await registry.addResource('./docs/broken.md');
|
|
663
|
-
} catch (error) {
|
|
664
|
-
console.error('Validation failed:', error.message);
|
|
665
|
-
}
|
|
666
|
-
```
|
|
667
|
-
|
|
668
906
|
## Examples
|
|
669
907
|
|
|
670
908
|
### Validate Project Documentation
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection matching utilities for determining which collections a file belongs to.
|
|
3
|
+
*
|
|
4
|
+
* Applies include/exclude pattern rules with precedence (exclude wins).
|
|
5
|
+
*/
|
|
6
|
+
import type { CollectionConfig } from './schemas/project-config.js';
|
|
7
|
+
/**
|
|
8
|
+
* Check if a file path matches a collection's include/exclude rules.
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* - File must match at least one include pattern
|
|
12
|
+
* - File must NOT match any exclude pattern
|
|
13
|
+
* - Exclude always wins over include
|
|
14
|
+
* - Pattern order does not matter
|
|
15
|
+
*
|
|
16
|
+
* Special handling for root-level patterns (*.md, *.json):
|
|
17
|
+
* - These match against the basename only, not the full path
|
|
18
|
+
* - Allows matching root-level files regardless of their absolute path
|
|
19
|
+
*
|
|
20
|
+
* Paths are normalized to forward slashes before matching for cross-platform consistency.
|
|
21
|
+
*
|
|
22
|
+
* @param filePath - Absolute file path to check
|
|
23
|
+
* @param collection - Collection configuration with include/exclude patterns
|
|
24
|
+
* @returns True if file belongs to collection
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const collection = {
|
|
29
|
+
* include: ['docs'],
|
|
30
|
+
* exclude: ['**\/README.md']
|
|
31
|
+
* };
|
|
32
|
+
*
|
|
33
|
+
* matchesCollection('/project/docs/guide.md', collection) // true
|
|
34
|
+
* matchesCollection('/project/docs/README.md', collection) // false (excluded)
|
|
35
|
+
* matchesCollection('/project/src/index.ts', collection) // false (not included)
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function matchesCollection(filePath: string, collection: CollectionConfig): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Find all collections that a file belongs to.
|
|
41
|
+
*
|
|
42
|
+
* A file can belong to multiple collections if it matches their rules.
|
|
43
|
+
*
|
|
44
|
+
* @param filePath - Absolute file path to check
|
|
45
|
+
* @param collections - Map of collection name to collection config
|
|
46
|
+
* @returns Array of collection names the file belongs to
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const collections = {
|
|
51
|
+
* 'rag-kb': { include: ['docs'], exclude: ['**\/README.md'] },
|
|
52
|
+
* 'skills': { include: ['**\/SKILL.md'] }
|
|
53
|
+
* };
|
|
54
|
+
*
|
|
55
|
+
* getCollectionsForFile('/project/docs/guide.md', collections)
|
|
56
|
+
* // ['rag-kb']
|
|
57
|
+
*
|
|
58
|
+
* getCollectionsForFile('/project/docs/SKILL.md', collections)
|
|
59
|
+
* // ['rag-kb', 'skills']
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function getCollectionsForFile(filePath: string, collections: Record<string, CollectionConfig>): string[];
|
|
63
|
+
//# sourceMappingURL=collection-matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-matcher.d.ts","sourceRoot":"","sources":["../src/collection-matcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAWpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAuDzF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAC5C,MAAM,EAAE,CAUV"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection matching utilities for determining which collections a file belongs to.
|
|
3
|
+
*
|
|
4
|
+
* Applies include/exclude pattern rules with precedence (exclude wins).
|
|
5
|
+
*/
|
|
6
|
+
import { basename as getBasename } from 'node:path';
|
|
7
|
+
import { toForwardSlash } from '@vibe-agent-toolkit/utils';
|
|
8
|
+
import picomatch from 'picomatch';
|
|
9
|
+
import { expandPatterns } from './pattern-expander.js';
|
|
10
|
+
/**
|
|
11
|
+
* Check if a pattern is a root-level pattern (e.g., *.md, *.json).
|
|
12
|
+
*
|
|
13
|
+
* Root-level patterns start with * but not **.
|
|
14
|
+
*/
|
|
15
|
+
function isRootLevelPattern(pattern) {
|
|
16
|
+
return pattern.startsWith('*') && !pattern.startsWith('**');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check if a file path matches a collection's include/exclude rules.
|
|
20
|
+
*
|
|
21
|
+
* Rules:
|
|
22
|
+
* - File must match at least one include pattern
|
|
23
|
+
* - File must NOT match any exclude pattern
|
|
24
|
+
* - Exclude always wins over include
|
|
25
|
+
* - Pattern order does not matter
|
|
26
|
+
*
|
|
27
|
+
* Special handling for root-level patterns (*.md, *.json):
|
|
28
|
+
* - These match against the basename only, not the full path
|
|
29
|
+
* - Allows matching root-level files regardless of their absolute path
|
|
30
|
+
*
|
|
31
|
+
* Paths are normalized to forward slashes before matching for cross-platform consistency.
|
|
32
|
+
*
|
|
33
|
+
* @param filePath - Absolute file path to check
|
|
34
|
+
* @param collection - Collection configuration with include/exclude patterns
|
|
35
|
+
* @returns True if file belongs to collection
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const collection = {
|
|
40
|
+
* include: ['docs'],
|
|
41
|
+
* exclude: ['**\/README.md']
|
|
42
|
+
* };
|
|
43
|
+
*
|
|
44
|
+
* matchesCollection('/project/docs/guide.md', collection) // true
|
|
45
|
+
* matchesCollection('/project/docs/README.md', collection) // false (excluded)
|
|
46
|
+
* matchesCollection('/project/src/index.ts', collection) // false (not included)
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function matchesCollection(filePath, collection) {
|
|
50
|
+
// Normalize to forward slashes for cross-platform consistency
|
|
51
|
+
const normalizedPath = toForwardSlash(filePath);
|
|
52
|
+
// Extract basename for root-level pattern matching
|
|
53
|
+
const basename = getBasename(filePath);
|
|
54
|
+
// Expand patterns (paths → globs)
|
|
55
|
+
const includePatterns = expandPatterns(collection.include);
|
|
56
|
+
const excludePatterns = collection.exclude ? expandPatterns(collection.exclude) : [];
|
|
57
|
+
// Separate root-level patterns from other patterns
|
|
58
|
+
const includeRootPatterns = includePatterns.filter(isRootLevelPattern);
|
|
59
|
+
const includeNonRootPatterns = includePatterns.filter((p) => !isRootLevelPattern(p));
|
|
60
|
+
const excludeRootPatterns = excludePatterns.filter(isRootLevelPattern);
|
|
61
|
+
const excludeNonRootPatterns = excludePatterns.filter((p) => !isRootLevelPattern(p));
|
|
62
|
+
// Check excludes first (exclude wins)
|
|
63
|
+
// Check non-root excludes against full path
|
|
64
|
+
if (excludeNonRootPatterns.length > 0) {
|
|
65
|
+
const excludeMatcher = picomatch(excludeNonRootPatterns);
|
|
66
|
+
if (excludeMatcher(normalizedPath)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Check root-level excludes against basename
|
|
71
|
+
if (excludeRootPatterns.length > 0) {
|
|
72
|
+
const excludeRootMatcher = picomatch(excludeRootPatterns);
|
|
73
|
+
if (excludeRootMatcher(basename)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Check includes (need to match at least one)
|
|
78
|
+
let matched = false;
|
|
79
|
+
// Check non-root includes against full path
|
|
80
|
+
if (includeNonRootPatterns.length > 0) {
|
|
81
|
+
const includeMatcher = picomatch(includeNonRootPatterns);
|
|
82
|
+
if (includeMatcher(normalizedPath)) {
|
|
83
|
+
matched = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check root-level includes against basename
|
|
87
|
+
if (!matched && includeRootPatterns.length > 0) {
|
|
88
|
+
const includeRootMatcher = picomatch(includeRootPatterns);
|
|
89
|
+
if (includeRootMatcher(basename)) {
|
|
90
|
+
matched = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return matched;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Find all collections that a file belongs to.
|
|
97
|
+
*
|
|
98
|
+
* A file can belong to multiple collections if it matches their rules.
|
|
99
|
+
*
|
|
100
|
+
* @param filePath - Absolute file path to check
|
|
101
|
+
* @param collections - Map of collection name to collection config
|
|
102
|
+
* @returns Array of collection names the file belongs to
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const collections = {
|
|
107
|
+
* 'rag-kb': { include: ['docs'], exclude: ['**\/README.md'] },
|
|
108
|
+
* 'skills': { include: ['**\/SKILL.md'] }
|
|
109
|
+
* };
|
|
110
|
+
*
|
|
111
|
+
* getCollectionsForFile('/project/docs/guide.md', collections)
|
|
112
|
+
* // ['rag-kb']
|
|
113
|
+
*
|
|
114
|
+
* getCollectionsForFile('/project/docs/SKILL.md', collections)
|
|
115
|
+
* // ['rag-kb', 'skills']
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function getCollectionsForFile(filePath, collections) {
|
|
119
|
+
const matchingCollections = [];
|
|
120
|
+
for (const [name, config] of Object.entries(collections)) {
|
|
121
|
+
if (matchesCollection(filePath, config)) {
|
|
122
|
+
matchingCollections.push(name);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return matchingCollections;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=collection-matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-matcher.js","sourceRoot":"","sources":["../src/collection-matcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file parser for vibe-agent-toolkit.config.yaml
|
|
3
|
+
*
|
|
4
|
+
* Discovers and parses project configuration files with directory tree walk-up.
|
|
5
|
+
*/
|
|
6
|
+
import { type ProjectConfig } from './schemas/project-config.js';
|
|
7
|
+
/**
|
|
8
|
+
* Find the config file by walking up the directory tree.
|
|
9
|
+
*
|
|
10
|
+
* Starts from the current directory and walks up until the config file is found
|
|
11
|
+
* or the root directory is reached.
|
|
12
|
+
*
|
|
13
|
+
* @param startDir - Directory to start searching from (default: process.cwd())
|
|
14
|
+
* @returns Absolute path to config file, or undefined if not found
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const configPath = await findConfigFile();
|
|
19
|
+
* if (configPath) {
|
|
20
|
+
* console.log(`Found config: ${configPath}`);
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function findConfigFile(startDir?: string): Promise<string | undefined>;
|
|
25
|
+
/**
|
|
26
|
+
* Parse a project configuration file.
|
|
27
|
+
*
|
|
28
|
+
* Reads the YAML file, parses it, and validates against the schema.
|
|
29
|
+
*
|
|
30
|
+
* @param configPath - Absolute path to config file
|
|
31
|
+
* @returns Parsed and validated configuration
|
|
32
|
+
* @throws Error if file cannot be read, YAML is invalid, or validation fails
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const config = await parseConfigFile('/project/vibe-agent-toolkit.config.yaml');
|
|
37
|
+
* console.log(`Version: ${config.version}`);
|
|
38
|
+
* console.log(`Collections: ${Object.keys(config.resources?.collections ?? {}).join(', ')}`);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseConfigFile(configPath: string): Promise<ProjectConfig>;
|
|
42
|
+
/**
|
|
43
|
+
* Load project configuration by discovering and parsing config file.
|
|
44
|
+
*
|
|
45
|
+
* Walks up the directory tree from startDir to find the config file,
|
|
46
|
+
* then parses and validates it.
|
|
47
|
+
*
|
|
48
|
+
* @param startDir - Directory to start searching from (default: process.cwd())
|
|
49
|
+
* @returns Parsed configuration, or undefined if no config file found
|
|
50
|
+
* @throws Error if config file is found but cannot be parsed or is invalid
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const config = await loadConfig();
|
|
55
|
+
* if (config) {
|
|
56
|
+
* console.log('Using project config');
|
|
57
|
+
* } else {
|
|
58
|
+
* console.log('No config found, using defaults');
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function loadConfig(startDir?: string): Promise<ProjectConfig | undefined>;
|
|
63
|
+
//# sourceMappingURL=config-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAItF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAwBlG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqBhF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,UAAU,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAOrG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration file parser for vibe-agent-toolkit.config.yaml
|
|
3
|
+
*
|
|
4
|
+
* Discovers and parses project configuration files with directory tree walk-up.
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { load as loadYaml } from 'js-yaml';
|
|
9
|
+
import { ProjectConfigSchema } from './schemas/project-config.js';
|
|
10
|
+
const CONFIG_FILENAME = 'vibe-agent-toolkit.config.yaml';
|
|
11
|
+
/**
|
|
12
|
+
* Find the config file by walking up the directory tree.
|
|
13
|
+
*
|
|
14
|
+
* Starts from the current directory and walks up until the config file is found
|
|
15
|
+
* or the root directory is reached.
|
|
16
|
+
*
|
|
17
|
+
* @param startDir - Directory to start searching from (default: process.cwd())
|
|
18
|
+
* @returns Absolute path to config file, or undefined if not found
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const configPath = await findConfigFile();
|
|
23
|
+
* if (configPath) {
|
|
24
|
+
* console.log(`Found config: ${configPath}`);
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export async function findConfigFile(startDir = process.cwd()) {
|
|
29
|
+
let currentDir = path.resolve(startDir);
|
|
30
|
+
const { root } = path.parse(currentDir);
|
|
31
|
+
while (true) {
|
|
32
|
+
const configPath = path.join(currentDir, CONFIG_FILENAME);
|
|
33
|
+
try {
|
|
34
|
+
// Check if file exists by attempting to read metadata
|
|
35
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- constructing path during tree walk
|
|
36
|
+
await readFile(configPath, 'utf-8');
|
|
37
|
+
return configPath;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// File doesn't exist, continue walking up
|
|
41
|
+
}
|
|
42
|
+
// Check if we've reached the root
|
|
43
|
+
if (currentDir === root) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
// Move up one directory
|
|
47
|
+
currentDir = path.dirname(currentDir);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse a project configuration file.
|
|
52
|
+
*
|
|
53
|
+
* Reads the YAML file, parses it, and validates against the schema.
|
|
54
|
+
*
|
|
55
|
+
* @param configPath - Absolute path to config file
|
|
56
|
+
* @returns Parsed and validated configuration
|
|
57
|
+
* @throws Error if file cannot be read, YAML is invalid, or validation fails
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const config = await parseConfigFile('/project/vibe-agent-toolkit.config.yaml');
|
|
62
|
+
* console.log(`Version: ${config.version}`);
|
|
63
|
+
* console.log(`Collections: ${Object.keys(config.resources?.collections ?? {}).join(', ')}`);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export async function parseConfigFile(configPath) {
|
|
67
|
+
// Read file content
|
|
68
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- configPath is from findConfigFile() walk-up
|
|
69
|
+
const content = await readFile(configPath, 'utf-8');
|
|
70
|
+
// Parse YAML
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = loadYaml(content);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new Error(`Invalid YAML in config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
77
|
+
}
|
|
78
|
+
// Validate against schema
|
|
79
|
+
const result = ProjectConfigSchema.safeParse(parsed);
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
const errors = result.error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ');
|
|
82
|
+
throw new Error(`Invalid config file: ${errors}`);
|
|
83
|
+
}
|
|
84
|
+
return result.data;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Load project configuration by discovering and parsing config file.
|
|
88
|
+
*
|
|
89
|
+
* Walks up the directory tree from startDir to find the config file,
|
|
90
|
+
* then parses and validates it.
|
|
91
|
+
*
|
|
92
|
+
* @param startDir - Directory to start searching from (default: process.cwd())
|
|
93
|
+
* @returns Parsed configuration, or undefined if no config file found
|
|
94
|
+
* @throws Error if config file is found but cannot be parsed or is invalid
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const config = await loadConfig();
|
|
99
|
+
* if (config) {
|
|
100
|
+
* console.log('Using project config');
|
|
101
|
+
* } else {
|
|
102
|
+
* console.log('No config found, using defaults');
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export async function loadConfig(startDir = process.cwd()) {
|
|
107
|
+
const configPath = await findConfigFile(startDir);
|
|
108
|
+
if (!configPath) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return await parseConfigFile(configPath);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=config-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-parser.js","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,mBAAmB,EAAsB,MAAM,6BAA6B,CAAC;AAEtF,MAAM,eAAe,GAAG,gCAAgC,CAAC;AAEzD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IACnE,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAExC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,sDAAsD;YACtD,yGAAyG;YACzG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpC,OAAO,UAAU,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;QAED,kCAAkC;QAClC,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,wBAAwB;QACxB,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,UAAkB;IACtD,oBAAoB;IACpB,kHAAkH;IAClH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEpD,aAAa;IACb,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC5G,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9F,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAC/D,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC"}
|