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:
- 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. - Frontmatter fields: Consumers MUST ignore unrecognized frontmatter keys in
SKILL.md,RULE.md,agent.yaml,system-prompt.md, and command.mdfiles. - Hook events: Consumers MUST ignore hook event names they do not support. Unknown events in
hooks.jsonMUST NOT cause a parse error. - Vendor extensions: Consumers MUST silently ignore unrecognized
x-*keys (restated from §3 for emphasis). - Enum values: When a field specifies an enumeration (e.g.,
installModevalues), 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:
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>, wherevendor-idis a lowercase alphanumeric slug matching[a-z0-9-]+, maximum 32 characters (e.g.x-claude,x-cursor,x-copilot). - The
vendor-idSHOULD match either a registered platform identifier from the compatibility matrix or the package's own scope identifier (e.g.x-myorgfor@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 validateMUST 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 unknownx-*key. - Registry indexing: only
x-<known-platform>keys (those matching a registered platform) are indexed and searchable. Unknownx-*keys are stored but not indexed. - Registry validation SHOULD produce a
WARN-level diagnostic forx-*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 registeredx-<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¶
| 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:
- User-invoked — triggered by typing
/command-name. - 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.mdfile (without a separateagent.yaml) is also accepted for backward compatibility. In this case the frontmatter fields from thesystem-prompt.mdtable 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, andnotificationevents 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:
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¶
| 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.mdandGEMINI.mdas agent-specific instruction files alongsideAGENTS.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:
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
aamCLI — seeaam_cli_new.md.
12.1 Archive Distribution Format¶
Packages are distributed as .aam archives (gzipped tar), providing a binary-safe, single-file distribution unit.
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:
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:
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
deprecatecommand marks a version as deprecated. Deprecated versions remain fully functional but emit warnings during resolution. - Published → Yanked: The
yankcommand 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¶
| 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-checkvstesting:lint-check. - If a skill is referenced without namespace, the closest scope wins (project > personal > plugin).
- Explicit disambiguation:
aam resolve skill lint-checklists 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/name → scope--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):
./package.agent.lock— project lock file always takes precedence for project-local installs.~/.aam/global.lock— governs--globalinstalls only; never consulted for project-local resolution.resolutionsfield inpackage.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:
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:
aam pkg publish→ uploads withapproval_status: pending- Approvers receive notification
- Approver runs
aam approve @org/agent@1.2.0 - 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¶
- Only install packages from trusted sources — skills can execute code and access the filesystem.
- Audit all scripts before enabling — look for network calls, file access, and unexpected operations.
- Use
allowed-toolsto restrict skill capabilities where possible. - Hook commands run as the user — apply least-privilege principles.
- MCP servers SHOULD NOT expose secrets; use environment variable injection.
- Environment Variables: Packages MUST explicitly declare required environment variables in the
envmanifest field. Host platforms SHOULD prompt users securely for these values rather than storing them in plaintext. - Permissions Model: The
permissionsfield inpackage.agent.jsonis OPTIONAL. When it is present, platforms MUST enforce it as defined in §17.1 Permissions Model below. A platform that ignores a declaredpermissionsfield is non-compliant. When the field is absent, platforms MAY apply their own default restrictions. - Sandbox untrusted packages before production deployment.
- Verify lock file integrity —
package.agent.lockhashes prevent supply-chain tampering. - Run
aam auditregularly to check dependencies for known vulnerabilities. - System dependency auto-install never uses
sudowithout explicit--allow-sudoflag. - Transitive dependencies SHOULD be audited —
aam treereveals 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¶
| 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¶
| 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¶
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.