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

cmaform

v0.2.0

Published

Terraform-style declarative management for Anthropic Managed Agents, Skills, Memory Stores, Environments, and Vaults.

Readme

cmaform is a Terraform-style CLI for managing Anthropic Managed Agents, Skills, Memory Stores, Environments, and Vaults as files in your repo. You declare each resource as a YAML file, run cmaform plan to see what will change, and cmaform apply to ship it.

  [~] update agent       "release-prep" (id=agent_01Qx..., version=6)
       file: agents/release-prep.yaml
       ~ system:
           ... (12 unchanged lines)
         - prior wording...
         + revised wording...
           ... (8 unchanged lines)
  [+] create skill       "spec-lookup"
       dir:  skills/spec-lookup
       hash: 7b8b14094e01...

Plan (agents):         0 to add, 1 to change, 0 to archive, 5 unchanged.
Plan (skills):         1 to add, 0 to change, 0 to delete, 0 unchanged.
Plan (memory_stores):  0 to add, 0 to change, 0 to archive, 0 unchanged.
Plan (environments):   0 to add, 0 to change, 0 to archive, 1 unchanged.
Plan (vaults):         0 to archive, 1 unchanged.

⚡ Quick Start

Try it first

npx cmaform --help

Install and use

npm install -g cmaform

Bootstrap a new config directory and import an existing managed agent

export ANTHROPIC_API_KEY=sk-ant-...

mkdir my-agents && cd my-agents
cmaform pull agent_011CaSWcCrMdQdp4SA6TVdH6   # writes agents/<name>.yaml + state
cmaform plan                                  # show the diff
cmaform apply                                 # confirm → push to Anthropic

Anthropic's Managed Agent / Skills / Memory Stores / Environments / Vaults APIs are currently beta. cmaform calls @anthropic-ai/sdk's beta.agents.* / beta.skills.* / beta.memoryStores.* / beta.environments.* / beta.vaults.* endpoints directly.

🚀 Usage

Commands

| Command | What it does | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cmaform pull <id> [--by-id] | Import a remote resource by ID (agent_* / skill_* / memstore_* / env_* / vlt_*) into local files + state. Pass --by-id to keep raw IDs in the written agent YAML instead of rewriting multiagent.agents[] / skills[] to the name form | | cmaform plan [--verbose\|-v] [target...] | Diff local YAML / state / remote and print a Terraform-style plan | | cmaform apply [--yes\|-y] [--verbose\|-v] [target...] | Show plan, prompt for confirmation, apply, save state | | cmaform sync [--by-id] | Re-fetch every entry in state from remote and rewrite local YAML. Pass --by-id to keep raw IDs in the written agent YAML instead of rewriting refs to the name form | | cmaform init | Initialize / reconcile the state file against remote (no remote writes; spirit of terraform init) | | cmaform list | Show local files / state / remote side-by-side | | cmaform fmt | Rewrite multiagent.agents[].id / skills[].skill_id in local YAML to the name form, using cmaform.state.json for the id → name lookup |

Filtering plan / apply

The last positional arguments to plan and apply are targets. A target can be a resource kind or an individual resource name — useful for staged rollouts (e.g. create a skill first, copy its skill_id into an agent YAML, then apply the agent).

cmaform apply skills                     # all skills
cmaform apply agents                     # all agents
cmaform apply slack-mention-lookup       # a single skill by name
cmaform apply release-prep --yes         # a single agent, skip confirmation
cmaform apply skills release-prep        # all skills + one agent

A <target> is either a kind alias (matches every resource of that type) or the individual name of one specific resource. Use the table below for kind aliases; anything else is treated as a resource name.

| Resource | Kind aliases | Individual name format | Example | | ------------ | ----------------------------------------------------------- | ------------------------------------- | ------------------------------------------------------- | | Agent | agent / agents | name: field in the YAML | release-prep (agents/release-prep.yaml) | | Skill | skill / skills | directory name under skills/ | slack-mention-lookup (skills/slack-mention-lookup/) | | Memory Store | memory_store / memory_stores / memstore / memstores | directory name under memory_stores/ | team-notes (memory_stores/team-notes/) | | Environment | environment / environments / env / envs | directory name under environments/ | python-dev (environments/python-dev/) | | Vault | vault / vaults | directory name under vaults/ | my-bot (vaults/my-bot/) |

plan expands create / update diffs symmetrically: a new resource is rendered as + field: ... blocks just like an update is rendered as ~ field: ... blocks. Long string fields (system, description) are truncated to 3 lines plus an ... (N lines hidden) marker. Pass --verbose to show full content.

