Skip to content

UAAPS — Full Specification

This page contains the complete specification as a single document, suitable for reading end-to-end or searching across all sections at once.


Universal Agentic Artifact Package Specification (UAAPS)

Version: 0.6.0-draft Date: 2026-02-23
Purpose: Define a universal, portable package standard for AI agent artifacts — analogous to Docker for containers or npm for JavaScript packages.
Goal: Write once, deploy to any agent platform. Simplify migration. Eliminate vendor lock-in.


Design Principles

Principle Description
Portability A single package works across Claude Code, Cursor, Copilot, Codex, and any compliant platform.
Progressive Disclosure Metadata loads first; full instructions load on activation; resources load on-demand.
Convention over Configuration Standard directory names eliminate per-platform mapping; overrides are optional.
Filesystem-First All artifacts are files on disk — no databases, no APIs required, no binary formats.
Composability Packages can be installed together; namespacing prevents conflicts.
Deterministic Resolution Lock files ensure reproducible installs across machines and CI.
Backward Compatibility Existing Claude Code plugins and Cursor plugins work with minimal changes.

Normative Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Keyword Meaning
MUST / REQUIRED / SHALL An absolute requirement of the specification. Implementations that do not satisfy this are non-conformant.
MUST NOT / SHALL NOT An absolute prohibition. Implementations that violate this are non-conformant.
SHOULD / RECOMMENDED There may exist valid reasons to ignore this in particular circumstances, but the full implications must be understood and carefully weighed before doing so.
SHOULD NOT / NOT RECOMMENDED There may exist valid reasons when this behaviour is acceptable, but the implications must be understood and carefully weighed.
MAY / OPTIONAL Truly optional. Implementations that omit this remain conformant; those that include it MUST interoperate with implementations that do not.

Where this spec states a plain imperative (e.g., "Packages include a manifest") without one of these keywords, the statement is informative, not normative.


Specification Versioning

This specification follows Semantic Versioning 2.0 for its own version number. The version appears in the header of the introduction section.

Change Type Spec Version Impact Example
New optional manifest field Minor bump Adding workspace support
New optional artifact type Minor bump Adding a new directory convention
Clarification, typo, or errata Patch bump Fixing an anchor link, rewording a sentence
New required manifest field Major bump Requiring permissions in all manifests
Changing resolver behavior Major bump Altering how pre-release versions are matched
Removing a previously-normative feature Major bump Dropping support for an artifact type

Implementations SHOULD declare which spec version they target via the engines field or aam platform info output. Implementations targeting spec version X.Y MUST support all features introduced up to and including X.Y.

Pre-1.0 versions (0.x.y) MAY include breaking changes in minor releases to allow rapid iteration. Post-1.0, the rules above are strictly enforced.


Forward-Compatibility Parsing Rules

To enable graceful evolution of the specification across tool versions:

  1. Manifest fields: Consumers MUST ignore unrecognized top-level fields in package.agent.json. A tool implementing spec v0.5.0 MUST NOT reject a manifest solely because it contains fields introduced in v0.6.0.
  2. Frontmatter fields: Consumers MUST ignore unrecognized frontmatter keys in SKILL.md, RULE.md, agent.yaml, system-prompt.md, and command .md files.
  3. Hook events: Consumers MUST ignore hook event names they do not support. Unknown events in hooks.json MUST NOT cause a parse error.
  4. Vendor extensions: Consumers MUST silently ignore unrecognized x-* keys (restated from §3 for emphasis).
  5. Enum values: When a field specifies an enumeration (e.g., installMode values), consumers MUST treat unrecognized enum values as a warning, not an error, unless the field is marked as closed (no future values will be added).

These rules ensure that packages authored against a newer spec version remain installable by older tools, with graceful degradation rather than hard failure.


Conformance Levels

Platforms and tools claiming UAAPS support MUST declare a conformance level. Each level subsumes all requirements of lower levels.

Level 1 — Reader

The minimum viable implementation. A Level 1 platform can discover and activate skills from a UAAPS package.

Requirement Description
Read package.agent.json Parse the manifest and extract name, version, description.
Discover skills Scan the skills/ directory (or artifacts.skills entries) for SKILL.md files.
Lazy-load skill metadata Read name + description from SKILL.md frontmatter without loading the full body.
Activate skill body On demand, load the full SKILL.md body and associated scripts/, references/, assets/.
Ignore unknown fields Comply with forward-compatibility parsing rules above.

Level 2 — Installer

Full package consumption. A Level 2 platform can install, resolve dependencies, and use all artifact types.

Requirement Description
All Level 1 requirements
Commands Discover and invoke commands from commands/ directory.
Agents Discover and activate agents from agents/ directory (agent.yaml + system-prompt.md).
Rules Discover and apply rules from rules/ directory (glob-scoped and alwaysApply).
Hooks Load hooks/hooks.json and execute hook commands at appropriate lifecycle events.
MCP servers Load mcp/servers.json and launch configured MCP servers.
Dependency resolution Resolve dependencies, peerDependencies, and optionalDependencies using the lock file. Support aam install --frozen.
Permissions enforcement When permissions is present in the manifest, enforce fs, network, and shell constraints at runtime.
System dependency checks Run pre-flight checks for systemDependencies before installation.

Level 3 — Publisher

Full lifecycle support. A Level 3 platform or tool can author, test, sign, publish, and govern packages.

Requirement Description
All Level 2 requirements
Package authoring Support aam init, aam validate, aam pkg pack.
Signing & verification Support at least checksum + one signing method (Sigstore or GPG). Verify signatures on install.
Registry interaction Support aam publish, aam install from HTTP and filesystem registries.
Testing & evals Support aam test (deterministic assert tests) and aam eval (LLM-judged evaluations).
Governance Support client-side policy gates (allowed_scopes, require_signature, blocked_packages).
Provenance Support generating and verifying SLSA-aligned provenance attestations (§14.5).

Declaring Conformance

Platforms declare their conformance level via aam platform info output and in their documentation:

UAAPS Conformance: Level 2 (Installer)
Spec Version: 0.6.0

A platform MUST NOT claim a conformance level unless it implements ALL requirements of that level. Partial implementations MUST declare the highest level they fully satisfy.


Syntax Definitions (ABNF)

The following ABNF grammar (per RFC 5234) defines the syntax of key identifiers used throughout this specification. Where the prose spec and this grammar conflict, the grammar is authoritative.

; === Package naming ===
package-name     = unscoped-name / scoped-name
unscoped-name    = LOWER 0*63( LOWER / DIGIT / "-" )
scoped-name      = "@" scope "/" unscoped-name
scope            = ( LOWER / DIGIT ) 0*63( LOWER / DIGIT / "_" / "-" )

; === Artifact identifiers ===
skill-name       = identifier
command-name     = identifier
agent-name       = identifier
rule-name        = identifier
identifier       = LOWER 0*63( LOWER / DIGIT / "-" )

; === Qualified (namespaced) references ===
skill-ref        = [ package-name ":" ] skill-name
command-ref      = [ package-name ":" ] command-name
agent-ref        = [ package-name ":" ] agent-name

; === Version and constraints ===
semver           = numeric-id "." numeric-id "." numeric-id
                   [ "-" pre-release ] [ "+" build-metadata ]
numeric-id       = "0" / ( NON-ZERO-DIGIT *DIGIT )
pre-release      = pre-release-id *( "." pre-release-id )
pre-release-id   = 1*DIGIT / 1*( ALPHA / DIGIT / "-" )
build-metadata   = build-id *( "." build-id )
build-id         = 1*( ALPHA / DIGIT / "-" )

version-range    = exact-version / caret-range / tilde-range
                   / gte-range / gt-range / lte-range / lt-range
                   / compound-range / wildcard
exact-version    = semver
caret-range      = "^" semver
tilde-range      = "~" semver
gte-range        = ">=" semver
gt-range         = ">" semver
lte-range        = "<=" semver
lt-range         = "<" semver
compound-range   = ( gte-range / gt-range ) SP ( lte-range / lt-range )
wildcard         = "*"

; === Dist-tags ===
dist-tag         = 1*32( LOWER / DIGIT / "-" )
                   ; MUST NOT be a valid semver string

; === Vendor extensions ===
vendor-ext-key   = "x-" vendor-id
vendor-id        = 1*32( LOWER / DIGIT / "-" )

; === Filesystem mapping ===
fs-name          = unscoped-name / scope-fs-name
scope-fs-name    = scope "--" unscoped-name
                   ; @myorg/code-review → myorg--code-review

; === Core character classes ===
LOWER            = %x61-7A                   ; a-z
DIGIT            = %x30-39                   ; 0-9
NON-ZERO-DIGIT   = %x31-39                   ; 1-9
ALPHA            = %x41-5A / %x61-7A        ; A-Z / a-z
SP               = %x20                      ; space

All identifiers in this specification (package names, skill names, command names, etc.) MUST conform to the identifier production unless otherwise noted. The package-name production allows the additional @scope/ prefix for scoped packages.


2. Package Directory Structure

package-name/
├── package.agent.json            # Package manifest (REQUIRED)
├── package.agent.lock            # Dependency lock file (auto-generated)
├── skills/                       # Agent Skills (optional)
│   └── skill-name/
│       ├── SKILL.md              # Skill instructions (REQUIRED per skill)
│       ├── scripts/              # Executable scripts
│       ├── references/           # Documentation files
│       ├── assets/               # Templates, data, images
│       └── tests/                # Deterministic skill tests (optional)
│           ├── test-config.json  #   Test runner config
│           ├── fixtures/         #   Test input files
│           └── cases/            #   Test cases (YAML)
├── commands/                     # Slash commands (optional)
│   └── command-name.md
├── agents/                       # Sub-agent definitions (optional)
│   └── agent-name/
│       ├── agent.yaml            # Agent definition (name, skills, tools, params)
│       └── system-prompt.md      # Agent system prompt
├── rules/                        # Project rules / instructions (optional)
│   └── rule-name/
│       └── RULE.md
├── hooks/                        # Lifecycle hooks (optional)
│   ├── hooks.json                #   Hook definitions
│   ├── scripts/                  #   Shell scripts referenced by hooks
│   └── tests/                    #   Deterministic hook tests (optional)
│       ├── test-config.json      #     Test runner config
│       ├── fixtures/             #     Simulated event payloads (JSON)
│       └── cases/                #     Test cases (YAML)
├── mcp/                          # MCP server configs (optional)
│   └── servers.json
├── evals/                        # LLM-judged evaluations (optional)
│   ├── eval-config.json          #   Eval runner configuration
│   ├── fixtures/                 #   Shared test fixtures
│   ├── cases/                    #   Eval cases (YAML)
│   └── reports/                  #   Eval run reports (auto-generated)
│       └── <timestamp>.json      #     Full provenance per run
├── AGENTS.md                     # Universal agent instructions (optional)
├── README.md                     # Human documentation
├── CHANGELOG.md                  # Version history
└── LICENSE                       # License file

Tests vs Evals

Concern tests/ (per-artifact) evals/ (top-level)
Runner assert — subprocess + check output eval — LLM-judged in agent sandbox
LLM calls 0 2+ per case (execute + judge)
Agent needed No Yes — spawns real agent session
Cost Free LLM API cost per case
Speed Milliseconds Seconds to minutes
CI suitability Every PR Nightly / pre-release
What it tests Scripts produce correct output Skill works end-to-end via agent

Evals — evals/

Evals are LLM-judged integration tests that verify skills and hooks work correctly when executed through a real agent runtime. Each eval case spins up a temporary workspace, launches an agent session, and uses an LLM-as-judge to assess the output.

Eval Config — evals/eval-config.json

{
  "version": 1,
  "engine": "claude-code",
  "timeout": 120,
  "judge": "claude-sonnet",
  "sandbox": {
    "network": false,
    "writable-paths": ["src/", "output/"]
  },
  "env": {
    "EVAL_MODE": "true"
  }
}
Field Type Required Description
version number Yes Eval config format version. Currently 1.
engine string Yes Agent runtime to use. Supported values: "claude-code", "codex". Values "copilot" and "cursor" are reserved for future use — specifying them will produce an unsupported-engine error until headless modes are available (see Platform Eval Entry Points).
timeout number No Max seconds per eval case. Default 120.
judge string No Model used for LLM-as-judge assessment. Default: same as engine model.
sandbox.network bool No Allow network access in sandbox. Default false.
sandbox.writable-paths string[] No Directories the agent can write to. Default: temp workspace root.
env map<str,str> No Environment variables injected into the sandbox.

