PluginWorld
Ma

massa-ai

Claude Code✓ SPEC VERIFIED

massa-ai plugin for Claude Code — semantic code search, durable memory, symbol graph, and context compression

@luizgmassa · MIT · updated 3d ago

SECURITY

A

SCORE

73

INSTALLS

12K

PLUG IN

/plugin marketplace add luizgmassa/massa-ai

Then run /plugin install <name> for any plugin it lists

README

massa-ai

massa-ai is a local-first MCP server that indexes your codebase — semantic search, keyword search, and a symbol graph ranked by dependency centrality — and keeps a persistent, cross-session memory of decisions, patterns, and critical facts.

Instead of loading whole files into context, your assistant retrieves just the relevant symbols, references, and memories, so it reads less, forgets nothing between sessions, and costs less to run. It runs on Ollama (free, offline), with optional LLM consolidation, rerank, and query understanding, and plugs into Claude Code, Codex, Cursor, and OpenCode via MCP plus passive-capture hooks.

FEATURES.md contains a complete reference for every feature — what it does, why it exists, and how to use it. This README covers installation, integration, and quick-start; FEATURES.md has the depth.


Quick Start

One-line install (recommended)

curl -fsSL https://raw.githubusercontent.com/luizgmassa/massa-ai/main/install.sh | bash

Installs interactively. Three modes:

Mode Requires Best for
Docker (default) Docker Production, quick start (PostgreSQL via Docker/colima, ~5GB RAM)
Docker build Docker + Git Custom builds, local changes (PostgreSQL via Docker/colima, ~5GB RAM)
Source Git + Bun Development (Native PostgreSQL ~100MB or Docker PostgreSQL)

⚠️ Docker modes run PostgreSQL through Docker/colima and reserve ~5GB RAM. For native PostgreSQL (~100MB, no Docker), use Source mode (./scripts/setup-local-first.sh) and pick Native PostgreSQL at the database prompt.

Non-interactive (CI/scripted):

# Docker mode, custom port, skip start
MASSA_AI_MODE=docker MASSA_AI_API_PORT=4000 MASSA_AI_NO_START=1 \
  curl -fsSL https://raw.githubusercontent.com/luizgmassa/massa-ai/main/install.sh | bash

Manual setup (from source)

# 1. Clone and install
git clone https://github.com/luizgmassa/massa-ai.git
cd massa-ai
bun install

# 2. Setup (100% offline with Ollama)
./scripts/setup-local-first.sh
# - Installs/starts Ollama
# - Pulls qwen3-embedding:4b (embeddings, 2560 dims), qwen2.5:7b-instruct (default LLM),
#   and qwen2.5-coder:7b (code-oriented LLM sites)
# - Creates .env with defaults
# - Runs bun run diagnose to validate the stack

# 3. Build and start
bun run build
bun run start:api

Verify: curl http://localhost:3333/health

Native PostgreSQL (macOS, recommended over Docker)

Instead of Docker (~5GB RAM), run PostgreSQL natively (~100MB):

# setup-local-first.sh option 1 does this automatically, or run standalone:
./scripts/setup-native-postgres.sh      # brew install postgresql@17 + pgvector, create role/db, migrate

# .env then contains:
#   DATABASE_URL=postgresql://massa_ai:massa_ai_password@localhost:5432/massa_ai

Linux/WSL: install postgresql + postgresql-*-pgvector from your distro, create the role/db/extension, then set DATABASE_URL. Or use Docker (option 3, ~5GB RAM).

Tip: Run bun run diagnose at any time to validate Ollama connectivity, database access, embedding generation, and migration status.


Integration

OpenCode (recommended)

The OpenCode plugin's hooks are in-process (no hooks.json to merge): the plugin registers lifecycle handlers (session.created, tool.execute.after, experimental.session.compacting, shell.env, event, dispose) directly, so observations are captured the moment the plugin loads.

Install from source (recommended — same as the other three hosts):

bun run build                             # produces apps/opencode-plugin/dist/index.js
bash apps/opencode-plugin/install.sh --user

That installs ~/.config/opencode/plugins/massa-ai/index.js as a real copy of the repo's dist/index.js (re-run the installer after bun run build to refresh it — a symlink here used to go dead whenever the gitignored dist/ vanished, and OpenCode skips an unresolvable local plugin silently), adds "./plugins/massa-ai/index.js" to the plugin array of opencode.json, and symlinks the 18 specialist agents into ~/.config/opencode/agents/. The plugin is hooks-only (AD-017: plugins deliver, MCP serves tools, hooks observe) — it registers zero in-process tools, so the installer delegates MCP registration to scripts/install-agents.sh --agent opencode, giving you all 59 MCP tools alongside the lifecycle hooks. Uninstalling the plugin (install.sh --uninstall) removes only the plugin file and its config entry; the MCP entry stays (bash scripts/install-agents.sh --agent opencode --uninstall removes it separately).

Install as an npm package (alternative):

npm install @massa-ai/opencode-plugin
# or from source:
bun add @massa-ai/opencode-plugin

Configure ~/.config/opencode/opencode.json:

File: ~/.config/opencode/opencode.json

