@vibe-agent-toolkit/resources 0.1.37 → 0.1.39-rc.1

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.
Files changed (87) hide show
  1. package/README.md +35 -26
  2. package/dist/ajv-factory.d.ts +33 -0
  3. package/dist/ajv-factory.d.ts.map +1 -0
  4. package/dist/ajv-factory.js +51 -0
  5. package/dist/ajv-factory.js.map +1 -0
  6. package/dist/config-parser.d.ts +0 -18
  7. package/dist/config-parser.d.ts.map +1 -1
  8. package/dist/config-parser.js +5 -46
  9. package/dist/config-parser.js.map +1 -1
  10. package/dist/frontmatter-editor.d.ts +45 -0
  11. package/dist/frontmatter-editor.d.ts.map +1 -0
  12. package/dist/frontmatter-editor.js +161 -0
  13. package/dist/frontmatter-editor.js.map +1 -0
  14. package/dist/frontmatter-link-validator.d.ts +5 -5
  15. package/dist/frontmatter-link-validator.d.ts.map +1 -1
  16. package/dist/frontmatter-link-validator.js +25 -24
  17. package/dist/frontmatter-link-validator.js.map +1 -1
  18. package/dist/frontmatter-validator.d.ts +3 -2
  19. package/dist/frontmatter-validator.d.ts.map +1 -1
  20. package/dist/frontmatter-validator.js +19 -20
  21. package/dist/frontmatter-validator.js.map +1 -1
  22. package/dist/index.d.ts +5 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +9 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/json-pointer-path.d.ts +13 -0
  27. package/dist/json-pointer-path.d.ts.map +1 -0
  28. package/dist/json-pointer-path.js +30 -0
  29. package/dist/json-pointer-path.js.map +1 -0
  30. package/dist/link-parser.js +14 -16
  31. package/dist/link-parser.js.map +1 -1
  32. package/dist/link-validator.d.ts +23 -1
  33. package/dist/link-validator.d.ts.map +1 -1
  34. package/dist/link-validator.js +107 -104
  35. package/dist/link-validator.js.map +1 -1
  36. package/dist/multi-schema-validator.d.ts.map +1 -1
  37. package/dist/multi-schema-validator.js +6 -8
  38. package/dist/multi-schema-validator.js.map +1 -1
  39. package/dist/resource-registry.d.ts +10 -2
  40. package/dist/resource-registry.d.ts.map +1 -1
  41. package/dist/resource-registry.js +25 -32
  42. package/dist/resource-registry.js.map +1 -1
  43. package/dist/rewriter-helpers.d.ts +49 -0
  44. package/dist/rewriter-helpers.d.ts.map +1 -0
  45. package/dist/rewriter-helpers.js +142 -0
  46. package/dist/rewriter-helpers.js.map +1 -0
  47. package/dist/schemas/project-config.d.ts +219 -171
  48. package/dist/schemas/project-config.d.ts.map +1 -1
  49. package/dist/schemas/project-config.js +2 -0
  50. package/dist/schemas/project-config.js.map +1 -1
  51. package/dist/schemas/validation-result.d.ts +36 -57
  52. package/dist/schemas/validation-result.d.ts.map +1 -1
  53. package/dist/schemas/validation-result.js +5 -27
  54. package/dist/schemas/validation-result.js.map +1 -1
  55. package/dist/types/resource-parser.d.ts.map +1 -1
  56. package/dist/types/resource-parser.js +2 -3
  57. package/dist/types/resource-parser.js.map +1 -1
  58. package/dist/types/resources.d.ts +1 -1
  59. package/dist/types/resources.d.ts.map +1 -1
  60. package/dist/types/resources.js.map +1 -1
  61. package/dist/types.d.ts +1 -1
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils.d.ts +50 -11
  66. package/dist/utils.d.ts.map +1 -1
  67. package/dist/utils.js +53 -13
  68. package/dist/utils.js.map +1 -1
  69. package/package.json +5 -5
  70. package/src/ajv-factory.ts +56 -0
  71. package/src/config-parser.ts +5 -51
  72. package/src/frontmatter-editor.ts +214 -0
  73. package/src/frontmatter-link-validator.ts +23 -25
  74. package/src/frontmatter-validator.ts +29 -22
  75. package/src/index.ts +21 -2
  76. package/src/json-pointer-path.ts +29 -0
  77. package/src/link-parser.ts +27 -20
  78. package/src/link-validator.ts +194 -119
  79. package/src/multi-schema-validator.ts +10 -8
  80. package/src/resource-registry.ts +48 -33
  81. package/src/rewriter-helpers.ts +166 -0
  82. package/src/schemas/project-config.ts +2 -0
  83. package/src/schemas/validation-result.ts +5 -29
  84. package/src/types/resource-parser.ts +2 -3
  85. package/src/types/resources.ts +2 -1
  86. package/src/types.ts +0 -1
  87. package/src/utils.ts +72 -14
package/README.md CHANGED
@@ -47,7 +47,7 @@ console.log(`Errors: ${result.errorCount}, Warnings: ${result.warningCount}`);
47
47
  // Show any issues
48
48
  for (const issue of result.issues) {
49
49
  console.log(`${issue.severity.toUpperCase()}: ${issue.message}`);
50
- if (issue.line) console.log(` at ${issue.resourcePath}:${issue.line}`);
50
+ if (issue.line) console.log(` at ${issue.location}:${issue.line}`);
51
51
  }
52
52
  ```
53
53
 
@@ -546,27 +546,35 @@ interface ValidationResult {
546
546
 
547
547
  A single validation issue found during link validation.
548
548
 
549
+ `ValidationIssue` is the unified issue shape every VAT validator emits (defined
550
+ in `@vibe-agent-toolkit/agent-schema`). Each issue carries a registry `code`
551
+ (see [Validation Codes](../../docs/validation-codes.md)) and a resolved
552
+ `severity`.
553
+
549
554
  ```typescript
550
555
  interface ValidationIssue {
551
- severity: ValidationSeverity; // Issue severity level
552
- resourcePath: string; // Absolute path to the resource containing the issue
553
- line?: number; // Line number where the issue occurs
554
- type: string; // Issue type identifier (e.g., 'broken_file', 'broken_anchor')
555
- link: string; // The problematic link
556
+ code: string; // Registry code, e.g. 'LINK_BROKEN_FILE', 'EXTERNAL_URL_DEAD'
557
+ severity: SeverityLevel; // Resolved severity (after config overrides)
556
558
  message: string; // Human-readable description
559
+ location?: string; // Project-relative path of the file containing the issue
560
+ line?: number; // Line number where the issue occurs
561
+ link?: string; // The problematic link (when applicable)
562
+ fix?: string; // Fix hint from the code registry
563
+ reference?: string; // Anchor into docs/validation-codes.md
557
564
  suggestion?: string; // Optional suggestion for fixing
558
565
  }
559
566
  ```
560
567
 
561
- ### ValidationSeverity
568
+ ### SeverityLevel
562
569
 
563
570
  ```typescript
564
- type ValidationSeverity = 'error' | 'warning' | 'info';
571
+ type SeverityLevel = 'error' | 'warning' | 'info' | 'ignore';
565
572
  ```
566
573
 
567
574
  - `error` - Critical issue that should block usage (e.g., broken file link)
568
- - `warning` - Non-critical issue that should be addressed (e.g., questionable link format)
569
- - `info` - Informational message (e.g., external URL not validated)
575
+ - `warning` - Non-critical issue that should be addressed (e.g., dead external URL)
576
+ - `info` - Informational message
577
+ - `ignore` - Suppressed; the issue is not emitted (set via `validation.severity` config)
570
578
 
571
579
  ## Frontmatter Support
572
580
 
@@ -721,19 +729,19 @@ vat resources validate docs/ --frontmatter-schema schema.json
721
729
 
722
730
  ### Validation Result
723
731
 
724
- Frontmatter validation results are included in `ValidationResult`:
732
+ Frontmatter findings are emitted into the same unified `result.issues` array as
733
+ link findings — each carries a `FRONTMATTER_*` registry code (e.g.
734
+ `FRONTMATTER_MISSING`, `FRONTMATTER_SCHEMA_ERROR`, `FRONTMATTER_INVALID_YAML`)
735
+ and a resolved `severity`. There is no separate `frontmatterValidation` field.
725
736
 
726
737
  ```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
- };
738
+ const result = await registry.validate();
739
+
740
+ const frontmatterIssues = result.issues.filter((i) =>
741
+ i.code.startsWith('FRONTMATTER_'),
742
+ );
743
+ for (const issue of frontmatterIssues) {
744
+ console.log(`[${issue.severity}] ${issue.code} at ${issue.location}: ${issue.message}`);
737
745
  }