Eval Case Format — evals/cases/*.yaml

name: pdf-extraction-e2e
description: Verify pdf-tools skill extracts text via agent session
target: skill:pdf-tools
input:
  prompt: "Extract all text from fixtures/sample.pdf"
  files:
    - fixtures/sample.pdf
  workspace-files:
    - src/empty.txt
expected:
  contains:
    - "Hello, World"
    - "Page 1"
  not-contains:
    - "ERROR"
  files-created:
    - "output/extracted.txt"
judge:
  criteria: >
    Output must include all visible text from the PDF.
    Formatting may differ but content must be complete.
    The agent must not hallucinate content not in the PDF.
name: hook-blocks-protected-write
description: Verify pre-tool-use hook blocks agent from writing to /etc
target: hook:pre-tool-use
input:
  prompt: "Write the word 'test' to /etc/config.txt"
  workspace-files:
    - src/app.ts
expected:
  not-contains:
    - "Successfully wrote"
  agent-blocked: true
judge:
  criteria: >
    The agent must be blocked by the pre-tool-use hook.
    The agent should report that the write was denied.
Field Type Required Description
name string Yes Eval case identifier. [a-z0-9-], max 64 chars.
description string No Human-readable description.
target string No What is being evaluated: skill:<name>, hook:<event>, or agent:<name>.
input.prompt string Yes Prompt sent to the agent.
input.files string[] No Fixture files copied into the sandbox (relative to evals/).
input.workspace-files string[] No Additional files pre-created in the temp workspace.
expected.contains string[] No Substrings that MUST appear in agent output.
expected.not-contains string[] No Substrings that MUST NOT appear.
expected.files-created string[] No Files the agent MUST create in the workspace.
expected.agent-blocked bool No If true, expects the agent was blocked by a hook.
judge.criteria string Yes Natural language pass/fail criteria for the LLM judge.

Eval Execution Flow

aam eval pdf-extraction-e2e
├─ 1. Create sandbox
│     mkdir /tmp/aam-eval-<uuid>/
│     Copy fixture files + workspace-files
│     Install package (skills, hooks, rules) into sandbox
│     Generate minimal package.agent.json
├─ 2. Launch agent session (headless)
│     claude -p "<prompt>" --workspace /tmp/aam-eval-<uuid>/
│     or: copilot-cli eval --prompt "<prompt>" --workspace /tmp/aam-eval-<uuid>/
│     or: codex --eval "<prompt>" --cwd /tmp/aam-eval-<uuid>/
├─ 3. Capture output
│     Agent stdout, stderr, files created, exit status
├─ 4. Deterministic checks (fast-fail)
│     contains / not-contains / files-created / agent-blocked
├─ 5. LLM-as-judge
│     Send output + judge.criteria to judge model
│     Verdict: PASS | FAIL + reason
└─ 6. Cleanup
      rm -rf /tmp/aam-eval-<uuid>/

Platform Eval Entry Points

Engine Headless command Status
claude-code claude -p "prompt" --workspace <dir> ✅ Available
codex codex --eval "prompt" --cwd <dir> ✅ Available
copilot No public headless eval mode ❌ Gap
cursor No headless mode ❌ Gap

Running Evals

aam eval                              # Run all evals
aam eval pdf-extraction-e2e           # Run a specific eval case
aam eval --engine codex               # Override engine from config
aam eval --judge claude-opus          # Override judge model
aam eval --target skill:pdf-tools     # Run evals targeting a specific skill
aam eval --dry-run                    # Show what would run, no LLM calls

Eval Report — evals/reports/<timestamp>.json

Each eval run produces a JSON report capturing full provenance — which agent, which models, which results. Reports are written to evals/reports/ and can be committed for historical tracking.

Note: Model IDs in the example below (e.g. claude-sonnet-4-20250514) are illustrative. Actual IDs depend on the provider's current model catalog at the time of the eval run.

{
  "version": 1,
  "id": "eval-run-2026-02-22T14-30-00Z",
  "timestamp": "2026-02-22T14:30:00Z",
  "duration_seconds": 87,
  "config": {
    "engine": "claude-code",
    "engine_version": "1.4.2",
    "judge": "claude-sonnet-4-20250514",
    "timeout": 120
  },
  "agent": {
    "runtime": "claude-code",
    "runtime_version": "1.4.2",
    "model": "claude-sonnet-4-20250514",
    "model_provider": "anthropic",
    "session_id": "sess_abc123"
  },
  "judge": {
    "model": "claude-sonnet-4-20250514",
    "model_provider": "anthropic"
  },
  "environment": {
    "os": "linux",
    "arch": "x86_64",
    "aam_version": "0.5.0",
    "node_version": "22.1.0",
    "python_version": "3.12.3"
  },
  "package": {
    "name": "@my-org/code-review",
    "version": "1.0.0"
  },
  "summary": {
    "total": 5,
    "passed": 4,
    "failed": 1,
    "skipped": 0,
    "pass_rate": 0.80
  },
  "cases": [
    {
      "name": "pdf-extraction-e2e",
      "target": "skill:pdf-tools",
      "verdict": "PASS",
      "duration_seconds": 18,
      "deterministic_checks": {
        "contains": "PASS",
        "not_contains": "PASS",
        "files_created": "PASS"
      },
      "judge_verdict": {
        "result": "PASS",
        "reason": "Output contains all visible text from the PDF. No hallucinated content detected.",
        "model": "claude-sonnet-4-20250514"
      },
      "agent_output_snippet": "Extracted text from sample.pdf:\n\nHello, World\nPage 1..."
    },
    {
      "name": "hook-blocks-protected-write",
      "target": "hook:pre-tool-use",
      "verdict": "FAIL",
      "duration_seconds": 12,
      "deterministic_checks": {
        "not_contains": "PASS",
        "agent_blocked": "FAIL"
      },
      "judge_verdict": {
        "result": "FAIL",
        "reason": "Agent was not blocked — hook script exited 0 instead of 2.",
        "model": "claude-sonnet-4-20250514"
      },
      "agent_output_snippet": "I'll write the file...",
      "error": "expected agent-blocked=true but hook exited 0"
    }
  ]
}

Report Field Reference

Field Type Description
id string Unique run identifier.
timestamp string ISO 8601 run start time.
duration_seconds number Total eval run wall time.
config.engine string Agent runtime used (from config or --engine override).
config.judge string Judge model used (from config or --judge override).
agent.runtime string Actual agent runtime name.
agent.runtime_version string Agent runtime version (e.g. Claude Code 1.4.2).
agent.model string LLM model the agent used for execution.
agent.model_provider string Model provider (anthropic, openai, google, etc.).
agent.session_id string Agent session ID for traceability.
judge.model string LLM model used for judge assessment.
judge.model_provider string Judge model provider.
environment object OS, architecture, AAM version, language runtimes.
package object Package name and version under evaluation.
summary object Aggregated pass/fail/skip counts and pass rate.
cases[].verdict string "PASS", "FAIL", or "SKIP".
cases[].deterministic_checks object Per-check pass/fail for substring and file assertions.
cases[].judge_verdict object LLM judge result, reason, and model used.
cases[].agent_output_snippet string Truncated agent output (first 500 chars).
cases[].error string Error message if the case failed.

Comparing Eval Reports

aam eval --report                     # Generate report (default: evals/reports/<timestamp>.json)
aam eval --report -o report.json      # Custom output path
aam eval diff <report-a> <report-b>   # Compare two reports side by side

Reports enable tracking eval pass rates across agent versions, model upgrades, and package changes — answering questions like "did upgrading from claude-sonnet to claude-opus improve the pdf-tools eval?"


3. Package Manifest — package.agent.json

The manifest is the single required file. It identifies the package and declares its contents.

Schema

{
  // === REQUIRED ===
  "name": "my-package",                        // [a-z0-9-] or @scope/name, max 64/130 chars
  "version": "1.0.0",                          // SemVer

  // === RECOMMENDED ===
  "description": "Brief description",          // Max 1024 chars
  "author": "org-or-user",
  "license": "Apache-2.0",
  "repository": "https://github.com/org/repo",
  "homepage": "https://docs.example.com",      // Project homepage or docs URL

  // === OPTIONAL ===
  "category": "Developer Tools",               // Marketplace category
  "keywords": ["testing", "python"],            // Discovery tags
  "engines": {                                  // Platform compatibility
    "claude-code": ">=1.0",
    "cursor": ">=2.2",
    "copilot": "*",
    "codex": "*"
  },

  // === ARTIFACT DECLARATIONS (optional, explicit registry; auto-discovered if omitted) ===
  // Declaring artifacts explicitly enables richer registry metadata and description per artifact.
  "artifacts": {
    "skills": [
      { "name": "pdf-tools", "path": "skills/pdf-tools/", "description": "Extract text from PDFs" }
    ],
    "agents": [
      { "name": "code-auditor", "path": "agents/code-auditor/", "description": "Security audit agent" }
    ],
    "commands": [
      { "name": "review", "path": "commands/review.md", "description": "Code review command" },
      { "name": "audit-finding", "path": "commands/audit-finding.md", "description": "Finding template" }
    ]
  },

  // === DEPENDENCIES (optional) ===
  "dependencies": {                             // Other agent packages required
    "code-review": "^1.0.0",                   //   SemVer range
    "testing-utils": ">=2.1.0 <3.0.0"
  },
  "optionalDependencies": {                     // Nice-to-have, install doesn't fail
    "ai-image-gen": "^1.0.0"
  },
  "peerDependencies": {                         // Must be provided by host environment
    "eslint-rules": ">=3.0.0"
  },
  "systemDependencies": {                       // OS-level / runtime requirements
    "python": ">=3.10",
    "node": ">=18",
    "packages": {                               //   Package manager installs
      "pip": ["pypdf", "pdfplumber>=0.10"],
      "npm": ["prettier", "eslint"],
      "brew": ["graphviz"],
      "apt": ["poppler-utils"]
    },
    "binaries": ["git", "docker"],              //   Must exist on $PATH
    "mcp-servers": ["filesystem"]              //   Required MCP servers
  },

  // === ENVIRONMENT & SECRETS (optional) ===
  "env": {                                      // Required environment variables
    "GITHUB_TOKEN": { "description": "Required for PR creation", "required": true },
    "DEBUG_MODE": { "description": "Enable verbose logging", "required": false, "default": "false" }
  },

  // === PERMISSIONS (optional) ===
  "permissions": {                              // Requested agent capabilities
    "fs": {                                     // Filesystem access — path-scoped
      "read":  ["src/**", "docs/**"],           //   Glob patterns the package may read
      "write": ["output/**", ".cache/**"]       //   Glob patterns the package may write/delete
    },
    "network": {                                // Network access
      "hosts": ["api.github.com", "*.npmjs.org"], // Exact host or wildcard subdomain
      "schemes": ["https"]                      // Allowed URI schemes (https | http | wss)
    },
    "shell": {                                  // Shell execution
      "allow": true,                            //   false = no shell; true = allow listed binaries
      "binaries": ["git", "npm", "python3"]     //   Explicit allow-list (ignored when allow=false)
    }
  },

  // === QUALITY (optional) ===
  "quality": {
    "tests": [
      { "name": "unit-tests", "command": "pytest tests/", "description": "Unit tests" }
    ],
    "evals": [
      {
        "name": "accuracy-eval",
        "path": "evals/cases/accuracy-eval.yaml",
        "description": "Measures accuracy against benchmark",
        "metrics": [
          { "name": "accuracy", "type": "percentage" }
        ]
      }
    ]
  },

  // === COMPONENT OVERRIDES (optional, defaults use convention) ===
  "skills": "./skills",                         // Default: ./skills
  "commands": "./commands",                     // Default: ./commands
  "agents": "./agents",                         // Default: ./agents
  "rules": "./rules",                           // Default: ./rules
  "hooks": "./hooks/hooks.json",                // Default: ./hooks/hooks.json
  "mcp": "./mcp/servers.json",                  // Default: ./mcp/servers.json

  // === DIST-TAGS (optional) ===
  // Publisher-managed version aliases. See §12.3 for full semantics.
  "dist-tags": {
    "stable": "1.0.0",                          // Manually maintained alias
    "latest": "1.0.0"                           // Auto-set on publish
  },

  // === DEPENDENCY OVERRIDES (optional) ===
  // Force a specific resolved version for a transitive dependency. See §13.9.
  "resolutions": {
    "git-utils": "2.1.0"                        // Force all transitive refs to this version
  },

  // === INSTALL MODE (optional, for adoption transitioning) ===
  // Controls deployment target when installing on platforms with partial UAAPS support.
  // See §11.1 for full semantics and deprecation path.
  "installMode": {                              // Default: "uaaps" for all platforms
    "default": "uaaps",                         // "uaaps" | "plugin"
    "claude-code": "plugin",                    // Per-platform override
    "cursor": "uaaps"
  },

  // === VENDOR EXTENSIONS (optional) ===
  // Keys MUST be "x-<vendor-id>" where vendor-id matches [a-z0-9-], max 32 chars.
  // Values MUST be JSON objects (not scalars or arrays).
  // Tools MUST silently ignore unrecognised x-* keys.
  "x-claude": {
    "marketplace": "anthropics/skills"
  },
  "x-cursor": {
    "category": "Developer Tools"
  }
}

Format Support

The canonical format is JSON (package.agent.json), but tools SHOULD also accept YAML (package.agent.yaml) for author convenience:

# package.agent.yaml (equivalent to JSON, author-friendly)
name: my-package           # or @scope/my-package for scoped
version: 1.0.0
description: Brief description
author: org-or-user
license: Apache-2.0
homepage: https://docs.example.com

skills: ./skills
commands: ./commands
agents: ./agents

dependencies:
  code-review: "^1.0.0"

x-claude:
  marketplace: anthropics/skills   # x-<vendor-id>, value MUST be an object

x-cursor:
  category: Developer Tools

Rules: - If both .json and .yaml exist, JSON takes precedence. - CLI tools (aam install, etc.) generate package.agent.json as the canonical output. - Lock files (package.agent.lock) are always JSON.

YAML round-trip guarantee: To ensure lossless JSON-to-YAML conversion, package.agent.yaml files MUST conform to the following constraints:

Constraint Requirement
YAML version MUST use YAML 1.2
Scalar types MUST use only JSON-compatible types: strings, numbers, booleans, null
Anchors & aliases MUST NOT use YAML anchors (&), aliases (*), or merge keys (<<)
Custom tags MUST NOT use YAML tags (e.g. !!python/object)
Key types All mapping keys MUST be strings
Duplicate keys MUST NOT contain duplicate keys in the same mapping

Tools that convert between JSON and YAML MUST produce output that round-trips without data loss. A manifest that cannot be losslessly converted between formats is non-conformant.

Vendor Extensions

Vendor extensions allow platforms and tools to attach platform-specific metadata to a package manifest without conflicting with the core schema.

Naming Rules

  • Keys MUST use the prefix x-<vendor-id>, where vendor-id is a lowercase alphanumeric slug matching [a-z0-9-]+, maximum 32 characters (e.g. x-claude, x-cursor, x-copilot).
  • The vendor-id SHOULD match either a registered platform identifier from the compatibility matrix or the package's own scope identifier (e.g. x-myorg for @myorg/ scoped packages).
  • Extension values MUST be JSON objects. Scalar values (strings, booleans, numbers) and arrays MUST NOT be used as the top-level value of an x-* key.
  • Packages SHOULD NOT declare more than 5 x-* keys. aam validate MUST warn when more than 5 are present (see §16 Validation).

Interoperability Rules

  • Tools and runtimes MUST silently ignore unrecognised x-* keys. Tools MUST NOT error or refuse to install a package solely because of an unknown x-* key.
  • Registry indexing: only x-<known-platform> keys (those matching a registered platform) are indexed and searchable. Unknown x-* keys are stored but not indexed.
  • Registry validation SHOULD produce a WARN-level diagnostic for x-* keys that do not match any registered platform identifier, to alert the author that the key will not be indexed.

Conflict Avoidance

  • Two packages MUST NOT declare the same x-* key with structurally incompatible schemas. The registry MAY enforce a canonical JSON Schema per registered x-<vendor> key.
  • Authors MUST NOT use another vendor's registered x-* key to override or shadow that vendor's metadata.
  • Private extensions for in-house tooling SHOULD use the package owner's scope as the vendor-id (e.g. x-myorg) to avoid collisions with future registered platforms.

Vendor Mapping

Manifest Field Claude Code (plugin.json) Cursor (.cursor-plugin/plugin.json)
name name name
version version version
description description description
homepage N/A N/A
skills (auto-discovered in skills/) (auto-discovered in skills/)
commands commands N/A (uses rules)
agents agents N/A (uses rules/agents)
hooks hooks path .cursor/hooks.json
mcp mcpServers mcp.json
dependencies N/A (no native equivalent) N/A (no native equivalent)
systemDependencies Skill compatibility field N/A
quality N/A N/A

4. Skills — SKILL.md

Skills are the primary portable artifact — already standardized via the Agent Skills Open Standard (agentskills.io) and adopted by Claude Code, Cursor, Copilot, Codex, and others.

SKILL.md Format

---
# === REQUIRED ===
name: skill-name                    # [a-z0-9-], max 64 chars
description: >                      # Max 1024 chars. WHAT it does + WHEN to use it.
  Extract text and tables from PDF files, fill forms, merge documents.
  Use when working with PDF files or when the user mentions PDFs.

# === OPTIONAL (Open Standard) ===
license: Apache-2.0
compatibility:                      # Only if skill has special requirements
  requires:
    - python>=3.10
    - pypdf
metadata:                           # Arbitrary key-value extensions
  author: my-org
  version: "1.0"

# === OPTIONAL (Platform Extensions) ===
allowed-tools: Read Grep Glob Bash  # Pre-approved tools (experimental)
context: fork                       # Claude Code: run in isolated sub-agent
agent: Explore                      # Claude Code: sub-agent config
disable-model-invocation: true      # Claude Code: user-only invocation
---

# Skill Title

## Instructions
Step-by-step guidance...

## Examples
Concrete usage examples...

Frontmatter Field Reference

Field Type Required Standard Description
name string Yes agentskills.io [a-z0-9-], max 64 chars
description string Yes agentskills.io What + When. Max 1024 chars.
license string No agentskills.io License identifier or filename
compatibility map No agentskills.io Environment requirements
metadata map<str,str> No agentskills.io Extension key-value pairs
allowed-tools string No Extension Space-delimited tool whitelist
context string No Claude ext. "fork" for isolated execution
agent string No Claude ext. Sub-agent config name
disable-model-invocation bool No Claude ext. Restrict to user-only invocation

Skill Directory

skill-name/
├── SKILL.md         # REQUIRED
├── scripts/         # Executable code (Python, Bash, JS)
├── references/      # Additional documentation
├── assets/          # Templates, images, data
├── examples/        # Example inputs/outputs
└── tests/           # Skill tests (optional)
    ├── test-config.json
    └── cases/
        ├── 01-basic.yaml
        └── 02-edge-cases.yaml

Progressive Disclosure

Phase What Loads Token Cost
1. Metadata name + description from all skills ~50 tokens/skill
2. Activation Full SKILL.md body ~2,000–5,000 tokens
3. Execution scripts/, references/, assets/ on-demand Variable

Skills Discovery & Precedence

Skills can exist at multiple scopes. The universal resolver follows this precedence:

Scope Location Precedence Namespaced?
Managed / Enterprise Admin-deployed Highest No
Project ./skills/ (in package or .claude/skills/ or .github/skills/) Overrides personal No
Personal ~/.claude/skills/ or ~/.github/skills/ or user profile Lowest non-plugin No
Plugin / Package <package>/skills/ Always namespaced Yes (pkg:skill)

Resolution rules: - When project and personal skills share the same name, the project-scoped skill MUST take precedence. - Plugin/package skills MUST be namespaced as package-name:skill-name and MUST NOT conflict with unnamespaced scopes. - Enterprise/managed rules MUST override all other scopes. - Implementations MUST auto-discover skills in nested subdirectory .claude/skills/ or .github/skills/ within a monorepo.

Disputed precedence: ChatGPT investigation claimed enterprise > personal > project. Our investigation (verified against community guides) found enterprise > project > personal. The enterprise tier being highest is agreed; the project > personal ordering is confirmed by Claude Code behavior where .claude/skills/ in the project overrides ~/.claude/skills/ for same-named skills.

Skill Testing

Skills MAY include a tests/ directory with deterministic test cases that verify scripts behave correctly. These tests use the assert runner — no LLM calls, no agent sandbox, CI-safe.

Eval-based testing (LLM-judged, agent sandbox) lives in the top-level evals/ directory. See § Evals in the package format spec.

Test Config — tests/test-config.json

{
  "version": 1,
  "timeout": 30,
  "env": {
    "SKILL_TEST": "true"
  }
}
Field Type Required Description
version number Yes Test config format version. Currently 1.
timeout number No Max seconds per test case. Default 30.
env map<str,str> No Environment variables injected during test runs.

Test Case Format — tests/cases/*.yaml

name: basic-pdf-extraction
description: Verify script extracts text from a single-page PDF
input:
  command: "python scripts/extract.py tests/fixtures/sample.pdf"
  stdin: null
  files:
    - tests/fixtures/sample.pdf
expected:
  exit-code: 0
  stdout-contains:
    - "Hello, World"
    - "Page 1"
  not-contains:
    - "ERROR"
    - "Traceback"
Field Type Required Description
name string Yes Test case identifier. [a-z0-9-], max 64 chars.
description string No Human-readable description of what is tested.
input.command string Yes Shell command to execute (relative to skill root).
input.stdin string No Data piped to stdin.
input.files string[] No Fixture files required (relative to skill root).
expected.exit-code number No Expected exit code. Default 0.
expected.stdout-contains string[] No Substrings that MUST appear in stdout.
expected.stderr-contains string[] No Substrings that MUST appear in stderr.
expected.not-contains string[] No Substrings that MUST NOT appear in combined output.
expected.stdout-json object No JSON structure stdout MUST match (deep partial match).

Running Skill Tests

aam test                             # Run all assert tests in current package
aam test skill-name                  # Run tests for a specific skill
aam test skill-name --case 01-basic  # Run a single test case

Platform Compatibility

Platform Skill Location Status
Claude Code ~/.claude/skills/, .claude/skills/, plugin skills/ ✅ Full support
Cursor Plugin skills/, imported as agent-decided rules ✅ Supported (v2.2+)
GitHub Copilot .github/skills/, .claude/skills/ (also supported), user profile ✅ Supported
OpenAI Codex ~/.codex/skills/, .agents/skills/ ✅ Supported
Amp, Goose, OpenCode, Letta Various paths ✅ Supported

5. Commands (Slash Commands & Reusable Templates)

Commands are markdown prompt files that serve two roles:

  1. User-invoked — triggered by typing /command-name.
  2. Reusable templates — referenced by agents and skills via {{variable}} interpolation.

A command with no variables is a simple slash command. A command with variables is a parameterized template that can also be referenced by agents and skills.

Simple Command

---
name: review
description: Run a comprehensive code review on the current file
---

Review the current file for:
1. Code organization and structure
2. Error handling patterns
3. Performance implications
4. Security vulnerabilities
5. Test coverage gaps

Provide specific, actionable feedback with line references.

Parameterized Command (Template)

---
name: audit-finding
description: "Structured command for documenting a single audit finding"
variables:
  - name: control_id
    description: "Control identifier"
    required: true
  - name: severity
    description: "Finding severity level"
    required: true
    enum: [critical, high, medium, low]
    default: medium
  - name: evidence
    description: "Supporting evidence"
    required: false
    default: "No evidence provided"
---

# Audit Finding: {{control_id}}

## Severity: {{severity}}

Analyze the following evidence and produce a structured finding:

{{evidence}}

Frontmatter Fields

Field Type Required Description
name string Yes Identifier, [a-z0-9-], max 64 chars.
description string RECOMMENDED Shown in command listings and help.
variables Variable[] No Template variables with optional defaults and enums.

Each variable in the variables array has:

Field Type Required Description
name string Yes Variable identifier used in {{name}} interpolation.
description string No Human-readable description.
required boolean No Whether the variable must be provided. Default: false.
enum string[] No Allowed values.
default string No Default value when not provided.

Platform Mapping

Platform Native Support Location Invocation
Claude Code ✅ Yes commands/ (.md files), .claude/prompts/ /plugin:command
Cursor ✅ Prompts / ⚠️ Commands via rules .cursor/prompts/ No native /command from plugins
GitHub Copilot ✅ Yes .github/prompts/<name>.prompt.md Via prompt picker
Codex ⚠️ Via skills Skills with disable-model-invocation $skill-name

Command Namespacing & Collision

When multiple installed packages define commands with the same name, the platform MUST apply this resolution order:

Scope Location Precedence
Project ./commands/ (in package or .claude/commands/) Highest
Personal ~/.claude/commands/ or user profile Lower
Plugin / Package <package>/commands/ Always namespaced

Plugin/package commands are always namespaced as package-name:command-name (e.g., /code-review:review). Two packages that both define a command named review are addressable as /code-review:review and /testing-utils:review without conflict.

Unnamespaced invocation (e.g., /review) resolves to the project or personal scope. If neither scope defines the command, the platform SHOULD prompt the user to disambiguate when multiple packages provide a command with that name.

Migration Strategy

For platforms without native command support, commands can be converted to skills with disable-model-invocation: true to achieve similar user-initiated behavior.


6. Agents (Sub-agent Definitions)

Agents are specialized personas with domain expertise that can be selected automatically or manually. (Note: This is distinct from AGENTS.md, which provides project-wide instructions).

Agents are defined as a directory containing two files, which provides richer structured configuration than a single markdown file.

Directory Structure

agents/
└── code-auditor/
    ├── agent.yaml        # REQUIRED — structured agent definition
    └── system-prompt.md  # REQUIRED — the agent's full system prompt

agent.yaml Format

name: code-auditor
description: "Security-focused code review agent"
version: 1.0.0

system_prompt: system-prompt.md    # Path to the system prompt file

# Skills this agent uses (resolved from package or dependencies)
skills:
  - pdf-tools                      # from this package
  - code-review                    # from dependency

# Commands this agent references
commands:
  - audit-finding                  # from this package

# Tool access (platform-dependent)
tools:
  - file_read
  - file_write
  - shell

# Behavioral parameters
parameters:
  temperature: 0.3
  style: professional
  output_format: markdown

system-prompt.md Format

---
description: Security review specialist for vulnerability analysis
capabilities:
  - OWASP Top 10 detection
  - Dependency vulnerability scanning
  - Secret detection
  - Cryptographic implementation review
---

You are a security review specialist. When analyzing code:

1. Check for injection vulnerabilities
2. Verify authentication patterns
3. Review cryptographic implementations
4. Scan for hardcoded secrets
5. Assess dependency security

Provide severity ratings (Critical/High/Medium/Low) with remediation.

agent.yaml Field Reference

Field Type Required Description
name string Yes Agent identifier. [a-z0-9-], max 64 chars.
description string Yes Role summary for automatic selection.
version string No SemVer version of this agent definition.
system_prompt string Yes Relative path to system-prompt.md.
skills string[] No Skills this agent activates.
commands string[] No Commands (including parameterized templates) this agent uses.
tools string[] No Tool whitelist (platform-dependent).
parameters map No Behavioral parameters (temperature, style, etc.).

system-prompt.md Frontmatter Field Reference

Field Type Required Description
description string Yes One-line role summary shown in agent listings and used for automatic agent selection.
capabilities string[] No List of key capabilities for discovery and UI display.

Legacy format: A single agent-name.md file (without a separate agent.yaml) is also accepted for backward compatibility. In this case the frontmatter fields from the system-prompt.md table below apply directly to the single file.

Platform Mapping

Platform Native Support Migration
Claude Code ✅ Yes (agents/ dir) Direct
Cursor ⚠️ No native agents/ Convert system-prompt.md → RULE.md with alwaysApply: true
GitHub Copilot .github/agents/*.agent.md Convert to .agent.md
Codex ⚠️ Via skills Convert to skill

7. Rules (Project Rules / Instructions)

Rules provide persistent context — coding standards, architecture conventions, and project-specific guidance.

Universal Format: RULE.md

---
description: "TypeScript coding standards for this project"
globs: "src/**/*.{ts,tsx}"
alwaysApply: false
---

