Adapter Contract Specification

Formal specification of the Adapter interface that AI coding CLI adapters must implement to be aidokit-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

  1. Purpose and audience
  2. Document conventions
  3. Concepts and terminology
  4. Adapter lifecycle
  5. The Adapter interface
  6. Supporting types
  7. Method specifications
  8. The AdapterManifest
  9. Capability profile enforcement
  10. Watchdog requirements
  11. Conformance levels and verification
  12. Worked example: MinimalAdapter
  13. Versioning and compatibility
  14. Migration policy
  15. Security requirements
  16. Open questions
  17. Appendix A: Required file outputs
  18. 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

1.3 Non-purpose

This document does NOT specify:


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


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:

The draft of this section also carried owner and syncOverwritePolicy fields for aidokit sync change-attribution. sync is 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:

The draft of this section carried requiresConfirmation and abortOnFailure flags 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:

SHOULD:

MAY:

Conformance: Minimum


7.2 emitEngineConfig

emitEngineConfig(ctx: ProjectContext): EmittedFile[]

Purpose: Produce the target CLI's configuration files (e.g., .claude/settings.json).

MUST:

SHOULD:

MAY:

Conformance: Minimum


7.3 emitVerbs

emitVerbs(ctx: ProjectContext): EmittedFile[]

Purpose: Produce verb implementations (slash commands, prompt files, etc.) for the three workflow verbs.

MUST:

SHOULD:

MAY:

Conformance: Minimum


7.4 emitRoles

emitRoles(ctx: ProjectContext): EmittedFile[]

Purpose: Produce role implementations for the six (or seven) workflow roles.

MUST:

SHOULD:

MAY:

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:

SHOULD:

MAY:

Conformance: Minimum


7.6 emitWatchdog

emitWatchdog(ctx: ProjectContext): EmittedFile[]

Purpose: Emit the deterministic stop-condition enforcement mechanism.

MUST:

SHOULD:

MAY:

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:

SHOULD:

MAY:

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:

SHOULD:

MUST NOT:

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:

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:

MAY:

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:

SHOULD:

MUST NOT:

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:

SHOULD:

MUST NOT:

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:

SHOULD:

MAY:

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, a contentHash, a structured AdapterGap[], and a CapabilityDeclarations.readsBeyondOwnFiles flag. The shipped schema omits all of them: package identity/version/maintainer live in the adapter's package.json, gaps is a free-form string[], 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:

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

9.2 docs-only-write

9.3 scope-limited-write

9.4 Enforcement strength

Adapters declare enforcement strength in the manifest gap entries:

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:


11. Conformance levels and verification

11.1 Three levels

Per the upstream orchestrator spec §18:

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 structured gaps array, 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

13.2 Compatibility rules

13.3 Deprecation policy

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

14.3 What migrations do NOT cover


15. Security requirements

15.1 Capability honesty

Adapters MUST accurately declare:

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:

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