Via MCP package:

{
  "mcp": {
    "massa-ai": {
      "type": "local",
      "command": [
        "bunx",
        "@massa-ai/mcp-client"
      ],
      "environment": {
        "MASSA_AI_API_URL": "http://localhost:3333"
      },
      "enabled": true
    }
  }
}

Via Plugin:

{
  "plugin": ["@massa-ai/opencode-plugin"]
}

From source (development):

{
  "mcpServers": {
    "massa-ai": {
      "type": "local",
      "command": ["bun", "run", "/path/to/massa-ai/apps/mcp-client/src/index.ts"],
      "enabled": true
    }
  }
}

Events wired (in-process, 6 lifecycle handlers): session.created, tool.execute.after, experimental.session.compacting, shell.env, event, dispose — all registered in-process by the plugin (no external hooks file).

Plugin Bundles (4-Tool Parity)

All four major AI coding tools have native plugin bundles that install skills (slash commands), MCP server config, and passive-capture hooks in one command. Hooks are auto-written (not just printed) using array-append merge with backup + _massaAiOwned marker — user hooks are always preserved.

Tool Install command Events Bundles Trust step?
Claude Code bash apps/claude-plugin/install.sh --user 5 6 slash commands + 18 subagent specialists + hooks into settings.json No
Codex bash apps/codex-plugin/install.sh --user 6 6 skills + 18 subagent specialists (TOML to ~/.codex/agents/) + hooks into hooks.json + MCP into ~/.codex/config.toml Yes — run /hooks in Codex
Cursor bash apps/cursor-plugin/install.sh --user 7 6 skills + hooks into hooks.json + MCP into ~/.cursor/mcp.json + 18 subagent specialists No
OpenCode bash apps/opencode-plugin/install.sh --user 6 (in-process) MCP into opencode.json/opencode.jsonc (59 tools) + lifecycle handlers + 18 subagent specialists (.md to ~/.config/opencode/agents/) No

Each plugin also ships generated slash commands — one per massa-ai workflow (40 today) (/massa-ai:debug, $debug, etc., naming varies by host) — alongside the 6 quick commands in the table above; see Workflow Commands in the feature reference for the full per-host naming table.

Claude Code and Codex additionally support their native plugin managers, which is what makes massa-ai visible in /plugin and /plugins:

