@graphorin/skills 0.5.0 → 0.6.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.
- package/CHANGELOG.md +44 -0
- package/README.md +13 -11
- package/anthropic-spec-snapshot.json +1 -1
- package/dist/activation/index.js +2 -2
- package/dist/activation/index.js.map +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/frontmatter/index.d.ts +1 -1
- package/dist/frontmatter/index.d.ts.map +1 -1
- package/dist/frontmatter/index.js +9 -4
- package/dist/frontmatter/index.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/loader/index.d.ts.map +1 -1
- package/dist/loader/index.js +3 -3
- package/dist/loader/index.js.map +1 -1
- package/dist/migration/index.d.ts +2 -2
- package/dist/migration/index.js +2 -2
- package/dist/migration/index.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/registry/bridge.js.map +1 -1
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.js.map +1 -1
- package/dist/spec/index.d.ts +13 -9
- package/dist/spec/index.d.ts.map +1 -1
- package/dist/spec/index.js +13 -9
- package/dist/spec/index.js.map +1 -1
- package/dist/types/index.d.ts +16 -16
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @graphorin/skills
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
|
|
10
|
+
- @graphorin/core@0.6.1
|
|
11
|
+
- @graphorin/security@0.6.1
|
|
12
|
+
- @graphorin/tools@0.6.1
|
|
13
|
+
|
|
14
|
+
## 0.6.0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [#134](https://github.com/o-stepper/graphorin/pull/134) [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6) Thanks [@o-stepper](https://github.com/o-stepper)! - Cap self-declared skill trust for npm and git sources (mcp-skills-01). The RP-9 rule - trust is granted by the integrator, never the artifact - previously applied only to `folder` sources: an npm/git skill without an operator `trustLevel` took its SKILL.md's self-declared `graphorin-trust-level: trusted` verbatim, and because the signature trust root allows an inline key in the same SKILL.md, a malicious package could self-sign, declare trusted, and load unsandboxed. The cap now applies to every source kind; an explicit operator `trustLevel` on the source still wins.
|
|
19
|
+
|
|
20
|
+
- [#135](https://github.com/o-stepper/graphorin/pull/135) [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3) Thanks [@o-stepper](https://github.com/o-stepper)! - MCP correctness tail (audit 2026-07-04 Wave B, cluster B8).
|
|
21
|
+
|
|
22
|
+
- mcp-skills-05: a `sampling/createMessage` carrying `tools` / `toolChoice` is rejected with an `McpError` (the 2025-11-25 MUST - the client does not declare `sampling.tools`; previously it silently answered as a plain completion). URL-mode elicitation is declined honestly instead of being surfaced as an empty form with the URL invisible.
|
|
23
|
+
- mcp-skills-06: MCP resource handles are scoped to their originating server (`mcp:<serverId>:<uri>`); `createMcpResourceReader` consults ONLY the matching client, closing the cross-server confused-deputy hop where server A's link (or a prompt-injected model) fetched a resource from trusted server B. Bare URIs are refused unless `allowCrossServer: true` is opted in.
|
|
24
|
+
- mcp-skills-07: server-supplied JSON-Schema `pattern`s are guarded before compilation - pattern/tested-string length caps plus a nested-quantifier heuristic (`(a+)+`-class) - so a malicious server can no longer stall the event loop with catastrophic backtracking; guarded-out patterns degrade to permissive like malformed ones.
|
|
25
|
+
- mcp-skills-10: new `onTransportClose` / `onTransportError` callbacks (plus `mcp.transport.closed|error.total` counters and a WARN log) surface a dead stdio child / dropped HTTP session; previously a disconnect was observable only as protocol errors on later calls.
|
|
26
|
+
- mcp-skills-11: new `MCPClient.readResourceContents(uri)` returns every content item; the single-content `readResource` convenience now WARNs + counts when it truncates a multi-content response.
|
|
27
|
+
- mcp-skills-04 (adjusted): a same-source tool re-registration increments `tool.registry.same-source-replaced.total`, so two server instances colliding on one identity are observable churn instead of a silent swap.
|
|
28
|
+
- mcp-skills-09 (F-10): the documented NESTED `metadata.graphorin` frontmatter form now actually resolves (flat dotted keys still win when both are present); skills.md fixes `sandboxTier` to `sandbox` and the `parseSlashCommand` output shape.
|
|
29
|
+
- mcp-skills-08 (F-9): mcp-client.md rewritten to the real observability surface (counters, no `mcp.tool.invoke` span, no per-call audit rows), the real executor error mapping, and the `.`-namespaced `sideEffectClassByTool` keys; sampling-with-tools / tasks / icons documented as known-unsupported.
|
|
30
|
+
|
|
31
|
+
- [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - Documentation-truth sweep (audit 2026-07-04 Wave E, cluster E10): stale npm package descriptions rewritten (cli "Phase 14a three commands", mcp "upcoming auth CLI", store-sqlite's nonexistent WorkerPool), the store-sqlite WorkerPool TSDoc and the cipher-pragma ordering comment corrected, the executor timeout-precedence JSDoc fixed to the real `inlineToolTimeoutMs > tier timeoutMs > default` order, the `rrf.<label>` explain signals documented, and the skills spec-snapshot wording no longer claims a CI cron refreshes it (the diff is a manual `--upstream` pass; the release gate only parses the bundled snapshot).
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [[`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`32f20c1`](https://github.com/o-stepper/graphorin/commit/32f20c110f184f8cef7eec85bf39f5f07c886cb6), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`4f850d9`](https://github.com/o-stepper/graphorin/commit/4f850d9bc0a05d6256c59c5117b010336fcb41d3), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`17a2d30`](https://github.com/o-stepper/graphorin/commit/17a2d30564154ca2ab87473335cdef43a5089c84), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`ce06b47`](https://github.com/o-stepper/graphorin/commit/ce06b472af9e30ac5d0792f7a8b6f42170a94627), [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a)]:
|
|
34
|
+
- @graphorin/tools@0.6.0
|
|
35
|
+
- @graphorin/core@0.6.0
|
|
36
|
+
- @graphorin/security@0.6.0
|
|
37
|
+
|
|
38
|
+
## 0.5.0
|
|
39
|
+
|
|
40
|
+
First version published to the npm registry (with Sigstore build
|
|
41
|
+
provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
|
|
42
|
+
milestones and were never published. All `@graphorin/*` packages release
|
|
43
|
+
lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
|
|
44
|
+
in the repository-level
|
|
45
|
+
[CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).
|
|
46
|
+
|
|
3
47
|
## 0.1.0
|
|
4
48
|
|
|
5
49
|
Initial release. See the workspace root `CHANGELOG.md` for the full
|
package/README.md
CHANGED
|
@@ -28,9 +28,11 @@ the `graphorin-*` prefix.
|
|
|
28
28
|
- **Spec-snapshot tracking.** A bundled
|
|
29
29
|
`anthropic-spec-snapshot.json` records the upstream fields the
|
|
30
30
|
framework recognises and the migration policy for every
|
|
31
|
-
`graphorin-*` extension. The `pnpm run check-anthropic-spec`
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
`graphorin-*` extension. The `pnpm run check-anthropic-spec` helper
|
|
32
|
+
diffs the snapshot against a maintainer-supplied upstream snapshot
|
|
33
|
+
(`--upstream <path>`) so new fields can be promoted with a clear
|
|
34
|
+
changelog entry; it runs manually or via the release `mvp-readiness`
|
|
35
|
+
gate (no scheduled job, no auto-refresh).
|
|
34
36
|
- **Supply-chain aware.** Loading from `npm-package` and `git-repo`
|
|
35
37
|
sources delegates to the supply-chain helpers in
|
|
36
38
|
`@graphorin/security` so untrusted skills install with
|
|
@@ -80,7 +82,7 @@ import { loadSkillFromSource, createSkillRegistry } from '@graphorin/skills';
|
|
|
80
82
|
|
|
81
83
|
const skill = await loadSkillFromSource(
|
|
82
84
|
{ kind: 'folder', path: './skills/finance-helper' },
|
|
83
|
-
{ conflictPolicy: 'warn', runtimeVersion: '0.
|
|
85
|
+
{ conflictPolicy: 'warn', runtimeVersion: '0.6.1' },
|
|
84
86
|
);
|
|
85
87
|
|
|
86
88
|
const registry = createSkillRegistry();
|
|
@@ -109,7 +111,7 @@ license: MIT
|
|
|
109
111
|
disable-model-invocation: false
|
|
110
112
|
|
|
111
113
|
# === Graphorin extensions (namespaced) ===
|
|
112
|
-
graphorin-runtime-compat: ^0.
|
|
114
|
+
graphorin-runtime-compat: ^0.6.1
|
|
113
115
|
graphorin-trust-level: trusted
|
|
114
116
|
graphorin-sensitivity: internal
|
|
115
117
|
graphorin-handoff-input-filter: lastUser
|
|
@@ -143,19 +145,19 @@ The body of the SKILL.md is loaded lazily on activation.
|
|
|
143
145
|
|
|
144
146
|
The supply-chain layer enforces `--ignore-scripts` and signature
|
|
145
147
|
verification through `@graphorin/security/supply-chain`. The skills
|
|
146
|
-
loader does not duplicate that logic
|
|
148
|
+
loader does not duplicate that logic - it delegates and surfaces the
|
|
147
149
|
resulting `ResolvedSkillTrustPolicy` alongside the loaded skill.
|
|
148
150
|
|
|
149
151
|
## Dependencies
|
|
150
152
|
|
|
151
|
-
- `yaml@^2.8.0`
|
|
153
|
+
- `yaml@^2.8.0` - required runtime dependency. Used for frontmatter
|
|
152
154
|
parsing and migrator output.
|
|
153
|
-
- `@graphorin/core`
|
|
155
|
+
- `@graphorin/core` - typed contracts (`ResolvedTool`, …) consumed by
|
|
154
156
|
the registry's tool-declaration surface.
|
|
155
|
-
- `@graphorin/security`
|
|
157
|
+
- `@graphorin/security` - supply-chain installer, ed25519 signature
|
|
156
158
|
verifier, trust-policy resolver. Skills loaded from `npm-package`
|
|
157
159
|
and `git-repo` sources go through this package.
|
|
158
|
-
- `@graphorin/tools`
|
|
160
|
+
- `@graphorin/tools` - peer surface the agent runtime uses to
|
|
159
161
|
register skill-bundled tools after activation. The skills loader
|
|
160
162
|
does not import the runtime registry directly; it returns a typed
|
|
161
163
|
`RegisteredToolDeclaration[]` the runtime consumes.
|
|
@@ -166,4 +168,4 @@ MIT. Copyright © 2026 Oleksiy Stepurenko.
|
|
|
166
168
|
|
|
167
169
|
---
|
|
168
170
|
|
|
169
|
-
**Project Graphorin** · v0.
|
|
171
|
+
**Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"snapshotDate": "2026-04-19",
|
|
5
5
|
"specSource": "https://agentskills.io/specification",
|
|
6
6
|
"specCommit": null,
|
|
7
|
-
"rationale": "Bundled snapshot of the publicly published `SKILL.md` file-format specification used to determine which frontmatter fields are recognised by the upstream loader. Updated
|
|
7
|
+
"rationale": "Bundled snapshot of the publicly published `SKILL.md` file-format specification used to determine which frontmatter fields are recognised by the upstream loader. Updated manually: `pnpm run check-anthropic-spec -- --upstream <snapshot>` diffs this file against a maintainer-fetched upstream snapshot (no scheduled CI job refreshes it).",
|
|
8
8
|
"knownFields": {
|
|
9
9
|
"name": {
|
|
10
10
|
"since": "2025-01-01",
|
package/dist/activation/index.js
CHANGED
|
@@ -6,12 +6,12 @@ import { SlashCommandParseError } from "../errors/index.js";
|
|
|
6
6
|
*
|
|
7
7
|
* Two activation paths are supported per DEC-206:
|
|
8
8
|
*
|
|
9
|
-
* 1. **Auto**
|
|
9
|
+
* 1. **Auto** - every step the agent runtime injects the metadata of
|
|
10
10
|
* every registered skill into the system prompt; the model elects
|
|
11
11
|
* a skill by invoking the synthetic `activate_skill(name)` tool.
|
|
12
12
|
* Skills with `disable-model-invocation: true` are excluded from
|
|
13
13
|
* the metadata advertisement so the model never sees them.
|
|
14
|
-
* 2. **Slash command**
|
|
14
|
+
* 2. **Slash command** - the user types `/skill:<name>` (optionally
|
|
15
15
|
* followed by free-form arguments) and the agent runtime activates
|
|
16
16
|
* the matched skill regardless of `disable-model-invocation`.
|
|
17
17
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/activation/index.ts"],"sourcesContent":["/**\n * Activation surface for `@graphorin/skills`.\n *\n * Two activation paths are supported per DEC-206:\n *\n * 1. **Auto**
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/activation/index.ts"],"sourcesContent":["/**\n * Activation surface for `@graphorin/skills`.\n *\n * Two activation paths are supported per DEC-206:\n *\n * 1. **Auto** - every step the agent runtime injects the metadata of\n * every registered skill into the system prompt; the model elects\n * a skill by invoking the synthetic `activate_skill(name)` tool.\n * Skills with `disable-model-invocation: true` are excluded from\n * the metadata advertisement so the model never sees them.\n * 2. **Slash command** - the user types `/skill:<name>` (optionally\n * followed by free-form arguments) and the agent runtime activates\n * the matched skill regardless of `disable-model-invocation`.\n *\n * The runtime owns the actual activation; this module only parses the\n * inputs into a structured payload.\n *\n * @packageDocumentation\n */\n\nimport { SlashCommandParseError } from '../errors/index.js';\nimport type { SlashCommandActivation } from '../types/index.js';\n\n/**\n * Parse a single message body for a `/skill:<name>` invocation. The\n * grammar accepts:\n *\n * ```\n * /skill:<name>\n * /skill:<name> <free-form-args>\n * ```\n *\n * `<name>` must match `^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$` (kebab-case\n * conventional). Whitespace before the leading `/` is tolerated; any\n * other prefix triggers a {@link SlashCommandParseError}.\n *\n * @stable\n */\nexport function parseSlashCommand(raw: string): SlashCommandActivation {\n if (typeof raw !== 'string' || raw.length === 0) {\n throw new SlashCommandParseError(raw ?? '', 'message body must be a non-empty string.');\n }\n const trimmed = raw.replace(/^\\s+/u, '');\n if (!trimmed.startsWith('/skill:')) {\n throw new SlashCommandParseError(raw, \"message must begin with '/skill:'.\");\n }\n const remainder = trimmed.slice('/skill:'.length);\n if (remainder.length === 0) {\n throw new SlashCommandParseError(raw, \"missing skill name after '/skill:'.\");\n }\n const match = /^([A-Za-z0-9][A-Za-z0-9_.-]{0,127})(?:\\s+([\\s\\S]*))?$/u.exec(remainder);\n if (match === null) {\n throw new SlashCommandParseError(\n raw,\n 'skill name must match /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/.',\n );\n }\n const name = match[1] ?? '';\n const args = (match[2] ?? '').trim();\n return Object.freeze({ name, args, raw });\n}\n\n/**\n * Convenience predicate. Returns `true` when {@link parseSlashCommand}\n * would succeed against the supplied body.\n *\n * @stable\n */\nexport function isSlashCommand(raw: string): boolean {\n try {\n parseSlashCommand(raw);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,kBAAkB,KAAqC;AACrE,KAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAC5C,OAAM,IAAI,uBAAuB,OAAO,IAAI,2CAA2C;CAEzF,MAAM,UAAU,IAAI,QAAQ,SAAS,GAAG;AACxC,KAAI,CAAC,QAAQ,WAAW,UAAU,CAChC,OAAM,IAAI,uBAAuB,KAAK,qCAAqC;CAE7E,MAAM,YAAY,QAAQ,MAAM,EAAiB;AACjD,KAAI,UAAU,WAAW,EACvB,OAAM,IAAI,uBAAuB,KAAK,sCAAsC;CAE9E,MAAM,QAAQ,yDAAyD,KAAK,UAAU;AACtF,KAAI,UAAU,KACZ,OAAM,IAAI,uBACR,KACA,8DACD;CAEH,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,QAAQ,MAAM,MAAM,IAAI,MAAM;AACpC,QAAO,OAAO,OAAO;EAAE;EAAM;EAAM;EAAK,CAAC;;;;;;;;AAS3C,SAAgB,eAAe,KAAsB;AACnD,KAAI;AACF,oBAAkB,IAAI;AACtB,SAAO;SACD;AACN,SAAO"}
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -103,7 +103,7 @@ declare class SlashCommandParseError extends GraphorinSkillsError {
|
|
|
103
103
|
cause?: unknown;
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
-
/** Convenience union
|
|
106
|
+
/** Convenience union - every `kind` discriminator the package may emit. */
|
|
107
107
|
type GraphorinSkillsErrorKind = 'frontmatter:conflict' | 'manifest:parse' | 'runtime-compat:mismatch' | 'frontmatter:missing-required' | 'handoff:input-filter-required' | 'load:failed' | 'registry:name-collision' | 'slash:parse';
|
|
108
108
|
//#endregion
|
|
109
109
|
export { GraphorinSkillsError, GraphorinSkillsErrorKind, InputFilterRequiredError, SkillFrontmatterConflictError, SkillLoadError, SkillManifestParseError, SkillNameCollisionError, SkillRequiredFieldMissingError, SkillRuntimeCompatError, SlashCommandParseError };
|
package/dist/errors/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/errors/index.ts"],"sourcesContent":["/**\n * Typed error hierarchy for `@graphorin/skills`.\n *\n * Every error carries a stable lowercase `kind` discriminator so the\n * agent runtime, the CLI, and the audit emitter can branch without\n * resorting to `instanceof` chains, plus an optional `hint` field with\n * an actionable remediation step (a CLI command or a documentation\n * link).\n *\n * @packageDocumentation\n */\n\n/** Base class for every error thrown by `@graphorin/skills`. */\nexport abstract class GraphorinSkillsError extends Error {\n abstract readonly kind: string;\n readonly hint?: string;\n override readonly cause?: unknown;\n constructor(message: string, options?: { hint?: string; cause?: unknown }) {\n super(message);\n this.name = new.target.name;\n if (options?.hint !== undefined) this.hint = options.hint;\n if (options?.cause !== undefined) this.cause = options.cause;\n }\n}\n\n/**\n * The frontmatter validator detected an Anthropic-base / `graphorin-*`\n * collision and the operator-resolved policy is `'error'`.\n */\nexport class SkillFrontmatterConflictError extends GraphorinSkillsError {\n readonly kind = 'frontmatter:conflict' as const;\n readonly skillName: string;\n readonly field: string;\n readonly conflictingFields: ReadonlyArray<string>;\n constructor(\n skillName: string,\n field: string,\n conflictingFields: ReadonlyArray<string>,\n options?: { hint?: string },\n ) {\n super(\n `Skill '${skillName}' frontmatter conflict on '${field}' (also set: ${conflictingFields\n .filter((f) => f !== field)\n .join(', ')}). Validator policy: 'error'.`,\n options,\n );\n this.skillName = skillName;\n this.field = field;\n this.conflictingFields = conflictingFields;\n }\n}\n\n/** A skill manifest could not be parsed (missing frontmatter / invalid YAML). */\nexport class SkillManifestParseError extends GraphorinSkillsError {\n readonly kind = 'manifest:parse' as const;\n}\n\n/**\n * The runtime-compat declaration on a skill does not satisfy the\n * loader's installed runtime version.\n */\nexport class SkillRuntimeCompatError extends GraphorinSkillsError {\n readonly kind = 'runtime-compat:mismatch' as const;\n readonly skillName: string;\n readonly declared: string;\n readonly installed: string;\n constructor(skillName: string, declared: string, installed: string, options?: { hint?: string }) {\n super(\n `Skill '${skillName}' declares runtime-compat '${declared}' which does not match installed Graphorin '${installed}'.`,\n options,\n );\n this.skillName = skillName;\n this.declared = declared;\n this.installed = installed;\n }\n}\n\n/** A required Anthropic-base field is missing from the frontmatter. */\nexport class SkillRequiredFieldMissingError extends GraphorinSkillsError {\n readonly kind = 'frontmatter:missing-required' as const;\n readonly field: string;\n constructor(field: string, options?: { hint?: string }) {\n super(`Skill frontmatter is missing required field '${field}'.`, options);\n this.field = field;\n }\n}\n\n/**\n * `Agent.toTool()` / `handoff(...)` would be invoked inside an\n * untrusted skill, but the skill did not declare\n * `graphorin-handoff-input-filter`. Throwing the error at activation\n * time prevents the runtime from materialising a sub-agent without an\n * explicit filter.\n */\nexport class InputFilterRequiredError extends GraphorinSkillsError {\n readonly kind = 'handoff:input-filter-required' as const;\n readonly skillName: string;\n constructor(skillName: string, options?: { hint?: string; cause?: unknown }) {\n super(\n `Untrusted skill '${skillName}' uses Agent.toTool()/handoff() but did not declare 'graphorin-handoff-input-filter'.`,\n {\n hint:\n options?.hint ??\n \"Add 'graphorin-handoff-input-filter: lastUser' (or another supported value) to the SKILL.md frontmatter.\",\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n },\n );\n this.skillName = skillName;\n }\n}\n\n/** A skill source could not be loaded from disk. */\nexport class SkillLoadError extends GraphorinSkillsError {\n readonly kind = 'load:failed' as const;\n readonly source: string;\n constructor(source: string, message: string, options?: { hint?: string; cause?: unknown }) {\n super(`Skill load failed (source='${source}'): ${message}`, options);\n this.source = source;\n }\n}\n\n/** A skill name in a registry registration collided with another already-loaded skill. */\nexport class SkillNameCollisionError extends GraphorinSkillsError {\n readonly kind = 'registry:name-collision' as const;\n readonly skillName: string;\n constructor(skillName: string, options?: { hint?: string; cause?: unknown }) {\n super(`Skill '${skillName}' is already registered.`, {\n hint:\n options?.hint ??\n 'Either rename one of the colliding skills or call `registry.unregister(name)` before re-registering.',\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n this.skillName = skillName;\n }\n}\n\n/**\n * The slash-command parser received a string that did not match the\n * `/skill:<name>` grammar.\n */\nexport class SlashCommandParseError extends GraphorinSkillsError {\n readonly kind = 'slash:parse' as const;\n readonly raw: string;\n constructor(raw: string, message: string, options?: { hint?: string; cause?: unknown }) {\n super(`Slash command '${raw}': ${message}`, {\n hint: options?.hint ?? 'Use `/skill:<name>` to activate a registered skill.',\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n this.raw = raw;\n }\n}\n\n/** Convenience union
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/errors/index.ts"],"sourcesContent":["/**\n * Typed error hierarchy for `@graphorin/skills`.\n *\n * Every error carries a stable lowercase `kind` discriminator so the\n * agent runtime, the CLI, and the audit emitter can branch without\n * resorting to `instanceof` chains, plus an optional `hint` field with\n * an actionable remediation step (a CLI command or a documentation\n * link).\n *\n * @packageDocumentation\n */\n\n/** Base class for every error thrown by `@graphorin/skills`. */\nexport abstract class GraphorinSkillsError extends Error {\n abstract readonly kind: string;\n readonly hint?: string;\n override readonly cause?: unknown;\n constructor(message: string, options?: { hint?: string; cause?: unknown }) {\n super(message);\n this.name = new.target.name;\n if (options?.hint !== undefined) this.hint = options.hint;\n if (options?.cause !== undefined) this.cause = options.cause;\n }\n}\n\n/**\n * The frontmatter validator detected an Anthropic-base / `graphorin-*`\n * collision and the operator-resolved policy is `'error'`.\n */\nexport class SkillFrontmatterConflictError extends GraphorinSkillsError {\n readonly kind = 'frontmatter:conflict' as const;\n readonly skillName: string;\n readonly field: string;\n readonly conflictingFields: ReadonlyArray<string>;\n constructor(\n skillName: string,\n field: string,\n conflictingFields: ReadonlyArray<string>,\n options?: { hint?: string },\n ) {\n super(\n `Skill '${skillName}' frontmatter conflict on '${field}' (also set: ${conflictingFields\n .filter((f) => f !== field)\n .join(', ')}). Validator policy: 'error'.`,\n options,\n );\n this.skillName = skillName;\n this.field = field;\n this.conflictingFields = conflictingFields;\n }\n}\n\n/** A skill manifest could not be parsed (missing frontmatter / invalid YAML). */\nexport class SkillManifestParseError extends GraphorinSkillsError {\n readonly kind = 'manifest:parse' as const;\n}\n\n/**\n * The runtime-compat declaration on a skill does not satisfy the\n * loader's installed runtime version.\n */\nexport class SkillRuntimeCompatError extends GraphorinSkillsError {\n readonly kind = 'runtime-compat:mismatch' as const;\n readonly skillName: string;\n readonly declared: string;\n readonly installed: string;\n constructor(skillName: string, declared: string, installed: string, options?: { hint?: string }) {\n super(\n `Skill '${skillName}' declares runtime-compat '${declared}' which does not match installed Graphorin '${installed}'.`,\n options,\n );\n this.skillName = skillName;\n this.declared = declared;\n this.installed = installed;\n }\n}\n\n/** A required Anthropic-base field is missing from the frontmatter. */\nexport class SkillRequiredFieldMissingError extends GraphorinSkillsError {\n readonly kind = 'frontmatter:missing-required' as const;\n readonly field: string;\n constructor(field: string, options?: { hint?: string }) {\n super(`Skill frontmatter is missing required field '${field}'.`, options);\n this.field = field;\n }\n}\n\n/**\n * `Agent.toTool()` / `handoff(...)` would be invoked inside an\n * untrusted skill, but the skill did not declare\n * `graphorin-handoff-input-filter`. Throwing the error at activation\n * time prevents the runtime from materialising a sub-agent without an\n * explicit filter.\n */\nexport class InputFilterRequiredError extends GraphorinSkillsError {\n readonly kind = 'handoff:input-filter-required' as const;\n readonly skillName: string;\n constructor(skillName: string, options?: { hint?: string; cause?: unknown }) {\n super(\n `Untrusted skill '${skillName}' uses Agent.toTool()/handoff() but did not declare 'graphorin-handoff-input-filter'.`,\n {\n hint:\n options?.hint ??\n \"Add 'graphorin-handoff-input-filter: lastUser' (or another supported value) to the SKILL.md frontmatter.\",\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n },\n );\n this.skillName = skillName;\n }\n}\n\n/** A skill source could not be loaded from disk. */\nexport class SkillLoadError extends GraphorinSkillsError {\n readonly kind = 'load:failed' as const;\n readonly source: string;\n constructor(source: string, message: string, options?: { hint?: string; cause?: unknown }) {\n super(`Skill load failed (source='${source}'): ${message}`, options);\n this.source = source;\n }\n}\n\n/** A skill name in a registry registration collided with another already-loaded skill. */\nexport class SkillNameCollisionError extends GraphorinSkillsError {\n readonly kind = 'registry:name-collision' as const;\n readonly skillName: string;\n constructor(skillName: string, options?: { hint?: string; cause?: unknown }) {\n super(`Skill '${skillName}' is already registered.`, {\n hint:\n options?.hint ??\n 'Either rename one of the colliding skills or call `registry.unregister(name)` before re-registering.',\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n this.skillName = skillName;\n }\n}\n\n/**\n * The slash-command parser received a string that did not match the\n * `/skill:<name>` grammar.\n */\nexport class SlashCommandParseError extends GraphorinSkillsError {\n readonly kind = 'slash:parse' as const;\n readonly raw: string;\n constructor(raw: string, message: string, options?: { hint?: string; cause?: unknown }) {\n super(`Slash command '${raw}': ${message}`, {\n hint: options?.hint ?? 'Use `/skill:<name>` to activate a registered skill.',\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n this.raw = raw;\n }\n}\n\n/** Convenience union - every `kind` discriminator the package may emit. */\nexport type GraphorinSkillsErrorKind =\n | 'frontmatter:conflict'\n | 'manifest:parse'\n | 'runtime-compat:mismatch'\n | 'frontmatter:missing-required'\n | 'handoff:input-filter-required'\n | 'load:failed'\n | 'registry:name-collision'\n | 'slash:parse';\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAsB,uBAAtB,cAAmD,MAAM;CAEvD,AAAS;CACT,AAAkB;CAClB,YAAY,SAAiB,SAA8C;AACzE,QAAM,QAAQ;AACd,OAAK,OAAO,IAAI,OAAO;AACvB,MAAI,SAAS,SAAS,OAAW,MAAK,OAAO,QAAQ;AACrD,MAAI,SAAS,UAAU,OAAW,MAAK,QAAQ,QAAQ;;;;;;;AAQ3D,IAAa,gCAAb,cAAmD,qBAAqB;CACtE,AAAS,OAAO;CAChB,AAAS;CACT,AAAS;CACT,AAAS;CACT,YACE,WACA,OACA,mBACA,SACA;AACA,QACE,UAAU,UAAU,6BAA6B,MAAM,eAAe,kBACnE,QAAQ,MAAM,MAAM,MAAM,CAC1B,KAAK,KAAK,CAAC,gCACd,QACD;AACD,OAAK,YAAY;AACjB,OAAK,QAAQ;AACb,OAAK,oBAAoB;;;;AAK7B,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,AAAS,OAAO;;;;;;AAOlB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,AAAS,OAAO;CAChB,AAAS;CACT,AAAS;CACT,AAAS;CACT,YAAY,WAAmB,UAAkB,WAAmB,SAA6B;AAC/F,QACE,UAAU,UAAU,6BAA6B,SAAS,8CAA8C,UAAU,KAClH,QACD;AACD,OAAK,YAAY;AACjB,OAAK,WAAW;AAChB,OAAK,YAAY;;;;AAKrB,IAAa,iCAAb,cAAoD,qBAAqB;CACvE,AAAS,OAAO;CAChB,AAAS;CACT,YAAY,OAAe,SAA6B;AACtD,QAAM,gDAAgD,MAAM,KAAK,QAAQ;AACzE,OAAK,QAAQ;;;;;;;;;;AAWjB,IAAa,2BAAb,cAA8C,qBAAqB;CACjE,AAAS,OAAO;CAChB,AAAS;CACT,YAAY,WAAmB,SAA8C;AAC3E,QACE,oBAAoB,UAAU,wFAC9B;GACE,MACE,SAAS,QACT;GACF,GAAI,SAAS,UAAU,SAAY,EAAE,GAAG,EAAE,OAAO,QAAQ,OAAO;GACjE,CACF;AACD,OAAK,YAAY;;;;AAKrB,IAAa,iBAAb,cAAoC,qBAAqB;CACvD,AAAS,OAAO;CAChB,AAAS;CACT,YAAY,QAAgB,SAAiB,SAA8C;AACzF,QAAM,8BAA8B,OAAO,MAAM,WAAW,QAAQ;AACpE,OAAK,SAAS;;;;AAKlB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,AAAS,OAAO;CAChB,AAAS;CACT,YAAY,WAAmB,SAA8C;AAC3E,QAAM,UAAU,UAAU,2BAA2B;GACnD,MACE,SAAS,QACT;GACF,GAAI,SAAS,UAAU,SAAY,EAAE,GAAG,EAAE,OAAO,QAAQ,OAAO;GACjE,CAAC;AACF,OAAK,YAAY;;;;;;;AAQrB,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,AAAS,OAAO;CAChB,AAAS;CACT,YAAY,KAAa,SAAiB,SAA8C;AACtF,QAAM,kBAAkB,IAAI,KAAK,WAAW;GAC1C,MAAM,SAAS,QAAQ;GACvB,GAAI,SAAS,UAAU,SAAY,EAAE,GAAG,EAAE,OAAO,QAAQ,OAAO;GACjE,CAAC;AACF,OAAK,MAAM"}
|
|
@@ -97,7 +97,7 @@ declare function parseAllowedToolsValue(value: unknown): ReadonlyArray<string> |
|
|
|
97
97
|
declare function parseHandoffInputFilter(value: unknown): HandoffInputFilterDeclaration | null;
|
|
98
98
|
/**
|
|
99
99
|
* Parse the `tools` field. Accepts either an array of strings (tool
|
|
100
|
-
* names
|
|
100
|
+
* names - the loader resolves modules through naming convention) or
|
|
101
101
|
* an array of objects with `name`, `module`, `description`, `tags`.
|
|
102
102
|
* Returns `null` for unsupported shapes.
|
|
103
103
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/frontmatter/index.ts"],"sourcesContent":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/frontmatter/index.ts"],"sourcesContent":[],"mappings":";;;;;AAsL0B,UAhJT,YAAA,CAgJS;EACJ,SAAA,WAAA,EAAA,MAAA;EACM,SAAA,IAAA,EAAA,MAAA;;;;;;;;;AASK,iBA/IjB,YAAA,CA+IiB,OAAA,EAAA,MAAA,CAAA,EA/Ic,YA+Id;;;;;AAajC;;AAEW,iBA7IK,oBAAA,CA6IL,WAAA,EAAA,MAAA,CAAA,EA7IgD,MA6IhD,CAAA,MAAA,EAAA,OAAA,CAAA;;;AAoOX;AA0BA;AAmEA;AAqCA;;;iBA1dgB,4CACD,mDAEF,IACV,gBAAgB;;;;;;UAmEF,0BAAA;;4BAEW;;gCAEI;;;;;;;;;UAUf,oBAAA;gBACD,SAAS;wBACD,cAAc;;mBAEnB;0BACO;sBACJ;4BACM;uBACL;2BACI;qCACU;yBACZ;4BACG;0BACF;kCACQ;sBACZ;iCACW;4BACL;sBACN;oBACF;;;;;;;;;iBAUJ,mBAAA,cACD,mCACJ,6BACR;;;;;;;;iBAmOa,sBAAA,kBAAwC;;;;;;;;;iBA0BxC,uBAAA,kBAAyC;;;;;;;;;iBAmEzC,eAAA,kBAAiC,cAAc;;;;;;;;;;;iBAqC/C,wBAAA"}
|
|
@@ -8,7 +8,7 @@ import { parse } from "yaml";
|
|
|
8
8
|
*
|
|
9
9
|
* Implements the field-resolution algorithm from ADR-043:
|
|
10
10
|
*
|
|
11
|
-
* 1. Anthropic-base (top-level, no prefix)
|
|
11
|
+
* 1. Anthropic-base (top-level, no prefix) - highest priority.
|
|
12
12
|
* 2. `metadata.graphorin.<field>` bucket per upstream `metadata`
|
|
13
13
|
* convention.
|
|
14
14
|
* 3. `graphorin-<field>` legacy top-level prefix.
|
|
@@ -66,7 +66,12 @@ function resolveSkillField(frontmatter, field, fallback) {
|
|
|
66
66
|
const conflictingSources = [];
|
|
67
67
|
const presentInBase = field in frontmatter;
|
|
68
68
|
const meta = frontmatter.metadata;
|
|
69
|
-
const
|
|
69
|
+
const metaRecord = meta !== void 0 && meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta : void 0;
|
|
70
|
+
const nested = metaRecord?.graphorin;
|
|
71
|
+
const nestedRecord = nested !== void 0 && nested !== null && typeof nested === "object" && !Array.isArray(nested) ? nested : void 0;
|
|
72
|
+
const presentInMetaFlat = metaRecord !== void 0 && `graphorin.${field}` in metaRecord;
|
|
73
|
+
const presentInMetaNested = nestedRecord !== void 0 && field in nestedRecord;
|
|
74
|
+
const presentInMeta = presentInMetaFlat || presentInMetaNested;
|
|
70
75
|
const presentInPrefix = `graphorin-${field}` in frontmatter;
|
|
71
76
|
if (presentInBase) conflictingSources.push(field);
|
|
72
77
|
if (presentInMeta) conflictingSources.push(`metadata.graphorin.${field}`);
|
|
@@ -79,7 +84,7 @@ function resolveSkillField(frontmatter, field, fallback) {
|
|
|
79
84
|
conflictingSources
|
|
80
85
|
});
|
|
81
86
|
if (presentInMeta) return Object.freeze({
|
|
82
|
-
value:
|
|
87
|
+
value: presentInMetaFlat ? metaRecord[`graphorin.${field}`] : nestedRecord[field],
|
|
83
88
|
source: "metadata-graphorin",
|
|
84
89
|
conflicting,
|
|
85
90
|
conflictingSources
|
|
@@ -340,7 +345,7 @@ function parseHandoffInputFilterStep(raw) {
|
|
|
340
345
|
}
|
|
341
346
|
/**
|
|
342
347
|
* Parse the `tools` field. Accepts either an array of strings (tool
|
|
343
|
-
* names
|
|
348
|
+
* names - the loader resolves modules through naming convention) or
|
|
344
349
|
* an array of objects with `name`, `module`, `description`, `tags`.
|
|
345
350
|
* Returns `null` for unsupported shapes.
|
|
346
351
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["parsed: unknown","parseYaml","conflictingSources: string[]","conflictPolicy: FrontmatterValidatorPolicy","unknownFieldPolicy: UnknownFieldPolicy","diagnostics: FrontmatterDiagnostic[]","severity: 'warn' | 'error'","steps: HandoffInputFilterStep[]","out: SkillToolDeclaration[]","declaration: Mutable<SkillToolDeclaration>","target"],"sources":["../../src/frontmatter/index.ts"],"sourcesContent":["/**\n * Frontmatter validator for `SKILL.md` files.\n *\n * Implements the field-resolution algorithm from ADR-043:\n *\n * 1. Anthropic-base (top-level, no prefix) — highest priority.\n * 2. `metadata.graphorin.<field>` bucket per upstream `metadata`\n * convention.\n * 3. `graphorin-<field>` legacy top-level prefix.\n * 4. caller-supplied fallback.\n *\n * The validator surfaces every diagnostic through the typed\n * {@link FrontmatterDiagnostic} contract so callers can decide whether\n * to log, fail, or escalate without re-parsing human strings.\n *\n * @packageDocumentation\n */\n\nimport { parse as parseYaml } from 'yaml';\n\nimport { SkillManifestParseError } from '../errors/index.js';\nimport {\n compareAuthorSpecHint,\n getGraphorinMapping,\n getKnownField,\n getSpecSnapshot,\n} from '../spec/index.js';\nimport type {\n FieldResolution,\n FrontmatterDiagnostic,\n FrontmatterValidatorPolicy,\n HandoffInputFilterDeclaration,\n HandoffInputFilterStep,\n SkillToolDeclaration,\n UnknownFieldPolicy,\n} from '../types/index.js';\n\n/** Result of {@link splitSkillMd}. */\nexport interface SplitSkillMd {\n readonly frontmatter: string;\n readonly body: string;\n}\n\n/**\n * Split a raw SKILL.md string into the YAML frontmatter and the\n * markdown body. The frontmatter delimiter is the canonical\n * `---\\n…\\n---\\n` pair.\n *\n * @stable\n */\nexport function splitSkillMd(skillMd: string): SplitSkillMd {\n const normalized = skillMd.replace(/^\\uFEFF/u, '').replace(/\\r\\n/g, '\\n');\n const match = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/u.exec(normalized);\n if (match === null) {\n throw new SkillManifestParseError(\n 'SKILL.md must begin with a YAML frontmatter block delimited by `---` lines.',\n );\n }\n return { frontmatter: match[1] ?? '', body: match[2] ?? '' };\n}\n\n/**\n * Parse the YAML frontmatter into a record. Returns `{}` for an empty\n * block.\n *\n * @stable\n */\nexport function parseFrontmatterYaml(frontmatter: string): Record<string, unknown> {\n if (frontmatter.trim().length === 0) return {};\n let parsed: unknown;\n try {\n parsed = parseYaml(frontmatter);\n } catch (err) {\n throw new SkillManifestParseError('SKILL.md frontmatter is not valid YAML.', { cause: err });\n }\n if (parsed === null || parsed === undefined) return {};\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new SkillManifestParseError(\n `Top-level SKILL.md frontmatter must be an object; got '${Array.isArray(parsed) ? 'array' : typeof parsed}'.`,\n );\n }\n return parsed as Record<string, unknown>;\n}\n\n/**\n * Resolve a single field across the four field-resolution tiers.\n * Returns the resolved value plus the source tier the value came from\n * AND the list of conflicting source names so the validator can\n * surface a structured diagnostic.\n *\n * @stable\n */\nexport function resolveSkillField<T = unknown>(\n frontmatter: Record<string, unknown>,\n field: string,\n fallback?: T,\n): FieldResolution<T> {\n const conflictingSources: string[] = [];\n const presentInBase = field in frontmatter;\n const meta = frontmatter.metadata;\n const presentInMeta =\n meta !== undefined &&\n meta !== null &&\n typeof meta === 'object' &&\n !Array.isArray(meta) &&\n `graphorin.${field}` in (meta as Record<string, unknown>);\n const presentInPrefix = `graphorin-${field}` in frontmatter;\n if (presentInBase) conflictingSources.push(field);\n if (presentInMeta) conflictingSources.push(`metadata.graphorin.${field}`);\n if (presentInPrefix) conflictingSources.push(`graphorin-${field}`);\n const conflicting = conflictingSources.length > 1;\n\n if (presentInBase) {\n return Object.freeze({\n value: frontmatter[field] as T,\n source: 'anthropic-base' as const,\n conflicting,\n conflictingSources,\n });\n }\n if (presentInMeta) {\n return Object.freeze({\n value: (meta as Record<string, unknown>)[`graphorin.${field}`] as T,\n source: 'metadata-graphorin' as const,\n conflicting,\n conflictingSources,\n });\n }\n if (presentInPrefix) {\n return Object.freeze({\n value: frontmatter[`graphorin-${field}`] as T,\n source: 'graphorin-prefix' as const,\n conflicting,\n conflictingSources,\n });\n }\n return Object.freeze({\n value: fallback,\n source: 'fallback' as const,\n conflicting: false,\n conflictingSources: [],\n });\n}\n\n/**\n * Options accepted by {@link validateFrontmatter}.\n *\n * @stable\n */\nexport interface ValidateFrontmatterOptions {\n /** Policy for direct collisions (Anthropic-base + `graphorin-*`). */\n readonly conflictPolicy?: FrontmatterValidatorPolicy;\n /** Policy for fields not part of the bundled snapshot or `graphorin-*` catalogue. */\n readonly unknownFieldPolicy?: UnknownFieldPolicy;\n /**\n * Installed Graphorin runtime version. Used to validate\n * `graphorin-runtime-compat` declarations against the running\n * framework.\n */\n readonly runtimeVersion?: string;\n}\n\n/** Successful return of {@link validateFrontmatter}. */\nexport interface ValidatedFrontmatter {\n readonly raw: Readonly<Record<string, unknown>>;\n readonly diagnostics: ReadonlyArray<FrontmatterDiagnostic>;\n readonly resolved: {\n readonly name: FieldResolution<unknown>;\n readonly description: FieldResolution<unknown>;\n readonly license: FieldResolution<unknown>;\n readonly compatibility: FieldResolution<unknown>;\n readonly metadata: FieldResolution<unknown>;\n readonly allowedTools: FieldResolution<unknown>;\n readonly disableModelInvocation: FieldResolution<unknown>;\n readonly trustLevel: FieldResolution<unknown>;\n readonly runtimeCompat: FieldResolution<unknown>;\n readonly sensitivity: FieldResolution<unknown>;\n readonly sensitivityDefaults: FieldResolution<unknown>;\n readonly sandbox: FieldResolution<unknown>;\n readonly handoffInputFilter: FieldResolution<unknown>;\n readonly anthropicSpec: FieldResolution<unknown>;\n readonly version: FieldResolution<unknown>;\n readonly tools: FieldResolution<unknown>;\n };\n}\n\n/**\n * Validate a parsed frontmatter against the bundled spec snapshot and\n * the `graphorin-*` extension catalogue.\n *\n * @stable\n */\nexport function validateFrontmatter(\n frontmatter: Record<string, unknown>,\n options: ValidateFrontmatterOptions = {},\n): ValidatedFrontmatter {\n const conflictPolicy: FrontmatterValidatorPolicy = options.conflictPolicy ?? 'warn';\n const unknownFieldPolicy: UnknownFieldPolicy = options.unknownFieldPolicy ?? 'preserve';\n const diagnostics: FrontmatterDiagnostic[] = [];\n\n const resolveAndDiag = <T = unknown>(field: string, fallback?: T): FieldResolution<T> => {\n const resolution = resolveSkillField<T>(frontmatter, field, fallback);\n if (resolution.conflicting) {\n const message =\n `Both '${resolution.conflictingSources.join(\"' and '\")}' are set for '${field}'. ` +\n `The Anthropic-base / metadata.graphorin.* tier wins; remove the lower-priority field to silence this diagnostic.`;\n const hint = `Keep the highest-priority entry: '${resolution.source === 'anthropic-base' ? field : resolution.source === 'metadata-graphorin' ? `metadata.graphorin.${field}` : `graphorin-${field}`}'.`;\n const severity: 'warn' | 'error' = conflictPolicy === 'error' ? 'error' : 'warn';\n if (conflictPolicy !== 'silent') {\n diagnostics.push(\n Object.freeze({\n kind: 'conflict',\n field,\n severity,\n message,\n hint,\n }),\n );\n }\n }\n return resolution;\n };\n\n const name = resolveAndDiag<string>('name');\n const description = resolveAndDiag<string>('description');\n const license = resolveAndDiag<string>('license');\n const compatibility = resolveAndDiag<string>('compatibility');\n const metadata = resolveAndDiag<Record<string, unknown>>('metadata');\n const allowedTools = resolveAndDiag<unknown>('allowed-tools');\n const disableModelInvocation = resolveAndDiag<boolean>('disable-model-invocation', false);\n const trustLevel = resolveAndDiag<string>('trust-level');\n const runtimeCompat = resolveAndDiag<string>('runtime-compat');\n // The graphorin-only `version` field carries runtime-compat semantics; the\n // Anthropic-base `metadata.version` is the skill's own version. We honour\n // both — the loader uses `runtimeCompat` for compat, `version` for\n // graphorin-runtime-compat-as-version aliasing.\n const version = resolveAndDiag<string>('version');\n const sensitivity = resolveAndDiag<string>('sensitivity');\n const sensitivityDefaults = resolveAndDiag<Record<string, string>>('sensitivity-defaults');\n const sandbox = resolveAndDiag<Record<string, unknown>>('sandbox');\n const handoffInputFilter = resolveAndDiag<unknown>('handoff-input-filter');\n const anthropicSpec = resolveAndDiag<string>('anthropic-spec');\n const tools = resolveAndDiag<unknown>('tools');\n\n if (typeof name.value !== 'string' || name.value.trim().length === 0) {\n diagnostics.push(\n Object.freeze({\n kind: 'missing-required-field',\n field: 'name',\n severity: 'error',\n message: \"Required field 'name' is missing or empty.\",\n hint: \"Add a unique 'name' (kebab-case is conventional) to the SKILL.md frontmatter.\",\n }),\n );\n }\n if (typeof description.value !== 'string' || description.value.trim().length === 0) {\n diagnostics.push(\n Object.freeze({\n kind: 'missing-required-field',\n field: 'description',\n severity: 'error',\n message: \"Required field 'description' is missing or empty.\",\n hint: 'Add a description that explains when the model should activate the skill.',\n }),\n );\n }\n if (allowedTools.value !== undefined) {\n const list = parseAllowedToolsValue(allowedTools.value);\n if (list === null) {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-field-type',\n field: 'allowed-tools',\n severity: 'warn',\n message:\n \"'allowed-tools' must be a string ('read_file write_file') or an array (['read_file', 'write_file']).\",\n hint: 'Switch to one of the two supported shapes; entries are split on whitespace for the string form.',\n }),\n );\n }\n }\n if (\n allowedTools.value !== undefined &&\n getKnownField('allowed-tools')?.stability === 'experimental'\n ) {\n diagnostics.push(\n Object.freeze({\n kind: 'experimental-field',\n field: 'allowed-tools',\n severity: 'info',\n message:\n \"'allowed-tools' is marked experimental in the bundled spec snapshot; behaviour may evolve.\",\n }),\n );\n }\n if (anthropicSpec.value !== undefined && typeof anthropicSpec.value === 'string') {\n const compare = compareAuthorSpecHint(anthropicSpec.value);\n if (compare === 'newer') {\n diagnostics.push(\n Object.freeze({\n kind: 'spec-newer-than-loader',\n field: 'anthropic-spec',\n severity: 'warn',\n message: `Skill targets spec snapshot '${anthropicSpec.value}', newer than the bundled '${getSpecSnapshot().snapshotDate}'. Unknown fields will be preserved leniently.`,\n }),\n );\n } else if (compare === 'older') {\n diagnostics.push(\n Object.freeze({\n kind: 'spec-older-than-loader',\n field: 'anthropic-spec',\n severity: 'info',\n message: `Skill targets spec snapshot '${anthropicSpec.value}', older than the bundled '${getSpecSnapshot().snapshotDate}'.`,\n }),\n );\n } else if (compare === 'unparseable') {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-field-type',\n field: 'anthropic-spec',\n severity: 'warn',\n message: `'anthropic-spec' must be an ISO-8601 date string (YYYY-MM-DD); got '${String(anthropicSpec.value)}'.`,\n }),\n );\n }\n }\n if (\n runtimeCompat.value !== undefined &&\n typeof runtimeCompat.value === 'string' &&\n options.runtimeVersion !== undefined\n ) {\n if (!isRuntimeCompatSatisfied(runtimeCompat.value, options.runtimeVersion)) {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-runtime-compat',\n field: 'runtime-compat',\n severity: 'error',\n message: `Skill declares runtime-compat '${runtimeCompat.value}' which does not match installed Graphorin '${options.runtimeVersion}'.`,\n hint: 'Adjust the skill or upgrade Graphorin so the declared range covers the installed version.',\n }),\n );\n }\n }\n for (const key of Object.keys(frontmatter)) {\n if (isRecognisedField(key)) continue;\n if (key.startsWith('graphorin-')) {\n // `graphorin-*` fields not in the mapping table are tolerated as\n // opaque metadata per Phase 08 § Frontmatter validator. Emit a\n // single info-level diagnostic so loaders can surface them.\n if (unknownFieldPolicy === 'reject') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'error',\n message: `Unknown 'graphorin-*' field '${key}' rejected by unknownFieldPolicy='reject'.`,\n }),\n );\n } else if (unknownFieldPolicy === 'warn') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'warn',\n message: `Unknown 'graphorin-*' field '${key}'. Will be preserved as opaque metadata.`,\n }),\n );\n }\n continue;\n }\n if (unknownFieldPolicy === 'reject') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'error',\n message: `Unknown frontmatter field '${key}' rejected by unknownFieldPolicy='reject'.`,\n }),\n );\n } else if (unknownFieldPolicy === 'warn') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'warn',\n message: `Unknown frontmatter field '${key}'. Will be preserved as opaque metadata.`,\n }),\n );\n }\n }\n\n return Object.freeze({\n raw: Object.freeze({ ...frontmatter }),\n diagnostics: Object.freeze(diagnostics),\n resolved: Object.freeze({\n name,\n description,\n license,\n compatibility,\n metadata,\n allowedTools,\n disableModelInvocation,\n trustLevel,\n runtimeCompat,\n sensitivity,\n sensitivityDefaults,\n sandbox,\n handoffInputFilter,\n anthropicSpec,\n version,\n tools,\n }),\n });\n}\n\n/**\n * Parse the `allowed-tools` field. Accepts either a string (with\n * whitespace-separated entries) or a string array. Returns `null` for\n * unsupported shapes so the validator can attach a typed diagnostic.\n *\n * @stable\n */\nexport function parseAllowedToolsValue(value: unknown): ReadonlyArray<string> | null {\n if (Array.isArray(value)) {\n if (!value.every((entry) => typeof entry === 'string' && entry.trim().length > 0)) {\n return null;\n }\n return Object.freeze(value.map((entry) => entry.trim()) as string[]);\n }\n if (typeof value === 'string') {\n const tokens = value\n .split(/\\s+/u)\n .map((tok) => tok.trim())\n .filter((tok) => tok.length > 0);\n if (tokens.length === 0) return null;\n return Object.freeze(tokens);\n }\n return null;\n}\n\n/**\n * Parse the `handoff-input-filter` field into a structured\n * declaration. Returns `null` for unsupported shapes; callers should\n * attach a diagnostic when the return value is `null` and the source\n * value was non-undefined.\n *\n * @stable\n */\nexport function parseHandoffInputFilter(value: unknown): HandoffInputFilterDeclaration | null {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed === 'lastUser') return Object.freeze({ kind: 'lastUser' });\n if (trimmed === 'summary') return Object.freeze({ kind: 'summary' });\n if (trimmed === 'full') return Object.freeze({ kind: 'full' });\n const lastN = /^lastN-(\\d+)$/u.exec(trimmed);\n if (lastN !== null) {\n const n = Number.parseInt(lastN[1] ?? '0', 10);\n if (Number.isFinite(n) && n > 0) return Object.freeze({ kind: 'lastN', n });\n }\n return null;\n }\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n const obj = value as Record<string, unknown>;\n if (Array.isArray(obj.compose)) {\n const steps: HandoffInputFilterStep[] = [];\n for (const raw of obj.compose) {\n const step = parseHandoffInputFilterStep(raw);\n if (step === null) return null;\n steps.push(step);\n }\n return Object.freeze({ kind: 'compose', steps: Object.freeze(steps) });\n }\n }\n return null;\n}\n\nfunction parseHandoffInputFilterStep(raw: unknown): HandoffInputFilterStep | null {\n if (typeof raw === 'string') {\n if (raw === 'lastUser') return Object.freeze({ kind: 'lastUser' });\n if (raw === 'summary') return Object.freeze({ kind: 'summary' });\n if (raw === 'stripReasoning') return Object.freeze({ kind: 'stripReasoning' });\n return null;\n }\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n const obj = raw as Record<string, unknown>;\n if (typeof obj.lastN === 'number' && Number.isFinite(obj.lastN) && obj.lastN > 0) {\n return Object.freeze({ kind: 'lastN', n: obj.lastN });\n }\n if (\n obj.stripSensitiveOutputs !== undefined &&\n typeof obj.stripSensitiveOutputs === 'object' &&\n obj.stripSensitiveOutputs !== null\n ) {\n const inner = obj.stripSensitiveOutputs as Record<string, unknown>;\n const keepTier = typeof inner.keepTier === 'string' ? inner.keepTier : undefined;\n return Object.freeze({\n kind: 'stripSensitiveOutputs',\n ...(keepTier === undefined ? {} : { keepTier }),\n });\n }\n if (typeof obj.lastUser === 'boolean') return Object.freeze({ kind: 'lastUser' });\n if (typeof obj.summary === 'boolean') return Object.freeze({ kind: 'summary' });\n if (typeof obj.stripReasoning === 'boolean') return Object.freeze({ kind: 'stripReasoning' });\n }\n return null;\n}\n\n/**\n * Parse the `tools` field. Accepts either an array of strings (tool\n * names — the loader resolves modules through naming convention) or\n * an array of objects with `name`, `module`, `description`, `tags`.\n * Returns `null` for unsupported shapes.\n *\n * @stable\n */\nexport function parseToolsField(value: unknown): ReadonlyArray<SkillToolDeclaration> | null {\n if (!Array.isArray(value)) return null;\n const out: SkillToolDeclaration[] = [];\n for (const entry of value) {\n if (typeof entry === 'string') {\n if (entry.trim().length === 0) return null;\n out.push(Object.freeze({ name: entry.trim() }));\n continue;\n }\n if (entry !== null && typeof entry === 'object' && !Array.isArray(entry)) {\n const obj = entry as Record<string, unknown>;\n if (typeof obj.name !== 'string' || obj.name.trim().length === 0) return null;\n const declaration: Mutable<SkillToolDeclaration> = { name: obj.name.trim() };\n if (typeof obj.module === 'string' && obj.module.trim().length > 0)\n declaration.module = obj.module.trim();\n if (typeof obj.description === 'string') declaration.description = obj.description;\n if (Array.isArray(obj.tags) && obj.tags.every((t) => typeof t === 'string')) {\n declaration.tags = Object.freeze([...(obj.tags as string[])]);\n }\n out.push(Object.freeze(declaration as SkillToolDeclaration));\n continue;\n }\n return null;\n }\n return Object.freeze(out);\n}\n\n/**\n * Best-effort semver-range satisfaction check. Supports the patterns\n * the framework actually emits (`^x.y.z`, `~x.y.z`, `>=x.y.z`,\n * `>x.y.z`, `<=x.y.z`, `<x.y.z`, plain `x.y.z`, the AND combinator\n * with whitespace) without pulling a runtime dependency on `semver`.\n * Unrecognised inputs return `false` so the validator emits a typed\n * diagnostic.\n *\n * @stable\n */\nexport function isRuntimeCompatSatisfied(range: string, version: string): boolean {\n const versionTuple = parseSemver(version);\n if (versionTuple === null) return false;\n const expressions = range.split(/\\s+/u).filter((seg) => seg.length > 0);\n for (const expression of expressions) {\n if (!evaluateSemverExpression(expression, versionTuple)) return false;\n }\n return true;\n}\n\nfunction evaluateSemverExpression(\n expression: string,\n version: readonly [number, number, number],\n): boolean {\n if (expression.startsWith('^')) {\n const target = parseSemver(expression.slice(1));\n if (target === null) return false;\n if (target[0] === 0 && target[1] === 0) {\n return version[0] === 0 && version[1] === 0 && version[2] === target[2];\n }\n if (target[0] === 0) {\n return version[0] === 0 && version[1] === target[1] && cmpTuple(version, target) >= 0;\n }\n return version[0] === target[0] && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('~')) {\n const target = parseSemver(expression.slice(1));\n if (target === null) return false;\n return version[0] === target[0] && version[1] === target[1] && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('>=')) {\n const target = parseSemver(expression.slice(2));\n return target !== null && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('>')) {\n const target = parseSemver(expression.slice(1));\n return target !== null && cmpTuple(version, target) > 0;\n }\n if (expression.startsWith('<=')) {\n const target = parseSemver(expression.slice(2));\n return target !== null && cmpTuple(version, target) <= 0;\n }\n if (expression.startsWith('<')) {\n const target = parseSemver(expression.slice(1));\n return target !== null && cmpTuple(version, target) < 0;\n }\n if (expression === '*') return true;\n const target = parseSemver(expression);\n return target !== null && cmpTuple(version, target) === 0;\n}\n\nfunction parseSemver(value: string): [number, number, number] | null {\n const trimmed = value.trim();\n // Strip optional `v` prefix and any pre-release / build metadata —\n // we only care about the canonical `MAJOR.MINOR.PATCH` triple.\n const stripped = trimmed.replace(/^v/u, '').split(/[-+]/u, 1)[0] ?? '';\n const parts = stripped.split('.');\n if (parts.length < 1 || parts.length > 3) return null;\n const [major, minor, patch] = [parts[0] ?? '0', parts[1] ?? '0', parts[2] ?? '0'];\n if (!/^\\d+$/u.test(major) || !/^\\d+$/u.test(minor) || !/^\\d+$/u.test(patch)) return null;\n return [Number.parseInt(major, 10), Number.parseInt(minor, 10), Number.parseInt(patch, 10)];\n}\n\nfunction cmpTuple(\n a: readonly [number, number, number],\n b: readonly [number, number, number],\n): number {\n if (a[0] !== b[0]) return a[0] - b[0];\n if (a[1] !== b[1]) return a[1] - b[1];\n return a[2] - b[2];\n}\n\nfunction isRecognisedField(field: string): boolean {\n if (getKnownField(field) !== undefined) return true;\n if (getGraphorinMapping(field) !== undefined) return true;\n if (field === 'metadata') return true;\n return false;\n}\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["parsed: unknown","parseYaml","conflictingSources: string[]","conflictPolicy: FrontmatterValidatorPolicy","unknownFieldPolicy: UnknownFieldPolicy","diagnostics: FrontmatterDiagnostic[]","severity: 'warn' | 'error'","steps: HandoffInputFilterStep[]","out: SkillToolDeclaration[]","declaration: Mutable<SkillToolDeclaration>","target"],"sources":["../../src/frontmatter/index.ts"],"sourcesContent":["/**\n * Frontmatter validator for `SKILL.md` files.\n *\n * Implements the field-resolution algorithm from ADR-043:\n *\n * 1. Anthropic-base (top-level, no prefix) - highest priority.\n * 2. `metadata.graphorin.<field>` bucket per upstream `metadata`\n * convention.\n * 3. `graphorin-<field>` legacy top-level prefix.\n * 4. caller-supplied fallback.\n *\n * The validator surfaces every diagnostic through the typed\n * {@link FrontmatterDiagnostic} contract so callers can decide whether\n * to log, fail, or escalate without re-parsing human strings.\n *\n * @packageDocumentation\n */\n\nimport { parse as parseYaml } from 'yaml';\n\nimport { SkillManifestParseError } from '../errors/index.js';\nimport {\n compareAuthorSpecHint,\n getGraphorinMapping,\n getKnownField,\n getSpecSnapshot,\n} from '../spec/index.js';\nimport type {\n FieldResolution,\n FrontmatterDiagnostic,\n FrontmatterValidatorPolicy,\n HandoffInputFilterDeclaration,\n HandoffInputFilterStep,\n SkillToolDeclaration,\n UnknownFieldPolicy,\n} from '../types/index.js';\n\n/** Result of {@link splitSkillMd}. */\nexport interface SplitSkillMd {\n readonly frontmatter: string;\n readonly body: string;\n}\n\n/**\n * Split a raw SKILL.md string into the YAML frontmatter and the\n * markdown body. The frontmatter delimiter is the canonical\n * `---\\n…\\n---\\n` pair.\n *\n * @stable\n */\nexport function splitSkillMd(skillMd: string): SplitSkillMd {\n const normalized = skillMd.replace(/^\\uFEFF/u, '').replace(/\\r\\n/g, '\\n');\n const match = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/u.exec(normalized);\n if (match === null) {\n throw new SkillManifestParseError(\n 'SKILL.md must begin with a YAML frontmatter block delimited by `---` lines.',\n );\n }\n return { frontmatter: match[1] ?? '', body: match[2] ?? '' };\n}\n\n/**\n * Parse the YAML frontmatter into a record. Returns `{}` for an empty\n * block.\n *\n * @stable\n */\nexport function parseFrontmatterYaml(frontmatter: string): Record<string, unknown> {\n if (frontmatter.trim().length === 0) return {};\n let parsed: unknown;\n try {\n parsed = parseYaml(frontmatter);\n } catch (err) {\n throw new SkillManifestParseError('SKILL.md frontmatter is not valid YAML.', { cause: err });\n }\n if (parsed === null || parsed === undefined) return {};\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new SkillManifestParseError(\n `Top-level SKILL.md frontmatter must be an object; got '${Array.isArray(parsed) ? 'array' : typeof parsed}'.`,\n );\n }\n return parsed as Record<string, unknown>;\n}\n\n/**\n * Resolve a single field across the four field-resolution tiers.\n * Returns the resolved value plus the source tier the value came from\n * AND the list of conflicting source names so the validator can\n * surface a structured diagnostic.\n *\n * @stable\n */\nexport function resolveSkillField<T = unknown>(\n frontmatter: Record<string, unknown>,\n field: string,\n fallback?: T,\n): FieldResolution<T> {\n const conflictingSources: string[] = [];\n const presentInBase = field in frontmatter;\n const meta = frontmatter.metadata;\n const metaRecord =\n meta !== undefined && meta !== null && typeof meta === 'object' && !Array.isArray(meta)\n ? (meta as Record<string, unknown>)\n : undefined;\n // mcp-skills-09 (F-10): the docs' preferred authoring form is the\n // NESTED object (`metadata: { graphorin: { sensitivity: ... } }`),\n // which YAML parses to a nested record - the old resolver only read\n // the flat dotted key (`metadata: { 'graphorin.sensitivity': ... }`)\n // and silently dropped nested values. Both shapes now resolve; the\n // flat key wins when both are present (it was the only working form).\n const nested = metaRecord?.graphorin;\n const nestedRecord =\n nested !== undefined && nested !== null && typeof nested === 'object' && !Array.isArray(nested)\n ? (nested as Record<string, unknown>)\n : undefined;\n const presentInMetaFlat = metaRecord !== undefined && `graphorin.${field}` in metaRecord;\n const presentInMetaNested = nestedRecord !== undefined && field in nestedRecord;\n const presentInMeta = presentInMetaFlat || presentInMetaNested;\n const presentInPrefix = `graphorin-${field}` in frontmatter;\n if (presentInBase) conflictingSources.push(field);\n if (presentInMeta) conflictingSources.push(`metadata.graphorin.${field}`);\n if (presentInPrefix) conflictingSources.push(`graphorin-${field}`);\n const conflicting = conflictingSources.length > 1;\n\n if (presentInBase) {\n return Object.freeze({\n value: frontmatter[field] as T,\n source: 'anthropic-base' as const,\n conflicting,\n conflictingSources,\n });\n }\n if (presentInMeta) {\n return Object.freeze({\n value: (presentInMetaFlat\n ? (metaRecord as Record<string, unknown>)[`graphorin.${field}`]\n : (nestedRecord as Record<string, unknown>)[field]) as T,\n source: 'metadata-graphorin' as const,\n conflicting,\n conflictingSources,\n });\n }\n if (presentInPrefix) {\n return Object.freeze({\n value: frontmatter[`graphorin-${field}`] as T,\n source: 'graphorin-prefix' as const,\n conflicting,\n conflictingSources,\n });\n }\n return Object.freeze({\n value: fallback,\n source: 'fallback' as const,\n conflicting: false,\n conflictingSources: [],\n });\n}\n\n/**\n * Options accepted by {@link validateFrontmatter}.\n *\n * @stable\n */\nexport interface ValidateFrontmatterOptions {\n /** Policy for direct collisions (Anthropic-base + `graphorin-*`). */\n readonly conflictPolicy?: FrontmatterValidatorPolicy;\n /** Policy for fields not part of the bundled snapshot or `graphorin-*` catalogue. */\n readonly unknownFieldPolicy?: UnknownFieldPolicy;\n /**\n * Installed Graphorin runtime version. Used to validate\n * `graphorin-runtime-compat` declarations against the running\n * framework.\n */\n readonly runtimeVersion?: string;\n}\n\n/** Successful return of {@link validateFrontmatter}. */\nexport interface ValidatedFrontmatter {\n readonly raw: Readonly<Record<string, unknown>>;\n readonly diagnostics: ReadonlyArray<FrontmatterDiagnostic>;\n readonly resolved: {\n readonly name: FieldResolution<unknown>;\n readonly description: FieldResolution<unknown>;\n readonly license: FieldResolution<unknown>;\n readonly compatibility: FieldResolution<unknown>;\n readonly metadata: FieldResolution<unknown>;\n readonly allowedTools: FieldResolution<unknown>;\n readonly disableModelInvocation: FieldResolution<unknown>;\n readonly trustLevel: FieldResolution<unknown>;\n readonly runtimeCompat: FieldResolution<unknown>;\n readonly sensitivity: FieldResolution<unknown>;\n readonly sensitivityDefaults: FieldResolution<unknown>;\n readonly sandbox: FieldResolution<unknown>;\n readonly handoffInputFilter: FieldResolution<unknown>;\n readonly anthropicSpec: FieldResolution<unknown>;\n readonly version: FieldResolution<unknown>;\n readonly tools: FieldResolution<unknown>;\n };\n}\n\n/**\n * Validate a parsed frontmatter against the bundled spec snapshot and\n * the `graphorin-*` extension catalogue.\n *\n * @stable\n */\nexport function validateFrontmatter(\n frontmatter: Record<string, unknown>,\n options: ValidateFrontmatterOptions = {},\n): ValidatedFrontmatter {\n const conflictPolicy: FrontmatterValidatorPolicy = options.conflictPolicy ?? 'warn';\n const unknownFieldPolicy: UnknownFieldPolicy = options.unknownFieldPolicy ?? 'preserve';\n const diagnostics: FrontmatterDiagnostic[] = [];\n\n const resolveAndDiag = <T = unknown>(field: string, fallback?: T): FieldResolution<T> => {\n const resolution = resolveSkillField<T>(frontmatter, field, fallback);\n if (resolution.conflicting) {\n const message =\n `Both '${resolution.conflictingSources.join(\"' and '\")}' are set for '${field}'. ` +\n `The Anthropic-base / metadata.graphorin.* tier wins; remove the lower-priority field to silence this diagnostic.`;\n const hint = `Keep the highest-priority entry: '${resolution.source === 'anthropic-base' ? field : resolution.source === 'metadata-graphorin' ? `metadata.graphorin.${field}` : `graphorin-${field}`}'.`;\n const severity: 'warn' | 'error' = conflictPolicy === 'error' ? 'error' : 'warn';\n if (conflictPolicy !== 'silent') {\n diagnostics.push(\n Object.freeze({\n kind: 'conflict',\n field,\n severity,\n message,\n hint,\n }),\n );\n }\n }\n return resolution;\n };\n\n const name = resolveAndDiag<string>('name');\n const description = resolveAndDiag<string>('description');\n const license = resolveAndDiag<string>('license');\n const compatibility = resolveAndDiag<string>('compatibility');\n const metadata = resolveAndDiag<Record<string, unknown>>('metadata');\n const allowedTools = resolveAndDiag<unknown>('allowed-tools');\n const disableModelInvocation = resolveAndDiag<boolean>('disable-model-invocation', false);\n const trustLevel = resolveAndDiag<string>('trust-level');\n const runtimeCompat = resolveAndDiag<string>('runtime-compat');\n // The graphorin-only `version` field carries runtime-compat semantics; the\n // Anthropic-base `metadata.version` is the skill's own version. We honour\n // both - the loader uses `runtimeCompat` for compat, `version` for\n // graphorin-runtime-compat-as-version aliasing.\n const version = resolveAndDiag<string>('version');\n const sensitivity = resolveAndDiag<string>('sensitivity');\n const sensitivityDefaults = resolveAndDiag<Record<string, string>>('sensitivity-defaults');\n const sandbox = resolveAndDiag<Record<string, unknown>>('sandbox');\n const handoffInputFilter = resolveAndDiag<unknown>('handoff-input-filter');\n const anthropicSpec = resolveAndDiag<string>('anthropic-spec');\n const tools = resolveAndDiag<unknown>('tools');\n\n if (typeof name.value !== 'string' || name.value.trim().length === 0) {\n diagnostics.push(\n Object.freeze({\n kind: 'missing-required-field',\n field: 'name',\n severity: 'error',\n message: \"Required field 'name' is missing or empty.\",\n hint: \"Add a unique 'name' (kebab-case is conventional) to the SKILL.md frontmatter.\",\n }),\n );\n }\n if (typeof description.value !== 'string' || description.value.trim().length === 0) {\n diagnostics.push(\n Object.freeze({\n kind: 'missing-required-field',\n field: 'description',\n severity: 'error',\n message: \"Required field 'description' is missing or empty.\",\n hint: 'Add a description that explains when the model should activate the skill.',\n }),\n );\n }\n if (allowedTools.value !== undefined) {\n const list = parseAllowedToolsValue(allowedTools.value);\n if (list === null) {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-field-type',\n field: 'allowed-tools',\n severity: 'warn',\n message:\n \"'allowed-tools' must be a string ('read_file write_file') or an array (['read_file', 'write_file']).\",\n hint: 'Switch to one of the two supported shapes; entries are split on whitespace for the string form.',\n }),\n );\n }\n }\n if (\n allowedTools.value !== undefined &&\n getKnownField('allowed-tools')?.stability === 'experimental'\n ) {\n diagnostics.push(\n Object.freeze({\n kind: 'experimental-field',\n field: 'allowed-tools',\n severity: 'info',\n message:\n \"'allowed-tools' is marked experimental in the bundled spec snapshot; behaviour may evolve.\",\n }),\n );\n }\n if (anthropicSpec.value !== undefined && typeof anthropicSpec.value === 'string') {\n const compare = compareAuthorSpecHint(anthropicSpec.value);\n if (compare === 'newer') {\n diagnostics.push(\n Object.freeze({\n kind: 'spec-newer-than-loader',\n field: 'anthropic-spec',\n severity: 'warn',\n message: `Skill targets spec snapshot '${anthropicSpec.value}', newer than the bundled '${getSpecSnapshot().snapshotDate}'. Unknown fields will be preserved leniently.`,\n }),\n );\n } else if (compare === 'older') {\n diagnostics.push(\n Object.freeze({\n kind: 'spec-older-than-loader',\n field: 'anthropic-spec',\n severity: 'info',\n message: `Skill targets spec snapshot '${anthropicSpec.value}', older than the bundled '${getSpecSnapshot().snapshotDate}'.`,\n }),\n );\n } else if (compare === 'unparseable') {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-field-type',\n field: 'anthropic-spec',\n severity: 'warn',\n message: `'anthropic-spec' must be an ISO-8601 date string (YYYY-MM-DD); got '${String(anthropicSpec.value)}'.`,\n }),\n );\n }\n }\n if (\n runtimeCompat.value !== undefined &&\n typeof runtimeCompat.value === 'string' &&\n options.runtimeVersion !== undefined\n ) {\n if (!isRuntimeCompatSatisfied(runtimeCompat.value, options.runtimeVersion)) {\n diagnostics.push(\n Object.freeze({\n kind: 'invalid-runtime-compat',\n field: 'runtime-compat',\n severity: 'error',\n message: `Skill declares runtime-compat '${runtimeCompat.value}' which does not match installed Graphorin '${options.runtimeVersion}'.`,\n hint: 'Adjust the skill or upgrade Graphorin so the declared range covers the installed version.',\n }),\n );\n }\n }\n for (const key of Object.keys(frontmatter)) {\n if (isRecognisedField(key)) continue;\n if (key.startsWith('graphorin-')) {\n // `graphorin-*` fields not in the mapping table are tolerated as\n // opaque metadata per Phase 08 § Frontmatter validator. Emit a\n // single info-level diagnostic so loaders can surface them.\n if (unknownFieldPolicy === 'reject') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'error',\n message: `Unknown 'graphorin-*' field '${key}' rejected by unknownFieldPolicy='reject'.`,\n }),\n );\n } else if (unknownFieldPolicy === 'warn') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'warn',\n message: `Unknown 'graphorin-*' field '${key}'. Will be preserved as opaque metadata.`,\n }),\n );\n }\n continue;\n }\n if (unknownFieldPolicy === 'reject') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'error',\n message: `Unknown frontmatter field '${key}' rejected by unknownFieldPolicy='reject'.`,\n }),\n );\n } else if (unknownFieldPolicy === 'warn') {\n diagnostics.push(\n Object.freeze({\n kind: 'unknown-field',\n field: key,\n severity: 'warn',\n message: `Unknown frontmatter field '${key}'. Will be preserved as opaque metadata.`,\n }),\n );\n }\n }\n\n return Object.freeze({\n raw: Object.freeze({ ...frontmatter }),\n diagnostics: Object.freeze(diagnostics),\n resolved: Object.freeze({\n name,\n description,\n license,\n compatibility,\n metadata,\n allowedTools,\n disableModelInvocation,\n trustLevel,\n runtimeCompat,\n sensitivity,\n sensitivityDefaults,\n sandbox,\n handoffInputFilter,\n anthropicSpec,\n version,\n tools,\n }),\n });\n}\n\n/**\n * Parse the `allowed-tools` field. Accepts either a string (with\n * whitespace-separated entries) or a string array. Returns `null` for\n * unsupported shapes so the validator can attach a typed diagnostic.\n *\n * @stable\n */\nexport function parseAllowedToolsValue(value: unknown): ReadonlyArray<string> | null {\n if (Array.isArray(value)) {\n if (!value.every((entry) => typeof entry === 'string' && entry.trim().length > 0)) {\n return null;\n }\n return Object.freeze(value.map((entry) => entry.trim()) as string[]);\n }\n if (typeof value === 'string') {\n const tokens = value\n .split(/\\s+/u)\n .map((tok) => tok.trim())\n .filter((tok) => tok.length > 0);\n if (tokens.length === 0) return null;\n return Object.freeze(tokens);\n }\n return null;\n}\n\n/**\n * Parse the `handoff-input-filter` field into a structured\n * declaration. Returns `null` for unsupported shapes; callers should\n * attach a diagnostic when the return value is `null` and the source\n * value was non-undefined.\n *\n * @stable\n */\nexport function parseHandoffInputFilter(value: unknown): HandoffInputFilterDeclaration | null {\n if (typeof value === 'string') {\n const trimmed = value.trim();\n if (trimmed === 'lastUser') return Object.freeze({ kind: 'lastUser' });\n if (trimmed === 'summary') return Object.freeze({ kind: 'summary' });\n if (trimmed === 'full') return Object.freeze({ kind: 'full' });\n const lastN = /^lastN-(\\d+)$/u.exec(trimmed);\n if (lastN !== null) {\n const n = Number.parseInt(lastN[1] ?? '0', 10);\n if (Number.isFinite(n) && n > 0) return Object.freeze({ kind: 'lastN', n });\n }\n return null;\n }\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n const obj = value as Record<string, unknown>;\n if (Array.isArray(obj.compose)) {\n const steps: HandoffInputFilterStep[] = [];\n for (const raw of obj.compose) {\n const step = parseHandoffInputFilterStep(raw);\n if (step === null) return null;\n steps.push(step);\n }\n return Object.freeze({ kind: 'compose', steps: Object.freeze(steps) });\n }\n }\n return null;\n}\n\nfunction parseHandoffInputFilterStep(raw: unknown): HandoffInputFilterStep | null {\n if (typeof raw === 'string') {\n if (raw === 'lastUser') return Object.freeze({ kind: 'lastUser' });\n if (raw === 'summary') return Object.freeze({ kind: 'summary' });\n if (raw === 'stripReasoning') return Object.freeze({ kind: 'stripReasoning' });\n return null;\n }\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n const obj = raw as Record<string, unknown>;\n if (typeof obj.lastN === 'number' && Number.isFinite(obj.lastN) && obj.lastN > 0) {\n return Object.freeze({ kind: 'lastN', n: obj.lastN });\n }\n if (\n obj.stripSensitiveOutputs !== undefined &&\n typeof obj.stripSensitiveOutputs === 'object' &&\n obj.stripSensitiveOutputs !== null\n ) {\n const inner = obj.stripSensitiveOutputs as Record<string, unknown>;\n const keepTier = typeof inner.keepTier === 'string' ? inner.keepTier : undefined;\n return Object.freeze({\n kind: 'stripSensitiveOutputs',\n ...(keepTier === undefined ? {} : { keepTier }),\n });\n }\n if (typeof obj.lastUser === 'boolean') return Object.freeze({ kind: 'lastUser' });\n if (typeof obj.summary === 'boolean') return Object.freeze({ kind: 'summary' });\n if (typeof obj.stripReasoning === 'boolean') return Object.freeze({ kind: 'stripReasoning' });\n }\n return null;\n}\n\n/**\n * Parse the `tools` field. Accepts either an array of strings (tool\n * names - the loader resolves modules through naming convention) or\n * an array of objects with `name`, `module`, `description`, `tags`.\n * Returns `null` for unsupported shapes.\n *\n * @stable\n */\nexport function parseToolsField(value: unknown): ReadonlyArray<SkillToolDeclaration> | null {\n if (!Array.isArray(value)) return null;\n const out: SkillToolDeclaration[] = [];\n for (const entry of value) {\n if (typeof entry === 'string') {\n if (entry.trim().length === 0) return null;\n out.push(Object.freeze({ name: entry.trim() }));\n continue;\n }\n if (entry !== null && typeof entry === 'object' && !Array.isArray(entry)) {\n const obj = entry as Record<string, unknown>;\n if (typeof obj.name !== 'string' || obj.name.trim().length === 0) return null;\n const declaration: Mutable<SkillToolDeclaration> = { name: obj.name.trim() };\n if (typeof obj.module === 'string' && obj.module.trim().length > 0)\n declaration.module = obj.module.trim();\n if (typeof obj.description === 'string') declaration.description = obj.description;\n if (Array.isArray(obj.tags) && obj.tags.every((t) => typeof t === 'string')) {\n declaration.tags = Object.freeze([...(obj.tags as string[])]);\n }\n out.push(Object.freeze(declaration as SkillToolDeclaration));\n continue;\n }\n return null;\n }\n return Object.freeze(out);\n}\n\n/**\n * Best-effort semver-range satisfaction check. Supports the patterns\n * the framework actually emits (`^x.y.z`, `~x.y.z`, `>=x.y.z`,\n * `>x.y.z`, `<=x.y.z`, `<x.y.z`, plain `x.y.z`, the AND combinator\n * with whitespace) without pulling a runtime dependency on `semver`.\n * Unrecognised inputs return `false` so the validator emits a typed\n * diagnostic.\n *\n * @stable\n */\nexport function isRuntimeCompatSatisfied(range: string, version: string): boolean {\n const versionTuple = parseSemver(version);\n if (versionTuple === null) return false;\n const expressions = range.split(/\\s+/u).filter((seg) => seg.length > 0);\n for (const expression of expressions) {\n if (!evaluateSemverExpression(expression, versionTuple)) return false;\n }\n return true;\n}\n\nfunction evaluateSemverExpression(\n expression: string,\n version: readonly [number, number, number],\n): boolean {\n if (expression.startsWith('^')) {\n const target = parseSemver(expression.slice(1));\n if (target === null) return false;\n if (target[0] === 0 && target[1] === 0) {\n return version[0] === 0 && version[1] === 0 && version[2] === target[2];\n }\n if (target[0] === 0) {\n return version[0] === 0 && version[1] === target[1] && cmpTuple(version, target) >= 0;\n }\n return version[0] === target[0] && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('~')) {\n const target = parseSemver(expression.slice(1));\n if (target === null) return false;\n return version[0] === target[0] && version[1] === target[1] && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('>=')) {\n const target = parseSemver(expression.slice(2));\n return target !== null && cmpTuple(version, target) >= 0;\n }\n if (expression.startsWith('>')) {\n const target = parseSemver(expression.slice(1));\n return target !== null && cmpTuple(version, target) > 0;\n }\n if (expression.startsWith('<=')) {\n const target = parseSemver(expression.slice(2));\n return target !== null && cmpTuple(version, target) <= 0;\n }\n if (expression.startsWith('<')) {\n const target = parseSemver(expression.slice(1));\n return target !== null && cmpTuple(version, target) < 0;\n }\n if (expression === '*') return true;\n const target = parseSemver(expression);\n return target !== null && cmpTuple(version, target) === 0;\n}\n\nfunction parseSemver(value: string): [number, number, number] | null {\n const trimmed = value.trim();\n // Strip optional `v` prefix and any pre-release / build metadata -\n // we only care about the canonical `MAJOR.MINOR.PATCH` triple.\n const stripped = trimmed.replace(/^v/u, '').split(/[-+]/u, 1)[0] ?? '';\n const parts = stripped.split('.');\n if (parts.length < 1 || parts.length > 3) return null;\n const [major, minor, patch] = [parts[0] ?? '0', parts[1] ?? '0', parts[2] ?? '0'];\n if (!/^\\d+$/u.test(major) || !/^\\d+$/u.test(minor) || !/^\\d+$/u.test(patch)) return null;\n return [Number.parseInt(major, 10), Number.parseInt(minor, 10), Number.parseInt(patch, 10)];\n}\n\nfunction cmpTuple(\n a: readonly [number, number, number],\n b: readonly [number, number, number],\n): number {\n if (a[0] !== b[0]) return a[0] - b[0];\n if (a[1] !== b[1]) return a[1] - b[1];\n return a[2] - b[2];\n}\n\nfunction isRecognisedField(field: string): boolean {\n if (getKnownField(field) !== undefined) return true;\n if (getGraphorinMapping(field) !== undefined) return true;\n if (field === 'metadata') return true;\n return false;\n}\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { FieldStability, GraphorinFieldPolicy, GraphorinMappingEntry, KnownField
|
|
|
11
11
|
//#region src/index.d.ts
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* `@graphorin/skills`
|
|
14
|
+
* `@graphorin/skills` - skills surface for the Graphorin framework.
|
|
15
15
|
*
|
|
16
16
|
* The package owns:
|
|
17
17
|
*
|
|
@@ -45,8 +45,7 @@ import { FieldStability, GraphorinFieldPolicy, GraphorinMappingEntry, KnownField
|
|
|
45
45
|
*
|
|
46
46
|
* @packageDocumentation
|
|
47
47
|
*/
|
|
48
|
-
|
|
49
|
-
declare const VERSION = "0.5.0";
|
|
48
|
+
declare const VERSION: string;
|
|
50
49
|
//#endregion
|
|
51
50
|
export { ActivatedSkill, ActivationRequest, FieldResolution, FieldStability, FrontmatterDiagnostic, FrontmatterValidatorPolicy, GraphorinFieldPolicy, GraphorinMappingEntry, GraphorinSkillsError, GraphorinSkillsErrorKind, HandoffInputFilterDeclaration, HandoffInputFilterStep, InlineSkill, InlineSkillTool, InputFilterRequiredError, KnownFieldEntry, LoadSkillOptions, LoadSkillsOptions, MigrateFrontmatterOptions, MigrationResult, MigrationRewrite, ParsedActivationTrigger, RegisteredToolDeclaration, ResolvedSkillTrustPolicy, Skill, SkillFrontmatterConflictError, SkillLoadError, SkillManifestParseError, SkillMetadata, SkillNameCollisionError, SkillRegistry, SkillRegistryOptions, SkillRequiredFieldMissingError, SkillResource, SkillRuntimeCompatError, SkillSignatureVerificationResult, SkillSource, SkillToolDeclaration, SkillToolStamper, SkillTrustLevel, SkillTrustLevelValue, SkillsTrustLevel, SlashCommandActivation, SlashCommandParseError, SpecSnapshot, SplitSkillMd, StampedSkillTool, SupplyChainSkillSource, UnknownFieldPolicy, VERSION, ValidateFrontmatterOptions, ValidatedFrontmatter, _setSpecSnapshotForTesting, compareAuthorSpecHint, createSkillRegistry, getGraphorinMapping, getKnownField, getSpecSnapshot, isRuntimeCompatSatisfied, isSlashCommand, loadSkillFromSource, loadSkills, migrateFrontmatter, parseActivationTrigger, parseAllowedToolsValue, parseFrontmatterYaml, parseHandoffInputFilter, parseSlashCommand, parseToolsField, requireHandoffInputFilter, resolveSkillField, sortKeysAnthropicFirst, splitSkillMd, stampSkillTool, stampSkillToolFromMetadata, validateFrontmatter };
|
|
52
51
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuCA;;;;;;;;;;;;;;;;;;;;;;;;cAAa"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { version } from "./package.js";
|
|
1
2
|
import { GraphorinSkillsError, InputFilterRequiredError, SkillFrontmatterConflictError, SkillLoadError, SkillManifestParseError, SkillNameCollisionError, SkillRequiredFieldMissingError, SkillRuntimeCompatError, SlashCommandParseError } from "./errors/index.js";
|
|
2
3
|
import { isSlashCommand, parseSlashCommand } from "./activation/index.js";
|
|
3
4
|
import { _setSpecSnapshotForTesting, compareAuthorSpecHint, getGraphorinMapping, getKnownField, getSpecSnapshot } from "./spec/index.js";
|
|
@@ -9,7 +10,7 @@ import { createSkillRegistry, parseActivationTrigger } from "./registry/index.js
|
|
|
9
10
|
|
|
10
11
|
//#region src/index.ts
|
|
11
12
|
/**
|
|
12
|
-
* `@graphorin/skills`
|
|
13
|
+
* `@graphorin/skills` - skills surface for the Graphorin framework.
|
|
13
14
|
*
|
|
14
15
|
* The package owns:
|
|
15
16
|
*
|
|
@@ -43,8 +44,8 @@ import { createSkillRegistry, parseActivationTrigger } from "./registry/index.js
|
|
|
43
44
|
*
|
|
44
45
|
* @packageDocumentation
|
|
45
46
|
*/
|
|
46
|
-
/** Canonical version constant
|
|
47
|
-
const VERSION =
|
|
47
|
+
/** Canonical version constant, derived from `package.json` at build time. */
|
|
48
|
+
const VERSION = version;
|
|
48
49
|
|
|
49
50
|
//#endregion
|
|
50
51
|
export { GraphorinSkillsError, InputFilterRequiredError, SkillFrontmatterConflictError, SkillLoadError, SkillManifestParseError, SkillNameCollisionError, SkillRequiredFieldMissingError, SkillRuntimeCompatError, SlashCommandParseError, VERSION, _setSpecSnapshotForTesting, compareAuthorSpecHint, createSkillRegistry, getGraphorinMapping, getKnownField, getSpecSnapshot, isRuntimeCompatSatisfied, isSlashCommand, loadSkillFromSource, loadSkills, migrateFrontmatter, parseActivationTrigger, parseAllowedToolsValue, parseFrontmatterYaml, parseHandoffInputFilter, parseSlashCommand, parseToolsField, requireHandoffInputFilter, resolveSkillField, sortKeysAnthropicFirst, splitSkillMd, stampSkillTool, stampSkillToolFromMetadata, validateFrontmatter };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/skills`
|
|
1
|
+
{"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@graphorin/skills` - skills surface for the Graphorin framework.\n *\n * The package owns:\n *\n * - `SKILL.md` loader with three-tier progressive disclosure\n * (metadata always available; body and resources lazy-loaded).\n * - Frontmatter validator implementing the field-resolution\n * algorithm and conflict policy from ADR-043.\n * - Bundled snapshot of the publicly published `SKILL.md` packaging-\n * format specification + the framework-specific extension\n * catalogue.\n * - Slash-command parser (`/skill:<name>`) + auto-activation\n * metadata generator.\n * - `SkillRegistry` with name-collision detection, activation\n * resolution, and a typed tool-declaration surface the agent\n * runtime bridges into the `@graphorin/tools` registry.\n * - Idempotent `migrateFrontmatter` library that rewrites legacy\n * `graphorin-*` fields onto their upstream equivalents per the\n * bundled mapping table.\n *\n * Stable sub-paths:\n *\n * ```ts\n * import { loadSkills, loadSkillFromSource } from '@graphorin/skills/loader';\n * import { createSkillRegistry } from '@graphorin/skills/registry';\n * import { validateFrontmatter, resolveSkillField } from '@graphorin/skills/frontmatter';\n * import { migrateFrontmatter } from '@graphorin/skills/migration';\n * import { parseSlashCommand } from '@graphorin/skills/activation';\n * import { getSpecSnapshot, getKnownField } from '@graphorin/skills/spec';\n * import { SkillFrontmatterConflictError } from '@graphorin/skills/errors';\n * ```\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport * from './activation/index.js';\nexport * from './errors/index.js';\nexport * from './frontmatter/index.js';\nexport * from './loader/index.js';\nexport * from './migration/index.js';\nexport * from './registry/index.js';\nexport * from './spec/index.js';\nexport * from './types/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAaA,UAAkBC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/loader/index.ts"],"sourcesContent":[],"mappings":";;;;;;UAyEiB,gBAAA;4BACW;gCACI;;+BAED;oBACX;;;;;UAMH,iBAAA,SAA0B;;;;;;;;;;;;;;;;iBAwCrB,mBAAA,SACZ,uBACC,mBACR,QAAQ;;;;;;;;;iBAmEW,UAAA,UACX,cAAc,wBACd,oBACR,QAAQ,cAAc;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/loader/index.ts"],"sourcesContent":[],"mappings":";;;;;;UAyEiB,gBAAA;4BACW;gCACI;;+BAED;oBACX;;;;;UAMH,iBAAA,SAA0B;;;;;;;;;;;;;;;;iBAwCrB,mBAAA,SACZ,uBACC,mBACR,QAAQ;;;;;;;;;iBAmEW,UAAA,UACX,cAAc,wBACd,oBACR,QAAQ,cAAc;;;;;;;;;;iBA+kBT,yBAAA,WAAoC,gBAAgB"}
|
package/dist/loader/index.js
CHANGED
|
@@ -138,7 +138,7 @@ async function loadFromFolder(folderPath, source, options, precomputedSignature)
|
|
|
138
138
|
}
|
|
139
139
|
const parsed = parseAndValidate(skillMd, options);
|
|
140
140
|
const { diagnostics, body } = parsed;
|
|
141
|
-
const effectiveTrust = extractTrustLevel(source) ??
|
|
141
|
+
const effectiveTrust = extractTrustLevel(source) ?? capSelfDeclaredTrust(parsed.metadata.graphorinTrustLevel);
|
|
142
142
|
const metadata = effectiveTrust === parsed.metadata.graphorinTrustLevel ? parsed.metadata : Object.freeze({
|
|
143
143
|
...parsed.metadata,
|
|
144
144
|
graphorinTrustLevel: effectiveTrust
|
|
@@ -409,8 +409,8 @@ function extractTrustLevel(source) {
|
|
|
409
409
|
if (source.kind === "npm-package" || source.kind === "git-repo" || source.kind === "folder") return source.trustLevel;
|
|
410
410
|
}
|
|
411
411
|
/**
|
|
412
|
-
* Cap a folder skill's self-declared trust level. A directory on disk
|
|
413
|
-
* possibly downloaded from the internet
|
|
412
|
+
* Cap a folder skill's self-declared trust level. A directory on disk -
|
|
413
|
+
* possibly downloaded from the internet - cannot self-promote to a trusted
|
|
414
414
|
* tier without an operator override (RP-9): `trusted` /
|
|
415
415
|
* `trusted-with-scripts` collapse to `'unknown'` (sandbox forced, signature
|
|
416
416
|
* optional, outputs taint-marked), while `untrusted` / `unknown` pass
|