Skip to content

Architecture Overview ​

n-dx is organized as a pnpm monorepo with a strict four-tier dependency hierarchy.

Dependency Tiers ​

  Orchestration   cli.js, web.js, ci.js        (spawns CLIs, no library imports)
                  config.js                     (spawn-exempt — see below)
       ↓
  Execution       hench                         (agent loops, tool dispatch)
       ↓
  Domain          rex · sourcevision            (independent, never import each other)
       ↓
  Foundation      @n-dx/llm-client              (shared types, API client)
                  @n-dx/lang-kit                (language facts, pack contract)

Rules:

  • Each layer imports only from the layer below
  • Domain packages never import each other
  • Orchestration scripts spawn CLIs via execFile — they never import from packages
  • Zero circular dependencies

The web package sits alongside orchestration — it imports domain packages through gateway modules to serve the dashboard and MCP endpoints.

Spawn-exempt exception ​

config.js directly reads/writes package config files (.rex/config.json, .hench/config.json, .sourcevision/manifest.json, .n-dx.json) rather than spawning CLIs. Config operations require cross-package reads, atomic merges, and validation logic that can't be expressed as a single CLI spawn.

Monorepo Structure ​

packages/
  core/            # CLI orchestrator (published as @n-dx/core)
    cli.js         #   n-dx entry point (orchestration + delegation)
    ci.js          #   CI pipeline (analysis + PRD health validation)
    web.js         #   server orchestration (start/stop/status)
    config.js      #   unified config command (view/edit all package settings)
  sourcevision/    # analysis engine
  rex/             # PRD + task tracker
  hench/           # autonomous agent
  llm-client/      # vendor-neutral LLM foundation
  lang-kit/        # language facts + pack contract (foundation, no deps)
  web/             # dashboard + MCP HTTP server

Cross-Tier Communication ​

Spawn vs. Gateway ​

SignalUse spawnUse gateway
Caller tierOrchestration (cli.js, ci.js, web.js)Execution or Domain
Data flowFire-and-forget or exit-code onlyStructured return values
FrequencyPer-command (once per CLI invocation)Per-request (hot path)
Error handlingExit code + stderrTyped errors, retries, partial results
State sharingNone (stateless)Shared in-memory state

See Gateway Modules for the full gateway pattern.

Extensions ​

Extensions sit outside the tiers. An extension is a separately installed npm package that core discovers by reading its package.json. Core spawns the extension's commands, and the hub spawns and proxies its sidecar at /x/<name>/. No package imports extension code, and extensions reach n-dx only by spawning ndx or calling the hub and project endpoints. The design is in Extensions, and the author-facing contract is in the extension guide.

Shared State via Filesystem ​

Packages share state through filesystem rather than runtime imports:

DirectoryOwnerReaders
.sourcevision/sourcevisionrex, web
.rex/rexhench, web
.hench/henchweb

Single writer per file. Hench and web read .rex/ files but modify PRD state by invoking rex APIs. See PACKAGE_GUIDELINES.md for the full write-access protocol.

Concurrency ​

Command pairSafe?Notes
start + statusYesStatus is read-only
start + workYesDifferent write targets — except a settings save to n-dx's own config (.n-dx.json, .n-dx.local.json, .ndx/config*.json, .mcp.json): the run's config backstop reverts it at its next restore point and fails the run; save again after the run
start + planWarningPlan writes PRD roots; restart server to flush in-process caches
ci + workNoBoth write analysis and PRD roots
plan + workNoBoth write PRD roots
refresh + any writerNoRefresh can rewrite analysis and PRD state
config + configNoBoth merge shared config files
extensions add|remove + another extension writerNoBoth run npm in the shared per-user extensions folder and rewrite extensions.json; serialize them across worktrees.
sv pack add|remove + another sv pack add|removeNoBoth write the per-user pack directory, shared by every worktree and project on one ndx home; the second fails naming the holder's PID

General rule: Commands that write to PRD roots, analysis output, or hench config must not run concurrently. On v2 the PRD roots are product/ and changes/ under the resolved rex directory (.ndx/rex/ or legacy .rex/); on v1 it is prd_tree/. A PRD lock is per workspace, so separate worktrees can each write their own tree. A lock around a save alone cannot repair a stale snapshot. See PRD write concurrency and the public contract and merge requirements.

Released under the Elastic License 2.0.