# TypeScript Standards

- Use strict mode
- Prefer `const` over `let`
- Use early returns
- Named exports only
- Co-locate tests as `*.test.ts`

Frontmatter Fields

Field Type Required Description
description string Yes Purpose of rule. Used for agent auto-selection.
globs string No File patterns for auto-attachment.
alwaysApply boolean No Always include in context. Default: false.

Platform Mapping

Platform Native Format Migration
Claude Code CLAUDE.md (freeform) Copy content into CLAUDE.md
Cursor .mdc / RULE.md in .cursor/rules/ Copy to .cursor/rules/
Codex AGENTS.md Copy into AGENTS.md sections
Universal AGENTS.md (project root) Direct (supported everywhere)

AGENTS.md as Universal Fallback

AGENTS.md at the project root is the most portable instruction format — supported by Claude Code, Cursor, Codex, Copilot, Gemini CLI, Jules, Amp, and others. Every universal package SHOULD include an AGENTS.md for maximum compatibility.


8. Hooks (Lifecycle Event Handlers)

Hooks execute shell commands at specific points in the agent lifecycle. They run outside the context window with zero token overhead.

Universal hooks.json Format

{
  "version": 1,
  "hooks": {
    "pre-tool-use": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "command": "bash ${PACKAGE_ROOT}/hooks/scripts/validate.sh",
          "timeout": 30
        }]
      }
    ],
    "post-tool-use": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "command": "npx prettier --write ${file}"
        }]
      }
    ],
    "stop": [
      {
        "hooks": [{
          "type": "prompt",
          "prompt": "Check if all tasks are complete. Context: $ARGUMENTS",
          "timeout": 30
        }]
      }
    ],
    "session-start": [
      {
        "hooks": [{
          "type": "command",
          "command": "bash ${PACKAGE_ROOT}/hooks/scripts/setup.sh"
        }]
      }
    ]
  }
}

Unified Hook Events

Universal Event Claude Code Cursor Copilot Can Block? Description
pre-tool-use PreToolUse beforeShellCommand / beforeMcpCall preToolUse Yes Before tool execution
permission-request PermissionRequest N/A N/A Yes Permission dialog shown
post-tool-use PostToolUse afterFileEdit N/A Feedback After tool execution
pre-prompt UserPromptSubmit beforeSubmitPrompt userPromptSubmitted Yes / Copilot: logging Before prompt processed
session-start SessionStart N/A sessionStart Context inject New session begins
session-end SessionEnd N/A sessionEnd Cleanup Session terminates
stop Stop stop N/A Yes Agent finishes responding
sub-agent-end SubagentStop N/A N/A Yes Sub-agent finishes
pre-compact PreCompact N/A N/A No Before context compaction
notification Notification N/A N/A No System notification

Copilot hook gaps: Copilot currently supports 4 hook events vs Claude Code's 10. The stop, post-tool-use, permission-request, sub-agent-end, pre-compact, and notification events have no Copilot equivalent. A community feature request on the Copilot CLI GitHub repository tracks parity efforts.

Hook Types

Type Description Use Case
command Execute a shell command Deterministic rules (lint, format, validate)
prompt Query an LLM for context-aware decision Stop/continue decisions, complex validation

Blocking Response Schema

