Skip to content

Latest commit

ย 

History

History
1327 lines (1116 loc) ยท 43.1 KB

File metadata and controls

1327 lines (1116 loc) ยท 43.1 KB

AI Agent Implementation Plan

Overview

Build a minimal AI agent combining OpenClaw's retrieval-based memory system with SOUL.md identity mechanism. The agent is distributed as a CLI called xz, which provides both interactive TUI mode and command-line access to memory/history.

Core Philosophy:

  • Identity (WHO) โ†’ SOUL.md/USER.md - Injected at session start
  • Knowledge (WHAT) โ†’ Pre-loaded MEMORY.md + searchable via xz memory
  • History โ†’ Searchable via xz history, paginated access
  • Scheduler โ†’ TUI-embedded (2s tick), tasks execute in chat flow
  • Agent invokes itself โ†’ Skills wrap xz CLI commands

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         TUI Mode (xz)                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  Background Scheduler (ๆฏ 2s ๆฃ€ๆŸฅ)                                 โ”‚  โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚  โ”‚
โ”‚  โ”‚  โ”‚ Check tasks โ”‚ โ†’  โ”‚ Task due?   โ”‚ โ†’  โ”‚ Insert wakeup msg   โ”‚   โ”‚  โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                         Context Window (200K)                           โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  [PRE-LOADED: Always in context]                                        โ”‚
โ”‚   - SOUL.md (~1-2K tokens)         Identity: who I am                   โ”‚
โ”‚   - USER.md (~0.5-1K tokens)       Identity: who the user is            โ”‚
โ”‚   - MEMORY.md (~2-4K tokens)       Knowledge: key facts                 โ”‚
โ”‚   - Recent daily logs (~1-2K)      Knowledge: recent events             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  [ACTIVE: Growing conversation]                                         โ”‚
โ”‚   - User messages                                                       โ”‚
โ”‚   - Agent responses                                                     โ”‚
โ”‚   - โฐ Wakeup messages (from scheduler)                                  โ”‚
โ”‚   - Task execution output                                               โ”‚
โ”‚   - Compaction at ~150K (rarely needed)                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                    โ”‚
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ–ผ                     โ–ผ                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Identity Docs   โ”‚  โ”‚   Knowledge Source   โ”‚  โ”‚   Search Index    โ”‚
โ”‚                   โ”‚  โ”‚                      โ”‚  โ”‚   (SQLite)        โ”‚
โ”‚  .agents/SOUL.md  โ”‚  โ”‚  .agents/MEMORY.md   โ”‚  โ”‚                   โ”‚
โ”‚  .agents/USER.md  โ”‚  โ”‚  memory/2026-03-03   โ”‚  โ”‚  BM25 (FTS5)      โ”‚
โ”‚                   โ”‚  โ”‚  memory/2026-03-04   โ”‚  โ”‚  Vector (vec0)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                       โ”‚                         โ”‚
         โ”‚                       โ”‚                         โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                โ–ผ                                    โ”‚
โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”‚
โ”‚              โ”‚      Chat History (SQLite)        โ”‚                  โ”‚
โ”‚              โ”‚  sessions + messages + FTS5       โ”‚                  โ”‚
โ”‚              โ”‚  (stored in data/agent.db)        โ”‚                  โ”‚
โ”‚              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚
โ”‚                                โ”‚                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                 โ”‚
                                 โ–ผ
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚   xz CLI (Self-Invocation)        โ”‚
              โ”‚                                   โ”‚
              โ”‚  xz memory search <query>         โ”‚
              โ”‚  xz memory get <file> --lines     โ”‚
              โ”‚  xz history search <query>        โ”‚
              โ”‚  xz history session <id>          โ”‚
              โ”‚  xz history list                  โ”‚
              โ”‚                                   โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

xz CLI Design

้ฆ–ๆฌกไฝฟ็”จ้…็ฝฎๆต็จ‹

ๅฝ“ ~/.xz/config.toml ไธๅญ˜ๅœจๆ—ถ๏ผŒTUI ๅฏๅŠจไผš่ฟ›ๅ…ฅ้…็ฝฎๅ‘ๅฏผ๏ผš

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๐Ÿค– Welcome to xz - AI Agent                                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚  ้ฆ–ๆฌกไฝฟ็”จ๏ผŒ่ฏท้€‰ๆ‹ฉๆจกๅž‹ๆไพ›ๅ•†๏ผš                                  โ”‚
โ”‚                                                              โ”‚
โ”‚  [1] Kimi Code (ๆŽจ่)                                        โ”‚
โ”‚      โœ“ ๆฃ€ๆต‹ๅˆฐๆœฌๅœฐ OAuth ๅ‡ญ่ฏ                                  โ”‚
โ”‚      ๆจกๅž‹: kimi-for-coding (256K context)                     โ”‚
โ”‚                                                              โ”‚
โ”‚  [2] OpenAI                                                  โ”‚
โ”‚      ้œ€่ฆ OPENAI_API_KEY                                      โ”‚
โ”‚                                                              โ”‚
โ”‚  [3] Anthropic (Claude)                                      โ”‚
โ”‚      ้œ€่ฆ ANTHROPIC_API_KEY                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  [4] ่‡ชๅฎšไน‰ OpenAI-compatible                                โ”‚
โ”‚      ๅ…ผๅฎน OpenAI API ็š„็ฌฌไธ‰ๆ–นๆœๅŠก                             โ”‚
โ”‚                                                              โ”‚
โ”‚  > 1                                                         โ”‚
โ”‚  โœ… ๅทฒ้€‰ๆ‹ฉ Kimi Code                                         โ”‚
โ”‚  ้…็ฝฎๅทฒไฟๅญ˜ๅˆฐ ~/.xz/config.toml                              โ”‚
โ”‚                                                              โ”‚
โ”‚  ๆŒ‰ Enter ๅผ€ๅง‹...                                            โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

