Skip to main content
Comis is a TypeScript monorepo built on hexagonal architecture — core domain logic at the center, with port interfaces defining boundaries and adapter packages implementing platform-specific behavior. Every function returns Result<T, E> instead of throwing exceptions, and security rules are enforced at lint time via ESLint. This guide covers everything you need to extend Comis: building custom channel adapters, writing source-integrated lifecycle hooks, creating advanced skills, and orchestrating multi-agent pipelines.

What You Can Build

Custom Channel Adapters

Connect Comis to any chat platform by implementing the ChannelPort interface. Comis ships with 10 adapters (Discord, Telegram, Slack, WhatsApp, Signal, iMessage, LINE, IRC, Echo, Email) and you can add more without modifying core code. Each adapter handles platform-specific messaging, media, reactions, and threading while presenting a unified interface to the rest of the system. The EchoChannelAdapter serves as the simplest reference implementation — an in-memory adapter with zero external dependencies.

Lifecycle Plugins

Hook into lifecycle points to modify behavior or observe application events. The plugin system provides 3 modifying hooks and 6 observational hooks. The public registry exposes registerHook only. Plugins are source-integrated, trusted application code. Comis does not yet provide a stable third-party code-plugin loader, and the plugin registry does not register tools, HTTP routes, or configuration schemas. Those extensions use their own ports and composition wiring.

Custom Skills

Create prompt-based skills with SKILL.md manifests or connect external MCP servers. Skills extend what agents can do — from answering domain-specific questions to executing multi-step workflows. Prompt skills are Markdown files with YAML frontmatter defining the skill’s name, permissions, allowed tools, and input schema. The body contains the prompt template with variable placeholders. For more advanced integrations, MCP (Model Context Protocol) servers expose tools and resources to agents over a standardized protocol.

Execution Pipelines

Orchestrate multi-agent workflows as directed acyclic graphs (DAGs). Define nodes with dependencies, input passing, and barrier modes for parallel agent coordination. Each node in the graph runs an agent with a specific task. Nodes can depend on other nodes, receive output from upstream nodes as input variables, and run in parallel when their dependencies are satisfied. The graph coordinator manages node scheduling, timeout enforcement, and aggregate result collection.

Architecture at a Glance

All domain logic lives in @comis/core. Port interfaces define boundaries — what the system needs. Adapter packages implement those boundaries — how it’s done. The composition root in core/src/bootstrap.ts wires everything together at startup, returning an AppContainer with the fully configured event bus, plugin registry, and hook runner. Domain and service operations return Result<T, E> so callers handle failures explicitly. Narrow boundary wrappers and caught CLI or web entry flows may throw with a local rationale. Core utilities such as ok(), err(), tryCatch(), and fromPromise() from @comis/shared keep the normal path typed and composable across all 16 packages.
The codebase spans 16 focused packages, with port contracts, typed events, and lifecycle hooks providing the main extension boundaries.

Key Patterns

A few patterns appear throughout the codebase and are important to understand before diving in:
  • Result<T, E> — Domain and service control flow uses the Result discriminated union. Use ok() for success and err() for failure; reserve throws for the narrow boundary cases described in the engineering protocol.
  • Factory functions — Prefer createXxx() factory functions returning typed interfaces over direct class instantiation (e.g., createCircuitBreaker() returns CircuitBreaker).
  • TypedEventBus — The inter-module communication layer. Packages subscribe to typed events instead of importing each other directly. The EventMap interface provides compile-time safety for all typed events.
  • AsyncLocalStorage — Request-scoped context is available via runWithContext() and getContext() in core/src/context/, providing trace IDs and agent context without passing them through every function call.

Prerequisites

Before extending Comis, you should be comfortable with:
  • TypeScript — strict mode, generics, discriminated unions, and ES modules
  • Node.js >= 22 — the runtime target for all packages
  • pnpm — the package manager used for the monorepo workspace
Set up the development environment with:
The primary validation command before any commit is pnpm validate (expands to pnpm docs:check && pnpm build:clean && pnpm cycles && pnpm cycles:refs && pnpm lint:security && pnpm test:coverage).

Getting Started

Architecture

Hexagonal pattern, ports, adapters, and the composition root

Packages

All 16 packages with roles and dependency graph

Event Bus

Typed events across 5 domain subsystems (Messaging, Agent, Channel, Infra, Terminal)

Custom Adapters

Step-by-step channel adapter tutorial

Plugins

Source-integrated lifecycle hooks

Custom Skills

Advanced skill development beyond prompt skills

Pipelines

Multi-agent execution graphs (DAGs)

Contributing

Setup, coding standards, and PR process