# 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();
```