Generation prerequisite for a checkout (not a tarball install): the skills/, agents/, and agent-profiles/ trees under every apps/*-plugin/ directory are generated build output, gitignored rather than checked in. Run bun run generate:artifacts once before marketplace add against a checkout — any of the install.sh scripts or install-harness.sh already run it for you, so this only matters if you point the marketplace at a checkout without running an installer first. A published npm/tarball install ships the bundles pre-generated and needs nothing extra.

# Claude Code
/plugin marketplace add ~/Projects/massa-ai
/plugin install massa-ai@massa-ai
# Codex
codex plugin marketplace add ~/Projects/massa-ai
codex plugin add massa-ai@massa-ai

After every git pull, regenerate the bundles a checkout-based marketplace entry serves — bun run generate:artifacts — or the plugin manager keeps serving the pre-pull skills/agents until you do. This is opt-in, not automatic; the repo does not install a hook for you. If you want it automated, copy this into .git/hooks/post-merge (and chmod +x it):

#!/bin/sh
# Optional: keep checkout-based plugin bundles fresh after every pull.
cd "$(git rev-parse --show-toplevel)" && bun run generate:artifacts

Standalone massa-ai-config agents install run against an ungenerated checkout fails (the source agents//agent-profiles/ directories it copies from do not exist yet) until bun run generate:artifacts has run at least once. Tarball installs are unaffected — they ship pre-generated.

On Claude Code the plugin ships its own hooks, so running install.sh after a plugin install skips the hook merge instead of double-firing. On Codex the two are complementary — a Codex plugin manifest cannot carry hooks, so install.sh is still what wires ~/.codex/hooks.json. MCP registration stays with scripts/install-agents.sh on both, the single writer of host MCP config.

All four installers support --user (default, e.g. ~/.claude), --project (e.g. ./.claude), --uninstall (removes only massa-ai-owned entries), and --quiet / --verbose. The OpenCode installer additionally needs apps/opencode-plugin/dist/index.js to exist — run bun run build first; it exits non-zero with that hint otherwise. npm install @massa-ai/opencode-plugin plus "plugin": ["@massa-ai/opencode-plugin"] remains a supported alternative.

Or pick the p option from the root bash install.sh post-install menu, which offers all four plugin choices plus an "All four" shortcut. The k option in the same menu opens the harness sub-menu: skills only, MCP registration only, everything including plugin bundles, or a dry-run preview.

When the plugin bundles are installed through the harness — the root install.sh k) harness menu, scripts/setup-local-first.sh, or bash scripts/install-harness.sh --plugins — the plugin phase detects which hosts are present (the host's config dir exists, or its binary is on PATH) and installs only those; absent hosts produce one skip log line and no filesystem writes. Every successful plugin install records the bundle version in ~/.config/massa-ai/install-state.json: a re-run at the same version is a no-op, an older recorded version upgrades automatically, and a newer recorded version is never downgraded. --dry-run reports the per-host decision (install / upgrade / skip-current / skip-absent) without writing anything. Direct per-host installs (apps/<host>-plugin/install.sh --user) behave exactly as before, plus the same version recording.

Shared binary: Claude Code, Codex, and Cursor all use the same massa-ai-hook.ts Bun binary from apps/claude-plugin/hooks/. Codex and Cursor symlink to it. OpenCode uses in-process handlers (no external hooks file).

MCP has exactly one writer. scripts/install-agents.sh owns every host's MCP config; the plugin installers call it for you (--agent claude-code / codex / cursor). Nothing else writes an MCP entry, so installing a plugin and running the installer directly cannot double-register. MCP is always registered at user scope, even for a --project plugin install.

OpenCode is no longer an exception (AD-017): install-agents.sh always writes its MCP entry, regardless of which form opencode.json lists the plugin in — the npm package name (@massa-ai/opencode-plugin), the local path (./plugins/massa-ai/index.js), or the bare dir name (massa-ai). The OpenCode plugin is hooks-only and registers zero in-process tools; its installer delegates MCP registration to install-agents.sh --agent opencode on every install (mirroring the Codex delegation pattern), so OpenCode users get all 59 MCP tools rather than a 14-tool in-process subset. Uninstalling the plugin does not remove the MCP entry — plugin lifecycle and MCP tool-surface lifecycle are independent; remove the entry with bash scripts/install-agents.sh --agent opencode --uninstall if wanted.

18 subagent specialists: all four plugins ship the 18 massa-ai sub-agent specialists (investigator, planner, builder, reviewer, context-curator, verification-agent, requirements-analyst, architecture-specialist, test-engineer, documentation-agent, audit-specialist, mobile-specialist, designer, plan-critic, furps-analyst, navigator, meta-judge, judge) as host-native subagent definitions, registered under the prefixed names massa-ai-<role>. Model + effort are pinned per host: Claude effort: high + aliases (haiku/sonnet/opus); Codex model_reasoning_effort = "high" + IDs (gpt-5.4-mini/gpt-5.6-terra/gpt-5.6-sol); Cursor/OpenCode reasoningEffort: max + charter model hints (DeepSeek V4 Pro / GLM-5.2 / MiniMax M3 / kimi-k3). See FEATURES.md → Subagent Skills (18 Specialists) for the full per-agent model/effort/permission tables, file locations, and the generator + parity-test contract.

Cursor advanced (extension authors): register the plugin directory programmatically via vscode.cursor.plugins.registerPath("/abs/path/to/apps/cursor-plugin").

See §Passive Capture (Hooks) for the full event tables and FEATURES.md for details.


Skills & Install System

The repo ships a set of repo-local skills plus a unified installer that symlinks them into each tool's config directory. The per-plugin installers (above) handle hooks + MCP + subagent specialists; this installer handles skills and the bootstrap contract.

Included skills

Skill Location Purpose
massa-ai skills/massa-ai/ Workflow router (spec-driven, debug, feature, refactor, audits, ADR/RFC/TDD, etc.)
persona-router skills/persona-router/ Automatic persona selection from catalog (skills/massa-ai/personas/)

Unified skills installer

Symlinks all skills/*/SKILL.md into each detected tool's config dir and writes the bootstrap contract block into the tool's AGENTS.md. Symlink-based — updates to the repo are immediately reflected without re-running.

# Install skills for all detected tools
bash scripts/install-skills.sh --apply --platform all --yes

# Install for one platform
bash scripts/install-skills.sh --apply --platform claude --yes

# Preview changes (write nothing)
bash scripts/install-skills.sh --dry-run --platform all

# Check for drift (exit 1 if symlinks missing or pointing wrong)
bash scripts/install-skills.sh --check --platform all

# Uninstall (remove only massa-ai-owned symlinks + bootstrap block)
bash scripts/install-skills.sh --uninstall --platform all --yes

State: ~/.config/massa-ai/install-state.json (v2 format; v1 auto-migrates).

Safety: aborts on non-symlink conflict (won't overwrite user files); --dry-run and --check write nothing; requires --yes for real $HOME.

MCP registration

scripts/install-agents.sh is the single writer of host MCP config. It merges the massa-ai server entry into each host's own config shape, preserving every existing user key and taking a <config>.massa-ai.bak-<ts> backup first.

bash scripts/install-agents.sh --yes                 # every applicable host
bash scripts/install-agents.sh --agent codex --yes   # one host
bash scripts/install-agents.sh --dry-run             # plan only, writes nothing
bash scripts/install-agents.sh --uninstall --yes     # remove only owned entries
Agent Config file Shape
claude-code ~/.claude.json mcpServers, type: "stdio", string command + args, env, npx
claude-desktop ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) mcpServers, env, npx
codex ~/.codex/config.toml [mcp_servers.massa-ai] (comments and user tables preserved)
cursor ~/.cursor/mcp.json mcpServers, env, npx
opencode ~/.config/opencode/opencode.json mcp, environment, bunx

Uninstall removes only entries carrying _massaAiOwned: true — a massa-ai entry you wrote by hand is left alone.

