dex-termux-cli 0.3.0-beta.2 → 0.3.0-beta.4
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/bin/dex-project-context +12 -0
- package/package.json +4 -2
- package/src/app/main.js +4 -2
- package/src/commands/android-shell.js +76 -12
- package/src/commands/menu.js +2 -1
- package/src/core/scopes.js +10 -0
- package/src/ui/output.js +5 -6
- package/src/utils/platform.js +44 -10
- package/src/utils/project-context.js +56 -31
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { main } from '../src/app/main.js';
|
|
3
|
+
|
|
4
|
+
const argv = process.argv.slice(2);
|
|
5
|
+
|
|
6
|
+
if (argv.includes('--root')) {
|
|
7
|
+
await main(['--prompt-project-root']);
|
|
8
|
+
} else if (argv.includes('--path')) {
|
|
9
|
+
await main(['--prompt-project-path']);
|
|
10
|
+
} else {
|
|
11
|
+
await main(['--prompt-context']);
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dex-termux-cli",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
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": [
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"bin": {
|
|
22
|
-
"dex": "bin/dex"
|
|
22
|
+
"dex": "bin/dex",
|
|
23
|
+
"dex-project-context": "bin/dex-project-context"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
26
|
"start": "node ./bin/dex",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"author": "farllirs",
|
|
30
31
|
"files": [
|
|
31
32
|
"bin/dex",
|
|
33
|
+
"bin/dex-project-context",
|
|
32
34
|
"src",
|
|
33
35
|
"data/commands/explain.json",
|
|
34
36
|
"README.md",
|
package/src/app/main.js
CHANGED
|
@@ -11,10 +11,12 @@ import { runTreeCommand } from '../commands/tree.js';
|
|
|
11
11
|
import { runVersionCommand } from '../commands/version.js';
|
|
12
12
|
import { printHelp, printProjectContextLine } from '../ui/output.js';
|
|
13
13
|
import { detectProjectContext, formatProjectContext } from '../utils/project-context.js';
|
|
14
|
+
import { resolvePlatformMode } from '../utils/platform.js';
|
|
14
15
|
|
|
15
16
|
export async function main(argv = process.argv.slice(2)) {
|
|
16
17
|
const parsed = parseArgs(argv);
|
|
17
18
|
const config = await loadUserConfig();
|
|
19
|
+
const platformMode = resolvePlatformMode(config);
|
|
18
20
|
const isPromptOnly = parsed.command === 'prompt-context' || parsed.command === 'prompt-project-root' || parsed.command === 'prompt-project-path';
|
|
19
21
|
const isContextOnly = parsed.command === 'context';
|
|
20
22
|
const isVersionOnly = parsed.command === 'version';
|
|
@@ -27,7 +29,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
if (parsed.help || !argv.length) {
|
|
30
|
-
printHelp();
|
|
32
|
+
printHelp(platformMode);
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -91,5 +93,5 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
91
93
|
return;
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
printHelp();
|
|
96
|
+
printHelp(platformMode);
|
|
95
97
|
}
|
|
@@ -197,18 +197,50 @@ function buildLinuxShellRc({ aliases, root, label, shellName }) {
|
|
|
197
197
|
return [
|
|
198
198
|
...commonLines,
|
|
199
199
|
'[[ -f "$HOME/.zshrc" ]] && source "$HOME/.zshrc"',
|
|
200
|
+
'_dex_linux_project_root_zsh() {',
|
|
201
|
+
' local helper',
|
|
202
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
203
|
+
' [[ -z "$helper" ]] && return',
|
|
204
|
+
' "$helper" --root 2>/dev/null',
|
|
205
|
+
'}',
|
|
206
|
+
'_dex_linux_project_path_zsh() {',
|
|
207
|
+
' local helper',
|
|
208
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
209
|
+
' [[ -z "$helper" ]] && return',
|
|
210
|
+
' "$helper" --path 2>/dev/null',
|
|
211
|
+
'}',
|
|
200
212
|
'_dex_linux_project_badge_zsh() {',
|
|
201
|
-
' local
|
|
202
|
-
'
|
|
203
|
-
' [[ -z "$
|
|
204
|
-
' context="$($
|
|
213
|
+
' local helper context',
|
|
214
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
215
|
+
' [[ -z "$helper" ]] && return',
|
|
216
|
+
' context="$("$helper" 2>/dev/null)"',
|
|
205
217
|
' [[ -z "$context" ]] && return',
|
|
206
218
|
' print -n "%F{81}[$context]%f"',
|
|
207
219
|
'}',
|
|
220
|
+
'_dex_linux_path_label_zsh() {',
|
|
221
|
+
' local project_name project_path current relative',
|
|
222
|
+
' project_name="$(_dex_linux_project_root_zsh)"',
|
|
223
|
+
' project_path="$(_dex_linux_project_path_zsh)"',
|
|
224
|
+
' current="$PWD"',
|
|
225
|
+
' if [[ -n "$project_name" && -n "$project_path" ]]; then',
|
|
226
|
+
' project_name="${project_name##*/}"',
|
|
227
|
+
' if [[ "$current" == "$project_path" ]]; then',
|
|
228
|
+
' print -r -- "$project_name"',
|
|
229
|
+
' return',
|
|
230
|
+
' fi',
|
|
231
|
+
' relative="${current#$project_path/}"',
|
|
232
|
+
' if [[ "$relative" != "$current" ]]; then',
|
|
233
|
+
' print -r -- "$project_name/$relative"',
|
|
234
|
+
' return',
|
|
235
|
+
' fi',
|
|
236
|
+
' fi',
|
|
237
|
+
' print -r -- "%~"',
|
|
238
|
+
'}',
|
|
208
239
|
'_dex_linux_prompt_zsh() {',
|
|
209
|
-
' local badge',
|
|
240
|
+
' local badge path_label',
|
|
210
241
|
' badge="$(_dex_linux_project_badge_zsh)"',
|
|
211
|
-
"
|
|
242
|
+
' path_label="$(_dex_linux_path_label_zsh)"',
|
|
243
|
+
" PROMPT=$'%F{45}Dex@linux%f %F{117}'\"${path_label}\"$'%f\\n%F{45}>%f '",
|
|
212
244
|
' RPROMPT="$badge"',
|
|
213
245
|
'}',
|
|
214
246
|
'autoload -Uz add-zsh-hook 2>/dev/null || true',
|
|
@@ -221,18 +253,50 @@ function buildLinuxShellRc({ aliases, root, label, shellName }) {
|
|
|
221
253
|
if (shellName === 'bash') {
|
|
222
254
|
return [
|
|
223
255
|
...commonLines,
|
|
256
|
+
'_dex_linux_project_root_bash() {',
|
|
257
|
+
' local helper',
|
|
258
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
259
|
+
' [[ -z "$helper" ]] && return',
|
|
260
|
+
' "$helper" --root 2>/dev/null',
|
|
261
|
+
'}',
|
|
262
|
+
'_dex_linux_project_path_bash() {',
|
|
263
|
+
' local helper',
|
|
264
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
265
|
+
' [[ -z "$helper" ]] && return',
|
|
266
|
+
' "$helper" --path 2>/dev/null',
|
|
267
|
+
'}',
|
|
224
268
|
'_dex_linux_project_badge_bash() {',
|
|
225
|
-
' local
|
|
226
|
-
'
|
|
227
|
-
' [[ -z "$
|
|
228
|
-
' context="$($
|
|
269
|
+
' local helper context',
|
|
270
|
+
' helper="$(command -v dex-project-context 2>/dev/null)"',
|
|
271
|
+
' [[ -z "$helper" ]] && return',
|
|
272
|
+
' context="$("$helper" 2>/dev/null)"',
|
|
229
273
|
' [[ -z "$context" ]] && return',
|
|
230
274
|
' printf "\\[\\e[38;5;81m\\][%s]\\[\\e[0m\\]" "$context"',
|
|
231
275
|
'}',
|
|
276
|
+
'_dex_linux_path_label_bash() {',
|
|
277
|
+
' local project_name project_path current relative',
|
|
278
|
+
' project_name="$(_dex_linux_project_root_bash)"',
|
|
279
|
+
' project_path="$(_dex_linux_project_path_bash)"',
|
|
280
|
+
' current="$PWD"',
|
|
281
|
+
' if [[ -n "$project_name" && -n "$project_path" ]]; then',
|
|
282
|
+
' project_name="${project_name##*/}"',
|
|
283
|
+
' if [[ "$current" == "$project_path" ]]; then',
|
|
284
|
+
' printf "%s" "$project_name"',
|
|
285
|
+
' return',
|
|
286
|
+
' fi',
|
|
287
|
+
' relative="${current#$project_path/}"',
|
|
288
|
+
' if [[ "$relative" != "$current" ]]; then',
|
|
289
|
+
' printf "%s/%s" "$project_name" "$relative"',
|
|
290
|
+
' return',
|
|
291
|
+
' fi',
|
|
292
|
+
' fi',
|
|
293
|
+
' printf "%s" "\\w"',
|
|
294
|
+
'}',
|
|
232
295
|
'_dex_linux_prompt_bash() {',
|
|
233
|
-
' local badge',
|
|
296
|
+
' local badge path_label',
|
|
234
297
|
' badge="$(_dex_linux_project_badge_bash)"',
|
|
235
|
-
'
|
|
298
|
+
' path_label="$(_dex_linux_path_label_bash)"',
|
|
299
|
+
' PS1="\\[\\e[38;5;45m\\]Dex@linux\\[\\e[0m\\] \\[\\e[38;5;117m\\]${path_label}\\[\\e[0m\\] ${badge}\\n\\[\\e[38;5;45m\\]>\\[\\e[0m\\] "',
|
|
236
300
|
'}',
|
|
237
301
|
'PROMPT_COMMAND=_dex_linux_prompt_bash',
|
|
238
302
|
'_dex_linux_prompt_bash',
|
package/src/commands/menu.js
CHANGED
package/src/core/scopes.js
CHANGED
|
@@ -30,6 +30,16 @@ export function getScopeOptions(platformMode) {
|
|
|
30
30
|
root: getAndroidStorageRoot(platformMode),
|
|
31
31
|
description: 'Busca dentro del almacenamiento compartido accesible.',
|
|
32
32
|
});
|
|
33
|
+
} else {
|
|
34
|
+
const sharedRoot = getAndroidStorageRoot(platformMode);
|
|
35
|
+
if (sharedRoot) {
|
|
36
|
+
options.push({
|
|
37
|
+
key: 'shared',
|
|
38
|
+
label: 'Almacenamiento compartido',
|
|
39
|
+
root: sharedRoot,
|
|
40
|
+
description: 'Busca dentro del almacenamiento compartido montado en Linux.',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
return options;
|
package/src/ui/output.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { getUserConfigPath } from '../core/config.js';
|
|
3
|
+
import { getScopeOptions } from '../core/scopes.js';
|
|
4
|
+
import { isHostTermux } from '../utils/platform.js';
|
|
3
5
|
|
|
4
6
|
const ANSI = {
|
|
5
7
|
bold: '\x1b[1m',
|
|
@@ -9,7 +11,7 @@ const ANSI = {
|
|
|
9
11
|
reset: '\x1b[0m',
|
|
10
12
|
};
|
|
11
13
|
|
|
12
|
-
export function printHelp() {
|
|
14
|
+
export function printHelp(platformMode = '') {
|
|
13
15
|
printBanner();
|
|
14
16
|
console.log('');
|
|
15
17
|
console.log(' Explorar, entender y preparar proyectos desde Termux o Linux.');
|
|
@@ -37,11 +39,8 @@ export function printHelp() {
|
|
|
37
39
|
'dex --prompt-project-path',
|
|
38
40
|
]);
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
'home tu HOME del entorno actual',
|
|
43
|
-
'android almacenamiento accesible en modo Termux',
|
|
44
|
-
]);
|
|
42
|
+
const scopeOptions = getScopeOptions(platformMode || (isHostTermux() ? 'termux' : 'linux'));
|
|
43
|
+
printBlock('Scopes', scopeOptions.map((option) => `${option.key.padEnd(19, ' ')}${option.description}`));
|
|
45
44
|
|
|
46
45
|
printBlock('Atajos', [
|
|
47
46
|
'-h --help ayuda',
|
package/src/utils/platform.js
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
|
|
3
4
|
const TERMUX_HOME = '/data/data/com.termux/files/home';
|
|
4
5
|
const ANDROID_STORAGE = '/sdcard';
|
|
6
|
+
const SHARED_STORAGE_CANDIDATES = [
|
|
7
|
+
'/storage/emulated/0',
|
|
8
|
+
'/sdcard',
|
|
9
|
+
];
|
|
5
10
|
|
|
6
11
|
export function getHomeDirectory() {
|
|
7
12
|
return process.env.HOME || TERMUX_HOME;
|
|
8
13
|
}
|
|
9
14
|
|
|
15
|
+
export function getSharedStorageRoot() {
|
|
16
|
+
for (const candidate of SHARED_STORAGE_CANDIDATES) {
|
|
17
|
+
try {
|
|
18
|
+
if (fs.existsSync(candidate)) {
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// ignore
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
export function isHostTermux() {
|
|
11
30
|
const home = getHomeDirectory();
|
|
12
31
|
return Boolean(process.env.TERMUX_VERSION) || home === TERMUX_HOME || home.startsWith(TERMUX_HOME + path.sep);
|
|
@@ -53,11 +72,21 @@ export function canUseAndroidFeatures(platformMode) {
|
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
export function getAndroidStorageRoot(platformMode) {
|
|
56
|
-
|
|
75
|
+
if (canUseAndroidFeatures(platformMode)) {
|
|
76
|
+
return ANDROID_STORAGE;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return getSharedStorageRoot();
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
export function getQuickAccessRoot(platformMode) {
|
|
60
|
-
|
|
83
|
+
const sharedStorage = getSharedStorageRoot();
|
|
84
|
+
|
|
85
|
+
if (platformMode === 'termux') {
|
|
86
|
+
return sharedStorage || ANDROID_STORAGE;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return sharedStorage || getHomeDirectory();
|
|
61
90
|
}
|
|
62
91
|
|
|
63
92
|
export function getQuickAccessTitle(platformMode) {
|
|
@@ -71,13 +100,17 @@ export function getQuickAccessModeDescription(platformMode) {
|
|
|
71
100
|
}
|
|
72
101
|
|
|
73
102
|
export function getQuickAccessLabel(platformMode) {
|
|
74
|
-
|
|
103
|
+
if (platformMode === 'termux') {
|
|
104
|
+
return 'ANDROID STORAGE';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return getSharedStorageRoot() ? 'SHARED STORAGE' : 'LINUX HOME';
|
|
75
108
|
}
|
|
76
109
|
|
|
77
110
|
export function getQuickAccessShortcutSummary(platformMode) {
|
|
78
111
|
return platformMode === 'termux'
|
|
79
112
|
? 'dl, docs, dcim, pics, music, movies, shared'
|
|
80
|
-
: 'home, dl, docs, desk, pics, music, vids, tmp
|
|
113
|
+
: 'shared, home, dl, docs, desk, pics, music, vids, tmp';
|
|
81
114
|
}
|
|
82
115
|
|
|
83
116
|
export function getQuickAccessAliases(platformMode) {
|
|
@@ -94,15 +127,16 @@ export function getQuickAccessAliases(platformMode) {
|
|
|
94
127
|
}
|
|
95
128
|
|
|
96
129
|
const home = getHomeDirectory();
|
|
130
|
+
const shared = getSharedStorageRoot() || home;
|
|
97
131
|
return {
|
|
98
132
|
home,
|
|
99
|
-
shared
|
|
100
|
-
dl: path.join(
|
|
101
|
-
docs: path.join(
|
|
133
|
+
shared,
|
|
134
|
+
dl: path.join(shared, 'Download'),
|
|
135
|
+
docs: path.join(shared, 'Documents'),
|
|
102
136
|
desk: path.join(home, 'Desktop'),
|
|
103
|
-
pics: path.join(
|
|
104
|
-
music: path.join(
|
|
105
|
-
vids: path.join(
|
|
137
|
+
pics: path.join(shared, 'Pictures'),
|
|
138
|
+
music: path.join(shared, 'Music'),
|
|
139
|
+
vids: path.join(shared, 'Movies'),
|
|
106
140
|
tmp: '/tmp',
|
|
107
141
|
};
|
|
108
142
|
}
|
|
@@ -24,6 +24,8 @@ const LANGUAGE_META = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const MAX_PARENT_LOOKUP = 16;
|
|
27
|
+
const MAX_IMPLICIT_SCAN_DEPTH = 3;
|
|
28
|
+
const MAX_IMPLICIT_SCAN_ENTRIES = 160;
|
|
27
29
|
|
|
28
30
|
export async function detectProjectContext(cwd = process.cwd()) {
|
|
29
31
|
const searchChain = buildSearchChain(cwd);
|
|
@@ -136,7 +138,7 @@ async function detectProjectContextAt(targetDir, cwd) {
|
|
|
136
138
|
return createContext('ruby', cwd, targetDir, await detectRubyVersion(targetDir), 'implicit');
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
const nestedImplicitType = await
|
|
141
|
+
const nestedImplicitType = await detectImplicitLanguageFromTree(targetDir);
|
|
140
142
|
if (nestedImplicitType) {
|
|
141
143
|
return createContext(nestedImplicitType, cwd, targetDir, await detectVersionForType(nestedImplicitType, targetDir), 'implicit');
|
|
142
144
|
}
|
|
@@ -144,51 +146,60 @@ async function detectProjectContextAt(targetDir, cwd) {
|
|
|
144
146
|
return null;
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
async function
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
.slice(0, 8);
|
|
149
|
+
async function detectImplicitLanguageFromTree(rootDir) {
|
|
150
|
+
const queue = [{ dir: rootDir, depth: 0 }];
|
|
151
|
+
let visitedEntries = 0;
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
const
|
|
153
|
+
while (queue.length && visitedEntries < MAX_IMPLICIT_SCAN_ENTRIES) {
|
|
154
|
+
const current = queue.shift();
|
|
155
|
+
const nestedEntries = await safeReadDir(current.dir);
|
|
154
156
|
if (!nestedEntries.length) {
|
|
155
157
|
continue;
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
160
|
+
for (const item of nestedEntries) {
|
|
161
|
+
visitedEntries += 1;
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
if (!item.isDirectory()) {
|
|
164
|
+
if (item.name.endsWith('.py')) {
|
|
165
|
+
return 'python';
|
|
166
|
+
}
|
|
165
167
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
if (isNodeScript(item.name)) {
|
|
169
|
+
return 'node';
|
|
170
|
+
}
|
|
169
171
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
172
|
+
if (item.name.endsWith('.php')) {
|
|
173
|
+
return 'php';
|
|
174
|
+
}
|
|
174
175
|
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
if (item.name.endsWith('.rb')) {
|
|
177
|
+
return 'ruby';
|
|
178
|
+
}
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
if (current.depth + 1 > MAX_IMPLICIT_SCAN_DEPTH) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
183
186
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
if (shouldSkipImplicitDirectory(item.name)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
187
190
|
|
|
188
|
-
|
|
191
|
+
queue.push({
|
|
192
|
+
dir: path.join(current.dir, item.name),
|
|
193
|
+
depth: current.depth + 1,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
|
-
return
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function pickBestDetection(detections) {
|
|
202
|
+
return detections[0];
|
|
192
203
|
}
|
|
193
204
|
|
|
194
205
|
function createContext(type, cwd, projectRoot, version, detectionKind) {
|
|
@@ -402,3 +413,17 @@ function firstNonEmptyLine(content) {
|
|
|
402
413
|
function isNodeScript(name) {
|
|
403
414
|
return name.endsWith('.js') || name.endsWith('.mjs') || name.endsWith('.cjs') || name.endsWith('.ts');
|
|
404
415
|
}
|
|
416
|
+
|
|
417
|
+
function shouldSkipImplicitDirectory(name) {
|
|
418
|
+
return [
|
|
419
|
+
'.git',
|
|
420
|
+
'.venv',
|
|
421
|
+
'node_modules',
|
|
422
|
+
'__pycache__',
|
|
423
|
+
'.mypy_cache',
|
|
424
|
+
'.pytest_cache',
|
|
425
|
+
'.ruff_cache',
|
|
426
|
+
'dist',
|
|
427
|
+
'build',
|
|
428
|
+
].includes(name);
|
|
429
|
+
}
|