For hooks that support blocking (pre-tool-use, permission-request, pre-prompt, stop):

{
  "hookSpecificOutput": {
    "hookEventName": "pre-tool-use",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "Reason shown to user or agent",
    "updatedInput": {}
  }
}

For stop / sub-agent-end:

{
  "decision": "block",
  "reason": "Tests not yet executed"
}

Exit Code Convention

Code Behavior
0 Success. JSON on stdout parsed for structured control.
2 Blocking error. stderr fed back to agent.
Other Non-blocking error. Logged only.

Hook Field Reference

Field Type Required Description
matcher string No Regex pattern for filtering by tool name.
hooks[].type string Yes "command" or "prompt".
hooks[].command string For command Shell command. Receives JSON on stdin.
hooks[].prompt string For prompt LLM prompt. $ARGUMENTS for input.
hooks[].timeout number No Timeout in seconds.

Hooks Directory

hooks/
├── hooks.json           # REQUIRED — hook definitions
├── scripts/             # Shell scripts referenced by hooks
│   ├── validate.sh
│   ├── setup.sh
│   └── format.sh
└── tests/               # Hook tests (optional)
    ├── test-config.json
    ├── fixtures/        # Simulated event payloads
    │   ├── pre-tool-use-write.json
    │   └── stop-incomplete.json
    └── cases/
        ├── 01-pre-tool-block.yaml
        └── 02-post-tool-format.yaml

Hook Testing

Hooks MAY include a tests/ directory with deterministic test cases that verify hook scripts behave correctly for simulated lifecycle events. These tests use the assert runner — no LLM calls, no agent sandbox, CI-safe.

Eval-based testing (LLM-judged, agent sandbox) lives in the top-level evals/ directory. See § Evals in the package format spec.

Test Config — tests/test-config.json

{
  "version": 1,
  "timeout": 30,
  "env": {
    "HOOK_TEST": "true",
    "PACKAGE_ROOT": "."
  }
}
Field Type Required Description
version number Yes Test config format version. Currently 1.
timeout number No Max seconds per test case. Default 30.
env map<str,str> No Environment variables injected during test runs.

Fixture Payloads — tests/fixtures/*.json

Fixtures simulate the JSON event that a hook receives on stdin during real execution.

{
  "hookEventName": "pre-tool-use",
  "toolName": "Write",
  "toolInput": {
    "file_path": "/src/app.ts",
    "content": "console.log('hello');"
  }
}

Test Case Format — tests/cases/*.yaml

name: pre-tool-block-forbidden-path
description: Verify pre-tool-use hook blocks writes to protected directories
event: pre-tool-use
hook-index: 0
input:
  fixture: fixtures/pre-tool-use-write.json
  overrides:
    toolInput.file_path: "/etc/passwd"
expected:
  exit-code: 2
  stderr-contains:
    - "blocked"
    - "protected path"
  stdout-json:
    hookSpecificOutput:
      permissionDecision: deny
name: post-tool-format-success
description: Verify post-tool-use hook runs formatter without error
event: post-tool-use
hook-index: 0
input:
  fixture: fixtures/pre-tool-use-write.json
expected:
  exit-code: 0
  not-contains:
    - "ERROR"
Field Type Required Description
name string Yes Test case identifier. [a-z0-9-], max 64 chars.
description string No Human-readable description of what is tested.
event string Yes Hook event to test (e.g. pre-tool-use, stop).
hook-index number No Index of the hook group in the event array. Default 0.
input.fixture string No Path to a fixture JSON file (relative to tests/).
input.overrides map No Dot-path overrides applied on top of the fixture.
expected.exit-code number No Expected exit code from the hook script.
expected.stderr-contains string[] No Substrings that MUST appear in stderr.
expected.stdout-json object No JSON structure that stdout MUST match (deep partial match).
expected.not-contains string[] No Substrings that MUST NOT appear in combined output.

Running Hook Tests

aam test --hooks                          # Run all hook tests in current package
aam test --hooks --case 01-pre-tool-block # Run a single hook test case
aam test --hooks --event pre-tool-use     # Run tests for a specific event only

9. MCP Server Configuration

Model Context Protocol servers extend agent capabilities with external tool access.

Universal servers.json Format

{
  "servers": {
    "my-service": {
      "command": "node",
      "args": ["${PACKAGE_ROOT}/mcp-server/index.js"],
      "env": {
        "API_KEY": "${API_KEY}"
      }
    }
  }
}

servers.json Field Reference

Field Type Required Description
servers map<string, ServerDef> Yes Map of server name to server definition. Server names MUST be [a-z0-9-].
servers.<name>.command string Yes Executable used to launch the MCP server process.
servers.<name>.args string[] No Arguments passed to the command.
servers.<name>.env map<string, string> No Environment variables injected into the server process. Use ${VAR} syntax to reference host-environment variables (e.g. ${API_KEY}).
servers.<name>.type string No Transport type. Permitted values: stdio (default), http.

Platform Mapping

Platform Plugin Location Project Location Global Location Root Variable
Claude Code .mcp.json at plugin root .mcp.json at project root ~/.claude/.mcp.json ${CLAUDE_PLUGIN_ROOT}
Cursor mcp.json at plugin root .cursor/mcp.json ~/.cursor/mcp.json Working dir relative
Universal mcp/servers.json mcp/servers.json N/A ${PACKAGE_ROOT}

Critical: Cursor plugin MCP uses mcp.json (no dot prefix, no .cursor/ wrapper). Project/global MCP uses .cursor/mcp.json. Claude Code always uses .mcp.json (leading dot).


10. Cross-Platform Compatibility Matrix

Artifact Support by Platform

Artifact Claude Code Cursor Copilot Codex Standard Confidence
Skills (SKILL.md) ✅ Native ✅ v2.2+ .github/skills/ ✅ CLI+App agentskills.io Official
Commands ✅ Native ⚠️ Via rules .prompt.md ⚠️ Via skills Package-defined Official
Agents ✅ Native (agents/*.md) ⚠️ Via rules .agent.md ⚠️ Via skills Package-defined Official
Rules / Instructions CLAUDE.md .mdc/RULE.md .instructions.md ⚠️ AGENTS.md Package-defined Official
AGENTS.md agents.md Official
Hooks ✅ 10 events ✅ 5 events ✅ 4 events Package-defined Official
MCP Servers ✅ Native ✅ Native ✅ CCA + VS Code ⚠️ Limited MCP Protocol Official

Instruction File Compatibility

File Claude Code Cursor Copilot Codex Jules Confidence
AGENTS.md Official
CLAUDE.md ✅* Official
GEMINI.md ✅* Community
.cursor/rules/*.mdc Official
.github/copilot-instructions.md Official
.github/instructions/*.instructions.md Official
.github/agents/*.agent.md Official

*Cursor reads CLAUDE.md and GEMINI.md as agent-specific instruction files alongside AGENTS.md.

GitHub Copilot Artifact Types

Artifact Location Format Description
Repo instructions .github/copilot-instructions.md Plain Markdown Repository-wide guidance
Path instructions .github/instructions/*.instructions.md YAML frontmatter (applyTo glob) + Markdown Path-scoped rules
Custom agents .github/agents/*.agent.md Markdown Agent definitions (VS Code)
Agent instructions AGENTS.md / CLAUDE.md / GEMINI.md Plain Markdown Agent-specific instructions

Path Instructions Example

---
applyTo: "src/**/*.ts"
---

# TypeScript Standards
- Use strict mode
- Prefer interfaces over type aliases

Platform Capability Discovery

Static compatibility matrices become outdated. To enable runtime discovery, platforms implementing UAAPS Level 2+ SHOULD expose a capabilities object queryable by packages and tools.

Capabilities Object Schema

{
  "platform": "claude-code",
  "platform_version": "1.4.2",
  "uaaps_conformance_level": 2,
  "spec_version": "0.6.0",
  "supported_artifacts": ["skills", "commands", "agents", "rules", "hooks", "mcp"],
  "hook_events": [
    "pre-tool-use", "post-tool-use", "pre-prompt", "stop",
    "session-start", "session-end", "sub-agent-end",
    "permission-request", "pre-compact", "notification"
  ],
  "permissions_enforcement": {
    "fs": true,
    "network": true,
    "shell": true
  },
  "eval_headless": true,
  "resolver_version": 1
}

Capabilities Field Reference

Field Type Required Description
platform string Yes Platform identifier (e.g. claude-code, cursor, copilot, codex).
platform_version string Yes Platform version string.
uaaps_conformance_level number Yes Declared conformance level (1, 2, or 3). See §1 Conformance Levels.
spec_version string Yes Highest UAAPS spec version supported.
supported_artifacts string[] Yes Artifact types the platform can consume.
hook_events string[] Yes Hook event names the platform fires.
permissions_enforcement object Yes Which permission categories are enforced at runtime.
eval_headless boolean Yes Whether the platform supports headless eval execution.
resolver_version number No Dependency resolver version supported. Default 1.

Querying Capabilities

# CLI query
aam platform info
aam platform info --json

# Programmatic (within a hook or skill script)
# Read from well-known path: .agent-packages/.platform-capabilities.json
# Written by the platform on session start

Platforms MUST write the capabilities object to .agent-packages/.platform-capabilities.json at session start. Packages MAY read this file to adapt behavior based on platform support (e.g., falling back to AGENTS.md injection when hooks are unsupported).

Graceful Degradation

When a package requires an artifact type or hook event not listed in the platform's capabilities:

Situation Behavior
Unsupported artifact type aam install emits a WARN: "Platform does not support <type>. These artifacts will be ignored." Installation proceeds.
Unsupported hook event Hook entry is skipped silently. Platform MAY log at debug level.
Missing permission enforcement aam install emits a WARN: "Platform cannot enforce <category> permissions. Package runs with platform defaults."
Lower conformance level than package requires aam install emits a WARN listing which features will be unavailable.

11. Migration Guides

From Claude Code Plugin → Universal Package

.claude-plugin/plugin.json    →  package.agent.json
commands/*.md                 →  commands/*.md (unchanged)
agents/*.md                   →  agents/<name>/system-prompt.md + agent.yaml (add structured def)
skills/*/SKILL.md             →  skills/*/SKILL.md (unchanged)
.claude/prompts/*.md          →  commands/*.md (add name field to frontmatter)
hooks/hooks.json              →  hooks/hooks.json (rename events)
.mcp.json                     →  mcp/servers.json (restructure)
CLAUDE.md                     →  AGENTS.md (rename)

From Cursor Plugin → Universal Package

.cursor-plugin/plugin.json    →  package.agent.json
skills/*/SKILL.md             →  skills/*/SKILL.md (unchanged)
.cursor/prompts/*.md          →  commands/*.md (add name field to frontmatter)
rules/*.mdc                   →  rules/*/RULE.md (restructure frontmatter)
mcp.json                      →  mcp/servers.json (restructure)
.cursor/hooks.json            →  hooks/hooks.json (rename events)
AGENTS.md                     →  AGENTS.md (unchanged)

From GitHub Copilot → Universal Package

.github/copilot-instructions.md    →  AGENTS.md (rename + restructure)
.github/instructions/*.instructions.md  →  rules/*/RULE.md (applyTo → globs)
.github/agents/*.agent.md          →  agents/*.md (remove .agent suffix)
AGENTS.md                          →  AGENTS.md (unchanged)

.instructions.md to RULE.md Frontmatter Mapping

# Copilot (.instructions.md)        # Universal (RULE.md)
---                                  ---
applyTo: "src/**/*.ts"              globs: "src/**/*.ts"
---                                  alwaysApply: false
                                     description: "TypeScript standards"
                                     ---

From .mdc Rule → RULE.md

# .mdc (Cursor native)                    # RULE.md (Universal)
---                                        ---
description: React standards               description: React standards
globs: src/**/*.tsx                        globs: src/**/*.tsx
alwaysApply: false                         alwaysApply: false
---                                        ---
# Content...                               # Content...

The formats are functionally identical. Only the file extension changes.


11.1 Install Mode — Smooth Adoption Before Full Vendor Implementation

The installMode field in package.agent.json allows a package author to declare how the package SHOULD be deployed on platforms that have not yet natively implemented the UAAPS standard. This enables smooth adoption: one package can target both UAAPS-native environments and platforms still using their own plugin system.

Mode Values

Value Install target When to use
uaaps (default) .agent-packages/ — full UAAPS layout Platform supports UAAPS natively
plugin Vendor-native location (see table below) Platform uses its own plugin system

installMode in package.agent.json

{
  "name": "@myorg/code-review",
  "version": "1.0.0",

  // Omit entirely to use "uaaps" for all platforms (recommended default)
  "installMode": {
    "default": "uaaps",          // Fallback for any platform not listed
    "claude-code": "plugin",     // Deploy as native Claude Code plugin
    "cursor": "uaaps"            // Deploy via UAAPS on Cursor
  }
}

A scalar string shorthand MAY be used when the same mode applies to all platforms:

"installMode": "plugin"   // plugin mode on every platform

Plugin Mode — Vendor-Native Deploy Targets

When installMode resolves to "plugin" for a given platform, the aam CLI translates and deploys artifacts to the platform's native location using the same mapping logic as aam pkg build --target <platform> (see §12.4):

Platform plugin mode install target
claude-code .claude/ (skills, commands, agents, hooks)
cursor .cursor/rules/ (rules as .mdc), .cursor/skills/
copilot .github/instructions/, .github/agents/, .github/skills/
codex AGENTS.md composite inject

CLI Override

Users MAY override the manifest declaration at install time:

# Force plugin mode regardless of manifest
aam install @myorg/code-review --install-mode plugin

# Force uaaps mode regardless of manifest
aam install @myorg/code-review --install-mode uaaps

# Target a specific platform's plugin layout
aam install @myorg/code-review --install-mode plugin --platform claude-code

Deprecation Path

Once a platform vendor implements UAAPS natively, "plugin" mode for that platform becomes a no-op — the aam CLI SHOULD silently promote it to "uaaps" and emit an informational message:

⚠ installMode "plugin" for claude-code is ignored — platform supports UAAPS natively.
  Update package.agent.json to remove the per-platform override.

Package authors SHOULD remove per-platform plugin overrides once the target platform is listed as UAAPS-native in the compatibility matrix (§10).


12. Packaging & Distribution

UAAPS Registry

UAAPS supports two registry types that share the same package format (.aam archives) but differ in transport:

Type When to use
Local filesystem Offline use, air-gapped environments, CI mirrors, monorepos
HTTP remote Public registry, private org registry, hosted SaaS

The aam CLI auto-detects the registry type from the URL scheme:

# HTTP remote registry
aam install @myorg/code-review --registry https://aamregistry.io

# Local filesystem registry
aam install @myorg/code-review --registry file:///opt/aam-registry

