dex-termux-cli 0.1.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 +89 -0
- package/bin/Dex +7 -0
- package/bin/dex +7 -0
- package/data/commands/explain.json +74 -0
- package/icon/banner.png +0 -0
- package/icon/icon-dex.png +0 -0
- package/icon/logo-transparent.png +0 -0
- package/icon/logo.png +0 -0
- package/package.json +40 -0
- package/src/app/main.js +65 -0
- package/src/commands/android-shell.js +53 -0
- package/src/commands/explain.js +25 -0
- package/src/commands/install.js +1013 -0
- package/src/commands/menu.js +120 -0
- package/src/commands/safe-shell.js +200 -0
- package/src/commands/search.js +41 -0
- package/src/commands/tree.js +61 -0
- package/src/core/args.js +114 -0
- package/src/core/config.js +122 -0
- package/src/core/scopes.js +31 -0
- package/src/ui/output.js +129 -0
- package/src/ui/prompt.js +299 -0
- package/src/utils/fs-search.js +63 -0
- package/src/utils/fs-tree.js +131 -0
- package/src/utils/project-context.js +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./icon/icon-dex.png" alt="Dex icon" width="124" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<h1 align="center">Dex Termux CLI</h1>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
CLI visual para Termux con búsqueda guiada, árbol legible, explicación de comandos y flujos de instalación segura para proyectos dentro de Android storage.
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img alt="platform" src="https://img.shields.io/badge/platform-Termux-111111?style=flat-square&labelColor=F97316&color=1F2937">
|
|
15
|
+
<img alt="storage" src="https://img.shields.io/badge/storage-Android-111111?style=flat-square&labelColor=0F766E&color=1F2937">
|
|
16
|
+
<img alt="safe-mode" src="https://img.shields.io/badge/safe%20mode-Python%20%7C%20Node%20%7C%20PHP-111111?style=flat-square&labelColor=2563EB&color=1F2937">
|
|
17
|
+
<img alt="status" src="https://img.shields.io/badge/status-active-111111?style=flat-square&labelColor=7C3AED&color=1F2937">
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<strong>Explorar.</strong> <strong>Entender.</strong> <strong>Instalar.</strong> <strong>Entrar al modo seguro.</strong>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
> Estado actual: soporte seguro para Python, Node y PHP.
|
|
25
|
+
|
|
26
|
+
## Visión general
|
|
27
|
+
|
|
28
|
+
Dex está pensado para mejorar el trabajo diario en Termux cuando el proyecto vive dentro de almacenamiento Android. En lugar de obligar al usuario a resolver todo manualmente, Dex ofrece una capa más clara para explorar, entender y preparar proyectos sin perder control del entorno real.
|
|
29
|
+
|
|
30
|
+
## En qué destaca
|
|
31
|
+
|
|
32
|
+
- interfaz más legible para tareas comunes de terminal
|
|
33
|
+
- detección del lenguaje del proyecto actual
|
|
34
|
+
- instalación segura para proyectos en Android storage
|
|
35
|
+
- shell segura por proyecto con el contexto correcto
|
|
36
|
+
- enfoque práctico en lugar de automatización opaca
|
|
37
|
+
|
|
38
|
+
## Capacidades principales
|
|
39
|
+
|
|
40
|
+
| Comando | Uso principal |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `dex -m` | abre el menú principal |
|
|
43
|
+
| `dex -b` | busca archivos o carpetas |
|
|
44
|
+
| `dex -e` | explica comandos comunes |
|
|
45
|
+
| `dex -t` | muestra árbol guiado |
|
|
46
|
+
| `dex -a` | acceso rápido a Android |
|
|
47
|
+
| `dex -i` | instala dependencias del proyecto actual |
|
|
48
|
+
| `dex -r` | abre el modo seguro del proyecto |
|
|
49
|
+
|
|
50
|
+
## Qué resuelve
|
|
51
|
+
|
|
52
|
+
- salida visual más clara que varios comandos crudos de terminal
|
|
53
|
+
- exploración rápida de carpetas y archivos sin recordar tanta sintaxis
|
|
54
|
+
- detección del lenguaje del proyecto actual
|
|
55
|
+
- instalación segura cuando Android storage rompe el flujo normal
|
|
56
|
+
- shell segura por proyecto para seguir trabajando con el entorno correcto
|
|
57
|
+
|
|
58
|
+
## Instalación segura por lenguaje
|
|
59
|
+
|
|
60
|
+
| Lenguaje | Estrategia de instalación | Modo seguro |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| Python | `pip`, `requirements.txt`, `pyproject.toml`, `setup.py` o imports inferidos | `venv` seguro |
|
|
63
|
+
| Node | `npm ci` o `npm install` | copia segura del proyecto en `HOME` |
|
|
64
|
+
| PHP | `composer install --no-interaction` | copia segura del proyecto en `HOME` |
|
|
65
|
+
|
|
66
|
+
## Cómo funciona el modo seguro
|
|
67
|
+
|
|
68
|
+
### Python
|
|
69
|
+
Dex prepara un `venv` seguro y abre una shell con el entorno correcto para ejecutar `python`, `pip` o el entrypoint del proyecto.
|
|
70
|
+
|
|
71
|
+
### Node
|
|
72
|
+
Dex puede trabajar con una copia segura del proyecto fuera de Android storage para que `npm` y `node_modules` no choquen con las limitaciones del almacenamiento compartido.
|
|
73
|
+
|
|
74
|
+
### PHP
|
|
75
|
+
Dex puede instalar dependencias con `composer` en una copia segura del proyecto y abrir una shell lista para seguir trabajando desde ahí.
|
|
76
|
+
|
|
77
|
+
## Estado actual
|
|
78
|
+
|
|
79
|
+
- instalación segura activa para Python, Node y PHP
|
|
80
|
+
- shell segura activa para Python, Node y PHP
|
|
81
|
+
- branding base ya integrado al repositorio
|
|
82
|
+
- lectura del proyecto centrada en Termux y Android storage
|
|
83
|
+
|
|
84
|
+
## Roadmap
|
|
85
|
+
|
|
86
|
+
- soporte seguro para Ruby, Go y Rust
|
|
87
|
+
- mejor traducción de errores nativos y de compilación
|
|
88
|
+
- ejecución guiada de entrypoints del proyecto
|
|
89
|
+
- refinamiento final de branding y presentación pública
|
package/bin/Dex
ADDED
package/bin/dex
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ls": {
|
|
3
|
+
"summary": "Lista archivos y carpetas.",
|
|
4
|
+
"usage": "ls\nls -lah\nls /sdcard/Download",
|
|
5
|
+
"risk": "bajo",
|
|
6
|
+
"notes": [
|
|
7
|
+
"Usa -l para ver detalles y -a para mostrar ocultos.",
|
|
8
|
+
"En Termux, ls -lah es una combinacion util para inspeccionar carpetas."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"cd": {
|
|
12
|
+
"summary": "Cambia de carpeta actual.",
|
|
13
|
+
"usage": "cd carpeta\ncd ..\ncd ~\ncd /sdcard",
|
|
14
|
+
"risk": "bajo",
|
|
15
|
+
"notes": [
|
|
16
|
+
"cd .. sube un nivel.",
|
|
17
|
+
"cd ~ vuelve a tu home de Termux."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"rm": {
|
|
21
|
+
"summary": "Borra archivos o carpetas.",
|
|
22
|
+
"usage": "rm archivo.txt\nrm -r carpeta",
|
|
23
|
+
"risk": "alto",
|
|
24
|
+
"notes": [
|
|
25
|
+
"No manda a papelera. Borra directamente.",
|
|
26
|
+
"Ten cuidado con rm -r y especialmente con rutas fuera de tu carpeta actual."
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"cp": {
|
|
30
|
+
"summary": "Copia archivos o carpetas.",
|
|
31
|
+
"usage": "cp origen destino\ncp -r carpeta_a carpeta_b",
|
|
32
|
+
"risk": "medio",
|
|
33
|
+
"notes": [
|
|
34
|
+
"Usa -r para copiar carpetas completas.",
|
|
35
|
+
"Puede sobrescribir si el destino ya existe."
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"mv": {
|
|
39
|
+
"summary": "Mueve o renombra archivos y carpetas.",
|
|
40
|
+
"usage": "mv origen destino\nmv nombre_viejo nombre_nuevo",
|
|
41
|
+
"risk": "medio",
|
|
42
|
+
"notes": [
|
|
43
|
+
"Sirve tanto para mover como para renombrar.",
|
|
44
|
+
"Si el destino existe, puedes sobrescribir sin querer."
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"pkg": {
|
|
48
|
+
"summary": "Instala y gestiona paquetes en Termux.",
|
|
49
|
+
"usage": "pkg search git\npkg install git\npkg update && pkg upgrade",
|
|
50
|
+
"risk": "medio",
|
|
51
|
+
"notes": [
|
|
52
|
+
"Es la forma comoda de usar apt dentro de Termux.",
|
|
53
|
+
"Antes de instalar mucho, conviene actualizar repos y paquetes."
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"git": {
|
|
57
|
+
"summary": "Control de versiones y clonacion de repositorios.",
|
|
58
|
+
"usage": "git clone URL\ngit status\ngit add .\ngit commit -m \"mensaje\"",
|
|
59
|
+
"risk": "medio",
|
|
60
|
+
"notes": [
|
|
61
|
+
"git status es el comando base para saber en que estado estas.",
|
|
62
|
+
"No uses comandos destructivos si no sabes exactamente que hacen."
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"find": {
|
|
66
|
+
"summary": "Busca archivos y carpetas desde terminal.",
|
|
67
|
+
"usage": "find . -name \"*.js\"\nfind /sdcard -iname \"foto*\"",
|
|
68
|
+
"risk": "bajo",
|
|
69
|
+
"notes": [
|
|
70
|
+
"Puede ser lento en rutas muy grandes.",
|
|
71
|
+
"Dex -b esta pensado precisamente para evitarte recordar esta sintaxis."
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
package/icon/banner.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/icon/logo.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dex-termux-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Termux CLI for Android with guided search, readable tree views, command help, and safe project install flows for Python, Node, and PHP.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"termux",
|
|
8
|
+
"android",
|
|
9
|
+
"cli",
|
|
10
|
+
"python",
|
|
11
|
+
"node",
|
|
12
|
+
"php",
|
|
13
|
+
"safe-shell"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"bin": "./bin/dex",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node ./bin/dex",
|
|
19
|
+
"help": "node ./bin/dex --help"
|
|
20
|
+
},
|
|
21
|
+
"author": "farllirs",
|
|
22
|
+
"files": [
|
|
23
|
+
"bin/dex",
|
|
24
|
+
"src",
|
|
25
|
+
"data/commands/explain.json",
|
|
26
|
+
"README.md",
|
|
27
|
+
"icon"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/farllirs/dex-termux-cli.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/farllirs/dex-termux-cli#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/farllirs/dex-termux-cli/issues"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/app/main.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { parseArgs } from '../core/args.js';
|
|
2
|
+
import { loadUserConfig } from '../core/config.js';
|
|
3
|
+
import { runAndroidShellCommand } from '../commands/android-shell.js';
|
|
4
|
+
import { runExplainCommand } from '../commands/explain.js';
|
|
5
|
+
import { runInstallCommand } from '../commands/install.js';
|
|
6
|
+
import { runMenuCommand } from '../commands/menu.js';
|
|
7
|
+
import { runSafeShellCommand } from '../commands/safe-shell.js';
|
|
8
|
+
import { runSearchCommand } from '../commands/search.js';
|
|
9
|
+
import { runTreeCommand } from '../commands/tree.js';
|
|
10
|
+
import { printHelp, printProjectContextLine } from '../ui/output.js';
|
|
11
|
+
import { detectProjectContext, formatProjectContext } from '../utils/project-context.js';
|
|
12
|
+
|
|
13
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
14
|
+
const parsed = parseArgs(argv);
|
|
15
|
+
const config = await loadUserConfig();
|
|
16
|
+
|
|
17
|
+
if (config.features.projectBadge) {
|
|
18
|
+
const context = await detectProjectContext();
|
|
19
|
+
if (context) {
|
|
20
|
+
printProjectContextLine(formatProjectContext(context));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (parsed.help || !argv.length) {
|
|
25
|
+
printHelp();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (parsed.command === 'search') {
|
|
30
|
+
await runSearchCommand(parsed);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (parsed.command === 'explain') {
|
|
35
|
+
await runExplainCommand(parsed);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (parsed.command === 'menu') {
|
|
40
|
+
await runMenuCommand();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (parsed.command === 'tree') {
|
|
45
|
+
await runTreeCommand(parsed);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (parsed.command === 'android') {
|
|
50
|
+
await runAndroidShellCommand();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (parsed.command === 'install') {
|
|
55
|
+
await runInstallCommand();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (parsed.command === 'safe-shell') {
|
|
60
|
+
await runSafeShellCommand();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
printHelp();
|
|
65
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { getUserConfigPath, loadUserConfig } from '../core/config.js';
|
|
4
|
+
|
|
5
|
+
const ANDROID_ROOT = '/sdcard';
|
|
6
|
+
|
|
7
|
+
export async function runAndroidShellCommand() {
|
|
8
|
+
const config = await loadUserConfig();
|
|
9
|
+
|
|
10
|
+
if (!config.features.androidShortcut) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`La opcion Android esta desactivada. Activa features.androidShortcut en ${getUserConfigPath()}`,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await fs.access(ANDROID_ROOT);
|
|
18
|
+
} catch {
|
|
19
|
+
throw new Error(`No se puede acceder a la ruta Android: ${ANDROID_ROOT}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const shell = process.env.SHELL || '/data/data/com.termux/files/usr/bin/sh';
|
|
23
|
+
|
|
24
|
+
console.log(`Abriendo shell en ${ANDROID_ROOT}`);
|
|
25
|
+
console.log('Escribe exit para volver.');
|
|
26
|
+
console.log('');
|
|
27
|
+
|
|
28
|
+
await new Promise((resolve, reject) => {
|
|
29
|
+
const child = spawn(shell, {
|
|
30
|
+
cwd: ANDROID_ROOT,
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
env: {
|
|
33
|
+
...process.env,
|
|
34
|
+
DEX_CONTEXT: 'android-shell',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
child.on('error', reject);
|
|
39
|
+
child.on('exit', (code, signal) => {
|
|
40
|
+
if (signal) {
|
|
41
|
+
reject(new Error(`La shell Android termino por senal: ${signal}`));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (code && code !== 0) {
|
|
46
|
+
reject(new Error(`La shell Android termino con codigo ${code}`));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
resolve();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { printExplainEntry, printSection } from '../ui/output.js';
|
|
4
|
+
import { chooseExplainTopic } from '../ui/prompt.js';
|
|
5
|
+
|
|
6
|
+
const EXPLAIN_DATA_PATH = path.resolve(process.cwd(), 'data/commands/explain.json');
|
|
7
|
+
|
|
8
|
+
export async function runExplainCommand({ topic }) {
|
|
9
|
+
const entries = await loadExplainEntries();
|
|
10
|
+
const selectedTopic = topic ? topic.toLowerCase() : await chooseExplainTopic(entries);
|
|
11
|
+
const entry = entries[selectedTopic];
|
|
12
|
+
|
|
13
|
+
if (!entry) {
|
|
14
|
+
const available = Object.keys(entries).join(', ');
|
|
15
|
+
throw new Error(`No conozco ese comando todavia. Disponibles: ${available}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
printSection(`Explicar: ${selectedTopic}`);
|
|
19
|
+
console.log(printExplainEntry(selectedTopic, entry));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function loadExplainEntries() {
|
|
23
|
+
const raw = await fs.readFile(EXPLAIN_DATA_PATH, 'utf8');
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|