npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fam-tung-lam/ptlam-agent-plugin-compiler

v0.3.0

Published

Deterministic compiler for PTLam-compatible agent plugin projects.

Readme

Agent Plugin Compiler

Author once. Validate the graph. Compile every host manifest.

npm version CI status Required Node.js version License

Docs · Quick Start · Reference

Markdown-based agent skills have no build step. Agent Plugin Compiler adds one: declare your skills, dependencies, lifecycle, and portable hooks in a single manifest, then compile self-contained public skills, a visual catalog, and the plugin manifests for Claude, Codex, Copilot, Gemini, and Kimi.

Full docs at agent-plugin-compiler.phamtunglam.com.

What you get

  • A validated skill graph. Missing, duplicate, self-referencing, circular, and invalid lifecycle dependencies fail before generated files change.
  • Complete installable skills. Every public root recursively carries its required skills, generated dependency instructions, frontmatter, and supporting files.
  • A reviewable catalog. skills/README.md combines an installable-skill table with a Mermaid dependency graph grouped by category and labelled with lifecycle status and visibility.
  • Publication and invocation controls. Visibility and lifecycle metadata decide what ships, while invocation metadata can keep supported-host workflows explicit-only.
  • Portable hooks. One ordered handler tree maps 19 universal events to each selected host where equivalent native semantics exist.
  • Five built-in hosts and an extension seam. Generate Claude, Codex, GitHub Copilot CLI, Gemini CLI, and Kimi Code CLI output, or register a custom provider adapter through the Node.js API.
  • Deterministic, bounded output. Exact ownership, drift detection, atomic managed-path writes, and post-write verification make committed output safe to review and straightforward to enforce in CI.

The problem

An agent skill is a directory with a Markdown file in it. That is the whole format. There is no place to record what a skill depends on, nothing that resolves a reference, and no build step that fails when a reference goes stale.

Suppose a plugin publishes two skills, and skill-a cannot do its job unless skill-b runs first:

skills/
├── skill-a/
│   └── SKILL.md   ← names skill-b, in prose
└── skill-b/
    └── SKILL.md

The dependency exists only as sentences inside skill-a/SKILL.md:

# Skill A

Summarize the release.

Run `skill-b` first to collect the commit facts, because the summary must not
invent them. Pass the table it returns into step 2 unchanged. Its input format
is described in [skill-b](../skill-b/SKILL.md).

1. Read the milestone.
2. Group the commit facts by area.

That paragraph hard-codes four separate facts: the other skill's name, why it is required, how to call it, and where it lives. Nothing checks any of them, and the same paragraph is copied into every other skill with the same dependency. Two failures follow.

  1. Users install incomplete skills. Installers show a flat list. Someone installs skill-a alone, and its first instruction refers to something they do not have. Nothing warned them, because nothing in the directory records that skill-a is incomplete on its own.
  2. Authors break dependencies silently. Rename skill-b to collect-commit-facts and the prose is wrong. Nothing fails: not the editor, not the tests, not the publish step. The plugin ships, and the agent follows an instruction that points at a skill that does not exist. Retiring skill-b, changing what it returns, or forgetting one of the copies produces the same silent result.

In a programming language none of this survives to release: the module system resolves the import, the compiler rejects the missing symbol, the linter flags the dead reference. A folder of Markdown files has none of those guarantees, and neither a human nor an AI agent reliably keeps every hand-written cross-reference synchronized.

This package supplies the missing guarantees.

The build step

---
config:
  htmlLabels: false
---
flowchart TD
    subgraph Authored["You author"]
        PluginManifest["plugin/plugin.yml"]
        SkillSources["plugin/skills/**"]
        HookSources["plugin/hooks/**"]
    end

    AgentPluginCompiler{{"plugin-compiler"}}

    subgraph Generated["The compiler owns"]
        SharedSkills["skills/**"]
        SharedHooks["hooks/handlers/** (when needed)"]
        ClaudePlugin[".claude-plugin/**"]
        CodexPlugin[".codex-plugin/plugin.json"]
        CopilotPlugin["plugin.json"]
        GeminiExtension["gemini-extension.json"]
        KimiPlugin["kimi.plugin.json"]
    end

    PluginManifest --> AgentPluginCompiler
    SkillSources --> AgentPluginCompiler
    HookSources --> AgentPluginCompiler
    AgentPluginCompiler --> SharedSkills
    AgentPluginCompiler --> SharedHooks
    AgentPluginCompiler --> ClaudePlugin
    AgentPluginCompiler --> CodexPlugin
    AgentPluginCompiler --> CopilotPlugin
    AgentPluginCompiler --> GeminiExtension
    AgentPluginCompiler --> KimiPlugin

