termux-dev 1.4.16 → 1.4.17
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 +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/headless.js +1 -1
- package/dist/cli/index (# Edit conflict 2026-08-29 pe1jzcC #).js +1966 -0
- package/dist/cli/index.js +5 -5
- package/dist/cli/prompt.js +37 -122
- package/dist/cli/server.js +1 -1
- package/dist/mcp/client.js +1 -1
- package/package.json +1 -1
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.
|
|
487
|
+
' ' + theme.boldFn('v1.4.17'),
|
|
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.
|
|
502
|
+
indent + theme.boldFn('v1.4.17'),
|
|
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.
|
|
648
|
+
message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.17 • 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.
|
|
689
|
+
name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.17 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.
|
|
705
|
+
p.note(`⚡ devx v1.4.17 — 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` +
|
package/dist/cli/prompt.js
CHANGED
|
@@ -53,7 +53,6 @@ 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;
|
|
57
56
|
let historyIndex = GLOBAL_PROMPT_HISTORY.length;
|
|
58
57
|
let tempDraft = '';
|
|
59
58
|
const imageAttachments = [];
|
|
@@ -169,78 +168,7 @@ export function askPrompt(opts = {}) {
|
|
|
169
168
|
if (disposed)
|
|
170
169
|
return;
|
|
171
170
|
const cols = Math.max(20, process.stdout.columns || 40);
|
|
172
|
-
const rows = Math.max(8, process.stdout.rows || 20);
|
|
173
171
|
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
|
-
// On very small screens (keyboard open on Android), limit dropdown aggressively
|
|
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
|
-
// === SAFE RENDERING: Use \x1b7/\x1b8 save/restore + \x1b[B (down) instead of \n ===
|
|
242
|
-
// This prevents hardware scroll when cursor is at the bottom of a small screen.
|
|
243
|
-
// 1. Format and write the Prompt line safely (guaranteed <= cols - 1 chars)
|
|
244
172
|
let inputDisplay = pc.dim('│') + ' ';
|
|
245
173
|
let renderCursorCol = 3;
|
|
246
174
|
if (input.length === 0) {
|
|
@@ -250,9 +178,28 @@ export function askPrompt(opts = {}) {
|
|
|
250
178
|
renderCursorCol = 3;
|
|
251
179
|
}
|
|
252
180
|
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
|
+
}
|
|
253
200
|
const maxInputLen = Math.max(10, cols - 6);
|
|
254
201
|
if (input.length <= maxInputLen) {
|
|
255
|
-
inputDisplay += formatInputWithBadges(input) + '\x1b[0m';
|
|
202
|
+
inputDisplay += formatInputWithBadges(input) + (ghostText ? pc.dim(ghostText) : '') + hintBadge + '\x1b[0m';
|
|
256
203
|
renderCursorCol = 3 + cursorPos;
|
|
257
204
|
}
|
|
258
205
|
else {
|
|
@@ -265,26 +212,8 @@ export function askPrompt(opts = {}) {
|
|
|
265
212
|
renderCursorCol = 3 + (prefix ? 1 : 0) + (cursorPos - start);
|
|
266
213
|
}
|
|
267
214
|
}
|
|
268
|
-
// Write prompt line (
|
|
269
|
-
process.stdout.write(`\x1b[0m\r\x1b[2K${inputDisplay}\x1b[0m`);
|
|
270
|
-
// 2. Draw dropdown below using \x1b[1B (cursor down) + \r without scrolling viewport
|
|
271
|
-
const maxDropdowns = Math.max(dropdownLines.length, lastRenderedDropdownLines);
|
|
272
|
-
if (maxDropdowns > 0) {
|
|
273
|
-
// Save cursor position on the prompt line
|
|
274
|
-
process.stdout.write('\x1b7');
|
|
275
|
-
for (let i = 0; i < maxDropdowns; i++) {
|
|
276
|
-
// Move to next line: \x1b[1B moves down 1 without scrolling, \r returns to col 0, \x1b[2K clears line
|
|
277
|
-
process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
|
|
278
|
-
if (i < dropdownLines.length) {
|
|
279
|
-
process.stdout.write(dropdownLines[i] + '\x1b[0m');
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Restore cursor position (back to prompt line)
|
|
283
|
-
process.stdout.write('\x1b8');
|
|
284
|
-
}
|
|
285
|
-
// 3. Place cursor precisely on the prompt line
|
|
286
|
-
process.stdout.write(`\r\x1b[${renderCursorCol}C`);
|
|
287
|
-
lastRenderedDropdownLines = dropdownLines.length;
|
|
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`);
|
|
288
217
|
}
|
|
289
218
|
render();
|
|
290
219
|
function cleanup() {
|
|
@@ -300,14 +229,6 @@ export function askPrompt(opts = {}) {
|
|
|
300
229
|
process.stdin.pause();
|
|
301
230
|
}
|
|
302
231
|
function clearBoxAndExit(finalInput) {
|
|
303
|
-
if (lastRenderedDropdownLines > 0) {
|
|
304
|
-
process.stdout.write('\x1b7');
|
|
305
|
-
for (let i = 0; i < lastRenderedDropdownLines; i++) {
|
|
306
|
-
process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
|
|
307
|
-
}
|
|
308
|
-
process.stdout.write('\x1b8');
|
|
309
|
-
lastRenderedDropdownLines = 0;
|
|
310
|
-
}
|
|
311
232
|
const fullText = expandPastes(finalInput);
|
|
312
233
|
if (fullText.trim()) {
|
|
313
234
|
if (GLOBAL_PROMPT_HISTORY.length === 0 ||
|
|
@@ -359,14 +280,6 @@ export function askPrompt(opts = {}) {
|
|
|
359
280
|
}
|
|
360
281
|
// Ctrl+C / SIGINT
|
|
361
282
|
if (str === '\x03') {
|
|
362
|
-
if (lastRenderedDropdownLines > 0) {
|
|
363
|
-
process.stdout.write('\x1b7');
|
|
364
|
-
for (let i = 0; i < lastRenderedDropdownLines; i++) {
|
|
365
|
-
process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
|
|
366
|
-
}
|
|
367
|
-
process.stdout.write('\x1b8');
|
|
368
|
-
lastRenderedDropdownLines = 0;
|
|
369
|
-
}
|
|
370
283
|
process.stdout.write(`\x1b[0m\r\x1b[2K\n`);
|
|
371
284
|
cleanup();
|
|
372
285
|
resolve('__CANCEL__');
|
|
@@ -433,21 +346,23 @@ export function askPrompt(opts = {}) {
|
|
|
433
346
|
if (str === '\t' || (str.length === 1 && str.charCodeAt(0) === 9)) {
|
|
434
347
|
if (items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length) {
|
|
435
348
|
const selected = items[selectedIndex];
|
|
436
|
-
input
|
|
437
|
-
input
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
349
|
+
if (input.startsWith('/')) {
|
|
350
|
+
input = selected.replacement + ' ';
|
|
351
|
+
cursorPos = input.length;
|
|
352
|
+
render();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
input =
|
|
357
|
+
input.slice(0, selected.replaceStart) +
|
|
358
|
+
selected.replacement +
|
|
359
|
+
input.slice(selected.replaceStart + selected.replaceLen);
|
|
360
|
+
cursorPos = selected.replaceStart + selected.replacement.length;
|
|
361
|
+
render();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
442
364
|
}
|
|
443
365
|
else {
|
|
444
|
-
if (lastRenderedDropdownLines > 0) {
|
|
445
|
-
for (let i = 0; i < lastRenderedDropdownLines; i++) {
|
|
446
|
-
process.stdout.write(`\n\x1b[0m\x1b[2K`);
|
|
447
|
-
}
|
|
448
|
-
process.stdout.write(`\x1b[0m\x1b[${lastRenderedDropdownLines}A`);
|
|
449
|
-
lastRenderedDropdownLines = 0;
|
|
450
|
-
}
|
|
451
366
|
process.stdout.write(`\x1b[0m\r\x1b[2K`);
|
|
452
367
|
cleanup();
|
|
453
368
|
resolve(`__TOGGLE_MODE__:${input}`);
|
package/dist/cli/server.js
CHANGED
|
@@ -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.
|
|
142
|
+
<div class="footer">devx v1.4.17 • Terminal-Native AI Assistant</div>
|
|
143
143
|
</div>
|
|
144
144
|
</body>
|
|
145
145
|
</html>`;
|
package/dist/mcp/client.js
CHANGED
package/package.json
CHANGED