้…็ฝฎๆ–‡ไปถ็ป“ๆž„

# ~/.xz/config.toml

[model]
provider = "kimi"                    # kimi | openai | anthropic | custom
model = "kimi-for-coding"            # ๅ…ทไฝ“ๆจกๅž‹ID
base_url = "https://api.kimi.com/coding/v1"  # API base URL

# OAuth ๆˆ– API Key ้…็ฝฎ
[auth]
type = "oauth"                       # oauth | api_key
# OAuth ้…็ฝฎ๏ผˆๅฆ‚ Kimi๏ผ‰
oauth_credentials_path = "~/.kimi/credentials/kimi-code.json"
# ๆˆ– API Key๏ผˆๅฆ‚ OpenAI๏ผ‰
# api_key = "sk-..."

# ้ซ˜็บง้…็ฝฎ๏ผˆๅฏ้€‰๏ผ‰
[context]
max_tokens = 262144                  # ๆœ€ๅคงไธŠไธ‹ๆ–‡
preload_identity = true              # ้ข„ๅŠ ่ฝฝ SOUL.md/USER.md
preload_memory = true                # ้ข„ๅŠ ่ฝฝ MEMORY.md

[scheduler]
enabled = true                       # ๅฏ็”จๅ†…็ฝฎ่ฐƒๅบฆๅ™จ
check_interval_ms = 2000             # ๆฃ€ๆŸฅ้—ด้š”

[memory]
hybrid_search = true                 # ๅฏ็”จๆททๅˆๆœ็ดข
semantic_weight = 0.7                # ่ฏญไน‰ๆœ็ดขๆƒ้‡
keyword_weight = 0.3                 # ๅ…ณ้”ฎ่ฏๆœ็ดขๆƒ้‡

# ้ฆ–ๆฌก้…็ฝฎๆ ‡ๅฟ—
[setup]
completed = true                     # ๆ˜ฏๅฆๅฎŒๆˆ้ฆ–ๆฌก้…็ฝฎ
completed_at = "2026-03-03T12:00:00+08:00"

้…็ฝฎ็ฎก็†ๅ‘ฝไปค

# ๆŸฅ็œ‹ๅฝ“ๅ‰้…็ฝฎ
xz config

# ไฟฎๆ”น้…็ฝฎ๏ผˆไบคไบ’ๅผ๏ผ‰
xz config set

# ๅˆ‡ๆขๆจกๅž‹
xz config provider <provider>
xz config model <model>

# ้‡ๆ–ฐ่ฟ่กŒ้ฆ–ๆฌก้…็ฝฎๅ‘ๅฏผ
xz config setup --reset

# ้ชŒ่ฏ้…็ฝฎ
xz config verify

้…็ฝฎๅŠ ่ฝฝ้€ป่พ‘

