AgentSpec v1 — runtime-agnostic agent definitions: schema validation, project loading, platform-neutral remote Git skill resolution (ref → commit pinned), and runtime adapters (hermes today; claude-code / openclaw planned).
npm i @aisa-one/agent-specimport {
effectiveReleaseTargets,
loadAgentProject,
resolveSkills,
getAdapter,
} from "@aisa-one/agent-spec";
const project = await loadAgentProject("./my-agent");
const skills = await resolveSkills(project.manifest.skills.remote);
for (const target of effectiveReleaseTargets(project.manifest)) {
await getAdapter(target).build({ project, resolvedSkills: skills }, `./out/${target}`);
}Remote skills are declared explicitly and resolved at build time:
skills:
inline:
- portfolio-report
remote:
- type: git
url: https://github.com/example/shared-skills.git
path: packages/skills/twitter-post
name: twitter-post
ref: v1.2.0name defaults to the final segment of path; type defaults to git; and
ref defaults to main. The resolved commit is recorded in agent.lock.json.
my-agent/ # one agent definition (its own git repo)
├── agent.yaml # manifest — see field table below
├── soul/ # persona prompt, markdown ({{VARS}} allowed)
│ └── SOUL.md
├── skills/ # inline skills (SKILL.md + scripts), always flat
│ └── portfolio-report/… # one level only — see "Inline skills are flat"
├── cron/
│ └── jobs.yaml # runtime-agnostic scheduled tasks
└── assets/ # extra code/data copied into the build as-is
Every skills.inline entry is a single directory name — portfolio-report, never
finance/portfolio-report. The schema rejects /.
This is not a stylistic preference. Skill discovery is not part of the open spec, which defines only a single skill's internal layout, so every runtime implements its own — and most scan exactly one level:
| Runtime | Nested skills |
|---|---|
| Claude Code plugins | not discovered |
Codex plugin skill roots (DirectChildren) |
not discovered |
Gemini CLI (globs ['SKILL.md', '*/SKILL.md']) |
not discovered |
Codex non-plugin roots (.agents/skills) |
discovered, max depth 6 |
| Cursor | discovered, documented recursive |
Nested skills fail silently — they are dropped with no warning, and
claude plugin validate still passes because it validates the manifest and
never checks that on-disk skills are reachable. Anthropic's own anthropics/skills
repo keeps skills flat and expresses grouping in the marketplace manifest instead.
Group skills with naming (portfolio-report, portfolio-update) or with
frontmatter metadata, not with directories. See ADR-0009 in aisa-agent-platform.
| Field | Meaning |
|---|---|
spec |
always agentspec/v1 |
id |
globally unique slug; also the E2B sandbox AGENT_SPEC_ID |
name |
human-readable display name |
version |
artifact release version (semver) |
description |
one-line description |
language |
display language (default en) |
models |
default / optional fast model + provider (default aisa); env can override at install time via MODEL_DEFAULT / MODEL_PROVIDER |
env |
required / optional env var declarations (name, description, optional degrade); never contains secrets |
skills.inline |
skills shipped inside this repo under skills/ |
skills.remote |
public HTTPS Git skills: explicit url + repository path, optional output name, and ref (default main); pinned to a commit in agent.lock.json |
cron |
path to the cron jobs YAML (optional) |
update |
channel: latest|pinned, auto: true|false — auto-update policy |
release.targets |
optional non-empty, unique subset of hermes, openclaw, claude-plugin, codex-plugin, agent-plugin; controls which artifacts release tooling produces; omission preserves legacy behavior by selecting all five |
targets.hermes.config |
hermes-only config overrides, deep-merged onto the base profile config |
release.targets and targets have different responsibilities: release.targets
selects publish artifacts, while targets.<runtime> contains runtime-specific
configuration. Release tooling should call effectiveReleaseTargets(manifest)
instead of reading the optional field or reimplementing its default. The field is
a set: declaration order has no scheduling meaning, and the helper returns the
selected targets in the canonical order exported as RELEASE_TARGETS.
A profile bundle with {{VARS}} preserved (rendered at install time):
profile/ (SOUL, config, cron), skills/, assets, agent.lock.json, .env.example.
- Public
https://repositories only; credentials, query strings, SSH,file://, and local paths are rejected. - The selected directory must directly contain
SKILL.md. - Symlinks, submodules/gitlinks, and non-blob entries are rejected.
- Remote files are copied as bytes; no remote script or Git hook is executed.
- Default limits: 1,000 files, 5 MiB per file, 20 MiB total, 60-second Git timeout.
- GitHub, GitLab, and self-hosted Git use the same Git CLI resolver.
Spec: see aisa_cio_agent/docs/superpowers/specs/2026-07-14-agent-spec-design.md.