PTENES
MODULE 5.1

πŸ›οΈ The Agent Skills standard, the pain point, and the polyskill architecture

Before the CLI, the concept. Why polyskill exists, what problem it solves, and how its 3-part architecture (IR + adapters + CLI) scales to new runtimes without a rewrite.

7
Topics
45
Minutes
Adv.
Level
Theory
Type

🎯What you get here

Complete mental model of polyskill β€” why it exists, what problem it addresses, and how it works internally. Without this, the CLI in the next lesson becomes "memorized commands." With it, the architecture is clear.

Detailed content

1

πŸ“ The open Agent Skills spec (agentskills.io)

Standard originating at Anthropic and released as an open spec. Adopted by 40+ tools today. Defines the canonical skill format: SKILL.md + YAML frontmatter + Markdown body + folder conventions.

The 4 pillars β€” what ALL runtimes agree on

1
File Name: SKILL.md β€” don't invent variations
2
YAML frontmatter with at least name e description
3
Body in standard Markdown
4
Convention-based folders: scripts/, references/, assets/

πŸ’‘Why this matters

Everything you write within these 4 pillars works in any compatible runtime. Everything OUTSIDE (allowed-tools, backtick-bang, openai.yaml, model override) ties you to a specific runtimeβ€”and that’s where the polyskill work comes in.

Key concepts

Open spec
agentskills.io
40+ runtimes
Broad adoption
4 pillars
Portable core
Extras = glue
Outside the 4 = runtime
2

😩 The pain β€” maintaining two divergent skills

Without understanding the pain, polyskill seems like overkill. This is the story EVERYONE who uses both runtimes goes through:

D1

Day 1 β€” Create a skill in Claude

Works perfectly. You’re happy.

D2

Day 2 β€” Copy/adapt it for Codex

Move it to .agents/skills/, remove the backtick-bang, adjust the description. It works in both. You’re still happy with it.

S2

Week 2 β€” Improve Claude's (forget Codex's)

Find a new use case, add instructions to Claude. Forget to propagate them.

S3

Week 3 β€” Improve Codex's (forget Claude's)

It was in Codex, spotted a bug, fixed it. Forgot to bring the change over to Claude.

M1

Month 1 β€” TWO DIFFERENT skills

The versions diverged in 4 places. You don't remember which one is right. Every time you improve one, you throw away the improvement in the other. Ambiguous source of truth.

πŸ’€Invisible costs of drift

  • β€’ Time wasted deciding "which version is the good one"
  • β€’ Bugs come back (you fixed it in one version and forgot in the other)
  • β€’ Feature works in one agent, but not the other
  • β€’ Team gets confused (skill behaves differently)
  • β€’ Documentation gets outdated on both sides

Key concepts

Organic drift
Inevitable without tooling
Accidental fork
Diverging versions
Ambiguous source
What's the best option?
Cognitive cost
Constant decision-making
3

πŸ’‘ The core idea β€” one source, multiple targets

Polyskill solves the pain point by a compiler turns "portable code". You write the skill ONCE in the canonical portable format (definition.md). The polyskill compiles to dist/claude/ e dist/codex/, each optimized for the target runtime.

Direct analogy β€” Babel/TypeScript

TypeScript:
source.ts β†’ tsc β†’ source.js (ES5) + source.js (ES2020)
Babel:
source.jsx β†’ babel β†’ source.js (target: ie11) + source.js (modern)
Polyskill:
definition.md β†’ polyskill build β†’ dist/claude/SKILL.md + dist/codex/SKILL.md

Same metaphor: canonical source, selective compilation by target, optimized output.

What the structure looks like

minha-skill/
β”œβ”€β”€ definition.md          ← SOURCE canΓ΄nica (vocΓͺ edita aqui)
β”œβ”€β”€ scripts/
β”œβ”€β”€ references/
β”œβ”€β”€ assets/
└── dist/                  ← OUTPUTS (gerados pelo polyskill build)
    β”œβ”€β”€ claude/
    β”‚   └── minha-skill/
    β”‚       └── SKILL.md  ← versΓ£o Claude (com allowed-tools, backtick-bang...)
    └── codex/
        └── minha-skill/
            β”œβ”€β”€ SKILL.md  ← versΓ£o Codex (sem bang, description front-loaded)
            └── agents/
                └── openai.yaml  ← sidecar gerado automaticamente