738
746
  ```
739
747
 
@@ -889,9 +897,10 @@ const warnings = result.issues.filter(i => i.severity === 'warning');
889
897
  // Group by resource
890
898
  const issuesByResource = new Map<string, ValidationIssue[]>();
891
899
  for (const issue of result.issues) {
892
- const issues = issuesByResource.get(issue.resourcePath) ?? [];
900
+ const key = issue.location ?? '(unknown)';
901
+ const issues = issuesByResource.get(key) ?? [];
893
902
  issues.push(issue);
894
- issuesByResource.set(issue.resourcePath, issues);
903
+ issuesByResource.set(key, issues);
895
904
  }
896
905
 
897
906
  // Show summary
@@ -926,7 +935,7 @@ async function validateDocs() {
926
935
  console.error(`\nValidation failed with ${result.errorCount} errors\n`);
927
936
 
928
937
  for (const issue of result.issues.filter(i => i.severity === 'error')) {
929
- console.error(`${issue.resourcePath}:${issue.line ?? '?'}`);
938
+ console.error(`${issue.location ?? '(unknown)'}:${issue.line ?? '?'}`);
930
939
  console.error(` ${issue.message}`);
931
940
  if (issue.suggestion) {
932
941
  console.error(` Suggestion: ${issue.suggestion}`);
@@ -1082,7 +1091,7 @@ Valid by default.
1082
1091
 
1083
1092
  ## Related Packages
1084
1093
 
1085
- - [@vibe-agent-toolkit/utils](../utils) - Core shared utilities
1094
+ - [@vibe-agent-toolkit/utils](../utils/README.md) - Core shared utilities
1086
1095
 
1087
1096
  ## Future Enhancements
1088
1097
 
@@ -1098,7 +1107,7 @@ Planned features for future releases:
1098
1107
 
1099
1108
  ## Documentation
1100
1109
 
1101
- - [Project Documentation](../../docs)
1110
+ - [Project Documentation](../../docs/README.md)
1102
1111
  - [Architecture](../../docs/architecture/README.md)
1103
1112
 
1104
1113
  ## License
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Ajv factory for adopters consuming VAT-generated schemas.
3
+ *
4
+ * VAT's frontmatter walker treats `format: "uri-reference"` (plus `uri`,
5
+ * `iri`, `iri-reference`) as first-class URI families and validates the
6
+ * referenced files via {@link import('./utils.js').resolveLocalHref}, not via
7
+ * Ajv. But adopters consuming the same schemas with vanilla
8
+ * `new Ajv(...)` hit Ajv's default strict mode, which upgrades
9
+ * `unknown format "uri-reference" ignored` from a warning to a thrown error.
10
+ *
11
+ * This helper returns an Ajv instance with the standard JSON Schema format
12
+ * vocabulary registered (via `ajv-formats`) plus no-op shims for
13
+ * `iri` / `iri-reference` (which `ajv-formats` does not ship). All
14
+ * URI-family schemas compile cleanly under strict mode.
15
+ */
16
+ import { Ajv, type Options as AjvOptions } from 'ajv';
17
+ /**
18
+ * Construct an Ajv instance pre-registered with the URI-family formats VAT
19
+ * schemas use. Use this anywhere downstream code compiles a schema that may
20
+ * reference `format: "uri-reference"` (or `uri`, `iri`, `iri-reference`).
21
+ *
22
+ * @param options - Ajv options. Passed through unchanged — caller controls
23
+ * `allErrors`, `strict`, `allowUnionTypes`, `verbose`, etc.
24
+ *
25
+ * @example
26
+ * import { createAjvWithUriFormats } from '@vibe-agent-toolkit/resources';
27
+ *
28
+ * const ajv = createAjvWithUriFormats({ allErrors: true });
29
+ * const validate = ajv.compile(mySchemaWithUriReference);
30
+ * if (!validate(data)) console.error(validate.errors);
31
+ */
32
+ export declare function createAjvWithUriFormats(options?: AjvOptions): Ajv;
33
+ //# sourceMappingURL=ajv-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ajv-factory.d.ts","sourceRoot":"","sources":["../src/ajv-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,GAAG,EAAE,KAAK,OAAO,IAAI,UAAU,EAAE,MAAM,KAAK,CAAC;AActD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,UAAe,GAAG,GAAG,CAUrE"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Ajv factory for adopters consuming VAT-generated schemas.
3
+ *
4
+ * VAT's frontmatter walker treats `format: "uri-reference"` (plus `uri`,
5
+ * `iri`, `iri-reference`) as first-class URI families and validates the
6
+ * referenced files via {@link import('./utils.js').resolveLocalHref}, not via
7
+ * Ajv. But adopters consuming the same schemas with vanilla
8
+ * `new Ajv(...)` hit Ajv's default strict mode, which upgrades
9
+ * `unknown format "uri-reference" ignored` from a warning to a thrown error.
10
+ *
11
+ * This helper returns an Ajv instance with the standard JSON Schema format
12
+ * vocabulary registered (via `ajv-formats`) plus no-op shims for
13
+ * `iri` / `iri-reference` (which `ajv-formats` does not ship). All
14
+ * URI-family schemas compile cleanly under strict mode.
15
+ */
16
+ import { Ajv } from 'ajv';
17
+ // ajv-formats is a CJS module published with `module.exports = formatsPlugin`
18
+ // plus an `exports.default` alias. Under NodeNext module resolution the
19
+ // default import is typed as the namespace object (not callable), even
20
+ // though the runtime value IS the plugin function. The `.default ??
21
+ // namespace` pattern below resolves both at type level and runtime.
22
+ import * as ajvFormatsModule from 'ajv-formats';
23
+ const addFormats = ajvFormatsModule.default ??
24
+ ajvFormatsModule;
25
+ /**
26
+ * Construct an Ajv instance pre-registered with the URI-family formats VAT
27
+ * schemas use. Use this anywhere downstream code compiles a schema that may
28
+ * reference `format: "uri-reference"` (or `uri`, `iri`, `iri-reference`).
29
+ *
30
+ * @param options - Ajv options. Passed through unchanged — caller controls
31
+ * `allErrors`, `strict`, `allowUnionTypes`, `verbose`, etc.
32
+ *
33
+ * @example
34
+ * import { createAjvWithUriFormats } from '@vibe-agent-toolkit/resources';
35
+ *
36
+ * const ajv = createAjvWithUriFormats({ allErrors: true });
37
+ * const validate = ajv.compile(mySchemaWithUriReference);
38
+ * if (!validate(data)) console.error(validate.errors);
39
+ */
40
+ export function createAjvWithUriFormats(options = {}) {
41
+ const ajv = new Ajv(options);
42
+ addFormats(ajv);
43
+ // ajv-formats does not register `iri` / `iri-reference`. Adopters whose
44
+ // schemas declare those would still hit "unknown format" under strict
45
+ // mode. Register no-op validators — Ajv accepts the format token, and
46
+ // semantic validation happens through resolveLocalHref / equivalent.
47
+ ajv.addFormat('iri', true);
48
+ ajv.addFormat('iri-reference', true);
49
+ return ajv;
50
+ }
51
+ //# sourceMappingURL=ajv-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ajv-factory.js","sourceRoot":"","sources":["../src/ajv-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,GAAG,EAA8B,MAAM,KAAK,CAAC;AACtD,8EAA8E;AAC9E,wEAAwE;AACxE,uEAAuE;AACvE,oEAAoE;AACpE,oEAAoE;AACpE,OAAO,KAAK,gBAAgB,MAAM,aAAa,CAAC;AAIhD,MAAM,UAAU,GACb,gBAA0D,CAAC,OAAO;IAClE,gBAA4C,CAAC;AAEhD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAsB,EAAE;IAC9D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,UAAU,CAAC,GAAG,CAAC,CAAC;IAChB,wEAAwE;IACxE,sEAAsE;IACtE,sEAAsE;IACtE,qEAAqE;IACrE,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3B,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -4,24 +4,6 @@
4
4
  * Discovers and parses project configuration files with directory tree walk-up.