Which command gets registered — --mcp-source. The MCP entry can point at either a local checkout or the published npm package:

--mcp-source Command written Chosen by
local bun run <repo>/apps/mcp-client/src/index.ts scripts/setup-local-first.sh
npx npx -y -p @massa-ai/mcp-client massa-ai (OpenCode: bunx -p …) the root install.sh
auto (default) local when apps/mcp-client/src/index.ts exists, else npx direct invocation

MASSA_AI_MCP_SOURCE sets it via the environment; the flag wins over the env var. Switching sources rewrites the entry in place — you never end up with two.

Prefer local when you have a clone. The npx path resolves @massa-ai/core, which compiles native tree-sitter grammars on first run, and an MCP host will time out during the handshake long before that finishes. Note also that -p is required in both launchers: the package's bin is named massa-ai, not mcp-client, so a bare npx @massa-ai/mcp-client fails with "could not determine executable to run". bunx takes -p but has no -y.

Claude Code reads MCP definitions from ~/.claude.json, not from ~/.claude/settings.json (that file holds approval controls — allowedMcpServers, enabledMcpjsonServers, disabledMcpServers — plus hooks). Installs before this release wrote to settings.json, where Claude Code ignored them; the installer now migrates that stale owned entry away on the next apply.

One-shot harness install

scripts/install-harness.sh runs skills, MCP registration, and the plugin bundles in one pass. Both install.sh (menu option k) and scripts/setup-local-first.sh (step 6/6) call it.

bash scripts/install-harness.sh --all --yes           # skills + MCP + all four plugin bundles
bash scripts/install-harness.sh --skills --agents     # skip the plugin bundles
bash scripts/install-harness.sh --all --dry-run       # preview everything (forces --verbose)
bash scripts/install-harness.sh --all --yes --verbose # per-file / per-key detail

Output is quiet by default — one line per changed thing plus a summary. --verbose (or MASSA_AI_VERBOSE=1) adds the per-file and per-config-key detail; --dry-run and --check force it on. Errors and warnings are never suppressed. The banner prints at most once per process tree, so nesting the plugin installers under the harness no longer repeats it four times.

Plugin bundles are installed by default. Set MASSA_AI_INSTALL_PLUGINS=0 to make scripts/setup-local-first.sh step 6 fall back to --skills --agents.

Exit codes: 0 when every requested step completed, otherwise the first failing sub-installer's code (13 = consent gate refused).

See FEATURES.md → Skills & Install System for the full reference.

Workflow guides

Migrated documentation for massa-ai workflows lives in docs/:

Guide File
Spec-Driven docs/massa-ai-spec-driven.md
TDD docs/massa-ai-tdd.md
RFC docs/massa-ai-rfc.md
Commit docs/massa-ai-commit.md
Ticket docs/massa-ai-ticket.md
Maestro docs/massa-ai-maestro.md
Mobile Figma docs/massa-ai-mobile-figma.md
Context Slices docs/context-slices.md
Onboarding (generated) docs/ONBOARDING.md — see Understanding the codebase
{
  "mcpServers": {
    "massa-ai": {
      "type": "local",
      "command": ["docker", "compose", "run", "--rm", "-i", "mcp"],
      "enabled": true
    }
  }
}

Step-by-step: the memory lifecycle

bootstrap → capture → recall/search → handoff → proposals → checkpoint

1. Bootstrap a project

Seed initial context (git log, README, docs, top central files) so an agent starts with usable memories instead of a cold start. Idempotent — re-running is a no-op unless force: true. Degrades silently to rule-based seeds when the LLM is off.

# MCP
bootstrap { projectId: "my-app", projectPath: "/abs/path" }
# REST
curl -X POST http://localhost:3333/api/v1/bootstrap \
  -H "Content-Type: application/json" \
  -d '{"projectId":"my-app","projectPath":"/abs/path"}'

2. Enable passive capture

Install hooks for your tool (see §Passive Capture (Hooks)) so observations are streamed to /api/v1/hook. All four tools (Claude Code, Codex, Cursor, OpenCode) have plugin installers that auto-write hooks. Non-hook hosts use hook_ingest or POST /api/v1/hook/batch.

# Observations persist in PostgreSQL and are consolidated into memories only
# when MASSA_AI_LLM_ENABLED=true (else stored raw, bridge skips silently).

3. Work — recall and search

# Semantic memory search
recall { query: "how does auth work", projectId: "my-app" }
# Code search (enriched = full content + imports + parent symbol in one call)
search { query: "token validation", projectId: "my-app", responseMode: "enriched" }

Quality knobs (default OFF, opt-in via env): queryUnderstanding (LLM rewrite + HyDE) and rerank (LLM-judge). See FEATURES.md and FEATURES.md.

4. Hand off to the next session

Session A leaves a structured record; session B discovers and accepts it.

# Session A — leave the handoff
handoff_begin {
  projectId: "my-app",
  summary: "Auth refactor in progress; token rotation unfinished",
  nextSteps: ["finish rotateToken in auth.ts", "add tests"],
  files: ["src/auth.ts", "src/token.ts"]
}
# Session B — discover and accept
handoff_list_pending { projectId: "my-app" }
handoff_accept { id: "<handoff-id>" }

