- start.sh: use dirname instead of hardcoded path - src/zcode.js: remove hardcoded chat_id fallback - src/utils/rtk.js: use 'rtk' from PATH instead of hardcoded binary path - src/telegram-bot.ts: use process.cwd() instead of hardcoded path - TELEGRAM_SETUP.md: replace token/chat_id with placeholders - QUICKSTART.md: sanitize all references - SERVICE_MAP.md: use relative paths instead of absolute
270 lines
12 KiB
Markdown
270 lines
12 KiB
Markdown
# 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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `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** | `src/utils/memoryFileDetection.ts` |
|
|
| | `src/memdir/memoryTypes.ts` |
|
|
| | `src/memdir/memoryScan.ts` |
|
|
| | `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** | `src/services/compact/compact.ts` (1706 lines) |
|
|
| | `src/services/compact/cachedMicrocompact.ts` |
|
|
| | `src/services/compact/apiMicrocompact.ts` |
|
|
| | `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** | `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** | `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();
|
|
```
|