Documentation menu

Development and testing

Source checkouts need Node.js 22.13+ for node:sqlite, pnpm, and Git. Run pnpm install at the root. The source entry is node packages/nid/bin/nid.mjs; release binaries compile the same CLI with Bun.

LocationPurpose
src/cliCLI commands and formatting.
src/daemonOrchestration, RPC, integration and agent prompts.
src/driversClaude, Codex, ACP, PTY and mock drivers.
src/dashboardLocal listener and bundled browser UI.
src/mcpAgent coordination tools.
testUnit, end-to-end, browser and optional live tests.
scriptsBinary builds, signing and release publication.
spikesEarly integration probes.

See building and releasing for compile targets, signing credentials and local publishing; Daemon API for integration contracts.

Development and tests#

pnpm install
pnpm --filter nid typecheck
pnpm --filter nid test       # node:test: unit tests plus e2e suites with mock agents
pnpm --filter nid test:bun   # real PTY tests under Bun; PTY tests skip under Node

The end-to-end suites start a real daemon, real nid-mcp servers and real git worktrees in a temporary NID_HOME, driven by the scripted mock agent (see src/drivers/mock.ts). Set NID_BIN to a compiled binary to run them against it instead of the sources (CI does this on Linux, macOS and Windows). Check that it is unset when you mean to test the sources: an installed nid may have put it in your environment, and the suites then silently test that binary.

The Node, Bun and browser suites run each file under a process supervisor (test/run-tests.mjs). It supplies temporary NID_HOME, TERMINID_HOME and PAWMUX_HOME, journals child launches (including detached daemon autostarts and MCP grandchildren), and fails with the file name, PID and command when teardown leaves a process alive. It cleans up those owned processes on failure, timeout or interruption. Each file has a four-minute deadline (override with NID_TEST_FILE_TIMEOUT_MS; the opt-in migration build uses twenty minutes). To supervise one file, run node test/run-tests.mjs test/e2e.test.ts from packages/nid. Fixtures must still register teardown before starting agents and wait for shutdown; the supervisor is a final check, not a substitute for teardown. It never kills a process based only on its name or a scan of the user's home.

Use setupE2E() for a shared daemon, or stopTestDaemon(home) from test/process-tracker.ts in a custom fixture's teardown. Register teardown before waiting for readiness, so failed startup is covered too. Scratch homes outside the system temp directory must call registerTestHome(home) before their first launch so the supervisor can recover a compiled daemon after a test timeout.

For the compiled launch smoke test, run these commands from the repository root in PowerShell (use the matching dist binary on other platforms):

pnpm build:bin --target native
$env:NID_BIN = (Resolve-Path dist/nid-windows-x64.exe).Path
$env:NID_COMPILED_SMOKE = "1"
node packages/nid/test/run-tests.mjs packages/nid/test/compiled-smoke.test.ts
Remove-Item Env:NID_COMPILED_SMOKE, Env:NID_BIN

This checks init --yes, starts its generated config with a mock agent, forces compaction through rotate, runs doctor and read-only autostart/uninstall commands, and checks the embedded dashboard includes setup and context controls. It isolates the repository, daemon data and user directories, excludes vendor CLIs from PATH, and answers the release check locally. It does not change the real installation, login items or user PATH. Without NID_COMPILED_SMOKE=1, the test skips.

For the interactive attach --tty smoke test, build a native binary, set NID_TEST_BINARY to its absolute path, and run pnpm --filter nid test:bun. It starts a disposable daemon and scripted PTY agent, checks input, Ctrl-C, resize, queued delivery, and Ctrl-] detach. Without that variable the compiled CLI smoke test skips.

The upgrades to nid have their own end-to-end test with real binaries: pnpm test:migration builds the last pawmux alpha and the last terminid alpha (v0.1.0-alpha.12) from git history (or copies released ones with --old-pawmux <path> / --old-terminid <path>) and the native nid target (or uses --dist <dir>), and runs two upgrades in scratch homes against a local fake release server. First the whole history: a pawmux daemon with mock agents, worktrees (one held open during the first update), an API key and a pawmux.yaml runs its own pawmux update to terminid alpha.12, then that terminid's own terminid update to nid. Then a pawmux-only user runs pawmux update straight to nid. Both check that nid took over: install moved, PATH updated, old daemon stopped, state and every worktree group migrated (including the one the pawmux -> terminid move had left behind), the running sessions resumed and working (the stopped one still stopped), the key readable, the terminid and pawmux shims and an untouched old terminid binary all reaching the nid daemon, nid doctor green, and nid migrate renaming the config. It takes a minute or two and never touches your real install, homes, PATH or login items; --keep leaves the scratch tree for inspection. In pnpm test it skips.

The optional browser smoke suite uses Playwright Chromium and a disposable daemon; the normal test command does not require a browser download:

pnpm --filter nid exec playwright install chromium
pnpm --filter nid test:browser

It skips with installation instructions when Chromium is absent. Set NID_BROWSER_EXECUTABLE to use an existing compatible Chromium executable, or set NID_REQUIRE_BROWSER=1 to fail instead of skip (the Ubuntu browser CI job does this). The suite checks browser login, the authenticated API, rejection of foreign-origin requests, and that the page makes no requests to external hosts. It follows a real mock agent from overview to transcript, approves its request in the inbox, and sends a chat message. Additional browser suites cover task/config/group actions, live board updates, questions, session stop/resume, and 375px layouts.

NID_LIVE_CODEX=1 node --import tsx --test test/codex-live.test.ts (in packages/nid) runs a short smoke test against the real codex CLI: a sandboxed write, a denied escalation, the nid tools, stop + resume, and token counts. It spends a few model tokens; set NID_LIVE_CODEX_MODEL to pick the model.

More opt-in live suites against real CLIs (each spends a few tokens and uses its own daemon and NID_HOME; NID_LIVE_LOG=<file> dumps events where supported):

Command (in packages/nid)Checks
NID_LIVE_OPENCODE=1 node --import tsx --test test/opencode-live.test.tsopencode over ACP: system prompt, nid tools, claiming, mid-turn messages, an assigned task end to end, cancel
NID_LIVE_PTY=1 pnpm test:live-ptythe PTY driver against the node REPL and the claude, codex and opencode TUIs (Bun)
NID_LIVE_NATIVE=1 node --import tsx --test test/native-live.test.tsattach --native takeover and hand-back for claude, codex and opencode (NID_LIVE_NATIVE_AGENTS picks agents)