5. Review auto-improvement proposals

The auto-improve loop detects recurring patterns (repeated queries, hot files, common fixes) and generates memory-edit proposals. With AUTO_IMPROVE_REVIEW_GATE=false (the default) proposals are auto-approved; set it true to surface them for review.

list_proposals   { projectId: "my-app" }
approve_proposal { id: "<proposal-id>" }
reject_proposal  { id: "<proposal-id>", reason: "stale" }

6. Snapshot and restore

Save task progress (status, steps, file changes, decisions, next action) and resume later — across a restart, a context compaction, or a new agent.

create_checkpoint {
  taskId: "auth-refactor",
  description: "Token rotation mid-flight",
  progressPercent: 60,
  nextAction: "finish rotateToken in src/auth.ts"
}
list_checkpoints   { taskId: "auth-refactor" }
restore_checkpoint { checkpointId: "<cp-id>" }

Available Tools

59 tools total, grouped by category: Indexing & Search, Symbol Graph, Code Execution (Sandbox), Memory & Lifecycle, Synapse (Cognitive Layer), Passive Capture, Project Bootstrap, Cross-session Handoffs, Auto-improvement (Proposals), and Checkpoints.

The current roster fits in one MCP tools/list page (pagination via nextCursor activates over 100 tools).

See FEATURES.md for the complete tool roster with required/optional params for every tool.

Workflow integration

The massa-ai workflow skill (skills/massa-ai/) references all 59 tools by their canonical un-prefixed names. Each workflow adopts the tools that materially benefit its flow — e.g. spec-driven and long-session use checkpoints for task save/resume; debug uses trace_path for call-path tracing and execute_file for large-file analysis; architecture-audit uses impact_analysis and get_architecture; onboarding uses bootstrap. See FEATURES.md → Workflow Tools (52-Tool Adoption) for the full tool-to-workflow adoption map.


Local-first LLM (Ollama)

All LLM-driven features run against a local Ollama instance and default OFF, degrading silently to rule-based behavior when disabled. Everything still works without an LLM — you just lose consolidation, polish, rerank, and query rewrite.

Prerequisites

# Install Ollama (if missing)
curl -fsSL https://ollama.com/install.sh | sh

# Start the daemon
ollama serve

# Pull models
ollama pull qwen3-embedding:4b    # embeddings (2560 dims)
ollama pull qwen2.5:7b-instruct   # default LLM (consolidation, salience, handoff, query rewrite, HyDE)
ollama pull qwen2.5-coder:7b      # code-oriented LLM sites (bootstrap seed, reranker, code compression)

Validate the stack

bun run diagnose (also auto-runs as predev / predev:api / predev:mcp) checks Ollama connectivity, database access, embedding generation, and migration status.

Ran without a reachable embedding provider before? Re-index. Earlier versions silently substituted random vectors when no provider was available, and stored and searched them as if they were real — so retrieval returned plausible-looking nonsense with no error anywhere. That fallback is gone: embedding now fails loudly instead.

There is no detector and no repair. A 384-dimension random vector is indistinguishable from a genuine embedding after the fact, so the only fix is to overwrite the affected rows:

# per affected project
curl -X POST http://localhost:3333/api/v1/project/reindex \
  -H "x-api-key: $MASSA_AI_API_KEY" -H "Content-Type: application/json" \
  -d '{"projectId": "my-project", "force": true}'

Memories stored during such a window are not covered by re-indexing — their embeddings were written at store_memory time — and need to be re-created.

Turn the LLM features on

# .env  — all LLM-gated features default OFF; flip one switch to enable them all
MASSA_AI_LLM_ENABLED=true
MASSA_AI_LLM_BASE_URL=http://localhost:11434/v1
MASSA_AI_LLM_API_KEY=ollama
MASSA_AI_LLM_MODEL=qwen2.5:7b-instruct        # default instruct model (NL-judgment sites)
MASSA_AI_LLM_CODE_MODEL=qwen2.5-coder:7b      # code-oriented sites (bootstrap seed, reranker, compress)
# MASSA_AI_LLM_DISABLE_THINK=true             # best-effort thinking-disable (default true; safety net)

With MASSA_AI_LLM_ENABLED=true you get: hook→memory consolidation, handoff-summary polish, query understanding (rewrite + HyDE), LLM-judge rerank, and auto importance scoring. Set it false (the default) and every one of those silently falls back to its rule-based path.

Per-task model routing (new 2026-07-09): the 11 LLM call sites split by task shape. The 8 NL-judgment sites (salience judge, consolidator, observation/auto-improve jobs, handoff summary, query rewrite, HyDE) use MASSA_AI_LLM_MODEL; the 3 code-oriented sites (bootstrap SeedMemoriesSchema, reranker, code-compressor) use MASSA_AI_LLM_CODE_MODEL. Routing is per-call via a modelRole option in packages/core/src/services/memory/llm-client.ts. Both default to non-thinking instruct models so structured-output calls return fast (~5 s) and reliably, instead of burning a 90 s wall-clock timeout on a thinking model (the prior default routed answers into the reasoning channel and silently degraded). Override either with the env vars above.

