dex-termux-cli 0.2.3 → 0.3.0-beta.2
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 +58 -61
- package/data/commands/explain.json +63 -0
- package/package.json +9 -3
- package/src/app/main.js +11 -1
- package/src/commands/android-shell.js +220 -27
- package/src/commands/context.js +22 -0
- package/src/commands/explain.js +11 -2
- package/src/commands/install.js +279 -28
- package/src/commands/menu.js +38 -5
- package/src/commands/safe-shell.js +211 -10
- package/src/commands/search.js +5 -1
- package/src/commands/tree.js +5 -1
- package/src/commands/version.js +84 -11
- package/src/core/args.js +13 -3
- package/src/core/config.js +43 -4
- package/src/core/scopes.js +22 -12
- package/src/ui/output.js +10 -7
- package/src/ui/prompt.js +77 -14
- package/src/utils/platform.js +116 -0
- package/src/utils/project-context.js +183 -31
- package/src/utils/shell.js +33 -7
package/README.md
CHANGED
|
@@ -7,89 +7,86 @@
|
|
|
7
7
|
<h1 align="center">Dex Termux CLI</h1>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
|
-
CLI visual para Termux con
|
|
10
|
+
CLI visual para Termux y Linux con busqueda guiada, tree legible, contexto de proyecto, version visible y flujos seguros para Python, Node y PHP.
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
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">
|
|
14
|
+
<img alt="platform" src="https://img.shields.io/badge/platform-Termux%20%7C%20Linux-111111?style=flat-square&labelColor=F97316&color=1F2937">
|
|
16
15
|
<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="
|
|
16
|
+
<img alt="channel" src="https://img.shields.io/badge/channel-beta-111111?style=flat-square&labelColor=B45309&color=1F2937">
|
|
18
17
|
</p>
|
|
19
18
|
|
|
20
19
|
<p align="center">
|
|
21
20
|
<strong>Explorar.</strong> <strong>Entender.</strong> <strong>Instalar.</strong> <strong>Entrar al modo seguro.</strong>
|
|
22
21
|
</p>
|
|
23
22
|
|
|
24
|
-
> Estado actual: soporte seguro para Python, Node y PHP,
|
|
23
|
+
> Estado actual: beta `0.3.0-beta.1` con soporte seguro para Python, Node y PHP, contexto de proyecto y modo de plataforma `auto | termux | linux`.
|
|
25
24
|
|
|
26
|
-
##
|
|
25
|
+
## Vision general
|
|
27
26
|
|
|
28
|
-
Dex
|
|
27
|
+
Dex mejora tareas comunes de terminal sin tapar el entorno real. Sirve para explorar carpetas, entender comandos, detectar proyectos y preparar instalaciones con una capa mas clara encima de Termux o Linux.
|
|
29
28
|
|
|
30
|
-
##
|
|
29
|
+
## Lo que ya incluye
|
|
31
30
|
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| `dex -m` | abre el menú principal |
|
|
43
|
-
| `dex -v` | muestra la versión local y revisa la publicada |
|
|
44
|
-
| `dex -c` | muestra el contexto del proyecto actual |
|
|
45
|
-
| `dex -b` | busca archivos o carpetas |
|
|
46
|
-
| `dex -e` | explica comandos comunes |
|
|
47
|
-
| `dex -t` | muestra árbol guiado |
|
|
48
|
-
| `dex -a` | acceso rápido a Android |
|
|
49
|
-
| `dex -i` | instala dependencias del proyecto actual |
|
|
50
|
-
| `dex -r` | abre el modo seguro del proyecto |
|
|
51
|
-
|
|
52
|
-
## Qué resuelve
|
|
53
|
-
|
|
54
|
-
- salida visual más clara que varios comandos crudos de terminal
|
|
55
|
-
- exploración rápida de carpetas y archivos sin recordar tanta sintaxis
|
|
56
|
-
- detección del lenguaje del proyecto actual con una vista compacta útil para prompt
|
|
57
|
-
- instalación segura cuando Android storage rompe el flujo normal
|
|
58
|
-
- shell segura por proyecto para seguir trabajando con el entorno correcto
|
|
31
|
+
- `dex -m`: menu principal
|
|
32
|
+
- `dex -v`: version local y publicada
|
|
33
|
+
- `dex -c`: contexto del proyecto actual
|
|
34
|
+
- `dex --prompt-context`: badge compacto para prompt
|
|
35
|
+
- `dex -b`: busqueda guiada
|
|
36
|
+
- `dex -e`: explicacion de comandos comunes
|
|
37
|
+
- `dex -t`: tree guiado
|
|
38
|
+
- `dex -a`: shell de acceso rapido redisenada para Android storage en Termux o para HOME en Linux
|
|
39
|
+
- `dex -i`: instalacion del proyecto actual incluso desde subcarpetas del proyecto
|
|
40
|
+
- `dex -r`: shell segura del proyecto actual incluso desde subcarpetas del proyecto
|
|
59
41
|
|
|
60
42
|
## Contexto del proyecto
|
|
61
43
|
|
|
62
|
-
Dex
|
|
63
|
-
|
|
64
|
-
## Instalación segura por lenguaje
|
|
44
|
+
Dex detecta el lenguaje segun los archivos del directorio actual y tambien desde estructuras simples como `src/main.py` o `app/main.js`. El badge compacto del prompt usa formato limpio como `PYTHON`, `NODE` o `PHP`. La version detectada del runtime se ve en `dex -c`, no dentro del badge.
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
| --- | --- | --- |
|
|
68
|
-
| Python | `pip`, `requirements.txt`, `pyproject.toml`, `setup.py` o imports inferidos | `venv` seguro |
|
|
69
|
-
| Node | `npm ci` o `npm install` | copia segura del proyecto en `HOME` |
|
|
70
|
-
| PHP | `composer install --no-interaction` | copia segura del proyecto en `HOME` |
|
|
71
|
-
|
|
72
|
-
## Cómo funciona el modo seguro
|
|
46
|
+
Lenguajes detectables actualmente:
|
|
73
47
|
|
|
74
|
-
|
|
75
|
-
|
|
48
|
+
- Python
|
|
49
|
+
- Node
|
|
50
|
+
- PHP
|
|
51
|
+
- Rust
|
|
52
|
+
- Go
|
|
53
|
+
- Java
|
|
54
|
+
- Ruby
|
|
76
55
|
|
|
77
|
-
|
|
78
|
-
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.
|
|
56
|
+
## Modo de plataforma
|
|
79
57
|
|
|
80
|
-
|
|
81
|
-
Dex puede instalar dependencias con `composer` en una copia segura del proyecto y abrir una shell lista para seguir trabajando desde ahí.
|
|
58
|
+
Dex puede trabajar en tres modos:
|
|
82
59
|
|
|
83
|
-
|
|
60
|
+
- `auto`: detecta si estas en Termux o Linux
|
|
61
|
+
- `termux`: habilita Android storage y restricciones propias de Termux
|
|
62
|
+
- `linux`: desactiva Android y trabaja como entorno Linux puro
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
- shell segura activa para Python, Node y PHP
|
|
87
|
-
- menú y ayuda con una salida más clara
|
|
88
|
-
- lectura del proyecto centrada en Termux y Android storage
|
|
64
|
+
El modo actual se guarda en `~/.config/dex/config.json`.
|
|
89
65
|
|
|
90
|
-
##
|
|
66
|
+
## Instalacion segura por lenguaje
|
|
91
67
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
68
|
+
| Lenguaje | Estrategia | Modo seguro |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Python | `pip`, `requirements.txt`, `pyproject.toml`, `setup.py` o imports inferidos | `venv` seguro |
|
|
71
|
+
| Node | `npm ci` o `npm install` | workspace seguro en `HOME` |
|
|
72
|
+
| PHP | `composer install --no-interaction` | workspace seguro en `HOME` |
|
|
73
|
+
| Ruby | `bundle install` | workspace seguro en `HOME` |
|
|
74
|
+
| Go | `go mod download` | workspace seguro en `HOME` |
|
|
75
|
+
| Rust | `cargo fetch` | workspace seguro en `HOME` |
|
|
76
|
+
| Java | `mvn dependency:go-offline`, `./mvnw dependency:go-offline`, `gradle dependencies` o `./gradlew dependencies` | workspace seguro en `HOME` |
|
|
77
|
+
|
|
78
|
+
## Estado real de la beta
|
|
79
|
+
|
|
80
|
+
- `dex -m`, `-a`, `-b`, `-t`, `-v`, `-c` y `--prompt-context` funcionan
|
|
81
|
+
- `dex -a` ahora abre una shell de acceso rapido con prompt propio y atajos. En Termux apunta a Android storage y en Linux apunta a HOME
|
|
82
|
+
- `dex -i` funciona en proyectos validos y ahora usa el root detectado del proyecto aunque entres desde una subcarpeta
|
|
83
|
+
- `dex -r` funciona para proyectos soportados, hereda mejor el root del proyecto y en Termux exige Android storage
|
|
84
|
+
- el modo seguro real ya cubre Python, Node, PHP, Ruby, Go, Rust y Java
|
|
85
|
+
- `dex -e` ya resuelve su data interna sin depender del directorio actual
|
|
86
|
+
|
|
87
|
+
## Roadmap inmediato
|
|
88
|
+
|
|
89
|
+
- pulir textos y mensajes de error
|
|
90
|
+
- validar modo Linux de punta a punta
|
|
91
|
+
- seguir puliendo mensajes, UX y validacion prolongada en Linux y Java
|
|
92
|
+
- explorar ejecucion guiada de entrypoints
|
|
@@ -70,5 +70,68 @@
|
|
|
70
70
|
"Puede ser lento en rutas muy grandes.",
|
|
71
71
|
"Dex -b esta pensado precisamente para evitarte recordar esta sintaxis."
|
|
72
72
|
]
|
|
73
|
+
},
|
|
74
|
+
"pwd": {
|
|
75
|
+
"summary": "Muestra la ruta actual donde estas parado.",
|
|
76
|
+
"usage": "pwd",
|
|
77
|
+
"risk": "bajo",
|
|
78
|
+
"notes": [
|
|
79
|
+
"Es util para confirmar si Dex te movio al root del proyecto o al workspace seguro.",
|
|
80
|
+
"En Android storage puede mostrar /storage/emulated/0 aunque entres por /sdcard."
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"mkdir": {
|
|
84
|
+
"summary": "Crea carpetas nuevas.",
|
|
85
|
+
"usage": "mkdir carpeta\nmkdir -p ruta/larga",
|
|
86
|
+
"risk": "bajo",
|
|
87
|
+
"notes": [
|
|
88
|
+
"Usa -p para crear rutas completas sin error si parte del camino ya existe.",
|
|
89
|
+
"No borra ni sobrescribe contenido existente."
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
"npm": {
|
|
93
|
+
"summary": "Gestiona dependencias y scripts de proyectos Node.",
|
|
94
|
+
"usage": "npm install\nnpm ci\nnpm run dev",
|
|
95
|
+
"risk": "medio",
|
|
96
|
+
"notes": [
|
|
97
|
+
"npm ci es mejor cuando ya existe package-lock.json y quieres una instalacion reproducible.",
|
|
98
|
+
"En Android storage Dex puede rescatar proyectos Node en un workspace seguro dentro de HOME."
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"node": {
|
|
102
|
+
"summary": "Ejecuta codigo JavaScript y proyectos Node.",
|
|
103
|
+
"usage": "node app.js\nnode -v\nnode -e \"console.log(1)\"",
|
|
104
|
+
"risk": "bajo",
|
|
105
|
+
"notes": [
|
|
106
|
+
"Si el proyecto usa dependencias locales, entra primero al root correcto o al workspace seguro.",
|
|
107
|
+
"Dex usa node tambien para correr su propio CLI."
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"python": {
|
|
111
|
+
"summary": "Ejecuta scripts Python e inspecciona el runtime actual.",
|
|
112
|
+
"usage": "python bot.py\npython -V\npython -m pip list",
|
|
113
|
+
"risk": "bajo",
|
|
114
|
+
"notes": [
|
|
115
|
+
"Dentro de safe-shell puede apuntar a un venv preparado por Dex.",
|
|
116
|
+
"python -m pip suele ser mas fiable que usar pip suelto."
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"pip": {
|
|
120
|
+
"summary": "Instala y gestiona paquetes Python.",
|
|
121
|
+
"usage": "pip install paquete\npip install -r requirements.txt",
|
|
122
|
+
"risk": "medio",
|
|
123
|
+
"notes": [
|
|
124
|
+
"En proyectos sensibles conviene usar venv o el modo seguro de Dex.",
|
|
125
|
+
"Si estas en Android storage, Dex puede rescatar la instalacion automaticamente en ciertos casos."
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"composer": {
|
|
129
|
+
"summary": "Instala dependencias y maneja proyectos PHP.",
|
|
130
|
+
"usage": "composer install\ncomposer update\ncomposer dump-autoload",
|
|
131
|
+
"risk": "medio",
|
|
132
|
+
"notes": [
|
|
133
|
+
"En Dex, composer puede ejecutarse dentro de un workspace seguro para evitar problemas de Android storage.",
|
|
134
|
+
"composer update cambia versiones; composer install intenta respetar composer.lock."
|
|
135
|
+
]
|
|
73
136
|
}
|
|
74
137
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dex-termux-cli",
|
|
3
|
-
"version": "0.2
|
|
3
|
+
"version": "0.3.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Visual CLI for Termux and Linux with guided search, readable tree views, project context, version checks, Android mode, and safe flows for Python, Node, PHP, Ruby, Go, Rust, and Java.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"termux",
|
|
8
|
+
"linux",
|
|
8
9
|
"android",
|
|
9
10
|
"cli",
|
|
10
11
|
"python",
|
|
11
12
|
"node",
|
|
12
13
|
"php",
|
|
14
|
+
"ruby",
|
|
15
|
+
"go",
|
|
16
|
+
"rust",
|
|
17
|
+
"java",
|
|
13
18
|
"safe-shell"
|
|
14
19
|
],
|
|
15
20
|
"license": "MIT",
|
|
@@ -18,7 +23,8 @@
|
|
|
18
23
|
},
|
|
19
24
|
"scripts": {
|
|
20
25
|
"start": "node ./bin/dex",
|
|
21
|
-
"help": "node ./bin/dex --help"
|
|
26
|
+
"help": "node ./bin/dex --help",
|
|
27
|
+
"test": "node --test ./tests/**/*.test.js"
|
|
22
28
|
},
|
|
23
29
|
"author": "farllirs",
|
|
24
30
|
"files": [
|
package/src/app/main.js
CHANGED
|
@@ -15,7 +15,7 @@ import { detectProjectContext, formatProjectContext } from '../utils/project-con
|
|
|
15
15
|
export async function main(argv = process.argv.slice(2)) {
|
|
16
16
|
const parsed = parseArgs(argv);
|
|
17
17
|
const config = await loadUserConfig();
|
|
18
|
-
const isPromptOnly = parsed.command === 'prompt-context';
|
|
18
|
+
const isPromptOnly = parsed.command === 'prompt-context' || parsed.command === 'prompt-project-root' || parsed.command === 'prompt-project-path';
|
|
19
19
|
const isContextOnly = parsed.command === 'context';
|
|
20
20
|
const isVersionOnly = parsed.command === 'version';
|
|
21
21
|
|
|
@@ -46,6 +46,16 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
if (parsed.command === 'prompt-project-root') {
|
|
50
|
+
await runContextCommand({ projectRoot: true });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (parsed.command === 'prompt-project-path') {
|
|
55
|
+
await runContextCommand({ projectPath: true });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
if (parsed.command === 'search') {
|
|
50
60
|
await runSearchCommand(parsed);
|
|
51
61
|
return;
|
|
@@ -1,55 +1,248 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
2
4
|
import { spawn } from 'node:child_process';
|
|
3
5
|
import { getUserConfigPath, loadUserConfig } from '../core/config.js';
|
|
4
6
|
import { resolveInteractiveShell } from '../utils/shell.js';
|
|
7
|
+
import {
|
|
8
|
+
getQuickAccessAliases,
|
|
9
|
+
getQuickAccessLabel,
|
|
10
|
+
getQuickAccessModeDescription,
|
|
11
|
+
getQuickAccessRoot,
|
|
12
|
+
getQuickAccessShortcutSummary,
|
|
13
|
+
getQuickAccessTitle,
|
|
14
|
+
resolvePlatformMode,
|
|
15
|
+
} from '../utils/platform.js';
|
|
5
16
|
|
|
6
|
-
const
|
|
17
|
+
const ANDROID_BASH_CANDIDATES = [
|
|
18
|
+
'/data/data/com.termux/files/usr/bin/bash',
|
|
19
|
+
'/bin/bash',
|
|
20
|
+
];
|
|
7
21
|
|
|
8
22
|
export async function runAndroidShellCommand() {
|
|
9
23
|
const config = await loadUserConfig();
|
|
10
24
|
|
|
11
25
|
if (!config.features.androidShortcut) {
|
|
12
26
|
throw new Error(
|
|
13
|
-
`
|
|
27
|
+
`El acceso rapido esta desactivado. Activa features.androidShortcut en ${getUserConfigPath()}`,
|
|
14
28
|
);
|
|
15
29
|
}
|
|
16
30
|
|
|
31
|
+
const platformMode = resolvePlatformMode(config);
|
|
32
|
+
const quickAccessRoot = getQuickAccessRoot(platformMode);
|
|
33
|
+
|
|
17
34
|
try {
|
|
18
|
-
await fs.access(
|
|
35
|
+
await fs.access(quickAccessRoot);
|
|
19
36
|
} catch {
|
|
20
|
-
throw new Error(`No se puede acceder a la ruta
|
|
37
|
+
throw new Error(`No se puede acceder a la ruta base de acceso rapido: ${quickAccessRoot}`);
|
|
21
38
|
}
|
|
22
39
|
|
|
23
|
-
const interactiveShell = await
|
|
40
|
+
const interactiveShell = await resolveAndroidInteractiveShell(platformMode);
|
|
41
|
+
const shellSession = await createQuickAccessShellSession(platformMode, interactiveShell);
|
|
24
42
|
|
|
25
|
-
console.log(
|
|
43
|
+
console.log(getQuickAccessTitle(platformMode));
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log(`Raiz : ${quickAccessRoot}`);
|
|
26
46
|
console.log(`Shell : ${interactiveShell.shellName} interactiva`);
|
|
27
|
-
console.log(
|
|
47
|
+
console.log(`Modo : ${getQuickAccessModeDescription(platformMode)}`);
|
|
48
|
+
console.log(`Atajos : ${getQuickAccessShortcutSummary(platformMode)}`);
|
|
49
|
+
console.log('Salida : escribe exit para volver');
|
|
28
50
|
console.log('');
|
|
29
51
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
try {
|
|
53
|
+
await new Promise((resolve, reject) => {
|
|
54
|
+
const child = spawn(shellSession.shellPath, shellSession.shellArgs, {
|
|
55
|
+
cwd: quickAccessRoot,
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
env: {
|
|
58
|
+
...process.env,
|
|
59
|
+
...shellSession.env,
|
|
60
|
+
DEX_CONTEXT: platformMode === 'termux' ? 'android-shell' : 'linux-shell',
|
|
61
|
+
DEX_ANDROID_ROOT: quickAccessRoot,
|
|
62
|
+
DEX_ANDROID_LABEL: getQuickAccessLabel(platformMode),
|
|
63
|
+
DEX_ANDROID_SHELL: interactiveShell.shellName,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
child.on('error', reject);
|
|
68
|
+
child.on('exit', (code, signal) => {
|
|
69
|
+
if (signal) {
|
|
70
|
+
reject(new Error(`La shell de acceso rapido termino por senal: ${signal}`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
46
73
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
if (code && code !== 0) {
|
|
75
|
+
reject(new Error(`La shell de acceso rapido termino con codigo ${code}`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
53
81
|
});
|
|
82
|
+
} finally {
|
|
83
|
+
await shellSession.cleanup();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function resolveAndroidInteractiveShell(platformMode) {
|
|
88
|
+
const interactiveShell = await resolveInteractiveShell(platformMode);
|
|
89
|
+
|
|
90
|
+
if (interactiveShell.shellName !== 'sh') {
|
|
91
|
+
return interactiveShell;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const shellPath of ANDROID_BASH_CANDIDATES) {
|
|
95
|
+
try {
|
|
96
|
+
await fs.access(shellPath);
|
|
97
|
+
return {
|
|
98
|
+
shellPath,
|
|
99
|
+
shellArgs: ['-i'],
|
|
100
|
+
shellName: 'bash',
|
|
101
|
+
};
|
|
102
|
+
} catch {
|
|
103
|
+
// sigue con el siguiente candidato
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return interactiveShell;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function createQuickAccessShellSession(platformMode, interactiveShell) {
|
|
111
|
+
const tmpBase = await fs.mkdtemp(path.join(os.tmpdir(), 'dex-shell-'));
|
|
112
|
+
const aliases = getQuickAccessAliases(platformMode);
|
|
113
|
+
const root = getQuickAccessRoot(platformMode);
|
|
114
|
+
const label = getQuickAccessLabel(platformMode);
|
|
115
|
+
const rcContent = buildShellRc({
|
|
116
|
+
aliases,
|
|
117
|
+
root,
|
|
118
|
+
label,
|
|
119
|
+
shellName: interactiveShell.shellName,
|
|
120
|
+
platformMode,
|
|
54
121
|
});
|
|
122
|
+
|
|
123
|
+
if (interactiveShell.shellName === 'bash') {
|
|
124
|
+
const rcPath = path.join(tmpBase, 'dex-bashrc');
|
|
125
|
+
await fs.writeFile(rcPath, rcContent, 'utf8');
|
|
126
|
+
return {
|
|
127
|
+
shellPath: interactiveShell.shellPath,
|
|
128
|
+
shellArgs: ['--rcfile', rcPath, '-i'],
|
|
129
|
+
env: {},
|
|
130
|
+
cleanup: () => fs.rm(tmpBase, { recursive: true, force: true }),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (interactiveShell.shellName === 'zsh') {
|
|
135
|
+
const zdotdir = path.join(tmpBase, 'zsh');
|
|
136
|
+
await fs.mkdir(zdotdir, { recursive: true });
|
|
137
|
+
await fs.writeFile(path.join(zdotdir, '.zshrc'), rcContent, 'utf8');
|
|
138
|
+
return {
|
|
139
|
+
shellPath: interactiveShell.shellPath,
|
|
140
|
+
shellArgs: ['-i'],
|
|
141
|
+
env: { ZDOTDIR: zdotdir },
|
|
142
|
+
cleanup: () => fs.rm(tmpBase, { recursive: true, force: true }),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const rcPath = path.join(tmpBase, 'dex-shrc');
|
|
147
|
+
await fs.writeFile(rcPath, rcContent, 'utf8');
|
|
148
|
+
return {
|
|
149
|
+
shellPath: interactiveShell.shellPath,
|
|
150
|
+
shellArgs: ['-i'],
|
|
151
|
+
env: { ENV: rcPath },
|
|
152
|
+
cleanup: () => fs.rm(tmpBase, { recursive: true, force: true }),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function buildShellRc({ aliases, root, label, shellName, platformMode }) {
|
|
157
|
+
if (platformMode === 'termux') {
|
|
158
|
+
return buildTermuxShellRc(shellName);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return buildLinuxShellRc({ aliases, root, label, shellName });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildTermuxShellRc(shellName) {
|
|
165
|
+
const lines = [
|
|
166
|
+
'# dex quick access shell',
|
|
167
|
+
'export PATH="$HOME/bin:$PATH"',
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
if (shellName === 'zsh') {
|
|
171
|
+
lines.push('[[ -f "$HOME/.zshrc" ]] && source "$HOME/.zshrc"');
|
|
172
|
+
lines.push('printf "Dex: acceso rapido cargado en %s\\n" "$PWD"');
|
|
173
|
+
return lines.join('\n') + '\n';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (shellName === 'bash') {
|
|
177
|
+
lines.push('[[ -f "$HOME/.bashrc" ]] && source "$HOME/.bashrc"');
|
|
178
|
+
lines.push('printf "Dex: acceso rapido cargado en %s\\n" "$PWD"');
|
|
179
|
+
return lines.join('\n') + '\n';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
lines.push('printf "Dex: acceso rapido cargado en %s\\n" "$PWD"');
|
|
183
|
+
return lines.join('\n') + '\n';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function buildLinuxShellRc({ aliases, root, label, shellName }) {
|
|
187
|
+
const aliasLines = Object.entries(aliases).map(([name, target]) => `alias ${name}='cd "${target}"'`);
|
|
188
|
+
const commonLines = [
|
|
189
|
+
'# dex quick access shell',
|
|
190
|
+
'export PATH="$HOME/bin:$PATH"',
|
|
191
|
+
`export DEX_QUICK_ROOT="${root}"`,
|
|
192
|
+
`export DEX_QUICK_LABEL="${label}"`,
|
|
193
|
+
...aliasLines,
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
if (shellName === 'zsh') {
|
|
197
|
+
return [
|
|
198
|
+
...commonLines,
|
|
199
|
+
'[[ -f "$HOME/.zshrc" ]] && source "$HOME/.zshrc"',
|
|
200
|
+
'_dex_linux_project_badge_zsh() {',
|
|
201
|
+
' local dex_bin context',
|
|
202
|
+
' dex_bin="$(command -v dex 2>/dev/null)"',
|
|
203
|
+
' [[ -z "$dex_bin" ]] && return',
|
|
204
|
+
' context="$($dex_bin --prompt-context 2>/dev/null)"',
|
|
205
|
+
' [[ -z "$context" ]] && return',
|
|
206
|
+
' print -n "%F{81}[$context]%f"',
|
|
207
|
+
'}',
|
|
208
|
+
'_dex_linux_prompt_zsh() {',
|
|
209
|
+
' local badge',
|
|
210
|
+
' badge="$(_dex_linux_project_badge_zsh)"',
|
|
211
|
+
" PROMPT=$'%F{45}Dex@linux%f %F{117}%~%f\\n%F{45}>%f '",
|
|
212
|
+
' RPROMPT="$badge"',
|
|
213
|
+
'}',
|
|
214
|
+
'autoload -Uz add-zsh-hook 2>/dev/null || true',
|
|
215
|
+
'add-zsh-hook precmd _dex_linux_prompt_zsh 2>/dev/null || true',
|
|
216
|
+
'_dex_linux_prompt_zsh',
|
|
217
|
+
'printf "Dex: acceso rapido cargado en %s\\n" "$PWD"',
|
|
218
|
+
].join('\n') + '\n';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (shellName === 'bash') {
|
|
222
|
+
return [
|
|
223
|
+
...commonLines,
|
|
224
|
+
'_dex_linux_project_badge_bash() {',
|
|
225
|
+
' local dex_bin context',
|
|
226
|
+
' dex_bin="$(command -v dex 2>/dev/null)"',
|
|
227
|
+
' [[ -z "$dex_bin" ]] && return',
|
|
228
|
+
' context="$($dex_bin --prompt-context 2>/dev/null)"',
|
|
229
|
+
' [[ -z "$context" ]] && return',
|
|
230
|
+
' printf "\\[\\e[38;5;81m\\][%s]\\[\\e[0m\\]" "$context"',
|
|
231
|
+
'}',
|
|
232
|
+
'_dex_linux_prompt_bash() {',
|
|
233
|
+
' local badge',
|
|
234
|
+
' badge="$(_dex_linux_project_badge_bash)"',
|
|
235
|
+
' PS1="\\[\\e[38;5;45m\\]Dex@linux\\[\\e[0m\\] \\[\\e[38;5;117m\\]\\w\\[\\e[0m\\] ${badge}\\n\\[\\e[38;5;45m\\]>\\[\\e[0m\\] "',
|
|
236
|
+
'}',
|
|
237
|
+
'PROMPT_COMMAND=_dex_linux_prompt_bash',
|
|
238
|
+
'_dex_linux_prompt_bash',
|
|
239
|
+
'printf "Dex: acceso rapido cargado en %s\\n" "$PWD"',
|
|
240
|
+
].join('\n') + '\n';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return [
|
|
244
|
+
...commonLines,
|
|
245
|
+
`export PS1='[dex ${label}] \\w \\$ '`,
|
|
246
|
+
'printf "Dex: acceso rapido cargado en %s\\n" "$PWD"',
|
|
247
|
+
].join('\n') + '\n';
|
|
55
248
|
}
|
package/src/commands/context.js
CHANGED
|
@@ -2,6 +2,8 @@ import {
|
|
|
2
2
|
detectProjectContext,
|
|
3
3
|
formatProjectContext,
|
|
4
4
|
formatPromptContext,
|
|
5
|
+
formatPromptProjectRoot,
|
|
6
|
+
formatPromptProjectPath,
|
|
5
7
|
formatProjectContextDetails,
|
|
6
8
|
} from '../utils/project-context.js';
|
|
7
9
|
|
|
@@ -9,6 +11,26 @@ export async function runContextCommand(options = {}) {
|
|
|
9
11
|
const context = await detectProjectContext();
|
|
10
12
|
|
|
11
13
|
if (!context) {
|
|
14
|
+
if (!options.prompt && !options.projectRoot && !options.projectPath) {
|
|
15
|
+
console.log('No detecte un proyecto compatible en esta carpeta.');
|
|
16
|
+
console.log('Prueba entrando a un proyecto Python, Node, PHP, Rust, Go, Java o Ruby.');
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (options.projectRoot) {
|
|
22
|
+
const rootName = formatPromptProjectRoot(context);
|
|
23
|
+
if (rootName) {
|
|
24
|
+
process.stdout.write(rootName);
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (options.projectPath) {
|
|
30
|
+
const projectPath = formatPromptProjectPath(context);
|
|
31
|
+
if (projectPath) {
|
|
32
|
+
process.stdout.write(projectPath);
|
|
33
|
+
}
|
|
12
34
|
return;
|
|
13
35
|
}
|
|
14
36
|
|
package/src/commands/explain.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
import { printExplainEntry, printSection } from '../ui/output.js';
|
|
4
5
|
import { chooseExplainTopic } from '../ui/prompt.js';
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const EXPLAIN_DATA_PATH = path.resolve(MODULE_DIR, '..', '..', 'data', 'commands', 'explain.json');
|
|
7
9
|
|
|
8
10
|
export async function runExplainCommand({ topic }) {
|
|
9
11
|
const entries = await loadExplainEntries();
|
|
10
|
-
const selectedTopic = topic ? topic
|
|
12
|
+
const selectedTopic = topic ? normalizeTopic(topic) : await chooseExplainTopic(entries);
|
|
11
13
|
const entry = entries[selectedTopic];
|
|
12
14
|
|
|
13
15
|
if (!entry) {
|
|
@@ -23,3 +25,10 @@ async function loadExplainEntries() {
|
|
|
23
25
|
const raw = await fs.readFile(EXPLAIN_DATA_PATH, 'utf8');
|
|
24
26
|
return JSON.parse(raw);
|
|
25
27
|
}
|
|
28
|
+
|
|
29
|
+
function normalizeTopic(topic) {
|
|
30
|
+
return String(topic)
|
|
31
|
+
.trim()
|
|
32
|
+
.split(/\s+/)[0]
|
|
33
|
+
.toLowerCase();
|
|
34
|
+
}
|