If you pass an individual name that doesn't exist anywhere (local YAML, state, or remote), cmaform exits with code 2. An unmatched kind is fine — it just shows 0 to add, 0 to change, ....

Pulling existing resources

cmaform pull agent_011CaSWcCrMdQdp4SA6TVdH6   # writes agents/<name>.yaml
cmaform pull skill_013uPS15B3Kw82NpjH4uNQep   # state only — SKILL.md is not regenerated
cmaform pull memstore_01ABC...                # writes memory_stores/<name>/manifest.yaml
cmaform pull env_015G...                      # writes environments/<name>/manifest.yaml
cmaform pull vlt_011CaQ...                    # writes vaults/<name>/manifest.yaml (credentials not managed yet)

Skill content is not returned by the Anthropic API once uploaded, so pull for skills only records the ID + version + display title into state. The local SKILL.md must be authored by you.

📦 Resources

cmaform manages five resource types — agents, skills, memory stores, environments, and vaults. Each lives under its own top-level directory beneath the config root. (Create vaults with cmaform, then finish configuring them on the Anthropic Console.)

Agent (agents/<name>.yaml)

name: my-agent # unique within the workspace — this is the identity key
model:
  id: claude-sonnet-4-6
  speed: standard # standard | fast
description: short description
system: |-
  ... full system prompt ...
mcp_servers:
  - name: slack
    type: url
    url: https://mcp.slack.com/mcp
tools:
  - type: agent_toolset_20260401
    default_config:
      enabled: true
      permission_policy:
        type: always_allow
    configs: []
skills: []
metadata: {}
  • Identity: the name field. agent_id lives in state, not YAML.
  • Diff: deep-equal on name / model / description / system / tools / mcp_servers / skills / multiagent / metadata.
  • Delete: present in state but missing locally ⇒ archive (reversible).
  • Arrays (tools, mcp_servers, skills) are fully replaced — local is the source of truth.

See the Anthropic Agent Setup docs for the full schema.

Skill (skills/<localName>/)

Each skill is a directory. The Anthropic API uploads a folder containing SKILL.md plus any auxiliary files.

skills/<localName>/
├── SKILL.md            # required (YAML frontmatter + markdown body)
├── REFERENCE.md        # optional
└── scripts/
    └── helper.py       # optional

SKILL.md requires frontmatter:

---
name: my-skill
description: What this skill does, and when Claude should use it.
---

# My Skill

...
  • name: ≤64 chars, [a-z0-9-] only. anthropic / claude are reserved.
  • description: ≤1024 chars.

cmaform computes a SHA-256 hash of the entire directory and compares it against the hash stored in state. If they differ, apply uploads a new version of the skill.

Referencing a skill from an agent

# agents/foo.yaml
skills:
  - type: anthropic
    skill_id: xlsx # well-known Anthropic skills stay id-based
  - type: custom
    name: slack-mention-lookup # = local directory name under skills/
    version: latest # optional

The raw-ID form (skill_id: skill_01XXXXXX) is also accepted for type: customname and skill_id are interchangeable. See Name-based references below for resolution semantics.

⚠️ Skills cannot be archived. Deleting the directory and running apply permanently deletes the skill and all of its versions.

🔗 Name-based references

multiagent.agents[] and skills[] accept references by logical name in addition to raw IDs. Names are resolved at plan / apply time, so YAML can reference resources without committing any workspace-specific IDs.

# agents/coordinator.yaml
multiagent:
  type: coordinator
  agents:
    - type: agent
      name: spec-qa # = the `name` field of agents/spec-qa.yaml
    - type: agent
      name: release-prep
      version: latest # optional
skills:
  - type: custom
    name: slack-mention-lookup # = the directory name under skills/

Resolution order for each name:

  1. cmaform.state.json — if the resource is already tracked locally, use its ID.
  2. Remote — findAgentByName / findSkillByDisplayTitle (cached per run).
  3. Local apply set — if a YAML for that name exists locally and is being created in this run, the reference is treated as a forward dependency. cmaform substitutes a placeholder during plan and replaces it with the real ID right after the dependency is created.

If none of the above match, plan / apply fails with an error pointing at the unresolved name.

pull / sync write back in name form

When writing remote agents back to YAML, cmaform replaces any id / skill_id that resolves to a known local name with the name form. IDs for resources not tracked in state are kept as-is.

Pass --by-id to pull / sync to opt out of the rewrite and keep raw IDs in the written YAML.

Raw-ID form

{ type: agent, id: agent_... } and { type: custom, skill_id: skill_... } are also accepted and behave identically to the name form.

