Skip to content

A tour of the dna CLI

The dna binary is the terminal surface over the same kernel the SDKs expose: every command boots a local Kernel, points it at your manifests via DNA_SOURCE_URL (or DNA_BASE_DIR for a plain filesystem directory), and reads or writes instances through the same ports. Nothing here is a second code path — what the CLI validates and stores is exactly what Kernel.quick() would load.

Install it with pip install dna-cli (or uv tool install dna-cli); inside the repo it is already on PATH after the dev-venv setup (cd packages/sdk-py && uv venv && uv pip install -e ".[dev]" -e ../cli).

This page is a practical tour of every command group — what it is for and one real, executed example each. It does not list flags; each heading links to the generated CLI reference, which is regenerated from the live Click tree on every build and can't drift.

$ dna --help
Usage: dna [OPTIONS] COMMAND [ARGS]...

  DNA — declarative lifecycle + instance CLI.

  Boots a local kernel via DNA_SOURCE_URL / DNA_BASE_DIR (filesystem source).
  Run `dna kind list` to start exploring, `dna sdlc --help` for the lifecycle
  verbs.

Options:
  --version   Show the version and exit.
  -h, --help  Show this message and exit.

Commands:
  api       Expose the live DNA (definitions + memory) over a REST read-API.
  doc       List, show, create, edit, delete instances.
  docs      Browse the in-product Doc corpus.
  emit      Emit a DNA agent as a target runtime's native artifact (the...
  eval      Run EvalSuites locally (offline, deterministic) and compare...
  explain   Show per-section provenance for a composed agent prompt.
  init      Make a project agent-ready: board + skill + AGENTS.md + git...
  install   Install bundles/Kinds from a repository into the local source.
  intel     Portfolio intelligence — run passes, inspect sources + insights.
  kind      List + inspect registered Kinds.
  mcp       Expose the live DNA (definitions + SDLC + memory) over MCP.
  memory    Declarative memory over existing Kinds...
  new       Scaffold a valid Kind skeleton into a scope (agent | soul |...
  recall    Hybrid semantic search (dense + lexical + RRF) over the...
  research  Manage Research synthesis instances (curated syntheses of...
  scope     List + inspect scopes (manifest modules).
  sdlc      Declarative lifecycle tracking...
  search    Alias of ``dna recall`` (neutral naming).
  source    Source-level operations: declarative replicas, introspection.
  specify   Bidirectional GitHub Spec Kit ↔ DNA bridge (import / export).

Set up a playground

The examples below run in a small throwaway scope, so you can paste along without touching a real project. Create a playground directory with one scope named docs containing a Genome (the scope root), two pages of the built-in Doc Kind (one per locale), and a KindDefinition declaring a custom Kind called Snippet — to show how you extend the type system with your own Kinds, no Python required:

mkdir -p ~/dna-playground/.dna/{_lib,docs/docs/welcome,docs/docs/boas-vindas,docs/kinds/snippet,docs/snippets}
cd ~/dna-playground
export DNA_BASE_DIR=~/dna-playground/.dna

# Scopes inherit from a shared `_lib` library scope by default — give the
# playground an empty one so nothing warns about a missing parent.
cat > .dna/_lib/Genome.yaml <<'EOF'
apiVersion: github.com/ruinosus/dna/v1
kind: Genome
metadata: { name: _lib }
spec: {}
EOF

cat > .dna/docs/Genome.yaml <<'EOF'
apiVersion: github.com/ruinosus/dna/v1
kind: Genome
metadata:
  name: docs
  description: In-product documentation corpus
spec: {}
EOF

# A Doc is authored as a bundle: docs/<name>/DOC.md, YAML frontmatter +
# markdown body. No KindDefinition needed — the Kind ships with the SDK.
cat > .dna/docs/docs/welcome/DOC.md <<'EOF'
---
description: Welcome to the corpus
icon: "👋"
order: 1
locale: en
kind_of: tutorial
category: Getting started
---

# Welcome

This corpus is served in-product: agents and the UI read these pages
through the kernel, so editing markdown updates the product help.
EOF

cat > .dna/docs/docs/boas-vindas/DOC.md <<'EOF'
---
description: Bem-vindo ao corpus
icon: "👋"
order: 1
locale: pt-BR
kind_of: tutorial
category: Primeiros passos
---

# Bem-vindo

Este corpus é servido dentro do produto: os agentes e a UI leem estas
páginas pelo kernel.
EOF

cat > .dna/docs/kinds/snippet/KIND.yaml <<'EOF'
apiVersion: github.com/ruinosus/dna/core/v1
kind: KindDefinition
metadata: { name: snippet }
spec:
  target_api_version: example.com/docs/v1
  target_kind: Snippet
  alias: docs-snippet
  origin: example.com
  docs: A reusable content snippet (markdown body + audience tag).
  schema:
    type: object
    required: [body]
    additionalProperties: false
    properties:
      body: { type: string }
      audience: { type: string }
  storage:
    type: yaml
    container: snippets
EOF

cat > .dna/docs/snippets/hello.yaml <<'EOF'
apiVersion: example.com/docs/v1
kind: Snippet
metadata:
  name: hello
  description: Greeting snippet
spec:
  audience: newcomers
  body: Welcome! Everything in this playground is throwaway.
EOF

dna scope — list and inspect scopes

Reference →

A scope is a directory of manifests — the unit of loading, inheritance and tenancy (Tenancy & layers). dna scope list shows every scope the configured source can see; dna scope tree inventories one scope's instances grouped by Kind — the fastest way to answer "what is actually in here?".

$ dna scope list
scope
-----
_lib 
docs 

$ dna scope tree docs

Genome
  • docs

KindDefinition
  • snippet

Snippet
  • hello

Note the two-phase load at work: the KindDefinition registered the Snippet Kind, and the hello instance was then parsed as a first-class instance of it — no Python was written. The two Doc pages are absent on purpose: Doc is a record-plane Kind (pure typed content that never composes into agent prompts), and the tree inventories the composition plane — records are reached with dna instance list Doc --scope docs and the dna docs group below.

dna genome view — the derived Genome view

dna genome view <scope> composes a scope's Genome into one picture — its identity (version, default agent, tags), its ships (the copilots, agents, tools and federations the scope actually contains), and its tenant LayerPolicy (what a tenant overlay may customize vs. what is pinned). Nothing is a stored list: the ships are enumerated live from the scope, so the view never drifts.

$ dna genome view dna-cloud
$ dna genome view dna-cloud --json   # machine-readable

dna definition — customize a tenant's Strain

Reference →

dna definition get <kind> <name> --tenant <wid> shows a definition as the tenant sees it (effective vs base). dna definition set <kind> <name> --file spec.yaml --tenant <wid> writes a copy-on-write override into the tenant's scope — the mutation is vetoed by LayerPolicy for locked Kinds or non-overlayable fields. dna definition revert <kind> <name> --tenant <wid> removes it. The REST face exposes the same surface at GET|PUT|DELETE /v1/definitions/{kind}/{name}.

For a bundle-pattern Kind (e.g. a Skill, stored as a directory of files rather than one YAML doc), dna definition entries <kind> <name> lists its entry files (base ∪ tenant overlay), each flagged whether THIS tenant forked it. The dna definition entry get|set|revert <kind> <name> <entry> sub-group reads, forks (set --file <f>), or reverts ONE entry file — the same Strain mutation as get/set/revert above, at file grain; a LOCKED Kind vetoes entry set exactly like set. The REST face mirrors this at GET|PUT|DELETE /v1/definitions/{kind}/{name}/entries[/{entry}].

dna kind — list and inspect registered Kinds

Reference →

Kinds are the type system (Kinds — identity & composition). dna kind list prints everything registered on the kernel — built-ins plus any KindDefinition-declared Kinds in the scope — and dna kind describe dumps one Kind's identity, JSON Schema and storage descriptor, which is exactly what the write boundary will enforce.

$ dna kind list | wc -l
      84

$ dna kind list | grep -E '^(Genome|Agent|Doc|Skill|Soul|Comment) '
Agent               (use describe)  (use describe)                           
Comment             (use describe)  (use describe)                           
Doc                 (use describe)  (use describe)                           
Genome              (use describe)  (use describe)                           
Skill               (use describe)  (use describe)                           
Soul                (use describe)  (use describe)                           

$ dna kind describe Doc | head -8
{
  "kind": "Doc",
  "alias": "dna-doc",
  "api_version": "github.com/ruinosus/dna/doc/v1",
  "display_label": "Docs",
  "schema": {
    "type": "object",
    "required": [

dna instance — generic instance CRUD

Reference →

The workhorse group: list, show, create, edit and delete instances of any Kind, with the Kind's JSON Schema enforced on every write. dna instance fields prints the fields a Kind accepts (straight from its schema), and dna instance make builds an instance from field=value arguments — values are coerced to the schema's types, so you rarely need to hand-craft JSON. For bulk upserts from files there is dna instance apply, and Kinds that declare a status machine get generic dna instance transition.

$ dna instance fields Comment --scope docs
Fields for Comment
  required: ['author', 'body', 'created_at', 'target_ref', 'type']

  assignee                 (string)   
  attachments              (array)   
  author                   (string) *   
  body                     (string) *   
  created_at               (string) *   
  edited_at                (string)   
  from_status              (string)   
  target_ref               (string) *   Kind:name of the target instance
  to_status                (string)   
  type                     (string) enum=['note', 'status_change', 'assignment', 'system'] *   

$ dna instance make Comment note-1 --scope docs target_ref=Doc:welcome \
    author=ada body='Ship the welcome page.' type=note \
    created_at=2026-07-09T12:00:00Z
Created Comment/note-1 in scope docs (5 fields)

$ dna instance show Comment note-1 --scope docs
{
  "kind": "Comment",
  "name": "note-1",
  "metadata": {
    "name": "note-1"
  },
  "spec": {
    "target_ref": "Doc:welcome",
    "author": "ada",
    "body": "Ship the welcome page.",
    "type": "note",
    "created_at": "2026-07-09T12:00:00Z"
  }
}

dna docs — browse the in-product Doc corpus

Reference →

Not to be confused with dna instance above: dna docs (plural) is a reader over one specific corpus — a scope named docs holding instances of the built-in Doc Kind, the pattern a DNA-based product uses to serve its own help pages from the kernel. Each page is a docs/<name>/DOC.md bundle: a markdown body plus sidebar metadata (icon, order, locale, Diátaxis kind_of, category). It works out of the box — the two pages authored in the playground setup are already a corpus, one per locale:

$ dna docs list
order  icon  name         title                kind_of   category        
-----  ----  -----------  -------------------  --------  ----------------
1      👋     boas-vindas  Bem-vindo ao corpus  tutorial  Primeiros passos

$ dna docs list --locale en
order  icon  name     title                  kind_of   category       
-----  ----  -------  ---------------------  --------  ---------------
1      👋     welcome  Welcome to the corpus  tutorial  Getting started

$ dna docs show welcome --locale en
# Welcome

This corpus is served in-product: agents and the UI read these pages
through the kernel, so editing markdown updates the product help.

The Doc schema is deliberately content-shaped (dna kind describe Doc shows the exact contract the write boundary enforces). If your product needs a different documentation shape, the KindDefinition route the playground used for Snippet works just as well for a custom docs Kind — it is an extension point now, not a prerequisite.

dna research — curated Research syntheses

Reference →

A Research instance is a curated synthesis of external sources — the agent-facing knowledge model: cited findings and recommendations as data, not generated wiki prose. The CLI lists a scope's research catalog and pretty-prints one synthesis with its citation graph. This repo dogfoods it — run from a DNA checkout:

$ dna research list --scope dna-development
name                           status     method                #F  #S when        title
--------------------------------------------------------------------------------------------------------------
rsh-doc-frameworks-oss         published  web-search-curated     4   0 2026-07-09  Documentation frameworks & tooling for a public OS
rsh-exemplar-sdk-repos         published  web-search-curated     5   0 2026-07-09  How exemplary OSS SDK repos structure their docume
rsh-memory-similarity-evolution published  synthesis              6   0 2026-07-09  Evolving memory + similarity search into DNA, serv
rsh-openwiki-analysis          published  synthesis              3   0 2026-07-09  LangChain OpenWiki — analysis and fit with DNA arc

$ dna research show rsh-doc-frameworks-oss --scope dna-development | head -12

🔬 Research/rsh-doc-frameworks-oss
  title:        Documentation frameworks & tooling for a public OSS SDK
  status:       published
  methodology:  web-search-curated
  confidence:   high
  conducted_by: claude-code
  conducted_at: 2026-07-09T00:00:00+00:00
  scope_ref:    dna-development
  visibility:   shared

  objective:

There is also dna research recall — semantic search over the research catalog — which needs the search extras installed (see How to use semantic recall & memory).

dna source — replicas and source-level introspection

Reference →

Where every other group works inside a source, this group works on sources. dna source replica manages .dna-replicas.yaml, a declarative config (discovered by upward walk, like .gitignore) that host platforms read at boot to mirror writes into other sources — e.g. keep a filesystem copy of selected scopes while the source of truth is a database. dna source diff and dna source push compare and reconcile one scope between the current source and another URL using Kind-aware content digests, so formatting and volatile stamps never show as drift.

$ dna source replica add docs-backup --replica fs://./backup --scopes docs
ADDED replica/docs-backup -> ~/dna-playground/.dna-replicas.yaml

$ dna source replica list
id           replica        scopes  kinds  enabled
-----------  -------------  ------  -----  -------
docs-backup  fs://./backup  docs    all    yes    

diff reports drift in three buckets — added (in the current source, missing in the other), changed (content digest drifted), removed (in the other only). push is dry-run by default; --apply writes the minimal set through the target's atomic save path (doc + bundle entries together). Here the ./copy replica is stale: one agent drifted, one is missing entirely:

$ dna source diff fs://./copy --scope demo
demo: 1 added · 1 changed · 0 removed  (current ↔ fs://./copy)
  A Agent/release-notes
  C Agent/code-reviewer

$ dna source push fs://./copy --scope demo
[DRY-RUN] demo → fs://./copy: would write 2 (1 added, 1 changed)
  + Agent/release-notes
  ~ Agent/code-reviewer
  (dry-run — re-run with --apply to write)

$ dna source push fs://./copy --scope demo --apply
[APPLIED] demo → fs://./copy: wrote 2 (1 added, 1 changed)
  + Agent/release-notes
  ~ Agent/code-reviewer

$ dna source diff fs://./copy --scope demo
✔ in sync — demo matches fs://./copy (0 diffs)

The digests are Kind-aware and source-independent: the same scope held in a filesystem tree and in a database digests identically when in sync, so a diff never needs to transfer content — and re-serialization noise (key order, volatile stamps) never shows up as drift.

dna install — install bundles from a repository

Reference → · full guide →

The front door of the ecosystem: point dna install at a repository — github:owner/repo[/subdir][@ref] (a shallow clone, the same URI grammar Genome dependencies use) or local:<path> (a directory you already have) — and it detects the DNA instances in the fetched tree with the kernel's registered readers, validates each one, and writes the valid ones into your source through kernel.write_instance, so every write guard runs. Third-party manifests are untrusted data: schema validation is the first defense, and an invalid instance is rejected with the reason while the install continues with the valid ones (see the threat model). --dry-run prints the plan and stops:

$ dna install local:../market-drop --scope playground --dry-run
install plan — /Users/you/market-drop → scope 'playground'
  ! reject    Skill/bad-skill  (prompts/bad-skill.yaml)
      schema validation failed at spec.instruction: 42 is not of type 'string' — see `dna kind show Skill` for the expected shape
  + install   Skill/greeter  (skills/greeter)

dry-run: 1 to write · 0 to skip · 1 rejected — re-run without --dry-run to apply

$ dna install local:../market-drop --scope playground
...
installed 1 · skipped 0 · rejected 1
provenance → ~/dna-playground/.dna/playground/installed.lock

Installing straight from a real marketplace repo works the same way — this pulls one official Anthropic skill into a market scope:

$ dna install github:anthropics/skills/skills/pdf --scope market
install plan — anthropics/skills/skills/pdf (commit 9d2f1ae18723) → scope 'market'
  + install   Skill/pdf  (.)

installed 1 · skipped 0 · rejected 0
provenance → ~/dna-playground/.dna/market/installed.lock

Re-running is idempotent (existing instances are skipped with a warning; --force overwrites), and every install upserts installed.lock in the scope — each entry records the origin URI pinned to the fetched commit, the path inside the fetched tree, and a content SHA-256.

dna explain — where each part of a prompt comes from

Reference →

build_prompt renders a flat system prompt, but the composition knows which artifact contributed which section — and dna explain surfaces exactly that. It composes the agent (the printed provenance is for the same prompt dna emit embeds) and prints one row per composed section — the agent instruction, its Soul, each referenced Skill, each Guardrail — with the source file, a content hash, the version, and the layer the section resolved from:

$ dna explain concierge --scope concierge
Prompt provenance — concierge (scope: concierge)
section                  source file                              hash          version  origin
-----------------------  ---------------------------------------  ------------  -------  ---------
instruction              agents/concierge/AGENT.md                a1b2c3d4e5f6  -        concierge
soul                     souls/helpdesk-host/SOUL.md              f6e5d4c3b2a1  -        concierge
guardrail:grounded-...   guardrails/grounded-citation/GUARDRAIL.md  0f1e2d3c4b5a  -        concierge

Pass --tenant <t> to resolve through a tenant's overlays. When a tenant layer wins a section, that row is flagged so a per-tenant customization is visible without diffing artifacts:

$ dna explain concierge --scope concierge --tenant acme
...
  ⚠ soul: OVERRIDDEN by tenant overlay

--json returns the composed prompt plus the machine-readable provenance list, and --show-prompt prints the composed prompt below the table. The prompt in explain mode is byte-identical to build_prompt — explain never re-renders, it only attributes the string composition already produces. The same map is available from the SDK as mi.explain_prompt(agent) (Python) / mi.explainPrompt({ agent }) (TS).

The groups with their own guides

The remaining groups already have dedicated prose — one line each here:

  • dna init — make a project agent-ready: project the SDLC skill + AGENTS.md into your agent tools and bootstrap an SDLC board. This is the project onboarding command, distinct from dna install (which writes Kinds into your source — see the comparison). The full walkthrough is Make your project agent-ready. (reference)
  • dna eval — author EvalCase/EvalSuite instances and run them offline and deterministically, with the composed prompt as the default target and an EvalBaseline to gate CI on regressions; the walkthrough is How to evaluate agents. (reference)
  • dna recall (and its neutral-name alias dna search) — hybrid semantic search (dense + lexical + RRF) over a scope's instances; the full walkthrough is in How to use semantic recall & memory and the model behind it in Search & memory. (recall reference · search reference)
  • dna memory — the remember / recall-as-memory verbs layered over the Kinds you already have; same guide, memory half. (reference)
  • dna sdlc — the lifecycle tracker (Story/Feature/Issue/… plus the git symbiosis hooks) this repo runs itself on; the whole story is in Your git log is your SDLC. (reference)
  • dna intel — the intelligence layer's driver: dna intel run <source> researches an IntelSource and ranks the candidates, suppressing those below the source's actionability threshold. --analyzer [auto|llm|seed] chooses how the research happens: llm reads the source live (a repo's README + docs, a scope's docs, or an external URL hint) and asks the model for candidate insights; seed uses the offline curated insights; auto (the default) picks the LLM when an LLM key is configured and falls back to seed otherwise — so it works out of the box offline. dna intel list shows the delivered IntelInsights. Re-running a pass never re-delivers what was already surfaced — the engine dedups each candidate (a deterministic key plus a semantic cosine over the memory co-pillar's embedding space) against the source's existing insights. And the loop closes on itself: marking an insight dismissed (or actioned) records a feedback engram (an Engram) that tunes the ranker so semantically-similar candidates are suppressed (or reinforced) next time. dna intel metrics prints the feedback KPIs — precision (actioned ÷ actioned+dismissed) and the product's north-star noise rate, which should fall as the loop learns. It is a thin face over the in-core engine — the same logic the REST /v1/insights and /v1/insights/metrics faces serve. (reference)