@agentxm/registry-protocol 0.28.4-bootstrap.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.
Files changed (137) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +13 -0
  3. package/dist/src/unstable/content/agent-skills-standard.d.ts +17 -0
  4. package/dist/src/unstable/content/agent-skills-standard.js +24 -0
  5. package/dist/src/unstable/content/frontmatter.d.ts +52 -0
  6. package/dist/src/unstable/content/frontmatter.js +115 -0
  7. package/dist/src/unstable/content/index.d.ts +11 -0
  8. package/dist/src/unstable/content/index.js +11 -0
  9. package/dist/src/unstable/content/skill-content.d.ts +43 -0
  10. package/dist/src/unstable/content/skill-content.js +184 -0
  11. package/dist/src/unstable/content/skill-types.d.ts +22 -0
  12. package/dist/src/unstable/content/skill-types.js +9 -0
  13. package/dist/src/unstable/content/subagent-content.d.ts +58 -0
  14. package/dist/src/unstable/content/subagent-content.js +101 -0
  15. package/dist/src/unstable/knowledge/index.d.ts +8 -0
  16. package/dist/src/unstable/knowledge/index.js +8 -0
  17. package/dist/src/unstable/knowledge/knowledge-search.d.ts +32 -0
  18. package/dist/src/unstable/knowledge/knowledge-search.js +147 -0
  19. package/dist/src/unstable/knowledge/okf.d.ts +86 -0
  20. package/dist/src/unstable/knowledge/okf.js +1090 -0
  21. package/dist/src/unstable/lint/catalog/hook/decision-portability.d.ts +4 -0
  22. package/dist/src/unstable/lint/catalog/hook/decision-portability.js +40 -0
  23. package/dist/src/unstable/lint/catalog/hook/entrypoint-exists.d.ts +4 -0
  24. package/dist/src/unstable/lint/catalog/hook/entrypoint-exists.js +39 -0
  25. package/dist/src/unstable/lint/catalog/hook/envelope.d.ts +6 -0
  26. package/dist/src/unstable/lint/catalog/hook/envelope.js +15 -0
  27. package/dist/src/unstable/lint/catalog/hook/matcher-raw-portability.d.ts +4 -0
  28. package/dist/src/unstable/lint/catalog/hook/matcher-raw-portability.js +51 -0
  29. package/dist/src/unstable/lint/catalog/hook.d.ts +4 -0
  30. package/dist/src/unstable/lint/catalog/hook.js +17 -0
  31. package/dist/src/unstable/lint/catalog/knowledge/diagnostics.d.ts +128 -0
  32. package/dist/src/unstable/lint/catalog/knowledge/diagnostics.js +78 -0
  33. package/dist/src/unstable/lint/catalog/knowledge/envelope.d.ts +6 -0
  34. package/dist/src/unstable/lint/catalog/knowledge/envelope.js +15 -0
  35. package/dist/src/unstable/lint/catalog/knowledge.d.ts +5 -0
  36. package/dist/src/unstable/lint/catalog/knowledge.js +11 -0
  37. package/dist/src/unstable/lint/catalog/mcp-server/envelope.d.ts +6 -0
  38. package/dist/src/unstable/lint/catalog/mcp-server/envelope.js +15 -0
  39. package/dist/src/unstable/lint/catalog/mcp-server.d.ts +4 -0
  40. package/dist/src/unstable/lint/catalog/mcp-server.js +6 -0
  41. package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.d.ts +28 -0
  42. package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.js +38 -0
  43. package/dist/src/unstable/lint/catalog/pack/manifest-present.d.ts +17 -0
  44. package/dist/src/unstable/lint/catalog/pack/manifest-present.js +24 -0
  45. package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.d.ts +25 -0
  46. package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.js +33 -0
  47. package/dist/src/unstable/lint/catalog/pack-accessor/contexts.d.ts +51 -0
  48. package/dist/src/unstable/lint/catalog/pack-accessor/contexts.js +45 -0
  49. package/dist/src/unstable/lint/catalog/pack-accessor/vft.d.ts +60 -0
  50. package/dist/src/unstable/lint/catalog/pack-accessor/vft.js +85 -0
  51. package/dist/src/unstable/lint/catalog/pack.d.ts +37 -0
  52. package/dist/src/unstable/lint/catalog/pack.js +47 -0
  53. package/dist/src/unstable/lint/catalog/rule/envelope.d.ts +6 -0
  54. package/dist/src/unstable/lint/catalog/rule/envelope.js +15 -0
  55. package/dist/src/unstable/lint/catalog/rule.d.ts +4 -0
  56. package/dist/src/unstable/lint/catalog/rule.js +6 -0
  57. package/dist/src/unstable/lint/catalog/shared/envelope-rules.d.ts +93 -0
  58. package/dist/src/unstable/lint/catalog/shared/envelope-rules.js +89 -0
  59. package/dist/src/unstable/lint/catalog/shared/manifest-json.d.ts +18 -0
  60. package/dist/src/unstable/lint/catalog/shared/manifest-json.js +28 -0
  61. package/dist/src/unstable/lint/catalog/shared/manifest-present.d.ts +25 -0
  62. package/dist/src/unstable/lint/catalog/shared/manifest-present.js +37 -0
  63. package/dist/src/unstable/lint/catalog/shared/recommended-packs-rules.d.ts +70 -0
  64. package/dist/src/unstable/lint/catalog/shared/recommended-packs-rules.js +139 -0
  65. package/dist/src/unstable/lint/catalog/shared/schema-rule.d.ts +58 -0
  66. package/dist/src/unstable/lint/catalog/shared/schema-rule.js +103 -0
  67. package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.d.ts +31 -0
  68. package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.js +148 -0
  69. package/dist/src/unstable/lint/catalog/skill/frontmatter-standard-valid.d.ts +13 -0
  70. package/dist/src/unstable/lint/catalog/skill/frontmatter-standard-valid.js +61 -0
  71. package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.d.ts +26 -0
  72. package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.js +41 -0
  73. package/dist/src/unstable/lint/catalog/skill/manifest-present.d.ts +23 -0
  74. package/dist/src/unstable/lint/catalog/skill/manifest-present.js +32 -0
  75. package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.d.ts +21 -0
  76. package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.js +36 -0
  77. package/dist/src/unstable/lint/catalog/skill/recommended-packs-valid.d.ts +8 -0
  78. package/dist/src/unstable/lint/catalog/skill/recommended-packs-valid.js +12 -0
  79. package/dist/src/unstable/lint/catalog/skill/skill-md-present.d.ts +17 -0
  80. package/dist/src/unstable/lint/catalog/skill/skill-md-present.js +37 -0
  81. package/dist/src/unstable/lint/catalog/skill/standalone-declaration-valid.d.ts +8 -0
  82. package/dist/src/unstable/lint/catalog/skill/standalone-declaration-valid.js +12 -0
  83. package/dist/src/unstable/lint/catalog/skill-accessor/contexts.d.ts +61 -0
  84. package/dist/src/unstable/lint/catalog/skill-accessor/contexts.js +55 -0
  85. package/dist/src/unstable/lint/catalog/skill-accessor/vft.d.ts +77 -0
  86. package/dist/src/unstable/lint/catalog/skill-accessor/vft.js +135 -0
  87. package/dist/src/unstable/lint/catalog/skill.d.ts +44 -0
  88. package/dist/src/unstable/lint/catalog/skill.js +64 -0
  89. package/dist/src/unstable/lint/catalog/subagent/envelope.d.ts +6 -0
  90. package/dist/src/unstable/lint/catalog/subagent/envelope.js +15 -0
  91. package/dist/src/unstable/lint/catalog/subagent.d.ts +4 -0
  92. package/dist/src/unstable/lint/catalog/subagent.js +6 -0
  93. package/dist/src/unstable/lint/compose-path.d.ts +28 -0
  94. package/dist/src/unstable/lint/compose-path.js +64 -0
  95. package/dist/src/unstable/lint/config.d.ts +103 -0
  96. package/dist/src/unstable/lint/config.js +140 -0
  97. package/dist/src/unstable/lint/context.d.ts +246 -0
  98. package/dist/src/unstable/lint/context.js +21 -0
  99. package/dist/src/unstable/lint/describe-document.d.ts +31 -0
  100. package/dist/src/unstable/lint/describe-document.js +50 -0
  101. package/dist/src/unstable/lint/evaluate.d.ts +47 -0
  102. package/dist/src/unstable/lint/evaluate.js +82 -0
  103. package/dist/src/unstable/lint/issues-to-findings.d.ts +35 -0
  104. package/dist/src/unstable/lint/issues-to-findings.js +149 -0
  105. package/dist/src/unstable/lint/publish.d.ts +49 -0
  106. package/dist/src/unstable/lint/publish.js +86 -0
  107. package/dist/src/unstable/lint/rule.d.ts +98 -0
  108. package/dist/src/unstable/lint/rule.js +18 -0
  109. package/dist/src/unstable/publish/archive-guardrails.d.ts +39 -0
  110. package/dist/src/unstable/publish/archive-guardrails.js +250 -0
  111. package/dist/src/unstable/publish/filtered-package-validation.d.ts +24 -0
  112. package/dist/src/unstable/publish/filtered-package-validation.js +117 -0
  113. package/dist/src/unstable/publish/index.d.ts +12 -0
  114. package/dist/src/unstable/publish/index.js +12 -0
  115. package/dist/src/unstable/publish/ingest-limits.d.ts +25 -0
  116. package/dist/src/unstable/publish/ingest-limits.js +41 -0
  117. package/dist/src/unstable/publish/input-normalization.d.ts +40 -0
  118. package/dist/src/unstable/publish/input-normalization.js +125 -0
  119. package/dist/src/unstable/publish/manifest-policy.d.ts +611 -0
  120. package/dist/src/unstable/publish/manifest-policy.js +196 -0
  121. package/dist/src/unstable/publish/test-zip-helpers.d.ts +13 -0
  122. package/dist/src/unstable/publish/test-zip-helpers.js +117 -0
  123. package/dist/src/unstable/publish/visibility.d.ts +161 -0
  124. package/dist/src/unstable/publish/visibility.js +124 -0
  125. package/dist/src/unstable/registry/discover-schema.d.ts +68 -0
  126. package/dist/src/unstable/registry/discover-schema.js +35 -0
  127. package/dist/src/unstable/registry/index.d.ts +9 -0
  128. package/dist/src/unstable/registry/index.js +9 -0
  129. package/dist/src/unstable/registry/publication-set.d.ts +303 -0
  130. package/dist/src/unstable/registry/publication-set.js +507 -0
  131. package/dist/src/unstable/registry/release-age-policy.d.ts +57 -0
  132. package/dist/src/unstable/registry/release-age-policy.js +117 -0
  133. package/dist/src/unstable/registry/schema.d.ts +220 -0
  134. package/dist/src/unstable/registry/schema.js +114 -0
  135. package/dist/src/unstable/suggested-action.d.ts +19 -0
  136. package/dist/src/unstable/suggested-action.js +42 -0
  137. package/package.json +214 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared factory for `manifest-present` advisory rules.
