karnel-termux 4.7.6 → 4.7.7
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 +230 -225
- package/install.sh +30 -9
- package/karnel/bin/karnel +2 -2
- package/karnel/cli/commands/backup.sh +10 -2
- package/karnel/cli/commands/brain.sh +28 -23
- package/karnel/cli/commands/cleanup.sh +10 -4
- package/karnel/cli/commands/deploy.sh +53 -0
- package/karnel/cli/commands/doctor/code.sh +225 -0
- package/karnel/cli/commands/doctor/code_detect.sh +184 -0
- package/karnel/cli/commands/doctor/code_exec.sh +278 -0
- package/karnel/cli/commands/doctor/code_langs.sh +229 -0
- package/karnel/cli/commands/doctor/fixes.sh +63 -46
- package/karnel/cli/commands/doctor.sh +222 -80
- package/karnel/cli/commands/env.sh +2 -0
- package/karnel/cli/commands/ia.sh +12 -2
- package/karnel/cli/commands/init.sh +17 -9
- package/karnel/cli/commands/install.sh +90 -54
- package/karnel/cli/commands/list.sh +36 -0
- package/karnel/cli/commands/pg.sh +21 -6
- package/karnel/cli/commands/reinstall.sh +99 -0
- package/karnel/cli/commands/restore.sh +43 -3
- package/karnel/cli/commands/search.sh +2 -2
- package/karnel/cli/commands/update.sh +6 -1
- package/karnel/cli/commands/upgrade.sh +8 -1
- package/karnel/cli/commands/voice.sh +2 -1
- package/karnel/cli/karnel.sh +11 -5
- package/karnel/modules/games.sh +11 -0
- package/karnel/tools/ai/all.sh +1 -0
- package/karnel/tools/ai/copilot-termux/README.md +40 -0
- package/karnel/tools/ai/copilot-termux/install.sh +80 -0
- package/karnel/tools/ai/copilot-termux/package.json +20 -0
- package/karnel/tools/db/all.sh +2 -0
- package/karnel/tools/deploy/railway/install.sh +2 -2
- package/karnel/tools/dev/all.sh +8 -0
- package/karnel/tools/dev/applaunch/__pycache__/applaunch.cpython-314.pyc +0 -0
- package/karnel/tools/dev/applaunch/applaunch.py +434 -0
- package/karnel/tools/dev/applaunch/install.sh +51 -0
- package/karnel/tools/dev/fconv/__pycache__/fconv.cpython-314.pyc +0 -0
- package/karnel/tools/dev/fconv/fconv.py +749 -0
- package/karnel/tools/dev/fconv/install.sh +51 -0
- package/karnel/tools/dev/filecheck/__pycache__/filecheck.cpython-314.pyc +0 -0
- package/karnel/tools/dev/filecheck/filecheck.py +429 -0
- package/karnel/tools/dev/filecheck/install.sh +51 -0
- package/karnel/tools/dev/notes/__pycache__/notes.cpython-314.pyc +0 -0
- package/karnel/tools/dev/notes/install.sh +51 -0
- package/karnel/tools/dev/notes/notes.py +849 -0
- package/karnel/tools/dev/passman/__pycache__/passman.cpython-314.pyc +0 -0
- package/karnel/tools/dev/passman/install.sh +51 -0
- package/karnel/tools/dev/passman/passman.py +536 -0
- package/karnel/tools/dev/splash/__pycache__/splash.cpython-314.pyc +0 -0
- package/karnel/tools/dev/splash/install.sh +51 -0
- package/karnel/tools/dev/splash/splash.py +188 -0
- package/karnel/tools/dev/treex/install.sh +51 -0
- package/karnel/tools/dev/treex/treex.py +2036 -0
- package/karnel/tools/dev/websites/__pycache__/websites.cpython-314.pyc +0 -0
- package/karnel/tools/dev/websites/install.sh +51 -0
- package/karnel/tools/dev/websites/websites.py +844 -0
- package/karnel/tools/games/all.sh +62 -0
- package/karnel/tools/games/arcade/__pycache__/arcade.cpython-314.pyc +0 -0
- package/karnel/tools/games/arcade/arcade.py +193 -0
- package/karnel/tools/games/arcade/install.sh +51 -0
- package/karnel/tools/games/buzz/__pycache__/buzz.cpython-314.pyc +0 -0
- package/karnel/tools/games/buzz/buzz.py +1264 -0
- package/karnel/tools/games/buzz/install.sh +51 -0
- package/karnel/tools/games/ctfgod/__pycache__/ctfgod.cpython-314.pyc +0 -0
- package/karnel/tools/games/ctfgod/ctfgod.py +1576 -0
- package/karnel/tools/games/ctfgod/install.sh +51 -0
- package/karnel/tools/games/detective/__pycache__/detective.cpython-314.pyc +0 -0
- package/karnel/tools/games/detective/detective.py +2091 -0
- package/karnel/tools/games/detective/install.sh +51 -0
- package/karnel/tools/games/pet-friends/__pycache__/pet-friends.cpython-314.pyc +0 -0
- package/karnel/tools/games/pet-friends/install.sh +51 -0
- package/karnel/tools/games/pet-friends/pet-friends.py +15802 -0
- package/karnel/tools/games/tamagotchi/__pycache__/tamagotchi.cpython-314.pyc +0 -0
- package/karnel/tools/games/tamagotchi/install.sh +51 -0
- package/karnel/tools/games/tamagotchi/tamagotchi.py +1224 -0
- package/karnel/tools/games/terminal-arcade/__pycache__/terminal-arcade.cpython-314.pyc +0 -0
- package/karnel/tools/games/terminal-arcade/terminal-arcade.py +193 -0
- package/karnel/tools/npm/all.sh +1 -0
- package/karnel/utils/log.sh +34 -12
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Project detection engine for karnel doctor code
|
|
4
|
+
# Analyzes directory structure and identifies languages/frameworks
|
|
5
|
+
|
|
6
|
+
_detect_project() {
|
|
7
|
+
local dir="${1:-.}"
|
|
8
|
+
dir="$(cd "$dir" 2>/dev/null && pwd)" || { log_error "Directory not found: $dir"; return 1; }
|
|
9
|
+
|
|
10
|
+
PROJECT_LANGS=()
|
|
11
|
+
PROJECT_FRAMEWORKS=()
|
|
12
|
+
PROJECT_MANIFESTS=()
|
|
13
|
+
PROJECT_SUBDIRS=()
|
|
14
|
+
_scan_project "$dir" 0 4 "$dir"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
_scan_project() {
|
|
18
|
+
local current="$1" depth="$2" max_depth="$3" root_dir="${4:-$1}"
|
|
19
|
+
(( depth > max_depth )) && return
|
|
20
|
+
|
|
21
|
+
local dir_langs=()
|
|
22
|
+
local dir_fws=()
|
|
23
|
+
local dir_manifests=()
|
|
24
|
+
|
|
25
|
+
for entry in "$current"/* "$current"/.github "$current"/.luacheckrc; do
|
|
26
|
+
[[ -e "$entry" ]] || continue
|
|
27
|
+
[[ -L "$entry" ]] && continue
|
|
28
|
+
local name; name=$(basename "$entry")
|
|
29
|
+
case "$name" in
|
|
30
|
+
node_modules|.git|.venv|venv|__pycache__|target|dist|build|vendor|coverage|.cache|.terraform|.next|.nuxt|.angular|cache|elixir_build|_build|deps|.bundle|.gem|bin|obj|.gradle|.mvn|CMakeFiles|.cargo|.rustup|go/pkg|Godeps)
|
|
31
|
+
continue ;;
|
|
32
|
+
esac
|
|
33
|
+
|
|
34
|
+
if [[ -f "$entry" ]]; then
|
|
35
|
+
_check_manifest "$name" "$entry" dir_langs dir_fws dir_manifests
|
|
36
|
+
elif [[ -d "$entry" ]]; then
|
|
37
|
+
_scan_project "$entry" $((depth + 1)) "$max_depth" "$root_dir"
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
if [[ ${#dir_langs[@]} -gt 0 ]]; then
|
|
42
|
+
for lang in "${dir_langs[@]}"; do PROJECT_LANGS+=("$lang"); done
|
|
43
|
+
for fw in "${dir_fws[@]}"; do PROJECT_FRAMEWORKS+=("$fw"); done
|
|
44
|
+
for mf in "${dir_manifests[@]}"; do PROJECT_MANIFESTS+=("$mf"); done
|
|
45
|
+
|
|
46
|
+
# Register subproject if deeper than root
|
|
47
|
+
if (( depth > 0 )) && [[ "$current" != "$root_dir" ]]; then
|
|
48
|
+
local rel; rel="${current#"$root_dir"/}"
|
|
49
|
+
PROJECT_SUBDIRS+=("$rel:$current:${dir_langs[*]}:${dir_fws[*]}")
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_check_manifest() {
|
|
55
|
+
local name="$1" path="$2"
|
|
56
|
+
# shellcheck disable=SC2178
|
|
57
|
+
local -n _langs="$3" _fws="$4" _manifests="$5"
|
|
58
|
+
|
|
59
|
+
case "$name" in
|
|
60
|
+
# JavaScript/TypeScript
|
|
61
|
+
package.json)
|
|
62
|
+
_langs+=("JavaScript")
|
|
63
|
+
_manifests+=("JavaScript:$path")
|
|
64
|
+
if grep -qE '"typescript"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
65
|
+
_langs+=("TypeScript")
|
|
66
|
+
fi
|
|
67
|
+
if grep -qiE '"next"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
68
|
+
_fws+=("JavaScript:Next.js")
|
|
69
|
+
elif grep -qiE '"nuxt"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
70
|
+
_fws+=("JavaScript:Nuxt")
|
|
71
|
+
elif grep -qiE '"vite"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
72
|
+
_fws+=("JavaScript:Vite")
|
|
73
|
+
elif grep -qiE '"@angular/[^\"]+"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
74
|
+
_fws+=("JavaScript:Angular")
|
|
75
|
+
elif grep -qiE '"react"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
76
|
+
_fws+=("JavaScript:React")
|
|
77
|
+
elif grep -qiE '"vue"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
78
|
+
_fws+=("JavaScript:Vue")
|
|
79
|
+
elif grep -qiE '"svelte"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
80
|
+
_fws+=("JavaScript:Svelte")
|
|
81
|
+
elif grep -qiE '"astro"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
82
|
+
_fws+=("JavaScript:Astro")
|
|
83
|
+
fi
|
|
84
|
+
if grep -qiE '"express"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
85
|
+
_fws+=("JavaScript:Express")
|
|
86
|
+
elif grep -qiE '"fastify"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
87
|
+
_fws+=("JavaScript:Fastify")
|
|
88
|
+
elif grep -qiE '"@nestjs/[^\"]+"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
89
|
+
_fws+=("JavaScript:NestJS")
|
|
90
|
+
elif grep -qiE '"hono"[[:space:]]*:' "$path" 2>/dev/null; then
|
|
91
|
+
_fws+=("JavaScript:Hono")
|
|
92
|
+
fi ;;
|
|
93
|
+
tsconfig.json) _langs+=("TypeScript"); _manifests+=("TypeScript:$path") ;;
|
|
94
|
+
# Python
|
|
95
|
+
pyproject.toml)
|
|
96
|
+
_langs+=("Python"); _manifests+=("Python:$path")
|
|
97
|
+
if grep -qi 'django' "$path" 2>/dev/null; then
|
|
98
|
+
_fws+=("Python:Django")
|
|
99
|
+
elif grep -qi 'fastapi' "$path" 2>/dev/null; then
|
|
100
|
+
_fws+=("Python:FastAPI")
|
|
101
|
+
elif grep -qi 'flask' "$path" 2>/dev/null; then
|
|
102
|
+
_fws+=("Python:Flask")
|
|
103
|
+
elif grep -qi 'pytest' "$path" 2>/dev/null; then
|
|
104
|
+
_fws+=("Python:pytest")
|
|
105
|
+
elif grep -qi 'tox' "$path" 2>/dev/null; then
|
|
106
|
+
_fws+=("Python:tox")
|
|
107
|
+
elif grep -qi 'poetry' "$path" 2>/dev/null; then
|
|
108
|
+
_fws+=("Python:Poetry")
|
|
109
|
+
fi ;;
|
|
110
|
+
requirements.txt|requirements-dev.txt|Pipfile|setup.py|setup.cfg)
|
|
111
|
+
[[ " ${_langs[*]} " == *" Python "* ]] || _langs+=("Python")
|
|
112
|
+
_manifests+=("Python:$path") ;;
|
|
113
|
+
manage.py) _langs+=("Python"); _fws+=("Python:Django"); _manifests+=("Python:$path") ;;
|
|
114
|
+
# Go
|
|
115
|
+
go.mod) _langs+=("Go"); _manifests+=("Go:$path") ;;
|
|
116
|
+
# Rust
|
|
117
|
+
Cargo.toml) _langs+=("Rust"); _manifests+=("Rust:$path") ;;
|
|
118
|
+
# Ruby
|
|
119
|
+
Gemfile) _langs+=("Ruby"); _manifests+=("Ruby:$path") ;;
|
|
120
|
+
# PHP
|
|
121
|
+
composer.json) _langs+=("PHP"); _manifests+=("PHP:$path") ;;
|
|
122
|
+
# Dart/Flutter
|
|
123
|
+
pubspec.yaml) _langs+=("Dart"); _manifests+=("Dart:$path")
|
|
124
|
+
grep -qi "flutter" "$path" 2>/dev/null && _fws+=("Dart:Flutter") ;;
|
|
125
|
+
# C/C++
|
|
126
|
+
CMakeLists.txt) _langs+=("C/C++"); _fws+=("C/C++:CMake"); _manifests+=("C/C++:$path") ;;
|
|
127
|
+
Makefile) [[ -f "$(dirname "$path")/CMakeLists.txt" ]] || _detect_c_makefile "$path" _langs _manifests ;;
|
|
128
|
+
# Java
|
|
129
|
+
pom.xml) _langs+=("Java"); _fws+=("Java:Maven"); _manifests+=("Java:$path") ;;
|
|
130
|
+
build.gradle|build.gradle.kts|settings.gradle*)
|
|
131
|
+
_langs+=("Java"); _fws+=("Java:Gradle"); _manifests+=("Java:$path")
|
|
132
|
+
grep -qi "kotlin" "$path" 2>/dev/null && _langs+=("Kotlin") ;;
|
|
133
|
+
# .NET
|
|
134
|
+
*.csproj|*.fsproj|*.vbproj) _langs+=("C#"); _fws+=("C#:.NET"); _manifests+=("C#:$path") ;;
|
|
135
|
+
# Docker
|
|
136
|
+
Dockerfile|docker-compose.yml|docker-compose.yaml)
|
|
137
|
+
_langs+=("Docker"); _manifests+=("Docker:$path") ;;
|
|
138
|
+
# Terraform
|
|
139
|
+
*.tf) _langs+=("Terraform"); _manifests+=("Terraform:$path") ;;
|
|
140
|
+
# Elixir
|
|
141
|
+
mix.exs) _langs+=("Elixir"); _manifests+=("Elixir:$path") ;;
|
|
142
|
+
# Haskell
|
|
143
|
+
*.cabal|stack.yaml) _langs+=("Haskell"); _manifests+=("Haskell:$path") ;;
|
|
144
|
+
# Lua
|
|
145
|
+
*.rockspec|.luacheckrc|selene.toml|stylua.toml) _langs+=("Lua"); _manifests+=("Lua:$path") ;;
|
|
146
|
+
# Swift
|
|
147
|
+
Package.swift) _langs+=("Swift"); _manifests+=("Swift:$path") ;;
|
|
148
|
+
# Julia
|
|
149
|
+
Project.toml|Manifest.toml) _langs+=("Julia"); _manifests+=("Julia:$path") ;;
|
|
150
|
+
# Zig
|
|
151
|
+
build.zig) _langs+=("Zig"); _manifests+=("Zig:$path") ;;
|
|
152
|
+
# Nix
|
|
153
|
+
*.nix|flake.lock) _langs+=("Nix"); _manifests+=("Nix:$path") ;;
|
|
154
|
+
# YAML ecosystems
|
|
155
|
+
*.yml|*.yaml)
|
|
156
|
+
if [[ "$(dirname "$path")" == *".github/workflows" ]]; then
|
|
157
|
+
_langs+=("GitHub Actions"); _manifests+=("GitHub Actions:$path")
|
|
158
|
+
elif [[ "$name" == "playbook.yml" || "$name" == "site.yml" || "$name" == "site.yaml" ]]; then
|
|
159
|
+
_langs+=("Ansible"); _manifests+=("Ansible:$path")
|
|
160
|
+
fi ;;
|
|
161
|
+
# Shell scripts (detect by extension + shebang)
|
|
162
|
+
*.sh|*.bash|*.zsh)
|
|
163
|
+
[[ " ${_langs[*]} " != *" Shell "* ]] && _langs+=("Shell") ;;
|
|
164
|
+
*.sql) [[ " ${_langs[*]} " != *" SQL "* ]] && _langs+=("SQL") ;;
|
|
165
|
+
*.kt|*.kts) _langs+=("Kotlin"); _manifests+=("Kotlin:$path") ;;
|
|
166
|
+
*.dart) _langs+=("Dart") ;;
|
|
167
|
+
*.rs) _langs+=("Rust") ;;
|
|
168
|
+
*.go) _langs+=("Go") ;;
|
|
169
|
+
*.rb) _langs+=("Ruby") ;;
|
|
170
|
+
*.php) _langs+=("PHP") ;;
|
|
171
|
+
*.py) _langs+=("Python") ;;
|
|
172
|
+
*.swift) _langs+=("Swift") ;;
|
|
173
|
+
*.ex|*.exs) _langs+=("Elixir") ;;
|
|
174
|
+
esac
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
_detect_c_makefile() {
|
|
178
|
+
local path="$1"
|
|
179
|
+
# shellcheck disable=SC2178
|
|
180
|
+
local -n _langs="$2" _manifests="$3"
|
|
181
|
+
if grep -qE '^(CC|CXX|CFLAGS|CXXFLAGS)' "$path" 2>/dev/null; then
|
|
182
|
+
_langs+=("C/C++"); _manifests+=("C/C++:$path")
|
|
183
|
+
fi
|
|
184
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
declare -g _CODE_REPORT_DIR=""
|
|
4
|
+
declare -ga _CODE_RESULTS=()
|
|
5
|
+
|
|
6
|
+
_exec_init() {
|
|
7
|
+
_CODE_REPORT_DIR="$KARNEL_DATA/doctor_code_reports"
|
|
8
|
+
_CODE_RESULTS=()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
_exec_run_tool() {
|
|
12
|
+
local tool="$1" cat="$2" cmd="$3" lang="$4" file="$5" dir="${6:-$PWD}"
|
|
13
|
+
local full_cmd; full_cmd="$(_tool_command "$cmd" "$file")"
|
|
14
|
+
|
|
15
|
+
if ! (cd "$dir" && _is_tool_available "$tool"); then
|
|
16
|
+
_CODE_RESULTS+=("__MISSING__|$lang|$tool|$cat|||")
|
|
17
|
+
return 2
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
local out rc
|
|
21
|
+
if out="$(cd "$dir" && _run_tool_check "$tool" "$full_cmd" 30)"; then
|
|
22
|
+
rc=0
|
|
23
|
+
else
|
|
24
|
+
rc=$?
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [[ "$out" == "__TOOL_NOT_FOUND__" ]]; then
|
|
28
|
+
_CODE_RESULTS+=("__MISSING__|$lang|$tool|$cat|||")
|
|
29
|
+
return 2
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
local severity="info" count=0
|
|
33
|
+
if (( rc != 0 )); then
|
|
34
|
+
severity="error"
|
|
35
|
+
count=1
|
|
36
|
+
if [[ -z "$out" ]]; then
|
|
37
|
+
if (( rc == 124 )); then
|
|
38
|
+
out="Timed out after 30 seconds"
|
|
39
|
+
else
|
|
40
|
+
out="Command exited with status $rc"
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
elif [[ -z "$out" ]]; then
|
|
44
|
+
severity="ok"
|
|
45
|
+
else
|
|
46
|
+
local lower; lower=$(printf '%s\n' "$out" | tr '[:upper:]' '[:lower:]')
|
|
47
|
+
if printf '%s\n' "$lower" | grep -qE 'error|fail|critical|fatal|panic'; then
|
|
48
|
+
severity="error"
|
|
49
|
+
count=$(printf '%s\n' "$lower" | grep -cE 'error|fail|critical|fatal|panic' || true)
|
|
50
|
+
elif printf '%s\n' "$lower" | grep -qE 'warning|warn|deprecated|should'; then
|
|
51
|
+
severity="warning"
|
|
52
|
+
count=$(printf '%s\n' "$lower" | grep -cE 'warning|warn|deprecated|should' || true)
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
local detail line_count
|
|
57
|
+
detail=$(printf '%s\n' "$out" | head -5 | tr '\n' '; ')
|
|
58
|
+
line_count=$(printf '%s\n' "$out" | wc -l)
|
|
59
|
+
(( line_count > 5 )) && detail+="..."
|
|
60
|
+
_CODE_RESULTS+=("$severity|$lang|$tool|$cat|$count|$detail")
|
|
61
|
+
return "$rc"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_find_tool_sample() {
|
|
65
|
+
local tool="$1" lang="$2" dir="$3"
|
|
66
|
+
local -a names=()
|
|
67
|
+
|
|
68
|
+
case "$tool" in
|
|
69
|
+
jq) names=(-name '*.json') ;;
|
|
70
|
+
xmllint) names=(-name '*.xml') ;;
|
|
71
|
+
*)
|
|
72
|
+
case "$lang" in
|
|
73
|
+
Shell) names=(-name '*.sh' -o -name '*.bash' -o -name '*.zsh') ;;
|
|
74
|
+
SQL) names=(-name '*.sql') ;;
|
|
75
|
+
C/C++|C|C++) names=(-name '*.c' -o -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.hpp') ;;
|
|
76
|
+
PHP) names=(-name '*.php') ;;
|
|
77
|
+
Ruby) names=(-name '*.rb') ;;
|
|
78
|
+
Java) names=(-name '*.java') ;;
|
|
79
|
+
Kotlin) names=(-name '*.kt' -o -name '*.kts') ;;
|
|
80
|
+
Dart) names=(-name '*.dart') ;;
|
|
81
|
+
Lua) names=(-name '*.lua') ;;
|
|
82
|
+
esac
|
|
83
|
+
;;
|
|
84
|
+
esac
|
|
85
|
+
|
|
86
|
+
[[ ${#names[@]} -gt 0 ]] || return 1
|
|
87
|
+
local sample
|
|
88
|
+
sample=$(find "$dir" \
|
|
89
|
+
\( -type d \( -name node_modules -o -name .git -o -name .venv -o -name venv -o -name target -o -name dist -o -name build -o -name vendor \) -prune \) -o \
|
|
90
|
+
\( -type f \( "${names[@]}" \) -print -quit \) 2>/dev/null)
|
|
91
|
+
[[ -n "$sample" ]] || return 1
|
|
92
|
+
printf '%s\n' "$sample"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_exec_run_lang() {
|
|
96
|
+
local lang="$1" dir="$2" mode="$3" include_global="${4:-true}"
|
|
97
|
+
local entry tool category cmd_run sample
|
|
98
|
+
|
|
99
|
+
while IFS= read -r entry; do
|
|
100
|
+
[[ -z "$entry" ]] && continue
|
|
101
|
+
_parse_lang_tool "$entry"
|
|
102
|
+
tool="${PARSED_LANG_TOOL[0]}"
|
|
103
|
+
category="${PARSED_LANG_TOOL[1]}"
|
|
104
|
+
cmd_run="${PARSED_LANG_TOOL[2]}"
|
|
105
|
+
|
|
106
|
+
sample=""
|
|
107
|
+
if [[ "$cmd_run" == *'{}'* ]]; then
|
|
108
|
+
sample=$(_find_tool_sample "$tool" "$lang" "$dir") || continue
|
|
109
|
+
fi
|
|
110
|
+
_exec_run_tool "$tool" "$category" "$cmd_run" "$lang" "$sample" "$dir" || true
|
|
111
|
+
done < <(_get_lang_tools "$lang" "$mode" "$include_global")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_exec_apply_fix() {
|
|
115
|
+
local tool="$1" fix_cmd="$2" lang="$3" file="$4" dir="${5:-$PWD}"
|
|
116
|
+
local full_cmd; full_cmd="$(_tool_command "$fix_cmd" "$file")"
|
|
117
|
+
|
|
118
|
+
if [[ "$fix_cmd" == *'{}'* && -z "$file" ]]; then
|
|
119
|
+
log_warn "Cannot fix — no matching file found for $tool"
|
|
120
|
+
return 1
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
if ! (cd "$dir" && _is_tool_available "$tool"); then
|
|
124
|
+
log_warn "Cannot fix — $tool not installed"
|
|
125
|
+
return 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
log_info "Applying fix: $tool ($lang)..."
|
|
129
|
+
local out rc
|
|
130
|
+
if out=$(cd "$dir" && timeout 60 bash -c "$full_cmd" 2>&1); then
|
|
131
|
+
rc=0
|
|
132
|
+
else
|
|
133
|
+
rc=$?
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if (( rc == 0 )); then
|
|
137
|
+
[[ -n "$out" ]] && log_info "$tool: $out"
|
|
138
|
+
log_success "$tool fix applied"
|
|
139
|
+
return 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
log_warn "$tool fix failed (status $rc)${out:+: $out}"
|
|
143
|
+
return "$rc"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_format_report() {
|
|
147
|
+
local mode="${1:-text}" dir="$2"
|
|
148
|
+
|
|
149
|
+
if [[ "$mode" == "json" ]]; then
|
|
150
|
+
_format_json "$dir"
|
|
151
|
+
return
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
local lang_count=0 tool_count=0
|
|
155
|
+
local -A langs_seen tools_seen
|
|
156
|
+
|
|
157
|
+
# Collect unique langs and tools
|
|
158
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
159
|
+
IFS='|' read -r severity lang tool cat count detail <<< "$entry"
|
|
160
|
+
[[ -z "$lang" || "$lang" == "__MISSING__" ]] && continue
|
|
161
|
+
if [[ "$lang" != "Cross-language" && -z "${langs_seen[$lang]:-}" ]]; then
|
|
162
|
+
langs_seen[$lang]=1
|
|
163
|
+
lang_count=$((lang_count + 1))
|
|
164
|
+
fi
|
|
165
|
+
[[ -z "${tools_seen[$tool]:-}" ]] && tools_seen[$tool]=1 && ((tool_count++))
|
|
166
|
+
done
|
|
167
|
+
|
|
168
|
+
echo
|
|
169
|
+
separator_section "Results"
|
|
170
|
+
echo
|
|
171
|
+
|
|
172
|
+
if [[ ${#_CODE_RESULTS[@]} -eq 0 ]]; then
|
|
173
|
+
log_info "No analysis results — no tools were executed."
|
|
174
|
+
echo
|
|
175
|
+
return
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
local errors=0 warnings=0 infos=0 missing=0
|
|
179
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
180
|
+
IFS='|' read -r severity lang tool cat count detail <<< "$entry"
|
|
181
|
+
case "$severity" in
|
|
182
|
+
__MISSING__) ((missing++)) ;;
|
|
183
|
+
error) ((errors++)) ;;
|
|
184
|
+
warning) ((warnings++)) ;;
|
|
185
|
+
ok) ;;
|
|
186
|
+
*) ((infos++)) ;;
|
|
187
|
+
esac
|
|
188
|
+
done
|
|
189
|
+
|
|
190
|
+
printf " ${D_CYAN}Languages:${NC} %-2d ${D_CYAN}Tools:${NC} %-2d ${D_CYAN}Checks:${NC} %-2d\n" "$lang_count" "$tool_count" "${#_CODE_RESULTS[@]}"
|
|
191
|
+
echo
|
|
192
|
+
printf " ${D_RED}%-4s${NC} ${D_YELLOW}%-4s${NC} ${D_BLUE}%-4s${NC}\n" "Errors" "Warns" "Info"
|
|
193
|
+
printf " ${D_RED}%-4d${NC} ${D_YELLOW}%-4d${NC} ${D_BLUE}%-4d${NC}\n" "$errors" "$warnings" "$infos"
|
|
194
|
+
if (( missing > 0 )); then
|
|
195
|
+
echo; log_warn "$missing tool(s) not installed — install them for deeper analysis"
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
echo
|
|
199
|
+
separator_section "Detailed Findings"
|
|
200
|
+
echo
|
|
201
|
+
|
|
202
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
203
|
+
IFS='|' read -r severity lang tool cat count detail <<< "$entry"
|
|
204
|
+
if [[ "$severity" == "__MISSING__" ]]; then
|
|
205
|
+
printf " ${D_YELLOW}⚠${NC} %s ${D_CYAN}%s${NC} %s\n" "$lang" "$tool" "(not installed)"
|
|
206
|
+
continue
|
|
207
|
+
fi
|
|
208
|
+
local color="$D_GREEN"
|
|
209
|
+
case "$severity" in
|
|
210
|
+
error) color="$D_RED" ;;
|
|
211
|
+
warning) color="$D_YELLOW" ;;
|
|
212
|
+
ok) color="$D_GREEN" ;;
|
|
213
|
+
*) color="$D_BLUE" ;;
|
|
214
|
+
esac
|
|
215
|
+
if [[ "$severity" == "ok" ]]; then
|
|
216
|
+
printf " ${color}✔${NC} %s ${D_CYAN}%s${NC} (%s)\n" "$lang" "$tool" "$cat"
|
|
217
|
+
else
|
|
218
|
+
printf " ${color}◈${NC} %s ${D_CYAN}%s${NC} [%s] %s\n" "$lang" "$tool" "$cat" "$(echo "$detail" | head -c 120)"
|
|
219
|
+
fi
|
|
220
|
+
done
|
|
221
|
+
|
|
222
|
+
echo
|
|
223
|
+
local report_file
|
|
224
|
+
report_file="$_CODE_REPORT_DIR/doctor_code_$(date +%Y%m%d_%H%M%S).txt"
|
|
225
|
+
_save_report "$report_file" "$dir"
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_format_json() {
|
|
229
|
+
local dir="$1"
|
|
230
|
+
local first=true
|
|
231
|
+
echo "{"
|
|
232
|
+
printf ' "directory":"%s",\n' "$(_json_escape "$dir")"
|
|
233
|
+
echo " \"results\": ["
|
|
234
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
235
|
+
$first || echo ","
|
|
236
|
+
first=false
|
|
237
|
+
IFS='|' read -r severity lang tool cat count detail <<< "$entry"
|
|
238
|
+
printf ' {"severity":"%s","language":"%s","tool":"%s","category":"%s","count":%d,"detail":"%s"}' \
|
|
239
|
+
"$(_json_escape "$severity")" "$(_json_escape "$lang")" "$(_json_escape "$tool")" \
|
|
240
|
+
"$(_json_escape "$cat")" "${count:-0}" "$(_json_escape "$detail")"
|
|
241
|
+
done
|
|
242
|
+
echo ""
|
|
243
|
+
echo " ]"
|
|
244
|
+
echo "}"
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_json_escape() {
|
|
248
|
+
local value="$1" out="" c
|
|
249
|
+
value="${value//\\/\\\\}"
|
|
250
|
+
value="${value//\"/\\\"}"
|
|
251
|
+
value="${value//$'\n'/\\n}"
|
|
252
|
+
value="${value//$'\r'/\\r}"
|
|
253
|
+
value="${value//$'\t'/\\t}"
|
|
254
|
+
value="${value//$'\b'/\\b}"
|
|
255
|
+
value="${value//$'\f'/\\f}"
|
|
256
|
+
for ((i=0; i<${#value}; i++)); do
|
|
257
|
+
c="${value:$i:1}"
|
|
258
|
+
if [[ "$c" < ' ' ]] && [[ "$c" != $'\n' ]] && [[ "$c" != $'\r' ]] && [[ "$c" != $'\t' ]]; then
|
|
259
|
+
printf '\\u%04x' "'$c"
|
|
260
|
+
else
|
|
261
|
+
printf '%s' "$c"
|
|
262
|
+
fi
|
|
263
|
+
done
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
_save_report() {
|
|
267
|
+
local file="$1" dir="$2"
|
|
268
|
+
{
|
|
269
|
+
echo "Karnel Doctor Code Report"
|
|
270
|
+
echo "Generated: $(date)"
|
|
271
|
+
echo "Directory: $dir"
|
|
272
|
+
echo "---"
|
|
273
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
274
|
+
IFS='|' read -r severity lang tool cat count detail <<< "$entry"
|
|
275
|
+
echo "[$severity] $lang / $tool ($cat): $detail"
|
|
276
|
+
done
|
|
277
|
+
} > "$file" 2>/dev/null && list_item "Report saved: ${D_CYAN}$file${NC}" || log_warn "Failed to save report: $file"
|
|
278
|
+
}
|