🎯The "aha moment"

You never edit dist/. Edit only definition.md. Run polyskill build. O dist/ reflects. Runs polyskill install. Both runtimes get the latest version. A single source of truth.

Key concepts

Canonical source
definition.md
Compilation
polyskill build
generated dist/
Don't edit by hand
Optimization/target
By adapter
4

🧱 The 3 pieces β€” IR, adapters, CLI

Inside, polyskill has three parts. Once you understand that, you can see that adding Gemini/Cursor/Copilot is ONE file β€” the adapter. No rewrite.

πŸ›οΈ

1. IR (Intermediate Representation)

A runtime-neutral version of everything a skill needs to be, with NO ties to a specific runtime. It’s the esperanto of polyskill.

πŸ”Œ

2. Adapters

One TypeScript file per runtime. Each adapter implements parse() + emit() + validate(). Plug in β†’ supported. Remove β†’ gone.

⚑

3. CLI

What you run in the terminal. It orchestrates adapters via the registry. It doesn’t know runtime details β€” ask the adapter.

Visual architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ definition.md β”‚ ← YOU edit
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€ portable adapter ─┐
β”‚ Parse (portable)β”‚ ──▢│ parse() / emit() β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ IR (neutral) β”‚ ← internal representation
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ split by target
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚claude adapterβ”‚ β”‚codex adapter β”‚ β”‚gemini adapterβ”‚ ← 1 file each
β”‚ emit() β”‚ β”‚ emit() β”‚ β”‚ emit() β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό β–Ό β–Ό
dist/claude/ dist/codex/ dist/gemini/

The Adapter interface

interface Adapter {
  name: string;

  // LΓͺ o formato do runtime e devolve IR
  parse(path: string): IR;

  // Pega IR e escreve no formato do runtime
  emit(ir: IR, outputDir: string): void;

  // Roda regras especΓ­ficas do runtime (lint)
  validate(ir: IR): ValidationResult;
}

// Registrar Γ© literalmente uma linha:
register(new CodexAdapter());

πŸš€Why this scales

When Gemini CLI gains traction, someone implements src/adapters/gemini.ts with parse/emit/validate, registers in the registry, opens a PR. Polyskill gains Gemini support without changing a single line of core. Same thing for Cursor, Copilot, whatever comes next.

Key concepts

Neutral GO
No runtime lock-in
parse+emit+validate
Adapter contract
Registry pattern
Dynamic plugin
Open core
Via PR adapter
5

πŸ” Round-trip β€” Claude ↔ portable ↔ Codex

The Adapter β€œreads AND writes.” You can import an existing Claude skill (--from claude), make it portable, then emit for both. You don't have to start from scratch.

Round-trip workflows

# Case 1: started in Claude, wants portability
~/.claude/skills/x/ ──▢ claude.parse() ──▢ IR ──▢ portable.emit() ──▢ ./x/definition.md
# Case 2: started in Codex, wants portability
~/.agents/skills/x/ ──▢ codex.parse() ──▢ IR ──▢ portable.emit() ──▢ ./x/definition.md
# Case 3: has portable, wants it for both
./x/definition.md ──▢ portable.parse() ──▢ IR ──▢ claude.emit() + codex.emit()

Claude β†’ portable import

$ polyskill import \
  ~/.claude/skills/x \
  --from claude

Reads SKILL.md + scripts + references. Generates definition.md while preserving everything.

Codex β†’ portable import

$ polyskill import \
  ~/.agents/skills/x \
  --from codex

Reads SKILL.md + sidecar openai.yaml. Normalizes to portable.

Lossless when possible, lossy when needed

Round-trip preserves almost everything:

  • βœ“ scripts/, references/, assets/ pass through intact in both directions
  • βœ“ Portable frontmatter (name, description) is preserved
  • βœ“ Markdown body is preserved

Runtime-specific things become markers in the IR:

  • ⚠️ Claude’s backtick-bang β†’ IR marks it as "dynamic injection" β†’ emitted to Codex as prose
  • ⚠️ Codex’s openai.yaml sidecar β†’ IR marks it as "mcp deps" + "branding" β†’ emitted to Claude as allowed-tools