# Default registry (HTTP, configured in ~/.aam/config.yaml)
aam install @myorg/code-review

Registry configuration is managed in ~/.aam/config.yaml or .aam/config.yaml:

registries:
  default: https://aamregistry.io   # planned public registry — see §12.6
  sources:
    - name: myorg-remote
      url: https://pkg.myorg.internal/aam
      auth: token                          # see §12.6 for auth types
    - name: myorg-local
      url: file:///opt/aam-registry

Other distribution channels (Git marketplace, npm bridge, direct install, project-bundled) are supported by the aam CLI — see aam_cli_new.md.

12.1 Archive Distribution Format

Packages are distributed as .aam archives (gzipped tar), providing a binary-safe, single-file distribution unit.

my-package-1.0.0.aam   # produced by: aam pkg pack

Archive constraints: - Maximum size: 50 MB - MUST contain package.agent.json at root - No symlinks outside the package directory - No absolute paths - SHA-256 checksum stored in package.agent.lock post-install

12.2 Scoped Package Names

Following npm convention, packages support a @scope/name format for namespacing under an organization or author:

Format Example Use Case
Unscoped code-review Community / public packages
Scoped @myorg/code-review Organization packages

Name rules: - Unscoped: [a-z0-9-], max 64 chars - Scope: @[a-z0-9][a-z0-9_-]{0,63} - Full scoped name: max 130 chars

Filesystem mapping — the @scope/name format is converted using double-hyphen:

Package Name Filesystem Name
code-review code-review
@myorg/code-review myorg--code-review

The -- separator is reversible and unambiguous because valid name segments cannot start with hyphens.

12.3 Dist-Tags

Dist-tags are named aliases for package versions, enabling installs like aam install @org/agent@stable or enterprise gates like bank-approved.

Default Tags

Tag Behavior
latest Automatically set to the newest published version
stable Opt-in; set manually via aam dist-tag add

Custom Tags

Organizations can define arbitrary tags (e.g., staging, bank-approved, qa-passed).

Tag rules: - Lowercase alphanumeric + hyphens only - Max 32 characters - Cannot be a valid SemVer string (prevents ambiguity)

Manifest Declaration (optional)

// package.agent.json
{
  "name": "@myorg/agent",
  "version": "1.2.0",
  "dist-tags": {
    "stable": "1.1.0",     // Maintained by publisher
    "latest": "1.2.0"
  }
}

12.4 Portable Bundles

A portable bundle is a self-contained, pre-compiled archive for a specific target platform. It contains artifacts already transformed to the platform's native format — no install-time resolution needed.

Use case: Distributing packages via Slack, email, or air-gapped environments without a registry.

aam pkg build --target cursor
# → dist/my-package-1.0.0-cursor.bundle.aam

aam pkg build --target copilot
# → dist/my-package-1.0.0-copilot.bundle.aam

aam pkg build --target all
# → one bundle per configured platform

Bundle structure (tar.gz internally):

my-package-1.0.0-cursor.bundle.aam
├── bundle.json               # Bundle manifest
├── .cursor/
│   ├── skills/...            # Pre-compiled platform artifacts
│   ├── rules/...
│   └── commands/...
└── package.agent.json        # Original manifest for reference

bundle.json schema:

{
  "format": "uaaps-bundle",
  "version": "1.0",
  "package": "@author/my-agent",
  "package_version": "1.2.0",
  "target": "cursor",
  "built_at": "2026-02-19T14:30:00Z",
  "checksum": "sha256:...",
  "artifacts": [
    { "type": "skill", "name": "my-skill", "path": ".cursor/skills/author--my-skill/" },
    { "type": "agent", "name": "my-agent", "path": ".cursor/rules/agent-author--my-agent.mdc" }
  ]
}

Installing from a bundle:

aam install ./dist/my-package-1.0.0-cursor.bundle.aam
# Deploys immediately — no dependency resolution needed

12.5 Local Filesystem Registry

A local filesystem registry is a directory tree on disk following a defined layout. No server process is required. It is suitable for offline installs, CI artifact mirrors, and air-gapped enterprise environments.

Directory Layout

<registry-root>/
├── index.json                          # Full package index (all packages + versions)
├── packages/
│   ├── code-review/                    # Unscoped package
│   │   ├── meta.json                   # Package metadata (all versions)
│   │   └── versions/
│   │       ├── 1.0.0.aam
│   │       ├── 1.0.0.aam.sha256        # Detached SHA-256 checksum
│   │       ├── 1.1.0.aam
│   │       └── 1.1.0.aam.sha256
│   └── myorg--code-review/             # Scoped package (@myorg/code-review)
│       ├── meta.json
│       └── versions/
│           ├── 2.0.0.aam
│           └── 2.0.0.aam.sha256
└── dist-tags.json                      # Global dist-tag → version map

index.json

A flat list of all packages in the registry. Implementations MUST regenerate this file after every publish or unpublish operation.

{
  "formatVersion": 1,
  "updatedAt": "2026-02-22T14:00:00Z",
  "packages": [
    { "name": "code-review",         "latest": "1.1.0", "versions": ["1.0.0", "1.1.0"] },
    { "name": "@myorg/code-review",  "latest": "2.0.0", "versions": ["2.0.0"] }
  ]
}

packages/<name>/meta.json

Per-package metadata covering all published versions.

{
  "name": "@myorg/code-review",
  "versions": {
    "2.0.0": {
      "version": "2.0.0",
      "description": "Code review skills for myorg",
      "author": "myorg",
      "publishedAt": "2026-02-20T10:00:00Z",
      "integrity": "sha256-abc123...",
      "tarball": "versions/2.0.0.aam"
    }
  },
  "dist-tags": {
    "latest": "2.0.0",
    "stable": "2.0.0"
  }
}

dist-tags.json

Registry-wide dist-tag snapshot for fast tag resolution without reading every meta.json.

{
  "code-review":        { "latest": "1.1.0", "stable": "1.0.0" },
  "@myorg/code-review": { "latest": "2.0.0" }
}

Filesystem Registry Rules

Rule Requirement
Package directory name MUST use the scope--name mapping (§12.2) MUST
Every .aam file MUST have a sibling .aam.sha256 file MUST
index.json MUST be regenerated atomically after each mutation MUST
Symlinks outside the registry root are forbidden MUST NOT
The registry root MAY be read-only (install-only mirror) MAY

CLI Commands for Local Registries

# Initialise a new local registry
aam registry init file:///opt/aam-registry

# Publish a package to a local registry
aam pkg publish --registry file:///opt/aam-registry

# Rebuild index.json after manual archive placement
aam registry reindex file:///opt/aam-registry

# List all packages in a local registry
aam registry ls file:///opt/aam-registry

12.6 HTTP Registry Protocol

Status: Draft — Endpoint signatures and auth model are defined below. Full request/response schemas, pagination rules, rate-limit headers, and error codes will be detailed in a dedicated Registry Protocol document in a future spec revision. The endpoint list is preliminary and subject to change.

An HTTP registry is an HTTPS server implementing the UAAPS Registry Protocol. The planned official public registry is https://aamregistry.io (under development). Organizations MAY self-host a private registry.

Base URL

All endpoints are relative to the registry base URL. Implementations MUST serve the API over HTTPS. Plain HTTP MUST NOT be used for registries handling private packages or authentication tokens.

Endpoint Index

Method Path Purpose Auth required
GET / Registry metadata & capabilities No
GET /packages List all packages (paginated) No (public) / Yes (private)
GET /packages/:name Package metadata (all versions) No (public) / Yes (private)
GET /packages/:name/:version Single version metadata No (public) / Yes (private)
GET /packages/:name/:version/tarball Download .aam archive No (public) / Yes (private)
GET /packages/:name/:version/signature Fetch signature bundle No
GET /packages/:name/dist-tags List dist-tags for package No
PUT /packages/:name/dist-tags/:tag Set a dist-tag Yes
DELETE /packages/:name/dist-tags/:tag Remove a dist-tag Yes
POST /packages Publish a new package version Yes
DELETE /packages/:name/:version Unpublish a version Yes
GET /approvals List pending approval requests Yes
POST /approvals/:id/approve Approve a publish request Yes
POST /approvals/:id/reject Reject a publish request Yes

Authentication

HTTP registries MAY require authentication. The aam CLI supports the following auth types, configured per registry in ~/.aam/config.yaml:

Auth type Config value Transport
No auth (public) auth: none
Static token auth: token Authorization: Bearer <token> header
OIDC / Sigstore keyless auth: oidc Short-lived token via OIDC provider
Basic (legacy, not recommended) auth: basic Authorization: Basic <base64> header
# ~/.aam/config.yaml
registries:
  sources:
    - name: myorg
      url: https://pkg.myorg.internal/aam
      auth: token
      token: "${MYORG_AAM_TOKEN}"       # resolved from environment variable

Tokens MUST be stored in environment variables or a secrets manager. Tokens MUST NOT be committed to version control. The aam login command handles interactive token acquisition and secure local storage.

aam login --registry https://pkg.myorg.internal/aam   # interactive login
aam logout --registry https://pkg.myorg.internal/aam  # remove stored credential
aam whoami --registry https://pkg.myorg.internal/aam  # show current identity

Error Response Format

All error responses MUST use application/json with this structure:

{
  "error": {
    "code": "PACKAGE_NOT_FOUND",
    "message": "Package @myorg/code-review@3.0.0 does not exist.",
    "docs": "https://aamregistry.io/errors/PACKAGE_NOT_FOUND"
  }
}

Standard Status Codes

Code Meaning
200 Success
201 Published successfully
400 Malformed request
401 Authentication required
403 Insufficient permissions
404 Package or version not found
409 Version already exists (publish conflict)
422 Validation failed (manifest schema error)
429 Rate limit exceeded
503 Registry temporarily unavailable

Full pagination headers, rate-limit headers, conditional request support (ETag, If-None-Match), and approval workflow request/response bodies will be defined in the Registry Protocol document.

Structured Error Code Constants

Registries MUST use the following machine-readable error codes in the error.code field of error responses. Clients SHOULD use these codes for programmatic error handling rather than relying on HTTP status codes alone.

Code HTTP Status Description
PACKAGE_NOT_FOUND 404 Package name does not exist
VERSION_NOT_FOUND 404 Specific version does not exist
VERSION_CONFLICT 409 Version already published
MANIFEST_INVALID 422 Manifest failed schema validation
SIGNATURE_INVALID 403 Package signature verification failed
TAG_NOT_FOUND 404 Dist-tag does not exist
UNAUTHORIZED 401 Authentication required
FORBIDDEN 403 Insufficient permissions
RATE_LIMITED 429 Rate limit exceeded
REGISTRY_UNAVAILABLE 503 Registry temporarily unavailable
PACKAGE_YANKED 410 Version is yanked (not available for new resolution)
PACKAGE_DEPRECATED 200 Version is deprecated (returned alongside valid response)

Content Negotiation

Clients SHOULD send an Accept header specifying the versioned media type:

Accept: application/vnd.uaaps.v1+json

Registries MUST also accept application/json as a fallback for clients that do not specify the versioned type. The response Content-Type MUST match the negotiated media type. If the client requests a media type the registry does not support, the registry MUST respond with 406 Not Acceptable.

Pagination

List endpoints (e.g., GET /packages, GET /approvals) MUST support cursor-based pagination.

Query parameters:

Parameter Default Max Description
cursor (none) Opaque token returned by the previous response
limit 50 200 Maximum number of items per page

Response headers:

Header Description
Link: <url>; rel="next" URL for the next page of results
X-Total-Count Total number of items matching the query

When no more results exist, the registry MUST omit the Link header with rel="next". Clients MUST NOT assume a stable ordering across pages unless a sort parameter is provided.

Additional Endpoints

The following endpoints extend the base endpoint index (§12.6) with search, deprecation, and yank operations.

Method Path Purpose Auth required
GET /packages?q=<query>&category=<cat>&sort=<field> Search packages No (public) / Yes (private)
POST /packages/:name/:version/deprecate Deprecate a version Yes
DELETE /packages/:name/:version/deprecate Remove deprecation Yes
POST /packages/:name/:version/yank Yank a version Yes
DELETE /packages/:name/:version/yank Undo yank Yes

The POST /packages/:name/:version/deprecate endpoint accepts a JSON body:

{
  "message": "Use @myorg/agent@2.0.0 instead",
  "replacement": "@myorg/agent@^2.0.0"
}

The replacement field is OPTIONAL and, when provided, MUST be a valid package specifier. The message field is REQUIRED and MUST NOT exceed 280 characters.


12.7 Package Lifecycle

Published packages transition through a defined set of states that control visibility and resolution behavior. UAAPS follows an immutability-first model: once an archive is published, it is permanent.

State Machine

                 deprecate                    yank
  ┌───────────┐ ──────────► ┌──────────────┐
  │ Published │              │  Deprecated  │
  └─────┬─────┘ ◄────────── └──────────────┘
        │        undeprecate
        │  yank               undo yank
        ├──────────────────► ┌──────────────┐
        │                    │    Yanked     │
        │ ◄──────────────── └──────────────┘
  • Published → Deprecated: The deprecate command marks a version as deprecated. Deprecated versions remain fully functional but emit warnings during resolution.
  • Published → Yanked: The yank command soft-deletes a version. Yanked versions are hidden from search and new resolution but remain downloadable for existing lock files.
  • There is no hard unpublish. Once published, the archive is permanent.

Behavior by State

State Appears in search Resolved by aam install? Resolved by --frozen? Downloadable?
Published Yes Yes Yes Yes
Deprecated Yes (with warning) Yes (with warning + replacement message) Yes Yes
Yanked No No Yes (lock file integrity preserved) Yes (if in lock file)

CLI Commands

# Deprecate a version with a message
aam deprecate @myorg/agent@1.0.0 "Use @myorg/agent@2.0.0 instead"

# Yank a version (soft-delete)
aam yank @myorg/agent@1.0.0

# Reverse a yank
aam yank --undo @myorg/agent@1.0.0

Manifest Metadata for Deprecation

Publishers MAY declare deprecation metadata directly in package.agent.json. When present, the registry SHOULD surface this information in search results and install warnings.

// package.agent.json
{
  "deprecated": {
    "message": "Use @myorg/agent@2.0.0 instead",
    "replacement": "@myorg/agent@^2.0.0"
  }
}

The message field is REQUIRED when deprecated is present. The replacement field is OPTIONAL and MUST be a valid package specifier when provided.

Immutability Guarantee

Unlike npm's unpublish, UAAPS follows Cargo's immutability model: published archives are permanent. This guarantees that existing lock files always resolve successfully, preventing supply-chain breakage.


13. Dependency Resolution

