Adapter Contract Specification
Formal specification of the
Adapterinterface that AI coding CLI adapters must implement to beaidokit-compatible.
| Field | Value |
|---|---|
| Spec name | aidokit Adapter Contract |
| Spec version | 1.0 |
| Status | Pre-alpha — supporting types reconciled with @aidokit/core (ADR-0012) |
| Targets | AI Dev Orchestrator Spec v2.0 |
| Compatible CLI | aidokit v0.1+ |
| Last reviewed | Pre-publication |
Table of contents
- Purpose and audience
- Document conventions
- Concepts and terminology
- Adapter lifecycle
- The
Adapterinterface - Supporting types
- Method specifications
- The
AdapterManifest - Capability profile enforcement
- Watchdog requirements
- Conformance levels and verification
- Worked example:
MinimalAdapter - Versioning and compatibility
- Migration policy
- Security requirements
- Open questions
- Appendix A: Required file outputs
- Appendix B: Minimum implementation skeleton
1. Purpose and audience
1.1 Purpose
This specification defines the contract every aidokit adapter MUST satisfy. An adapter translates the tool-agnostic AI Dev Orchestrator Spec v2.0 into the concrete file layout, capability mechanisms, and runtime configuration of one specific AI coding CLI (e.g. Aider, Claude Code, Codex CLI, Copilot CLI, Cursor — listed alphabetically; no priority order implied).
This document is normative. Wording follows RFC 2119 conventions.
1.2 Audience
- Adapter authors building a new
@aidokit/adapter-*or@scope/aidokit-adapter-*package - Reviewers auditing an adapter's conformance claim
- Core team evolving the contract between spec versions
- Tooling that consumes adapter manifests (e.g.,
aidokit doctor, marketplace verification)
1.3 Non-purpose
This document does NOT specify:
- The CLI command surface of
aidokititself (seedocs/specs/cli-reference.md) - The stack-pack contract (see
docs/specs/stack-pack-contract.md) - The MCP catalog format (see
docs/specs/mcp-catalog.md) - The underlying orchestrator workflow semantics (see the upstream
orchestrator-spec.md)
2. Document conventions
2.1 RFC 2119 keywords
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, and REQUIRED in this document are to be interpreted as described in RFC 2119.
2.2 Type syntax
All types are presented in TypeScript. Adapters implemented in other languages MUST satisfy the same semantics; the TypeScript signatures are normative even when the implementation is not in TypeScript.
2.3 Reserved namespaces
@aidokit/*— reserved for first-party packages by theaidokitcore team@aidokit/adapter-*— first-party adapter packages- Third-party adapters MUST publish under their own scope (e.g.,
@acme/aidokit-adapter-mytool) or unscoped (aidokit-adapter-mytool)
3. Concepts and terminology
| Term | Definition |
|---|---|
| Adapter | A package implementing this contract for one specific AI coding CLI |
| Target CLI | The AI coding CLI the adapter targets (e.g., Claude Code, Codex CLI, or Copilot CLI) |
| Engine directory | The adapter-specific config directory (e.g., .claude/, .codex/) |
| Agent rules file | The auto-loaded project rules file (e.g., CLAUDE.md, AGENTS.md) |
| Verb | A user-triggered workflow command (intake, implement-task, orchestrator-next) |
| Role | A bounded execution unit (Researcher, Architect, Planner, Builder, Tester-Reviewer, Maintainer) |
| Skill | A procedural-knowledge file the agent loads on demand |
| Capability profile | A set of permitted operations (read-only, docs-only-write, scope-limited-write) |
| Watchdog | Deterministic stop conditions the adapter enforces |
| Emitted file | A file the adapter produces during init or sync |
ProjectContext |
The immutable context object passed to every adapter method |
4. Adapter lifecycle
The aidokit CLI invokes adapter methods in a defined order. Adapters MUST handle being called in the orders shown below; adapters MUST NOT assume any particular method has been called previously unless documented.
4.1 During aidokit init
1. ctx = build ProjectContext from user selections + detection
2. files = []
3. files.push(adapter.emitAgentRulesFile(ctx))
4. files.push(...adapter.emitEngineConfig(ctx))
5. files.push(...adapter.emitVerbs(ctx))
6. files.push(...adapter.emitRoles(ctx))
7. skills = collectSkills(coreBaseSkills, stackPacks)
8. files.push(...adapter.emitSkills(ctx, skills))
9. files.push(...adapter.emitWatchdog(ctx))
10. files.push(...adapter.emitOutputStyles(ctx)) // optional
11. writeFilesStaged(files, ctx.projectRoot)
12. entries = SHARED_GITIGNORE_ENTRIES ∪ adapter.gitignoreEntries?(ctx)
mergeGitignore(read .gitignore) and write // ADR-0032; merge, not overwrite
13. for each selected MCP:
cmds = adapter.installMCP(mcp, suggestedRoles)
executeShell(cmds)
14. cmds = adapter.postInstall(ctx)
15. executeShell(cmds)
16. write adapter.md to <projectRoot>/.aido/adapter.md
Step 12 runs even under --brownfield (the merge is non-destructive). It is
surfaced in --dry-run as a .gitignore create/modify/unchanged entry.
4.2 During aidokit sync
1. ctx = build ProjectContext from existing adapter.md + current state
2. plan = adapter.preSync(ctx)
3. newFiles = re-run steps 3–10 from init
4. diff = computeDiff(currentFiles, newFiles)
5. show diff to user; await confirmation
6. writeFilesStaged(approvedChanges, ctx.projectRoot)
7. with --apply: re-merge the .gitignore managed block // ADR-0032
4.3 During aidokit doctor
1. ctx = build ProjectContext from existing adapter.md
2. checks = adapter.doctor(ctx)
3. print checks; exit 0 if all pass, 1 otherwise
4.4 During aido mcp add <id>
1. ctx = build ProjectContext from existing adapter.md
2. resolve MCP from catalog
3. cmds = adapter.installMCP(mcp, userSelectedRoles)
4. executeShell(cmds)
4.5 During aido mcp remove <id>
1. ctx = build ProjectContext from existing adapter.md
2. cmds = adapter.removeMCP(id)
3. executeShell(cmds)
Adapters MUST be deterministic: given identical ProjectContext and identical input, every emission method MUST produce byte-identical output across runs.
5. The Adapter interface
The normative TypeScript interface. Every adapter MUST implement every method.
import type {
AdapterManifest,
ProjectContext,
EmittedFile,
ShellCommand,
SkillTemplate,
MCPDef,
RoleName,
InstalledMCP,
SyncPlan,
DoctorCheck,
CapabilityProfile,
} from '@aidokit/core';
export interface Adapter {
/** Static identity and capability declarations. */
readonly manifest: AdapterManifest;
/** The agent rules file (e.g., CLAUDE.md). MUST return exactly one file. */
emitAgentRulesFile(ctx: ProjectContext): EmittedFile;
/** The engine directory configuration (e.g., .claude/settings.json). */
emitEngineConfig(ctx: ProjectContext): EmittedFile[];
/** Verb implementations: intake, implement-task, orchestrator-next. */
emitVerbs(ctx: ProjectContext): EmittedFile[];
/** Role implementations: Researcher, Architect, Planner, Builder, Tester-Reviewer, Maintainer. */
emitRoles(ctx: ProjectContext): EmittedFile[];
/** Skills: base + stack-pack-contributed. */
emitSkills(ctx: ProjectContext, skills: SkillTemplate[]): EmittedFile[];
/** Watchdog enforcement (hook scripts, validators, etc.). */
emitWatchdog(ctx: ProjectContext): EmittedFile[];
/** Optional output-style enforcement. MAY return an empty array. */
emitOutputStyles(ctx: ProjectContext): EmittedFile[];
/**
* OPTIONAL: extra `.gitignore` entries for this adapter's runtime artifacts
* (e.g. per-user local settings, subagent worktrees). Merged with the shared
* `SHARED_GITIGNORE_ENTRIES` into the aidokit-managed block in the project's
* `.gitignore`. Adapters with no adapter-specific runtime files omit this
* method. `.gitignore` is merged (append-with-dedupe, marker-scoped), NOT
* emitted as an `EmittedFile`. See ADR-0032.
*/
gitignoreEntries?(ctx: ProjectContext): string[];
/** Install an MCP server scoped to the given roles. */
installMCP(mcp: MCPDef, roles: RoleName[], ctx: ProjectContext): ShellCommand[];
/** Remove a previously-installed MCP server. */
removeMCP(mcpId: string, ctx: ProjectContext): ShellCommand[];
/** Enumerate MCP servers currently registered with the target CLI for this project. */
listInstalledMCPs(ctx: ProjectContext): Promise<InstalledMCP[]>;
/** Post-install shell commands (chmod, init, etc.). */
postInstall(ctx: ProjectContext): ShellCommand[];
/** Plan changes for `aidokit sync` before any writes. */
preSync(ctx: ProjectContext): SyncPlan;
/** Health checks reported during `aidokit doctor`. */
doctor(ctx: ProjectContext): Promise<DoctorCheck[]>;
}
6. Supporting types
All types are exported from @aidokit/core. The definitions below are
reconciled with the shipped @aidokit/core schemas per ADR-0012 — where this
section once carried draft-only fields (EmittedFile.owner,
ShellCommand.requiresConfirmation, a structured SyncPlan, …), it now
matches packages/core/src/{schema,types}.ts, which is authoritative.
6.1 EmittedFile
export interface EmittedFile {
/** Path relative to project root. MUST use forward slashes. */
path: string;
/** UTF-8 string content. Binary files are not supported in v0.1. */
content: string;
/** POSIX file mode. Use 0o755 for executable scripts; omit otherwise. */
mode?: number;
}
Rules:
pathMUST be a relative path; MUST NOT begin with/,.., or contain..segmentspathMUST NOT collide with anotherEmittedFilefrom the same adapter (deduplication is the adapter's responsibility)modeMUST be0o755for any.mjs,.sh, or other executable script
The draft of this section also carried
ownerandsyncOverwritePolicyfields foraidokit syncchange-attribution.syncis v0.5 scope; those fields will be re-introduced as a deliberate, ADR-tracked addition when the command is designed (ADR-0012, Negative consequences).
6.2 ShellCommand
export interface ShellCommand {
/** The command to execute. MUST be a single shell-safe string. */
command: string;
/** Explicit argv, when the command is not a full shell string. */
args?: string[];
/** Working directory; defaults to project root. */
cwd?: string;
/** Environment variables to add. */
env?: Record<string, string>;
/** Human-readable description for the CLI to display before execution. */
description?: string;
}
Rules:
commandMUST NOT include shell metacharacters that the adapter does not intend (e.g., unintended pipes or redirects)commandMUST NOT execute downloaded scripts (curl … | sh)- Commands that install global software, modify files outside the project root, or make network calls beyond the npm/Beads registries documented in the manifest MUST be surfaced to the user for confirmation by the CLI before execution
The draft of this section carried
requiresConfirmationandabortOnFailureflags on each command. The shipped type omits them; confirmation policy is the CLI's responsibility (it prompts before running any returned command), and abort-on-failure is decided per call site by the CLI. Per-command policy fields may return in a later spec revision if a real consumer needs them.
6.3 SkillTemplate
export interface SkillTemplate {
/** Skill identifier (slug). */
id: string;
/** Human-readable name. */
name: string;
/** Source: built-in vs contributed by a stack pack. */
source: 'core' | 'stack-pack';
/** Stack pack id if source is 'stack-pack'. */
sourcePackId?: string;
/** Skill content (Markdown). */
content: string;
/** Auto-load triggers (file globs, role names, or 'always'). */
autoLoadTriggers: string[];
/** Roles that preload this skill at session start. */
preloadedBy: RoleName[];
/** Whether the skill is required for the declared conformance level. */
required: boolean;
}
6.4 MCPDef
Reference only; full definition in docs/specs/mcp-catalog.md.
export interface MCPDef {
id: string;
name: string;
purpose: string;
suggestedFor: RoleName[];
triggers: string[];
securitySensitive: boolean;
install: Record<AdapterName, MCPInstallSpec>;
}
6.5 RoleName
export type RoleName =
| 'researcher'
| 'architect'
| 'planner'
| 'builder'
| 'tester-reviewer'
| 'maintainer'
| 'frontend-browser-tester';
Adapters MUST support the first six roles. The seventh (frontend-browser-tester) is OPTIONAL at Minimum and SHOULD be supported at Standard if the target CLI can host browser automation.
6.6 InstalledMCP
export interface InstalledMCP {
id: string;
/** Roles the MCP is scoped to, if the adapter tracks scoping. */
scope?: RoleName[];
/** Free-form per-adapter metadata; not interpreted by `@aidokit/core`. */
extras?: Readonly<Record<string, unknown>>;
}
6.7 SyncPlan
export type SyncFileStatus =
| 'identical'
| 'template-updated'
| 'user-modified'
| 'removed-in-template'
| 'new-in-template';
export interface SyncFileEntry {
path: string;
status: SyncFileStatus;
/** Unified diff against the on-disk file (omitted for identical/removed). */
diff?: string;
}
export interface SyncPlan {
entries: readonly SyncFileEntry[];
}
6.8 DoctorCheck
export type DoctorSeverity = 'pass' | 'warn' | 'fail';
export interface DoctorCheck {
/** Stable identifier for this check. */
id: string;
/** Human-readable label. */
label: string;
/** Outcome. */
severity: DoctorSeverity;
/** Details shown to the user. */
message: string;
/** Suggested fix or next step, if any. */
hint?: string;
}
6.9 CapabilityProfile
export type CapabilityProfile = 'read-only' | 'docs-only-write' | 'scope-limited-write';
7. Method specifications
Each method is specified with: signature, purpose, inputs, outputs, MUST/SHOULD/MAY requirements, error handling, and conformance level.
7.1 emitAgentRulesFile
emitAgentRulesFile(ctx: ProjectContext): EmittedFile
Purpose: Produce the project-level rules file the target CLI auto-loads at session start.
MUST:
- Return exactly one
EmittedFile - Set
pathto the file location appropriate to the target CLI (CLAUDE.md,AGENTS.md, etc.) - Set
owner: 'adapter'andsyncOverwritePolicy: 'on-confirm'(the user customizes this file) - Include the source-of-truth priority chain
- Include validation commands derived from
ctx.optionsor stack-pack suggestions - Reference the documentation locations relative to
ctx.projectRoot
SHOULD:
- Stay under 3,000 tokens (kit guideline) to preserve session context
- Use plain Markdown, no adapter-specific frontmatter
MAY:
- Include adapter-specific tips or links in a clearly-marked "Adapter notes" section
Conformance: Minimum
7.2 emitEngineConfig
emitEngineConfig(ctx: ProjectContext): EmittedFile[]
Purpose: Produce the target CLI's configuration files (e.g., .claude/settings.json).
MUST:
- Return zero or more files, all under the engine directory
- Set
owner: 'adapter'andsyncOverwritePolicy: 'always'for adapter-managed configs - Register every required hook / watchdog component
- Configure per-role permissions/sandbox modes matching the project's conformance level
SHOULD:
- Centralize tool configuration in a single file when the target CLI supports it
- Use minimal configuration (omit defaults)
MAY:
- Produce additional adapter-specific files (e.g.,
.claude/output-styles/)
Conformance: Minimum
7.3 emitVerbs
emitVerbs(ctx: ProjectContext): EmittedFile[]
Purpose: Produce verb implementations (slash commands, prompt files, etc.) for the three workflow verbs.
MUST:
- Emit implementations for:
intake,implement-task,orchestrator-next - Implementations MUST enforce the hard-stop after
intake(no auto-progression to code) - Implementations MUST enforce the hard-stop after
implement-task(no auto-progression to the next task)
SHOULD:
- Use the target CLI's native command mechanism (Claude Code: slash commands; Codex: prompt files)
- Reference roles by their canonical names
MAY:
- Emit additional adapter-specific verbs (e.g.,
/intake-brownfieldfor digest support)
Conformance: Minimum
7.4 emitRoles
emitRoles(ctx: ProjectContext): EmittedFile[]
Purpose: Produce role implementations for the six (or seven) workflow roles.
MUST:
- Emit role definitions for:
researcher,architect,planner,builder,tester-reviewer,maintainer - Each role MUST run under the correct capability profile (see §9)
builderMUST be configured forscope-limited-writetester-reviewer,researcher,architect,plannerMUST be configured forread-onlymaintainerMAY be configured fordocs-only-writeorscope-limited-writedepending on adapter mechanism
SHOULD:
- Provide per-role MCP scoping fields the CLI can populate later
- Preload role-appropriate skills
MAY:
- Emit
frontend-browser-testerrole for Standard+ conformance
Conformance: Minimum (six roles); Standard (seven roles)
7.5 emitSkills
emitSkills(ctx: ProjectContext, skills: SkillTemplate[]): EmittedFile[]
Purpose: Emit the procedural-knowledge skill files. The adapter receives both core base skills and stack-pack-contributed skills via skills parameter.
MUST:
- Emit every skill marked
required: true - Emit skills in the target CLI's native skill mechanism (Claude Code:
.claude/skills/<id>/SKILL.md; Codex: inlined into role prompts or referenced docs) - Preserve every skill's
autoLoadTriggersandpreloadedBymetadata in the emitted form
SHOULD:
- Emit all skills, not just required ones
- Mark stack-pack-contributed skills with a header so users can identify them
MAY:
- Group skills by category in the emitted layout if the target CLI supports it
Conformance: Minimum
7.6 emitWatchdog
emitWatchdog(ctx: ProjectContext): EmittedFile[]
Purpose: Emit the deterministic stop-condition enforcement mechanism.
MUST:
- Emit watchdog mechanisms enforcing ALL of the following stop conditions:
- Same test fails 3+ times
- Same shell command fails 5+ times
- Same file edited 5+ times in one task
- Repair attempts exceed the configured cap (default 2)
- Stage timeout exceeded (default per-stage)
- For Claude Code: emit
.claude/scripts/*.mjsPreToolUse / PostToolUse / Stop / SubagentStop hooks - For Codex: emit equivalent pre-execution validators or document the gap in the manifest
SHOULD:
- Emit a heartbeat / monitoring stream writer to
agent-artifacts/monitoring/events.jsonl - Emit a blocker-report writer triggered on watchdog stops
MAY:
- Add adapter-specific runtime hooks beyond the spec minimum
Conformance: Minimum (all listed stops); Standard adds monitoring stream
7.7 emitOutputStyles
emitOutputStyles(ctx: ProjectContext): EmittedFile[]
Purpose: Emit output-style enforcement (e.g., structured report formats).
MUST:
- Return either an empty array OR valid output-style files for the target CLI
SHOULD:
- Emit styles for test reports, change summaries, maintenance reports, blocker reports
MAY:
- Omit entirely; adapters at Minimum conformance MAY skip this
Conformance: MAY at all levels; SHOULD at Strict
7.8 installMCP
installMCP(mcp: MCPDef, roles: RoleName[], ctx: ProjectContext): ShellCommand[]
Purpose: Return the shell commands needed to install one MCP server and scope it to the given roles.
MUST:
- Return commands that register the MCP with the target CLI
- Return commands that update each role's frontmatter / config to scope the MCP
- Set
requiresConfirmation: truefor any command that installs software globally - Set
abortOnFailure: truefor the registration command itself
SHOULD:
- Use the target CLI's native MCP registration (Claude Code:
claude mcp add; Codex: config file mutation) - Be idempotent: re-running the command MUST NOT fail or produce duplicate registrations
MUST NOT:
- Install MCPs not present in the catalog passed in
MCPDef - Scope MCPs to roles not present in
mcp.suggestedForunless the user explicitly overrode
Conformance: SHOULD at Minimum; MUST at Standard
7.9 removeMCP
removeMCP(mcpId: string, ctx: ProjectContext): ShellCommand[]
Purpose: Return the shell commands to uninstall an MCP and remove its role scoping.
MUST:
- Return commands that remove the MCP registration
- Return commands that strip the MCP from all role frontmatter
- Be idempotent (no error if the MCP is not installed)
Conformance: SHOULD at Minimum; MUST at Standard
7.10 listInstalledMCPs
listInstalledMCPs(ctx: ProjectContext): Promise<InstalledMCP[]>
Purpose: Enumerate MCP servers currently registered with the target CLI for this project.
MUST:
- Return an empty array if no MCPs are installed
- Return
InstalledMCPentries with accuratescopedToRolesreflecting current state
MAY:
- Query the target CLI directly (e.g.,
claude mcp list --json) - Read state from a sidecar file the adapter maintains
Conformance: SHOULD at Minimum; MUST at Standard
7.11 postInstall
postInstall(ctx: ProjectContext): ShellCommand[]
Purpose: Return shell commands to run after all files have been written.
MUST:
- Set
mode: 0o755via the filemodefield on emitted scripts rather than relying onchmodin postInstall - Order commands deterministically; the CLI will execute them in order
SHOULD:
- Include
bd initifctx.beadsEnabledis true and the directory does not already contain a Beads database - Include
.gitignoreentries for the engine directory and any sidecar state files
MUST NOT:
- Modify files outside the project root
- Make network calls beyond those declared in the manifest's
capabilityDeclarations.networkCalls
Conformance: Minimum
7.12 preSync
preSync(ctx: ProjectContext): SyncPlan
Purpose: Compute the plan for aidokit sync before any writes occur. The CLI uses the returned SyncPlan to show diffs and request confirmation.
MUST:
- Compute the plan from the current adapter state and
ctx - Classify every emitted file into
toCreate,toUpdate,toDelete, ortoSkip - Include warnings if the adapter version mismatches the version recorded in the project's
.aido/adapter.md
SHOULD:
- Provide unified diffs in
toUpdate[].diff - Mark files with
syncOverwritePolicy: 'never'astoSkipwith a reason
MUST NOT:
- Write any files (planning only)
- Make network calls
Conformance: SHOULD at Minimum; MUST at Standard
7.13 doctor
doctor(ctx: ProjectContext): Promise<DoctorCheck[]>
Purpose: Return health-check results for the installed adapter state.
MUST:
- Verify the target CLI is installed and the version satisfies
manifest.cliTarget - Verify the engine directory exists and contains the expected files
- Verify every required skill file is present
- Verify each registered MCP is scoped to the correct roles
- Return results regardless of pass/fail (no short-circuit)
SHOULD:
- Run a subset of the conformance harness as a sanity check
- Verify watchdog scripts have execute permissions
MAY:
- Run target-CLI-specific health probes (e.g.,
claude --healthif available)
Conformance: SHOULD at Minimum; MUST at Standard
8. The AdapterManifest
The static manifest accompanies every adapter and declares its identity, conformance, and capabilities.
8.1 Type
Reconciled with the shipped @aidokit/core schema per ADR-0012.
export interface AdapterManifest {
/** Adapter identifier (lowercase kebab-case). */
name: string;
/** Target CLI name and version range. */
cliTarget: string;
/** AI Dev Orchestrator Spec version implemented. */
specVersion: '2.0';
/** Semver range of @aidokit/core the adapter is compatible with. */
sdkVersion: string;
/** Declared conformance level. */
conformance: 'minimum' | 'standard' | 'strict';
/** What this adapter does at runtime. */
capabilityDeclarations: CapabilityDeclarations;
/**
* v1.0 capability declarations (supply-chain hardening surface). OPTIONAL
* in the schema for backwards-compat with v0.5 adapters; required by the
* AD-MIN-13 conformance check. At Standard level, AD-STD-CAP-01
* additionally cross-checks the declarations against the adapter's
* actual source (child_process imports, fetch/http(s).request calls)
* when the conformance harness is given a `packagePath`.
*/
capabilities?: Capabilities;
/** OPTIONAL: documented gaps where the adapter cannot satisfy the spec. */
gaps?: string[];
}
export interface CapabilityDeclarations {
/** Glob patterns of paths the adapter writes during init/sync. */
writesPaths: string[];
/** Shell command prefixes the adapter executes. */
runsShellCommands: string[];
/** Network endpoints the adapter contacts. */
networkCalls: string[];
}
export interface Capabilities {
/** Shell commands the adapter invokes (e.g., 'claude mcp add'). */
shellCommands: string[];
/**
* Filesystem paths (relative to projectRoot) the adapter writes outside
* its declared engineDirectoryPath and agentRulesFilePath.
*/
writesOutsideEngine: string[];
/** Network endpoints the adapter contacts during emit/sync/doctor. */
networkEndpoints: string[];
/**
* Whether the adapter relies on a background process or daemon. MUST be
* false for all first-party adapters per ARCHITECTURE §1.
*/
backgroundProcesses: boolean;
}
The draft of this section carried
displayName,adapterVersion,maintainer, acontentHash, a structuredAdapterGap[], and aCapabilityDeclarations.readsBeyondOwnFilesflag. The shipped schema omits all of them: package identity/version/maintainer live in the adapter'spackage.json,gapsis a free-formstring[], and content hashing / provenance is deferred to the v1.0 signed-packages work. See ADR-0012.
8.2 Manifest file format
Adapters MUST ship an adapter.md (Markdown with YAML frontmatter) at the package root. The example below shows the claude-code adapter; the same shape applies identically to codex, copilot, and any third-party adapter — only the field values differ.
---
name: claude-code
cliTarget: 'Claude Code >=2.1.32'
specVersion: '2.0'
sdkVersion: 'workspace:*'
conformance: standard
capabilityDeclarations:
writesPaths:
- 'CLAUDE.md'
- '.claude/**'
- '.aido/**'
- 'agent-artifacts/**'
- '.gitignore'
runsShellCommands:
- 'claude mcp add'
- 'claude mcp remove'
- 'claude mcp list'
- 'bd init'
networkCalls: []
gaps: []
---
# Claude Code Adapter
(Human-readable description of the adapter.)
The frontmatter keys are exactly the AdapterManifest fields from §8.1 —
no displayName, adapterVersion, or maintainer.
8.3 Manifest exposure
The manifest is exposed in two forms:
- At rest in the adapter package as
adapter.md(for users to inspect) - At runtime as
adapter.manifest(theAdapterManifestobject)
Both forms MUST be consistent. CI MUST verify they match.
9. Capability profile enforcement
Adapters MUST map the three capability profiles to mechanisms in their target CLI.
9.1 read-only
- Roles using this profile: Researcher, Architect, Planner, Tester-Reviewer
- Claude Code:
permissionMode: plan - Codex:
sandbox: read-only,approval_policy: untrusted - Generic requirement: The role MUST NOT be able to write files, execute mutating shell commands, or modify project state
9.2 docs-only-write
- Roles using this profile: Maintainer (optionally)
- Claude Code:
permissionMode: default+ PreToolUse hookvalidate-docs-only.mjs - Codex:
sandbox: workspace-write+ role-prompt scope discipline - Generic requirement: The role MAY write to
docs/**andCHANGELOG.md; MUST NOT touch source code
9.3 scope-limited-write
- Roles using this profile: Builder, Maintainer (typically), Frontend-Browser-Tester
- Claude Code:
permissionMode: default+ PreToolUse hookvalidate-scope.mjs - Codex:
sandbox: workspace-write+ role-prompt scope + per-edit approval where needed - Generic requirement: The role MAY edit only files listed in the active task brief's
In-scope files; out-of-scope writes MUST be blocked
9.4 Enforcement strength
Adapters declare enforcement strength in the manifest gap entries:
- Hook-level (Claude Code): deterministic, the model cannot bypass
- Prompt-level (some Codex configurations): advisory, requires model cooperation
- Sandbox-level (Codex with
workspace-write): partial — blocks some actions, not all
Adapters MUST honestly disclose enforcement strength. Marketing a prompt-level enforcement as deterministic is a conformance violation.
10. Watchdog requirements
The adapter's emitted watchdog MUST enforce the following stop conditions. Adapters MAY add more.
| Stop condition | Trigger threshold (defaults) | Target outcome |
|---|---|---|
| Same test fails repeatedly | 3 consecutive failures | Block further test invocations; emit blocker |
| Same shell command fails repeatedly | 5 consecutive failures | Block further invocations; emit blocker |
| Same file edited too many times | 5 edits in one task | Warn; require explicit override |
| Repair attempts exceed cap | 2 attempts | Stop; emit blocker |
| Stage exceeds timeout | Configurable per stage | Stop; emit blocker |
| Out-of-scope file write attempted | Always | Block the write |
| Version bump without approval | Always | Block the write |
| Tracked-file deletion without approval | Always | Block the deletion |
Adapters MUST emit a agent-artifacts/blockers/blocker-<id>.md file when a stop condition is hit, containing:
- Stage where the stop occurred
- Trigger condition matched
- Last 10 actions attempted
- Suggested next action
11. Conformance levels and verification
11.1 Three levels
Per the upstream orchestrator spec §18:
- Minimum — all MUST requirements from this contract
- Standard — Minimum + all SHOULD requirements
- Strict — Standard + all MAY requirements where SHOULD was upgraded
11.2 Per-method conformance summary
| Method | Minimum | Standard | Strict |
|---|---|---|---|
emitAgentRulesFile |
MUST | MUST | MUST |
emitEngineConfig |
MUST | MUST | MUST |
emitVerbs |
MUST | MUST | MUST |
emitRoles (6 roles) |
MUST | MUST | MUST |
emitRoles (7th role) |
MAY | SHOULD | MUST |
emitSkills |
MUST | MUST | MUST |
emitWatchdog |
MUST | MUST | MUST |
emitOutputStyles |
MAY | SHOULD | MUST |
installMCP |
SHOULD | MUST | MUST |
removeMCP |
SHOULD | MUST | MUST |
listInstalledMCPs |
SHOULD | MUST | MUST |
postInstall |
MUST | MUST | MUST |
preSync |
SHOULD | MUST | MUST |
doctor |
SHOULD | MUST | MUST |
11.3 The conformance harness
@aidokit/core exports a test harness:
import { runConformanceTests } from '@aidokit/core';
import { adapter } from './src/index.js';
const results = await runConformanceTests(adapter, 'standard');
// results: TestResult[]
The harness runs adapter methods against synthetic ProjectContext fixtures and verifies the outputs satisfy the level's MUST/SHOULD requirements. Failures are reported with the offending requirement and a remediation hint.
Adapters MUST run the harness in their package's CI. Adapters MUST NOT publish a level claim the harness cannot verify.
11.4 Verification of published adapters
aidokit doctor runs a subset of the harness on the user's machine against the installed adapter. If the installed adapter's actual behavior fails to match its declared level, aidokit doctor reports a warning.
12. Worked example: MinimalAdapter
A complete-but-minimal adapter for a hypothetical CLI called basic-cli. Demonstrates the contract at Minimum conformance.
Note (ADR-0012): this example predates the §6/§8 type reconciliation and still shows draft-only fields (
EmittedFile.owner,EmittedFile.syncOverwritePolicy,ShellCommand.requiresConfirmation,AdapterManifest.displayName/maintainer, a structuredgapsarray,DoctorCheck.status/description). Treat the example's structure as illustrative; the authoritative field set is §6/§8 as reconciled with@aidokit/core. The example will be rewritten in a follow-up.
// packages/aidokit-adapter-basic-cli/src/index.ts
import type {
Adapter,
AdapterManifest,
ProjectContext,
EmittedFile,
ShellCommand,
SkillTemplate,
MCPDef,
RoleName,
InstalledMCP,
SyncPlan,
DoctorCheck,
} from '@aidokit/core';
const manifest: AdapterManifest = {
name: 'basic-cli',
displayName: 'Basic CLI',
cliTarget: 'Basic CLI >=1.0',
specVersion: '2.0',
sdkVersion: '^1.0',
conformance: 'minimum',
adapterVersion: '0.1.0',
maintainer: { name: 'Example Author', url: 'https://example.com' },
capabilityDeclarations: {
writesPaths: ['AGENTS.md', '.basic-cli/**'],
runsShellCommands: ['chmod', 'basic-cli mcp add'],
networkCalls: [],
readsBeyondOwnFiles: false,
},
gaps: [
{
specSection: '§17.2 SHOULD: heartbeat/monitoring stream',
description: 'basic-cli has no hook mechanism for emitting events.',
compensation: 'Roles log to stdout; users can capture manually.',
},
],
};
export const adapter: Adapter = {
manifest,
emitAgentRulesFile(ctx) {
return {
path: 'AGENTS.md',
content: `# ${ctx.projectName}\n\n[source-of-truth chain, validation commands, etc.]\n`,
owner: 'adapter',
syncOverwritePolicy: 'on-confirm',
};
},
emitEngineConfig(ctx) {
return [
{
path: '.basic-cli/config.json',
content: JSON.stringify({ adapter: 'basic-cli', version: '0.1.0' }, null, 2),
owner: 'adapter',
syncOverwritePolicy: 'always',
},
];
},
emitVerbs(ctx) {
return ['intake', 'implement-task', 'orchestrator-next'].map((v) => ({
path: `.basic-cli/verbs/${v}.md`,
content: `# /${v}\n\n[verb prompt content]\n`,
owner: 'adapter',
syncOverwritePolicy: 'always',
}));
},
emitRoles(ctx) {
const roles: RoleName[] = [
'researcher',
'architect',
'planner',
'builder',
'tester-reviewer',
'maintainer',
];
return roles.map((r) => ({
path: `.basic-cli/roles/${r}.md`,
content: `# Role: ${r}\n\n[capability profile + role prompt]\n`,
owner: 'adapter',
syncOverwritePolicy: 'always',
}));
},
emitSkills(ctx, skills) {
return skills.map((s) => ({
path: `.basic-cli/skills/${s.id}.md`,
content: s.content,
owner: s.source === 'core' ? 'adapter' : 'stack-pack',
syncOverwritePolicy: 'always',
}));
},
emitWatchdog(ctx) {
return [
{
path: '.basic-cli/watchdog.sh',
content: '#!/usr/bin/env bash\n# basic watchdog stubs\n',
mode: 0o755,
owner: 'adapter',
syncOverwritePolicy: 'always',
},
];
},
emitOutputStyles(_ctx) {
return [];
},
installMCP(mcp, roles, _ctx) {
return [
{
command: `basic-cli mcp add ${mcp.id}`,
description: `Register ${mcp.name} with basic-cli`,
requiresConfirmation: false,
abortOnFailure: true,
},
];
},
removeMCP(mcpId, _ctx) {
return [
{
command: `basic-cli mcp remove ${mcpId}`,
description: `Unregister ${mcpId}`,
requiresConfirmation: false,
abortOnFailure: false,
},
];
},
async listInstalledMCPs(_ctx) {
// shell out to `basic-cli mcp list --json`, parse, return
return [];
},
postInstall(ctx) {
const cmds: ShellCommand[] = [];
if (ctx.beadsEnabled) {
cmds.push({
command: 'bd init',
description: 'Initialize Beads task graph',
requiresConfirmation: false,
abortOnFailure: false,
});
}
return cmds;
},
preSync(_ctx) {
// compute plan from current vs new files
return { toCreate: [], toUpdate: [], toDelete: [], toSkip: [], warnings: [] };
},
async doctor(ctx) {
const checks: DoctorCheck[] = [];
// verify basic-cli installed, files present, etc.
checks.push({
id: 'cli-installed',
description: 'Basic CLI is installed',
status: 'pass',
message: 'basic-cli 1.0.2 found',
});
return checks;
},
};
The adapter is published as a regular npm package and consumed via:
npx aidokit init --adapter basic-cli
13. Versioning and compatibility
13.1 Three independent semvers
- Spec version — this contract's version. Currently
1.0. Bumped only with breaking changes to the interface. @aidokit/coreSDK version — the package containing the TypeScript types. Follows semver.- Adapter package version — each adapter has its own semver.
13.2 Compatibility rules
- Adapters MUST declare which spec version they target (
specVersionin manifest) - Adapters MUST declare which SDK version they were built against (
sdkVersion, semver range) aidokitCLI MUST refuse to load adapters declaring a spec version different from the CLI's ownaidokitCLI MUST warn on SDK version mismatches and refuse if the mismatch is major
13.3 Deprecation policy
- Removing a method or changing its semantics is a breaking change → new major spec version
- Renaming a field is a breaking change → new major spec version
- Adding an optional method is non-breaking → new minor spec version
- Adding required fields to existing methods is breaking
- Tightening a MAY to a SHOULD is non-breaking (advisory upgrade)
- Tightening a SHOULD to a MUST is breaking
13.4 Sunset window
When this spec issues a major version bump, the previous major remains supported by aidokit CLI for at least two minor aidokit CLI releases beyond the bump release. Adapter authors have at least one full release cycle to migrate.
14. Migration policy
14.1 Migration scripts
Every breaking change to this spec MUST ship with a migration script published as part of @aidokit/core:
import { migrate } from '@aidokit/core/migrations';
await migrate({
from: '1.0',
to: '2.0',
projectRoot: process.cwd(),
});
14.2 What migrations cover
- File path changes (e.g.,
.claude/agents/→.claude/roles/) - Manifest field renames
- Type shape changes that affect emitted files
- Newly required methods (provide skeleton implementations)
14.3 What migrations do NOT cover
- Adapter implementation logic (the adapter author updates this)
- User customizations to emitted files (these are preserved via diff-then-merge in
aidokit sync)
15. Security requirements
15.1 Capability honesty
Adapters MUST accurately declare:
- Every path glob they write to (
writesPaths) - Every shell command prefix they execute (
runsShellCommands) - Every network endpoint they contact (
networkCalls)
Misdeclaration is a conformance violation. The conformance harness inspects emitted commands and file paths to verify the declarations.
15.2 Forbidden operations
Adapters MUST NOT:
- Execute downloaded scripts (
curl | sh,iex (irm …)) withoutrequiresConfirmation: true - Read files outside the project root (except for environment probes the user has consented to)
- Make network calls beyond those declared
- Phone home (no telemetry, no analytics, no version-check pings)
- Embed credentials, API keys, or third-party tracking identifiers in emitted files
15.3 Signed packages (v1.0+ of aidokit)
From aidokit CLI v1.0 onward, aidokit MUST verify npm provenance signatures on all @aidokit/* adapter packages. Third-party adapters MAY publish unsigned but receive a "community / unverified" trust badge in aidokit search.
15.4 Reporting issues
Security issues in any adapter (first-party or third-party) SHOULD be reported per the adapter package's SECURITY.md. For aidokit itself, see SECURITY.md.
16. Open questions
These are unresolved and will be answered before the contract is frozen at v1.0.
| Question | Resolution target |
|---|---|
Whether emitSkills should also receive locale information for i18n |
Spec 1.1 |
Whether installMCP should be async (currently sync; some CLIs need network) |
Spec 1.0 freeze |
Whether gaps should be machine-readable (currently free-form description) |
Spec 1.0 freeze |
| How to declare adapter dependencies on specific MCPs (e.g., adapter ships with mandatory Beads MCP) | Spec 1.1 |
Whether to add a preInit hook for adapters that need user setup beyond prompts |
Spec 1.1 if demand arises |
| Standardizing role frontmatter format across adapters (currently CLI-specific) | Spec 2.0 |
Appendix A: Required file outputs
At Minimum conformance, an adapter's emission methods MUST collectively produce at least the following files when called with a baseline ProjectContext:
<projectRoot>/
├── <agent-rules-file> ← from emitAgentRulesFile
└── <engine-directory>/
├── <engine-config> ← from emitEngineConfig
├── <verbs>/intake.* ← from emitVerbs
├── <verbs>/implement-task.* ← from emitVerbs
├── <verbs>/orchestrator-next.* ← from emitVerbs
├── <roles>/researcher.* ← from emitRoles
├── <roles>/architect.* ← from emitRoles
├── <roles>/planner.* ← from emitRoles
├── <roles>/builder.* ← from emitRoles
├── <roles>/tester-reviewer.* ← from emitRoles
├── <roles>/maintainer.* ← from emitRoles
├── <skills>/<skill-id>.* ← from emitSkills
└── <watchdog mechanism> ← from emitWatchdog
The exact file extensions and subdirectory names are adapter-specific. The Claude Code adapter uses .md with .claude/agents/, .claude/commands/, etc. The Codex adapter uses different conventions.
Appendix B: Minimum implementation skeleton
Scaffolding script (proposed; ships in @aidokit/sdk-adapter post-1.0):
npx @aidokit/sdk-adapter create my-tool
✔ Created packages/aidokit-adapter-my-tool/
├── adapter.md ← manifest
├── src/
│ ├── index.ts ← exports adapter
│ ├── emit/
│ │ ├── agent-rules.ts
│ │ ├── engine-config.ts
│ │ ├── verbs.ts
│ │ ├── roles.ts
│ │ ├── skills.ts
│ │ └── watchdog.ts
│ ├── mcp/
│ │ ├── install.ts
│ │ └── remove.ts
│ ├── doctor.ts
│ └── files/ ← template files
├── test/
│ ├── conformance.test.ts ← runs @aidokit/core harness
│ └── integration.test.ts
├── package.json
└── tsconfig.json
For pre-1.0 (before the SDK ships), authors can copy any of the first-party adapters (packages/adapter-claude-code/, packages/adapter-codex/, packages/adapter-copilot/) as a starting point — pick the one whose conformance level and target CLI shape most closely matches your adapter, and adapt it from there.
See also
ARCHITECTURE.md— system design contextdocs/specs/stack-pack-contract.md— sibling contract for stack packsdocs/specs/cli-reference.md—aidokitcommand surfacedocs/specs/mcp-catalog.md— MCP definitionsdocs/specs/conformance-levels.md— per-level checklistsdocs/specs/security-model.md— threat model and mitigations- Upstream
orchestrator-spec.md— the tool-agnostic workflow spec this contract implements