5
5
  */
6
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
7
  /**
26
8
  * Parse a project configuration file.
27
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config-parser.d.ts","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,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,CAsBhF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,UAAU,CAAC,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAOrG"}
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;AAEtF;;;;;;;;;;;;;;;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,CAQrG"}
@@ -4,50 +4,9 @@
4
4
  * Discovers and parses project configuration files with directory tree walk-up.
5
5
  */
6
6
  import { readFile } from 'node:fs/promises';
7
- import path from 'node:path';
8
- import { safePath } from '@vibe-agent-toolkit/utils';
9
- import { CORE_SCHEMA, load as loadYaml } from 'js-yaml';
7
+ import { findConfigFile } from '@vibe-agent-toolkit/utils';
8
+ import { parse as parseYaml } from 'yaml';
10
9
  import { ProjectConfigSchema } from './schemas/project-config.js';
11
- const CONFIG_FILENAME = 'vibe-agent-toolkit.config.yaml';
12
- /**
13
- * Find the config file by walking up the directory tree.
14
- *
15
- * Starts from the current directory and walks up until the config file is found
16
- * or the root directory is reached.
17
- *
18
- * @param startDir - Directory to start searching from (default: process.cwd())
19
- * @returns Absolute path to config file, or undefined if not found
20
- *
21
- * @example
22
- * ```typescript
23
- * const configPath = await findConfigFile();
24
- * if (configPath) {
25
- * console.log(`Found config: ${configPath}`);
26
- * }
27
- * ```
28
- */
29
- export async function findConfigFile(startDir = process.cwd()) {
30
- let currentDir = safePath.resolve(startDir);
31
- const { root } = path.parse(currentDir);
32
- while (true) {
33
- const configPath = safePath.join(currentDir, CONFIG_FILENAME);
34
- try {
35
- // Check if file exists by attempting to read metadata
36
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- constructing path during tree walk
37
- await readFile(configPath, 'utf-8');
38
- return configPath;
39
- }
40
- catch {
41
- // File doesn't exist, continue walking up
42
- }
43
- // Check if we've reached the root
44
- if (currentDir === root) {
45
- return undefined;
46
- }
47
- // Move up one directory
48
- currentDir = path.dirname(currentDir);
49
- }
50
- }
51
10
  /**
52
11
  * Parse a project configuration file.
53
12
  *
@@ -71,8 +30,7 @@ export async function parseConfigFile(configPath) {
71
30
  // Parse YAML
72
31
  let parsed;
73
32
  try {
74
- // CORE_SCHEMA: YAML 1.2 spec — see link-parser.ts for rationale.
75
- parsed = loadYaml(content, { schema: CORE_SCHEMA });
33
+ parsed = parseYaml(content);
76
34
  }
77
35
  catch (error) {
78
36
  throw new Error(`Invalid YAML in config file: ${error instanceof Error ? error.message : String(error)}`);
@@ -106,7 +64,8 @@ export async function parseConfigFile(configPath) {
106
64
  * ```
107
65
  */