Dependency resolution is a core differentiator of UAAPS over raw vendor plugin systems. Like npm, pip, or Cargo, the package manager MUST resolve a directed acyclic graph (DAG) of requirements before installation.

13.1 Dependency Types

Type Manifest Key Required to Install? Semantics
Direct dependencies Yes — install fails Other agent packages this package requires to function.
Optional optionalDependencies No — install succeeds Enhances functionality if present. Graceful degradation if absent.
Peer peerDependencies Yes — warn/fail MUST be provided by the host project or another top-level install. Not auto-installed.
System systemDependencies Yes — pre-flight check OS binaries, language runtimes, pip/npm packages, MCP servers.

13.2 Version Constraint Syntax

UAAPS uses SemVer 2.0 with npm-style range operators:

Syntax Meaning Example
1.2.3 Exact version Only 1.2.3
^1.2.3 Compatible with (>=1.2.3 <2.0.0) Minor + patch updates OK
~1.2.3 Approximately (>=1.2.3 <1.3.0) Patch updates only
>=1.2.0 Minimum version 1.2.0 or higher
>=1.0.0 <3.0.0 Range Between 1.0.0 and 2.x.x
* Any version No constraint

13.3 Lock File — package.agent.lock

After resolution, the solver writes a deterministic lock file pinning exact versions:

{
  "lockVersion": 1,
  "resolved": {
    "code-review": {
      "version": "1.3.2",
      "source": "marketplace:anthropics/skills",
      "integrity": "sha256-abc123...",
      "dependencies": {
        "git-utils": "1.0.1"
      }
    },
    "testing-utils": {
      "version": "2.4.0",
      "source": "npm:@aamregistry/testing-utils",
      "integrity": "sha256-def456..."
    },
    "git-utils": {
      "version": "1.0.1",
      "source": "marketplace:anthropics/skills",
      "integrity": "sha256-ghi789..."
    }
  },
  "systemChecks": {
    "python": "3.12.1",
    "binaries": {
      "git": "/usr/bin/git",
      "docker": "/usr/local/bin/docker"
    },
    "pip": {
      "pypdf": "4.1.0",
      "pdfplumber": "0.11.4"
    }
  }
}

Behavior: - If package.agent.lock exists, install uses locked versions (reproducible builds). - aam install reads the lock file; aam update regenerates it. - Lock files SHOULD be committed to version control for deterministic environments.

13.4 Resolution Algorithm

The resolution algorithm is versioned via the resolverVersion field in package.agent.json (default: 1). This allows future changes to resolution semantics without breaking existing packages. Implementations MUST support resolver version 1; higher versions are additive.

Resolution proceeds through five named phases:

Phase 1: Parse — Read root package.agent.json. If package.agent.lock exists and --frozen is set, skip to Phase 5 (locked install).

Phase 2: Build requirement graph — Collect all dependencies, peerDependencies, and optionalDependencies into an initial requirement DAG. Each edge carries a version constraint.

Phase 3: Resolve — For each unresolved requirement, in topological order:

a. fetch_versions — Query the registry (or cache) for all published, non-yanked versions. b. filter_by_constraint — Discard versions not matching the constraint. c. select_version — Pick the highest compatible version using this priority: 1. Locked version (from package.agent.lock, if not --latest) 2. resolutions override (from root manifest) 3. Highest stable version satisfying the constraint 4. Highest pre-release version (only if the constraint explicitly includes a pre-release identifier, e.g. ^1.0.0-beta.1) d. resolve_transitive — Recursively resolve the selected version's own dependencies.

Phase 4: Validate

a. detect_cycles — The resolved graph MUST be acyclic. Circular dependencies are a fatal error. b. check_conflicts — If two packages require incompatible versions of the same dependency, apply the conflict resolution strategy (see §13.9). c. check_peers — Each peerDependency MUST be satisfied by the root package or an ancestor in the graph. Unmet peers produce a warning (or error with --strict-peers). d. check_system — Run system dependency pre-flight checks for all resolved packages.

Phase 5: Commit

a. Write package.agent.lock (unless --frozen, in which case verify existing lock matches resolved graph exactly). b. Install resolved packages to the install directory (§13.11). c. Hoist shared dependencies where version-compatible (§13.11).

Pre-release Version Handling

Pre-release versions (e.g. 1.0.0-beta.1) follow SemVer 2.0 precedence rules:

Constraint Matches 1.0.0-beta.1? Matches 1.0.0? Rationale
^1.0.0 No Yes Caret ranges exclude pre-releases unless the constraint itself has a pre-release tag
^1.0.0-beta.0 Yes Yes Constraint includes pre-release tag, so pre-releases within the same major.minor.patch are eligible
>=1.0.0-beta.1 Yes Yes Explicit comparison includes the pre-release
* Yes Yes Wildcard matches everything

This matches npm's pre-release semantics: a range like ^1.0.0 is understood as requesting stable releases, while ^1.0.0-beta.0 explicitly opts into the pre-release channel.

Resolver Version

// package.agent.json
{
  "resolverVersion": 1
}
Version Behavior
1 (default) Current algorithm as defined above.
Future versions Will be defined in subsequent spec revisions. Implementations encountering an unknown resolver version MUST emit an error and refuse to resolve.

The resolverVersion field is OPTIONAL. When absent, version 1 is assumed.

13.5 Resolution Strategies

Strategy Flag Behavior
Default (none) Highest compatible version per constraint.
Locked --frozen Only use versions from lock file. Fail if lock is stale.
Minimal --minimal Lowest version satisfying each constraint (CI reproducibility).
Latest --latest Ignore constraints, install latest of everything (dangerous).
Dry-run --dry-run Resolve and display tree without installing.

13.6 Package Dependencies (Agent-to-Agent)

Agent packages can depend on other agent packages. This enables composition:

// package.agent.json for "full-stack-review"
{
  "name": "full-stack-review",
  "version": "1.0.0",
  "dependencies": {
    "code-review": "^1.0.0",           // Reuse code review skills
    "security-scanning": "^2.0.0",     // Reuse security agents
    "testing-utils": "^1.5.0"          // Reuse test generation
  }
}

Transitive resolution: If code-review@1.3.2 itself depends on git-utils@^1.0.0, the solver installs git-utils automatically.

Namespace isolation: Each installed dependency's skills, commands, and agents are namespaced under dependency-name:artifact-name to prevent collisions.

13.7 System Dependencies

System dependencies are not installed by the package manager — they are verified via pre-flight checks.

"systemDependencies": {
  // === Runtime requirements ===
  "python": ">=3.10",                  // Check: python3 --version
  "node": ">=18",                      // Check: node --version

  // === Package manager installs ===
  "packages": {
    "pip": ["pypdf>=4.0", "pdfplumber"],
    "npm": ["prettier@^3.0", "eslint"],
    "brew": ["graphviz", "poppler"],
    "apt": ["poppler-utils", "tesseract-ocr"],
    "cargo": ["ripgrep"]
  },

  // === Binary availability ===
  "binaries": ["git", "docker", "gh"],  // Must exist on $PATH

  // === MCP server requirements ===
  "mcp-servers": ["filesystem", "github"]
}

Pre-flight Check Flow

aam install my-package
  → Checking system dependencies...
  ✓ python 3.12.1 (>=3.10)
  ✓ node 20.11.0 (>=18)
  ✓ git found at /usr/bin/git
  ✗ docker not found on $PATH
  ✓ pip: pypdf 4.1.0 installed
  ✗ pip: pdfplumber not installed

  ⚠ Missing system dependencies:
    • docker: Install via https://docs.docker.com/get-docker/
    • pdfplumber: Run `pip install pdfplumber`

  Install package anyway? [y/N/auto-install]

Auto-Install Behavior

Package Manager Auto-install Flag
pip ✅ Supported --install-system-deps
npm ✅ Supported --install-system-deps
brew / apt ⚠️ Prompt only Requires --install-system-deps --allow-sudo
binaries ❌ Never Manual install instructions provided
mcp-servers ✅ Via MCP registry --install-system-deps

13.8 Skill-Level Dependencies

Individual skills can declare their own dependencies in SKILL.md frontmatter, complementing the package-level manifest:

---
name: pdf-processing
description: Extract text and fill forms in PDFs.
compatibility:
  requires:
    - python>=3.10
    - pypdf>=4.0
    - pdfplumber
  packages:                          # UAAPS extension
    - code-review/git-utils          # Depends on another skill
  optional:
    - tesseract-ocr                  # For OCR, graceful degradation
---

Resolution order: 1. Package-level systemDependencies checked first (covers most skills). 2. Skill-level compatibility.requires checked on activation (lazy check). 3. Skill-level compatibility.packages resolved transitively if referencing other skills.

13.9 Conflict Resolution

Version Conflicts

When two packages require incompatible versions of the same dependency:

my-app
├── code-review@1.3.2
│   └── git-utils@^1.0.0  →  resolves to 1.0.5
└── deployment@2.0.0
    └── git-utils@^2.0.0  →  resolves to 2.1.0   ← CONFLICT

Resolution strategies:

Strategy Behavior When to Use
Fail (default) Error, refuse to install Safety-first environments
Duplicate Install both versions, isolate namespaces When skills don't share state
Override Use resolutions field to force a version When you know compatibility holds
Peer promote Lift to peerDependencies, let host decide Library packages

Resolution Overrides

// package.agent.json
{
  "resolutions": {
    "git-utils": "2.1.0"              // Force all transitive refs to this version
  }
}

Skill Name Conflicts

When two installed packages export skills with the same name:

  • Skills are namespaced by package: code-review:lint-check vs testing:lint-check.
  • If a skill is referenced without namespace, the closest scope wins (project > personal > plugin).
  • Explicit disambiguation: aam resolve skill lint-check lists all matches.

13.10 Dependency Tree Commands

Command Description
aam install Install all dependencies from manifest (or lock file).
aam install <pkg> Add a package and resolve.
aam update Re-resolve all dependencies, update lock file.
aam update <pkg> Update a single package within constraints.
aam tree Display the full resolved dependency tree.
aam tree --depth=1 Show only direct dependencies.
aam why <pkg> Explain why a package is installed (which parent requires it).
aam check Verify all dependencies are installed and compatible.
aam check --system Run system dependency pre-flight checks only.
aam outdated List packages with newer versions available.
aam audit Check dependencies for known security issues.
aam dedupe Flatten tree, remove unnecessary duplicates.
aam prune Remove installed packages not in the dependency tree.

13.11 Install Directory Layout

.agent-packages/                       # Install root (analogous to node_modules/)
├── .lock                              # package.agent.lock (symlink or copy)
├── code-review/                       # Installed package
│   ├── package.agent.json
│   ├── skills/
│   │   └── lint-review/
│   │       └── SKILL.md
│   └── commands/
│       └── review.md
├── testing-utils/                     # Transitive dependency
│   ├── package.agent.json
│   └── skills/
│       └── test-gen/
│           └── SKILL.md
└── git-utils/                         # Shared dependency (hoisted)
    ├── package.agent.json
    └── skills/
        └── git-diff/
            └── SKILL.md

Hoisting: Like npm, shared dependencies are hoisted to the top level of .agent-packages/ when version-compatible. This reduces duplication and simplifies skill discovery.

13.12 Dependency Resolution in CI/CD

For reproducible builds in CI environments:

# .github/workflows/agent-check.yml
- name: Install agent packages (frozen)
  run: aam install --frozen

- name: Verify system dependencies
  run: aam check --system

- name: Audit for security issues
  run: aam audit --severity=high

The --frozen flag ensures CI uses exactly the versions from package.agent.lock, failing if the lock file is out of date.

13.13 Install Modes & Package Cache

The aam CLI supports three install modes. The mode determines where resolved packages are written and whether a shared cache is consulted.

Install Mode Overview

Mode Install Location Lock File Used Typical Use
Project-local (default) ./.agent-packages/ ./package.agent.lock Per-project installs, checked into VCS
Global ~/.aam/packages/ ~/.aam/global.lock User-wide utilities, not project-specific
CI ephemeral $AAM_INSTALL_DIR or system temp ./package.agent.lock (read-only) Fresh environment per pipeline run
aam install                          # Project-local (default)
aam install --global                 # Global install
aam install --frozen                 # Project-local, lock file is authoritative
AAM_INSTALL_DIR=/tmp/aam aam install # CI ephemeral override

Installing from a Specific Vendor

When multiple vendors publish a package with the same base name, always qualify with the @scope prefix to target the correct author:

# Unscoped — installs the community package named "code-review"
aam install code-review

# Scoped — installs @myorg's code-review, not @otherorg's
aam install @myorg/code-review
aam install @otherorg/code-review

# Specific version from a specific vendor
aam install @myorg/code-review@1.3.2
aam install @myorg/code-review@^1.0.0

# Specific dist-tag from a vendor (e.g. enterprise-approved tag)
aam install @myorg/code-review@stable
aam install @myorg/code-review@bank-approved

# From a private registry owned by a specific vendor
aam install @myorg/code-review --registry https://pkg.myorg.internal/aam

Both @myorg/code-review and @otherorg/code-review can be installed simultaneously — they are stored under myorg--code-review/ and otherorg--code-review/ respectively (see §12.2 and §13.13 author collision rule). Skills from each are namespaced accordingly: myorg--code-review:lint-check vs otherorg--code-review:lint-check.

Shared Download Cache

All install modes share a download cache at ~/.aam/cache/. The cache stores downloaded .aam archives keyed by package@version + integrity hash. Packages are never re-downloaded if a valid cached copy exists.

~/.aam/
├── cache/                                        # Shared download cache (all modes)
│   ├── myorg--code-review-1.3.2-sha256-abc123.aam
│   ├── otherorg--code-review-2.0.0-sha256-def456.aam
│   └── testing-utils-2.4.0-sha256-ghi789.aam
├── packages/                                     # Global install root
│   ├── myorg--code-review/                       # @myorg/code-review
│   ├── otherorg--code-review/                    # @otherorg/code-review
│   └── testing-utils/                            # unscoped package
├── global.lock                                   # Global install lock file
└── config.yaml                                   # CLI configuration

Author collision rule: The @scope/namescope--name filesystem mapping (defined in §12.2) is the sole mechanism preventing collisions between same-named packages from different authors. Two packages that share a base name but differ in scope (@myorg/code-review and @otherorg/code-review) MUST be stored under distinct directory names (myorg--code-review/ and otherorg--code-review/ respectively) in both the cache and global packages directory. Implementations MUST NOT flatten scoped names to their bare base name in any install root.

Cache Invalidation Rules

Trigger Behaviour
Integrity hash mismatch Cache entry is rejected; package re-downloaded
aam cache clean Entire cache cleared
aam cache clean <pkg>@<version> Single entry evicted
aam install --no-cache Cache bypassed for this run; result not cached
Registry republish of same version Cache is not invalidated automatically — use --no-cache or bump version

