@exreve/exk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # TalkToCode CLI
2
+
3
+ CLI tool for interacting with TalkToCode backend.
4
+
5
+ ## Quick install (one-liner)
6
+
7
+ **Linux / macOS:**
8
+ ```bash
9
+ curl -s https://api.talk-to-code.com/sh?email=YOUR_EMAIL | bash
10
+ ```
11
+
12
+ ## Installation (from source)
13
+
14
+ ```bash
15
+ cd cli
16
+ npm install
17
+ npm run build
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### One-time Commands
23
+
24
+ ```bash
25
+ # Configure API URL
26
+ node dist/index.js config --api-url https://api.talk-to-code.com
27
+ node dist/index.js config # show current config
28
+
29
+ # Register device (one-time)
30
+ node dist/index.js register "My Device Name"
31
+
32
+ # List sessions
33
+ node dist/index.js sessions
34
+
35
+ # Create session
36
+ node dist/index.js spawn
37
+
38
+ # Send prompt to session
39
+ node dist/index.js send <session-id> "Your prompt here"
40
+
41
+ # Monitor session
42
+ node dist/index.js monitor <session-id>
43
+
44
+ # List devices
45
+ node dist/index.js devices
46
+ ```
47
+
48
+ ### Daemon Mode (Background Service)
49
+
50
+ To run the CLI as a background daemon that stays connected:
51
+
52
+ ```bash
53
+ # Run daemon manually (for testing)
54
+ node dist/index.js daemon
55
+
56
+ # Or install as pm2 process (ttc install, or manually):
57
+ ./install-service.sh
58
+
59
+ # After installation:
60
+ pm2 status # list processes
61
+ pm2 logs ttc_cli # view logs
62
+ pm2 restart ttc_cli # restart
63
+
64
+ # To uninstall:
65
+ ttc uninstall
66
+ # or: ./install-service.sh --uninstall
67
+ ```
68
+
69
+ ## Daemon Behavior
70
+
71
+ The daemon:
72
+ - Connects to the backend and stays connected
73
+ - Registers the device on connection
74
+ - Updates `lastSeen` every 30 seconds (heartbeat)
75
+ - Automatically reconnects on disconnect
76
+ - Handles graceful shutdown (SIGTERM/SIGINT)
77
+
78
+ ## Configuration
79
+
80
+ Configuration is stored in `~/.claude-voice/config.json`:
81
+
82
+ ```json
83
+ {
84
+ "apiUrl": "https://api.talk-to-code.com"
85
+ }
86
+ ```
87
+
88
+ Device ID is stored in `~/.talk-to-code/device-id.json` and is generated automatically on first run.
89
+
90
+ ## Memory Feature
91
+
92
+ This CLI includes **persistent memory** using local embeddings:
93
+
94
+ - **Model**: `Xenova/bge-small-en-v1.5` (MTEB score 68.1, 384 dimensions)
95
+ - **100% Local**: Runs in-process, no API keys needed
96
+ - **Per-Project**: Each project stores memories in `.claude-memory/memories.jsonl`
97
+ - **First Run**: Downloads ~130MB model (cached in `~/.cache/huggingface/`)
98
+
99
+ ### Disable Memory
100
+
101
+ ```bash
102
+ export ENABLE_MEMORY=false
103
+ ```
104
+
105
+ ### Clear Project Memory
106
+
107
+ ```bash
108
+ rm -rf .claude-memory/memories.jsonl
109
+ ```
package/agentLogger.ts ADDED
@@ -0,0 +1,162 @@
1
+ import fs from 'fs/promises'
2
+ import path from 'path'
3
+ import os from 'os'
4
+
5
+ const LOGS_DIR = path.join(os.homedir(), '.talk-to-code', 'agent-logs')
6
+
7
+ const ensureLogsDir = () => fs.mkdir(LOGS_DIR, { recursive: true })
8
+
9
+ const getLogFilePath = (sessionId: string) => path.join(LOGS_DIR, `session-${sessionId}.log`)
10
+
11
+ const formatTimestamp = () => new Date().toISOString()
12
+
13
+ async function writeLog(sessionId: string, level: 'INFO' | 'ERROR' | 'WARN' | 'DEBUG', message: string, data?: any): Promise<void> {
14
+ try {
15
+ await ensureLogsDir()
16
+ const logFile = getLogFilePath(sessionId)
17
+ const dataStr = data ? ` | Data: ${JSON.stringify(data)}` : ''
18
+ await fs.appendFile(logFile, `[${formatTimestamp()}] [${level}] ${message}${dataStr}\n`, 'utf-8')
19
+ } catch (error) {
20
+ console.error(`Failed to write log: ${error}`)
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Agent session logger
26
+ */
27
+ export class AgentLogger {
28
+ private sessionId: string
29
+
30
+ constructor(sessionId: string) {
31
+ this.sessionId = sessionId
32
+ }
33
+
34
+ async info(message: string, data?: any): Promise<void> {
35
+ await writeLog(this.sessionId, 'INFO', message, data)
36
+ const dataStr = data ? ` | ${JSON.stringify(data).substring(0, 200)}` : ''
37
+ console.log(`[Agent] [${this.sessionId}] ${message}${dataStr}`)
38
+ }
39
+
40
+ async error(message: string, error?: any): Promise<void> {
41
+ const errorData = error instanceof Error
42
+ ? { message: error.message, stack: error.stack, name: error.name }
43
+ : error
44
+ await writeLog(this.sessionId, 'ERROR', message, errorData)
45
+ console.error(`[Agent] [${this.sessionId}] ✗ ERROR: ${message}`)
46
+ if (error instanceof Error) {
47
+ console.error(` → ${error.message}`)
48
+ if (error.stack && process.env.DEBUG_AGENT) {
49
+ console.error(` Stack: ${error.stack}`)
50
+ }
51
+ } else if (error) {
52
+ console.error(` → ${JSON.stringify(error).substring(0, 500)}`)
53
+ }
54
+ }
55
+
56
+ async warn(message: string, data?: any): Promise<void> {
57
+ await writeLog(this.sessionId, 'WARN', message, data)
58
+ const dataStr = data ? ` | ${JSON.stringify(data).substring(0, 200)}` : ''
59
+ console.warn(`[Agent] [${this.sessionId}] ⚠ WARN: ${message}${dataStr}`)
60
+ }
61
+
62
+ async debug(message: string, data?: any): Promise<void> {
63
+ await writeLog(this.sessionId, 'DEBUG', message, data)
64
+ if (process.env.DEBUG_AGENT) {
65
+ const dataStr = data ? ` | ${JSON.stringify(data).substring(0, 200)}` : ''
66
+ console.log(`[Agent] [${this.sessionId}] 🔍 DEBUG: ${message}${dataStr}`)
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Log prompt processing start
72
+ */
73
+ async logPromptStart(prompt: string, projectPath: string): Promise<void> {
74
+ await this.info('📤 Prompt processing started', {
75
+ promptLength: prompt.length,
76
+ promptPreview: prompt.substring(0, 200),
77
+ projectPath
78
+ })
79
+ console.log(`[Agent] [${this.sessionId}] 📤 Processing prompt (${prompt.length} chars)`)
80
+ console.log(`[Agent] [${this.sessionId}] Preview: ${prompt.substring(0, 150)}${prompt.length > 150 ? '...' : ''}`)
81
+ }
82
+
83
+ async logPromptEnd(exitCode: number | null, duration: number, tokenUsage?: { inputTokens: number; outputTokens: number; costUsd: number }): Promise<void> {
84
+ const durationSec = (duration / 1000).toFixed(2)
85
+ const status = exitCode === 0 || exitCode === null ? '✓' : '✗'
86
+ await this.info('Prompt processing completed', {
87
+ exitCode,
88
+ durationMs: duration,
89
+ durationSeconds: durationSec,
90
+ tokenUsage
91
+ })
92
+ console.log(`[Agent] [${this.sessionId}] ${status} Prompt completed in ${durationSec}s (exit: ${exitCode ?? 'null'})`)
93
+ if (tokenUsage) {
94
+ console.log(`[Agent] [${this.sessionId}] Tokens: ${tokenUsage.inputTokens} in + ${tokenUsage.outputTokens} out = ${tokenUsage.inputTokens + tokenUsage.outputTokens} total`)
95
+ console.log(`[Agent] [${this.sessionId}] Cost: $${tokenUsage.costUsd.toFixed(6)}`)
96
+ }
97
+ }
98
+
99
+ async logToolCall(toolName: string, toolData: any): Promise<void> {
100
+ await this.debug('Tool called', {
101
+ toolName,
102
+ toolData: typeof toolData === 'string' ? toolData.substring(0, 500) : toolData
103
+ })
104
+ console.log(`[Agent] [${this.sessionId}] 🔧 Tool: ${toolName}`)
105
+ }
106
+
107
+ async logToolResult(toolName: string, result: any, success: boolean): Promise<void> {
108
+ await this.debug('Tool result', {
109
+ toolName,
110
+ success,
111
+ resultPreview: typeof result === 'string' ? result.substring(0, 500) : JSON.stringify(result).substring(0, 500)
112
+ })
113
+ const status = success ? '✓' : '✗'
114
+ const resultPreview = typeof result === 'string'
115
+ ? result.substring(0, 100)
116
+ : JSON.stringify(result).substring(0, 100)
117
+ const resultLen = typeof result === 'string' ? result.length : JSON.stringify(result).length
118
+ console.log(`[Agent] [${this.sessionId}] ${status} Tool result (${toolName}): ${resultPreview}${resultLen > 100 ? '...' : ''}`)
119
+ }
120
+
121
+ async logAgentResponse(response: string, metadata?: any): Promise<void> {
122
+ await this.info('Agent response', {
123
+ responseLength: response.length,
124
+ responsePreview: response.substring(0, 500),
125
+ metadata
126
+ })
127
+ }
128
+
129
+ async logSessionCreated(projectPath: string): Promise<void> {
130
+ await this.info('Session created', { projectPath })
131
+ console.log(`[Agent] [${this.sessionId}] 🚀 Session created`)
132
+ console.log(`[Agent] [${this.sessionId}] Project: ${projectPath}`)
133
+ }
134
+
135
+ async logSessionError(error: string | Error, context?: any): Promise<void> {
136
+ const errorMessage = error instanceof Error ? error.message : error
137
+ await this.error('Session error', {
138
+ error: errorMessage,
139
+ stack: error instanceof Error ? error.stack : undefined,
140
+ context
141
+ })
142
+ }
143
+
144
+ async logClaudeSessionId(sessionId: string): Promise<void> {
145
+ await this.info('Claude SDK session ID captured', { claudeSessionId: sessionId })
146
+ console.log(`[Agent] [${this.sessionId}] 🔗 Claude SDK session ID: ${sessionId.substring(0, 20)}...`)
147
+ }
148
+
149
+ async logModelConfig(model: string, baseUrl?: string): Promise<void> {
150
+ await this.info('Model configuration', { model, baseUrl })
151
+ console.log(`[Agent] [${this.sessionId}] ⚙️ Model: ${model}`)
152
+ if (baseUrl) {
153
+ console.log(`[Agent] [${this.sessionId}] Base URL: ${baseUrl}`)
154
+ }
155
+ }
156
+
157
+ async logTokenUsage(usage: { inputTokens: number; outputTokens: number; totalTokens: number; costUsd: number }): Promise<void> {
158
+ await this.info('Token usage', usage)
159
+ console.log(`[Agent] [${this.sessionId}] 📊 Token usage: ${usage.inputTokens} in + ${usage.outputTokens} out = ${usage.totalTokens} total`)
160
+ console.log(`[Agent] [${this.sessionId}] Cost: $${usage.costUsd.toFixed(6)}`)
161
+ }
162
+ }