Skip to content
nikcli
Docs
Documentation

Brain

The Brain subsystem periodically consolidates session memory and re-evaluates long-running context — TUI plugin, server route, and headless scheduler.

Overview

Brain is nikcli's "long-term memory" subsystem. While a session is running, every tool call, message, and decision lives in the session's JSON store; Brain's job is to periodically consolidate that signal into a single, well-organised memory.instruction.md file that future coding sessions can read at startup to orient quickly.

Brain is implemented in packages/nikcli/src/brain/ and surfaces in three places: a headless scheduler that ticks every hour, a TUI feature plugin for the brain model picker, and a server route (/brain) plus a typed httpapi/brain.ts group for the Studio dashboard and external clients.

Experimental feature flag
Brain is disabled by default. Enable it by setting experimental.brain to true in nikcli.json, or turn on Enable Brain in Brain Settings.

What Brain does

When triggered, Brain runs a real nikcli session under the hood — but a session with a very narrow scope. It:

  1. Reads the existing memory file (if any) under .github/instructions/memory.instruction.md.
  2. Pulls the most recent N sessions (default 10) and formats their message parts into a compact review.
  3. Sends the prompt (Brain.buildBrainPrompt(memoryPath, reviews, currentMemory)) to a dedicated session titled BRAIN_SESSION_TITLE, with permissions restricted to read/edit-only — no task, no todo, no bash.
  4. Cancels the session via prompt.cancel after BRAIN_SESSION_TIMEOUT_MS = 5 minutes.
  5. Diff-checks the memory file before/after. If it changed, marks the run as a success and writes the brain lock file (Global.Path.state/.brain-lock) so the next shouldTrigger check has a fresh anchor.

The consolidating model is configurable through experimental.brainModel ("provider/model"). When the configured model is unknown or unreachable, Brain silently falls back to the user's default model so the pass still runs.

Trigger conditions

Brain.shouldTrigger() decides whether the scheduler should fire. It refuses to run when Brain or memory is disabled, when the cooldown has not elapsed, when a scan has happened in the last SCAN_THROTTLE_MS = 10 minutes, or when there aren't enough sessions since the last successful Brain pass.

Constant Default Meaning
DEFAULTS.minHours 24 Minimum hours between successful Brain passes (configurable via experimental.brainMinHours).
DEFAULTS.minSessions 5 Minimum new sessions since the last pass before Brain fires (configurable via experimental.brainMinSessions).
SCAN_THROTTLE_MS 10 * 60 * 1000 How often shouldTrigger() is allowed to scan session history.
LOCK_DURATION_MS 1 hour Stale-lease duration for the brain flock lock.
BRAIN_SESSION_TIMEOUT_MS 5 minutes Hard ceiling on a single Brain session; cancelled via prompt.cancel on expiry.
SESSION_REVIEW_LIMIT 10 How many recent sessions are included in the prompt.
SESSION_REVIEW_MAX_CHARS 12_000 Per-session review truncation length.

The scheduler registers itself with Scheduler.register({ id: "brain", interval: 60 * 60 * 1000, scope: "instance" }) — one tick per hour, scoped to the project instance.

Force runs bypass the throttle
Brain.trigger({ force: true }) skips the shouldTrigger gate (except for the enabled and memoryEnabled checks) and reviews the most recent sessions even when no time has elapsed. The TUI dialog and the /brain/trigger route expose this path.

Configuration

All Brain knobs live under experimental in nikcli.json. This example enables Brain:

nikcli.json
json
{
  "experimental": {
    "brain": true,
    "memory": true,
    "brainMinHours": 24,
    "brainMinSessions": 5,
    "brainModel": "anthropic/claude-sonnet-4.5"
  }
}
Field Default Meaning
experimental.brain false Master switch for the Brain subsystem. When false, neither the scheduler nor a manual trigger will fire.
experimental.memory true Whether Brain is allowed to write the memory file. Turning this off keeps Brain's read path active but disables consolidation.
experimental.brainMinHours 24 Override DEFAULTS.minHours.
experimental.brainMinSessions 5 Override DEFAULTS.minSessions.
experimental.brainModel (default provider/model) "provider/model" string the consolidation session runs on. Falls back silently to the user's default model when unavailable.

Memory file

Brain writes to .github/instructions/memory.instruction.md inside the project directory (created on demand). The path is the same convention used by GitHub Copilot for project-level instructions, so the file is shared automatically with anything in the repo that reads .github/instructions/*.md.

The consolidating prompt focuses on four phases — orient, gather, consolidate, prune — and explicitly forbids creating a new memory file or asking the user questions. The full prompt template is in Brain.buildBrainPrompt(); the resulting memory file is meant to be concise, dated, and indexed for fast lookup.

Brain is also a long-term signal for other agents
Because the file lives under .github/instructions/, every future nikcli session in the same project picks it up automatically — it becomes part of the system prompt context for the next agent run.

TUI & API surface

Brain is reachable from three places: the scheduler (passive), the TUI dialog (dialog-brain.tsx — pick the model and force a run), and the server routes.

dialog-brain.tsx

TUI dialog

Bound to the brain feature plugin and surfaced from the command palette. Lets you pick the consolidation model and trigger a forced run.

server/httpapi/brain.ts

Server routes

GET /brain returns the full status payload (enabled, memory enabled, min hours, min sessions, last brain at, hours since, sessions since, should trigger, configured model). POST /brain/trigger accepts {force: boolean} and runs Brain.trigger.

server/httpapi/brain.ts

Typed HttpApi

Same surface under the typed HttpApi group (operationId: brain.status, brain.trigger) — mounted through HttpApiBridge when NIKCLI_EXPERIMENTAL_HTTPAPI is set. Used by the Studio dashboard and SDK clients.

Headless scheduler
The scheduler registers itself from initBrainScheduler() with scope: "instance". The scheduler definition is in packages/nikcli/src/scheduler/; one tick per hour, per project instance.

Source anchors

Concern File
Core service + consolidation prompt packages/nikcli/src/brain/index.ts
Hourly scheduler packages/nikcli/src/brain/scheduler.ts
Constants @nikcli-ai/util/brain-constants
Classic routes packages/nikcli/src/server/httpapi/brain.ts
Typed HttpApi packages/nikcli/src/server/httpapi/brain.ts
TUI plugin packages/tui/src/feature-plugins/brain/index.tsx
TUI dialog packages/tui/src/component/dialog-brain.tsx