3
+ *
4
+ * @experimental This API is unstable and may change without notice.
5
+ */
6
+ import * as Effect from "effect/Effect";
7
+ export const makeManifestPresentRule = (args) => {
8
+ const severity = args.severity ?? "error";
9
+ const getFiles = args.getFiles ?? ((context) => context.files);
10
+ const applies = args.applies ?? (() => true);
11
+ return {
12
+ id: args.ruleId,
13
+ description: args.description,
14
+ kind: "advisory",
15
+ severity,
16
+ check: (context) => {
17
+ if (!applies(context)) {
18
+ return Effect.succeed([]);
19
+ }
20
+ return Effect.map(getFiles(context).exists(args.manifestFile), (present) => {
21
+ if (present) {
22
+ return [];
23
+ }
24
+ return [
25
+ {
26
+ kind: "advisory",
27
+ ruleId: args.ruleId,
28
+ severity,
29
+ message: args.missingMessage,
30
+ location: { file: args.manifestFile },
31
+ },
32
+ ];
33
+ });
34
+ },
35
+ };
36
+ };
37
+ //# sourceMappingURL=manifest-present.js.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Shared factories for the `standalone` / `recommendedPacks` coherence rules.
3
+ *
4
+ * Both fields come from `NonPackManifestFields` in
5
+ * `extensions/common.ts`, so every non-pack catalog (`skill`, `subagent`,
6
+ * `mcp-server`, `rule`, `hook`, `knowledge`) enforces the same two invariants
7
+ * against its own manifest. Rather than copy twelve near-identical rule
8
+ * bodies, each catalog registers a rule produced here and parameterized by
9
+ * namespace and manifest accessor — the same shape `shared/schema-rule.ts`
10
+ * establishes for the `-schema-valid` rules.
11
+ *
12
+ * Both rules ship at `warning`. They would otherwise retroactively reject
13
+ * manifests published before the rules existed, which the rule-authoring
14
+ * guide's "new rules start soft" clause forbids.
15
+ *
16
+ * @experimental This API is unstable and may change without notice.
17
+ * @packageDocumentation
18
+ */
19
+ import type { AdvisoryRule } from "../../rule.js";
20
+ /**
21
+ * Per-catalog parameters for the shared non-pack manifest rules.
22
+ *
23
+ * @experimental This API is unstable and may change without notice.
24
+ */
25
+ export interface RecommendedPacksRuleOptions<C> {
26
+ /** Rule-id namespace; matches the context kind (`skill`, `hook`, ...). */
27
+ readonly namespace: string;
28
+ /** Accessor-relative manifest filename stamped on `location.file`. */
29
+ readonly manifestFile: string;
30
+ /** Reads the caller-decoded manifest JSON off the rule context. */
31
+ readonly manifestJson: (context: C) => unknown;
32
+ }
33
+ /**
34
+ * Split a `recommendedPacks` entry into its FQN and optional version range.
35
+ *
36
+ * Mirrors the separator search in `PackSpecSchema`: the range delimiter is the
37
+ * first `@` after the last `/`, since the owner segment also starts with `@`.
38
+ *
39
+ * @experimental This API is unstable and may change without notice.
40
+ */
41
+ export declare const splitPackSpec: (spec: string) => {
42
+ readonly fqn: string;
43
+ readonly range: string | undefined;
44
+ };
45
+ /**
46
+ * Build the `<namespace>/standalone-declaration-valid` rule for one catalog.
47
+ *
48
+ * A manifest with `standalone: false` declares that it is meaningless without
49
+ * one of its recommended packs. With `recommendedPacks` absent or empty that
50
+ * declaration is self-contradictory and nothing downstream — registry, Library
51
+ * UI, or `workspace/recommended-packs-retained` — can act on it.
52
+ *
53
+ * @experimental This API is unstable and may change without notice.
54
+ */
55
+ export declare const makeStandaloneDeclarationValidRule: <C>(options: RecommendedPacksRuleOptions<C>) => AdvisoryRule<C>;
56
+ /**
57
+ * Build the `<namespace>/recommended-packs-valid` rule for one catalog.
58
+ *
59
+ * `recommendedPacks` is typed `PackSpecSchema`, which permits a trailing
60
+ * version range, but nothing consumes one — recommendations are matched by
61
+ * name — and `axm help packs` tells authors to use the bare reference. One
62
+ * finding per offending entry.
63
+ *
64
+ * When published manifests have drained, the field can move to
65
+ * `PackFqnSchema` and this rule retires: decoding takes over.
66
+ *
67
+ * @experimental This API is unstable and may change without notice.
68
+ */
69
+ export declare const makeRecommendedPacksValidRule: <C>(options: RecommendedPacksRuleOptions<C>) => AdvisoryRule<C>;
70
+ //# sourceMappingURL=recommended-packs-rules.d.ts.map
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Shared factories for the `standalone` / `recommendedPacks` coherence rules.
3
+ *
4
+ * Both fields come from `NonPackManifestFields` in
5
+ * `extensions/common.ts`, so every non-pack catalog (`skill`, `subagent`,
6
+ * `mcp-server`, `rule`, `hook`, `knowledge`) enforces the same two invariants
7
+ * against its own manifest. Rather than copy twelve near-identical rule
8
+ * bodies, each catalog registers a rule produced here and parameterized by
9
+ * namespace and manifest accessor — the same shape `shared/schema-rule.ts`
10
+ * establishes for the `-schema-valid` rules.
11
+ *
12
+ * Both rules ship at `warning`. They would otherwise retroactively reject
13
+ * manifests published before the rules existed, which the rule-authoring
14
+ * guide's "new rules start soft" clause forbids.
15
+ *
16
+ * @experimental This API is unstable and may change without notice.
17
+ * @packageDocumentation
18
+ */
19
+ import * as Effect from "effect/Effect";
20
+ import { isManifestJsonParseFailure } from "./manifest-json.js";
21
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22
+ /**
23
+ * Narrow a caller-decoded manifest value to a plain record, or `undefined`
24
+ * when there is nothing for these rules to read. Absent manifests are the
25
+ * `-present` rule's problem; unparseable ones are the `-schema-valid` rule's.
26
+ */
27
+ const manifestRecord = (input) => {
28
+ if (input === undefined || isManifestJsonParseFailure(input) || !isRecord(input)) {
29
+ return undefined;
30
+ }
31
+ return input;
32
+ };
33
+ const stringEntries = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
34
+ /**
35
+ * Split a `recommendedPacks` entry into its FQN and optional version range.
36
+ *
37
+ * Mirrors the separator search in `PackSpecSchema`: the range delimiter is the
38
+ * first `@` after the last `/`, since the owner segment also starts with `@`.
39
+ *
40
+ * @experimental This API is unstable and may change without notice.
41
+ */
42
+ export const splitPackSpec = (spec) => {
43
+ const lastSlash = spec.lastIndexOf("/");
44
+ const rangeAt = lastSlash > 0 ? spec.indexOf("@", lastSlash + 1) : -1;
45
+ if (rangeAt <= 0) {
46
+ return { fqn: spec, range: undefined };
47
+ }
48
+ return { fqn: spec.slice(0, rangeAt), range: spec.slice(rangeAt + 1) };
49
+ };
50
+ // -----------------------------------------------------------------------------
51
+ // <namespace>/standalone-declaration-valid
52
+ // -----------------------------------------------------------------------------
53
+ /**
54
+ * Build the `<namespace>/standalone-declaration-valid` rule for one catalog.
55
+ *
56
+ * A manifest with `standalone: false` declares that it is meaningless without
57
+ * one of its recommended packs. With `recommendedPacks` absent or empty that
58
+ * declaration is self-contradictory and nothing downstream — registry, Library
59
+ * UI, or `workspace/recommended-packs-retained` — can act on it.
60
+ *
61
+ * @experimental This API is unstable and may change without notice.
62
+ */
63
+ export const makeStandaloneDeclarationValidRule = (options) => {
64
+ const ruleId = `${options.namespace}/standalone-declaration-valid`;
65
+ return {
66
+ id: ruleId,
67
+ description: "Extensions that are not standalone recommend at least one pack.",
68
+ kind: "advisory",
69
+ severity: "warning",
70
+ check: (context) => {
71
+ const manifest = manifestRecord(options.manifestJson(context));
72
+ if (manifest === undefined || manifest["standalone"] !== false) {
73
+ return Effect.succeed([]);
74
+ }
75
+ if (stringEntries(manifest["recommendedPacks"]).length > 0) {
76
+ return Effect.succeed([]);
77
+ }
78
+ return Effect.succeed([
79
+ {
80
+ kind: "advisory",
81
+ ruleId,
82
+ severity: "warning",
83
+ message: "`standalone` is set to false, which says this extension only works alongside one of its recommended packs, but no `recommendedPacks` are declared. " +
84
+ `To keep the restriction, add each pack's fully qualified name (\`@owner/packs/<name>\`) under \`recommendedPacks\` in \`${options.manifestFile}\`. ` +
85
+ `If the extension does work on its own, remove the \`standalone\` key from \`${options.manifestFile}\`.`,
86
+ location: { file: options.manifestFile },
87
+ },
88
+ ]);
89
+ },
90
+ };
91
+ };
92
+ // -----------------------------------------------------------------------------
93
+ // <namespace>/recommended-packs-valid
94
+ // -----------------------------------------------------------------------------
95
+ /**
96
+ * Build the `<namespace>/recommended-packs-valid` rule for one catalog.
97
+ *
98
+ * `recommendedPacks` is typed `PackSpecSchema`, which permits a trailing
99
+ * version range, but nothing consumes one — recommendations are matched by
100
+ * name — and `axm help packs` tells authors to use the bare reference. One
101
+ * finding per offending entry.
102
+ *
103
+ * When published manifests have drained, the field can move to
104
+ * `PackFqnSchema` and this rule retires: decoding takes over.
105
+ *
106
+ * @experimental This API is unstable and may change without notice.
107
+ */
108
+ export const makeRecommendedPacksValidRule = (options) => {
109
+ const ruleId = `${options.namespace}/recommended-packs-valid`;
110
+ return {
111
+ id: ruleId,
112
+ description: "Recommended packs are referenced by name without a version range.",
113
+ kind: "advisory",
114
+ severity: "warning",
115
+ check: (context) => {
116
+ const manifest = manifestRecord(options.manifestJson(context));
117
+ if (manifest === undefined) {
118
+ return Effect.succeed([]);
119
+ }
120
+ const findings = [];
121
+ for (const entry of stringEntries(manifest["recommendedPacks"])) {
122
+ const { fqn, range } = splitPackSpec(entry);
123
+ if (range === undefined) {
124
+ continue;
125
+ }
126
+ findings.push({
127
+ kind: "advisory",
128
+ ruleId,
129
+ severity: "warning",
130
+ message: `\`recommendedPacks\` entry '${entry}' pins a version range, but recommended packs are matched by name only, so the range is ignored. ` +
131
+ `Replace it with the bare pack reference '${fqn}' under \`recommendedPacks\` in \`${options.manifestFile}\`.`,
132
+ location: { file: options.manifestFile },
133
+ });
134
+ }
135
+ return Effect.succeed(findings);
136
+ },
137
+ };
138
+ };
139
+ //# sourceMappingURL=recommended-packs-rules.js.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared schema-delegation plumbing for `-schema-valid` rules.
3
+ *
4
+ * Every rule whose id ends in `-schema-valid` implements `check` by
5
+ * running the canonical schema through `Schema.decodeUnknownResult` with
6
+ * `onExcessProperty: "ignore"` and `errors: "all"`, then mapping the issues
7
+ * through `issuesToFindings`.
8
+ *
9
+ * Phase 3a is the first catalog; landing the helper now keeps the Phase 3b
10
+ * pack catalog from duplicating the composition when it arrives. The helper is
11
+ * intentionally narrow — only the surface the `-schema-valid` rules need.
12
+ *
13
+ * @experimental This API is unstable and may change without notice.
14
+ * @packageDocumentation
15
+ */
16
+ import * as Effect from "effect/Effect";
17
+ import * as Schema from "effect/Schema";
18
+ import type { AdvisoryFinding, Severity } from "../../rule.js";
19
+ /**
20
+ * Decode `input` against `schema` (excess keys ignored, all issues collected)
21
+ * and return one advisory finding per leaf issue. Success produces `[]`.
22
+ *
23
+ * `input` of `undefined` short-circuits to `[]` — the caller is expected to
24
+ * have already guarded on "manifest exists" via the complementary
25
+ * `-present` rule.
26
+ *
27
+ * @param ruleId - `<namespace>/<name>` id of the calling rule.
28
+ * @param severity - Severity to stamp on each emitted finding.
29
+ * @param file - Accessor-relative file path to stamp on `location.file`.
30
+ * @param schema - Canonical Effect schema; `Schema.decodeUnknownResult`
31
+ * runs with `onExcessProperty: "ignore"` and `errors: "all"`.
32
+ * @param input - Raw decoded JSON value to check (typically `subject.*Json`).
33
+ *
34
+ * @experimental This API is unstable and may change without notice.
35
+ */
36
+ export declare const schemaDecodeFindings: <A, I>(ruleId: string, severity: Severity, file: string, schema: Schema.Codec<A, I>, input: unknown) => Effect.Effect<ReadonlyArray<AdvisoryFinding>>;
37
+ /**
38
+ * Read the top-level field names of a `Schema.Struct` value.
39
+ *
40
+ * Callers produce an `allowedKeys` set in one place, driven by the schema
41
+ * itself — no copy-paste of field names into rule bodies. If the schema
42
+ * gains a field the allowed-keys set grows by construction.
43
+ *
44
+ * @experimental This API is unstable and may change without notice.
45
+ */
46
+ export declare const structFieldKeys: (struct: {
47
+ readonly fields: Readonly<Record<string, unknown>>;
48
+ }) => ReadonlySet<string>;
49
+ /**
50
+ * Enumerate top-level keys present on `input` that are not declared by
51
+ * `allowedKeys`. Returns one advisory finding per unknown key.
52
+ *
53
+ * `input` of `undefined` or non-object short-circuits to `[]`.
54
+ *
55
+ * @experimental This API is unstable and may change without notice.
56
+ */
57
+ export declare const enumerateUnknownTopLevelKeys: (ruleId: string, severity: Severity, file: string, allowedKeys: ReadonlySet<string>, input: unknown) => ReadonlyArray<AdvisoryFinding>;
58
+ //# sourceMappingURL=schema-rule.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Shared schema-delegation plumbing for `-schema-valid` rules.
3
+ *
4
+ * Every rule whose id ends in `-schema-valid` implements `check` by
5
+ * running the canonical schema through `Schema.decodeUnknownResult` with
6
+ * `onExcessProperty: "ignore"` and `errors: "all"`, then mapping the issues
7
+ * through `issuesToFindings`.
8
+ *
9
+ * Phase 3a is the first catalog; landing the helper now keeps the Phase 3b
10
+ * pack catalog from duplicating the composition when it arrives. The helper is
11
+ * intentionally narrow — only the surface the `-schema-valid` rules need.
12
+ *
13
+ * @experimental This API is unstable and may change without notice.
14
+ * @packageDocumentation
15
+ */
16
+ import * as Effect from "effect/Effect";
17
+ import * as Result from "effect/Result";
18
+ import * as Schema from "effect/Schema";
19
+ import { describeSchemaDocument } from "../../describe-document.js";
20
+ import { issuesToFindings } from "../../issues-to-findings.js";
21
+ import { isManifestJsonParseFailure, manifestJsonParseFailureToFinding } from "./manifest-json.js";
22
+ // -----------------------------------------------------------------------------
23
+ // schemaDecodeFindings
24
+ // -----------------------------------------------------------------------------
25
+ /**
26
+ * Decode `input` against `schema` (excess keys ignored, all issues collected)
27
+ * and return one advisory finding per leaf issue. Success produces `[]`.
28
+ *
29
+ * `input` of `undefined` short-circuits to `[]` — the caller is expected to
30
+ * have already guarded on "manifest exists" via the complementary
31
+ * `-present` rule.
32
+ *
33
+ * @param ruleId - `<namespace>/<name>` id of the calling rule.
34
+ * @param severity - Severity to stamp on each emitted finding.
35
+ * @param file - Accessor-relative file path to stamp on `location.file`.
36
+ * @param schema - Canonical Effect schema; `Schema.decodeUnknownResult`
37
+ * runs with `onExcessProperty: "ignore"` and `errors: "all"`.
38
+ * @param input - Raw decoded JSON value to check (typically `subject.*Json`).
39
+ *
40
+ * @experimental This API is unstable and may change without notice.
41
+ */
42
+ export const schemaDecodeFindings = (ruleId, severity, file, schema, input) => {
43
+ if (input === undefined) {
44
+ return Effect.succeed([]);
45
+ }
46
+ if (isManifestJsonParseFailure(input)) {
47
+ return Effect.succeed([manifestJsonParseFailureToFinding(ruleId, severity, file, input)]);
48
+ }
49
+ const result = Schema.decodeUnknownResult(schema)(input, {
50
+ onExcessProperty: "ignore",
51
+ errors: "all",
52
+ });
53
+ if (Result.isSuccess(result)) {
54
+ return Effect.succeed([]);
55
+ }
56
+ return Effect.succeed(issuesToFindings(ruleId, severity, file, result.failure.issue));
57
+ };
58
+ // -----------------------------------------------------------------------------
59
+ // enumerateUnknownTopLevelKeys
60
+ // -----------------------------------------------------------------------------
61
+ /**
62
+ * Read the top-level field names of a `Schema.Struct` value.
63
+ *
64
+ * Callers produce an `allowedKeys` set in one place, driven by the schema
65
+ * itself — no copy-paste of field names into rule bodies. If the schema
66
+ * gains a field the allowed-keys set grows by construction.
67
+ *
68
+ * @experimental This API is unstable and may change without notice.
69
+ */
70
+ export const structFieldKeys = (struct) => new Set(Object.keys(struct.fields));
71
+ /**
72
+ * Enumerate top-level keys present on `input` that are not declared by
73
+ * `allowedKeys`. Returns one advisory finding per unknown key.
74
+ *
75
+ * `input` of `undefined` or non-object short-circuits to `[]`.
76
+ *
77
+ * @experimental This API is unstable and may change without notice.
78
+ */
79
+ export const enumerateUnknownTopLevelKeys = (ruleId, severity, file, allowedKeys, input) => {
80
+ if (isManifestJsonParseFailure(input)) {
81
+ return [];
82
+ }
83
+ if (!isPlainRecord(input)) {
84
+ return [];
85
+ }
86
+ const findings = [];
87
+ for (const key of Object.keys(input)) {
88
+ if (allowedKeys.has(key)) {
89
+ continue;
90
+ }
91
+ findings.push({
92
+ kind: "advisory",
93
+ ruleId,
94
+ severity,
95
+ message: `${describeSchemaDocument(file)} has unrecognized top-level field '${key}'. ` +
96
+ `The current schema does not recognize this field.`,
97
+ location: { file },
98
+ });
99
+ }
100
+ return findings;
101
+ };
102
+ const isPlainRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
103
+ //# sourceMappingURL=schema-rule.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `skill/frontmatter-parseable` — SKILL.md frontmatter parses as a YAML
3
+ * mapping.
4
+ *
5
+ * Cascade (reports the first failure in the order below; later arms
6
+ * short-circuit):
7
+ *
8
+ * 1. SKILL.md begins with `---` at byte 0 (no BOM, no leading whitespace, no
9
+ * leading HTML comment). This is the arm that caught the
10
+ * `@agentxm/skills/axm` regression — an HTML comment preceded the
11
+ * frontmatter delimiter, so YAML parsers silently treated the content as
12
+ * body.
13
+ * 2. Frontmatter YAML parses without error.
14
+ * 3. Parsed frontmatter is a mapping (not a list, not a scalar).
15
+ *
16
+ * Presence is handled by `skill/skill-md-present`; this rule early-returns
17
+ * `[]` when `SKILL.md` is absent.
18
+ *
19
+ * Advisory-only — the cascade mixes one mechanically-fixable arm
20
+ * (strip-leading-bytes) with arms requiring human judgment (fix YAML
21
+ * syntax). Per the lint design, splitting by
22
+ * kind or adding a byte-range mutation Operation is deferred until a second
23
+ * mechanical arm justifies it.
24
+ *
25
+ * @experimental This API is unstable and may change without notice.
26
+ * @packageDocumentation
27
+ */
28
+ import type { SkillRuleContext } from "../../context.js";
29
+ import type { AdvisoryRule } from "../../rule.js";
30
+ export declare const frontmatterParseableRule: AdvisoryRule<SkillRuleContext>;
31
+ //# sourceMappingURL=frontmatter-parseable.d.ts.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * `skill/frontmatter-parseable` — SKILL.md frontmatter parses as a YAML
3
+ * mapping.
4
+ *
5
+ * Cascade (reports the first failure in the order below; later arms
6
+ * short-circuit):
7
+ *
8
+ * 1. SKILL.md begins with `---` at byte 0 (no BOM, no leading whitespace, no
9
+ * leading HTML comment). This is the arm that caught the
10
+ * `@agentxm/skills/axm` regression — an HTML comment preceded the
11
+ * frontmatter delimiter, so YAML parsers silently treated the content as
12
+ * body.
13
+ * 2. Frontmatter YAML parses without error.
14
+ * 3. Parsed frontmatter is a mapping (not a list, not a scalar).
15
+ *
16
+ * Presence is handled by `skill/skill-md-present`; this rule early-returns
17
+ * `[]` when `SKILL.md` is absent.
18
+ *
19
+ * Advisory-only — the cascade mixes one mechanically-fixable arm
20
+ * (strip-leading-bytes) with arms requiring human judgment (fix YAML
21
+ * syntax). Per the lint design, splitting by
22
+ * kind or adding a byte-range mutation Operation is deferred until a second
23
+ * mechanical arm justifies it.
24
+ *
25
+ * @experimental This API is unstable and may change without notice.
26
+ * @packageDocumentation
27
+ */
28
+ import * as Effect from "effect/Effect";
29
+ import YAML from "yaml";
30
+ const RULE_ID = "skill/frontmatter-parseable";
31
+ const SKILL_MD = "SKILL.md";
32
+ const DELIMITER = "---";
33
+ // U+FEFF — UTF-8 BOM as a single code point. Escape form so the editor /
34
+ // formatter can't silently strip the literal.
35
+ const UTF8_BOM = "\uFEFF";
36
+ // `ignoreBOM: true` here means "pass the BOM through to the decoded string,
37
+ // don't strip it" — cascade arm 1 must see the BOM to flag it.
38
+ const decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
39
+ export const frontmatterParseableRule = {
40
+ id: RULE_ID,
41
+ description: "SKILL.md frontmatter is a valid YAML mapping.",
42
+ kind: "advisory",
43
+ severity: "error",
44
+ check: (context) => Effect.gen(function* () {
45
+ const exists = yield* context.files.exists(SKILL_MD);
46
+ if (!exists) {
47
+ // Presence is covered by `skill/skill-md-present`.
48
+ return [];
49
+ }
50
+ const bytesResult = yield* context.files.readBytes(SKILL_MD).pipe(Effect.map((bytes) => ({ kind: "ok", bytes })),
51
+ // Treat read errors the same as absence — presence rule already owns
52
+ // that path. If `exists` said true but read fails, the cascade can't
53
+ // run anyway; silence here keeps the finding count at one.
54
+ Effect.catch(() => Effect.succeed({ kind: "none" })));
55
+ if (bytesResult.kind === "none") {
56
+ return [];
57
+ }
58
+ const content = decoder.decode(bytesResult.bytes);
59
+ return evaluateCascade(content);
60
+ }),
61
+ };
62
+ // -----------------------------------------------------------------------------
63
+ // Cascade
64
+ // -----------------------------------------------------------------------------
65
+ const evaluateCascade = (content) => {
66
+ const leading = detectLeadingBytes(content);
67
+ if (leading !== undefined) {
68
+ return [leading];
69
+ }
70
+ const parsed = parseFrontmatterYaml(content);
71
+ if (parsed === undefined) {
72
+ // No frontmatter block. Treat as bad (cascade arm 1 "begins with ---"
73
+ // already handled the "literally no ---" case; reaching here means the
74
+ // delimiter was found but the closing delimiter is missing).
75
+ return [
76
+ finding("SKILL.md frontmatter is malformed because the closing `---` delimiter is missing. Add a closing `---` after the frontmatter block."),
77
+ ];
78
+ }
79
+ if (parsed.kind === "parse-error") {
80
+ return [
81
+ finding(`SKILL.md frontmatter YAML is invalid. Detail: ${parsed.message}. Fix the YAML syntax in the frontmatter block.`),
82
+ ];
83
+ }
84
+ if (!isMapping(parsed.value)) {
85
+ return [
86
+ finding("SKILL.md frontmatter must be a YAML mapping, not a list or scalar. Rewrite the frontmatter block in `SKILL.md` as `key: value` pairs."),
87
+ ];
88
+ }
89
+ return [];
90
+ };
91
+ // -----------------------------------------------------------------------------
92
+ // Arm 1: leading bytes before `---`
93
+ // -----------------------------------------------------------------------------
94
+ const detectLeadingBytes = (content) => {
95
+ if (content.startsWith(UTF8_BOM)) {
96
+ return finding("SKILL.md starts with a UTF-8 BOM before the frontmatter delimiter. Remove the BOM so the file starts with `---`.", { line: 1 });
97
+ }
98
+ if (content.startsWith(DELIMITER)) {
99
+ return undefined;
100
+ }
101
+ // Any non-`---` leading content is a cascade-arm-1 violation. Detect the
102
+ // common HTML-comment case explicitly so the finding message is clear.
103
+ if (/^\s*<!--/.test(content)) {
104
+ return finding("SKILL.md starts with an HTML comment before the frontmatter delimiter. Move or remove the comment so the file starts with `---`.", { line: 1 });
105
+ }
106
+ return finding("SKILL.md must start with the frontmatter delimiter `---`. Remove any leading content so the file starts with `---`.", { line: 1 });
107
+ };
108
+ const parseFrontmatterYaml = (content) => {
109
+ // content starts with `---` (arm 1 guaranteed). Find closing delimiter on a
110
+ // new line.
111
+ const afterOpening = content.indexOf("\n");
112
+ if (afterOpening === -1) {
113
+ return undefined;
114
+ }
115
+ const closingIndex = content.indexOf(`\n${DELIMITER}`, afterOpening);
116
+ if (closingIndex === -1) {
117
+ return undefined;
118
+ }
119
+ const yamlContent = content.slice(afterOpening + 1, closingIndex);
120
+ try {
121
+ // YAML.parse returns `null | undefined` for empty input; we care about
122
+ // shape, not emptiness at this arm.
123
+ const value = YAML.parse(yamlContent);
124
+ return { kind: "parsed", value };
125
+ }
126
+ catch (error) {
127
+ return {
128
+ kind: "parse-error",
129
+ message: error instanceof Error ? error.message : String(error),
130
+ };
131
+ }
132
+ };
133
+ const isMapping = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
134
+ // -----------------------------------------------------------------------------
135
+ // Finding helpers
136
+ // -----------------------------------------------------------------------------
137
+ const finding = (message, position) => ({
138
+ kind: "advisory",
139
+ ruleId: RULE_ID,
140
+ severity: "error",
141
+ message,
142
+ location: {
143
+ file: SKILL_MD,
144
+ ...(position?.line !== undefined ? { line: position.line } : {}),
145
+ ...(position?.column !== undefined ? { column: position.column } : {}),
146
+ },
147
+ });
148
+ //# sourceMappingURL=frontmatter-parseable.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `skill/frontmatter-standard-valid` — SKILL.md metadata conforms to the
3
+ * pinned Agent Skills specification and reference validator.
4
+ *
5
+ * Parsing failures are owned by `skill/frontmatter-parseable`; this rule
6
+ * returns no findings until the frontmatter is a YAML mapping.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import type { SkillRuleContext } from "../../context.js";
11
+ import type { AdvisoryRule } from "../../rule.js";
12
+ export declare const frontmatterStandardValidRule: AdvisoryRule<SkillRuleContext>;
13
+ //# sourceMappingURL=frontmatter-standard-valid.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `skill/frontmatter-standard-valid` — SKILL.md metadata conforms to the
3
+ * pinned Agent Skills specification and reference validator.
4
+ *
5
+ * Parsing failures are owned by `skill/frontmatter-parseable`; this rule
6
+ * returns no findings until the frontmatter is a YAML mapping.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as Effect from "effect/Effect";
11
+ import YAML from "yaml";
12
+ import { validateSkillFrontmatter } from "../../../content/skill-content.js";
13
+ const RULE_ID = "skill/frontmatter-standard-valid";
14
+ const SKILL_MD = "SKILL.md";
15
+ const decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
16
+ const parseFrontmatterMapping = (content) => {
17
+ if (!content.startsWith("---"))
18
+ return undefined;
19
+ const openingEnd = content.indexOf("\n");
20
+ if (openingEnd === -1)
21
+ return undefined;
22
+ const closingStart = content.indexOf("\n---", openingEnd);
23
+ if (closingStart === -1)
24
+ return undefined;
25
+ try {
26
+ const parsed = YAML.parse(content.slice(openingEnd + 1, closingStart));
27
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
28
+ ? parsed
29
+ : undefined;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ };
35
+ const finding = (message) => ({
36
+ kind: "advisory",
37
+ ruleId: RULE_ID,
38
+ severity: "error",
39
+ message,
40
+ location: { file: SKILL_MD, line: 1 },
41
+ });
42
+ export const frontmatterStandardValidRule = {
43
+ id: RULE_ID,
44
+ description: "SKILL.md metadata conforms to the pinned Agent Skills standard.",
45
+ kind: "advisory",
46
+ severity: "error",
47
+ check: (context) => Effect.gen(function* () {
48
+ const exists = yield* context.files.exists(SKILL_MD);
49
+ if (!exists)
50
+ return [];
51
+ const content = yield* context.files.readBytes(SKILL_MD).pipe(Effect.map((bytes) => decoder.decode(bytes)), Effect.catch(() => Effect.succeed(undefined)));
52
+ if (content === undefined)
53
+ return [];
54
+ const parsed = parseFrontmatterMapping(content);
55
+ if (parsed === undefined)
56
+ return [];
57
+ const validation = validateSkillFrontmatter(parsed, context.subject.expectedName);
58
+ return validation.valid ? [] : validation.errors.map(finding);
59
+ }),
60
+ };
61
+ //# sourceMappingURL=frontmatter-standard-valid.js.map