termux-dev 1.4.4 → 1.4.6

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/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.4.4</tspan>
28
+ THE TERMINAL-NATIVE AI CODING AGENT <tspan fill="#00f2fe" font-weight="bold">v1.4.6</tspan>
29
29
  </text>
30
30
 
31
31
  <!-- Top Accent Line -->
Binary file
@@ -18,7 +18,7 @@ async function pingUrl(url) {
18
18
  const req = https.get(url, {
19
19
  timeout: 3000,
20
20
  headers: {
21
- 'User-Agent': 'devx-doctor/1.4.4'
21
+ 'User-Agent': 'devx-doctor/1.4.6'
22
22
  }
23
23
  }, (res) => {
24
24
  res.resume(); // consume response data to free up memory and release socket
@@ -35,7 +35,7 @@ export async function runHeadlessMode(userPrompt, config, options = {}) {
35
35
  });
36
36
  try {
37
37
  if (!isQuiet && !isJson) {
38
- console.log(pc.bold(pc.cyan(`⚡ devx v1.4.4 (headless) | ${planMode ? 'PLAN' : 'AGENT'} | ${config.model}`)));
38
+ console.log(pc.bold(pc.cyan(`⚡ devx v1.4.6 (headless) | ${planMode ? 'PLAN' : 'AGENT'} | ${config.model}`)));
39
39
  console.log(pc.dim(`Task: ${userPrompt}\n`));
40
40
  }
41
41
  for await (const event of agent.run(abortController.signal)) {
package/dist/cli/index.js CHANGED
@@ -484,7 +484,7 @@ function drawLogo() {
484
484
  theme.colorFn(' █▀▀▄ █▀▀▀ █ █ █ █'),
485
485
  theme.colorFn(' █ █ █▀▀▀ ▀▄▀ ▀▄▀ '),
486
486
  theme.colorFn(' █▄▄▀ █▄▄▄ ▀ ▀ ▀ '),
487
- ' ' + theme.boldFn('v1.4.4'),
487
+ ' ' + theme.boldFn('v1.4.6'),
488
488
  ''
489
489
  ];
490
490
  for (const line of logo) {
@@ -499,7 +499,7 @@ function drawLogo() {
499
499
  indent + theme.colorFn('▀▀▀█▀▀▀ █▀▀▀ █▀▀█ █▄ ▄█ █ █ ▀▄ ▄▀ █▀▀▄ █▀▀▀ █ █'),
500
500
  indent + theme.colorFn(' █ █▀▀▀ █▄▄▀ █ █ █ █ █ █ ▀▀ █ █ █▀▀▀ █ █'),
501
501
  indent + theme.colorFn(' █ █▄▄▄ █ ▀▄ █ █ ▀▄▄▀ ▄▀ ▀▄ █▄▄▀ █▄▄▄ ▀▄▀ '),
502
- indent + theme.boldFn('v1.4.4'),
502
+ indent + theme.boldFn('v1.4.6'),
503
503
  ''
504
504
  ];
505
505
  for (const line of logo) {
@@ -645,7 +645,7 @@ async function handleSettings(config) {
645
645
  const maxIterLabel = maxIter >= 9999 ? 'Unlimited' : `${maxIter} steps`;
646
646
  const currentTh = getCurrentTheme();
647
647
  const choice = await select({
648
- message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.4 • theme: ${currentTh.name})`)}`,
648
+ message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.6 • theme: ${currentTh.name})`)}`,
649
649
  choices: [
650
650
  {
651
651
  name: `🎨 Color Theme: ${currentTh.emoji} ${currentTh.name}`,
@@ -686,7 +686,7 @@ async function handleSettings(config) {
686
686
  description: 'Limit how many tool steps (file edits, terminal commands) agent can do per request'
687
687
  },
688
688
  {
689
- name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.4 by ApvCode)')}`,
689
+ name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.6 by ApvCode)')}`,
690
690
  value: 'about',
691
691
  description: 'Terminal-Native AI Coding Agent created by ApvCode (https://github.com/apvcode/Termux-Dev)'
692
692
  },
@@ -702,7 +702,7 @@ async function handleSettings(config) {
702
702
  continue;
703
703
  }
704
704
  if (choice === 'about') {
705
- p.note(`⚡ devx v1.4.4 — Terminal-Native AI Coding Agent\n` +
705
+ p.note(`⚡ devx v1.4.6 — Terminal-Native AI Coding Agent\n` +
706
706
  `🎨 Theme: ${currentTh.emoji} ${currentTh.name}\n` +
707
707
  `👤 Author: ApvCode (https://github.com/apvcode)\n` +
708
708
  `🌟 Repository: https://github.com/apvcode/Termux-Dev\n` +
@@ -771,7 +771,7 @@ export async function main() {
771
771
  program
772
772
  .name('devx')
773
773
  .description('Terminal-native AI coding assistant and vibe-coding agent')
774
- .version('1.4.4')
774
+ .version('1.4.6')
775
775
  .option('-p, --prompt <task>', 'Run one-shot task non-interactively (headless mode)')
776
776
  .option('-y, --yolo', 'Automatically approve all tool executions without confirmation')
777
777
  .option('-m, --model <model>', 'Specify AI model to use for this execution')
@@ -42,8 +42,7 @@ export function askPrompt(opts = {}) {
42
42
  let input = opts.initialValue || '';
43
43
  let cursorPos = input.length;
44
44
  let selectedIndex = 0;
45
- let lastDropdownLines = 0;
46
- let lastCursorLine = 0;
45
+ let lastTotalLines = 0;
47
46
  let disposed = false;
48
47
  let historyIndex = GLOBAL_PROMPT_HISTORY.length;
49
48
  let tempDraft = '';
@@ -156,19 +155,49 @@ export function askPrompt(opts = {}) {
156
155
  function stripAnsi(str) {
157
156
  return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
158
157
  }
158
+ // Helper: clear all previously rendered lines and move cursor to line 0
159
+ function clearPreviousRender() {
160
+ if (lastTotalLines > 1) {
161
+ process.stdout.write(`\x1b[${lastTotalLines - 1}A`);
162
+ }
163
+ for (let i = 0; i < lastTotalLines; i++) {
164
+ process.stdout.write(`\r\x1b[2K`);
165
+ if (i < lastTotalLines - 1)
166
+ process.stdout.write('\n');
167
+ }
168
+ if (lastTotalLines > 1) {
169
+ process.stdout.write(`\x1b[${lastTotalLines - 1}A`);
170
+ }
171
+ }
159
172
  function render() {
160
173
  if (disposed)
161
174
  return;
162
175
  const items = getDropdownItems();
163
- const dropdownLines = [];
164
176
  const cols = Math.max(28, process.stdout.columns || 80);
165
177
  const rows = Math.max(8, process.stdout.rows || 24);
178
+ const prefixLen = 3;
179
+ const maxPerLine = Math.max(10, cols - prefixLen - 1);
180
+ const allLines = [];
181
+ let inputLineCount = 0;
182
+ if (input.length === 0) {
183
+ const displayPlace = placeholder.length > maxPerLine ? placeholder.slice(0, maxPerLine - 1) + '…' : placeholder;
184
+ allLines.push(pc.dim('│') + ' ' + pc.dim(displayPlace));
185
+ inputLineCount = 1;
186
+ }
187
+ else {
188
+ let pos = 0;
189
+ while (pos < input.length) {
190
+ const chunk = input.slice(pos, pos + maxPerLine);
191
+ allLines.push(pc.dim('│') + ' ' + formatInputWithBadges(chunk));
192
+ pos += maxPerLine;
193
+ }
194
+ inputLineCount = allLines.length;
195
+ }
166
196
  if (items.length > 0) {
167
197
  if (selectedIndex >= items.length)
168
198
  selectedIndex = 0;
169
199
  if (selectedIndex < 0)
170
200
  selectedIndex = items.length - 1;
171
- // Ensure inner box width strictly fits: innerBoxWidth + 5 <= cols - 2
172
201
  const innerBoxWidth = Math.max(16, Math.min(cols - 8, 54));
173
202
  const isNarrowOrShort = rows <= 16 || cols < 50;
174
203
  const pageSize = isNarrowOrShort
@@ -194,7 +223,7 @@ export function askPrompt(opts = {}) {
194
223
  const mid = Math.max(0, Math.floor(innerBoxWidth / 2) - 2);
195
224
  botBorderStr = '─'.repeat(mid) + ' ▼ ' + '─'.repeat(Math.max(0, innerBoxWidth - mid - 3));
196
225
  }
197
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╭' + topBorderStr + '╮'));
226
+ allLines.push(pc.dim('│') + ' ' + pc.dim('╭' + topBorderStr + '╮'));
198
227
  for (let i = startIndex; i < endIndex; i++) {
199
228
  const item = items[i];
200
229
  const isSelected = i === selectedIndex;
@@ -202,13 +231,11 @@ export function askPrompt(opts = {}) {
202
231
  const availWidth = innerBoxWidth - 2;
203
232
  let row = '';
204
233
  if (availWidth < 20) {
205
- // Very narrow mobile screen: show command name only
206
234
  const labelStr = item.label.length > availWidth ? item.label.slice(0, availWidth - 1) + '…' : item.label.padEnd(availWidth);
207
235
  const plain = pointer + labelStr;
208
236
  row = isSelected ? theme.badgeFn(plain) : (pointer + theme.boldFn(labelStr));
209
237
  }
210
238
  else {
211
- // Show command name and truncated description
212
239
  const labelMax = Math.min(15, Math.floor(availWidth * 0.45));
213
240
  const labelStr = item.label.length > labelMax ? item.label.slice(0, labelMax - 1) + '…' : item.label.padEnd(labelMax);
214
241
  const descMax = availWidth - labelMax - 1;
@@ -221,74 +248,37 @@ export function askPrompt(opts = {}) {
221
248
  row = `${pointer}${theme.boldFn(labelStr)} ${pc.gray(descStr)}`;
222
249
  }
223
250
  }
224
- // Safety guarantee: exact visible width must match innerBoxWidth
225
251
  const currentLen = stripAnsi(row).length;
226
252
  if (currentLen < innerBoxWidth) {
227
253
  row += ' '.repeat(innerBoxWidth - currentLen);
228
254
  }
229
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('│') + row + pc.dim('│'));
255
+ allLines.push(pc.dim('│') + ' ' + pc.dim('│') + row + pc.dim('│'));
230
256
  }
231
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╰' + botBorderStr + '╯'));
232
- }
233
- // Calculate cursor position
234
- // Calculate how many wrapped lines inputDisplay occupies
235
- const plainAll = stripAnsi(pc.dim('│') + ' ' + formatInputWithBadges(input));
236
- const allLines = plainAll.split('\n');
237
- let totalInputLines = 0;
238
- for (const l of allLines) {
239
- totalInputLines += l.length === 0 ? 1 : Math.max(1, Math.floor((l.length - 1) / cols) + 1);
240
- }
241
- // Calculate where the cursor is inside inputDisplay
242
- const plainBefore = stripAnsi(pc.dim('│') + ' ' + formatInputWithBadges(input.slice(0, cursorPos)));
243
- const linesBefore = plainBefore.split('\n');
244
- let curLine = 0;
245
- for (let i = 0; i < linesBefore.length - 1; i++) {
246
- const l = linesBefore[i];
247
- curLine += l.length === 0 ? 1 : Math.max(1, Math.floor((l.length - 1) / cols) + 1);
248
- }
249
- const lastLineSegment = linesBefore[linesBefore.length - 1];
250
- const wrapOffsetInLastLine = Math.floor(lastLineSegment.length / cols);
251
- curLine += wrapOffsetInLastLine;
252
- const curCol = lastLineSegment.length % cols;
253
- // 1. Single-line fast path (flicker-free atomic render)
254
- let inputDisplay = pc.dim('│') + ' ';
255
- if (input.length === 0) {
256
- const maxPlace = Math.max(12, cols - 8);
257
- const displayPlace = placeholder.length > maxPlace ? placeholder.slice(0, maxPlace - 1) + '…' : placeholder;
258
- inputDisplay += pc.dim(displayPlace);
259
- }
260
- else {
261
- inputDisplay += formatInputWithBadges(input);
262
- }
263
- if (totalInputLines === 1 && dropdownLines.length === 0 && lastCursorLine === 0 && lastDropdownLines === 0) {
264
- process.stdout.write(`\r\x1b[2K${inputDisplay}`);
265
- process.stdout.write(`\r\x1b[${curCol}C`);
266
- lastCursorLine = 0;
267
- lastDropdownLines = 0;
268
- return;
269
- }
270
- // 2. Multi-line / dropdown path: move to Line 0 and erase down
271
- if (lastCursorLine > 0) {
272
- process.stdout.write(`\x1b[${lastCursorLine}A`);
273
- }
274
- process.stdout.write('\r\x1b[J');
275
- process.stdout.write(inputDisplay);
276
- if (dropdownLines.length > 0) {
277
- for (const dl of dropdownLines) {
278
- process.stdout.write(`\n${dl}`);
257
+ allLines.push(pc.dim('│') + ' ' + pc.dim('╰' + botBorderStr + '╯'));
258
+ }
259
+ clearPreviousRender();
260
+ const maxLinesToWrite = Math.max(allLines.length, lastTotalLines);
261
+ for (let i = 0; i < maxLinesToWrite; i++) {
262
+ process.stdout.write('\r\x1b[2K');
263
+ if (i < allLines.length) {
264
+ process.stdout.write(allLines[i]);
265
+ }
266
+ if (i < maxLinesToWrite - 1) {
267
+ process.stdout.write('\n');
279
268
  }
280
269
  }
281
- const totalBottomLine = (totalInputLines - 1) + dropdownLines.length;
282
- const linesUp = totalBottomLine - curLine;
283
- if (linesUp > 0) {
284
- process.stdout.write(`\x1b[${linesUp}A`);
270
+ const cursorInputLine = input.length === 0 ? 0 : Math.min(Math.floor(cursorPos / maxPerLine), inputLineCount - 1);
271
+ const cursorCol = input.length === 0 ? prefixLen : prefixLen + (cursorPos % maxPerLine);
272
+ const currentLine = maxLinesToWrite - 1;
273
+ const moveUp = currentLine - cursorInputLine;
274
+ if (moveUp > 0) {
275
+ process.stdout.write(`\x1b[${moveUp}A`);
285
276
  }
286
277
  process.stdout.write('\r');
287
- if (curCol > 0) {
288
- process.stdout.write(`\x1b[${curCol}C`);
278
+ if (cursorCol > 0) {
279
+ process.stdout.write(`\x1b[${cursorCol}C`);
289
280
  }
290
- lastCursorLine = curLine;
291
- lastDropdownLines = dropdownLines.length;
281
+ lastTotalLines = allLines.length;
292
282
  }
293
283
  render();
294
284
  function cleanup() {
@@ -303,10 +293,8 @@ export function askPrompt(opts = {}) {
303
293
  process.stdin.pause();
304
294
  }
305
295
  function clearBoxAndExit(finalInput) {
306
- if (lastCursorLine > 0) {
307
- process.stdout.write(`\x1b[${lastCursorLine}A`);
308
- }
309
- process.stdout.write('\r\x1b[J');
296
+ clearPreviousRender();
297
+ process.stdout.write('\r\x1b[2K');
310
298
  const fullText = expandPastes(finalInput);
311
299
  if (fullText.trim()) {
312
300
  if (GLOBAL_PROMPT_HISTORY.length === 0 || GLOBAL_PROMPT_HISTORY[GLOBAL_PROMPT_HISTORY.length - 1] !== fullText.trim()) {
@@ -314,7 +302,7 @@ export function askPrompt(opts = {}) {
314
302
  }
315
303
  }
316
304
  const lines = fullText.split('\n');
317
- process.stdout.write(`\r\x1b[2K${pc.dim('│')} ${formatInputWithBadges(lines[0])}\n`);
305
+ process.stdout.write(`${pc.dim('│')} ${formatInputWithBadges(lines[0])}\n`);
318
306
  for (let i = 1; i < lines.length; i++) {
319
307
  process.stdout.write(pc.dim('│') + ' ' + lines[i] + '\n');
320
308
  }
@@ -440,10 +428,8 @@ export function askPrompt(opts = {}) {
440
428
  }
441
429
  // Ctrl+C
442
430
  if (str === '\x03' || (str.length === 1 && str.charCodeAt(0) === 3)) {
443
- if (lastCursorLine > 0) {
444
- process.stdout.write(`\x1b[${lastCursorLine}A`);
445
- }
446
- process.stdout.write('\r\x1b[J\n');
431
+ clearPreviousRender();
432
+ process.stdout.write('\r\x1b[2K\n');
447
433
  cleanup();
448
434
  resolve('__CANCEL__');
449
435
  return;
@@ -503,10 +489,8 @@ export function askPrompt(opts = {}) {
503
489
  render();
504
490
  }
505
491
  else {
506
- if (lastCursorLine > 0) {
507
- process.stdout.write(`\x1b[${lastCursorLine}A`);
508
- }
509
- process.stdout.write('\r\x1b[J');
492
+ clearPreviousRender();
493
+ process.stdout.write('\r\x1b[2K');
510
494
  cleanup();
511
495
  resolve(`__TOGGLE_MODE__:${input}`);
512
496
  }
@@ -139,7 +139,7 @@ function renderDirectoryHtml(dirPath, relPath, files, port) {
139
139
  ${parentLink}
140
140
  ${items || '<li style="padding: 20px; text-align: center; color: #6e7681;">No visible files in this directory</li>'}
141
141
  </ul>
142
- <div class="footer">devx v1.4.4 &bull; Terminal-Native AI Assistant</div>
142
+ <div class="footer">devx v1.4.6 &bull; Terminal-Native AI Assistant</div>
143
143
  </div>
144
144
  </body>
145
145
  </html>`;
@@ -63,7 +63,7 @@ export class MCPClient {
63
63
  capabilities: {},
64
64
  clientInfo: {
65
65
  name: 'devx',
66
- version: '1.4.4'
66
+ version: '1.4.6'
67
67
  }
68
68
  });
69
69
  if (!initResult) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termux-dev",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "Ultra-fast, terminal-native AI coding assistant and agent built for Android Termux, Windows, macOS, and Linux.",
5
5
  "main": "dist/cli/index.js",
6
6
  "type": "module",