Embeddings note: The config default embedding model is qwen3-embedding:4b (2560d — see massa-ai-config.ts). It balances recall against on-device speed better than nomic-embed-text (768d) or bge-m3 (1024d), but it is slower: bulk indexing a large corpus takes minutes. Override via OLLAMA_EMBEDDING_MODEL or config embedding.model, and move embedding.dimensions with it — a width that disagrees with what the model returns fails loudly rather than degrading. Switch to bge-m3 for speed if its recall quality is sufficient.


Passive Capture (Hooks)

Passive capture streams agent lifecycle events into massa-ai as Observations, so the agent's behaviour is recorded without any change to how you prompt.

  • Fire-and-forget: each hook POSTs to the endpoint with a 2s timeout and always exit 0. The agent is never blocked, even if the API is down.
  • No stdout: scripts produce no output.
  • Empty stdin is a no-op: if a hook fires with no payload, the script exits without posting (the API requires a non-empty payload object).

Install hooks for your tool

All four tools have plugin installers that auto-write the hooks config (not just print it) using array-append merge with backup + _massaAiOwned marker, so user hooks are always preserved:

bash apps/claude-plugin/install.sh --user   # 5 events → ~/.claude/settings.json
bash apps/codex-plugin/install.sh --user    # 6 events → ~/.codex/hooks.json
bash apps/cursor-plugin/install.sh --user   # 7 events → ~/.cursor/hooks.json

OpenCode has in-process hooks — nothing to merge into a hooks.json:

bash apps/opencode-plugin/install.sh --user   # 6 in-process lifecycle handlers

Or pick the p option from the root bash install.sh post-install menu, which offers all four plugin choices plus an "All four" shortcut. The k option in the same menu opens the harness sub-menu (skills only, MCP only, everything including plugin bundles, or a dry-run preview). See §Integration for per-plugin details.

What each hook captures

Six lifecycle event kinds are recognised: session-start, user-prompt, pre-tool-use, post-tool-use, pre-compact, session-end. The pre-compact hook does a dual-POST (observation + snapshot to /api/v1/hook/compact-snapshot) to build a bounded, reference-based table-of-contents of the session's observations — zero loss across /compact.

Events wired per tool

Claude Code (5 events) — wired by apps/claude-plugin/install.sh into settings.json (nested matcher-group + hooks[] form):

Claude event Binary subcommand Observation source
SessionStart session-start session-start
UserPromptSubmit user-prompt-submit user-prompt
PostToolUse post-tool-use post-tool-use
PreCompact pre-compact pre-compact
Stop stop session-end

Codex (6 events) — wired by apps/codex-plugin/install.sh into ~/.codex/hooks.json:

Codex event Binary subcommand Observation source
SessionStart session-start session-start
UserPromptSubmit user-prompt-submit user-prompt
PreToolUse pre-tool-use pre-tool-use
PostToolUse post-tool-use post-tool-use
PreCompact pre-compact pre-compact
Stop stop session-end

Trust step (Codex only): after install, run /hooks in Codex to trust massa-ai hooks — Codex skips non-managed plugin hooks until trusted.

Cursor (7 events) — wired by apps/cursor-plugin/install.sh into ~/.cursor/hooks.json:

Cursor event Binary subcommand Observation source
sessionStart session-start session-start
sessionEnd stop session-end
beforeSubmitPrompt user-prompt-submit user-prompt
preToolUse pre-tool-use pre-tool-use
postToolUse post-tool-use post-tool-use
preCompact pre-compact pre-compact
stop stop session-end

OpenCode (in-process, 6 lifecycle handlers) — registered by the plugin itself (local install or @massa-ai/opencode-plugin), no external hooks file: session.created, tool.execute.after, experimental.session.compacting, shell.env, event, dispose.

Env

Variable Default Notes
MASSA_AI_API_BASE http://localhost:3333 Tools API base URL
MASSA_AI_API_KEY auto-provisioned Required by every route except /health, /swagger and /ui. Generated on first API start and saved to security.apiKey in ~/.config/massa-ai/config.json; set this only to pin a specific value. See docs/web-ui-access.md
MASSA_AI_PROJECT_ID cwd basename Project the observations attach to

Non-Claude hosts

Use the MCP tool hook_ingest, or POST directly to /api/v1/hook/batch with { events: [...] } — useful for Docker deployments where the repo (and hook scripts) aren't on the host filesystem.

For a complete feature reference

See FEATURES.md for every feature, what it does, why it exists, and how to use it.


Web UI

Read-only browser for memories, search, handoffs, and checkpoints, served by the Tools API.

  • URL: http://localhost:3333/ui
  • Run: bun run dev:api (the UI is served by the API — there is no separate UI dev script). See §Scripts.
  • Disable: set WEB_UI_ENABLED=false (the /ui prefix then returns 404).
bun run dev:api
# then open http://localhost:3333/ui

The dev:ui script was removed — @massa-ai/ui-client (its target) did not exist. The web UI is served exclusively via dev:api at /ui.


Search Quality Tuning