108
66
  export async function loadConfig(startDir = process.cwd()) {
109
- const configPath = await findConfigFile(startDir);
67
+ // findConfigFile from utils is synchronous; awaiting a non-promise is a no-op.
68
+ const configPath = findConfigFile(startDir);
110
69
  if (!configPath) {
111
70
  return undefined;
112
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config-parser.js","sourceRoot":"","sources":["../src/config-parser.ts"],"names":[],"mappings":"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"}
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;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,EAAE,mBAAmB,EAAsB,MAAM,6BAA6B,CAAC;AAEtF;;;;;;;;;;;;;;;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,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9B,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,+EAA+E;IAC/E,MAAM,UAAU,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * FrontmatterEditor — round-trip-safe primitive for editing YAML frontmatter
3
+ * in markdown files.
4
+ *
5
+ * Public surface: openFrontmatter(markdown) → FrontmatterEditor.
6
+ *
7
+ * Round-trip identity contract: openFrontmatter(x).toString() === x for any
8
+ * well-formed input, byte-for-byte. Mutations preserve comments, blank lines,
9
+ * key ordering, quoting style, and detected EOL.
10
+ *
11
+ * See docs/superpowers/specs/2026-05-17-frontmatter-editor-and-yaml-consolidation-design.md
12
+ * §5 for the full contract.
13
+ */
14
+ export declare class FrontmatterParseError extends Error {
15
+ readonly cause: unknown;
16
+ constructor(message: string, cause: unknown);
17
+ }
18
+ /** Path to a value in the parsed frontmatter document. */
19
+ export type FrontmatterPath = string | readonly (string | number)[];
20
+ /** Scalar value type accepted by mutation methods. */
21
+ export type FrontmatterScalar = string | number | boolean | null;
22
+ export interface FrontmatterEditor {
23
+ body: string;
24
+ get(path: FrontmatterPath): unknown;
25
+ set(path: FrontmatterPath, value: FrontmatterScalar): void;
26
+ setArrayItem(path: FrontmatterPath, index: number, value: FrontmatterScalar): void;
27
+ appendArrayItem(path: FrontmatterPath, value: FrontmatterScalar): void;
28
+ delete(path: FrontmatterPath): void;
29
+ toString(): string;
30
+ /**
31
+ * Returns true if any mutating method has been called or `body` was
32
+ * reassigned to a different string. Use to gate writeFileSync and avoid
33
+ * the no-op-rewrite churn described in §"What's preserved, what isn't"
34
+ * of the markdown-rewriting skill.
35
+ *
36
+ * **Caveat:** any call to `set` / `setArrayItem` / `appendArrayItem` /
37
+ * `delete` flips the flag, even if the underlying value didn't change
38
+ * (e.g. `set('foo', sameValue)`). For strict byte-level dirty detection
39
+ * compare `editor.toString() !== originalText` instead. `body =` is the
40
+ * one exception — it only flips dirty on actual string change.
41
+ */
42
+ isDirty(): boolean;
43
+ }
44
+ export declare function openFrontmatter(markdown: string): FrontmatterEditor;
45
+ //# sourceMappingURL=frontmatter-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter-editor.d.ts","sourceRoot":"","sources":["../src/frontmatter-editor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAyB,KAAK,EAAE,OAAO,CAAC;gBAE5B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAK5C;AAED,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAEpE,sDAAsD;AACtD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAEjE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IACpC,GAAG,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC3D,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnF,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACvE,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC,QAAQ,IAAI,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,OAAO,CAAC;CACpB;AA8JD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAEnE"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * FrontmatterEditor — round-trip-safe primitive for editing YAML frontmatter
3
+ * in markdown files.
4
+ *
5
+ * Public surface: openFrontmatter(markdown) → FrontmatterEditor.
6
+ *
7
+ * Round-trip identity contract: openFrontmatter(x).toString() === x for any
8
+ * well-formed input, byte-for-byte. Mutations preserve comments, blank lines,
9
+ * key ordering, quoting style, and detected EOL.
10
+ *
11
+ * See docs/superpowers/specs/2026-05-17-frontmatter-editor-and-yaml-consolidation-design.md
12
+ * §5 for the full contract.
13
+ */
14
+ import { Document, parseDocument } from 'yaml';
15
+ export class FrontmatterParseError extends Error {
16
+ cause;
17
+ constructor(message, cause) {
18
+ super(message);
19
+ this.name = 'FrontmatterParseError';
20
+ this.cause = cause;
21
+ }
22
+ }
23
+ const OPENING_FENCE = /^---\r?\n/;
24
+ // Closing fence: either immediately after opening (empty frontmatter), or
25
+ // preceded by a newline. Trailing variants accept newline or EOF.
26
+ const EMPTY_CLOSING_FENCE = /^---(?:\r?\n|$)/;
27
+ const CLOSING_FENCE = /(?:\r?\n---\r?\n|\r?\n---$)/;
28
+ function detectEol(input) {
29
+ const firstBreak = input.indexOf('\n');
30
+ if (firstBreak === -1)
31
+ return '\n';
32
+ return firstBreak > 0 && input.charAt(firstBreak - 1) === '\r' ? '\r\n' : '\n';
33
+ }
34
+ function splitFrontmatter(input) {
35
+ const eol = detectEol(input);
36
+ const openingMatch = OPENING_FENCE.exec(input);
37
+ if (!openingMatch) {
38
+ return { hasFrontmatter: false, frontmatterText: '', body: input, eol };
39
+ }
40
+ const afterOpening = input.slice(openingMatch[0].length);
41
+ // Handle empty frontmatter (closing fence immediately follows opening fence)
42
+ const emptyMatch = EMPTY_CLOSING_FENCE.exec(afterOpening);
43
+ if (emptyMatch) {
44
+ const bodyStart = emptyMatch[0].length;
45
+ return {
46
+ hasFrontmatter: true,
47
+ frontmatterText: '',
48
+ body: afterOpening.slice(bodyStart),
49
+ eol,
50
+ };
51
+ }
52
+ const closingMatch = CLOSING_FENCE.exec(afterOpening);
53
+ if (!closingMatch) {
54
+ return { hasFrontmatter: false, frontmatterText: '', body: input, eol };
55
+ }
56
+ const frontmatterText = afterOpening.slice(0, closingMatch.index);
57
+ const bodyStart = closingMatch.index + closingMatch[0].length;
58
+ const body = afterOpening.slice(bodyStart);
59
+ return { hasFrontmatter: true, frontmatterText, body, eol };
60
+ }
61
+ class FrontmatterEditorImpl {
62
+ doc;
63
+ hasFrontmatter;
64
+ eol;
65
+ _body;
66
+ _dirty = false;
67
+ get body() {
68
+ return this._body;
69
+ }
70
+ set body(value) {
71
+ if (value !== this._body) {
72
+ this._body = value;
73
+ this._dirty = true;
74
+ }
75
+ }
76
+ isDirty() {
77
+ return this._dirty;
78
+ }
79
+ constructor(input) {
80
+ const split = splitFrontmatter(input);
81
+ this.hasFrontmatter = split.hasFrontmatter;
82
+ this.eol = split.eol;
83
+ this._body = split.body;
84
+ if (!split.hasFrontmatter) {
85
+ this.doc = new Document({});
86
+ return;
87
+ }
88
+ try {
89
+ this.doc = parseDocument(split.frontmatterText, { prettyErrors: true });
90
+ if (this.doc.errors.length > 0) {
91
+ throw new FrontmatterParseError(`Invalid YAML frontmatter: ${this.doc.errors[0]?.message ?? 'unknown error'}`, this.doc.errors[0]);
92
+ }
93
+ }
94
+ catch (error) {
95
+ if (error instanceof FrontmatterParseError)
96
+ throw error;
97
+ throw new FrontmatterParseError(`Failed to parse frontmatter: ${error instanceof Error ? error.message : String(error)}`, error);
98
+ }
99
+ }
100
+ toPath(path) {
101
+ if (Array.isArray(path))
102
+ return path;
103
+ if (typeof path === 'string')
104
+ return [path];
105
+ return path;
106
+ }
107
+ get(path) {
108
+ const segments = this.toPath(path);
109
+ if (segments.length === 0)
110
+ return this.doc.toJS();
111
+ return this.doc.getIn(segments, false);
112
+ }
113
+ set(path, value) {
114
+ const segments = this.toPath(path);
115
+ this.doc.setIn(segments, value);
116
+ this._dirty = true;
117
+ }
118
+ setArrayItem(path, index, value) {
119
+ const segments = [...this.toPath(path), index];
120
+ this.doc.setIn(segments, value);
121
+ this._dirty = true;
122
+ }
123
+ appendArrayItem(path, value) {
124
+ const segments = this.toPath(path);
125
+ this.doc.addIn(segments, value);
126
+ this._dirty = true;
127
+ }
128
+ delete(path) {
129
+ const segments = this.toPath(path);
130
+ this.doc.deleteIn(segments);
131
+ this._dirty = true;
132
+ }
133
+ toString() {
134
+ // No frontmatter originally, and nothing was added → return body unchanged.
135
+ if (!this.hasFrontmatter && this.isDocEffectivelyEmpty()) {
136
+ return this.body;
137
+ }
138
+ // Empty frontmatter (e.g. `---\n---\n`) where the doc remained empty —
139
+ // preserve the empty fence block without injecting `null` or `{}` between.
140
+ if (this.hasFrontmatter && this.isDocEffectivelyEmpty()) {
141
+ return `---${this.eol}---${this.eol}${this.body}`;
142
+ }
143
+ const fmText = this.doc.toString();
144
+ const normalized = this.eol === '\r\n' ? fmText.replaceAll('\n', '\r\n') : fmText;
145
+ return `---${this.eol}${normalized}---${this.eol}${this.body}`;
146
+ }
147
+ isDocEffectivelyEmpty() {
148
+ const contents = this.doc.contents;
149
+ if (contents === null)
150
+ return true;
151
+ // yaml.YAMLMap and YAMLSeq expose `items`; an empty map/seq counts as empty.
152
+ const maybeItems = contents.items;
153
+ if (Array.isArray(maybeItems) && maybeItems.length === 0)
154
+ return true;
155
+ return false;
156
+ }
157
+ }
158
+ export function openFrontmatter(markdown) {
159
+ return new FrontmatterEditorImpl(markdown);
160
+ }
161
+ //# sourceMappingURL=frontmatter-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter-editor.js","sourceRoot":"","sources":["../src/frontmatter-editor.ts"],"names":[],"mappings":"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"}
@@ -7,11 +7,11 @@
7
7
  * frontmatter-specific type codes plus a list of external URLs the registry
8
8
  * can fold into its existing external URL collection.
9
9
  *
10
- * Type mapping:
11
- * broken_file -> frontmatter_link_broken
12
- * broken_anchor -> frontmatter_anchor_missing
13
- * link_to_gitignored -> frontmatter_link_to_gitignored
14
- * unknown_link -> frontmatter_unknown_link
10
+ * Code mapping:
11
+ * LINK_BROKEN_FILE -> FRONTMATTER_LINK_BROKEN
12
+ * LINK_BROKEN_ANCHOR -> FRONTMATTER_ANCHOR_MISSING
13
+ * LINK_TO_GITIGNORED -> FRONTMATTER_LINK_TO_GITIGNORED
14
+ * LINK_UNKNOWN -> FRONTMATTER_UNKNOWN_LINK
15
15
  *
16
16
  * Skipped (no issue, no external):
17
17
  * email (mailto:)
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter-link-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,WAAW,EAAgB,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7E,kFAAkF;AAClF,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,EAAE,sBAAsB,EAAE,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAC1C,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,+BAA+B,CAAC,CAoC1C"}
1
+ {"version":3,"file":"frontmatter-link-validator.d.ts","sourceRoot":"","sources":["../src/frontmatter-link-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,WAAW,EAAgB,eAAe,EAAE,MAAM,YAAY,CAAC;AAU7E,kFAAkF;AAClF,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,EAAE,sBAAsB,EAAE,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAChD,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAC1C,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,+BAA+B,CAAC,CAoC1C"}
@@ -7,19 +7,27 @@
7
7
  * frontmatter-specific type codes plus a list of external URLs the registry
8
8
  * can fold into its existing external URL collection.
9
9
  *
10
- * Type mapping:
11
- * broken_file -> frontmatter_link_broken
12
- * broken_anchor -> frontmatter_anchor_missing
13
- * link_to_gitignored -> frontmatter_link_to_gitignored
14
- * unknown_link -> frontmatter_unknown_link
10
+ * Code mapping:
11
+ * LINK_BROKEN_FILE -> FRONTMATTER_LINK_BROKEN
12
+ * LINK_BROKEN_ANCHOR -> FRONTMATTER_ANCHOR_MISSING
13
+ * LINK_TO_GITIGNORED -> FRONTMATTER_LINK_TO_GITIGNORED
14
+ * LINK_UNKNOWN -> FRONTMATTER_UNKNOWN_LINK
15
15
  *
16
16
  * Skipped (no issue, no external):
17
17
  * email (mailto:)
18
18
  * anchor-only (validated as anchor in current file via validateLink)
19
19
  */
20
+ import { createRegistryIssue } from '@vibe-agent-toolkit/agent-schema';
20
21
  import { classifyLink } from './link-parser.js';
21
22
  import { validateLink } from './link-validator.js';
22
23
  import { walkFrontmatterUriReferences } from './schema-uri-walker.js';
24
+ /** Map the link-level code emitted by validateLink to its frontmatter-scoped code. */
25
+ const LINK_CODE_TO_FRONTMATTER_CODE = {
26
+ LINK_BROKEN_FILE: 'FRONTMATTER_LINK_BROKEN',
27
+ LINK_BROKEN_ANCHOR: 'FRONTMATTER_ANCHOR_MISSING',
28
+ LINK_TO_GITIGNORED: 'FRONTMATTER_LINK_TO_GITIGNORED',
29
+ LINK_UNKNOWN: 'FRONTMATTER_UNKNOWN_LINK',
30
+ };
23
31
  /**
24
32
  * Validate every URI-family frontmatter value against the file system.
25
33
  *
@@ -63,24 +71,17 @@ export async function validateFrontmatterLinks(frontmatter, schema, sourceFilePa
63
71
  return { issues, externalUrls };
64
72
  }
65
73
  function rewriteIssue(issue, dottedPath) {
66
- return {
67
- ...issue,
68
- type: mapType(issue.type),
69
- message: `field \`${dottedPath}\`: ${issue.message}`,
70
- };
71
- }
72
- function mapType(originalType) {
73
- switch (originalType) {
74
- case 'broken_file':
75
- return 'frontmatter_link_broken';
76
- case 'broken_anchor':
77
- return 'frontmatter_anchor_missing';
78
- case 'link_to_gitignored':
79
- return 'frontmatter_link_to_gitignored';
80
- case 'unknown_link':
81
- return 'frontmatter_unknown_link';
82
- default:
83
- return originalType;
84
- }
74
+ const mappedCode = LINK_CODE_TO_FRONTMATTER_CODE[issue.code] ?? issue.code;
75
+ const message = `field \`${dottedPath}\`: ${issue.message}`;
76
+ const extras = {};
77
+ if (issue.location !== undefined)
78
+ extras.location = issue.location;
79
+ if (issue.line !== undefined)
80
+ extras.line = issue.line;
81
+ if (issue.link !== undefined)
82
+ extras.link = issue.link;
83
+ if (issue.suggestion !== undefined)
84
+ extras.suggestion = issue.suggestion;
85
+ return createRegistryIssue(mappedCode, message, extras);
85
86
  }
86
87
  //# sourceMappingURL=frontmatter-link-validator.js.map