Key concepts

Bidirectional
Reads AND writes
--from claude/codex
Origin flag
Lossless dirs
scripts/refs/assets
Lossy semantic
Bang β†’ prose
6

πŸ›‘οΈ Drift policy β€” protection against overwriting

Every time it builds, polyskill calculates the hash of the output files. On the next build, if any destination file was edited manually outside polyskill, the build ABORTS with an error. You decide.

The drift workflow

1.
polyskill build β†’ generates dist/claude/x/SKILL.md, stores the hash in .polyskill-hashes
2.
You edit them by hand ~/.claude/skills/x/SKILL.md (specific adjustment)
3.
Days later: polyskill build again
4.
Polyskill compares hash: drift detected. Aborts with a clear message.
5.
You choose: --force (overwrites, loses customization) OR polyskill reconcile (inspects, decides).

βœ“ Why this is safe

  • β€’ Never silently overwrites your work
  • β€’ Drift is SEEN, not hidden
  • β€’ You always have the option to force OR reconcile
  • β€’ Production edits can be detected in the next build

βœ— Without a drift policy, it would be…

  • β€’ A manual adjustment would disappear in the next build
  • β€’ You'd never know (no error)
  • β€’ Trust in the tool would drop
  • β€’ You'd go back to manual maintenance anyway

Output example

$ polyskill build
βœ— Drift detected!

The following targets have been modified outside polyskill:
  - ~/.claude/skills/x/SKILL.md (last hash: a3f9...; current: 8d2b...)

Options:
  - Run `polyskill build --force` to overwrite (loses local edits)
  - Run `polyskill reconcile` to inspect and merge

Build aborted.

Key concepts

Hash file
.polyskill-hashes
No silent loss
Abort first
--force
Opt-in override
reconcile
Interactive merge
7

🦜 The meta-skill β€” polyskill as a skill

Poetic detail: polyskill if it distributes using itself. Comes as an installable skill in both runtimes. You invoke it in natural language; the skill calls the CLI under the hood, translating your request into commands.

πŸ”· Claude Code

/polyskill converts my skill
y-compare to work in both
runtimes

Skill receives NL, translates to:

$ polyskill import \
    ~/.claude/skills/y-compare \
    --from claude
$ polyskill build

🟣 Codex

$polyskill converte minha skill
y-compare pra funcionar nos dois
runtimes

Same skill, same translation:

$ polyskill import \
    ~/.agents/skills/y-compare \
    --from codex
$ polyskill build

Installation paths β€” A vs. B

Path A β€” drag & drop

Copy skill/dist/claude/polyskill to ~/.claude/skills/ e skill/dist/codex/polyskill to ~/.agents/skills/. Without CLI. Only the meta-skill works; the commands it needs to run break.

Path B β€” source + CLI

Clone the repo, npm install && npm run build && npm link. Full CLI on the PATH. To build your own skills, B is required.

πŸ’‘Full dogfooding

Polyskill solves the "cross-runtime skills" problem by using a cross-runtime skill that it generates itself. If the meta-skill works in both runtimes, polyskill proves it works. If it breaks in one, that proves the opposite. Architectural honesty.

Key concepts

Meta-skill
Polyskill as a skill
NL interface
No need to memorize flags
Path A vs. B
Without CLI / with
Dogfooding
Use the tool itself

🎯Module summary

βœ“
Agent Skills is an open spec with 4 portable pillars β€” SKILL.md, name+description, markdown body, convention-based dirs.
βœ“
Drift without tooling is inevitable β€” one week is enough for them to diverge.
βœ“
Canonical source β†’ compiled targets β€” direct analogy with Babel/TypeScript.
βœ“
3 pieces: IR + Adapters + CLI β€” adding a runtime takes ONE file.
βœ“
Bidirectional round-trip β€” starts with Claude or Codex, then supports both.
βœ“
Drift policy = hash + reconcile β€” never silently overwrites.
βœ“
Meta-skill dogfoods polyskill β€” uses its own format to distribute itself.

Next module:

5.2 β€” polyskill CLI in practice (init, import, build, install, validate, reconcile)