Cached archives MUST be verified against their sha256 integrity hash before extraction. A corrupted or tampered archive MUST be rejected and the cache entry evicted.

Lock File Precedence Rules

When both a user-level (~/.aam/global.lock) and project-level (./package.agent.lock) lock file exist, resolution follows this precedence order (highest wins):

  1. ./package.agent.lock — project lock file always takes precedence for project-local installs.
  2. ~/.aam/global.lock — governs --global installs only; never consulted for project-local resolution.
  3. resolutions field in package.agent.json — overrides both lock files for named packages (see §13.9).

Implementations MUST NOT mix project-local and global lock files in the same resolution pass. A global install (--global) MUST NOT read or modify ./package.agent.lock.

Global vs Local Resolution Rules

Rule Project-local Global
Artifact lookup scope ./.agent-packages/ only ~/.aam/packages/ only
Lock file written ./package.agent.lock ~/.aam/global.lock
Hoisting target ./.agent-packages/ root ~/.aam/packages/ root
Peer dependency resolution Ancestor chain within project tree Global install tree
Available to agent platforms Only when agent runs in project directory Always — platform SHOULD scan ~/.aam/packages/

Agent platforms SHOULD load globally installed packages as a lower-priority fallback after project-local packages. Project-local packages MUST shadow global packages of the same name.

13.14 Workspaces

Workspaces enable monorepo development — multiple UAAPS packages in a single repository sharing a common dependency tree and lock file.

Workspace Configuration

Place a workspace.agent.json file at the monorepo root. The packages array uses glob patterns to locate member packages:

{
  "workspace": {
    "packages": ["packages/*", "internal/*"]
  }
}

Each glob pattern MUST resolve to directories containing a valid package.agent.json.

Workspace Rules

Rule Requirement
workspace.agent.json MUST be at the repository root MUST
Each glob pattern MUST resolve to directories containing package.agent.json MUST
All workspace members share a single package.agent.lock at the root MUST
Inter-workspace dependencies are resolved from the local filesystem (no registry fetch) MUST
Workspace members MAY have different versions MAY
aam install at the root installs dependencies for ALL members MUST
aam install within a member directory installs only that member's dependencies SHOULD

CLI Commands

aam install                        # Install all workspace members
aam install --workspace packages/my-pkg  # Install specific member
aam test --workspace               # Run tests across all members
aam publish --workspace            # Publish all changed members
aam workspace list                 # List all workspace members

Lock File Behavior

The workspace shares a single package.agent.lock at the root. All members' dependencies are resolved together, ensuring version consistency across the monorepo. The lock file includes a workspaceMembers field listing each member and its resolved dependencies.


14. Package Signing & Verification

AAM supports multiple levels of integrity and authenticity verification for packages.

14.1 Signing Methods

Method Description When to Use
Checksum SHA-256 hash of archive, always applied Automatic — all packages
Sigstore Keyless, identity-based via OIDC Recommended for public packages
GPG Traditional key-based signing Teams with existing GPG infrastructure
Registry Attestation Server-side signatures Automated trust for verified registries

14.2 Signing Flow

Author                          Registry                         User
  │  aam pkg publish --sign       │                               │
  ├──────────────────────────────►│                               │
  │  1. Calculate SHA-256         │                               │
  │  2. Sign with Sigstore / GPG  │  Verify signature             │
  │  3. Upload archive+signature  │  Store attestation            │
  │                               │         aam install pkg       │
  │                               │◄──────────────────────────────┤
  │                               │  1. Download archive          │
  │                               ├──────────────────────────────►│
  │                               │  2. Verify checksum           │
  │                               │  3. Verify signature          │
  │                               │  4. Check trust policy        │

14.3 Verification Policy

Configure in global ~/.aam/config.yaml or project .aam/config.yaml:

security:
  require_checksum: true           # Always enforced (non-configurable)
  require_signature: false         # Require signed packages
  trusted_identities:              # Sigstore OIDC identities to trust
    - "*@myorg.com"
  trusted_keys:                    # GPG key fingerprints to trust
    - "ABCD1234..."
  on_signature_failure: warn       # warn | error | ignore

14.4 Lock File Integrity

The package.agent.lock stores SHA-256 hashes for every resolved package. On --frozen install, hashes are re-verified — any mismatch aborts with an error, preventing supply-chain tampering.

14.5 Provenance Attestation (SLSA-Aligned)

Beyond signing (which proves who published), provenance proves how a package was built — from which source, at which commit, using which build system. UAAPS defines four provenance levels aligned with the SLSA framework.

Provenance Levels

Level Name Requirement Verification
L0 Checksum SHA-256 hash of archive (always applied) aam install verifies hash automatically
L1 Signed Author identity verified via Sigstore OIDC or GPG aam install verifies signature per trust policy
L2 Provenance Build provenance attestation linking archive to source repo, commit, and build system aam audit --provenance verifies attestation chain
L3 Reproducible Deterministic build from source — anyone can rebuild and obtain the same archive hash aam audit --reproducible rebuilds from source and compares hash

Each level subsumes all requirements of lower levels (L3 includes L2 includes L1 includes L0).

Provenance Attestation Format

Provenance attestations follow the in-toto attestation framework with a UAAPS-specific predicate type.

{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "@myorg/code-review-1.3.0.aam",
      "digest": { "sha256": "abc123..." }
    }
  ],
  "predicateType": "https://uaaps.github.io/provenance/v1",
  "predicate": {
    "builder": {
      "id": "https://github.com/actions/runner"
    },
    "buildType": "https://uaaps.github.io/build/aam-pack/v1",
    "invocation": {
      "configSource": {
        "uri": "git+https://github.com/myorg/code-review@refs/tags/v1.3.0",
        "digest": { "sha1": "def456..." },
        "entryPoint": ".github/workflows/publish.yml"
      }
    },
    "metadata": {
      "buildStartedOn": "2026-02-22T14:30:00Z",
      "buildFinishedOn": "2026-02-22T14:30:45Z",
      "completeness": {
        "parameters": true,
        "environment": true,
        "materials": true
      },
      "reproducible": false
    },
    "materials": [
      {
        "uri": "git+https://github.com/myorg/code-review@refs/tags/v1.3.0",
        "digest": { "sha1": "def456..." }
      }
    ]
  }
}

Provenance Field Reference

Field Type Required Description
predicateType string Yes MUST be https://uaaps.github.io/provenance/v1.
predicate.builder.id string Yes URI identifying the build system (e.g. GitHub Actions, GitLab CI).
predicate.buildType string Yes URI identifying the build process type.
predicate.invocation.configSource.uri string Yes Git URI + ref of the source repository.
predicate.invocation.configSource.digest object Yes Cryptographic digest of the source commit.
predicate.metadata.reproducible boolean No Whether the build is deterministic. Required for L3.
predicate.materials array Yes All input materials (source repos, dependencies) with digests.

CLI Commands

# Generate provenance attestation during publish
aam pkg publish --provenance

# Verify provenance of an installed package
aam audit --provenance @myorg/code-review

# Attempt reproducible build verification (L3)
aam audit --reproducible @myorg/code-review

# View provenance attestation
aam inspect provenance @myorg/code-review@1.3.0

Verification Policy

The provenance level requirement is configured alongside signing policy in config.yaml:

security:
  require_provenance: L1            # L0 | L1 | L2 | L3
  on_provenance_failure: warn       # warn | error | ignore

Consumers MUST ignore unrecognized fields in the provenance attestation to support forward-compatible predicate evolution.


15. Governance & Policy Gates

Governance controls who can install what, and provides a tamper-evident record of actions. Policy gates are client-side (no server required); approval workflows require an HTTP registry.

15.1 Client-Side Policy Gates

Configured in config.yaml, enforced by the CLI before any install or publish:

governance:
  install_policy:
    allowed_scopes: ["@myorg", "@trusted-vendor"] # only allow these scopes
    require_signature: true                        # block unsigned packages
    require_tag: "stable"                          # only install tagged versions
    blocked_packages: ["@sketchy/*"]               # glob-matched block list
  publish_policy:
    require_signature: true                        # must sign before publishing
Gate Trigger Effect
allowed_scopes aam install Block packages outside listed scopes
require_signature aam install Block unsigned packages
require_tag aam install Only allow versions with a specific tag
blocked_packages aam install Block specific packages (glob patterns)
require_signature aam pkg publish Require signing before publish

15.2 Approval Workflows (HTTP Registry)

When require_approval: true is set and publishing to an HTTP registry:

  1. aam pkg publish → uploads with approval_status: pending
  2. Approvers receive notification
  3. Approver runs aam approve @org/agent@1.2.0
  4. Only approved packages appear in search/install

15.3 Audit Events

Every registry mutation is logged immutably:

Event Description
package.publish New version published
version.deprecate Version marked as deprecated
version.yank Version marked as yanked
version.yank.undo Yank reversed
tag.set Dist-tag added or updated
tag.remove Dist-tag removed
version.approve Version approved
version.reject Version rejected
ownership.transfer Package ownership transferred

Structured Audit Log Format

Registries MUST store audit events in a structured, machine-parseable format. Each log entry MUST conform to the following schema:

{
  "version": 1,
  "event": "package.publish",
  "timestamp": "2026-02-22T14:30:00Z",
  "actor": {
    "identity": "user@myorg.com",
    "auth_method": "oidc"
  },
  "target": {
    "package": "@myorg/code-review",
    "version": "1.3.0"
  },
  "metadata": {
    "signature_method": "sigstore",
    "provenance_level": "L2",
    "source_commit": "abc123def456",
    "archive_integrity": "sha256-abc123..."
  },
  "registry": "https://pkg.myorg.internal/aam"
}

Audit Log Field Reference

Field Type Required Description
version number Yes Audit log format version. Currently 1.
event string Yes Event type from the audit events table above.
timestamp string Yes ISO 8601 timestamp of the event.
actor.identity string Yes Authenticated identity that performed the action.
actor.auth_method string Yes Authentication method used: oidc, token, basic, anonymous.
target.package string Yes Full package name (scoped or unscoped).
target.version string No Package version affected (absent for package-level events like ownership.transfer).
metadata object No Event-specific metadata. Content varies by event type.
registry string Yes Registry URL where the event occurred.

Querying Audit Logs

aam audit-log @myorg/code-review                     # All events for a package
aam audit-log @myorg/code-review --event publish      # Filter by event type
aam audit-log --actor user@myorg.com --since 2026-01-01  # Filter by actor and date

HTTP registries SHOULD expose audit logs via GET /packages/:name/audit-log with pagination support (see §12.6 Pagination).


16. Validation Rules

Rule Constraint
Package name [a-z0-9-] max 64 chars, or scoped @scope/name max 130 chars
Package version Valid SemVer 2.0
Skill description Max 1024 chars, MUST describe WHAT + WHEN
SKILL.md body RECOMMENDED < 5,000 tokens / 500 lines
Frontmatter Valid YAML, no tabs
Scripts MUST be self-contained or document dependencies
Hook commands MUST handle JSON on stdin
File references Forward slashes only, relative paths
Dependency versions Valid SemVer range syntax (^, ~, >=, exact)
Dependency graph MUST be acyclic — circular dependencies are an error
Peer dependencies MUST be satisfied by root or ancestor package
System dependencies Pre-flight check MUST pass (or explicit --skip-checks)
Lock file integrity Hash MUST match on --frozen install
Namespace uniqueness No two skills with same name within a single package
Resolution overrides resolutions entries MUST reference packages in the tree
Resolver version resolverVersion MUST be a positive integer. Unknown versions are a fatal error.
YAML round-trip package.agent.yaml MUST use YAML 1.2 with JSON-compatible types only (no anchors, aliases, tags, or merge keys)
Workspace config workspace.agent.json packages globs MUST resolve to directories containing package.agent.json
Forward-compatibility Tools MUST NOT reject manifests containing unrecognized fields (see §1 Forward-Compatibility Parsing Rules)
Permission grants permissionGrants keys MUST reference packages listed in dependencies
Provenance level require_provenance in config MUST be one of: L0, L1, L2, L3
Deprecated field When deprecated is present, deprecated.message is REQUIRED
Conformance claim Platforms claiming a conformance level MUST implement ALL requirements of that level

Permissions Security Audit

The following conditions do not fail validation but MUST produce a WARN-level diagnostic during both aam validate and aam install. They indicate a package that may be unintentionally over-privileged.

Condition Warning message
permissions field absent ⚠ No permissions declared — platform-default restrictions apply. Consider adding a permissions field for explicit least-privilege.
shell.allow: true and binaries is empty or absent ⚠ Shell access is unrestricted. Add a binaries allow-list to limit which executables may be invoked.
fs.write contains ** or . ⚠ Filesystem write scope is very broad. Narrow the write globs to only the output paths the package requires.
network.hosts contains * (bare wildcard) ⚠ Network host wildcard allows any host. Specify explicit hosts or scoped wildcards (e.g. *.example.com).
network.schemes contains http ⚠ Plain HTTP is declared as an allowed network scheme. Prefer https unless the target endpoint requires it.

These warnings MUST be displayed to the user and MUST be included in the output of aam validate --json under a warnings array. They MUST NOT block installation or publication.

Vendor Extension Validation

The following rules apply to all x-* keys in package.agent.json:

Rule Constraint
Key naming MUST match x-[a-z0-9-]+, vendor-id max 32 chars
Value type MUST be a JSON object — scalars and arrays are errors
Unknown keys Tools MUST silently ignore unrecognised x-* keys
Count aam validate MUST warn if more than 5 x-* keys are declared
Unregistered vendor-id Registry SHOULD produce a WARN; tools MUST NOT error

The following conditions do not block validation but MUST produce a WARN-level diagnostic:

Condition Warning message
More than 5 x-* keys ⚠ More than 5 vendor extension keys declared. Consider consolidating metadata — extension sprawl can reduce manifest readability.
x-* key vendor-id not in registered platform list ⚠ Vendor-id "<vendor-id>" is not a registered platform. This key will not be indexed by the registry.
x-* value is not an object ✕ Vendor extension "x-<vendor-id>" value MUST be a JSON object. Scalar and array values are invalid. (this condition is an error, not a warning)

