Skip to content

feat(client): Agent Skills value types - #29

Merged
XieX merged 2 commits into
xie/agent-skills-feature-ac9ac7from
xie/skills-01-types-frontmatter
Sep 18, 2026
Merged

XieX merged 2 commits into
xie/agent-skills-feature-ac9ac7from
xie/skills-01-types-frontmatter

Conversation

@XieX

@XieX XieX commented Aug 25, 2026

Copy link
Copy Markdown

Stacked PR 1 of 7 — merge bottom-up into xie/agent-skills-feature-ac9ac7.

  1. feat(client): Agent Skills value types #29 — value types ← you are here
  2. feat(client): symlink-refusing filesystem primitives #30 — symlink-refusing filesystem primitives
  3. feat(client): Agent Skills store seam, verification, and accessors #31 — store seam, verification, accessors
  4. test(client): integrity verification and the accessor telemetry sweep #32 — integrity + telemetry tests
  5. feat(client): writeSkills materialization and the abuse matrix #33 — writeSkills + abuse matrix
  6. test(client): the rest of the writeSkills suite #34 — remaining writeSkills tests
  7. docs(client): document Agent Skills #35 — docs

Each PR targets the one below it, so GitHub already shows only this PR's own diff.


First of seven stacked PRs landing Agent Skills. The feature was built as one-way-dependent layers, and the stack follows that layering so each piece can be read on its own. This one is the leaf: nothing here imports another skills module.

What's here

types.ts gains the four frozen value types — SkillReference, Skill, ReconcileAction, ReconcileReport — plus the untrusted wire shape RawSkillObject, the structural SkillStore seam, the freezing factories, and the key/version validators. parseAiConfig now applies them to a variation's skills array.

That validation fails closed: one malformed reference fails the whole parse. Silently dropping it would materialize a partial skill set without telling anyone.

Skills are opaque byte buffers — by construction

Skill.content is a Uint8Array: the verified verbatim bytes, exactly what was hashed, with no encoding or file-format claim attached. The SDK never interprets skill content anywhere — there is deliberately no frontmatter accessor and no YAML dependency in this package. A consumer that wants structure (frontmatter, markdown, anything else) decodes and parses the bytes itself, with whatever parser and bounds it trusts.

One TypedArray caveat is documented at the factory: Object.freeze throws on an array-buffer view, so the frozen Skill wrapper makes the content binding immutable while the bytes themselves are shared with the caller.

Cross-language contract

Every type and field name here, including the camelCase wire field contentHash, is an identical string to the Python SDK, which carries the mirror bytes-typed content. A polyglot fleet has to agree on the shape it exchanges, so changing one is a cross-language breaking change.

The accessor outcome type (security review LA-2)

SkillOutcome and SkillOutcomeReason land here with the other value types, plus the createSkillOutcome factory that freezes them like every sibling. They are consumed in #31 by getSkillResult.

SkillOutcomeReason is a closed five-token vocabulary — absent, integrity_failure, ok, store_unavailable, wrong_version — listed alphabetically, and the Python SDK publishes the same five for the same conditions. It is deliberately coarser than the eight-token IntegrityReasonCode from the log record: the integrity tokens say which check failed and go to the operator, these say what the caller got.

SkillStore.getObject also gains its third parameter here — getObject(kind, key, version?), matching Python's get_object(kind, key, version=None). A store may hold several versions of one key and only the store can pick between them; without the parameter, a pin the store could have satisfied gets reported as a version mismatch. The post-hoc equality check at the accessor boundary is kept regardless, because the store is untrusted.

Verification

Client package 294 → 342 tests. typecheck and biome clean.

🤖 Generated with Claude Code


Note

Overview
Introduces the Agent Skills foundation in the client package: frozen value types (SkillReference, Skill, reconcile/report types, SkillOutcome/SkillStore seam), factories, and strict validators for skill keys (^[a-z0-9][a-z0-9-]*$, max 256 chars) and versions (integer ≥ 1).

AiConfigRep gains an optional skills array of { key, version } references. parseAiConfig now validates that array when present and fails the entire config on any bad entry (no silent partial skill sets). InitBaseClientOptions documents an optional skillStore hook for later accessors.

Public index exports the new types plus createSkill / createSkillReference. Tests cover exhaustive skills parse rules in schema.test.ts and immutability / ReconcileReport.ok / errors behavior in new skills.test.ts.

Reviewed by Cursor Bugbot for commit 98c9853. Bugbot is set up for automated code reviews on this repo. Configure here.

First of a stack that lands Agent Skills as one-way-dependent layers. This is
the leaf: nothing here imports another skills module, so it reads on its own.

`types.ts` gains the four frozen value types — `SkillReference`, `Skill`,
`ReconcileAction`, `ReconcileReport` — plus the untrusted wire shape
`RawSkillObject`, the structural `SkillStore` seam, the freezing factories, and
the key/version validators. `parseAiConfig` now applies them to a variation's
`skills` array, and it fails **closed**: one malformed reference fails the whole
parse, because silently dropping it would materialize a partial skill set
without telling anyone.

Every one of these names, and the camelCase wire field `contentHash`, is an
identical string to the Python SDK. A polyglot fleet has to agree on the shape
it exchanges, so changing one is a cross-language breaking change.

`Skill.content` is `Uint8Array` — the verified verbatim bytes, exactly what was
hashed, with no encoding or file-format claim. The SDK never interprets skill
content: there is deliberately no frontmatter parser and no YAML dependency
anywhere in the package. Consumers that want structure parse the bytes
themselves.

Client package: 294 -> 342 tests. typecheck and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@XieX
XieX force-pushed the xie/skills-01-types-frontmatter branch from 89009c4 to 3c6cc27 Compare August 31, 2026 18:10
@XieX
XieX marked this pull request as ready for review September 14, 2026 20:34
…ific

Review feedback: code comments in this SDK should not lean on another
SDK as the explanation.

SkillOutcomeReason's five-token vocabulary is a real cross-language
contract, so the constraint stays — it is just stated as a property of
every language implementation rather than of the Python SDK
specifically. Same for the integer rule in the schema test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@XieX
XieX merged commit 2d5712d into xie/agent-skills-feature-ac9ac7 Sep 18, 2026
8 checks passed
@XieX
XieX deleted the xie/skills-01-types-frontmatter branch September 18, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants