A personal AI assistant that lives in your Telegram (and Discord). It remembers everything, manages your calendar, runs code, searches the web, SSHs into your machines, and gets smarter over time — all through natural conversation.
Built for a single user. Not a platform, not a framework — just a really good assistant that runs on your Mac.
Talk to it like a person. It streams responses in real-time, understands photos and documents, and has access to a growing set of tools:
| Memory | Learns about you over time. Facts stored as markdown in a private GitHub repo — fully auditable, version-controlled. |
| Web | Searches the web (Brave Search), fetches and reads URLs, browses pages. |
| Code | Runs JavaScript, TypeScript, Python, and shell commands. Reads, writes, and edits files. Full git integration. |
| Calendar & Mail | Native macOS Calendar (EventKit) and Mail integration. "Move my dentist appointment to Friday at 3pm" just works. |
| SSH | Connects to Tailscale devices, runs commands in persistent tmux sessions you can attach to from your phone. |
| Reminders | Native Apple Reminders integration (EventKit). Create, complete, update, and search tasks. |
| Scheduling | "Check Hacker News every morning" — creates cron tasks that run with full AI + tool access. |
| Skills | Reusable workflows the AI can discover, execute, and create at runtime. Stored as JSON in the memory repo. |
| Usage Tracking | Token usage and cost tracking per model/provider. On-demand reports and daily cost summaries. |
| Health Monitoring | Heartbeat checks, service health status, and GitHub webhook server for PR monitoring. |
| Memory Consolidation | DreamTask — automatic background consolidation after conversations. Merges new facts, prunes stale data, and keeps memory files lean. |
| Energy | Octopus Energy API integration — consumption history, tariffs, balance, and billing. |
| GUI Automation | Peekaboo tool for screenshot capture and GUI automation on your Mac Mini via SSH. |
You send a message on Telegram
→ Auth guard (your user ID only)
→ Rate limiter (10 msgs/min)
→ Loads identity + memory from GitHub
→ Routes to AI provider (Claude / OpenAI / MiniMax)
→ Streams response back with live typing updates
→ AI calls tools as needed (web search, code, files, calendar...)
→ Conversation archived, memory updated
The assistant has its own identity (personality, voice, rules) and an evolving memory — all stored as markdown in a separate private GitHub repo. You can browse, edit, and roll back anything it knows.
Switch between providers with a single command. The model string determines the backend:
| Provider | Models | Auth |
|---|---|---|
| Claude Agent | Opus, Sonnet, Haiku | Claude CLI (claude — uses your Max subscription) |
| OpenAI Responses | GPT-5.x, GPT-4o, o3, o4-mini | ChatGPT Plus/Pro subscription (OAuth) |
| OpenAI Codex Agent | codex-agent-* models |
Codex CLI (codex login) |
| MiniMax | M2.5, M2.5-highspeed | MiniMax subscription (OAuth) |
Claude uses the Agent SDK, which piggybacks on the Claude CLI's authentication — just run claude once to log in, and the bot picks it up automatically.
The Codex agent mode uses @openai/codex-sdk, which spawns the codex CLI under the hood for native coding tools and persistent threads.
chris model set sonnet # Switch to Claude Sonnet
chris model set gpt5 # Switch to OpenAI GPT-5.2
chris model set codex-agent # Switch to OpenAI Codex Agent
chris model set minimax # Switch to MiniMaxchris-assistant-memory/ ← Private GitHub repo
├── identity/
│ ├── SOUL.md # Personality and purpose
│ ├── RULES.md # Hard boundaries
│ └── VOICE.md # Tone and language
├── knowledge/
│ ├── about-chris.md # Facts about you
│ ├── preferences.md # Likes, dislikes, style
│ ├── projects.md # Current work
│ └── people.md # People you mention
├── memory/
│ ├── decisions.md # Important decisions
│ ├── learnings.md # Self-improvement notes
│ └── SUMMARY.md # Weekly consolidated summary
├── journal/ # Daily journal entries
├── skills/ # Reusable skill definitions
├── archive/ # Full conversation logs (JSONL)
└── conversations/summaries/ # AI-generated daily summaries
Every memory update is a git commit. DreamTask consolidation runs automatically after conversations — it merges new facts, prunes stale data, and updates SUMMARY.md.
Built-in dark-mode web UI at localhost:3000 — no extra dependencies, starts automatically with the bot.
Tabs: Status & Health, Schedules, Conversations, Memory viewer/editor, real-time log streaming (SSE).
Accessible over Tailnet with token auth.
- Node.js 22+
- A Telegram bot token (from @BotFather)
- A GitHub fine-grained PAT (Contents read/write on your memory repo)
- At least one AI provider subscription (ChatGPT Plus/Pro, Claude Max, or MiniMax)
git clone https://github.com/christayloruk/chris-assistant.git
cd chris-assistant
npm install
npm link # Makes 'chris' available globally
chris setup # Interactive wizard — creates .env# Claude — log in via the Claude CLI (Agent SDK reuses its auth)
claude
# OpenAI — browser OAuth, uses your ChatGPT subscription
chris openai login
# MiniMax — browser OAuth
chris minimax loginchris doctor # Verify all connections
chris start # Start via pm2
chris status # Confirm it's runningMessage your bot on Telegram. That's it.
Discord bot
- Create a bot in the Discord Developer Portal
- Enable Message Content Intent
- Invite it with
botscope + Send Messages, Read Message History - Add to
.env:DISCORD_BOT_TOKEN=your_token DISCORD_ALLOWED_USER_ID=your_discord_user_id - Restart — it connects automatically
Web search
Get a free API key at brave.com/search/api:
chris config set BRAVE_SEARCH_API_KEY your_key
chris restartmacOS Calendar & Mail
npm run setup:calendar-helper # Compiles Swift binary, creates app bundleFirst run triggers a macOS permission dialog. Calendar uses native EventKit for sub-second operations. Mail uses AppleScript.
# Process management
chris start / stop / restart / status
chris logs -f # Live tail logs
# Model switching
chris model # Show current model
chris model set <name> # Switch (opus, sonnet, gpt5, codex, minimax, ...)
chris model search # List all available models
# Memory
chris memory status # List files with sizes
chris memory show <file> # Print a file
chris memory edit <file> # Open in $EDITOR, push to GitHub on save
chris memory search <query> # Search across all memory files
# Identity
chris identity # Print SOUL.md
chris identity edit # Edit personality in $EDITOR
# Config & auth
chris config # Show all config (secrets redacted)
chris config set <key> <value> # Set a value
chris openai login / status # OpenAI OAuth
chris minimax login / status # MiniMax OAuth
# Diagnostics
chris doctor # Health checks
chris doctor --fix # Auto-diagnose and repair
chris setup # First-time setup wizard
# Memory consolidation
chris dream status # Show DreamTask consolidation state
chris dream run # Force consolidation now (bypasses gates)Symphony uses GitHub Issues by default. The workflow contract lives in WORKFLOW.md, including the managed issue labels:
If you want the plain-English explanation and use cases first, read docs/symphony-overview.md.
symphony:todosymphony:in-progresssymphony:reworksymphony:human-review
Basic operator loop:
- Label an issue with
symphony:todo. - Run
chris symphony run-once WORKFLOW.md. - Inspect progress with
chris symphony statusandchris symphony logs <issue>. - When Symphony reaches
symphony:human-review, it lands the workspace changes onto acodex/symphony/*branch and opens a draft PR automatically. - Reviewer assignment stays manual in v1; landing stops at a draft PR so a human can inspect before review handoff.
Maintenance:
chris symphony cleanupshows finished workspaces that can be removed.chris symphony cleanup --applyremoves them.chris symphony cleanup --delete-remote-branches --applyalso prunes stalecodex/symphony/*remote branches that no longer back an open PR.
| Command | Description |
|---|---|
/clear |
Reset conversation (long-term memory preserved) |
/model |
Show current model and provider |
/memory |
Show memory file status |
/project |
Show or set active workspace |
/reload |
Reload memory from GitHub |
/help |
List all commands |
| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | From @BotFather |
TELEGRAM_ALLOWED_USER_ID |
Yes | Your numeric Telegram user ID |
GITHUB_TOKEN |
Yes | Fine-grained PAT (Contents read/write on memory repo only) |
GITHUB_MEMORY_REPO |
Yes | owner/repo format |
AI_MODEL |
No | Model ID — determines provider (default: gpt-4o) |
BRAVE_SEARCH_API_KEY |
No | Enables web search tool |
WORKSPACE_ROOT |
No | Root for file/git tools (default: ~/Projects) |
CLAUDE_MODEL |
No | Alternative to AI_MODEL — used if AI_MODEL is not set |
IMAGE_MODEL |
No | Model for image generation (default: gpt-5.2) |
MAX_TOOL_TURNS |
No | Max tool call turns per conversation (default: 200) |
DISCORD_BOT_TOKEN |
No | Enables Discord bot |
DISCORD_ALLOWED_USER_ID |
No | Your Discord user ID |
DISCORD_GUILD_ID |
No | Discord server ID |
DASHBOARD_TOKEN |
No | Auth token for remote dashboard access |
WEBHOOK_PORT |
No | GitHub webhook server port (default: 3001) |
SYMPHONY_STATUS_URL |
No | Symphony status page URL (default: http://127.0.0.1:3010) |
Config is validated through a typed zod schema in src/infra/config/ (src/config.ts is a compatibility facade). Run chris setup for guided configuration.
The codebase is now organized into explicit layers:
src/
├── app/ # Bootstrap, lifecycle, service registry
├── agent/ # Chat orchestration + provider session handling
├── channels/ # Transport adapters (Telegram, Discord)
├── domain/ # Core business domains
│ ├── conversations/ # History, archive, backup, summaries
│ ├── memory/ # Memory repo access, journals, consolidation, prompts
│ └── schedules/ # Cron matching, storage, execution
├── infra/ # Shared infrastructure (config, storage)
├── providers/ # AI provider implementations
├── tools/ # Tool registry platform + tool modules
├── dashboard/ # Dashboard runtime + UI template
├── skills/ # Dynamic workflow system
├── swift/ # Swift source for EventKit binaries (Calendar, Reminders)
├── cli/ # Commander.js CLI
└── symphony/ # Autonomous workflow/orchestration subsystemTelegram / Discord message
→ channel handler
→ ChatService
→ provider routing (Claude / OpenAI / Codex Agent / MiniMax)
→ tool execution via shared registry
→ conversation + archive persistence
→ memory/journal updatessrc/app/— app startup, shutdown, service registrationsrc/agent/chat-service.ts— central provider routing, image routing, abort/session helperssrc/channels/telegram/*— Telegram bot, commands, streaming handlerssrc/channels/discord/*— Discord client, message handling, outbound notificationssrc/domain/conversations/*— rolling history, archives, backups, daily/weekly summariessrc/domain/memory/*— GitHub memory repository, prompt loading, journal service, consolidationsrc/domain/schedules/*— schedule CRUD, cron parsing, scheduled task executionsrc/tools/*— provider-agnostic tool registration, filtering, loop guard, adapterssrc/providers/*— Claude Agent SDK, OpenAI Responses, Codex Agent SDK, MiniMaxsrc/dashboard/*— HTTP runtime/API layer and HTML UI
Key design decisions:
- Tool registration is provider-agnostic — define once in
src/tools/, all providers discover it ChatServiceis the single orchestration layer used by channels and background jobs- Domain services own persistence and runtime behavior; top-level files are mostly compatibility facades
- Config is validated through a typed
zodloader insrc/infra/config/ - No
git pushtool — deliberate safety choice - Code execution is unsandboxed but has dangerous pattern blocking and timeout limits
- All file paths validated through
resolveSafePath()— symlinks outside workspace rejected - Memory writes validated for size, rate, and injection attempts
- Single-user auth — Telegram user ID guard, Discord user ID guard
- Workspace scoping — All file/git tools locked to
WORKSPACE_ROOTwith symlink-aware path validation - Dangerous command blocking —
pm2,kill,reboot,shutdown,rm -rf /blocked in code execution - Memory injection defense — Size limits, rate throttling, content validation
- No git push — The AI can commit but never push
- SSH safety —
BatchMode=yes(no password prompts),execFile()(no shell injection), bot session prefix enforcement
npm run dev # Auto-reload dev server
npm run typecheck # TypeScript + esbuild compat check
npm test # Vitest suiteNode.js 22+ / TypeScript / grammY / discord.js / Claude Agent SDK / Octokit / Commander.js / pm2 / zod