termux-dev 1.2.2 → 1.4.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 CHANGED
@@ -85,6 +85,20 @@ npm install
85
85
  npm link
86
86
  ```
87
87
 
88
+ ### 🗑️ Complete Uninstallation
89
+
90
+ To completely remove `devx` along with all configuration, saved sessions, and data:
91
+
92
+ ```bash
93
+ # 1. Uninstall the global npm package
94
+ npm uninstall -g termux-dev
95
+
96
+ # 2. Remove configuration and session history (Linux / macOS / Termux)
97
+ rm -rf ~/.devx ~/.devxrc.json
98
+
99
+ # (Windows PowerShell)
100
+ Remove-Item -Recurse -Force "$HOME\.devx", "$HOME\.devxrc.json" -ErrorAction SilentlyContinue
101
+ ```
88
102
 
89
103
  ---
90
104
 
@@ -101,6 +115,11 @@ devx
101
115
  ```bash
102
116
  devx --plan
103
117
  ```
118
+ * Run in **Headless One-Shot Mode** (scripts, CI/CD, Termux:Widget):
119
+ ```bash
120
+ devx -p "review latest git diff and find security bugs"
121
+ devx -p "fix build errors" --yolo
122
+ ```
104
123
 
105
124
  ---
106
125
 
@@ -112,6 +131,11 @@ Type `/` in the prompt to open the autocomplete command palette:
112
131
  | :--- | :--- |
113
132
  | **`/plan`** | Switch to **PLAN** mode (architect & requirements planner) |
114
133
  | **`/agent`** | Switch to **AGENT** mode (coder & autonomous executor) |
134
+ | **`/mcp`** | Manage Model Context Protocol (MCP) servers & external tools (`/mcp reload`) |
135
+ | **`/usage`** | View live network bandwidth (KB/MB), token costs, and API request counts |
136
+ | **`/export`** | Export session conversation to a clean GitHub-Flavored Markdown transcript |
137
+ | **`/theme`** | Switch UI color theme (Cyan, Purple, Matrix, Amber, Crimson, Monochrome) |
138
+ | **`/doctor`** | Run environment health diagnostics (Node, Termux, Compilers, API keys) |
115
139
  | **`/image`** | Paste image from clipboard as `[1.png 203kb]` |
116
140
  | **`/serve [port]`** | Start local HTTP server for web/game preview (`/serve stop` to halt) |
117
141
  | **`/memory`** | View, add (`/memory add <fact>`), or clear project memory bank |
@@ -213,6 +237,20 @@ npm install
213
237
  npm link
214
238
  ```
215
239
 
240
+ ### 🗑️ Полное удаление
241
+
242
+ Чтобы полностью удалить `devx` вместе с конфигурацией, историей сессий и сохранёнными данными:
243
+
244
+ ```bash
245
+ # 1. Удаляем глобальный npm-пакет
246
+ npm uninstall -g termux-dev
247
+
248
+ # 2. Удаляем конфиг и историю сессий (Linux / macOS / Termux)
249
+ rm -rf ~/.devx ~/.devxrc.json
250
+
251
+ # (Windows PowerShell)
252
+ Remove-Item -Recurse -Force "$HOME\.devx", "$HOME\.devxrc.json" -ErrorAction SilentlyContinue
253
+ ```
216
254
 
217
255
  ---
218
256
 
@@ -229,6 +267,11 @@ devx
229
267
  ```bash
230
268
  devx --plan
231
269
  ```
270
+ * Запуск в **Headless One-Shot режиме** (скрипты, CI/CD, Termux:Widget):
271
+ ```bash
272
+ devx -p "проверь последний git diff и найди баги"
273
+ devx -p "исправь ошибки сборки" --yolo
274
+ ```
232
275
 
233
276
  ---
234
277
 
@@ -240,6 +283,11 @@ devx
240
283
  | :--- | :--- |
241
284
  | **`/plan`** | Переключиться в режим архитектора (**PLAN**) |
242
285
  | **`/agent`** | Переключиться в режим исполнителя (**AGENT**) |
286
+ | **`/mcp`** | Управление MCP-серверами и внешними инструментами (`/mcp reload`) |
287
+ | **`/usage`** | Показать сетевой трафик (КБ/МБ), токены, число запросов и расходы ($) |
288
+ | **`/export`** | Экспортировать сессию диалога в чистый GitHub Markdown файл |
289
+ | **`/theme`** | Сменить цветовую тему (Cyan, Purple, Matrix, Amber, Crimson, Monochrome) |
290
+ | **`/doctor`** | Запустить системную диагностику (Node, Termux, Компиляторы, Ключи) |
243
291
  | **`/image`** | Вставить картинку из буфера обмена как `[1.png 203kb]` |
