Appearance
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| Tree | Owner | Lifecycle | Location |
|---|---|---|---|
| Source | Contributors | Normal git on the nubos-pilot repo | tools/nubos-pilot/ |
| Install-Payload | End user (managed by installer) | Overwritten on npx nubos-pilot reinstall, manifest-tracked | .claude/nubos-pilot/, .opencode/nubos-pilot/ |
| Project-State | End user's project | Mutated 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 inbin/np-tools/<name>.cjs, or composes a default phase payload viacomposeInit()and emits it to stdout.- Top-level commands are utility/leaf commands (
commit-task,checkpoint,metrics,doctor,dispatch,askuser, …) registered intopLevelCommands. Samebin/np-tools/<name>.cjsshape.
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:
- Resolves the project root and acquires
.nubos-pilot/.install.lock. - Detects
initvsre-installmode by checking for an existing.manifest.jsonin the payload. - On
init, runs the four-question interview (runtime, scope, model_profile, response_language) throughlib/askuser.cjsand writes.nubos-pilot/config.json. - Stages a copy of
templates/claude/payload/into.nubos-pilot/.staging.tmp/, builds a SHA-256 manifest, diffs against the old manifest. - Backs up user-modified files to
.bak.<n>, writes new files atomically viafs.renameSync. - Optionally repeats for
templates/opencode/payload/→.opencode/nubos-pilot/. - Rewrites the managed block in
CLAUDE.md/AGENTS.md/GEMINI.md. - 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 byyaml@^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 vianp-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.
