@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 +109 -0
- package/agentLogger.ts +162 -0
- package/agentSession.ts +1176 -0
- package/app-child.ts +2769 -0
- package/appManager.ts +275 -0
- package/appRunner.ts +475 -0
- package/bin/exk +45 -0
- package/container-entrypoint.sh +177 -0
- package/index.ts +2798 -0
- package/install-service.sh +122 -0
- package/moduleMcpServer.ts +131 -0
- package/package.json +67 -0
- package/projectAnalyzer.ts +341 -0
- package/projectManager.ts +111 -0
- package/runnerGenerator.ts +218 -0
- package/shared/types.ts +488 -0
- package/skills/code-review.md +49 -0
- package/skills/front-glass.md +36 -0
- package/skills/frontend-design.md +41 -0
- package/skills/index.ts +151 -0
- package/tsconfig.json +22 -0
- package/updater.ts +512 -0
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
|
+
}
|