If an entry writes both name: and id: (or skill_id:), cmaform resolves the name and asserts that it matches the pinned ID. Mismatches abort plan / apply with a clear error. This is useful as a safety net while migrating from id-based references — cmaform fmt can then drop the redundant raw IDs once everything verifies.

type: anthropic skills (well-known IDs like xlsx) always use the skill_id form. type: self agent references take neither.

Memory Store (memory_stores/<localName>/manifest.yaml)

name: my-store
description: optional
metadata:
  team: platform
  • Diff fields: name / description / metadata.
  • metadata is patched: keys missing locally are deleted, others are upserted.
  • Delete = archive (one-way; the store's memory data is preserved).

Environment (environments/<localName>/manifest.yaml)

name: python-dev
description: optional
config:
  type: cloud
  packages:
    pip:
      - pandas
      - numpy==2.2.0
    npm:
      - express
  networking:
    type: limited
    allowed_hosts:
      - https://api.example.com
    allow_mcp_servers: true
    allow_package_managers: true
metadata: {}
  • Diff fields: name / description / metadata / config.
  • Only config.type: cloud is currently supported (self-hosted environments are out of scope).
  • Empty package lists, false defaults in limited networking, and the server-side type: 'packages' marker are normalized away so the plan is idempotent.
  • Delete = archive. Archived environments stop accepting new sessions but existing sessions keep working.

Vault (vaults/<localName>/)

Status: ⚠️ Partial support — vault design is still evolving in cmaform.

| Operation | Supported by cmaform | Notes | | ------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Create vault | ✅ | New local manifest → vault is created on the server. | | Archive vault | ✅ | Removing a local manifest archives the vault. Cascades server-side to attached credentials. | | Update vault | ❌ | Edits to display_name / metadata after creation are silently ignored. Archive and recreate to rename / relabel. | | Manage credentials | ❌ | Credentials are not yet managed by cmaform — the secret-resolution design is still being settled. Use the Anthropic Vault Credentials API directly to attach / rotate credentials. |

The vault scope will be expanded (update, credential management with secret backends) in a future release.

A vault is a container that holds credentials for MCP servers. The local layout is intentionally minimal in this release:

vaults/
└── my-bot/
    └── manifest.yaml

manifest.yaml — the vault definition:

display_name: my-bot
metadata:
  external_user_id: bot

🗂️ Directory Layout

cmaform reads from the current working directory (or CMAFORM_DIR if set):

<cwd>/
├── agents/
│   └── *.yaml
├── skills/
│   └── <localName>/SKILL.md
├── memory_stores/
│   └── <localName>/manifest.yaml
├── environments/
│   └── <localName>/manifest.yaml
├── vaults/
│   └── <localName>/manifest.yaml
└── cmaform.state.json

🧾 State File (cmaform.state.json)

{
  "agents": {
    "release-prep": { "id": "agent_01Qx...", "version": 6 }
  },
  "skills": {
    "slack-mention-lookup": {
      "id": "skill_013uPS...",
      "version": "1778647403232223",
      "hash": "7b8b14094e01...",
      "display_title": "slack-mention-lookup"
    }
  },
  "memory_stores": {
    "team-notes": { "id": "memstore_01...", "name": "team-notes" }
  },
  "environments": {
    "python-dev": { "id": "env_01...", "name": "python-dev" }
  },
  "vaults": {
    "my-bot": { "id": "vlt_01...", "display_name": "my-bot" }
  }
}
  • Maintained by pull / apply / sync / init.
  • Recommended to .gitignore (treat as the local source of truth, like a Terraform state file).
  • If someone shares a state file with you, run cmaform sync to regenerate the local YAML for every agent (skill bodies cannot be restored — see above).

🔐 Environment Variables

| Variable | Required | Purpose | | ------------------- | -------- | -------------------------------------- | | ANTHROPIC_API_KEY | ✅ | Anthropic API authentication | | CMAFORM_DIR | | Config root directory (default: cwd) |

⚠️ Caveats

  • Renaming an agent creates a new agent. To rename, delete the old YAML, run apply (archives the old one), then add the new YAML with the new name.
  • Deleting a skill directory and running apply is destructive — there is no archive for skills.
  • For full reproducibility, avoid making manual changes in the Anthropic Console. Always go through the YAML / SKILL.md / manifest.yaml.

🛠️ Development

mise install
pnpm install
pnpm dev -- --help     # run from source (tsx)
pnpm typecheck
pnpm build             # bundle to dist/cli.js via tsup
node dist/cli.js --help

📋 Requirements

  • Node.js ≥ 22
  • An Anthropic API key with access to the Managed Agent / Skills / Memory Stores / Environments / Vaults beta

📄 License

MIT