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
package/install.sh
CHANGED
|
@@ -65,9 +65,13 @@ log_info() {
|
|
|
65
65
|
echo -e " ${P_BORDER}→${P_NC} $1"
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
_INSTALL_CREATED_KARNEL_REPO=false
|
|
69
|
+
|
|
68
70
|
_cleanup_failed() {
|
|
69
71
|
echo -e "\n ${P_FAIL}✖${P_NC} Installation failed at step ${CURRENT_STEP}. Cleaning up..."
|
|
70
|
-
[[ -d "$KARNEL_REPO" ]]
|
|
72
|
+
if $_INSTALL_CREATED_KARNEL_REPO && [[ -d "$KARNEL_REPO" ]]; then
|
|
73
|
+
rm -rf "$KARNEL_REPO"
|
|
74
|
+
fi
|
|
71
75
|
[[ -L "$PREFIX/bin/karnel" ]] && rm -f "$PREFIX/bin/karnel"
|
|
72
76
|
echo -e " ${P_DIM}Run install.sh again to retry${P_NC}"
|
|
73
77
|
exit 1
|
|
@@ -196,6 +200,7 @@ clone_repo() {
|
|
|
196
200
|
echo
|
|
197
201
|
log_ok "Repository updated"
|
|
198
202
|
else
|
|
203
|
+
_INSTALL_CREATED_KARNEL_REPO=true
|
|
199
204
|
progress_bar 0 10
|
|
200
205
|
git clone --depth=1 -b "$BRANCH" "$REPO" "$KARNEL_REPO" &>/dev/null &
|
|
201
206
|
local pid=$!
|
|
@@ -212,6 +217,14 @@ clone_repo() {
|
|
|
212
217
|
fi
|
|
213
218
|
|
|
214
219
|
export KARNEL_REPO
|
|
220
|
+
|
|
221
|
+
# Install shell completions
|
|
222
|
+
if [ -f "$KARNEL_REPO/scripts/completion.bash" ]; then
|
|
223
|
+
cp "$KARNEL_REPO/scripts/completion.bash" "$PREFIX/share/bash-completion/completions/karnel" 2>/dev/null || true
|
|
224
|
+
fi
|
|
225
|
+
if [ -f "$KARNEL_REPO/scripts/completion.zsh" ]; then
|
|
226
|
+
cp "$KARNEL_REPO/scripts/completion.zsh" "$PREFIX/share/zsh/site-functions/_karnel" 2>/dev/null || true
|
|
227
|
+
fi
|
|
215
228
|
}
|
|
216
229
|
|
|
217
230
|
create_symlink() {
|
|
@@ -219,6 +232,8 @@ create_symlink() {
|
|
|
219
232
|
|
|
220
233
|
[[ -z "$PREFIX" ]] && { log_fail "PREFIX is not set. Are you running in Termux?"; return 1; }
|
|
221
234
|
|
|
235
|
+
command -v termux-fix-shebang &>/dev/null && termux-fix-shebang "$KARNEL_REPO/karnel/bin/karnel" &>/dev/null
|
|
236
|
+
|
|
222
237
|
rm -f "$PREFIX/bin/karnel"
|
|
223
238
|
ln -sf "$KARNEL_REPO/karnel/bin/karnel" "$PREFIX/bin/karnel"
|
|
224
239
|
|
|
@@ -268,14 +283,20 @@ show_final_message() {
|
|
|
268
283
|
}
|
|
269
284
|
|
|
270
285
|
main() {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
286
|
+
if [[ -z "${PREFIX:-}" ]]; then
|
|
287
|
+
echo -e "\n ${P_FAIL}✖${P_NC} PREFIX is not set. Are you running in Termux?"
|
|
288
|
+
echo -e " ${P_DIM}This installer requires Termux environment.${P_NC}"
|
|
289
|
+
echo -e " ${P_DIM}Install Termux from GitHub or F-Droid first.${P_NC}"
|
|
290
|
+
exit 1
|
|
291
|
+
fi
|
|
292
|
+
bootstrap_dependencies
|
|
293
|
+
banner
|
|
294
|
+
install_dependencies
|
|
295
|
+
setup_directories
|
|
296
|
+
clone_repo
|
|
297
|
+
create_symlink
|
|
298
|
+
save_config
|
|
299
|
+
show_final_message
|
|
279
300
|
}
|
|
280
301
|
|
|
281
302
|
main
|
package/karnel/bin/karnel
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
1
|
+
#!/data/data/com.termux/files/usr/bin/env bash
|
|
2
2
|
|
|
3
3
|
SOURCE="${BASH_SOURCE[0]}"
|
|
4
4
|
|
|
@@ -42,7 +42,7 @@ check_for_updates() {
|
|
|
42
42
|
echo "$current_time" > "$version_file"
|
|
43
43
|
|
|
44
44
|
(
|
|
45
|
-
local remote_version=$(curl -fsSL "https://raw.githubusercontent.com/israelmarques1024-dotcom/karnel-termux/main/karnel/utils/env.sh" 2>/dev/null | grep "KARNEL_VERSION=" | cut -d'"' -f2)
|
|
45
|
+
local remote_version=$(curl -fsSL --max-time 5 "https://raw.githubusercontent.com/israelmarques1024-dotcom/karnel-termux/main/karnel/utils/env.sh" 2>/dev/null | grep "KARNEL_VERSION=" | cut -d'"' -f2)
|
|
46
46
|
local local_version="$KARNEL_VERSION"
|
|
47
47
|
if [[ -n "$remote_version" ]] && [[ "$remote_version" != "$local_version" ]]; then
|
|
48
48
|
echo "$remote_version" > "$KARNEL_CACHE/new_version"
|
|
@@ -66,9 +66,17 @@ backup_main() {
|
|
|
66
66
|
[[ -f "$HOME/$f" ]] && cp "$HOME/$f" "$tmp/config/home/"
|
|
67
67
|
done
|
|
68
68
|
[[ -d "$HOME/.termux" ]] && cp -r "$HOME/.termux" "$tmp/config/termux/" 2>/dev/null
|
|
69
|
-
[[ -d "$HOME/.ssh" ]]
|
|
69
|
+
if [[ -d "$HOME/.ssh" ]]; then
|
|
70
|
+
log_warn "Including SSH keys in backup — these are sensitive!"
|
|
71
|
+
cp "$HOME/.ssh/id_"* "$HOME/.ssh/config" "$HOME/.ssh/known_hosts" "$tmp/config/ssh/" 2>/dev/null
|
|
72
|
+
fi
|
|
70
73
|
for d in "$HOME/.config"/*; do
|
|
71
|
-
[[ -d "$d" ]]
|
|
74
|
+
if [[ -d "$d" ]]; then
|
|
75
|
+
local base; base=$(basename "$d")
|
|
76
|
+
if [[ "$base" != "github-copilot" && "$base" != "nvm" && "$base" != "coc" && "$base" != "Code" && "$base" != "yarn" ]]; then
|
|
77
|
+
cp -r "$d" "$tmp/config/config/" 2>/dev/null
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
72
80
|
done
|
|
73
81
|
[[ -f "$PREFIX/etc/apt/sources.list" ]] && cp "$PREFIX/etc/apt/sources.list" "$tmp/config/prefix-etc/" 2>/dev/null
|
|
74
82
|
|
|
@@ -274,13 +274,12 @@ brain_init() {
|
|
|
274
274
|
log_info "Creating private repo: ${D_CYAN}$gh_user/$repo_name${D_NC}..."
|
|
275
275
|
if gh repo create "$repo_name" --private &>/dev/null; then
|
|
276
276
|
(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
git
|
|
281
|
-
git
|
|
282
|
-
git
|
|
283
|
-
loading "Pushing to GitHub..." git push -u origin main
|
|
277
|
+
git -C "$BRAIN_DIR" init &>/dev/null
|
|
278
|
+
echo "# Karnel Brain" >"$BRAIN_DIR/README.md"
|
|
279
|
+
git -C "$BRAIN_DIR" add -A
|
|
280
|
+
git -C "$BRAIN_DIR" commit -m "init brain" &>/dev/null
|
|
281
|
+
git -C "$BRAIN_DIR" remote add origin "https://github.com/$gh_user/$repo_name.git"
|
|
282
|
+
loading "Pushing to GitHub..." git -C "$BRAIN_DIR" push -u origin main
|
|
284
283
|
)
|
|
285
284
|
log_success "Repo created and linked: ${D_CYAN}https://github.com/$gh_user/$repo_name${D_NC}"
|
|
286
285
|
else
|
|
@@ -295,6 +294,12 @@ brain_init() {
|
|
|
295
294
|
brain_save() {
|
|
296
295
|
_brain_ensure || return 1
|
|
297
296
|
|
|
297
|
+
if [[ ! -t 0 ]]; then
|
|
298
|
+
log_error "Interactive mode requires a terminal."
|
|
299
|
+
log_info "Use: ${D_CYAN}karnel brain add \"your text\"${NC}"
|
|
300
|
+
return 1
|
|
301
|
+
fi
|
|
302
|
+
|
|
298
303
|
separator
|
|
299
304
|
box "Save a New Memory"
|
|
300
305
|
separator
|
|
@@ -469,9 +474,10 @@ brain_search() {
|
|
|
469
474
|
local -a menu_titles=()
|
|
470
475
|
local idx=0
|
|
471
476
|
for result in "${results[@]}"; do
|
|
472
|
-
local f
|
|
473
|
-
|
|
474
|
-
|
|
477
|
+
local f ln text
|
|
478
|
+
f=$(echo "$result" | cut -d'|' -f1)
|
|
479
|
+
ln=$(echo "$result" | cut -d'|' -f2)
|
|
480
|
+
text=$(echo "$result" | cut -d'|' -f3-)
|
|
475
481
|
local relative="${f#$BRAIN_DIR/}"
|
|
476
482
|
local title
|
|
477
483
|
title=$(_brain_title "$f")
|
|
@@ -769,9 +775,10 @@ brain_sync() {
|
|
|
769
775
|
return 1
|
|
770
776
|
fi
|
|
771
777
|
|
|
772
|
-
|
|
778
|
+
local branch
|
|
779
|
+
branch=$(git -C "$BRAIN_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
773
780
|
|
|
774
|
-
if ! git remote -v &>/dev/null; then
|
|
781
|
+
if ! git -C "$BRAIN_DIR" remote -v &>/dev/null; then
|
|
775
782
|
log_info "No remote configured — local commit only"
|
|
776
783
|
list_item "Set up GitHub sync with: ${D_CYAN}karnel brain init${D_NC}"
|
|
777
784
|
separator
|
|
@@ -779,18 +786,15 @@ brain_sync() {
|
|
|
779
786
|
fi
|
|
780
787
|
|
|
781
788
|
local commit_msg="brain sync $(date +%Y-%m-%d_%H:%M)"
|
|
782
|
-
if [[ -n "$(git status --porcelain)" ]]; then
|
|
783
|
-
loading "Staging changes..." git add -A
|
|
784
|
-
loading "Committing changes..." git commit -m "$commit_msg"
|
|
789
|
+
if [[ -n "$(git -C "$BRAIN_DIR" status --porcelain)" ]]; then
|
|
790
|
+
loading "Staging changes..." git -C "$BRAIN_DIR" add -A
|
|
791
|
+
loading "Committing changes..." git -C "$BRAIN_DIR" commit -m "$commit_msg"
|
|
785
792
|
else
|
|
786
793
|
log_info "No changes to commit"
|
|
787
794
|
fi
|
|
788
795
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if ! git rev-parse --abbrev-ref --symbolic-full-name @{upstream} &>/dev/null; then
|
|
793
|
-
loading "Pushing to origin/$branch..." git push -u origin "$branch"
|
|
796
|
+
if ! git -C "$BRAIN_DIR" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' &>/dev/null; then
|
|
797
|
+
loading "Pushing to origin/$branch..." git -C "$BRAIN_DIR" push -u origin "$branch"
|
|
794
798
|
if [[ $? -eq 0 ]]; then
|
|
795
799
|
log_success "Brain synced with GitHub"
|
|
796
800
|
else
|
|
@@ -801,7 +805,7 @@ brain_sync() {
|
|
|
801
805
|
return
|
|
802
806
|
fi
|
|
803
807
|
|
|
804
|
-
loading "Pulling latest from remote..." git pull --rebase
|
|
808
|
+
loading "Pulling latest from remote..." git -C "$BRAIN_DIR" pull --rebase
|
|
805
809
|
local pull_exit=$?
|
|
806
810
|
if [[ $pull_exit -ne 0 ]]; then
|
|
807
811
|
log_error "Pull failed — your local and remote histories diverged"
|
|
@@ -811,7 +815,7 @@ brain_sync() {
|
|
|
811
815
|
return 1
|
|
812
816
|
fi
|
|
813
817
|
|
|
814
|
-
loading "Pushing to remote..." git push
|
|
818
|
+
loading "Pushing to remote..." git -C "$BRAIN_DIR" push
|
|
815
819
|
if [[ $? -eq 0 ]]; then
|
|
816
820
|
log_success "Brain synced with GitHub"
|
|
817
821
|
else
|
|
@@ -1299,7 +1303,8 @@ EOF
|
|
|
1299
1303
|
fi
|
|
1300
1304
|
|
|
1301
1305
|
if command -v jq &>/dev/null; then
|
|
1302
|
-
local temp
|
|
1306
|
+
local temp
|
|
1307
|
+
temp=$(mktemp)
|
|
1303
1308
|
if [[ "$val" == "true" ]] || [[ "$val" == "false" ]]; then
|
|
1304
1309
|
jq --argjson v "$val" ".${key} = \$v" "$config_file" > "$temp" && mv "$temp" "$config_file"
|
|
1305
1310
|
elif [[ "$val" =~ ^[0-9]+$ ]]; then
|
|
@@ -4,6 +4,12 @@ import "@/utils/log"
|
|
|
4
4
|
import "@/utils/colors"
|
|
5
5
|
|
|
6
6
|
cleanup_main() {
|
|
7
|
+
for arg in "$@"; do
|
|
8
|
+
case "$arg" in
|
|
9
|
+
--help|-h) echo; echo "Usage: karnel cleanup"; echo; echo " Cleans npm, pip, pkg caches and temp files."; echo; return ;;
|
|
10
|
+
esac
|
|
11
|
+
done
|
|
12
|
+
|
|
7
13
|
echo
|
|
8
14
|
box "Karnel Cleanup"
|
|
9
15
|
echo
|
|
@@ -42,10 +48,10 @@ cleanup_main() {
|
|
|
42
48
|
|
|
43
49
|
# __pycache__ dirs
|
|
44
50
|
local pycache_size
|
|
45
|
-
pycache_size=$(
|
|
46
|
-
if [[ -n "$pycache_size" ]]; then
|
|
47
|
-
log_info "Python __pycache__:
|
|
48
|
-
find "$PREFIX/lib/python3
|
|
51
|
+
pycache_size=$(find "$PREFIX/lib/"python3* -name "__pycache__" -type d 2>/dev/null | head -n 1)
|
|
52
|
+
if [[ -n "$pycache_size" ]] && compgen -G "$PREFIX/lib/python3.*" >/dev/null 2>&1; then
|
|
53
|
+
log_info "Python __pycache__: cleaning..."
|
|
54
|
+
find "$PREFIX/lib/"python3* -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null
|
|
49
55
|
log_success "Python cache cleaned"
|
|
50
56
|
((total_freed++))
|
|
51
57
|
fi
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
import "@/utils/colors"
|
|
5
|
+
|
|
6
|
+
deploy_main() {
|
|
7
|
+
if [[ $# -eq 0 ]]; then
|
|
8
|
+
echo
|
|
9
|
+
box "Karnel Deploy"
|
|
10
|
+
echo
|
|
11
|
+
log_info "Usage: karnel deploy <tool>"
|
|
12
|
+
echo
|
|
13
|
+
log_info "Available tools:"
|
|
14
|
+
echo
|
|
15
|
+
list_item "vercel - Deploy to Vercel"
|
|
16
|
+
list_item "railway - Deploy to Railway"
|
|
17
|
+
list_item "netlify - Deploy to Netlify"
|
|
18
|
+
echo
|
|
19
|
+
log_info "Usage: ${D_CYAN}karnel deploy vercel${NC}"
|
|
20
|
+
echo
|
|
21
|
+
return
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
local tool="$1"
|
|
25
|
+
case "$tool" in
|
|
26
|
+
vercel)
|
|
27
|
+
if ! command -v vercel &>/dev/null; then
|
|
28
|
+
log_warn "Vercel CLI not installed. Run: karnel install npm --vercel"
|
|
29
|
+
return 1
|
|
30
|
+
fi
|
|
31
|
+
exec vercel "${@:2}"
|
|
32
|
+
;;
|
|
33
|
+
railway)
|
|
34
|
+
if ! command -v railway &>/dev/null; then
|
|
35
|
+
log_warn "Railway CLI not installed. Run: karnel install deploy --railway"
|
|
36
|
+
return 1
|
|
37
|
+
fi
|
|
38
|
+
exec railway "${@:2}"
|
|
39
|
+
;;
|
|
40
|
+
netlify)
|
|
41
|
+
if ! command -v netlify &>/dev/null; then
|
|
42
|
+
log_warn "Netlify CLI not installed. Run: karnel install deploy --netlify"
|
|
43
|
+
return 1
|
|
44
|
+
fi
|
|
45
|
+
exec netlify "${@:2}"
|
|
46
|
+
;;
|
|
47
|
+
*)
|
|
48
|
+
log_warn "Unknown deploy tool: $tool"
|
|
49
|
+
echo "Available: vercel, railway, netlify"
|
|
50
|
+
return 1
|
|
51
|
+
;;
|
|
52
|
+
esac
|
|
53
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
import "@/utils/log"
|
|
4
|
+
import "@/utils/colors"
|
|
5
|
+
import "@/cli/commands/doctor/code_detect"
|
|
6
|
+
import "@/cli/commands/doctor/code_langs"
|
|
7
|
+
import "@/cli/commands/doctor/code_exec"
|
|
8
|
+
|
|
9
|
+
_doctor_code_language_dir() {
|
|
10
|
+
local lang="$1" fallback="$2" manifest manifest_lang manifest_path
|
|
11
|
+
for manifest in "${PROJECT_MANIFESTS[@]}"; do
|
|
12
|
+
IFS=':' read -r manifest_lang manifest_path <<< "$manifest"
|
|
13
|
+
if [[ "$manifest_lang" == "$lang" && -f "$manifest_path" ]]; then
|
|
14
|
+
dirname "$manifest_path"
|
|
15
|
+
return
|
|
16
|
+
fi
|
|
17
|
+
done
|
|
18
|
+
printf '%s\n' "$fallback"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
doctor_code() {
|
|
22
|
+
local target_dir="" mode="quick" fix_mode="none" output="text"
|
|
23
|
+
local -a args=()
|
|
24
|
+
|
|
25
|
+
# Parse options
|
|
26
|
+
while [[ $# -gt 0 ]]; do
|
|
27
|
+
case "$1" in
|
|
28
|
+
--quick|-q) mode="quick" ;;
|
|
29
|
+
--standard|-s) mode="standard" ;;
|
|
30
|
+
--deep|-d) mode="deep" ;;
|
|
31
|
+
--fix|--safe-fix) fix_mode="safe" ;;
|
|
32
|
+
--aggressive-fix) fix_mode="aggressive" ;;
|
|
33
|
+
--json|-j) output="json" ;;
|
|
34
|
+
--help|-h) _doctor_code_help; return ;;
|
|
35
|
+
--) shift; args+=("$@"); break ;;
|
|
36
|
+
-*)
|
|
37
|
+
if [[ -d "$1" ]]; then
|
|
38
|
+
target_dir="$1"
|
|
39
|
+
else
|
|
40
|
+
log_warn "Unknown option: $1"
|
|
41
|
+
_doctor_code_help
|
|
42
|
+
return 1
|
|
43
|
+
fi ;;
|
|
44
|
+
*)
|
|
45
|
+
if [[ -z "$target_dir" ]] && [[ -d "$1" ]]; then
|
|
46
|
+
target_dir="$1"
|
|
47
|
+
else
|
|
48
|
+
args+=("$1")
|
|
49
|
+
fi ;;
|
|
50
|
+
esac
|
|
51
|
+
shift
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
target_dir="${target_dir:-${args[0]:-${PWD}}}"
|
|
55
|
+
target_dir="$(cd "$target_dir" 2>/dev/null && pwd)" || {
|
|
56
|
+
log_error "Directory not found: $target_dir"; return 1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
local human_output=true
|
|
60
|
+
[[ "$output" == "json" ]] && human_output=false
|
|
61
|
+
local fix_msg=""
|
|
62
|
+
[[ "$fix_mode" != "none" ]] && fix_msg=" + fix: $fix_mode"
|
|
63
|
+
if $human_output; then
|
|
64
|
+
echo
|
|
65
|
+
box "◈ KARNEL DOCTOR — CODE ◈"
|
|
66
|
+
echo
|
|
67
|
+
log_info "Analyzing: ${D_CYAN}$target_dir${NC}"
|
|
68
|
+
log_info "Mode: ${D_CYAN}$mode${NC}${fix_msg}"
|
|
69
|
+
echo
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
_exec_init || return 1
|
|
73
|
+
|
|
74
|
+
if ! _detect_project "$target_dir"; then
|
|
75
|
+
return 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ ${#PROJECT_LANGS[@]} -eq 0 ]]; then
|
|
79
|
+
if $human_output; then
|
|
80
|
+
log_warn "No recognized project structure found."
|
|
81
|
+
echo
|
|
82
|
+
log_info "Supported files:"; echo
|
|
83
|
+
for sig in "package.json" "tsconfig.json" "pyproject.toml" "requirements.txt" \
|
|
84
|
+
"go.mod" "Cargo.toml" "Gemfile" "composer.json" "pubspec.yaml" \
|
|
85
|
+
"CMakeLists.txt" "Dockerfile" "pom.xml" "build.gradle" "*.csproj" \
|
|
86
|
+
"mix.exs" "build.zig" "flake.nix" "Project.toml" "*.sh" "*.sql"; do
|
|
87
|
+
printf " ${D_CYAN}%s${NC}\n" "$sig"
|
|
88
|
+
done
|
|
89
|
+
echo
|
|
90
|
+
else
|
|
91
|
+
_format_report "$output" "$target_dir"
|
|
92
|
+
fi
|
|
93
|
+
return
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Deduplicate langs
|
|
97
|
+
local -A seen; local unique_langs=()
|
|
98
|
+
for l in "${PROJECT_LANGS[@]}"; do [[ -z "${seen[$l]:-}" ]] && seen[$l]=1 && unique_langs+=("$l"); done
|
|
99
|
+
|
|
100
|
+
local manifest_map=()
|
|
101
|
+
for m in "${PROJECT_MANIFESTS[@]}"; do
|
|
102
|
+
IFS=':' read -r ml mp <<< "$m"
|
|
103
|
+
manifest_map+=("$ml|$mp")
|
|
104
|
+
done
|
|
105
|
+
|
|
106
|
+
if $human_output; then
|
|
107
|
+
separator_section "Ecosystems Detected"
|
|
108
|
+
echo
|
|
109
|
+
for lang in "${unique_langs[@]}"; do
|
|
110
|
+
local found_man=""; local found_fw=""
|
|
111
|
+
for m in "${manifest_map[@]}"; do
|
|
112
|
+
IFS='|' read -r ml mp <<< "$m"
|
|
113
|
+
[[ "$ml" == "$lang" ]] && found_man="$mp"
|
|
114
|
+
done
|
|
115
|
+
for fw_entry in "${PROJECT_FRAMEWORKS[@]}"; do
|
|
116
|
+
IFS=':' read -r fw_lang fw_name <<< "$fw_entry"
|
|
117
|
+
[[ "$fw_lang" == "$lang" ]] && found_fw="$fw_name"
|
|
118
|
+
done
|
|
119
|
+
printf " ${D_GREEN}✔${NC} %s" "$lang"
|
|
120
|
+
[[ -n "$found_man" ]] && printf " ${D_CYAN}(%s)${NC}" "$(basename "$(dirname "$found_man")" 2>/dev/null || printf '%s' "$(basename "$found_man")")"
|
|
121
|
+
[[ -n "$found_fw" ]] && printf " → ${D_YELLOW}%s${NC}" "$found_fw"
|
|
122
|
+
echo
|
|
123
|
+
done
|
|
124
|
+
|
|
125
|
+
if [[ ${#PROJECT_SUBDIRS[@]} -gt 0 ]]; then
|
|
126
|
+
echo; separator_section "Subprojects"
|
|
127
|
+
for sp in "${PROJECT_SUBDIRS[@]}"; do
|
|
128
|
+
IFS=':' read -r sp_name _sp_path sp_langs _sp_fws <<< "$sp"
|
|
129
|
+
printf " ${D_CYAN}├─${NC} %s ${D_GREEN}(%s)${NC}\n" "$sp_name" "${sp_langs// /, }"
|
|
130
|
+
done
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
echo; separator_section "Analysis"
|
|
134
|
+
echo
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
$human_output && log_info "Analyzing ${D_CYAN}cross-language checks${NC} in ${D_CYAN}$target_dir${NC}..."
|
|
138
|
+
_exec_run_lang "Cross-language" "$target_dir" "$mode" true
|
|
139
|
+
for lang in "${unique_langs[@]}"; do
|
|
140
|
+
local language_dir
|
|
141
|
+
language_dir=$(_doctor_code_language_dir "$lang" "$target_dir")
|
|
142
|
+
if $human_output; then
|
|
143
|
+
log_info "Analyzing ${D_CYAN}$lang${NC} in ${D_CYAN}$language_dir${NC}..."
|
|
144
|
+
fi
|
|
145
|
+
_exec_run_lang "$lang" "$language_dir" "$mode" false
|
|
146
|
+
done
|
|
147
|
+
|
|
148
|
+
# Apply fixes if requested
|
|
149
|
+
if [[ "$fix_mode" != "none" ]]; then
|
|
150
|
+
if $human_output; then
|
|
151
|
+
echo; separator_section "Auto-Fix"
|
|
152
|
+
echo
|
|
153
|
+
fi
|
|
154
|
+
local fix_count=0
|
|
155
|
+
for entry in "${_CODE_RESULTS[@]}"; do
|
|
156
|
+
IFS='|' read -r severity lang tool category _ _ <<< "$entry"
|
|
157
|
+
case "$severity" in
|
|
158
|
+
error|warning|info) ;;
|
|
159
|
+
*) continue ;;
|
|
160
|
+
esac
|
|
161
|
+
while IFS= read -r te; do
|
|
162
|
+
[[ -z "$te" ]] && continue
|
|
163
|
+
local t_name t_category t_fix t_safety fix_file=""
|
|
164
|
+
_parse_lang_tool "$te"
|
|
165
|
+
t_name="${PARSED_LANG_TOOL[0]}"
|
|
166
|
+
t_category="${PARSED_LANG_TOOL[1]}"
|
|
167
|
+
t_fix="${PARSED_LANG_TOOL[3]}"
|
|
168
|
+
t_safety="${PARSED_LANG_TOOL[4]}"
|
|
169
|
+
[[ "$t_name" == "$tool" && "$t_category" == "$category" && -n "$t_fix" ]] || continue
|
|
170
|
+
if [[ "$fix_mode" == "safe" && "$t_safety" != "safe" ]]; then
|
|
171
|
+
$human_output && log_info "Skipping $tool — fix is not classified as safe"
|
|
172
|
+
continue
|
|
173
|
+
fi
|
|
174
|
+
local fix_dir="$target_dir" include_registry_global=false
|
|
175
|
+
if [[ "$lang" == "Cross-language" ]]; then
|
|
176
|
+
include_registry_global=true
|
|
177
|
+
else
|
|
178
|
+
fix_dir=$(_doctor_code_language_dir "$lang" "$target_dir")
|
|
179
|
+
fi
|
|
180
|
+
if [[ "$t_fix" == *'{}'* ]]; then
|
|
181
|
+
fix_file=$(_find_tool_sample "$tool" "$lang" "$fix_dir") || continue
|
|
182
|
+
fi
|
|
183
|
+
if $human_output; then
|
|
184
|
+
_exec_apply_fix "$tool" "$t_fix" "$lang" "$fix_file" "$fix_dir" || continue
|
|
185
|
+
else
|
|
186
|
+
_exec_apply_fix "$tool" "$t_fix" "$lang" "$fix_file" "$fix_dir" >&2 || continue
|
|
187
|
+
fi
|
|
188
|
+
fix_count=$((fix_count + 1))
|
|
189
|
+
done < <(_get_lang_tools "$lang" "$mode" "$include_registry_global")
|
|
190
|
+
done
|
|
191
|
+
if (( fix_count == 0 )) && $human_output; then
|
|
192
|
+
log_info "No auto-fixes available for current findings"
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
_format_report "$output" "$target_dir"
|
|
197
|
+
$human_output && echo
|
|
198
|
+
return 0
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
_doctor_code_help() {
|
|
202
|
+
echo; box "◈ KARNEL DOCTOR — CODE ◈"
|
|
203
|
+
echo
|
|
204
|
+
log_info "Usage: karnel doctor code [options] [directory]"
|
|
205
|
+
echo
|
|
206
|
+
separator_section "Analysis Modes"
|
|
207
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--quick, -q" "Syntax, format, lint, type-check, tests (default)"
|
|
208
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--standard, -s" "Adds security, deps, coverage"
|
|
209
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--deep, -d" "All registered check categories"
|
|
210
|
+
echo
|
|
211
|
+
separator_section "Fix Modes"
|
|
212
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--fix, --safe-fix" "Apply safe auto-fixes only"
|
|
213
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--aggressive-fix" "Apply fixes not classified as safe"
|
|
214
|
+
echo
|
|
215
|
+
separator_section "Output"
|
|
216
|
+
printf " ${D_CYAN}%-20s${NC} %s\n" "--json, -j" "Machine-readable JSON output"
|
|
217
|
+
echo
|
|
218
|
+
separator_section "Examples"
|
|
219
|
+
list_item "${D_CYAN}karnel doctor code${NC} — Quick analysis of current dir"
|
|
220
|
+
list_item "${D_CYAN}karnel doctor code --standard${NC} — Standard analysis"
|
|
221
|
+
list_item "${D_CYAN}karnel doctor code --deep ~/projects/myapp${NC} — Deep analysis"
|
|
222
|
+
list_item "${D_CYAN}karnel doctor code --fix${NC} — Quick + safe auto-fix"
|
|
223
|
+
list_item "${D_CYAN}karnel doctor code --json > report.json${NC} — JSON output"
|
|
224
|
+
echo
|
|
225
|
+
}
|