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 ADDED
@@ -0,0 +1,89 @@
1
+ ![Dex Banner](./icon/banner.png)
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/app/main.js';
3
+
4
+ main().catch((error) => {
5
+ console.error(`Error: ${error.message}`);
6
+ process.exitCode = 1;
7
+ });
package/bin/dex ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from '../src/app/main.js';
3
+
4
+ main().catch((error) => {
5
+ console.error(`Error: ${error.message}`);
6
+ process.exitCode = 1;
7
+ });
@@ -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
+ }
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
+ }
@@ -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
+ }