Skip to main content
Comis is structured as a monorepo with 16 TypeScript packages under packages/. Each package has a focused responsibility and exports a public API through its package entry point. Cross-package consumers use those public exports rather than another package’s internal source paths. All packages use ES modules ("type": "module"), strict TypeScript with composite: true project references, and ES2023 as the compilation target.

Package Overview

Package Roles Explained

@comis/shared is the foundation. It defines the Result<T, E> discriminated union and its utility functions. Every other package depends on shared, but shared depends on nothing. This makes it safe to import anywhere without creating circular dependencies. @comis/core is the heart of the hexagonal architecture. It defines port interfaces in src/ports/, Zod-validated domain types, the TypedEventBus, security primitives, configuration schemas, and plugin infrastructure. The bootstrap() function returns the core AppContainer. @comis/agent is the execution engine. It contains the PiExecutor that orchestrates LLM calls, tool execution, and response generation. It also manages sessions, budgets, circuit breakers, RAG, and context preparation. @comis/channels contains the 11 platform adapters. Each adapter lives in its own directory (e.g., src/telegram/, src/discord/) with a standard file set: adapter, message mapper, media handler, credential validator, media resolver, voice sender, and plugin wrapper. @comis/daemon is the runtime composition root. It wires the agent, channels, gateway, persistence, skills, scheduler, orchestration, logging, and observability packages; it also manages startup, shutdown, and signals. @comis/web is a standalone single-page application built with Lit web components, Vite for bundling, and Tailwind CSS for styling. It communicates with the daemon exclusively through the gateway’s HTTP and WebSocket APIs.
The published umbrella package is named comisai on npm (the directory is packages/comis/). It bundles all @comis/* workspace packages and exposes a comis CLI binary. Subpath imports such as comisai/core or comisai/agent are exposed via the umbrella’s exports map.

Dependency relationships

Package manifests and TypeScript project references are the authoritative dependency graph. At a high level:
  • shared has no Comis runtime dependency; core depends on shared.
  • gateway, channels, scheduler, and observability build on core and shared.
  • memory and infra also use the observability substrate.
  • agent uses core, observability, and scheduler; orchestrator composes agent, channels, and core.
  • skills uses the agent and observability contracts needed by its tools and integrations.
  • daemon is the runtime composition root. cli uses the daemon client surface plus the core, memory, and observability APIs.
  • observability-otel is the opt-in telemetry extension; web is the standalone SPA; comisai is the umbrella package that re-exports the workspace packages.

Package Boundary Rules

These rules are mandatory across the codebase and enforced through code review and architectural convention (see AGENTS.md section 6.2).

No Cross-Package Internal Imports

Import from the package’s public API, never from internal source paths. Each package exports its public surface through dist/index.js. This ensures that internal refactoring within a package never breaks consumers.

Keep contracts inward

Core owns shared contracts and ports; runtime packages provide implementations. Cross-package imports must use declared dependencies and public exports. Cross-package lifecycle signals use TypedEventBus where an event is the appropriate contract.

Inject Logger via Deps Interface

Never import @comis/infra directly in non-infra packages. Pass a logger through the dependency injection pattern so that packages remain decoupled from the logging implementation. This also makes unit testing easier since you can inject a mock logger that captures log calls for assertions.

ES Modules Only

All packages use "type": "module" with .js extensions in import paths. There are no CommonJS modules in the codebase. Import statements use the .js extension even though the source files are .ts:

Build System

TypeScript project references (composite: true) enable incremental builds across the monorepo. Each package has its own tsconfig.json that references its dependencies, so pnpm build compiles all packages in the correct order respecting the reference graph.
Configuration details: To build and test a single package:
To run a single test file from a package directory:
Integration tests in test/integration/ require pnpm build first because they import from dist/, not from source. Run them with pnpm test:integration.

Explore the repository

Each package has a focused responsibility and a public entry point. The architecture page explains how packages connect through ports and adapters, while the event bus documents cross-package lifecycle signals.
To explore the codebase structure, start with packages/core/src/ports/ for all port interface definitions, packages/core/src/domain/ for Zod-validated domain types, and packages/core/src/bootstrap.ts for the composition root.

Architecture

How packages connect through ports and adapters

Event Bus

Cross-package communication via typed events

Contributing

Setup and build instructions

Developer Guide

Back to developer guide overview