Environment variables for fine-tuning retrieval (all optional). LLM-gated knobs (SEARCH_QUERY_UNDERSTANDING_*, SEARCH_RERANK_*) default OFF and require MASSA_AI_LLM_ENABLED=true.

See FEATURES.md for the full table of search quality variables and defaults.


REST API

# Development
bun run dev:api

# Production
bun run start:api

Swagger docs: http://localhost:3333/swagger · Web UI: http://localhost:3333/ui

Authentication (required)

Every route except /health, /swagger, /swagger/json and /ui requires an x-api-key header. There is no supported way to run the API unauthenticated — it binds 0.0.0.0, and the executor routes run commands.

You do not have to create the key. On first start the API generates one and saves it to security.apiKey in ~/.config/massa-ai/config.json, logging the path and never the value. It refuses to start only when no key exists and that file cannot be written. Set MASSA_AI_API_KEY only to pin a specific value; env wins over config.json.

# Read the provisioned key
grep -A2 '"security"' ~/.config/massa-ai/config.json

curl -X POST http://localhost:3333/api/v1/search/project \
  -H "x-api-key: $MASSA_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "auth", "projectId": "my-project"}'

The Web UI needs no configuration on a normal local install: /ui is served with the key embedded for loopback callers only. A browser reaching it from anywhere else gets a configure-access screen instead — see docs/web-ui-access.md, which also covers the Docker case and the reverse-proxy limitation.

Cross-origin browser access is closed by default. Add exact origins through MASSA_AI_API_CORS_ORIGINS (comma-separated) when you need it; * together with credentials is rejected at startup rather than served.

Endpoints

The examples below omit -H "x-api-key: …" for brevity. Every one of them requires it.

# Index a project
curl -X POST http://localhost:3333/api/v1/project/index \
  -H "Content-Type: application/json" \
  -d '{"projectPath": "/home/user/my-project", "projectId": "my-project"}'

# Search
curl -X POST http://localhost:3333/api/v1/search/project \
  -H "Content-Type: application/json" \
  -d '{"query": "authentication", "projectId": "my-project"}'

# Store memory
curl -X POST http://localhost:3333/api/v1/memory/store \
  -H "Content-Type: application/json" \
  -d '{"content": "Important decision...", "type": "decision"}'

# Update a memory (re-embeds on content change)
curl -X POST http://localhost:3333/api/v1/memory/update \
  -H "Content-Type: application/json" \
  -d '{"id": "<memory-id>", "content": "Updated...", "mergeTags": true}'

# List memories — level filter: 1=Project, 2=User, 3=Session
curl -X POST http://localhost:3333/api/v1/memory/list \
  -H "Content-Type: application/json" \
  -d '{"projectId": "my-project", "level": 1}'

# Compress context
curl -X POST http://localhost:3333/api/v1/context/compress \
  -H "Content-Type: application/json" \
  -d '{"content": "...", "strategy": "code_structure"}'

# Bootstrap a project (seed memories)
curl -X POST http://localhost:3333/api/v1/bootstrap \
  -H "Content-Type: application/json" \
  -d '{"projectId": "my-project", "projectPath": "/abs/path"}'

# Passive capture — batch lifecycle events
curl -X POST http://localhost:3333/api/v1/hook/batch \
  -H "Content-Type: application/json" \
  -d '{"events": [{"event": "user-prompt", "projectId": "my-project", "payload": {"prompt": "..."}}]}'

# Begin a cross-session handoff
curl -X POST http://localhost:3333/api/v1/handoff/begin \
  -H "Content-Type: application/json" \
  -d '{"projectId": "my-project", "summary": "WIP", "nextSteps": ["..."]}'

# Create a checkpoint
curl -X POST http://localhost:3333/api/v1/checkpoints/create \
  -H "Content-Type: application/json" \
  -d '{"taskId": "auth-refactor", "description": "mid-flight"}'

# List auto-improvement proposals
curl -X POST http://localhost:3333/api/v1/proposal/list \
  -H "Content-Type: application/json" \
  -d '{"projectId": "my-project"}'

Configuration

Config file: ~/.config/massa-ai/config.json (auto-created on first run). The canonical annotated reference for every environment variable is .env.example — mirror it into .env and edit there.

See FEATURES.md for the complete environment variable table, search quality tuning, operational knobs, embedding providers, and config CLI commands.


Scripts

Command Description
bun run build Build all packages
bun run dev Development (all apps)
bun run dev:api REST API with hot reload (also serves the Web UI at /ui)
bun run dev:mcp MCP server with watch
bun run start:api Start REST API
bun run start:mcp Start MCP server
bun run test Run tests
bun run lint Lint code (oxlint, correctness rules — CI-enforced)
bun run lint:fix Apply oxlint's safe auto-fixes
bun run type-check Type checking
bun run diagnose Validate full stack (Ollama, database, embeddings)
bun run bench:fixture Run the massa-ai retrieval fixture benchmark

dev:ui was removed. Its target (@massa-ai/ui-client) did not exist. The Web UI is served by the Tools API at http://localhost:3333/ui — run it with bun run dev:api.


Architecture

