docs blog library
github

Code map

This page helps you find where your change goes, and follow the conventions already there.

This is the contributor-facing counterpart to the maintainer documentation: where the code lives and how it’s wired, rather than what the harness does at runtime.


Core Principles

Jazz is a Bun workspace under packages/*. The dependency rule below is not just documented convention: it’s structurally enforced by TypeScript project references, so tsc -b rejects a package importing from one it doesn’t declare a reference to.

  • packages/core/ contains the domain, contracts (interfaces and types), and business logic.
    • No imports from packages/adapters/ or packages/cli/ allowed in core, except in tests.
    • Contracts are expressed as interfaces + Context tags (e.g., AgentConfigServiceTag).
    • Publishable standalone as @jazz/core: no workspace dependencies of its own.
  • packages/plugin-sdk/ is the zero-runtime-dependency public TypeScript contract for plugin authors. Core structurally implements that plain-JavaScript ABI without exposing Effect values.
  • packages/adapters/ implements adapters for model providers, configuration, storage, files, MCP, telemetry, notifications, peers, and webhooks, plus the goal and loop cycle runners and run-ownership primitives that the CLI and the daemon both use.
    • Adapters provide Layers that satisfy the tags declared in core/interfaces.
    • Depends on core only.
  • packages/daemon/ is the daemon application: the authenticated HTTP server, the trigger/goal/loop/job ticks, attention gating, and OS service install.
    • Depends on core and adapters; nothing below it imports it (an eslint rule and scripts/package-layering.test.ts enforce that).
  • packages/cli/ contains user-facing command implementations, Ink/OpenTUI presentation, and the terminal-rendering TerminalService implementation.
    • Depends on core, adapters, daemon, bot-shared, and the bridge packages it launches.
  • packages/runtime/ is the composition root: wires core, adapters, and cli into the Effect Layer graph that becomes the jazz binary.
    • Depends on core, adapters, and cli.
  • packages/bot-shared/, packages/telegram-bot/, packages/discord-bot/, packages/whatsapp-bot/, packages/imessage-bot/, and packages/photon-bot/ are the chat-bridge integrations. bot-shared holds the turn, approval, and sandbox helpers the bridges share.
  • packages/website/ is the Astro site that serves the public docs, the marketplace library, and the plugin catalog.

The dependency rule

flowchart TB
    CLI["<b>packages/cli/</b><br/>commands · Ink TUI · presentation · TerminalService"]
    CORE["<b>packages/core/</b><br/>agent loop · tools · context · types<br/><b>interfaces = ports</b><br/><i>imports nothing outward</i>"]
    ADP["<b>packages/adapters/</b><br/>llm · storage · mcp · history<br/>logger · telemetry · notification"]
    DMN["<b>packages/daemon/</b><br/>HTTP server · ticks · attention · service install"]
    RT["<b>packages/runtime/</b><br/>composition root · jazz binary"]

    CLI -->|"calls"| CORE
    ADP -->|"implements ports"| CORE
    DMN -->|"runs on"| ADP
    CLI -->|"jazz daemon"| DMN
    RT -.->|"merges Layers at startup"| CLI
    RT -.->|"merges Layers at startup"| ADP
    RT -.->|"merges Layers at startup"| CORE

    NO["core/ → adapters/ or cli/<br/><b>never</b> (except in tests)<br/>adapters/ → daemon/ <b>never</b>"]

    classDef core fill:#4f9d9d,stroke:#2f6d6d,color:#ffffff
    classDef forbidden fill:#c1443c,stroke:#7d2b26,color:#ffffff
    class CORE core
    class NO forbidden

Arrows point inward, always. That’s what lets you swap a storage backend or add an LLM provider by touching one package, and test the agent loop with plain mocks.

Adding a capability follows the arrows in reverse:

flowchart LR
    A["1 · Define the port<br/>packages/core/src/interfaces/foo.ts<br/>interface + Context.GenericTag"]
    B["2 · Implement the adapter<br/>packages/adapters/src/foo.ts<br/>+ a Layer"]
    C["3 · Register the Layer<br/>packages/runtime/src/app-layer.ts"]
    D["4 · Test with a mock Layer<br/>Layer.succeed(FooTag, fake)"]
    A --> B --> C --> D

    classDef step fill:#4f9d9d,stroke:#2f6d6d,color:#ffffff
    class A,B,C,D step

Directory Structure

packages/
├── cli/src/                      # @jazz/cli: user-facing CLI
│   ├── commands/                 # Command entry points and orchestration
│   ├── setup/                    # Shared interactive provider/search configuration flows
│   ├── helpers/                  # Narrow detection, parsing and formatting operations
│   ├── presentation/             # Output formatting (markdown, CLI renderer)
│   ├── chat-service.ts           # Chat orchestrator (UI-touching; lives here, not adapters)
│   ├── chat/                     # Chat service modules
│   │   ├── commands/             # Slash command handling
│   │   │   ├── parser.ts         # Parse /help, /new, etc.
│   │   │   ├── handler.ts        # Route commands and apply shared command behavior
│   │   │   └── conversation.ts   # Start, fork, resume and rename conversations
│   │   └── session/              # Session management
│   │       ├── manager.ts        # ID generation, logging
│   │       └── agent-setup.ts    # MCP connection setup
│   ├── terminal.ts               # TerminalService implementation (Ink/OpenTUI rendering)
│   └── ui/                       # Ink React components
│       ├── App.tsx               # Main app with store pattern
│       ├── ErrorBoundary.tsx     # Error boundary for graceful failures
│       ├── LineInput.tsx         # Readline-style input component
│       └── text-utils.ts         # Word boundary utilities
│
├── core/src/                     # @jazz/core: domain and contracts
│   ├── agent/                    # Agent execution engine
│   │   ├── agent-runner.ts       # Orchestrator (delegates to executors)
│   │   ├── types.ts              # Shared types (AgentRunnerOptions, etc.)
│   │   ├── context/              # Context management
│   │   │   └── summarizer.ts     # Auto-summarization for context window
│   │   ├── execution/            # LLM execution strategies
│   │   │   ├── streaming-executor.ts  # Real-time streaming
│   │   │   └── batch-executor.ts      # Non-streaming execution
│   │   ├── prompts/              # System prompts by agent type
│   │   └── tools/                # Tool implementations
│   │       ├── fs/               # Filesystem tools (read, write, grep, etc.)
│   │       ├── command-risk.ts   # execute_command risk classifier (LLM)
│   │       ├── tool-categories.ts # Builtin category ids + mappings
│   │       ├── register-tools.ts # Builtin tool registration
│   │       └── register-mcp-tools.ts # Per-agent MCP connect + register
│   ├── interfaces/               # Service contracts (Tag + Interface)
│   ├── types/                    # Domain types
│   └── utils/                    # Shared utilities
│
├── adapters/src/                 # @jazz/adapters: adapter implementations
│   ├── llm/                      # LLM provider adapters
│   ├── mcp/                      # MCP client + OAuth
│   ├── peers/                    # ask_peer ledger/token adapters
│   ├── goals/, loops/            # Goal/loop actions and cycle runners
│   ├── runs/                     # Run ownership: in-flight set, resume-owned-run
│   └── storage/                  # Persistence (JSON file storage)
│
├── daemon/src/                   # @jazz/daemon: HTTP server, ticks, attention, service install
│
├── runtime/src/                  # @jazz/runtime: composition root
│   ├── entry.ts                  # Binary entrypoint
│   ├── cli-app.ts                # Commander.js program, command registration
│   └── app-layer.ts              # Effect Layer composition
│
├── bot-shared/src/                # @jazz/bot-shared: shared bridge helpers
├── telegram-bot/src/              # Telegram bridge
├── discord-bot/src/               # Discord bridge
├── whatsapp-bot/src/              # WhatsApp bridge (linked device, Baileys)
├── imessage-bot/src/              # iMessage bridge (local Mac, imsg CLI)
├── photon-bot/src/                # iMessage bridge (hosted Photon line)
└── website/                       # Astro site: docs, marketplace library, plugin catalog

Key modules

Agent Runner (packages/core/src/agent/)

The agent runner is split into focused modules; runtime behavior is documented in Agent loop and Context management.

ModulePurpose
agent-runner.tsOrchestrator - delegates to executors
types.tsShared types: AgentRunnerOptions, AgentResponse, AgentRunContext
context/summarizer.tsAuto-compaction when context approaches token limit
execution/streaming-executor.tsReal-time LLM streaming with tool calls
execution/batch-executor.tsNon-streaming execution with retry logic

Dependency Injection Pattern: To avoid circular dependencies, the summarizer.ts accepts a RecursiveRunner function parameter instead of importing AgentRunner directly.

Prompt assembly

AgentPromptBuilder assembles system prompts through three semantic layers:

  • Core contains the persona and fixed Jazz harness guidance.
  • Scope contains instructions belonging to the current project or task.
  • Live contains runtime facts that may change while the agent runs, such as its environment.

prompts/layers.ts owns their Core → Scope → Live rendering order. Keep new prompt content in the narrowest-lived layer that accurately describes it. Tool-specific operating details belong in the tool description rather than any shared layer.

Chat Service (packages/cli/src/chat/)

The chat service is split into focused modules:

ModulePurpose
chat-service.tsSession orchestrator
commands/parser.tsParse slash commands from user input
commands/handler.tsRoute commands and shared command behavior
commands/conversation.tsStart, fork, resume and rename conversations
commands/types.tsSpecialCommand, CommandResult types
session/manager.tsSession ID generation, logging
session/agent-setup.tsMCP server connections before chat

Common Conventions

Service Contracts

A service contract is an interface + a Context tag, defined under packages/core/src/interfaces/.

// packages/core/src/interfaces/agent-config.ts
export interface AgentConfigService {
  getConfig(): Effect.Effect<AgentConfig, Error>;
}

export const AgentConfigServiceTag = Context.GenericTag<AgentConfigService>("AgentConfigService");

Using Services in Effect

const config = yield * AgentConfigServiceTag;
const value = yield * config.getConfig();

Providing Layers

Layer.effect(AgentConfigServiceTag, Effect.succeed(new ConfigServiceImpl(...)))

How to Add a New Adapter/Service

  1. Add the contract to packages/core/src/interfaces/ (interface + Tag).
  2. Implement the adapter in packages/adapters/src/ and create a Layer.
  3. Add registration in packages/runtime/src/app-layer.ts by merging the new Layer.
  4. Add tests with a mock Layer.

Testing Patterns

Pure Function Tests

For utilities like parseSpecialCommand or generateSessionId:

import { describe, expect, it } from "bun:test";
import { parseSpecialCommand } from "./parser";

describe("parseSpecialCommand", () => {
  it("should parse /help command", () => {
    const result = parseSpecialCommand("/help");
    expect(result.type).toBe("help");
  });
});

Effect Tests with Mocked Layers

const mockLogger: LoggerService = {
  debug: () => Effect.void,
  info: () => Effect.void,
  // ...
};

const testLayer = Layer.succeed(LoggerServiceTag, mockLogger);

const result = await Effect.runPromise(myEffect.pipe(Effect.provide(testLayer)));

UI Architecture

@jazz/cli projects one semantic presentation document into OpenTUI’s fullscreen interface or the classic Ink interface. Effect services mutate the presentation store; React consumes committed snapshots. Stable source IDs survive streaming, settlement and resume. Geometry, scroll position, prompt continuations and secret drafts remain outside persisted entries.

See Conversation presentation for ownership, source links, and the version 3 history migration. Use the terminal rendering tests to compare the emitted terminal screen with the renderer’s intended buffer.


Why This Structure

  • Separates policy (core) from mechanics (adapters): makes it easy to:
    • Swap LLM providers
    • Substitute storage backends
    • Test core logic with deterministic mocks
  • Good for open-source: Contributors can implement providers/adapters without changing core logic.

Troubleshooting

  • Missing tag at runtime: Ensure the Layer providing that tag is included in createAppLayer.
  • Circular dependency: Use dependency injection (pass functions as parameters) instead of direct imports.
  • Context overflow: The Summarizer automatically compacts context when tokens approach 80% of the limit.

machine-readable: /docs/maintainers/architecture.md · /llms.txt · /llms-full.txt