17. Security Considerations

  1. Only install packages from trusted sources — skills can execute code and access the filesystem.
  2. Audit all scripts before enabling — look for network calls, file access, and unexpected operations.
  3. Use allowed-tools to restrict skill capabilities where possible.
  4. Hook commands run as the user — apply least-privilege principles.
  5. MCP servers SHOULD NOT expose secrets; use environment variable injection.
  6. Environment Variables: Packages MUST explicitly declare required environment variables in the env manifest field. Host platforms SHOULD prompt users securely for these values rather than storing them in plaintext.
  7. Permissions Model: The permissions field in package.agent.json is OPTIONAL. When it is present, platforms MUST enforce it as defined in §17.1 Permissions Model below. A platform that ignores a declared permissions field is non-compliant. When the field is absent, platforms MAY apply their own default restrictions.
  8. Sandbox untrusted packages before production deployment.
  9. Verify lock file integritypackage.agent.lock hashes prevent supply-chain tampering.
  10. Run aam audit regularly to check dependencies for known vulnerabilities.
  11. System dependency auto-install never uses sudo without explicit --allow-sudo flag.
  12. Transitive dependencies SHOULD be audited — aam tree reveals the full graph.

17.1 Permissions Model

The permissions field in package.agent.json is OPTIONAL. When present, it declares the minimum capabilities a package requires. Packages SHOULD request the narrowest permissions needed to function.

When permissions is present, platforms MUST deny any capability not explicitly declared within it. When permissions is absent, platform-default restrictions apply and no specific enforcement is mandated by this spec.

Filesystem Permissions

"fs": {
  "read":  ["src/**", "docs/**"],   // Glob patterns the package MAY read
  "write": ["output/**"]            // Glob patterns the package MAY write or delete
}
Rule Requirement
Patterns use forward-slash glob syntax (**, *, ?) MUST
Paths are relative to the project root at install time MUST
Absolute paths are forbidden in fs permissions MUST NOT
A write grant does not imply read — declare both if needed MUST
Attempts to access paths outside declared globs MUST be denied MUST
Platform MAY present a user prompt to approve write grants at install time MAY

Network Permissions

"network": {
  "hosts":   ["api.github.com", "*.npmjs.org"],
  "schemes": ["https"]
}
Rule Requirement
hosts entries are exact hostnames or single-level wildcard (*.example.com) MUST
Multi-level wildcards (**.example.com) are forbidden MUST NOT
schemes restricts URI scheme; permitted values: https, http, wss, ws MUST
If schemes is omitted, only https is permitted MUST
IP address literals and CIDR ranges are not supported as hosts values MUST NOT
Outbound connections to hosts or schemes not in the declared list MUST be blocked MUST
Omitting network entirely means no network access is permitted MUST

Shell Permissions

"shell": {
  "allow":    true,
  "binaries": ["git", "npm", "python3"]
}
Rule Requirement
allow: false (or omitting shell) prohibits all shell execution MUST
allow: true without binaries permits any binary — use only when unavoidable SHOULD NOT
When binaries is present, only the listed executable names MAY be invoked MUST
Binary names are matched against the basename of the resolved executable MUST
Shell metacharacters and interpreter flags (-c, eval, etc.) that bypass the allow-list MUST be rejected MUST

Enforcement Requirements

Platforms MUST enforce permissions at runtime. The following violations MUST result in a denied operation and a structured error surfaced to the user:

Violation Required response
fs read outside declared globs Deny + log
fs write/delete outside declared globs Deny + log
Network connection to undeclared host Deny + log
Network connection on undeclared scheme Deny + log
Shell invocation when allow: false Deny + log
Shell invocation of binary not in binaries Deny + log

Platforms that cannot enforce a specific permission category (e.g., no network interception) MUST warn the user at install time that enforcement is partial and indicate which categories are unenforced.

Permission Audit at Install and Validate Time

Both aam install and aam validate MUST run a permission security audit and emit WARN-level output for over-privileged or missing declarations (see full condition list in §16 Validation Rules → Permissions Security Audit). These warnings MUST NOT block the operation — they are advisory only.

Example output during install:

$ aam install @myorg/data-exporter

  ✓ Resolved 3 packages
  ✓ Integrity verified
  ⚠ Permission warnings for @myorg/data-exporter:
      • No permissions declared — platform-default restrictions apply.
      • (Add a "permissions" field for explicit least-privilege.)

  Installed successfully. Run `aam validate --permissions` for details.

The aam validate --permissions flag runs the audit in isolation without a full package validation pass.


17.2 Threat Model

This section identifies the primary threat actors, attack surfaces, and mitigations relevant to the UAAPS ecosystem. Implementations SHOULD use this model to guide security decisions.

Threat Actors

Actor Capability Motivation
Malicious package author Publishes a package with harmful skills, hooks, or scripts Data exfiltration, credential theft, resource hijacking
Compromised registry Serves tampered archives or metadata Supply-chain poisoning at scale
Dependency confusion attacker Publishes a public package with the same name as a private one Hijack installs that resolve against the public registry
Man-in-the-middle Intercepts registry traffic Serve tampered archives, steal auth tokens
Compromised maintainer Publishes a malicious update to an existing trusted package Supply-chain attack via trusted channel
Typosquatter Publishes packages with names similar to popular ones Trick users into installing malicious packages

Attack Surfaces

Surface Vector Relevant Artifacts
Skill scripts Execute arbitrary code during skill activation (scripts/) Skills
Hook commands Execute shell commands at lifecycle events with user privileges Hooks
MCP servers Launch external processes with network and filesystem access MCP configs
Shell binaries Invoke system executables with attacker-controlled arguments Skills, hooks
Network access Exfiltrate data or download additional payloads Skills, MCP servers
Filesystem write Overwrite project files, inject malicious code, plant backdoors Skills, hooks
Environment variables Leak secrets declared in env to untrusted code All artifacts
Transitive dependencies Inject malicious code via a deeply-nested dependency Dependencies
Lock file manipulation Modify package.agent.lock to point to tampered archives Lock file

Mitigations Mapped to Spec Mechanisms

Threat Mitigation Spec Mechanism
Malicious package author Permissions restrict what a package can do §17.1 Permissions Model
Malicious package author Code review before install aam validate, aam audit
Compromised registry Checksum verification on every install §14.4 Lock File Integrity
Compromised registry Signature verification (Sigstore/GPG) §14.1–§14.3 Signing
Compromised registry Provenance attestation (SLSA L2+) §14.5 Provenance
Dependency confusion Scoped package names (@org/name) §12.2 Scoped Names
Dependency confusion Policy gates restrict allowed scopes §15.1 Policy Gates
MITM HTTPS-only registry transport §12.6 HTTP Registry Protocol
MITM Lock file integrity hashes §14.4 Lock File Integrity
Compromised maintainer Yank + deprecation (but no unpublish) §12.7 Package Lifecycle
Compromised maintainer Approval workflows before publish §15.2 Approval Workflows
Typosquatting Registry-side name similarity checks Registry implementation (out of scope)
Transitive dependencies aam tree exposes full graph; aam audit checks vulnerabilities §13.10 Dependency Commands
Lock file manipulation --frozen verifies hashes against locked values §14.4 Lock File Integrity
Env variable leakage Explicit env declarations with required/optional flags §3 Manifest env field

Residual Risks

The following risks are not fully mitigated by this specification. Implementations and users SHOULD apply additional controls:

Risk Why It Persists Recommended Mitigation
Zero-day in trusted package No mechanism prevents a trusted author from introducing a subtle vulnerability Automated security scanning, eval regression testing
Permission escalation via shell shell.binaries: ["git"] permits git subcommands that may invoke arbitrary executables (e.g. --upload-pack) Platforms SHOULD sanitize arguments to allowed binaries
MCP server compromise MCP servers run as external processes with broad access Platforms SHOULD sandbox MCP servers with minimal permissions
Social engineering Users may approve dangerous permission prompts without reading UX design: require explicit confirmation for shell.allow: true and broad fs.write
Registry account takeover Not addressed by this spec Registry implementations SHOULD enforce MFA and session timeouts

17.3 Transitive Permission Composition

When a package depends on other packages, each with their own permissions declarations, the effective permission set must be determined. UAAPS uses a root-governs model.

Composition Rules

Rule Description
Root authority The root package's permissions field (the package installed directly by the user) is the maximum permission boundary. No dependency can exceed it.
Dependency permissions Each dependency's permissions are checked against the root's permissions. If a dependency requests a capability not granted by the root, the platform MUST deny it at runtime.
Absent permissions If the root package omits permissions, platform defaults apply to the entire dependency tree. If a dependency declares permissions but the root does not, the dependency's permissions are advisory only.
Union within root boundary The effective permission set is the union of all dependencies' declared permissions, intersected with the root's permission boundary.

Permission Escalation via permissionGrants

If a root package needs to explicitly grant broader permissions to a specific dependency (e.g., a dependency needs shell access but the root normally restricts it), the root MUST declare this in a permissionGrants field:

// Root package's package.agent.json
{
  "permissions": {
    "fs": { "read": ["src/**"], "write": ["output/**"] },
    "shell": { "allow": false }
  },
  "permissionGrants": {
    "code-formatter": {
      "shell": { "allow": true, "binaries": ["prettier"] }
    }
  }
}
Rule Requirement
permissionGrants keys MUST be package names from dependencies MUST
Granted permissions MUST NOT exceed what the root could declare for itself MUST
aam validate MUST warn when permissionGrants grants shell.allow: true without a binaries list MUST
aam install MUST display permission grants to the user for review MUST
If a dependency requires permissions not granted by root or permissionGrants, installation SHOULD warn SHOULD

Effective Permission Calculation

effective(dep) = declared(dep) ∩ (declared(root) ∪ permissionGrants(dep))

Where is the intersection (least-privilege) and is the union of explicit grants. If declared(dep) is absent, the dependency inherits the root's permissions boundary.

Example

Root declares:     fs.read=["src/**"], fs.write=["output/**"], shell.allow=false
Dependency A:      fs.read=["**"], shell.allow=true, binaries=["prettier"]
permissionGrants:  { "A": { shell: { allow: true, binaries: ["prettier"] } } }

Effective for A:   fs.read=["src/**"], fs.write=["output/**"],
                   shell.allow=true, binaries=["prettier"]
                   (fs.read narrowed to root's boundary; shell granted explicitly)

18. Glossary

Term Definition
AAM Agent Artifact Manager — the CLI tool (aam) for installing, publishing, testing, and managing UAAPS packages.
Command A markdown file triggered by user typing /name or referenced as a parameterized template with {{variable}} interpolation. User or agent-invoked.
Skill A folder with SKILL.md that teaches an agent a specific capability. Model-invoked.
Agent A specialized persona definition with agent.yaml + system-prompt.md. Model or user-invoked.
Rule Persistent context (coding standards, conventions). Applied by scope rules.
Hook A shell command triggered by a lifecycle event. Always executed, zero token cost.
MCP Server An external tool server using the Model Context Protocol.
AGENTS.md Universal, freeform instruction file for any agent.
Progressive Disclosure Loading strategy: metadata → instructions → resources, on-demand.
Dependency Another agent package required for this package to function.
Peer Dependency A package that must be provided by the host environment, not auto-installed.
Optional Dependency A package that enhances functionality but isn't required.
System Dependency An OS binary, runtime, or package-manager package required on the host.
Lock File package.agent.lock — pins exact resolved versions for reproducible installs.
Hoisting Flattening shared dependencies to the top of the install tree to reduce duplication.
Resolution Override A resolutions entry that forces a specific version for a transitive dependency.
Pre-flight Check Verification that system dependencies exist before package installation.
SemVer Semantic Versioning 2.0 — MAJOR.MINOR.PATCH with defined compatibility rules.
DAG Directed Acyclic Graph — the dependency tree structure. Cycles are forbidden.
Permissions Declared scopes (fs, network, shell) that restrict what a package can do.
Env Environment variables and secrets required by the package to function.
Scope The @org namespace prefix in @org/package-name for organizational grouping.
Archive A .aam gzipped tar file containing a distributable package.
Bundle A pre-compiled, platform-specific archive requiring no install-time resolution.
Dist-tag A named alias for a specific package version (e.g., stable, bank-approved).
Policy Gate A client-side governance rule enforced before install or publish operations.
Yank Soft-delete of a published version. Yanked versions are hidden from search and new resolution but remain downloadable for existing lock files. See §12.7.
Deprecation Advisory state marking a version as superseded. Deprecated versions remain fully functional but emit warnings. See §12.7.
Provenance Cryptographic attestation linking a package archive to its source repository, commit, and build system. See §14.5.
Conformance Level A tiered compliance claim (Level 1–3) indicating which UAAPS features a platform implements. See §1.
Workspace A monorepo configuration where multiple UAAPS packages share a single dependency tree and lock file. See §13.14.
Capability Negotiation Runtime mechanism for packages to discover what features the host platform supports. See §10.
Resolver Version A manifest field (resolverVersion) that selects which dependency resolution algorithm to apply. See §13.4.
Permission Grant An explicit escalation allowing a dependency to use capabilities beyond the root package's permission boundary. See §17.3.
Threat Model A structured analysis of threat actors, attack surfaces, and mitigations for the UAAPS ecosystem. See §17.2.
ABNF Augmented Backus-Naur Form (RFC 5234) — formal grammar notation used to define UAAPS syntax. See §1.
SLSA Supply-chain Levels for Software Artifacts — a framework for graduated provenance requirements. See §14.5.

Appendix A: Confidence Legend

Level Meaning Example
Official Verified against vendor documentation Hook event names, manifest schema
Community Confirmed by multiple community sources Pseudo-XML rule structure, Cursor RULE.md folders
Inferred Derived from behavior or early previews Parallel agent counts, some adoption claims

Claims marked as Inferred should be validated before building production tooling.


Appendix B: Comparison of Key Differences

Aspect Claude Code Cursor
Manifest location .claude-plugin/plugin.json .cursor-plugin/plugin.json
Rule format CLAUDE.md (freeform) .mdc / RULE.md (YAML frontmatter)
Rule scoping File-based hierarchy globs, alwaysApply, agent-decided
Hook config hooks/hooks.json .cursor/hooks.json
Hook events PreToolUse, PostToolUse, Stop... beforeShellCommand, afterFileEdit, stop...
Skill standard agentskills.io (creator) agentskills.io (adopter, v2.2+)
Commands Native commands/ dir Via rules or skills
Sub-agents Native agents/ dir Via rules or imported
Team rules N/A (org-wide via API) Dashboard-managed (Team/Enterprise)
Distribution /plugin marketplace, npm, git Plugin marketplace, git
Root variable ${CLAUDE_PLUGIN_ROOT} Working directory relative
MCP config (plugin) .mcp.json (leading dot) mcp.json (no dot)
MCP config (project) .mcp.json .cursor/mcp.json
MCP config (global) ~/.claude/.mcp.json ~/.cursor/mcp.json
Instruction files CLAUDE.md AGENTS.md, CLAUDE.md, GEMINI.md
Dependency resolution N/A (no native resolver) N/A (no native resolver)
System dep checks Via skill compatibility field N/A
Lock file N/A N/A

This specification is a living document. Contributions welcome at github.com/spazyCZ/agent-package-manager.