dex-termux-cli 0.3.0-beta.1 → 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 +3 -3
- package/package.json +9 -6
- package/src/commands/android-shell.js +192 -41
- package/src/commands/menu.js +3 -3
- package/src/commands/version.js +84 -11
- package/src/core/args.js +3 -3
- package/src/core/config.js +4 -3
- package/src/ui/output.js +3 -2
- package/src/ui/prompt.js +2 -2
- package/src/utils/platform.js +51 -0
- package/src/utils/project-context.js +69 -1
package/README.md
CHANGED
|
@@ -35,13 +35,13 @@ Dex mejora tareas comunes de terminal sin tapar el entorno real. Sirve para expl
|
|
|
35
35
|
- `dex -b`: busqueda guiada
|
|
36
36
|
- `dex -e`: explicacion de comandos comunes
|
|
37
37
|
- `dex -t`: tree guiado
|
|
38
|
-
- `dex -a`: shell redisenada para Android storage en
|
|
38
|
+
- `dex -a`: shell de acceso rapido redisenada para Android storage en Termux o para HOME en Linux
|
|
39
39
|
- `dex -i`: instalacion del proyecto actual incluso desde subcarpetas del proyecto
|
|
40
40
|
- `dex -r`: shell segura del proyecto actual incluso desde subcarpetas del proyecto
|
|
41
41
|
|
|
42
42
|
## Contexto del proyecto
|
|
43
43
|
|
|
44
|
-
Dex detecta el lenguaje segun los archivos del directorio actual. 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.
|
|
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.
|
|
45
45
|
|
|
46
46
|
Lenguajes detectables actualmente:
|
|
47
47
|
|
|
@@ -78,7 +78,7 @@ El modo actual se guarda en `~/.config/dex/config.json`.
|
|
|
78
78
|
## Estado real de la beta
|
|
79
79
|
|
|
80
80
|
- `dex -m`, `-a`, `-b`, `-t`, `-v`, `-c` y `--prompt-context` funcionan
|
|
81
|
-
- `dex -a` ahora abre una
|
|
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
82
|
- `dex -i` funciona en proyectos validos y ahora usa el root detectado del proyecto aunque entres desde una subcarpeta
|
|
83
83
|
- `dex -r` funciona para proyectos soportados, hereda mejor el root del proyecto y en Termux exige Android storage
|
|
84
84
|
- el modo seguro real ya cubre Python, Node, PHP, Ruby, Go, Rust y Java
|
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dex-termux-cli",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Visual CLI for Termux and Linux with guided search, readable tree views, project context, version checks, and safe flows for Python, Node, and
|
|
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": [
|
|
@@ -38,8 +44,5 @@
|
|
|
38
44
|
},
|
|
39
45
|
"engines": {
|
|
40
46
|
"node": ">=18"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"tree": "^0.1.3"
|
|
44
47
|
}
|
|
45
48
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
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';
|
|
5
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getQuickAccessAliases,
|
|
9
|
+
getQuickAccessLabel,
|
|
10
|
+
getQuickAccessModeDescription,
|
|
11
|
+
getQuickAccessRoot,
|
|
12
|
+
getQuickAccessShortcutSummary,
|
|
13
|
+
getQuickAccessTitle,
|
|
14
|
+
resolvePlatformMode,
|
|
15
|
+
} from '../utils/platform.js';
|
|
6
16
|
|
|
7
17
|
const ANDROID_BASH_CANDIDATES = [
|
|
8
18
|
'/data/data/com.termux/files/usr/bin/bash',
|
|
@@ -14,63 +24,64 @@ export async function runAndroidShellCommand() {
|
|
|
14
24
|
|
|
15
25
|
if (!config.features.androidShortcut) {
|
|
16
26
|
throw new Error(
|
|
17
|
-
`
|
|
27
|
+
`El acceso rapido esta desactivado. Activa features.androidShortcut en ${getUserConfigPath()}`,
|
|
18
28
|
);
|
|
19
29
|
}
|
|
20
30
|
|
|
21
31
|
const platformMode = resolvePlatformMode(config);
|
|
22
|
-
|
|
23
|
-
if (!canUseAndroidFeatures(platformMode)) {
|
|
24
|
-
throw new Error('dex -a solo esta disponible en modo Termux/Android. Cambia el modo Linux desde ajustes si quieres evitar esta opcion.');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const androidRoot = getAndroidStorageRoot(platformMode);
|
|
32
|
+
const quickAccessRoot = getQuickAccessRoot(platformMode);
|
|
28
33
|
|
|
29
34
|
try {
|
|
30
|
-
await fs.access(
|
|
35
|
+
await fs.access(quickAccessRoot);
|
|
31
36
|
} catch {
|
|
32
|
-
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}`);
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
const interactiveShell = await resolveAndroidInteractiveShell(platformMode);
|
|
41
|
+
const shellSession = await createQuickAccessShellSession(platformMode, interactiveShell);
|
|
36
42
|
|
|
37
|
-
console.log(
|
|
43
|
+
console.log(getQuickAccessTitle(platformMode));
|
|
38
44
|
console.log('');
|
|
39
|
-
console.log(`Raiz : ${
|
|
45
|
+
console.log(`Raiz : ${quickAccessRoot}`);
|
|
40
46
|
console.log(`Shell : ${interactiveShell.shellName} interactiva`);
|
|
41
|
-
console.log(
|
|
42
|
-
console.log(
|
|
47
|
+
console.log(`Modo : ${getQuickAccessModeDescription(platformMode)}`);
|
|
48
|
+
console.log(`Atajos : ${getQuickAccessShortcutSummary(platformMode)}`);
|
|
43
49
|
console.log('Salida : escribe exit para volver');
|
|
44
50
|
console.log('');
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
});
|
|
66
|
+
|
|
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
|
+
}
|
|
73
|
+
|
|
74
|
+
if (code && code !== 0) {
|
|
75
|
+
reject(new Error(`La shell de acceso rapido termino con codigo ${code}`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
resolve();
|
|
80
|
+
});
|
|
72
81
|
});
|
|
73
|
-
}
|
|
82
|
+
} finally {
|
|
83
|
+
await shellSession.cleanup();
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
async function resolveAndroidInteractiveShell(platformMode) {
|
|
@@ -95,3 +106,143 @@ async function resolveAndroidInteractiveShell(platformMode) {
|
|
|
95
106
|
|
|
96
107
|
return interactiveShell;
|
|
97
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,
|
|
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';
|
|
248
|
+
}
|
package/src/commands/menu.js
CHANGED
|
@@ -101,8 +101,8 @@ async function runSettingsMenu() {
|
|
|
101
101
|
if (action === 'toggle-android-shortcut') {
|
|
102
102
|
const toggle = await chooseFeatureToggle(
|
|
103
103
|
config.features.androidShortcut,
|
|
104
|
-
'Acceso rapido
|
|
105
|
-
'permite usar dex -a',
|
|
104
|
+
'Acceso rapido',
|
|
105
|
+
'permite usar dex -a en Termux o Linux',
|
|
106
106
|
);
|
|
107
107
|
|
|
108
108
|
if (toggle === 'back') {
|
|
@@ -111,7 +111,7 @@ async function runSettingsMenu() {
|
|
|
111
111
|
|
|
112
112
|
const enabled = toggle === 'enable';
|
|
113
113
|
await setFeatureEnabled('androidShortcut', enabled);
|
|
114
|
-
console.log(`Acceso rapido
|
|
114
|
+
console.log(`Acceso rapido: ${enabled ? 'activado' : 'desactivado'}.`);
|
|
115
115
|
console.log('');
|
|
116
116
|
continue;
|
|
117
117
|
}
|
package/src/commands/version.js
CHANGED
|
@@ -69,14 +69,14 @@ async function readLatestPublishedVersion() {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function compareVersions(left, right) {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const maxLength = Math.max(
|
|
72
|
+
export function compareVersions(left, right) {
|
|
73
|
+
const leftVersion = parseSemver(left);
|
|
74
|
+
const rightVersion = parseSemver(right);
|
|
75
|
+
const maxLength = Math.max(leftVersion.core.length, rightVersion.core.length);
|
|
76
76
|
|
|
77
77
|
for (let index = 0; index < maxLength; index += 1) {
|
|
78
|
-
const leftValue =
|
|
79
|
-
const rightValue =
|
|
78
|
+
const leftValue = leftVersion.core[index] || 0;
|
|
79
|
+
const rightValue = rightVersion.core[index] || 0;
|
|
80
80
|
|
|
81
81
|
if (leftValue > rightValue) {
|
|
82
82
|
return 1;
|
|
@@ -87,12 +87,85 @@ function compareVersions(left, right) {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
return comparePrerelease(leftVersion.prerelease, rightVersion.prerelease);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseSemver(version) {
|
|
94
|
+
const normalized = String(version).trim();
|
|
95
|
+
const [mainPart = '', prereleasePart = ''] = normalized.split('-', 2);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
core: mainPart
|
|
99
|
+
.split('.')
|
|
100
|
+
.map((part) => Number.parseInt(part, 10))
|
|
101
|
+
.filter((part) => Number.isInteger(part)),
|
|
102
|
+
prerelease: prereleasePart
|
|
103
|
+
? prereleasePart
|
|
104
|
+
.split('.')
|
|
105
|
+
.filter(Boolean)
|
|
106
|
+
.map((part) => (/^\d+$/.test(part) ? Number.parseInt(part, 10) : part.toLowerCase()))
|
|
107
|
+
: [],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function comparePrerelease(left, right) {
|
|
112
|
+
const leftHasPrerelease = left.length > 0;
|
|
113
|
+
const rightHasPrerelease = right.length > 0;
|
|
114
|
+
|
|
115
|
+
if (!leftHasPrerelease && !rightHasPrerelease) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!leftHasPrerelease) {
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!rightHasPrerelease) {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const maxLength = Math.max(left.length, right.length);
|
|
128
|
+
|
|
129
|
+
for (let index = 0; index < maxLength; index += 1) {
|
|
130
|
+
const leftPart = left[index];
|
|
131
|
+
const rightPart = right[index];
|
|
132
|
+
|
|
133
|
+
if (leftPart === undefined) {
|
|
134
|
+
return -1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (rightPart === undefined) {
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const comparison = comparePrereleasePart(leftPart, rightPart);
|
|
142
|
+
if (comparison !== 0) {
|
|
143
|
+
return comparison;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
90
147
|
return 0;
|
|
91
148
|
}
|
|
92
149
|
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
150
|
+
function comparePrereleasePart(left, right) {
|
|
151
|
+
const leftIsNumber = typeof left === 'number';
|
|
152
|
+
const rightIsNumber = typeof right === 'number';
|
|
153
|
+
|
|
154
|
+
if (leftIsNumber && rightIsNumber) {
|
|
155
|
+
return left === right ? 0 : left > right ? 1 : -1;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (leftIsNumber) {
|
|
159
|
+
return -1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (rightIsNumber) {
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (left === right) {
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return left > right ? 1 : -1;
|
|
98
171
|
}
|
package/src/core/args.js
CHANGED
|
@@ -47,7 +47,7 @@ export function parseArgs(argv) {
|
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (token === '-b' || token === '--buscar') {
|
|
50
|
+
if (token === '-b' || token === '--buscar' || token === '--search' || token === 'search') {
|
|
51
51
|
parsed.command = 'search';
|
|
52
52
|
const next = argv[index + 1];
|
|
53
53
|
if (next && !next.startsWith('-')) {
|
|
@@ -57,7 +57,7 @@ export function parseArgs(argv) {
|
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
if (token === '-e' || token === '--explicar' || token === 'explicar') {
|
|
60
|
+
if (token === '-e' || token === '--explicar' || token === '--explain' || token === 'explicar' || token === 'explain') {
|
|
61
61
|
parsed.command = 'explain';
|
|
62
62
|
const next = argv[index + 1];
|
|
63
63
|
if (next && !next.startsWith('-')) {
|
|
@@ -87,7 +87,7 @@ export function parseArgs(argv) {
|
|
|
87
87
|
continue;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
if (token === '-r' || token === '--seguro' || token === '--safe-shell' || token === 'seguro') {
|
|
90
|
+
if (token === '-r' || token === '--seguro' || token === '--safe-shell' || token === 'seguro' || token === 'safe-shell') {
|
|
91
91
|
parsed.command = 'safe-shell';
|
|
92
92
|
continue;
|
|
93
93
|
}
|
package/src/core/config.js
CHANGED
|
@@ -4,8 +4,8 @@ import { getHomeDirectory, normalizePlatformMode } from '../utils/platform.js';
|
|
|
4
4
|
|
|
5
5
|
const DEFAULT_CONFIG = {
|
|
6
6
|
features: {
|
|
7
|
-
androidShortcut:
|
|
8
|
-
projectBadge:
|
|
7
|
+
androidShortcut: true,
|
|
8
|
+
projectBadge: true,
|
|
9
9
|
smartProjectInstall: false,
|
|
10
10
|
},
|
|
11
11
|
ui: {
|
|
@@ -82,13 +82,14 @@ export async function setPlatformMode(platformMode) {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
if (normalized === 'linux') {
|
|
85
|
-
nextFeatures.androidShortcut =
|
|
85
|
+
nextFeatures.androidShortcut = true;
|
|
86
86
|
nextFeatures.projectBadge = true;
|
|
87
87
|
nextFeatures.smartProjectInstall = true;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
if (normalized === 'termux') {
|
|
91
91
|
nextFeatures.androidShortcut = true;
|
|
92
|
+
nextFeatures.projectBadge = true;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
return saveUserConfig({
|
package/src/ui/output.js
CHANGED
|
@@ -25,7 +25,7 @@ export function printHelp() {
|
|
|
25
25
|
'dex -t src arbol guiado de una ruta',
|
|
26
26
|
'dex -i instalar dependencias del proyecto detectado',
|
|
27
27
|
'dex -r abrir modo seguro del proyecto (Py/Node/PHP/Ruby/Go/Rust/Java)',
|
|
28
|
-
'dex -a acceso rapido a Android',
|
|
28
|
+
'dex -a acceso rapido a Android o Linux',
|
|
29
29
|
]);
|
|
30
30
|
|
|
31
31
|
printBlock('Uso rapido', [
|
|
@@ -33,7 +33,8 @@ export function printHelp() {
|
|
|
33
33
|
'dex -t . --depth 3 --scope actual',
|
|
34
34
|
'dex -c',
|
|
35
35
|
'dex --prompt-context',
|
|
36
|
-
'dex-project-
|
|
36
|
+
'dex --prompt-project-root',
|
|
37
|
+
'dex --prompt-project-path',
|
|
37
38
|
]);
|
|
38
39
|
|
|
39
40
|
printBlock('Scopes', [
|
package/src/ui/prompt.js
CHANGED
|
@@ -75,9 +75,9 @@ export async function chooseSettingsAction(config) {
|
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
key: 'toggle-android-shortcut',
|
|
78
|
-
label: 'Acceso rapido
|
|
78
|
+
label: 'Acceso rapido',
|
|
79
79
|
status: formatStatus(config.features.androidShortcut),
|
|
80
|
-
usage: 'permite dex -a en Termux',
|
|
80
|
+
usage: 'permite dex -a en Termux o Linux',
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
key: 'toggle-project-badge',
|
package/src/utils/platform.js
CHANGED
|
@@ -56,6 +56,57 @@ export function getAndroidStorageRoot(platformMode) {
|
|
|
56
56
|
return canUseAndroidFeatures(platformMode) ? ANDROID_STORAGE : '';
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export function getQuickAccessRoot(platformMode) {
|
|
60
|
+
return platformMode === 'termux' ? ANDROID_STORAGE : getHomeDirectory();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getQuickAccessTitle(platformMode) {
|
|
64
|
+
return platformMode === 'termux' ? 'Dex Android' : 'Dex Linux';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getQuickAccessModeDescription(platformMode) {
|
|
68
|
+
return platformMode === 'termux'
|
|
69
|
+
? 'interfaz Android redisenada'
|
|
70
|
+
: 'interfaz Linux redisenada';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getQuickAccessLabel(platformMode) {
|
|
74
|
+
return platformMode === 'termux' ? 'ANDROID STORAGE' : 'LINUX HOME';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getQuickAccessShortcutSummary(platformMode) {
|
|
78
|
+
return platformMode === 'termux'
|
|
79
|
+
? 'dl, docs, dcim, pics, music, movies, shared'
|
|
80
|
+
: 'home, dl, docs, desk, pics, music, vids, tmp, shared';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getQuickAccessAliases(platformMode) {
|
|
84
|
+
if (platformMode === 'termux') {
|
|
85
|
+
return {
|
|
86
|
+
shared: '/sdcard',
|
|
87
|
+
dl: '/sdcard/Download',
|
|
88
|
+
docs: '/sdcard/Documents',
|
|
89
|
+
dcim: '/sdcard/DCIM',
|
|
90
|
+
pics: '/sdcard/Pictures',
|
|
91
|
+
music: '/sdcard/Music',
|
|
92
|
+
movies: '/sdcard/Movies',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const home = getHomeDirectory();
|
|
97
|
+
return {
|
|
98
|
+
home,
|
|
99
|
+
shared: home,
|
|
100
|
+
dl: path.join(home, 'Downloads'),
|
|
101
|
+
docs: path.join(home, 'Documents'),
|
|
102
|
+
desk: path.join(home, 'Desktop'),
|
|
103
|
+
pics: path.join(home, 'Pictures'),
|
|
104
|
+
music: path.join(home, 'Music'),
|
|
105
|
+
vids: path.join(home, 'Videos'),
|
|
106
|
+
tmp: '/tmp',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
59
110
|
export function shouldRestrictProjectToAndroidStorage(platformMode) {
|
|
60
111
|
return platformMode === 'termux';
|
|
61
112
|
}
|
|
@@ -63,7 +63,7 @@ export function formatPromptContext(context) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export function formatPromptProjectRoot(context) {
|
|
66
|
-
return
|
|
66
|
+
return context.projectRoot || context.cwd;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export function formatPromptProjectPath(context) {
|
|
@@ -136,9 +136,45 @@ async function detectProjectContextAt(targetDir, cwd) {
|
|
|
136
136
|
return createContext('ruby', cwd, targetDir, await detectRubyVersion(targetDir), 'implicit');
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
const nestedImplicitType = await detectImplicitLanguageFromChildren(targetDir, entries);
|
|
140
|
+
if (nestedImplicitType) {
|
|
141
|
+
return createContext(nestedImplicitType, cwd, targetDir, await detectVersionForType(nestedImplicitType, targetDir), 'implicit');
|
|
142
|
+
}
|
|
143
|
+
|
|
139
144
|
return null;
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
async function detectImplicitLanguageFromChildren(targetDir, entries) {
|
|
148
|
+
const childDirectories = entries
|
|
149
|
+
.filter((entry) => entry.isDirectory())
|
|
150
|
+
.slice(0, 8);
|
|
151
|
+
|
|
152
|
+
for (const entry of childDirectories) {
|
|
153
|
+
const nestedEntries = await safeReadDir(path.join(targetDir, entry.name));
|
|
154
|
+
if (!nestedEntries.length) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (nestedEntries.some((item) => !item.isDirectory() && item.name.endsWith('.py'))) {
|
|
159
|
+
return 'python';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (nestedEntries.some((item) => !item.isDirectory() && isNodeScript(item.name))) {
|
|
163
|
+
return 'node';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (nestedEntries.some((item) => !item.isDirectory() && item.name.endsWith('.php'))) {
|
|
167
|
+
return 'php';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (nestedEntries.some((item) => !item.isDirectory() && item.name.endsWith('.rb'))) {
|
|
171
|
+
return 'ruby';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return '';
|
|
176
|
+
}
|
|
177
|
+
|
|
142
178
|
function pickBestDetection(detections) {
|
|
143
179
|
let bestMatch = detections[0];
|
|
144
180
|
|
|
@@ -279,6 +315,38 @@ async function detectJavaVersion(cwd) {
|
|
|
279
315
|
);
|
|
280
316
|
}
|
|
281
317
|
|
|
318
|
+
async function detectVersionForType(type, cwd) {
|
|
319
|
+
if (type === 'python') {
|
|
320
|
+
return detectPythonVersion(cwd);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (type === 'node') {
|
|
324
|
+
return detectNodeVersion(cwd);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (type === 'php') {
|
|
328
|
+
return detectPhpVersion(cwd);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (type === 'ruby') {
|
|
332
|
+
return detectRubyVersion(cwd);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (type === 'go') {
|
|
336
|
+
return detectGoVersion(cwd);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (type === 'rust') {
|
|
340
|
+
return detectRustVersion(cwd);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (type === 'java') {
|
|
344
|
+
return detectJavaVersion(cwd);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
|
|
282
350
|
async function safeReadDir(directoryPath) {
|
|
283
351
|
try {
|
|
284
352
|
return await fs.readdir(directoryPath, { withFileTypes: true });
|