Skip to content

Architecture

nubos-pilot is split into a small set of layers. Every layer has a single owner and a single failure mode.

Three trees, three lifecycles

ADR-0005 defines the spine of the system. Three trees never overlap at runtime:

mermaid
flowchart LR
  subgraph Source["Source<br/>(contributors only)"]
    src["tools/nubos-pilot/<br/>git repo"]
  end
  subgraph Payload["Install-Payload<br/>(installer-managed)"]
    payload[".claude/nubos-pilot/<br/>+ .opencode/nubos-pilot/"]
  end
  subgraph State["Project-State<br/>(workflow-managed)"]
    state[".nubos-pilot/<br/>plans + history"]
  end
  src -- "npx nubos-pilot<br/>(install)" --> payload
  payload -- "host CLI invokes<br/>np:* commands" --> state
  state -. "read by<br/>workflows + agents" .-> payload
TreeOwnerLifecycleLocation
SourceContributorsNormal git on the nubos-pilot repotools/nubos-pilot/
Install-PayloadEnd user (managed by installer)Overwritten on npx nubos-pilot reinstall, manifest-tracked.claude/nubos-pilot/, .opencode/nubos-pilot/
Project-StateEnd user's projectMutated only by workflows under a single-writer file lock.nubos-pilot/

The orthogonality rule is hard: a file is source, payload or state, never two at once.

Source layout

tools/nubos-pilot/
├── bin/
│   ├── install.js              # entry point exposed as `nubos-pilot` bin
│   ├── check-coverage.cjs
│   ├── check-workflows.cjs
│   └── np-tools/               # one .cjs per top-level/init subcommand
├── lib/                        # parsers, runtime adapters, helpers
│   ├── core.cjs                # NubosPilotError, atomicWriteFileSync, withFileLock
│   ├── phase.cjs, plan.cjs, tasks.cjs, roadmap.cjs, state.cjs
│   ├── agents.cjs, frontmatter.cjs, model-profiles.cjs
│   ├── git.cjs, metrics.cjs, checkpoint.cjs, undo.cjs, verify.cjs
│   ├── install/                # manifest, staging, backup, runtime-detect
│   └── runtime/                # claude / codex / gemini / opencode adapters
├── np-tools.cjs                # single-entry CLI
├── agents/                     # np-* role markdowns
├── workflows/                  # np:* slash-command markdowns
├── templates/                  # PROJECT.md, CONTEXT.md, PLAN.md scaffolds
└── docs/                       # ADRs + canonical schemas (this site mirrors these)

CLI entry point

np-tools.cjs is the single executable surface. It dispatches in two layers:

  • init <workflow> for orchestrator workflows (plan-phase, discuss-phase, execute-phase, new-project, …). It either calls a registered handler in bin/np-tools/<name>.cjs, or composes a default phase payload via composeInit() and emits it to stdout.
  • Top-level commands are utility/leaf commands (commit-task, checkpoint, metrics, doctor, dispatch, askuser, …) registered in topLevelCommands. Same bin/np-tools/<name>.cjs shape.

Output is JSON. Payloads above 16 KB are written to .nubos-pilot/.tmp/init-<workflow>-<pid>-<rand>.json, and the path is emitted as @file:<path> so workflow shell blocks can cat it.

Installer

bin/install.js is the only thing that mutates the Install-Payload tree. It:

  1. Resolves the project root and acquires .nubos-pilot/.install.lock.
  2. Detects init vs re-install mode by checking for an existing .manifest.json in the payload.
  3. On init, runs the four-question interview (runtime, scope, model_profile, response_language) through lib/askuser.cjs and writes .nubos-pilot/config.json.
  4. Stages a copy of templates/claude/payload/ into .nubos-pilot/.staging.tmp/, builds a SHA-256 manifest, diffs against the old manifest.
  5. Backs up user-modified files to .bak.<n>, writes new files atomically via fs.renameSync.
  6. Optionally repeats for templates/opencode/payload/.opencode/nubos-pilot/.
  7. Rewrites the managed block in CLAUDE.md / AGENTS.md / GEMINI.md.
  8. Writes the new manifest at .claude/nubos-pilot/.manifest.json.

All filesystem mutations route through atomicWriteFileSync and withFileLock in lib/core.cjs, which rules out partial writes and concurrent writers.

Subagent model

Workflows orchestrate. Subagents do the actual reading, writing and reasoning. Every agent is a single markdown file in agents/ with the canonical D-09 frontmatter (reference):

yaml
---
name: np-planner
description: Creates executable phase plans …
tier: opus
tools: Read, Write, Bash, Glob, Grep
color: green
---

lib/agents.cjs validates frontmatter on every load; there is no cache. The validator runs four gates in strict order: REQUIRED → FORBIDDEN → TIER_ENUM → name-match. The first failure throws a NubosPilotError, and the remaining gates are skipped.

The forbidden list (model, model_profile, hooks) keeps agents portable across runtimes. Concrete model selection happens out-of-band through np-tools.cjs resolve-model <agent> <tier>, which the workflow consults at spawn time.

Runtime adapters

lib/runtime/ is a thin abstraction over the supported host CLIs. KNOWN_RUNTIMES in lib/runtime/index.cjs currently lists fourteen ids: four first-class (claude, codex, gemini, opencode) plus ten additional adapters (antigravity, augment, cline, codebuddy, copilot, cursor, kilo, qwen, trae, windsurf). The adapter contract is enforced by _contract.test.cjs. getCurrent() resolves the active runtime in priority order: it reads runtime from .nubos-pilot/config.json first, then falls back to env detection of the host process. The codex default applies only when neither config nor env identifies a runtime.

Runtime-specific concerns (slash-command syntax, hook registration) live only here, never in agent frontmatter and never in workflow bodies. This is the seam that lets the same source tree install into each supported CLI.

Project-state mutations

.nubos-pilot/ is the only place workflows write user data. The single-writer guarantee comes from withFileLock (built on O_EXCL lockfiles in lib/core.cjs); concurrent workflow runs queue rather than race. State files are append-only or atomic-write, never partial-update.

Key state files:

  • PROJECT.md — product truth (filled by /np:discuss-project).
  • REQUIREMENTS.md — requirement register.
  • roadmap.yaml — source of truth (schema_version: 2, parsed by yaml@^2.8).
  • STATE.md — current milestone, current task, session metadata.
  • milestones/M<NNN>/ — milestone artifacts. See Directory Layout.
  • checkpoints/<task-full-id>.json — crash-safety pointers managed via np-tools.cjs checkpoint.
  • metrics/*.jsonl — append-only metrics log.
  • todos/pending/*.md, notes/*.md, threads/*.md — capture artifacts.
  • codebase/ — module docs from /np:scan-codebase / /np:update-docs.