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 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 búsqueda guiada, árbol legible, explicación de comandos, contexto de proyecto y flujos de instalación segura para proyectos dentro de Android storage.
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="status" src="https://img.shields.io/badge/status-active-111111?style=flat-square&labelColor=7C3AED&color=1F2937">
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, con lectura compacta del contexto del proyecto actual.
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
- ## Visión general
25
+ ## Vision general
27
26
 
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.
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
- ## En qué destaca
29
+ ## Lo que ya incluye
31
30
 
32
- - interfaz más legible para tareas comunes de terminal
33
- - detección del lenguaje del proyecto actual con versión recortada
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 -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 puede detectar el tipo de proyecto desde la carpeta actual y resumirlo en una forma compacta como `PY 3.11`, `NODE 20` o `PHP 8.2`. Ese formato está pensado tanto para el comando `dex -c` como para integrarlo al prompt de la terminal.
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
- | Lenguaje | Estrategia de instalación | Modo seguro |
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
- ### Python
75
- Dex prepara un `venv` seguro y abre una shell con el entorno correcto para ejecutar `python`, `pip` o el entrypoint del proyecto.
48
+ - Python
49
+ - Node
50
+ - PHP
51
+ - Rust
52
+ - Go
53
+ - Java
54
+ - Ruby
76
55
 
77
- ### Node
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
- ### PHP
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
- ## Estado actual
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
- - instalación segura activa para Python, Node y PHP
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
- ## Roadmap
66
+ ## Instalacion segura por lenguaje
91
67
 
92
- - soporte seguro para Ruby, Go y Rust
93
- - mejor traducción de errores nativos y de compilación
94
- - ejecución guiada de entrypoints del proyecto
95
- - refinamiento final de branding y presentación pública
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",
3
+ "version": "0.3.0-beta.2",
4
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.",
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 ANDROID_ROOT = '/sdcard';
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
- `La opcion Android esta desactivada. Activa features.androidShortcut en ${getUserConfigPath()}`,
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(ANDROID_ROOT);
35
+ await fs.access(quickAccessRoot);
19
36
  } catch {
20
- throw new Error(`No se puede acceder a la ruta Android: ${ANDROID_ROOT}`);
37
+ throw new Error(`No se puede acceder a la ruta base de acceso rapido: ${quickAccessRoot}`);
21
38
  }
22
39
 
23
- const interactiveShell = await resolveInteractiveShell();
40
+ const interactiveShell = await resolveAndroidInteractiveShell(platformMode);
41
+ const shellSession = await createQuickAccessShellSession(platformMode, interactiveShell);
24
42
 
25
- console.log(`Abriendo shell en ${ANDROID_ROOT}`);
43
+ console.log(getQuickAccessTitle(platformMode));
44
+ console.log('');
45
+ console.log(`Raiz : ${quickAccessRoot}`);
26
46
  console.log(`Shell : ${interactiveShell.shellName} interactiva`);
27
- console.log('Escribe exit para volver.');
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
- await new Promise((resolve, reject) => {
31
- const child = spawn(interactiveShell.shellPath, interactiveShell.shellArgs, {
32
- cwd: ANDROID_ROOT,
33
- stdio: 'inherit',
34
- env: {
35
- ...process.env,
36
- DEX_CONTEXT: 'android-shell',
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
- child.on('error', reject);
41
- child.on('exit', (code, signal) => {
42
- if (signal) {
43
- reject(new Error(`La shell Android termino por senal: ${signal}`));
44
- return;
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
- if (code && code !== 0) {
48
- reject(new Error(`La shell Android termino con codigo ${code}`));
49
- return;
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
- resolve();
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
  }
@@ -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
 
@@ -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 EXPLAIN_DATA_PATH = path.resolve(process.cwd(), 'data/commands/explain.json');
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.toLowerCase() : await chooseExplainTopic(entries);
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
+ }