# zCode CLI X — Service Architecture Map Generated: May 5, 2026 Context: Wiring services into the Telegram bot --- ## Architecture Overview Two distinct layers coexist: 1. **Custom JS shim** (`src/zcode.js` → `bin/zcode.js`) — the simplified entry point that the Telegram bot uses. Clean, independent initialization chain. 2. **Original TypeScript codebase** (Claude Code fork: `src/main.tsx`, `src/entrypoints/cli.tsx`) — rich service layer used by the CLI. Most services live here but are NOT wired into the JS shim. **The JS shim (`zcode.js`) is what runs in production.** The TS codebase is available as a library but is not connected to the bot. --- ## PART 1: Custom JS Layer (Currently Wired) ### Initialization Chain in `src/zcode.js` ``` zcode(options) ├── 1. checkEnv() → env object ├── 2. initConfig() → config object ├── 3. initAPI() → api object ({config, client}) ├── 4. initTools() → tools[] array ├── 5. initSkills() → skills[] array ├── 6. initAgents() → agents[] array └── 7. initBot(config, api, tools, skills) → bot object └── import('./bot/index.js').initBot(config, api, tools, skills) ``` ### 1.1 `src/zcode.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/zcode.js` | | **Exported API** | `async function zcode(options)` | | **Init** | Called from `bin/zcode.js` via `import { zcode } from '../src/zcode.js'` | | **Options** | `{ bot: boolean, cli: boolean }` | | **Notes** | Passes `config, api, tools, skills` to `initBot()`. Does NOT pass agents. | ### 1.2 `src/utils/env.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/env.js` | | **Exported API** | `function checkEnv()` | | **Returns** | `{ valid, missing, ZAI_API_KEY, GLM_BASE_URL, TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USERS }` | | **Init** | `checkEnv()` — no constructor, stateless | | **Required vars** | `ZAI_API_KEY`, `GLM_BASE_URL` | ### 1.3 `src/config/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/config/index.js` | | **Exported API** | `async function initConfig()` | | **Returns** | Config object: `{ api, telegram, tools, skills, agents, logging }` | | **Init** | `const config = await initConfig()` | | **Config source** | `.zcode.config.json` in CWD (auto-created from defaults if absent) | ### 1.4 `src/api/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/api/index.js` | | **Exported API** | `async function initAPI()` — returns `{ config, client }` | | | `class ZAIProvider` — `constructor(api)`, `chat(messages, opts)`, `complete(prompt, opts)` | | | `function createZAIProvider(api)` — factory | | **Init** | `const api = await initAPI()` connects to Z.AI and tests `/models` endpoint | | **Notes** | `ZAIProvider` uses `api.client` (axios). `chat()` POSTs to `/chat/completions`. | | **Provider class usage** | `new ZAIProvider(api).chat([{role:'user', content: text}], {model:'glm-5.1'})` | ### 1.5 `src/tools/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/tools/index.js` | | **Exported API** | `async function initTools()` — returns `tools[]` | | | `class BashTool` — `.execute(command, options)` | | | `class FileEditTool` — `.read(path)`, `.write(path, content)`, `.append(path, content)`, `.edit(path, oldText, newText)` | | | `class WebSearchTool` — `.search(query, options)` | | | `class GitTool` — `.status()`, `.log(options)`, `.diff(options)`, `.commit(message)`, `.push()`, `.pull()` | | **Init** | `const tools = await initTools()` — instantiates each tool class, filtered by env flags | | **Env flags** | `ZCODE_ENABLE_BASH`, `ZCODE_ENABLE_FILE_EDIT`, `ZCODE_ENABLE_WEB_SEARCH`, `ZCODE_ENABLE_GIT` | ### 1.6 `src/skills/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/skills/index.js` | | **Exported API** | `async function initSkills()` — returns `skills[]` of `{ name, description, version, category }` | | **Init** | `const skills = await initSkills()` | | **Sources** | (1) `.json`/`.js` files in `skills/` dir in CWD, (2) 5 built-in skills hardcoded | | **Built-in skills** | `code_review`, `bug_fix`, `refactor`, `documentation`, `testing` | ### 1.7 `src/agents/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/agents/index.js` | | **Exported API** | `async function initAgents()` — returns `agents[]` of `{ id, name, description, capabilities, enabled }` | | | `class AgentOrchestrator` — `constructor(agents)`, `execute(agentId, task, context)`, `getAgent(id)`, `listAgents()` | | **Init** | `const agents = await initAgents()` | | **Built-in agents** | `coder`, `architect`, `devops` (all enabled by default) | ### 1.8 `src/bot/index.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/bot/index.js` | | **Exported API** | `async function initBot(config, api, tools, skills)` — returns `{ send, ws, waitForMessages, getConnections }` | | **Init** | `const bot = await import('./bot/index.js').then(m => m.initBot(config, api, tools, skills))` | | **Current state** | THIN: creates Express+WebSocket server, handles webhook POSTs, routes messages through ZAIProvider directly. Does NOT use tools/skills/agents params. | | **Return value** | `{ send: sendTelegramMessage(chatId, text, opts), ws: sendWebSocketMessage(chatId, msg), waitForMessages: async (), getConnections: () => num }` | ### 1.9 `src/utils/logger.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/logger.js` | | **Exported API** | `export const logger` — winston logger instance | | **Init** | Import and use directly: `import { logger } from '../utils/logger.js'` | | **Features** | Console transport (colorized), optional file transport via `LOG_FILE` env var | ### 1.10 `src/utils/rtk.js` | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/rtk.js` | | **Exported API** | `class RTKIntegration` — `init()`, `isCommandSupported(cmd)`, `optimizeCommand(command, args)`, `getTrackingStats()`, `listSupportedCommands()` | | | `function getRTK()` — singleton factory | | **Init** | `const rtk = getRTK(); await rtk.init()` | | **Status** | Singleton. Lazily instantiated. Used by BashTool and GitTool. | --- ## PART 2: Original TypeScript Service Layer (Available but NOT wired into bot) These services exist in the Claude Code fork but are **not imported or used by the JS shim** (`zcode.js` → `bot/index.js`). They can be imported directly if needed. ### 2.1 Voice Service | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/services/voice.ts` | | **Exported API** | `startRecording(fallbackToSoX?)`, `stopRecording()`, `checkRecordingAvailability()` (need full export list) | | **Init** | `import { startRecording, stopRecording } from '../services/voice.ts'` — no init, module-level state | | **Dependencies** | `audio-capture-napi` (native), falls back to SoX/arecord on Linux | ### 2.2 Cron Scheduler | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/cronScheduler.ts` | | **Exported API** | `class CronScheduler` with options `{ onFire, isLoading, assistantMode }`, `start()`, `stop()` | | | `isRecurringTaskAged(t, nowMs, maxAgeMs)` | | | `getSchedulerCheckDelayMs(nextFireAtMs, nowMs, options)` | | **Init** | Needs `cronTasks.ts` utilities: `readCronTasks`, `findMissedTasks`, `markCronTasksFired` | | **Dependencies** | `cronTasks.ts`, `cronTasksLock.ts`, `cron.ts`, `bootstrap/state.ts` | ### 2.3 MCP Validation | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/mcpValidation.ts` | | **Exported API** | `getMaxMcpOutputTokens()`, `getContentSizeEstimate(content)`, `MCPToolResult` type | | | Internal: `truncateContentBlocks(blocks, maxChars)`, `truncateString(content, maxChars)` | | **Init** | Import functions directly | | **Dependencies** | Anthropic SDK types, `imageResizer.ts`, `tokenEstimation.ts` | ### 2.4 Memory System | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/utils/memoryFileDetection.ts` | | | `/home/uroma2/zcode-cli-x/src/memdir/memoryTypes.ts` | | | `/home/uroma2/zcode-cli-x/src/memdir/memoryScan.ts` | | | `/home/uroma2/zcode-cli-x/src/memdir/memoryAge.ts` | | **Exported API (memoryTypes.ts)** | `MEMORY_TYPES` (`['user', 'feedback', 'project', 'reference']`), `parseMemoryType(raw)` | | | `TYPES_SECTION_COMBINED` (system prompt text), `TYPES_SECTION_PRIVATE` | ### 2.5 Context Compression (Compact) | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/services/compact/compact.ts` (1706 lines) | | | `/home/uroma2/zcode-cli-x/src/services/compact/cachedMicrocompact.ts` | | | `/home/uroma2/zcode-cli-x/src/services/compact/apiMicrocompact.ts` | | | `/home/uroma2/zcode-cli-x/src/services/compact/compactWarningState.ts` | | **Init** | Deeply integrated into the main loop (`main.tsx`/`query.ts`). Not standalone. | ### 2.6 Tool Orchestration | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/services/tools/toolOrchestration.ts` | | **Exported API** | `runTools(toolUseMessages, assistantMessages, canUseTool, toolUseContext)` — async generator | | | `DEFAULT_MAX_TOOL_USE_CONCURRENCY`, `getMaxToolUseConcurrency()` | | **Dependencies** | `toolExecution.ts`, `toolConcurrency.ts`, `StreamingToolExecutor.ts`, `toolHooks.ts` | ### 2.7 Team Memory Sync | Field | Value | |-------|-------| | **Path** | `/home/uroma2/zcode-cli-x/src/services/teamMemorySync/index.ts` | | **Exported API** | Sync service for team memory files between local FS and server API | | **Dependencies** | Axios, OAuth, git remote, secret scanner | ### 2.8 Other Services (brief) | Service | Path | Purpose | |---------|------|---------| | **Notifier** | `src/services/notifier.ts` | `sendNotification(opts, terminal)` — desktop notifications | | **Prevent Sleep** | `src/services/preventSleep.ts` | `startPreventSleep()`, `stopPreventSleep()`, `forceStopPreventSleep()` — macOS caffeinate | | **Token Estimation** | `src/services/tokenEstimation.ts` | Token counting with Anthropic/Bedrock/Vertex APIs | | **Rate Limit Messages** | `src/services/rateLimitMessages.ts` | `getRateLimitMessage(limits, model)`, `isRateLimitErrorMessage(text)` | | **VCR** | `src/services/vcr.ts` | Fixture caching for tests (`withVCR`) | | **MCP Services** | `src/services/mcp/` | VSCode SDK MCP, connection management, normalization, headers | | **OAuth** | `src/services/oauth/` | OAuth client, crypto, profile, auth-code listener | | **Analytics** | `src/services/analytics/` | Event logging and feature flags (GrowthBook/Statsig) | | **Scheduled Tasks** | `src/utils/cronTasks.ts` | `CronTask` type, `readCronTasks()`, `findMissedTasks()` | | **Model Providers** | `src/utils/model/providers.ts` | `APIProvider` type, `getAPIProvider()`, provider detection | --- ## PART 3: Missing Wire-Up in Current Bot The current `bot/index.js` receives `config, api, tools, skills` but **only uses `api`** (via `ZAIProvider`). Specifically: | Parameter | Passed by zcode.js? | Used by bot/index.js? | |-----------|-------------------|----------------------| | `config` | ✅ | ❌ (not referenced) | | `api` | ✅ | ✅ (used for ZAIProvider) | | `tools` | ✅ | ❌ (not referenced) | | `skills` | ✅ | ❌ (not referenced) | | `agents` | ❌ (not passed) | ❌ (not available) | ### Telegram Bot Options (TS — not wired) Two additional Telegram implementations exist but are **not connected**: 1. `src/telegram-bot.ts` — class `TelegramBot` with `startPolling()`, `sendMessage()`, `sendMarkdown()` — spawns zcode as subprocess 2. `src/telegram-bot-cli.ts` — CLI entrypoint that loads config from `.env` and runs `TelegramBot.startPolling()` These are standalone and do NOT use the service architecture. --- ## PART 4: Entry Points | Entry Point | Path | Description | |-------------|------|-------------| | **CLI (production)** | `bin/zcode.js` | Runs `zcode()` from `src/zcode.js`. Commander-based CLI, loads dotenv. | | **CLI (TS fork)** | `src/entrypoints/cli.tsx` | Full Claude Code fork CLI (Bun-bundled to `dist/cli.mjs`) | | **Init** | `src/entrypoints/init.ts` | Project initialization | | **MCP Server** | `src/entrypoints/mcp.ts` | MCP server entrypoint | | **SDK** | `src/entrypoints/sdk/` | Agent SDK entrypoints | --- ## PART 5: How to Wire a Service Into the Telegram Bot **Pattern** (from `src/zcode.js`): ```js // 1. Import init function import { initService } from './path/to/service/index.js'; // 2. Call init in zcode() const service = await initService(); // 3. Pass to bot init const bot = await botModule.initBot(config, api, tools, skills, service); ``` **To use TS services from the JS bot** — use dynamic `import()`: ```js const { someFunction } = await import('../services/someService.ts'); ``` **For singleton services** (like RTK), use the singleton factory pattern already established: ```js const rtk = getRTK(); await rtk.init(); ```