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.
Files changed (81) hide show
  1. package/README.md +230 -225
  2. package/install.sh +30 -9
  3. package/karnel/bin/karnel +2 -2
  4. package/karnel/cli/commands/backup.sh +10 -2
  5. package/karnel/cli/commands/brain.sh +28 -23
  6. package/karnel/cli/commands/cleanup.sh +10 -4
  7. package/karnel/cli/commands/deploy.sh +53 -0
  8. package/karnel/cli/commands/doctor/code.sh +225 -0
  9. package/karnel/cli/commands/doctor/code_detect.sh +184 -0
  10. package/karnel/cli/commands/doctor/code_exec.sh +278 -0
  11. package/karnel/cli/commands/doctor/code_langs.sh +229 -0
  12. package/karnel/cli/commands/doctor/fixes.sh +63 -46
  13. package/karnel/cli/commands/doctor.sh +222 -80
  14. package/karnel/cli/commands/env.sh +2 -0
  15. package/karnel/cli/commands/ia.sh +12 -2
  16. package/karnel/cli/commands/init.sh +17 -9
  17. package/karnel/cli/commands/install.sh +90 -54
  18. package/karnel/cli/commands/list.sh +36 -0
  19. package/karnel/cli/commands/pg.sh +21 -6
  20. package/karnel/cli/commands/reinstall.sh +99 -0
  21. package/karnel/cli/commands/restore.sh +43 -3
  22. package/karnel/cli/commands/search.sh +2 -2
  23. package/karnel/cli/commands/update.sh +6 -1
  24. package/karnel/cli/commands/upgrade.sh +8 -1
  25. package/karnel/cli/commands/voice.sh +2 -1
  26. package/karnel/cli/karnel.sh +11 -5
  27. package/karnel/modules/games.sh +11 -0
  28. package/karnel/tools/ai/all.sh +1 -0
  29. package/karnel/tools/ai/copilot-termux/README.md +40 -0
  30. package/karnel/tools/ai/copilot-termux/install.sh +80 -0
  31. package/karnel/tools/ai/copilot-termux/package.json +20 -0
  32. package/karnel/tools/db/all.sh +2 -0
  33. package/karnel/tools/deploy/railway/install.sh +2 -2
  34. package/karnel/tools/dev/all.sh +8 -0
  35. package/karnel/tools/dev/applaunch/__pycache__/applaunch.cpython-314.pyc +0 -0
  36. package/karnel/tools/dev/applaunch/applaunch.py +434 -0
  37. package/karnel/tools/dev/applaunch/install.sh +51 -0
  38. package/karnel/tools/dev/fconv/__pycache__/fconv.cpython-314.pyc +0 -0
  39. package/karnel/tools/dev/fconv/fconv.py +749 -0
  40. package/karnel/tools/dev/fconv/install.sh +51 -0
  41. package/karnel/tools/dev/filecheck/__pycache__/filecheck.cpython-314.pyc +0 -0
  42. package/karnel/tools/dev/filecheck/filecheck.py +429 -0
  43. package/karnel/tools/dev/filecheck/install.sh +51 -0
  44. package/karnel/tools/dev/notes/__pycache__/notes.cpython-314.pyc +0 -0
  45. package/karnel/tools/dev/notes/install.sh +51 -0
  46. package/karnel/tools/dev/notes/notes.py +849 -0
  47. package/karnel/tools/dev/passman/__pycache__/passman.cpython-314.pyc +0 -0
  48. package/karnel/tools/dev/passman/install.sh +51 -0
  49. package/karnel/tools/dev/passman/passman.py +536 -0
  50. package/karnel/tools/dev/splash/__pycache__/splash.cpython-314.pyc +0 -0
  51. package/karnel/tools/dev/splash/install.sh +51 -0
  52. package/karnel/tools/dev/splash/splash.py +188 -0
  53. package/karnel/tools/dev/treex/install.sh +51 -0
  54. package/karnel/tools/dev/treex/treex.py +2036 -0
  55. package/karnel/tools/dev/websites/__pycache__/websites.cpython-314.pyc +0 -0
  56. package/karnel/tools/dev/websites/install.sh +51 -0
  57. package/karnel/tools/dev/websites/websites.py +844 -0
  58. package/karnel/tools/games/all.sh +62 -0
  59. package/karnel/tools/games/arcade/__pycache__/arcade.cpython-314.pyc +0 -0
  60. package/karnel/tools/games/arcade/arcade.py +193 -0
  61. package/karnel/tools/games/arcade/install.sh +51 -0
  62. package/karnel/tools/games/buzz/__pycache__/buzz.cpython-314.pyc +0 -0
  63. package/karnel/tools/games/buzz/buzz.py +1264 -0
  64. package/karnel/tools/games/buzz/install.sh +51 -0
  65. package/karnel/tools/games/ctfgod/__pycache__/ctfgod.cpython-314.pyc +0 -0
  66. package/karnel/tools/games/ctfgod/ctfgod.py +1576 -0
  67. package/karnel/tools/games/ctfgod/install.sh +51 -0
  68. package/karnel/tools/games/detective/__pycache__/detective.cpython-314.pyc +0 -0
  69. package/karnel/tools/games/detective/detective.py +2091 -0
  70. package/karnel/tools/games/detective/install.sh +51 -0
  71. package/karnel/tools/games/pet-friends/__pycache__/pet-friends.cpython-314.pyc +0 -0
  72. package/karnel/tools/games/pet-friends/install.sh +51 -0
  73. package/karnel/tools/games/pet-friends/pet-friends.py +15802 -0
  74. package/karnel/tools/games/tamagotchi/__pycache__/tamagotchi.cpython-314.pyc +0 -0
  75. package/karnel/tools/games/tamagotchi/install.sh +51 -0
  76. package/karnel/tools/games/tamagotchi/tamagotchi.py +1224 -0
  77. package/karnel/tools/games/terminal-arcade/__pycache__/terminal-arcade.cpython-314.pyc +0 -0
  78. package/karnel/tools/games/terminal-arcade/terminal-arcade.py +193 -0
  79. package/karnel/tools/npm/all.sh +1 -0
  80. package/karnel/utils/log.sh +34 -12
  81. 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" ]] && rm -rf "$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
- bootstrap_dependencies
272
- banner
273
- install_dependencies
274
- setup_directories
275
- clone_repo
276
- create_symlink
277
- save_config
278
- show_final_message
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" ]] && cp "$HOME/.ssh/id_"* "$HOME/.ssh/config" "$HOME/.ssh/known_hosts" "$tmp/config/ssh/" 2>/dev/null
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" ]] && cp -r "$d" "$tmp/config/config/" 2>/dev/null
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
- cd "$BRAIN_DIR" || return 1
278
- git init &>/dev/null
279
- echo "# Karnel Brain" >README.md
280
- git add -A
281
- git commit -m "init brain" &>/dev/null
282
- git remote add origin "https://github.com/$gh_user/$repo_name.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=$(echo "$result" | cut -d'|' -f1)
473
- local ln=$(echo "$result" | cut -d'|' -f2)
474
- local text=$(echo "$result" | cut -d'|' -f3-)
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
- cd "$BRAIN_DIR" || return 1
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
- local branch
790
- branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
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=$(mktemp)
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=$(du -sh "$PREFIX/lib/python3"* 2>/dev/null | awk '{print $1}')
46
- if [[ -n "$pycache_size" ]]; then
47
- log_info "Python __pycache__: ${pycache_size} → cleaning..."
48
- find "$PREFIX/lib/python3"* -name "__pycache__" -exec rm -rf {} + 2>/dev/null
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
+ }