Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/hooks/dist/heartbeat.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { spawnSync } from "child_process";
import { join } from "path";
function getPgConnectionString() {
return process.env.OPC_POSTGRES_URL || "postgresql://opc:opc_dev_password@localhost:5432/opc";
return process.env.OPC_POSTGRES_URL || "process.env.DATABASE_URL || "postgresql://claude:claude_dev@localhost:5432/continuous_claude"";
}
function runPgQuery(pythonCode, args = []) {
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
Expand Down
16 changes: 8 additions & 8 deletions .claude/hooks/dist/shared/db-utils-pg.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export { SAFE_ID_PATTERN, isValidId } from './pattern-router.js';
*/
export function getPgConnectionString() {
return process.env.OPC_POSTGRES_URL ||
'postgresql://opc:opc_dev_password@localhost:5432/opc';
process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude';
}
/**
* Execute a PostgreSQL query via coordination_pg.py.
Expand Down Expand Up @@ -148,7 +148,7 @@ import os

pipeline_id = sys.argv[1]
current_stage = int(sys.argv[2])
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -329,7 +329,7 @@ from datetime import datetime
session_id = sys.argv[1]
project = sys.argv[2]
working_on = sys.argv[3] if len(sys.argv) > 3 else ''
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -383,7 +383,7 @@ import json
from datetime import datetime, timedelta

project_filter = sys.argv[1] if len(sys.argv) > 1 and sys.argv[1] != 'null' else None
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -456,7 +456,7 @@ import json
file_path = sys.argv[1]
project = sys.argv[2]
my_session_id = sys.argv[3]
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -516,7 +516,7 @@ import os
file_path = sys.argv[1]
project = sys.argv[2]
session_id = sys.argv[3]
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -558,7 +558,7 @@ session_id = sys.argv[1]
topic = sys.argv[2]
finding = sys.argv[3]
relevant_to = json.loads(sys.argv[4]) if len(sys.argv) > 4 else []
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down Expand Up @@ -609,7 +609,7 @@ import json
query = sys.argv[1]
exclude_session = sys.argv[2]
limit = int(sys.argv[3])
pg_url = os.environ.get('OPC_POSTGRES_URL', 'postgresql://opc:opc_dev_password@localhost:5432/opc')
pg_url = os.environ.get('OPC_POSTGRES_URL', process.env.DATABASE_URL || 'postgresql://claude:claude_dev@localhost:5432/continuous_claude')

async def main():
conn = await asyncpg.connect(pg_url)
Expand Down
2 changes: 1 addition & 1 deletion .claude/hooks/dist/subagent-start.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function isValidId(id) {
import { spawnSync } from "child_process";
import { join } from "path";
function getPgConnectionString() {
return process.env.OPC_POSTGRES_URL || "postgresql://opc:opc_dev_password@localhost:5432/opc";
return process.env.OPC_POSTGRES_URL || "process.env.DATABASE_URL || "postgresql://claude:claude_dev@localhost:5432/continuous_claude"";
}
function runPgQuery(pythonCode, args = []) {
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
Expand Down
2 changes: 1 addition & 1 deletion .claude/hooks/dist/subagent-stop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function isValidId(id) {
import { spawnSync } from "child_process";
import { join } from "path";
function getPgConnectionString() {
return process.env.OPC_POSTGRES_URL || "postgresql://opc:opc_dev_password@localhost:5432/opc";
return process.env.OPC_POSTGRES_URL || "process.env.DATABASE_URL || "postgresql://claude:claude_dev@localhost:5432/continuous_claude"";
}
function runPgQuery(pythonCode, args = []) {
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
Expand Down
2 changes: 1 addition & 1 deletion .claude/hooks/dist/working-on-sync.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { readFileSync } from "fs";
import { spawnSync } from "child_process";
import { join } from "path";
function getPgConnectionString() {
return process.env.OPC_POSTGRES_URL || "postgresql://opc:opc_dev_password@localhost:5432/opc";
return process.env.OPC_POSTGRES_URL || "process.env.DATABASE_URL || "postgresql://claude:claude_dev@localhost:5432/continuous_claude"";
}
function runPgQuery(pythonCode, args = []) {
const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
Expand Down
64 changes: 0 additions & 64 deletions .claude/runtime/mcp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,70 +147,6 @@ def _unwrap_mcp_response(result: Any) -> Any:
]


# ===========================================================================
# Config Loading Helpers - Reduces complexity in initialize
# ===========================================================================

async def _load_config_from_path(config_path: str) -> McpConfig:
"""Load MCP config from explicit path.

Args:
config_path: Path to config file

Returns:
Loaded McpConfig

Raises:
ConfigurationError: If load fails
"""
from pathlib import Path
path = Path(config_path)
if not path.exists():
raise ConfigurationError(f"Config file not found: {config_path}")
try:
async with aiofiles.open(path) as f:
content = await f.read()
return McpConfig.model_validate_json(content)
except json.JSONDecodeError as e:
raise ConfigurationError(f"Invalid JSON in config file {config_path}: {e}")
except Exception as e:
raise ConfigurationError(f"Failed to load config from {config_path}: {e}")


def _find_project_config() -> Path | None:
"""Find project config file (.mcp.json or mcp_config.json).

Returns:
Path to project config if found, None otherwise
"""
project_root = find_project_root(Path.cwd())
mcp_json = project_root / ".mcp.json"
mcp_config_json = project_root / "mcp_config.json"

if mcp_json.exists():
return mcp_json
elif mcp_config_json.exists():
return mcp_config_json
return None


def _merge_configs(global_cfg: McpConfig | None, project_cfg: McpConfig | None) -> McpConfig | None:
"""Merge global and project configs.

Project config takes precedence for servers with same name.

Args:
global_cfg: Global config (may be None)
project_cfg: Project config (may be None)

Returns:
Merged config or None if both are None
"""
if global_cfg and project_cfg:
return global_cfg.merge(project_cfg)
return project_cfg or global_cfg


class ConnectionState(Enum):
"""Explicit states for the MCP Client Manager lifecycle.

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ cd opc && docker compose up -d

```bash
# Database
DATABASE_URL=postgresql://opc:opc_dev_password@localhost:5432/opc
DATABASE_URL=postgresql://claude:claude_dev@localhost:5432/continuous_claude

# Embeddings (optional)
VOYAGE_API_KEY=...
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Claude Code](https://img.shields.io/badge/Claude-Code-orange.svg)](https://claude.ai/code)
[![Skills](https://img.shields.io/badge/Skills-105-green.svg)](#skills-system)
[![Agents](https://img.shields.io/badge/Agents-32-purple.svg)](#agents-system)
[![Hooks](https://img.shields.io/badge/Hooks-65-blue.svg)](#hooks-system)
[![Skills](https://img.shields.io/badge/Skills-111-green.svg)](#skills-system)
[![Agents](https://img.shields.io/badge/Agents-16-purple.svg)](#agents-system)
[![Hooks](https://img.shields.io/badge/Hooks-31-blue.svg)](#hooks-system)

**Continuous Claude** transforms Claude Code into a continuously learning system that maintains context across sessions, orchestrates specialized agents, and eliminates wasting tokens through intelligent code analysis.

Expand Down Expand Up @@ -46,7 +46,7 @@ Claude Code has a **compaction problem**: when context fills up, the system comp
| Starting fresh each session | Memory system recalls + daemon auto-extracts learnings |
| Reading entire files burns tokens | 5-layer code analysis + semantic index |
| Complex tasks need coordination | Meta-skills orchestrate agent workflows |
| Repeating workflows manually | 105 skills with natural language triggers |
| Repeating workflows manually | 111 skills with natural language triggers |

**The mantra: Compound, don't compact.** Extract learnings automatically, then start fresh with full context.

Expand Down Expand Up @@ -184,7 +184,7 @@ uv run python -m scripts.setup.wizard
| 2 | Check prerequisites (Docker, Python, uv) |
| 3-5 | Database + API key configuration |
| 6-7 | Start Docker stack, run migrations |
| 8 | Install Claude Code integration (32 agents, 105 skills, 65 hooks) |
| 8 | Install Claude Code integration (16 agents, 111 skills, 31 hooks) |
| 9 | Math features (SymPy, Z3, Pint - optional) |
| 10 | TLDR code analysis tool |
| 11-12 | Diagnostics tools + Loogle (optional) |
Expand Down Expand Up @@ -593,7 +593,7 @@ Agents are specialized AI workers spawned via the Task tool. Located in `.claude

Hooks intercept Claude Code at lifecycle points. Located in `.claude/hooks/`.

#### Hook Events (65 hooks total)
#### Hook Events (31 hooks total)

| Event | Key Hooks | Purpose |
|-------|-----------|---------|
Expand All @@ -614,7 +614,7 @@ Hooks intercept Claude Code at lifecycle points. Located in `.claude/hooks/`.
| **post-edit-diagnostics** | Runs pyright/ruff after edits |
| **memory-awareness** | Surfaces relevant learnings |

[See all 65 hooks →](docs/hooks/)
[See all 31 hooks →](docs/hooks/)

---

Expand Down
20 changes: 20 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,25 @@ services:
retries: 5
restart: unless-stopped

pgbouncer:
image: pgbouncer/pgbouncer:latest
container_name: continuous-claude-pgbouncer
environment:
DATABASES_HOST: postgres
DATABASES_PORT: 5432
DATABASES_USER: ${POSTGRES_USER:-claude}
DATABASES_PASSWORD: ${POSTGRES_PASSWORD:-claude_dev}
DATABASES_DBNAME: ${POSTGRES_DB:-continuous_claude}
PGBOUNCER_POOL_MODE: transaction
PGBOUNCER_MAX_CLIENT_CONN: 100
PGBOUNCER_DEFAULT_POOL_SIZE: 20
PGBOUNCER_IGNORE_STARTUP_PARAMETERS: extra_float_digits
ports:
- "6432:6432"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped

volumes:
continuous_claude_data:
17 changes: 8 additions & 9 deletions opc/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ services:
image: pgvector/pgvector:pg16
container_name: opc-postgres
environment:
POSTGRES_USER: opc
POSTGRES_PASSWORD: opc_dev_password
POSTGRES_DB: opc
POSTGRES_USER: ${POSTGRES_USER:-claude}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-claude_dev}
POSTGRES_DB: ${POSTGRES_DB:-continuous_claude}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
- ../docker/init-schema.sql:/docker-entrypoint-initdb.d/02-memory.sql:ro
- ../docker/init-schema.sql:/docker-entrypoint-initdb.d/init-schema.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U opc -d opc"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-claude} -d ${POSTGRES_DB:-continuous_claude}"]
interval: 5s
timeout: 5s
retries: 5
Expand All @@ -29,9 +28,9 @@ services:
environment:
DATABASES_HOST: postgres
DATABASES_PORT: 5432
DATABASES_USER: opc
DATABASES_PASSWORD: opc_dev_password
DATABASES_DBNAME: opc
DATABASES_USER: ${POSTGRES_USER:-claude}
DATABASES_PASSWORD: ${POSTGRES_PASSWORD:-claude_dev}
DATABASES_DBNAME: ${POSTGRES_DB:-continuous_claude}
PGBOUNCER_POOL_MODE: transaction
PGBOUNCER_MAX_CLIENT_CONN: 100
PGBOUNCER_DEFAULT_POOL_SIZE: 20
Expand Down
Loading
Loading