244
292
  | **`/serve [порт]`** | Запустить локальный веб-сервер (`/serve stop` для остановки) |
245
293
  | **`/memory`** | Посмотреть, добавить (`/memory add <факт>`) или очистить банк памяти |
package/assets/banner.svg CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  <!-- Tagline & Badge -->
27
27
  <text x="425" y="142" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="600" fill="#8b949e" text-anchor="middle" letter-spacing="3">
28
- THE TERMINAL-NATIVE AI CODING AGENT <tspan fill="#00f2fe" font-weight="bold">v1.2.2</tspan>
28
+ THE TERMINAL-NATIVE AI CODING AGENT <tspan fill="#00f2fe" font-weight="bold">v1.4.0</tspan>
29
29
  </text>
30
30
 
31
31
  <!-- Top Accent Line -->
Binary file
@@ -15,8 +15,14 @@ function checkCmd(cmd) {
15
15
  }
16
16
  async function pingUrl(url) {
17
17
  return new Promise((resolve) => {
18
- const req = https.get(url, { timeout: 3000 }, (res) => {
19
- resolve(res.statusCode !== undefined);
18
+ const req = https.get(url, {
19
+ timeout: 3000,
20
+ headers: {
21
+ 'User-Agent': 'devx-doctor/1.4.0'
22
+ }
23
+ }, (res) => {
24
+ res.resume(); // consume response data to free up memory and release socket
25
+ resolve(res.statusCode !== undefined && res.statusCode < 500);
20
26
  });
21
27
  req.on('error', () => resolve(false));
22
28
  req.on('timeout', () => {
@@ -0,0 +1,62 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ export class SessionExporter {
4
+ static async exportToMarkdown(messages, model, targetFilename) {
5
+ try {
6
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
7
+ let fileName = targetFilename?.trim() || `devx-session-${timestamp.slice(0, 19)}.md`;
8
+ if (!fileName.endsWith('.md'))
9
+ fileName += '.md';
10
+ const filePath = path.resolve(process.cwd(), fileName);
11
+ const lines = [
12
+ `# 📝 devx Session Transcript`,
13
+ ``,
14
+ `- **Date:** ${new Date().toLocaleString()}`,
15
+ `- **Model:** \`${model}\``,
16
+ `- **Total Messages:** ${messages.length}`,
17
+ `- **Working Directory:** \`${process.cwd()}\``,
18
+ ``,
19
+ `---`,
20
+ ``
21
+ ];
22
+ for (const msg of messages) {
23
+ if (msg.role === 'system')
24
+ continue; // Skip raw system prompt
25
+ if (msg.role === 'user') {
26
+ lines.push(`## 👤 User\n`);
27
+ lines.push(msg.content || '');
28
+ if (msg.images && msg.images.length > 0) {
29
+ lines.push(`\n*Attached Images: ${msg.images.map(i => i.path).join(', ')}*`);
30
+ }
31
+ lines.push(`\n---\n`);
32
+ }
33
+ else if (msg.role === 'assistant') {
34
+ lines.push(`## 🤖 Assistant\n`);
35
+ if (msg.content) {
36
+ lines.push(msg.content);
37
+ }
38
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
39
+ lines.push(`\n**Executed Tools:**`);
40
+ for (const tc of msg.tool_calls) {
41
+ lines.push(`- \`${tc.name}\`: \`\`\`json\n${JSON.stringify(tc.arguments, null, 2)}\n\`\`\``);
42
+ }
43
+ }
44
+ lines.push(`\n---\n`);
45
+ }
46
+ else if (msg.role === 'tool') {
47
+ lines.push(`> **Tool Result (${msg.name || 'tool'}):**\n`);
48
+ let displayContent = msg.content || '';
49
+ if (displayContent.length > 2000) {
50
+ displayContent = displayContent.slice(0, 2000) + '\n... [truncated]';
51
+ }
52
+ lines.push('```\n' + displayContent + '\n```\n');
53
+ }
54
+ }
55
+ await fs.writeFile(filePath, lines.join('\n'), 'utf8');
56
+ return { success: true, filePath };
57
+ }
58
+ catch (err) {
59
+ return { success: false, filePath: '', error: err.message };
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,110 @@
1
+ import pc from 'picocolors';
2
+ import { History } from '../core/history.js';
3
+ import { Agent } from '../core/loop.js';
4
+ import { buildSystemPrompt } from '../prompts/builder.js';
5
+ import { createProvider } from '../providers/index.js';
6
+ import { getTools } from '../tools/index.js';
7
+ import { CLIConsoleGuard } from '../permissions/guard.js';
8
+ import { globalSnapshotManager } from '../core/snapshot.js';
9
+ import { UsageTracker } from '../core/usage.js';
10
+ export async function runHeadlessMode(userPrompt, config, options = {}) {
11
+ const planMode = !!options.planMode;
12
+ const isYolo = !!options.yolo || !!config.autoApprove;
13
+ const isQuiet = !!options.quiet;
14
+ const isJson = !!options.json;
15
+ let history = new History();
16
+ const sysPrompt = await buildSystemPrompt(planMode);
17
+ history.addMessage({ role: 'system', content: sysPrompt });
18
+ history.addMessage({ role: 'user', content: userPrompt });
19
+ const provider = createProvider(config);
20
+ const tools = getTools(planMode);
21
+ const guard = new CLIConsoleGuard(isYolo, config.bashAllowlist || []);
22
+ const agent = new Agent(config, provider, tools, history, guard);
23
+ const executedTools = [];
24
+ let accumulatedText = '';
25
+ let accumulatedReasoning = '';
26
+ let isFatalError = false;
27
+ let errorMessage = '';
28
+ const abortController = new AbortController();
29
+ process.on('SIGINT', () => {
30
+ abortController.abort();
31
+ if (!isJson) {
32
+ console.error(pc.yellow('\n[devx headless] Aborted by user (SIGINT)'));
33
+ }
34
+ process.exit(130);
35
+ });
36
+ try {
37
+ if (!isQuiet && !isJson) {
38
+ console.log(pc.bold(pc.cyan(`⚡ devx v1.4.0 (headless) | ${planMode ? 'PLAN' : 'AGENT'} | ${config.model}`)));
39
+ console.log(pc.dim(`Task: ${userPrompt}\n`));
40
+ }
41
+ for await (const event of agent.run(abortController.signal)) {
42
+ if (event.type === 'reasoning_delta') {
43
+ accumulatedReasoning += event.delta;
44
+ if (!isQuiet && !isJson) {
45
+ process.stdout.write(pc.dim(event.delta));
46
+ }
47
+ }
48
+ else if (event.type === 'text_delta') {
49
+ accumulatedText += event.delta;
50
+ if (!isJson) {
51
+ process.stdout.write(event.delta);
52
+ }
53
+ }
54
+ else if (event.type === 'tool_start') {
55
+ if (!isQuiet && !isJson) {
56
+ console.log('\n' + pc.bold(pc.white(`› ${event.actionDesc}`)));
57
+ }
58
+ }
59
+ else if (event.type === 'tool_end') {
60
+ executedTools.push({ id: event.id, name: event.name, result: event.result });
61
+ }
62
+ else if (event.type === 'error') {
63
+ errorMessage = event.message;
64
+ if (event.isFatal) {
65
+ isFatalError = true;
66
+ break;
67
+ }
68
+ }
69
+ }
70
+ globalSnapshotManager.finishTurn();
71
+ if (isJson) {
72
+ const summary = UsageTracker.getInstance().getSummary();
73
+ const output = {
74
+ success: !isFatalError,
75
+ model: config.model,
76
+ prompt: userPrompt,
77
+ result: accumulatedText,
78
+ reasoning: accumulatedReasoning || undefined,
79
+ executedTools,
80
+ usage: {
81
+ requests: summary.requestsCount,
82
+ totalTokens: summary.totalTokens,
83
+ cost: summary.totalCost,
84
+ totalBytes: summary.totalBytes
85
+ },
86
+ error: isFatalError ? errorMessage : undefined
87
+ };
88
+ console.log(JSON.stringify(output, null, 2));
89
+ }
90
+ else {
91
+ if (accumulatedText && !accumulatedText.endsWith('\n')) {
92
+ process.stdout.write('\n');
93
+ }
94
+ if (isFatalError) {
95
+ console.error(pc.red(`\n❌ Error: ${errorMessage}`));
96
+ return 1;
97
+ }
98
+ }
99
+ return isFatalError ? 1 : 0;
100
+ }
101
+ catch (err) {
102
+ if (isJson) {
103
+ console.log(JSON.stringify({ success: false, error: err.message }, null, 2));
104
+ }
105
+ else {
106
+ console.error(pc.red(`\n❌ Fatal Headless Execution Error: ${err.message}`));
107
+ }
108
+ return 1;
109
+ }
110
+ }