// src/config/index.ts
import { existsSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { parse } from '@iarna/toml';

const CONFIG_DIR = join(homedir(), '.xz');
const CONFIG_FILE = join(CONFIG_DIR, 'config.toml');

export interface XZConfig {
  model: {
    provider: 'kimi' | 'openai' | 'anthropic' | 'custom';
    model: string;
    baseUrl: string;
  };
  auth: {
    type: 'oauth' | 'api_key';
    oauthCredentialsPath?: string;
    apiKey?: string;
  };
  context: {
    maxTokens: number;
    preloadIdentity: boolean;
    preloadMemory: boolean;
  };
  scheduler: {
    enabled: boolean;
    checkIntervalMs: number;
  };
  memory: {
    hybridSearch: boolean;
    semanticWeight: number;
    keywordWeight: number;
  };
  setup: {
    completed: boolean;
    completedAt?: string;
  };
}

// ๆฃ€ๆŸฅๆ˜ฏๅฆ้ฆ–ๆฌกไฝฟ็”จ
export function isFirstRun(): boolean {
  return !existsSync(CONFIG_FILE);
}

// ๅŠ ่ฝฝ้…็ฝฎ
export function loadConfig(): XZConfig {
  if (isFirstRun()) {
    throw new FirstRunError('Config not found. Run setup first.');
  }
  
  const content = readFileSync(CONFIG_FILE, 'utf-8');
  return parse(content) as XZConfig;
}

// ไฟๅญ˜้…็ฝฎ
export function saveConfig(config: XZConfig): void {
  if (!existsSync(CONFIG_DIR)) {
    mkdirSync(CONFIG_DIR, { recursive: true });
  }
  
  const toml = stringify(config);
  writeFileSync(CONFIG_FILE, toml);
}

้ฆ–ๆฌก้…็ฝฎๅ‘ๅฏผๅฎž็Žฐ

// src/tui/setup-wizard.ts
import { isFirstRun, saveConfig } from '../config';
import { detectKimiCredentials } from '../config/kimi';

export async function runSetupWizard(): Promise<void> {
  console.clear();
  console.log('๐Ÿค– Welcome to xz - AI Agent\n');
  
  // ๆฃ€ๆต‹ๅฏ็”จ็š„ๆไพ›ๅ•†
  const providers = await detectAvailableProviders();
  
  console.log('้ฆ–ๆฌกไฝฟ็”จ๏ผŒ่ฏท้€‰ๆ‹ฉๆจกๅž‹ๆไพ›ๅ•†๏ผš\n');
  
  providers.forEach((p, i) => {
    console.log(`[${i + 1}] ${p.name}`);
    if (p.detected) {
      console.log(`    โœ“ ๆฃ€ๆต‹ๅˆฐๆœฌๅœฐ้…็ฝฎ`);
    }
    console.log(`    ${p.description}\n`);
  });
  
  // ็”จๆˆท้€‰ๆ‹ฉ
  const choice = await prompt('> ');
  const selected = providers[parseInt(choice) - 1];
  
  // ๆ นๆฎ้€‰ๆ‹ฉ้…็ฝฎ
  const config = await configureProvider(selected);
  
  // ไฟๅญ˜้…็ฝฎ
  config.setup = { completed: true, completedAt: new Date().toISOString() };
  saveConfig(config);
  
  console.log('\nโœ… ้…็ฝฎๅทฒไฟๅญ˜ๅˆฐ ~/.xz/config.toml');
  console.log('ๆŒ‰ Enter ๅผ€ๅง‹...');
  await prompt('');
}

// ๆฃ€ๆต‹ๅฏ็”จ็š„ๆไพ›ๅ•†
async function detectAvailableProviders(): Promise<ProviderOption[]> {
  const providers: ProviderOption[] = [
    {
      id: 'kimi',
      name: 'Kimi Code (ๆŽจ่)',
      description: 'ๆจกๅž‹: kimi-for-coding (256K context)',
      detected: detectKimiCredentials(),
    },
    {
      id: 'openai',
      name: 'OpenAI',
      description: '้œ€่ฆ OPENAI_API_KEY',
      detected: !!process.env.OPENAI_API_KEY,
    },
    {
      id: 'anthropic',
      name: 'Anthropic (Claude)',
      description: '้œ€่ฆ ANTHROPIC_API_KEY',
      detected: !!process.env.ANTHROPIC_API_KEY,
    },
    {
      id: 'custom',
      name: '่‡ชๅฎšไน‰ OpenAI-compatible',
      description: 'ๅ…ผๅฎน OpenAI API ็š„็ฌฌไธ‰ๆ–นๆœๅŠก',
      detected: false,
    },
  ];
  
  return providers;
}

xz CLI Design

Command Structure

# Interactive TUI mode (default)
# ้ฆ–ๆฌก่ฟ่กŒ๏ผš่ฟ›ๅ…ฅ้…็ฝฎๅ‘ๅฏผ
# ๅทฒ้…็ฝฎ๏ผš่ฟ›ๅ…ฅ TUI
xz

# Memory commands
xz memory search <query> [--limit N] [--semantic] [--page N]
xz memory get <file> [--start-line N] [--end-line N]
xz memory list [--date YYYY-MM-DD]

# History commands  
xz history search <query> [--limit N] [--session ID] [--page N]
xz history session <session-id> [--limit N] [--offset N]
xz history list [--limit N] [--page N]

# Skill management
xz skill list
xz skill create <name> [--from-template]
xz skill reload

# Scheduler (TUI mode only)
xz schedule list                    # List scheduled tasks
xz schedule add <task> <time>       # Add task (e.g., "daily backup" "09:00")
xz schedule remove <task-id>        # Remove task
# Note: Tasks only execute while TUI is running (every 2s check)

CLI Implementation

// src/cli/index.ts
import { Command } from 'commander';

const program = new Command('xz');

// Memory commands
program
  .command('memory')
  .description('Knowledge memory operations')
  .addCommand(
    new Command('search')
      .argument('<query>', 'Search query')
      .option('-l, --limit <n>', 'Results per page', '10')
      .option('-p, --page <n>', 'Page number', '1')
      .option('--semantic', 'Use semantic search', false)
      .option('--keyword', 'Use keyword search only', false)
      .action(async (query, options) => {
        const results = await memorySearch(query, {
          limit: parseInt(options.limit),
          offset: (parseInt(options.page) - 1) * parseInt(options.limit),
          semantic: options.semantic,
          keywordOnly: options.keyword
        });
        console.table(results);
      })
  )
  .addCommand(
    new Command('get')
      .argument('<file>', 'Memory file path')
      .option('-s, --start-line <n>', 'Start line', '1')
      .option('-e, --end-line <n>', 'End line')
      .action(async (file, options) => {
        const content = await memoryGet(file, {
          lineStart: parseInt(options.startLine),
          lineEnd: options.endLine ? parseInt(options.endLine) : undefined
        });
        console.log(content);
      })
  );

// History commands
program
  .command('history')
  .description('Chat history operations')
  .addCommand(
    new Command('search')
      .argument('<query>', 'Search query')
      .option('-l, --limit <n>', 'Results per page', '10')
      .option('-p, --page <n>', 'Page number', '1')
      .option('--date-from <date>', 'Start date (YYYY-MM-DD)')
      .option('--date-to <date>', 'End date (YYYY-MM-DD)')
      .action(async (query, options) => {
        const results = await historySearch(query, {
          limit: parseInt(options.limit),
          offset: (parseInt(options.page) - 1) * parseInt(options.limit),
          dateFrom: options.dateFrom,
          dateTo: options.dateTo
        });
        printHistoryResults(results, parseInt(options.page), parseInt(options.limit));
      })
  )
  .addCommand(
    new Command('session')
      .argument('<session-id>', 'Session ID')
      .option('-l, --limit <n>', 'Messages per page', '20')
      .option('-o, --offset <n>', 'Offset', '0')
      .action(async (sessionId, options) => {
        const session = await getSession(sessionId, {
          limit: parseInt(options.limit),
          offset: parseInt(options.offset)
        });
        printSession(session, parseInt(options.offset));
      })
  )
  .addCommand(
    new Command('list')
      .option('-l, --limit <n>', 'Sessions per page', '10')
      .option('-p, --page <n>', 'Page number', '1')
      .action(async (options) => {
        const sessions = await listSessions({
          limit: parseInt(options.limit),
          offset: (parseInt(options.page) - 1) * parseInt(options.limit)
        });
        console.table(sessions);
      })
  );

// Default: TUI mode
if (process.argv.length === 2) {
  // No args - check first run
  if (isFirstRun()) {
    await runSetupWizard();
  }
  startTUI();
} else {
  program.parse();
}

Pagination Design

interface PaginatedResult<T> {
  items: T[];
  page: number;
  perPage: number;
  total: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface MemorySearchResult {
  file: string;
  lineStart: number;
  lineEnd: number;
  snippet: string;
  score: number;
}

interface HistorySearchResult {
  sessionId: string;
  sessionTitle?: string;
  messageId: string;
  role: 'user' | 'assistant' | 'tool';
  content: string;
  timestamp: string;
  score: number;
}

// Paginated output format
function printPaginated<T>(
  result: PaginatedResult<T>,
  formatItem: (item: T) => string
): void {
  result.items.forEach(formatItem);
  
  console.log(`\nPage ${result.page}/${result.totalPages} (${result.total} total)`);
  if (result.hasPrev) console.log('Use --page', result.page - 1, 'for previous');
  if (result.hasNext) console.log('Use --page', result.page + 1, 'for next');
}

Skills that Invoke xz CLI

Skills wrap xz CLI commands for the agent to use:

// ~/.claude/skills/memory-search/SKILL.md
---
name: memory-search
description: Search knowledge memory for facts, decisions, or observations
argument-hint: "<query> [--page N]"
disable-model-invocation: false
---

Search the knowledge base when you need to recall information not in the pre-loaded MEMORY.md.

**Usage from agent:**
```bash
xz memory search "<query>" --limit 5

When to use:

  • User asks about something from past conversations
  • Need specific details not in current context
  • Looking for user preferences or decisions

Handling results:

  • Results include file path, line numbers, and snippet
  • Use xz memory get <file> --start-line N --end-line N to read full content
  • Ask user if they want to see more results (--page 2, etc.)

// ~/.claude/skills/history-search/SKILL.md

name: history-search description: Search chat history for past conversations argument-hint: " [--page N] [--date-from YYYY-MM-DD]" disable-model-invocation: false

Search past chat sessions when user references earlier discussions.

Usage from agent:

xz history search "<query>" --limit 5

When to use:

  • User says "like we discussed yesterday"
  • Need context from earlier sessions
  • Looking for specific decisions or code from past chats

Handling pagination:

  • Default shows 10 results per page
  • If user wants more, use --page 2, etc.
  • To see full session: xz history session <session-id> --limit 20

// ~/.claude/skills/get-session/SKILL.md

name: get-session description: Get full message history of a specific chat session argument-hint: " [--offset N]" disable-model-invocation: true

Retrieve a complete session. Use when the user asks to see what was discussed.

Usage:

xz history session <session-id> --limit 20 --offset 0

Pagination:

  • Use --offset to paginate through long sessions
  • Increase offset by limit for next page

### Skill Implementation (Bash Tool)

```typescript
// Agent executes skills via bash tool
const bashTool: Tool = {
  name: 'bash',
  description: 'Execute bash commands including xz CLI for memory/history search',
  parameters: Type.Object({
    command: Type.String(),
    timeout: Type.Optional(Type.Number({ default: 60 }))
  })
};

// Example: Agent searching memory
// command: "xz memory search \"git preferences\" --limit 5"

// Example: Agent getting specific content
// command: "xz memory get memory/2026-03-03.md --start-line 15 --end-line 25"

// Example: Agent searching history
// command: "xz history search \"database schema\" --limit 5 --date-from 2026-02-01"

Memory System Design

1. Three-Layer Architecture

Layer Type Content Access Pattern
Identity Pre-loaded SOUL.md, USER.md Full injection into system prompt
Knowledge Pre-loaded MEMORY.md, recent daily logs Full injection into system prompt
Retrieval On-demand All history & memory via xz CLI Via skills that invoke xz

2. Pre-loaded Content (System Prompt)

interface SystemPromptBuilder {
  soul(): string;        // SOUL.md - who I am
  user(): string;        // USER.md - who the user is
  memory(): string;      // MEMORY.md - key facts
  recentDaily(days?: number): string; // Recent daily logs
}

// System prompt structure (~5-10K tokens typical)
const systemPrompt = `
${soul}           // ~1-2K tokens
${user}           // ~0.5-1K tokens  
${memory}         // ~2-4K tokens
${recentDaily}    // ~1-2K tokens

You have access to the xz CLI for retrieval:
- xz memory search <query> [--page N]
- xz memory get <file> [--start-line N] [--end-line N]
- xz history search <query> [--page N]
- xz history session <id> [--offset N]

Use these when you need information not in the pre-loaded context.
`;

3. SQLite Schema

-- Single database: data/agent.db

-- Knowledge index (for xz memory search)
CREATE TABLE knowledge_chunks (
  id TEXT PRIMARY KEY,
  file TEXT NOT NULL,
  line_start INTEGER,
  line_end INTEGER,
  content TEXT NOT NULL,
  tags TEXT,  -- JSON array
  created_at INTEGER,
  updated_at INTEGER
);

CREATE VIRTUAL TABLE knowledge_fts USING fts5(content, content='knowledge_chunks');
CREATE VIRTUAL TABLE knowledge_vec USING vec0(id TEXT PRIMARY KEY, embedding FLOAT[1536]);

-- Chat history
CREATE TABLE sessions (
  id TEXT PRIMARY KEY,
  created_at INTEGER,
  updated_at INTEGER,
  title TEXT,
  message_count INTEGER DEFAULT 0
);

CREATE TABLE messages (
  id TEXT PRIMARY KEY,
  session_id TEXT,
  role TEXT,
  content TEXT,
  tool_calls TEXT,
  metadata TEXT,
  created_at INTEGER
);

CREATE VIRTUAL TABLE messages_fts USING fts5(content, content='messages');

-- Scheduled tasks (TUI-embedded scheduler)
CREATE TABLE scheduled_tasks (
  id TEXT PRIMARY KEY,
  description TEXT NOT NULL,        -- What to do when triggered
  cron TEXT,                        -- Cron expression (optional)
  execute_at INTEGER,               -- Next execution timestamp (for one-time)
  interval_seconds INTEGER,         -- Recurring interval (optional)
  is_recurring BOOLEAN DEFAULT 0,
  is_enabled BOOLEAN DEFAULT 1,
  last_executed_at INTEGER,
  last_execution_status TEXT,       -- 'success' | 'failed' | null
  last_execution_output TEXT,       -- Output from last run
  created_at INTEGER,
  updated_at INTEGER
);

-- Task execution log
CREATE TABLE task_executions (
  id TEXT PRIMARY KEY,
  task_id TEXT,
  started_at INTEGER,
  completed_at INTEGER,
  status TEXT,                      -- 'success' | 'failed'
  output TEXT,                      -- Task output
  error TEXT,                       -- Error message if failed
  FOREIGN KEY (task_id) REFERENCES scheduled_tasks(id)
);

Scheduler Design (TUI-Embedded)

Philosophy

  • No separate service: TUI mode only, user must keep window open
  • Self-contained: Scheduler runs inside TUI process
  • Transparent: Task execution visible in chat flow
  • Context-aware: Tasks execute with full conversation context

Architecture

TUI Process
โ”œโ”€โ”€ UI Thread (pi-tui)
โ”‚   โ”œโ”€โ”€ Render chat
โ”‚   โ”œโ”€โ”€ Handle input
โ”‚   โ””โ”€โ”€ Display messages
โ”‚
โ””โ”€โ”€ Scheduler Thread
    โ”œโ”€โ”€ Timer (every 2s)
    โ”œโ”€โ”€ Check SQLite (scheduled_tasks table)
    โ””โ”€โ”€ Trigger task โ†’ Insert "wakeup" message

Wakeup Message Flow

// 1. Scheduler detects due task
const dueTasks = await scheduler.getDueTasks();
for (const task of dueTasks) {
  // 2. Insert wakeup message into conversation
  const wakeupMessage: Message = {
    role: 'system',
    content: `[Scheduled Task: ${task.description}]`,
    metadata: { type: 'wakeup', taskId: task.id }
  };
  
  // 3. Add to context (like user message)
  await conversation.addMessage(wakeupMessage);
  
  // 4. Notify UI to render
  tui.addSystemMessage(`โฐ Task: ${task.description}`);
  
  // 5. Trigger agent response (same as user input)
  await agent.handleWakeup(task);
}

TUI Display

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๐Ÿค– xz    gpt-4o-mini    โฐ Next: 09:00    ๐Ÿ“‹ 2 pending        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚  ๐Ÿง‘ You                                                      โ”‚
โ”‚  Check git status                                            โ”‚
โ”‚                                                              โ”‚
โ”‚  ๐Ÿค– Assistant                                                โ”‚
โ”‚  On branch main, nothing to commit...                        โ”‚
โ”‚                                                              โ”‚
โ”‚  โฐ System                                                   โ”‚
โ”‚  [Scheduled Task: Daily backup]                              โ”‚
โ”‚                                                              โ”‚
โ”‚  ๐Ÿค– Assistant                                                โ”‚
โ”‚  Starting daily backup...                                    โ”‚
โ”‚  [bash: ./scripts/backup.sh]                                 โ”‚
โ”‚  Backup completed: backup-2026-03-03.tar.gz                  โ”‚
โ”‚                                                              โ”‚
โ”‚  ๐Ÿง‘ You                                                      โ”‚
โ”‚  Thanks                                                      โ”‚
โ”‚                                                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ > _                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Scheduler API

interface ScheduledTask {
  id: string;
  description: string;           // Natural language instruction
  cron?: string;                 // "0 9 * * *" for 9am daily
  executeAt?: number;            // Unix timestamp for one-time
  intervalSeconds?: number;      // 3600 for hourly
  isRecurring: boolean;
  isEnabled: boolean;
  lastExecutedAt?: number;
  lastExecutionStatus?: 'success' | 'failed';
}

class Scheduler {
  private timer: NodeJS.Timer;
  private checkIntervalMs = 2000;  // 2 seconds
  
  // Start checking (called when TUI starts)
  start(): void {
    this.timer = setInterval(() => this.tick(), this.checkIntervalMs);
  }
  
  // Stop checking (called when TUI exits)
  stop(): void {
    clearInterval(this.timer);
  }
  
  // Check for due tasks
  private async tick(): Promise<void> {
    const now = Date.now();
    const dueTasks = await this.db.prepare(`
      SELECT * FROM scheduled_tasks
      WHERE is_enabled = 1
        AND (execute_at <= ? OR 
             (is_recurring = 1 AND 
              (last_executed_at IS NULL OR 
               last_executed_at + interval_seconds * 1000 <= ?)))
    `).all(now, now);
    
    for (const task of dueTasks) {
      await this.triggerTask(task);
    }
  }
  
  // Trigger task execution
  private async triggerTask(task: ScheduledTask): Promise<void> {
    // Update last_executed
    await this.db.prepare(
      'UPDATE scheduled_tasks SET last_executed_at = ? WHERE id = ?'
    ).run(Date.now(), task.id);
    
    // For one-time tasks, disable after execution
    if (!task.isRecurring) {
      await this.db.prepare(
        'UPDATE scheduled_tasks SET is_enabled = 0 WHERE id = ?'
      ).run(task.id);
    }
    
    // Notify agent via wakeup message
    this.onTaskDue?.(task);
  }
  
  // CRUD operations
  async addTask(task: Omit<ScheduledTask, 'id'>): Promise<string>;
  async removeTask(id: string): Promise<void>;
  async listTasks(): Promise<ScheduledTask[]>;
  async getNextTask(): Promise<ScheduledTask | null>;
  
  // Callback when task is due
  onTaskDue?: (task: ScheduledTask) => void;
}

Tool: schedule_task

const scheduleTaskTool: Tool = {
  name: 'schedule_task',
  description: 'Schedule a task for future execution. Tasks only run while TUI is open.',
  parameters: Type.Object({
    description: Type.String({ description: 'What to do when triggered' }),
    when: Type.String({ 
      description: 'When to execute. Supports: "HH:MM" (daily), "in X minutes/hours", ISO timestamp'
    }),
    recurring: Type.Optional(Type.String({ 
      enum: ['daily', 'hourly', 'none'],
      default: 'none'
    }))
  })
};

// Examples:
// schedule_task("Check emails", "09:00", "daily")
// schedule_task("Remind about meeting", "in 30 minutes", "none")
// schedule_task("Backup database", "2026-03-04T02:00:00+08:00", "none")

Important Notes

Limitations (by design):

  • Tasks only execute while TUI is running
  • If computer sleeps, tasks may be delayed
  • No persistent background service

Best practices:

  • Use for "while I'm working" reminders
  • Use for periodic maintenance during active sessions
  • For critical timed tasks, use system cron + xz once <task> pattern

Project Structure

xz/
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ tsconfig.json
โ”œโ”€โ”€ bin/
โ”‚   โ””โ”€โ”€ xz.js                   # CLI entry point
โ”‚
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ cli/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts            # CLI parser & commands
โ”‚   โ”‚   โ”œโ”€โ”€ memory.ts           # xz memory commands
โ”‚   โ”‚   โ”œโ”€โ”€ history.ts          # xz history commands
โ”‚   โ”‚   โ”œโ”€โ”€ skill.ts            # xz skill commands
โ”‚   โ”‚   โ””โ”€โ”€ schedule.ts         # xz schedule commands
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ tui/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts            # Interactive TUI mode
โ”‚   โ”‚   โ”œโ”€โ”€ app.ts
โ”‚   โ”‚   โ”œโ”€โ”€ chat.ts
โ”‚   โ”‚   โ””โ”€โ”€ input.ts
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ core/
โ”‚   โ”‚   โ”œโ”€โ”€ agent.ts            # Main agent orchestration
โ”‚   โ”‚   โ”œโ”€โ”€ llm.ts
โ”‚   โ”‚   โ”œโ”€โ”€ heartbeat.ts
โ”‚   โ”‚   โ””โ”€โ”€ scheduler.ts
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ config/                 # Configuration management
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts            # Config load/save + isFirstRun()
โ”‚   โ”‚   โ”œโ”€โ”€ types.ts            # XZConfig interface
โ”‚   โ”‚   โ”œโ”€โ”€ wizard.ts           # First-run setup wizard
โ”‚   โ”‚   โ”œโ”€โ”€ kimi.ts             # Kimi OAuth integration
โ”‚   โ”‚   โ””โ”€โ”€ validators.ts       # Config validation
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ identity/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ loader.ts           # Load SOUL.md, USER.md
โ”‚   โ”‚   โ””โ”€โ”€ builder.ts          # Build system prompt
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ knowledge/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ loader.ts           # Load MEMORY.md, daily logs
โ”‚   โ”‚   โ”œโ”€โ”€ chunker.ts          # 400-token chunks
โ”‚   โ”‚   โ”œโ”€โ”€ search.ts           # Hybrid search
โ”‚   โ”‚   โ””โ”€โ”€ manager.ts          # Read/append operations
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ history/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ database.ts         # SQLite connection
โ”‚   โ”‚   โ”œโ”€โ”€ session.ts          # Session CRUD
โ”‚   โ”‚   โ”œโ”€โ”€ messages.ts         # Message storage
โ”‚   โ”‚   โ”œโ”€โ”€ search.ts           # History search (BM25 + vector)
โ”‚   โ”‚   โ””โ”€โ”€ compaction.ts       # Session compaction
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ scheduler/              # TUI-embedded scheduler (2s tick)
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ database.ts         # Schedule schema in agent.db
โ”‚   โ”‚   โ”œโ”€โ”€ manager.ts          # Task CRUD
โ”‚   โ”‚   โ”œโ”€โ”€ runner.ts           # Execute due tasks
โ”‚   โ”‚   โ””โ”€โ”€ ticker.ts           # 2s interval checker
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ skills/
โ”‚   โ”‚   โ”œโ”€โ”€ index.ts
โ”‚   โ”‚   โ”œโ”€โ”€ loader.ts           # Load skills from .agents/skills/
โ”‚   โ”‚   โ”œโ”€โ”€ manager.ts          # Hot reload
โ”‚   โ”‚   โ”œโ”€โ”€ registry.ts         # Skill registry
โ”‚   โ”‚   โ””โ”€โ”€ builtin/            # Built-in skills (xz memory, xz history)
โ”‚   โ”‚       โ”œโ”€โ”€ memory-search/
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ SKILL.md
โ”‚   โ”‚       โ”œโ”€โ”€ history-search/
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ SKILL.md
โ”‚   โ”‚       โ””โ”€โ”€ get-session/
โ”‚   โ”‚           โ””โ”€โ”€ SKILL.md
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ tools/
โ”‚       โ”œโ”€โ”€ index.ts
โ”‚       โ”œโ”€โ”€ bash.ts             # Includes xz CLI invocation
โ”‚       โ”œโ”€โ”€ memory.ts           # memory_append
โ”‚       โ””โ”€โ”€ skill.ts            # Skill invocation
โ”‚
โ”œโ”€โ”€ .agents/                    # User's agent files
โ”‚   โ”œโ”€โ”€ SOUL.md
โ”‚   โ”œโ”€โ”€ USER.md
โ”‚   โ”œโ”€โ”€ MEMORY.md
โ”‚   โ”œโ”€โ”€ memory/
โ”‚   โ”‚   โ””โ”€โ”€ 2026-03-03.md
โ”‚   โ””โ”€โ”€ skills/                 # User's custom skills
โ”‚
โ”œโ”€โ”€ .claude/                    # Compatibility skills
โ”‚   โ””โ”€โ”€ skills/
โ”‚       โ””โ”€โ”€ git-helpers/
โ”‚           โ””โ”€โ”€ SKILL.md
โ”‚
โ””โ”€โ”€ data/
    โ””โ”€โ”€ agent.db                # SQLite: knowledge + history + schedule

Usage Examples

CLI Mode

# Search memory
$ xz memory search "git workflow" --limit 5
File                    Lines   Score   Snippet
memory/2026-03-03.md    15-22   0.92    User prefers short git log format...
memory/2026-03-02.md    8-15    0.85    Discussed git rebase vs merge...

Page 1/3 (12 total)
Use --page 2 for next

# Get specific content
$ xz memory get memory/2026-03-03.md --start-line 15 --end-line 22
## 09:15:00 - [preference] Git log format
User prefers `git log --oneline` for brevity.

# Search history
$ xz history search "database schema" --date-from 2026-02-01
Session                 Date                Role      Preview
2026-03-01-abc123       2026-03-01 14:23    user      Let's design the database...
2026-03-01-abc123       2026-03-01 14:24    assistant I suggest SQLite with...

# View session
$ xz history session 2026-03-01-abc123 --limit 20
[Session: Database Design Discussion]
14:23 user: Let's design the database schema...
14:24 assistant: I suggest SQLite with FTS5...
...
--offset 20 for more

# List sessions
$ xz history list --page 1
ID                      Title                   Messages  Date
2026-03-03-xyz789       Memory System Design    45        2026-03-03
2026-03-02-def456       Git Helpers Skill       23        2026-03-02

TUI Mode (Default)

# Start interactive session
$ xz

# In TUI:
> What are my git preferences?
๐Ÿค– Based on your pre-loaded MEMORY.md, you prefer short git logs.

> /memory-search "docker setup"
๐Ÿค– [bash: xz memory search "docker setup" --limit 5]
   Found: memory/2026-02-28.md mentions Docker setup...

> Show me what we discussed yesterday
๐Ÿค– [bash: xz history search "discussion" --date-from 2026-03-02 --limit 5]
   Found session 2026-03-02-def456: "Git Helpers Skill"
   
> /get-session 2026-03-02-def456
๐Ÿค– [bash: xz history session 2026-03-02-def456 --limit 20]
   [Shows full session content]

Scheduled Tasks (TUI Mode)

# List scheduled tasks
$ xz schedule list
ID          Description         Next Run        Recurring
backup      Daily backup        2026-03-04 02:00  daily
email-check Check emails        2026-03-03 09:00  daily

# Add task
$ xz schedule add "Daily backup" "02:00" --recurring daily
โœ… Task scheduled: backup

# Remove task
$ xz schedule remove backup
โœ… Task removed

In TUI - Task Execution:

# User has TUI open, working...
> Working on feature X...

โฐ System
[Scheduled Task: Check emails]

๐Ÿค– Assistant
Checking emails...
[bash: check-email.sh]
3 new emails. 1 requires attention: "Review requested for PR #42"

> Thanks, I'll review it later.

Important: Tasks only execute while TUI is running. If you close the window, tasks are delayed until next time you open xz.


Implementation Phases

Phase 1: Core + CLI Foundation

  • CLI framework (commander.js)
  • xz binary entry point
  • Config system (~/.xz/config.toml)
  • First-run setup wizard (model selection)
  • TUI with pi-tui (default mode)
  • Kimi Code OAuth ้›†ๆˆ๏ผˆ่ฏปๅ– ~/.kimi/credentials๏ผ‰

Phase 2: Identity & Pre-loading

  • SOUL.md, USER.md loading
  • System prompt builder
  • Pre-load into context

Phase 3: Knowledge Storage & CLI

  • Markdown memory files
  • Chunking & indexing
  • xz memory search command
  • xz memory get command

Phase 4: History Storage & CLI

  • SQLite schema
  • Session/message storage
  • xz history search command
  • xz history session/list commands

Phase 5: Search Skills

  • Built-in skills that wrap xz CLI
  • Skill: memory-search
  • Skill: history-search
  • Skill: get-session

Phase 6: Context Management

  • Compaction at 150K threshold
  • Optional pre-flush

Phase 7: TUI-Embedded Scheduler

  • SQLite schema for scheduled_tasks
  • 2s interval ticker in TUI
  • Wakeup message flow
  • schedule_task tool

Phase 8: Polish

  • Hot reload for skills
  • Pagination UX
  • Task execution logging

Kimi Code ่ฎข้˜…็”จๆˆท้…็ฝฎ

ๅฆ‚ๆžœไฝ ๅทฒ็ป่ฎข้˜…ไบ† Kimi Code๏ผŒๆœฌๅœฐๅทฒๆœ‰ kimi CLI ้…็ฝฎ๏ผŒๅฏไปฅ็›ดๆŽฅๅค็”จ่ฎค่ฏไฟกๆฏใ€‚

้…็ฝฎๆ–นๅผ

ๆ–นๅผ 1: ไฝฟ็”จ Kimi CLI ็š„ OAuth ๅ‡ญ่ฏ๏ผˆๆŽจ่๏ผ‰

Kimi CLI ๅทฒๅญ˜ๅ‚จ OAuth token ๅœจ ~/.kimi/credentials/ใ€‚

// src/config/kimi.ts
import { readFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';

export function getKimiCredentials() {
  const credPath = join(homedir(), '.kimi', 'credentials', 'kimi-code.json');
  const cred = JSON.parse(readFileSync(credPath, 'utf-8'));
  
  // ๆฃ€ๆŸฅ token ๆ˜ฏๅฆ่ฟ‡ๆœŸ
  if (cred.expires_at * 1000 < Date.now()) {
    // ้œ€่ฆไฝฟ็”จ refresh_token ๅˆทๆ–ฐ๏ผŒๆˆ–ๆ็คบ็”จๆˆท่ฟ่กŒ `kimi /login`
    throw new Error('Token expired. Run: kimi /login');
  }
  
  return {
    baseUrl: 'https://api.kimi.com/coding/v1',
    accessToken: cred.access_token,
    refreshToken: cred.refresh_token,
    expiresAt: cred.expires_at
  };
}

// ่Žทๅ– access token ็”จไบŽ API ่ฐƒ็”จ
export async function getKimiAccessToken(): Promise<string> {
  const cred = getKimiCredentials();
  return cred.accessToken;
}

ๆ–นๅผ 2: ็Žฏๅขƒๅ˜้‡้…็ฝฎ๏ผˆClaude Code ๅ…ผๅฎน๏ผ‰

ไปŽ Kimi Code ๆŽงๅˆถๅฐ่Žทๅ– API Key๏ผŒ็„ถๅŽ้…็ฝฎ็Žฏๅขƒๅ˜้‡๏ผš

# ~/.zshrc ๆˆ– ~/.bashrc
export ANTHROPIC_BASE_URL=https://api.kimi.com/coding/
export ANTHROPIC_AUTH_TOKEN=<ไฝ ็š„ Kimi Code API Key>
export ANTHROPIC_MODEL=kimi-for-coding
export ANTHROPIC_SMALL_FAST_MODEL=kimi-for-coding

ๆˆ–ไฝฟ็”จ kimi-for-coding ๆจกๅž‹๏ผš

# xz ๅฏๅŠจๆ—ถๅŠ ่ฝฝ
export XZ_PROVIDER=kimi
export XZ_MODEL=kimi-for-coding
export XZ_BASE_URL=https://api.kimi.com/coding/v1
export XZ_API_KEY=$(cat ~/.kimi/credentials/kimi-code.json | jq -r .access_token)

ๅœจ Plan ไธญไฝฟ็”จ

// src/llm.ts - ้…็ฝฎ pi-ai ไฝฟ็”จ Kimi
import { getModel } from '@mariozechner/pi-ai';
import { getKimiAccessToken } from './config/kimi';

export async function createKimiModel() {
  const accessToken = await getKimiAccessToken();
  
  // Kimi ไฝฟ็”จ OpenAI-compatible API
  const model = {
    id: 'kimi-for-coding',
    name: 'Kimi For Coding',
    api: 'openai-completions',
    provider: 'kimi',
    baseUrl: 'https://api.kimi.com/coding/v1',
    reasoning: true,
    input: ['text', 'image'],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, // ๅŒ…ๅซๅœจ่ฎข้˜…ไธญ
    contextWindow: 262144,  // 256K context
    maxTokens: 65536,
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  };
  
  return model;
}

Token ๅˆทๆ–ฐ

OAuth token ไผš่ฟ‡ๆœŸ๏ผŒ้œ€่ฆ่‡ชๅŠจๅˆทๆ–ฐ๏ผš

// src/config/kimi.ts
export async function refreshKimiToken(refreshToken: string): Promise<Credentials> {
  const response = await fetch('https://api.kimi.com/auth/token/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refresh_token: refreshToken })
  });
  
  const newCred = await response.json();
  
  // ไฟๅญ˜ๆ–ฐๅ‡ญ่ฏ
  saveCredentials(newCred);
  
  return newCred;
}

ๆฃ€ๆŸฅๆœฌๅœฐ่ฎค่ฏ็Šถๆ€

# ๆฃ€ๆŸฅ Kimi CLI ๆ˜ฏๅฆๅทฒ็™ปๅฝ•
$ xz auth check
โœ… Kimi Code OAuth found (~/.kimi/credentials/kimi-code.json)
   Expires: 2025-03-04 12:00:00 (23 hours remaining)
   
# ๆˆ–ๆ‰‹ๅŠจๆฃ€ๆŸฅ
$ ls ~/.kimi/credentials/kimi-code.json
$ cat ~/.kimi/credentials/kimi-code.json | jq '.expires_at'

ๆต‹่ฏ•้…็ฝฎ

# ๆ–นๅผ 1: ไฝฟ็”จๆœฌๅœฐ OAuth
xz --provider kimi --use-oauth

# ๆ–นๅผ 2: ไฝฟ็”จ็Žฏๅขƒๅ˜้‡
export XZ_PROVIDER=kimi
export XZ_MODEL=kimi-for-coding
xz

# ๆ–นๅผ 3: ๆ˜พๅผ API Key
xz --provider kimi --api-key <your-key>

่ฎข้˜…ๆƒ็›Š่ฏดๆ˜Ž

  • ๆจกๅž‹: kimi-for-coding (K2.5)
  • ไธŠไธ‹ๆ–‡: 256K tokens
  • ้ขๅบฆ: ๅ‘จๆœŸๆ€งๅˆทๆ–ฐ๏ผŒไธŽ Kimi Code ่ฎข้˜…ๅฅ—้ค็›ธๅ…ณ
  • ๅนถๅ‘: ๆœ€้ซ˜ 30 ๅนถๅ‘
  • ้€Ÿๅบฆ: ๆœ€้ซ˜ 100 tokens/s

Dependencies

{
  "name": "xz",
  "bin": {
    "xz": "./bin/xz.js"
  },
  "dependencies": {
    "@mariozechner/pi-ai": "^0.55.0",
    "@mariozechner/pi-tui": "^0.55.0",
    "@sinclair/typebox": "^0.34.0",
    "commander": "^12.0.0",
    "yaml": "^2.4.0",
    "better-sqlite3": "^9.0.0"
  },
  "optionalDependencies": {
    "sqlite-vec": "^0.1.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}