termux-dev 1.4.17 → 1.4.18

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.17</tspan>
28
+ THE TERMINAL-NATIVE AI CODING AGENT <tspan fill="#00f2fe" font-weight="bold">v1.4.18</tspan>
29
29
  </text>
30
30
 
31
31
  <!-- Top Accent Line -->
@@ -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.17'
21
+ 'User-Agent': 'devx-doctor/1.4.18'
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.17 (headless) | ${planMode ? 'PLAN' : 'AGENT'} | ${config.model}`)));
38
+ console.log(pc.bold(pc.cyan(`⚡ devx v1.4.18 (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.17'),
487
+ ' ' + theme.boldFn('v1.4.18'),
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.17'),
502
+ indent + theme.boldFn('v1.4.18'),
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.17 • theme: ${currentTh.name})`)}`,
648
+ message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.18 • 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.17 by ApvCode)')}`,
689
+ name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.18 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.17 — Terminal-Native AI Coding Agent\n` +
705
+ p.note(`⚡ devx v1.4.18 — 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` +
@@ -798,7 +798,7 @@ export async function main() {
798
798
  program
799
799
  .name('devx')
800
800
  .description('Terminal-native AI coding assistant and vibe-coding agent')
801
- .version('1.4.16')
801
+ .version('1.4.18')
802
802
  .option('-p, --prompt <task>', 'Run one-shot task non-interactively (headless mode)')
803
803
  .option('-y, --yolo', 'Automatically approve all tool executions without confirmation')
804
804
  .option('-m, --model <model>', 'Specify AI model to use for this execution')
@@ -53,6 +53,7 @@ export function askPrompt(opts = {}) {
53
53
  let input = opts.initialValue || '';
54
54
  let cursorPos = input.length;
55
55
  let selectedIndex = 0;
56
+ let lastRenderedDropdownLines = 0;
56
57
  let historyIndex = GLOBAL_PROMPT_HISTORY.length;
57
58
  let tempDraft = '';
58
59
  const imageAttachments = [];
@@ -168,7 +169,76 @@ export function askPrompt(opts = {}) {
168
169
  if (disposed)
169
170
  return;
170
171
  const cols = Math.max(20, process.stdout.columns || 40);
172
+ const rows = Math.max(8, process.stdout.rows || 20);
171
173
  const items = getDropdownItems();
174
+ const dropdownLines = [];
175
+ if (items.length > 0) {
176
+ if (selectedIndex >= items.length)
177
+ selectedIndex = items.length - 1;
178
+ if (selectedIndex < 0)
179
+ selectedIndex = 0;
180
+ // Limit dropdown height gracefully so it always fits comfortably
181
+ const maxDropdownRows = Math.max(2, Math.min(rows - 3, 6));
182
+ const pageSize = Math.min(4, Math.max(2, Math.min(maxDropdownRows - 2, Math.floor(rows / 4))));
183
+ const innerBoxWidth = Math.max(10, Math.min(cols - 8, 48));
184
+ const total = items.length;
185
+ let startIndex = 0;
186
+ if (total > pageSize) {
187
+ if (selectedIndex >= pageSize) {
188
+ startIndex = Math.min(selectedIndex - pageSize + 1, total - pageSize);
189
+ }
190
+ }
191
+ const endIndex = Math.min(startIndex + pageSize, total);
192
+ const hasMoreUp = startIndex > 0;
193
+ const hasMoreDown = endIndex < total;
194
+ let topBorderStr = '─'.repeat(innerBoxWidth);
195
+ if (hasMoreUp) {
196
+ const mid = Math.max(0, Math.floor(innerBoxWidth / 2) - 2);
197
+ topBorderStr = '─'.repeat(mid) + ' ▲ ' + '─'.repeat(Math.max(0, innerBoxWidth - mid - 3));
198
+ }
199
+ let botBorderStr = '─'.repeat(innerBoxWidth);
200
+ if (hasMoreDown) {
201
+ const mid = Math.max(0, Math.floor(innerBoxWidth / 2) - 2);
202
+ botBorderStr = '─'.repeat(mid) + ' ▼ ' + '─'.repeat(Math.max(0, innerBoxWidth - mid - 3));
203
+ }
204
+ dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╭' + topBorderStr + '╮') + '\x1b[0m');
205
+ for (let i = startIndex; i < endIndex; i++) {
206
+ const item = items[i];
207
+ const isSelected = i === selectedIndex;
208
+ const pointer = isSelected ? '› ' : ' ';
209
+ const availWidth = innerBoxWidth;
210
+ let row = '';
211
+ if (availWidth < 20) {
212
+ const labelStr = item.label.length > availWidth - 2
213
+ ? item.label.slice(0, availWidth - 3) + '…'
214
+ : item.label.padEnd(availWidth - 2, ' ');
215
+ const plain = (pointer + labelStr).padEnd(availWidth, ' ').slice(0, availWidth);
216
+ row = isSelected
217
+ ? theme.badgeFn(plain) + '\x1b[0m'
218
+ : pointer + theme.boldFn(labelStr) + '\x1b[0m';
219
+ }
220
+ else {
221
+ const labelMax = Math.min(13, Math.max(6, Math.floor(availWidth * 0.35)));
222
+ const labelStr = item.label.length > labelMax
223
+ ? item.label.slice(0, labelMax - 1) + '…'
224
+ : item.label.padEnd(labelMax, ' ');
225
+ const descMax = Math.max(4, availWidth - labelMax - pointer.length - 1);
226
+ const descStr = item.desc.length > descMax
227
+ ? item.desc.slice(0, descMax - 1) + '…'
228
+ : item.desc.padEnd(descMax, ' ');
229
+ const plain = (pointer + labelStr + ' ' + descStr).padEnd(availWidth, ' ').slice(0, availWidth);
230
+ if (isSelected) {
231
+ row = theme.badgeFn(plain) + '\x1b[0m';
232
+ }
233
+ else {
234
+ row = `${pointer}${theme.boldFn(labelStr)} ${pc.gray(descStr)}\x1b[0m`;
235
+ }
236
+ }
237
+ dropdownLines.push(pc.dim('│') + ' ' + pc.dim('│') + row + pc.dim('│') + '\x1b[0m');
238
+ }
239
+ dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╰' + botBorderStr + '╯') + '\x1b[0m');
240
+ }
241
+ // 1. Format and write the Prompt line safely
172
242
  let inputDisplay = pc.dim('│') + ' ';
173
243
  let renderCursorCol = 3;
174
244
  if (input.length === 0) {
@@ -178,28 +248,9 @@ export function askPrompt(opts = {}) {
178
248
  renderCursorCol = 3;
179
249
  }
180
250
  else {
181
- if (selectedIndex >= items.length)
182
- selectedIndex = Math.max(0, items.length - 1);
183
- if (selectedIndex < 0)
184
- selectedIndex = 0;
185
- const selected = items.length > 0 ? items[selectedIndex] : null;
186
- // Calculate ghost autocomplete text & badge if available
187
- let ghostText = '';
188
- let hintBadge = '';
189
- if (selected) {
190
- if (input.startsWith('/') && selected.label.toLowerCase().startsWith(input.toLowerCase())) {
191
- ghostText = selected.label.slice(input.length);
192
- const remainingWidth = cols - (3 + input.length + ghostText.length + 8);
193
- if (remainingWidth > 12) {
194
- const maxDesc = Math.min(remainingWidth, 32);
195
- const descStr = selected.desc.length > maxDesc ? selected.desc.slice(0, maxDesc - 1) + '…' : selected.desc;
196
- hintBadge = ' ' + pc.dim('•') + ' ' + pc.gray(descStr);
197
- }
198
- }
199
- }
200
251
  const maxInputLen = Math.max(10, cols - 6);
201
252
  if (input.length <= maxInputLen) {
202
- inputDisplay += formatInputWithBadges(input) + (ghostText ? pc.dim(ghostText) : '') + hintBadge + '\x1b[0m';
253
+ inputDisplay += formatInputWithBadges(input) + '\x1b[0m';
203
254
  renderCursorCol = 3 + cursorPos;
204
255
  }
205
256
  else {
@@ -212,8 +263,33 @@ export function askPrompt(opts = {}) {
212
263
  renderCursorCol = 3 + (prefix ? 1 : 0) + (cursorPos - start);
213
264
  }
214
265
  }
215
- // Write prompt line cleanly (stays strictly on line 0, 0 scrolling, 0 vertical jumps)
216
- process.stdout.write(`\x1b[0m\r\x1b[2K${inputDisplay}\x1b[0m\r\x1b[${renderCursorCol}C`);
266
+ // Write prompt line
267
+ process.stdout.write(`\x1b[0m\r\x1b[2K${inputDisplay}\x1b[0m`);
268
+ // 2. Draw or update dropdown lines safely
269
+ if (dropdownLines.length > lastRenderedDropdownLines) {
270
+ // Allocate additional new lines that don't exist yet
271
+ for (let i = 0; i < lastRenderedDropdownLines; i++) {
272
+ process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K${dropdownLines[i]}\x1b[0m`);
273
+ }
274
+ for (let i = lastRenderedDropdownLines; i < dropdownLines.length; i++) {
275
+ process.stdout.write(`\n\x1b[0m\x1b[2K${dropdownLines[i]}\x1b[0m`);
276
+ }
277
+ process.stdout.write(`\x1b[${dropdownLines.length}A`);
278
+ lastRenderedDropdownLines = dropdownLines.length;
279
+ }
280
+ else if (lastRenderedDropdownLines > 0) {
281
+ // Redraw over existing lines without allocating new ones (0 scrolling!)
282
+ for (let i = 0; i < lastRenderedDropdownLines; i++) {
283
+ process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
284
+ if (i < dropdownLines.length) {
285
+ process.stdout.write(`${dropdownLines[i]}\x1b[0m`);
286
+ }
287
+ }
288
+ process.stdout.write(`\x1b[${lastRenderedDropdownLines}A`);
289
+ lastRenderedDropdownLines = dropdownLines.length;
290
+ }
291
+ // 3. Position cursor on the prompt line
292
+ process.stdout.write(`\r\x1b[${renderCursorCol}C`);
217
293
  }
218
294
  render();
219
295
  function cleanup() {
@@ -229,6 +305,13 @@ export function askPrompt(opts = {}) {
229
305
  process.stdin.pause();
230
306
  }
231
307
  function clearBoxAndExit(finalInput) {
308
+ if (lastRenderedDropdownLines > 0) {
309
+ for (let i = 0; i < lastRenderedDropdownLines; i++) {
310
+ process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
311
+ }
312
+ process.stdout.write(`\x1b[${lastRenderedDropdownLines}A`);
313
+ lastRenderedDropdownLines = 0;
314
+ }
232
315
  const fullText = expandPastes(finalInput);
233
316
  if (fullText.trim()) {
234
317
  if (GLOBAL_PROMPT_HISTORY.length === 0 ||
@@ -280,6 +363,13 @@ export function askPrompt(opts = {}) {
280
363
  }
281
364
  // Ctrl+C / SIGINT
282
365
  if (str === '\x03') {
366
+ if (lastRenderedDropdownLines > 0) {
367
+ for (let i = 0; i < lastRenderedDropdownLines; i++) {
368
+ process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
369
+ }
370
+ process.stdout.write(`\x1b[${lastRenderedDropdownLines}A`);
371
+ lastRenderedDropdownLines = 0;
372
+ }
283
373
  process.stdout.write(`\x1b[0m\r\x1b[2K\n`);
284
374
  cleanup();
285
375
  resolve('__CANCEL__');
@@ -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.17 &bull; Terminal-Native AI Assistant</div>
142
+ <div class="footer">devx v1.4.18 &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.17'
66
+ version: '1.4.18'
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.17",
3
+ "version": "1.4.18",
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",