plugin/plugin.yml declares every skill, its visibility, its lifecycle status, and its dependency edges:

skills:
  - id: skill-b
    description: The reusable building block.
    category_id: example
    visibility: internal
    status: active
    required_skills: []

  - id: skill-a
    description: The skill users install.
    category_id: example
    visibility: public
    status: active
    required_skills:
      - skill_id: skill-b
        reason: skill-a cannot produce a correct result without it.
        instructions: Run skill-b first and pass its output forward.

  - id: skill-c
    description: A standalone skill with no dependencies.
    category_id: example
    visibility: public
    status: active
    required_skills: []

The same manifest can key ordered handler lists directly by universal event. Handler paths are relative to plugin/hooks/:

schema_version: 2

hooks:
  sessionStart:
    - handler: observability/audit.mjs
  userPromptSubmit:
    - handler: observability/audit.mjs
    - handler: simple-logger/request.mjs
  preToolUse:
    - handler: observability/audit.mjs
  postToolUseFailure:
    - handler: observability/audit.mjs
  permissionRequest:
    - handler: observability/audit.mjs
  stop:
    - handler: simple-logger/response.mjs
    - handler: observability/audit.mjs
  setup:
    - handler: observability/audit.mjs

The compiler copies those handlers once into hooks/handlers/** and translates each universal event into semantically equivalent provider-native events. Hook compatibility is evaluated per handler registration: an adapter still compiles its other output and reports unsupported events as skipped. Hooks do not have a required flag, do not become fallback skills, and do not write provider instruction files.

Everything under plugin/ is source you edit. Everything the compiler owns is a build result — never patch a generated skill or manifest by hand, change the source and compile again. The compiler always owns the root skills/ tree. Built-in provider adapters retain stable exact-file ownership for files that carry native hook configuration, so removing a hook cleans stale native output. The compiler owns the shared hooks/handlers/** tree only while at least one selected provider supports an authored event; when no effective hooks remain, an existing shared handler tree becomes unowned rather than being deleted. Every other path stays outside the write plan. Compilation is deterministic, so the same source always produces the same bytes and check can prove that committed output is current.

Invocation controls also let an author mark a workflow as manual-only:

skills:
  - id: deploy
    description: Deploy the application after explicit user confirmation.
    disable_model_invocation: true
    category_id: engineering
    visibility: public
    status: active
    required_skills: []

The compiler emits disable-model-invocation: true in the generated skill frontmatter. Claude Code, GitHub Copilot CLI, and Kimi Code CLI currently honor the restriction. Codex and Gemini CLI accept the skill but ignore this field, so they may still select it automatically.

Schema v2 also lets a skill merge its authored Markdown references into the generated SKILL.md:

skills:
  - id: deploy
    compilation:
      markdown_references: inline

Only Markdown files below plugin/skills/deploy/references/ are merged, in relative-path order. Put <!-- PLUGIN-COMPILER:MARKDOWN-REFERENCES --> in the authored SKILL.md to choose their position; without it, the compiler appends them. Local links are rebased to the generated root. Other resources, including non-Markdown files below references/, keep their relative paths and bytes. Omitting compilation or selecting preserve retains the existing generated bytes and tree.

Dependency instructions are generated

You write plugin/skills/skill-a/SKILL.md without mentioning skill-b at all:

# Skill A

What skill-a does.

<!-- PLUGIN-COMPILER:REQUIRED-SKILLS -->

1. First step.
2. Second step.

compile produces skills/skill-a/SKILL.md:

---
name: skill-a
description: The skill users install.
---

# Skill A

What skill-a does.

## Required skills

### `skill-b`

**Reason:** skill-a cannot produce a correct result without it.

**Instructions:** Run skill-b first and pass its output forward.

Read [skill-b](skills/skill-b/SKILL.md).

1. First step.
2. Second step.

Frontmatter, the dependency section, and the link are all derived from the manifest. Rename skill-b and every dependent skill is rewritten on the next compile. Remove it and validate fails instead of publishing a dangling reference. The <!-- PLUGIN-COMPILER:REQUIRED-SKILLS --> marker is optional and only chooses where the section lands.

Public and internal dependencies

Required skills are copied recursively into every public skill that needs them, so an installed skill is always complete. Visibility decides whether the dependency is also published on its own.

skill-b as internal — a building block, never installed separately:

skills/
├── README.md
├── skill-a/
│   ├── SKILL.md
│   └── skills/
│       └── skill-b/
│           └── SKILL.md
└── skill-c/
    └── SKILL.md

skill-b as public — embedded in skill-a and independently installable:

skills/
├── README.md
├── skill-a/
│   ├── SKILL.md
│   └── skills/
│       └── skill-b/
│           └── SKILL.md
├── skill-b/
│   └── SKILL.md
└── skill-c/
    └── SKILL.md

Either way, installing skill-a alone gets everything it needs. skills/README.md is the generated catalog of published skills. Its Mermaid dependency graph includes every published root and reachable required skill; each skill appears in its category subgraph, arrows point from each dependent skill to its requirement, and each multiline label shows the skill's status and visibility. Styles also distinguish public, internal, and deprecated skills. status controls publication over time: active and deprecated skills are published as root skills, while draft and archived ones are not.

Install

Project dependency

Install the compiler with the plugin project so local development and CI use the same exact version:

npm install --save-dev --save-exact \
  @fam-tung-lam/ptlam-agent-plugin-compiler

Requires Node.js 22.6 or newer. --save-exact pins the stable version resolved from npm. Recompile after every upgrade.

Homebrew CLI

Install the stable CLI globally through the PTLam Homebrew tap:

brew install fam-tung-lam/tap/ptlam-agent-plugin-compiler

Homebrew installs the required Node.js runtime. Use plugin-compiler directly, and let Homebrew manage upgrades and removal:

plugin-compiler --help
brew upgrade fam-tung-lam/tap/ptlam-agent-plugin-compiler
brew uninstall ptlam-agent-plugin-compiler

Homebrew follows stable npm releases but updates independently. Prefer the project dependency above when the compiler version must be committed and reproduced in CI.

Quick start

npm exec -- plugin-compiler init      # scaffold plugin/plugin.yml + example skills
# edit plugin/plugin.yml, plugin/skills/**, and optional plugin/hooks/**
npm exec -- plugin-compiler validate  # check the manifest, sources, and graph
npm exec -- plugin-compiler compile   # write skills/** and host manifests
npm exec -- plugin-compiler check     # confirm output matches the source

init writes a fully commented, schema-valid starter manifest with the three skills used above — an internal dependency, a public skill that requires it, and a standalone public skill. It only creates missing paths, so it is safe to run again.

validate rejects missing, duplicate, self-referencing, and circular dependencies, plus invalid lifecycle edges: an active skill cannot require a draft or archived one, and requiring a deprecated skill raises a warning. check writes nothing, reports every managed path that drifted, and exits 1, which makes it the command to run in CI.

Full walkthrough, with every file to paste → Quick Start.

Commands

| Command | Purpose | | ---------- | -------------------------------------------------------------- | | init | Create missing authored source paths, never overwriting | | validate | Check the manifest, skills, hooks, links, and dependency graph | | compile | Write shared resources plus selected host manifests and hooks | | check | Report managed output that no longer matches authored source |

validate, compile, and check share [--root <path>] [--provider <id>[,<id>...] | --no-providers]. Without a provider option the compiler uses the providers list in plugin/plugin.yml; --provider replaces that list for one run, and --no-providers compiles shared skills only.

Every flag, precedence rule, and exit code → CLI reference.

Supported hosts

| ID | Host | Generated manifests | | --------- | -------------------- | --------------------------------------------------------------- | | claude | Claude plugin | .claude-plugin/plugin.json, .claude-plugin/marketplace.json | | codex | Codex plugin | .codex-plugin/plugin.json | | copilot | GitHub Copilot CLI | plugin.json | | gemini | Gemini CLI extension | gemini-extension.json | | kimi | Kimi Code CLI plugin | kimi.plugin.json |

The portable hook contract accepts the 19 universal events documented in the manifest reference. Each built-in adapter declares the events for which its host has an equivalent. Custom adapters expose supportedHookEvents; omitted events produce structured, non-fatal handler-level skips.

Adapter behavior and custom providers → Providers.

Node.js API

The same pipeline is available programmatically:

import { AgentPluginCompiler } from "@fam-tung-lam/ptlam-agent-plugin-compiler";

const compiler = new AgentPluginCompiler({ rootDir: process.cwd() });

await compiler.validate();
await compiler.compile();

const { upToDate } = await compiler.check();

Pass providers to override the manifest selection, or register your own ProviderAdapter to emit a host the compiler does not ship.

Types, options, and a custom adapter example → Programmatic usage.

Documentation

Contributing

Issues and pull requests are welcome. Start with CONTRIBUTING.md, and report vulnerabilities through the security policy.


License

This project is licensed under the MIT license - see LICENSE for details.