massa-ai/ ├── apps/ │ ├── mcp-client/ # MCP Server (stdio) — 59 tools │ ├── tools-api/ # REST API (port 3333) + Web UI at /ui │ ├── web-ui/ # Read-only memory/search/handoff/checkpoint browser │ ├── claude-plugin/ # Claude Code plugin (slash commands + subagent + hooks) │ ├── codex-plugin/ # Codex plugin (skills + hooks + MCP) │ ├── cursor-plugin/ # Cursor plugin (skills + hooks + MCP + agent) │ └── opencode-plugin/ # OpenCode plugin (npm package, in-process hooks) ├── packages/ │ ├── core/ # Business logic, search, embeddings, compression │ └── shared/ # Shared types, config loader, utilities └── scripts/

Component Description
Semantic Search Hybrid vector + keyword with RRF ranking, enriched response mode
Synapse Post-retrieval cognitive modulation: task alignment, agent affinity, working-memory buffer
Symbol Graph PageRank-based centrality, definitions, references, go-to-definition
Embeddings Ollama (local) or Mistral/OpenAI API
Compression Rule-based code structure extraction (target 70% reduction)
Memory Persistent PostgreSQL/pgvector storage across sessions
Cache Multi-level L1/L2 with TTL
Passive Capture Fire-and-forget hooks (Claude Code, Codex, Cursor, OpenCode) → Observations → LLM bridge → memories
Bootstrap Repo scan (git log/README/docs/centrality) → idempotent seed memories
Handoffs Cross-session structured records (summary/next-steps/files), dual-written as searchable memories
Auto-improvement Rule-based pattern detection → proposals (auto-approve or review-gated)
Web UI Read-only browser served by the Tools API at /ui

Understanding the codebase

New to the repo? docs/ONBOARDING.md is a generated guide to the runtime architecture: the nine layers, the seven recurring patterns (two-transports-one-contract, LLM-off-by-default, get*()/reset*() factories, blue-green symbol-graph generations), a 13-step guided tour, a per-layer file map, and the complexity hotspots worth approaching carefully.

It is generated from a knowledge graph of the source tree — 1847 nodes, 4226 edges — built with the Understand-Anything plugin. The graph itself lives in .ua/knowledge-graph.json and is pinned to the commit it was built from, so it goes stale as main moves. Regenerate both when it drifts:

/understand              # rebuild .ua/knowledge-graph.json (scoped by .ua/.understandignore)
/understand-dashboard    # interactive graph explorer on 127.0.0.1:5173
/understand-onboard      # regenerate docs/ONBOARDING.md from the graph

Two caveats worth knowing before you trust it:

  • The graph covers src/ trees only — 733 of 2094 tracked files. scripts/, prisma/, skills/, .specs/, benchmarks/, and the four generated apps/*-plugin/ duplicate trees are excluded by .ua/.understandignore. So ONBOARDING.md describes the runtime architecture completely, but says nothing about the release chain, the agent-harness surface, or the Prisma schema. It names those gaps and points at CLAUDE.md and CONTRIBUTING.md for each.
  • Barrel index.ts files show as graph orphans. A re-export (export * from) is not a value import, so static analysis does not trace through it. That isolation is an artifact, not dead code.

ONBOARDING.md is a convenience, not a source of truth. CLAUDE.md, CONTRIBUTING.md, and .specs/ remain canonical.


Structural indexing (polyglot native Tree-sitter)

massa-ai indexes code with pinned native Tree-sitter grammars across all 33 canonical source extensions, producing a versioned symbol/edge graph ranked by dependency centrality. The native runtime is correct and verified; no WASM or runtime/post-install download is used.

Native target: macOS arm64 and Linux glibc x64. Application runtime is Bun 1.3.14; Node 25.9.0 (npm 11.14.1) is a build-only node-gyp helper.

Performance status: Correct and verified. The perf contract (MLTS-022) was reframed (spec-owner approved, 2026-07-17): the hard gate is the 16 MiB disposal-stress native-retention test (PASS); throughput/RSS are an absolute self-baseline, not a regex-relative threshold.

See FEATURES.md for supported languages, capability tiers, graph schema, FQN identities, embedded parsing, verification commands, and performance details.


Releases

Releases are automatic. Merging a PR into main with a green CI run cuts the next version, tags it, publishes a GitHub Release, and pushes the packages to both registries — there is no manual release step.

The version comes from the [Unreleased] section of CHANGELOG.md: feature-shaped entries cut a minor, bug-shaped entries cut a patch, and majors are only ever bumped by hand. Contributors — the exact heading-to-bump mapping lives in CONTRIBUTING.md, and the heading you write decides the released version.

Installing

The same build ships to two registries under two scopes.

npmjs.org — the public packages, no configuration needed:

npm install @massa-ai/core

GitHub Packages — mirrored under @luizgmassa, because GitHub Packages requires the npm scope to equal the repository owner. Add this to .npmrc first:

@luizgmassa:registry=https://npm.pkg.github.com
npm install @luizgmassa/core

Both carry identical code at identical versions; only the scope differs.


Credits

massa-ai builds on ideas and inspiration from these open-source projects:

License

MIT

SIMILAR PLUGINS