Canonical instructions for AI coding agents and human contributors.
CLAUDE.mdand any other tool-specific file is a thin forwarder pointing here. Every codefly repo uses this layout — see Maintaining this doc set.
- Module:
github.com/codefly-dev/cli - Repository:
https://github.com/codefly-dev/cli - Language: Go 1.27.3 (
go.modis the source of truth — see Bump the Go version)
The CLI is the primary interface for codefly. It orchestrates agent lifecycles, manages the daemon, runs services with their dependency graphs, and exposes an MCP server for AI tool integration.
These rules are fleet-wide (obin-ai/handbook#68) and outrank anything below them.
A gap in the tooling is a bug in the tooling — never a reason to reach around it. If
codefly cannot express what you need, the deliverable is the missing capability, not a
script that does it behind the CLI's back. Not as a "workaround", not "just this once", not
"until the verb lands".
Never hack. Always provide the best fix, even when it spans repos. The right fix living in
core, wool, or an agent repo is not a reason to work around it here — open the PR there.
When it genuinely cannot be fixed now, ship a precise issue against the owning repo plus an
explicitly labelled stopgap. Never an unlabelled one.
Classify every change that makes something work, in the PR body: a fix at the place that owns the behaviour, or a hack. A hack does not become a fix by working, by being small, by being local, or by the real fix belonging elsewhere.
Never hardcode what the system resolves. The CLI's whole job is resolving these — network
mappings into CODEFLY__… env vars, endpoint addresses, ports, agent binaries, credentials.
Hand-writing any of them makes the failure silent: a service that boots, serves, and never
registers looks identical to one that works. If you are typing a value the orchestration layer
is supposed to inject, you are encoding something true only on your machine for ten minutes.
Diagnose, do not pattern-match. "It started working when I set X" is not a diagnosis — set X back and confirm it breaks. Do not trust an error message before checking it: a reported digest mismatch has meant a missing token, with the digest verified correct by hand.
Say what you did not verify. Unverified is not the same as working. If you could not exercise a path — no cluster, no credentials, agents not rebuilt — the PR says so.
Prove reuse before you build. The default answer is that the platform already has it and your change fills a gap or adds a helper on it, so the PR body names what you reused and what you deliberately did not. A second mechanism for a job one seam already does is refused however clean it is. Two tells you are reinventing: what you are writing re-answers a question an existing seam answered, and your reason for its placement is that a convenient loop was already there rather than that the responsibility belongs to it.
Build the simplest thing that works; size is the signal. A coding task's budget is about 500 hand-written lines — generated output does not count, tests count apart — and past 2,000 the change has become a second system, so stop and ask the user instead of finishing it. Two tells, checkable before the code exists: you cannot name the one responsibility the change adds, and it is growing to make the end goal pass rather than to put a behaviour where it belongs. Wanting it to work licenses no means to get there.
Task-oriented procedures. Each links to a full runbook under docs/runbooks/ and is also
packaged as a skill in .claude/skills/, so it triggers without this file being read. Add a
new runbook (and its skill) whenever you do a multi-step operational task a second time.
- Bump the Go version (core + cli + agents + CI images) → docs/runbooks/bump-go-version.md
- Go standards (formatting, linting) → docs/go.md
- Merge a batch of PRs (merge queue,
merge_grouptrigger, ruleset) → docs/runbooks/merge-queue.md - Cut a release (
codefly publish→ GoReleaser → Homebrew cask) → docs/runbooks/cut-a-release.md - Release affected agents (only for required agent changes, never an unchanged Core protocol) → docs/runbooks/release-the-fleet.md
- How releases & self-update work → docs/cli-updates.md
- Add a new command (Cobra wiring, help, MCP exposure) → docs/runbooks/add-a-command.md
- Scaffold a solution (
codefly add solution: the one shape, its two services, and what the composition still owes it) → docs/commands.md#add-solution - Check a solution conforms (
codefly doctor solution: stable diagnostic codes,--json; the shape is checkable, not a survey) → docs/commands.md - Rebuild the CLI and agents from local source → docs/runbooks/update-agents.md
- Point a workspace at a coordinate (import explicit
codefly/coordinate/v1environment declarations) → docs/commands.md#codefly-environment - Export a module's API contracts → docs/commands.md#generate-contracts
- The development loop (change → render → build → deploy, what is cached, how to force a rebuild, and how to tell a stale build from a wrong one) → docs/development-loop.md
- All CLI commands, by category → docs/commands.md
- Every command and flag (generated from the command tree; regenerate with
go test ./cmd -run TestCLIReferenceIsCurrent -update-reference) → docs/cli-reference.md - Orchestration engine → docs/orchestration.md
- Runnables (what the CLI does with
runnable.codefly.yaml, and what is deliberately not implemented yet) → docs/runnable.md - Product-owned selections (Core resolution, local checkouts, evidence and execution blockers) → docs/composition-selections.md
- Deployment completion stages (rendered / applied / bootstrapped / healthy, bootstrap ordering, expand/contract schema rollout) → docs/deployment-completion.md
- Module manifests (the one signed document per module: the environment's
hostblock, its workloads, endpoints and authority request, what publish settles and signs, the delivery Job, the native run) → docs/module-manifests.md - Execution receipts (the gateway's governed execution: Work Contexts verified with core's authenticator, the live sources it needs, why
--governed-executionis unavailable until a client for them exists) → docs/execution-receipts.md - The prerelease gate (why a prerelease version never reaches
main, the one labelled exception, and how each repo wires the check in) → docs/prerelease-gate.md - Agent CI & port isolation (why sequential agent CI must not share a host port) → docs/agent-ci-port-isolation.md
- Supported CLI/core/agent combinations (the conformance matrix, and why a required row cannot skip itself) → docs/supported-matrix.md
- Container-recovery marker rollout (which agents read the process marker, which create containers, what each needs before a fleet release) → docs/container-recovery-rollout.md
- Daemon → docs/daemon.md
- Dashboard → docs/dashboard.md
- MCP server → docs/mcp-server.md
- Contributing / dev setup → docs/development.md
- Design docs → docs/design/
User runs: codefly run service
│
├── Load workspace/module/service from YAML
├── Build dependency graph (architecture pkg in core)
├── Create Flow → Playbook → Policy
├── For each service in dependency order:
│ ├── Spawn agent process (gRPC server)
│ ├── Load → Init → Start (with network mappings + configs)
│ └── Monitor health + logs
├── Inject connection strings as env vars
└── TUI or headless mode
Entry point: main.go at the root re-exports cmd/codefly/main.go; GoReleaser builds
./cmd/codefly. Cobra command tree is rooted at cmd/root.go (RootCmd).
Top-level commands are registered in cmd/root.go's init() via RootCmd.AddCommand(...).
Each command with subcommands has a cmd/<name>/ package. See
docs/commands.md for the full user-facing reference and
docs/runbooks/add-a-command.md to add one.
codefly explain and codefly <verb> --help are authoritative for the surface at any commit;
docs/commands.md groups it by category and flags what is only partially
implemented. Don't rely on a list kept here — it drifts the moment a command lands.
- pkg/orchestration/ — The heart of the CLI.
Flow→Playbook→Runnermanage the full Load → Init → Start service lifecycle over gRPC.StateManagertracks shared state;PlaybookPolicygates transitions;Builder/BuildExecutorhandle build/deploy;Signallerhandles pause/resume;Hubdistributes events. See docs/orchestration.md. - pkg/daemon/ — Background process management. Re-execs the CLI with internal flags; PID
file + logs under
~/.codefly/. See docs/daemon.md. - pkg/mcp/ — Model Context Protocol server exposing codefly to AI tools. See docs/mcp-server.md.
- pkg/cliupdate/ — Version stamping (
version/commit/buildDateset via ldflags at release), self-update, and the release-signing certificate. - pkg/platform/ / pkg/gateway/ — Platform ops and Mind Gateway gRPC server.
- pkg/agentkinds/ — the one owner of the short agent kind a user types (
runnable) ↔ the kind core registers (codefly:runnable). Used bycodefly agent install --kindand the MCPlist_agents/agent_infoschemas so the convention has a single copy. - pkg/runnables/ — the one projection of a
resources.Runnablethat every listing surface emits (list runnables --json,show runnable --json, MCPlist_runnables). - pkg/cli/, pkg/builder/, pkg/deployments/, pkg/generators/, pkg/imports/, pkg/observability/, pkg/types/, pkg/web/ — supporting packages.
go build -o codefly ./cmd/codefly # Build the CLI
codefly self build # Build from source and install over the running binary
codefly self build --with-agents # ...also rebuild every canonical agent repo
go test ./... # All tests
go test ./pkg/orchestration/ -v # Orchestration tests
make lint # golangci-lint (reproduces CI)
make hooks # once per clone: gofmt on commit; lint + CLI reference on push
make check-coverage # Coverage gatego.work (created by scripts/bootstrap.sh, git-ignored) links to local core/wool via
replace directives. When working on cli + core together, check them out at the expected
relative paths.
- Deployment declarations belong to CLI.
pkg/environmentsowns the coordinate parser, cluster/registry/namespace, GitOps, ingress, quotas, service injection and identity attachments. Core owns runtime configuration and secrets; passRuntime()to agents. Producers emit the CLI contract, never private inventories to translate. See docs/configuration-contract.md. - CLI ↔ Agent communication is ALWAYS gRPC. Never import agent code, never call agent
functions. The agent runs as a separate process. Clients:
runtimev0.RuntimeClient,builderv0.BuilderClient,agentv0.AgentClient,codev0.CodeClient. - Compatibility is runtime-only. No concrete-agent roster, name exceptions, release-pin admission or linked-Core matching in CLI/Core. Unchanged protocols need no fleet repinning. Require the running peer's protocol and operation capabilities; missing is an error. See docs/agent-compatibility.md for selection and bootstrap.
- CLI tests own the host boundary, not released agents. Use test-only gRPC peers with controlled responses and real files/sockets; no released-agent downloads, fleet pins or provider toolchains in CLI gates. Agent behavior is tested by its owner.
- The orchestration package is the most critical code. Changes there affect every
codefly run. Test thoroughly. - An endpoint declaration is core v0.14.0's three axes, and every hand-out is judged
with the composition.
visibilityis reach only (private/internal/public;internalpermits every module of the composition and names nobody),exposureis addressing (publicornone, required on every public endpoint, and the ONE condition a Public network instance exists on — mirror it withresources.IsExposedEndpoint, never a visibility),locationis where it lives. An authoredallow-modulesis refused by key before decoding: the allow-list is DERIVED from consumers' declared dependencies (Workspace.DeriveAllowModules) for mesh policy and is never an input to reach, so never consult it to decide whether a consumer may be handed an address. EveryResolveDependencyNetworkMappings/ConsumedDependencyEndpoints/PermittedDependencyEndpointscall takes the*resources.Workspacefirst; the CLI is the provider, soStateManager.GetDependenciesNetworkMappingsjudges withWorld.Workspacebefore anything reaches an agent request, the output env file or the SDK, and a hand-out with no composition is refused (resources.ErrUnjudgedProvenance), never assumed. Judge a producer's manifest endpoint whole (Endpoint.Declaration()), never the mapping an agent reported. See docs/orchestration.md. - A deployed service receives the same workspace configuration groups it receives
under
run. The set is declared ∪ the composition root's, resolved once inpkg/orchestration/workspace_configurations.go; the render is the source of truth andrunmatches it. Never resolve groups in a delivery path of your own, and select the whole set before resolving anything: core drops a root group's unresolvable${endpoint:…}silently, so discovering producers from the declared groups alone deletes values from every service that did not declare the group. Widening what resolves widens what must be checked: core's plan-timeCheckEndpointReferencesvalidates visibility and producer existence over the declared groups only, so the effective set is handed to that same check, or a root group becomes the way around a producer's export boundary. Checking a reference and resolving it are one selection, in core (resources.SelectEndpointForReference, core#702): the exact name wins, an exact name the consumer may not reach is refused rather than replaced by a permitted API sibling, an ambiguous reference is refused, and only the selected endpoint is bound. They used to be two — the check stopping at the first manifest endpoint a reference matched, the resolution taking the first bound mapping with an instance for the consumer's access — and this package spent 615 lines ordering, pruning and finally replaying core's scan to keep them in step. Pass core the consumer and the producer's manifest (Manager.ForConsumerModule) instead of modelling its scan beside it. The mappings bound for a consumer are still filtered by visibility (World.exportableTo), which is assembly rather than selection: a private address never enters the resolution at all. The filter covers the consumer's own dependency mappings as well — a bare dependency is handed every endpoint its producer published — and judges from the producer's manifest, never from the mapping an agent reported. And a root group's credentials are not run-wide: they reach the services that declare the group, in run and in render, so making the render resolve root groups does not hand every workload every credential; a value a service does not receive is decided before anything is checked or resolved, so it imposes no obligation on that service. See docs/orchestration.md. - Configuration uses runtime carriers. Live host routes are the endpoint addresses inside each module's applied manifest; the native run replaces
files in one stable directory. See
docs/module-manifests.mdfor delivery ownership. - Daemon state lives in
~/.codefly/(override withCODEFLY_HOME). PID file, logs, agent binaries all live there. - MCP exposes codefly capabilities to AI agents. When adding a CLI feature, consider whether it should also be an MCP tool.
- Two-repo changes: adding a core resource field from a CLI worktree needs a companion core PR + pseudo-version bump (core is read-only in CLI worktrees).
CODEFLY_DEBUG · CODEFLY_SILENT · CODEFLY_WORKSPACE · CODEFLY_HOME · CODEFLY_HELP_PROVIDER
AGENTS.mdis the entry point — it carries the behavioural rules, the how-to index, and a skimmable architecture summary. Hard cap ~200 lines: past that it costs context on every request and adherence drops. When it grows, add a nestedAGENTS.mdin the subdirectory (the closest file to the edited file wins, like.gitignore) — never append here..claude/skills/<name>/SKILL.mdmakes a runbook self-triggering: the frontmatterdescriptionis all an agent sees before loading it, so it must say what it does and when to use it. Keep the body to the decision — when this applies, what must not be skipped — and let it point at the runbook for the steps, so there is one copy to drift. Every runbook has one.docs/runbooks/*.mdanswer "how do I do X" as ordered, copy-pasteable steps.docs/*.mdare reference deep-dives (concepts, not procedures).- When a fact here (a version, a path, a flag) changes in code, update the doc in the same PR.
Prose that drifts from
go.mod/root.go/.goreleaser.yamlis worse than no prose.