Skip to content

Latest commit

 

History

History
282 lines (239 loc) · 18.9 KB

AGENTS.md

File metadata and controls

282 lines (239 loc) · 18.9 KB

AGENTS.md — codefly/cli

Canonical instructions for AI coding agents and human contributors. CLAUDE.md and any other tool-specific file is a thin forwarder pointing here. Every codefly repo uses this layout — see Maintaining this doc set.

Module & Repository

  • Module: github.com/codefly-dev/cli
  • Repository: https://github.com/codefly-dev/cli
  • Language: Go 1.27.3 (go.mod is 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.


How to work here

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.

How-To Index

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.

Toolchain & dependencies

Shipping

Extending the CLI

Reference (deep dives, not step-by-step)

  • 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 host block, 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-execution is 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/

Architecture Overview

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).

Command groups (cmd/)

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.

Package hierarchy (pkg/)

  • pkg/orchestration/ — The heart of the CLI. Flow → Playbook → Runner manage the full Load → Init → Start service lifecycle over gRPC. StateManager tracks shared state; PlaybookPolicy gates transitions; Builder/BuildExecutor handle build/deploy; Signaller handles pause/resume; Hub distributes 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/buildDate set 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 by codefly agent install --kind and the MCP list_agents/agent_info schemas so the convention has a single copy.
  • pkg/runnables/ — the one projection of a resources.Runnable that every listing surface emits (list runnables --json, show runnable --json, MCP list_runnables).
  • pkg/cli/, pkg/builder/, pkg/deployments/, pkg/generators/, pkg/imports/, pkg/observability/, pkg/types/, pkg/web/ — supporting packages.

Build & Test

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 gate

go.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.


Key Patterns & Rules

  • Deployment declarations belong to CLI. pkg/environments owns the coordinate parser, cluster/registry/namespace, GitOps, ingress, quotas, service injection and identity attachments. Core owns runtime configuration and secrets; pass Runtime() 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. visibility is reach only (private / internal / public; internal permits every module of the composition and names nobody), exposure is addressing (public or none, required on every public endpoint, and the ONE condition a Public network instance exists on — mirror it with resources.IsExposedEndpoint, never a visibility), location is where it lives. An authored allow-modules is 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. Every ResolveDependencyNetworkMappings / ConsumedDependencyEndpoints / PermittedDependencyEndpoints call takes the *resources.Workspace first; the CLI is the provider, so StateManager.GetDependenciesNetworkMappings judges with World.Workspace before 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 in pkg/orchestration/workspace_configurations.go; the render is the source of truth and run matches 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-time CheckEndpointReferences validates 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.md for delivery ownership.
  • Daemon state lives in ~/.codefly/ (override with CODEFLY_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).

Key environment variables

CODEFLY_DEBUG · CODEFLY_SILENT · CODEFLY_WORKSPACE · CODEFLY_HOME · CODEFLY_HELP_PROVIDER


Maintaining this doc set

  • AGENTS.md is 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 nested AGENTS.md in the subdirectory (the closest file to the edited file wins, like .gitignore) — never append here.
  • .claude/skills/<name>/SKILL.md makes a runbook self-triggering: the frontmatter description is 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/*.md answer "how do I do X" as ordered, copy-pasteable steps.
  • docs/*.md are 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.yaml is worse than no prose.