loki-mode 6.66.0 → 6.67.0

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/autonomy/loki CHANGED
@@ -37,6 +37,13 @@ log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
37
37
  log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
38
38
  log_debug() { echo -e "${CYAN}[DEBUG]${NC} $*"; }
39
39
 
40
+ # Source TUI library if available (spinners, progress bars, tables, diffs)
41
+ _LOKI_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42
+ if [ -f "$_LOKI_SCRIPT_DIR/tui.sh" ]; then
43
+ # shellcheck source=tui.sh
44
+ source "$_LOKI_SCRIPT_DIR/tui.sh"
45
+ fi
46
+
40
47
  # Resolve the script's real path (handles symlinks)
41
48
  resolve_script_path() {
42
49
  local script="$1"
@@ -421,6 +428,7 @@ show_help() {
421
428
  echo " checkpoint|cp Save/restore session checkpoints"
422
429
  echo " projects Multi-project registry management"
423
430
  echo " audit [cmd] Agent audit log and quality scanning (log|scan)"
431
+ echo " heal <path> Legacy system healing (archaeology, stabilize, modernize)"
424
432
  echo " review [opts] Standalone code review with quality gates (diff, staged, PR, files)"
425
433
  echo " optimize Optimize prompts based on session history"
426
434
  echo " enterprise Enterprise feature management (tokens, OIDC)"
@@ -442,6 +450,8 @@ show_help() {
442
450
  echo " plan <PRD> Dry-run PRD analysis: complexity, cost, and execution plan"
443
451
  echo " ci [opts] CI/CD quality gate integration (--pr, --report, --github-comment)"
444
452
  echo " test [opts] AI-powered test generation (--file, --dir, --changed, --dry-run)"
453
+ echo " context [cmd] Context window management (show|files|tools|add|clear)"
454
+ echo " code [cmd] Codebase intelligence (overview|symbols|deps|hotspots|diff)"
445
455
  echo " report [opts] Session report generator (--format text|markdown|html, --output)"
446
456
  echo " share [opts] Share session report as GitHub Gist (--private, --format)"
447
457
  echo " version Show version"
@@ -1700,13 +1710,40 @@ cmd_status() {
1700
1710
  echo -e "${CYAN}Pending Tasks:${NC} $task_count"
1701
1711
  fi
1702
1712
 
1703
- # Check budget
1713
+ # Check budget with visual gauge
1704
1714
  if [ -f "$LOKI_DIR/metrics/budget.json" ]; then
1705
1715
  local budget_limit budget_used budget_remaining
1706
1716
  budget_limit=$(LOKI_BUDGET_FILE="$LOKI_DIR/metrics/budget.json" python3 -c "import json,os; d=json.load(open(os.environ['LOKI_BUDGET_FILE'])); print(d.get('budget_limit', 0))" 2>/dev/null || echo "0")
1707
1717
  budget_used=$(LOKI_BUDGET_FILE="$LOKI_DIR/metrics/budget.json" python3 -c "import json,os; d=json.load(open(os.environ['LOKI_BUDGET_FILE'])); print(round(d.get('budget_used', 0), 2))" 2>/dev/null || echo "0")
1708
1718
  if [ "$budget_limit" != "0" ]; then
1709
1719
  echo -e "${CYAN}Budget:${NC} \$$budget_used / \$$budget_limit"
1720
+ # Show budget gauge if TUI is loaded
1721
+ if type context_gauge &>/dev/null; then
1722
+ local used_cents remaining_cents limit_cents
1723
+ used_cents=$(echo "scale=0; $budget_used * 100" | bc 2>/dev/null || echo "0")
1724
+ limit_cents=$(echo "scale=0; $budget_limit * 100" | bc 2>/dev/null || echo "100")
1725
+ # Render as integer cents for the gauge
1726
+ used_cents=${used_cents%.*}
1727
+ limit_cents=${limit_cents%.*}
1728
+ [ -z "$used_cents" ] && used_cents=0
1729
+ [ -z "$limit_cents" ] && limit_cents=100
1730
+ context_gauge "$used_cents" "$limit_cents" "Budget"
1731
+ fi
1732
+ else
1733
+ echo -e "${CYAN}Cost:${NC} \$$budget_used (no limit)"
1734
+ fi
1735
+ fi
1736
+
1737
+ # Context window usage (token tracking)
1738
+ if [ -f "$LOKI_DIR/state/context-usage.json" ]; then
1739
+ local ctx_used ctx_total
1740
+ ctx_total=$(python3 -c "import json; d=json.load(open('$LOKI_DIR/state/context-usage.json')); print(d.get('window_size', 200000))" 2>/dev/null || echo "200000")
1741
+ ctx_used=$(python3 -c "import json; d=json.load(open('$LOKI_DIR/state/context-usage.json')); print(d.get('used_tokens', 0))" 2>/dev/null || echo "0")
1742
+ if type context_gauge &>/dev/null; then
1743
+ context_gauge "$ctx_used" "$ctx_total" "Context"
1744
+ else
1745
+ local ctx_pct=$((ctx_used * 100 / ctx_total))
1746
+ echo -e "${CYAN}Context:${NC} ${ctx_pct}% (${ctx_used} / ${ctx_total} tokens)"
1710
1747
  fi
1711
1748
  fi
1712
1749
 
@@ -1718,6 +1755,10 @@ cmd_status() {
1718
1755
  echo -e "${CYAN}Dashboard:${NC} http://127.0.0.1:$port/"
1719
1756
  fi
1720
1757
  fi
1758
+
1759
+ echo ""
1760
+ echo -e "${DIM} Tip: loki context show - detailed token breakdown${NC}"
1761
+ echo -e "${DIM} Tip: loki code overview - codebase intelligence${NC}"
1721
1762
  }
1722
1763
 
1723
1764
  # JSON output for loki status --json
@@ -8600,6 +8641,418 @@ with open(manifest_path, 'w') as f:
8600
8641
  echo -e "View details: loki migrate --status ${migration_id}"
8601
8642
  }
8602
8643
 
8644
+ #===============================================================================
8645
+ # loki heal - Legacy System Healing (v6.67.0)
8646
+ # Inspired by Amazon AGI Lab's "How Agentic AI Helps Heal Systems We Can't Replace"
8647
+ # Modernize legacy codebases incrementally without breaking existing behavior.
8648
+ #===============================================================================
8649
+
8650
+ cmd_heal_help() {
8651
+ echo -e "${BOLD}loki heal${NC} - Legacy system healing (v6.67.0)"
8652
+ echo ""
8653
+ echo "Heal legacy codebases by understanding their real behaviors -- the quirks,"
8654
+ echo "delays, error states, and invisible dependencies -- then modernizing"
8655
+ echo "incrementally while preserving institutional logic."
8656
+ echo ""
8657
+ echo "Usage: loki heal <path-to-codebase> [options]"
8658
+ echo ""
8659
+ echo "Phases:"
8660
+ echo " archaeology Extract knowledge, map dependencies, catalog friction"
8661
+ echo " stabilize Add observability and tests without changing behavior"
8662
+ echo " isolate Create adapter boundaries between components"
8663
+ echo " modernize Replace components one at a time behind adapters"
8664
+ echo " validate Verify behavioral equivalence with baseline"
8665
+ echo ""
8666
+ echo "Options:"
8667
+ echo " --phase PHASE Start from specific phase (default: archaeology)"
8668
+ echo " --resume Resume healing from last checkpoint"
8669
+ echo " --status Show healing progress"
8670
+ echo " --report Generate healing report"
8671
+ echo " --strict Block ALL behavioral changes without approval"
8672
+ echo " --archaeology-only Extract knowledge only, don't modify code"
8673
+ echo " --friction-map Show friction map for target codebase"
8674
+ echo " --compliance PRESET Compliance mode (healthcare|fintech|government)"
8675
+ echo " --provider NAME AI provider (default: claude)"
8676
+ echo " --parallel N Parallel healing agents (default: 1)"
8677
+ echo " --no-dashboard Disable web dashboard"
8678
+ echo " --dry-run Show healing plan without executing"
8679
+ echo ""
8680
+ echo "Environment Variables:"
8681
+ echo " LOKI_HEAL_MODE Enable healing mode (auto-set by 'loki heal')"
8682
+ echo " LOKI_HEAL_PHASE Override starting phase"
8683
+ echo " LOKI_HEAL_PRESERVE_FRICTION Warn before removing friction points (default: true)"
8684
+ echo " LOKI_HEAL_STRICT Block all behavioral changes (default: false)"
8685
+ echo ""
8686
+ echo "Examples:"
8687
+ echo " loki heal ./legacy-app # Full healing pipeline"
8688
+ echo " loki heal ./legacy-app --phase archaeology # Knowledge extraction only"
8689
+ echo " loki heal ./legacy-app --archaeology-only # Extract without modifying"
8690
+ echo " loki heal ./legacy-app --resume # Resume from checkpoint"
8691
+ echo " loki heal ./legacy-app --strict # Strict behavioral preservation"
8692
+ echo " loki heal --status # Show healing progress"
8693
+ echo " loki heal --friction-map ./legacy-app # View friction map"
8694
+ }
8695
+
8696
+ cmd_heal() {
8697
+ local codebase_path=""
8698
+ local phase="${LOKI_HEAL_PHASE:-archaeology}"
8699
+ local do_resume="false"
8700
+ local do_status="false"
8701
+ local do_report="false"
8702
+ local do_friction_map="false"
8703
+ local archaeology_only="false"
8704
+ local strict="${LOKI_HEAL_STRICT:-false}"
8705
+ local compliance=""
8706
+ local provider="${LOKI_PROVIDER:-claude}"
8707
+ local parallel="1"
8708
+ local no_dashboard="false"
8709
+ local dry_run="false"
8710
+
8711
+ if [ $# -eq 0 ]; then
8712
+ cmd_heal_help
8713
+ return 0
8714
+ fi
8715
+
8716
+ while [[ $# -gt 0 ]]; do
8717
+ case "$1" in
8718
+ --help|-h)
8719
+ cmd_heal_help
8720
+ return 0
8721
+ ;;
8722
+ --status)
8723
+ do_status="true"
8724
+ shift
8725
+ ;;
8726
+ --report)
8727
+ do_report="true"
8728
+ shift
8729
+ ;;
8730
+ --friction-map)
8731
+ do_friction_map="true"
8732
+ shift
8733
+ ;;
8734
+ --phase)
8735
+ if [[ -z "${2:-}" ]]; then
8736
+ echo -e "${RED}Error: --phase requires a value (archaeology|stabilize|isolate|modernize|validate)${NC}"
8737
+ return 1
8738
+ fi
8739
+ phase="$2"
8740
+ shift 2
8741
+ ;;
8742
+ --phase=*)
8743
+ phase="${1#*=}"
8744
+ shift
8745
+ ;;
8746
+ --resume)
8747
+ do_resume="true"
8748
+ shift
8749
+ ;;
8750
+ --strict)
8751
+ strict="true"
8752
+ shift
8753
+ ;;
8754
+ --archaeology-only)
8755
+ archaeology_only="true"
8756
+ phase="archaeology"
8757
+ shift
8758
+ ;;
8759
+ --compliance)
8760
+ if [[ -z "${2:-}" ]]; then
8761
+ echo -e "${RED}Error: --compliance requires a value${NC}"
8762
+ return 1
8763
+ fi
8764
+ compliance="$2"
8765
+ shift 2
8766
+ ;;
8767
+ --provider)
8768
+ if [[ -z "${2:-}" ]]; then
8769
+ echo -e "${RED}Error: --provider requires a value${NC}"
8770
+ return 1
8771
+ fi
8772
+ provider="$2"
8773
+ shift 2
8774
+ ;;
8775
+ --parallel)
8776
+ if [[ -z "${2:-}" ]]; then
8777
+ echo -e "${RED}Error: --parallel requires a value${NC}"
8778
+ return 1
8779
+ fi
8780
+ parallel="$2"
8781
+ shift 2
8782
+ ;;
8783
+ --no-dashboard)
8784
+ no_dashboard="true"
8785
+ shift
8786
+ ;;
8787
+ --dry-run)
8788
+ dry_run="true"
8789
+ shift
8790
+ ;;
8791
+ -*)
8792
+ echo -e "${RED}Unknown option: $1${NC}"
8793
+ echo "Run 'loki heal --help' for usage."
8794
+ return 1
8795
+ ;;
8796
+ *)
8797
+ if [ -z "$codebase_path" ]; then
8798
+ codebase_path="$1"
8799
+ else
8800
+ echo -e "${RED}Error: Unexpected argument: $1${NC}"
8801
+ return 1
8802
+ fi
8803
+ shift
8804
+ ;;
8805
+ esac
8806
+ done
8807
+
8808
+ # Route to subcommands
8809
+ if [ "$do_status" = "true" ]; then
8810
+ local heal_dir="${codebase_path:-.}/.loki/healing"
8811
+ if [[ ! -d "$heal_dir" ]]; then
8812
+ echo -e "${YELLOW}No healing session found.${NC}"
8813
+ echo "Start one with: loki heal <path-to-codebase>"
8814
+ return 0
8815
+ fi
8816
+ local progress_file="$heal_dir/healing-progress.json"
8817
+ if [[ -f "$progress_file" ]]; then
8818
+ echo -e "${BOLD}Healing Progress${NC}"
8819
+ echo ""
8820
+ python3 -c "
8821
+ import json, sys
8822
+ with open(sys.argv[1]) as f:
8823
+ data = json.load(f)
8824
+ print(f\" Codebase: {data.get('codebase', '?')}\")
8825
+ print(f\" Started: {data.get('started', '?')}\")
8826
+ print(f\" Overall Health: {data.get('overall_health', 0):.0%}\")
8827
+ print()
8828
+ for c in data.get('components', []):
8829
+ pct = c.get('characterization_passing', 0) / max(c.get('characterization_tests', 1), 1)
8830
+ print(f\" [{c.get('phase', '?'):12s}] {c.get('name', '?')}\")
8831
+ print(f\" Friction: {c.get('friction_resolved', 0)}/{c.get('friction_points', 0)} resolved\")
8832
+ print(f\" Tests: {c.get('characterization_passing', 0)}/{c.get('characterization_tests', 0)} passing\")
8833
+ print(f\" Health: {c.get('health_score', 0):.0%}\")
8834
+ print()
8835
+ " "$progress_file" 2>/dev/null || echo " Error reading progress file."
8836
+ else
8837
+ echo " No progress data yet. Healing may still be in archaeology phase."
8838
+ fi
8839
+ return 0
8840
+ fi
8841
+
8842
+ if [ "$do_friction_map" = "true" ]; then
8843
+ local friction_file="${codebase_path:-.}/.loki/healing/friction-map.json"
8844
+ if [[ ! -f "$friction_file" ]]; then
8845
+ echo -e "${YELLOW}No friction map found.${NC}"
8846
+ echo "Run archaeology first: loki heal ${codebase_path:-.} --phase archaeology"
8847
+ return 0
8848
+ fi
8849
+ echo -e "${BOLD}Friction Map${NC}"
8850
+ echo ""
8851
+ python3 -c "
8852
+ import json, sys
8853
+ with open(sys.argv[1]) as f:
8854
+ data = json.load(f)
8855
+ for f in data.get('frictions', []):
8856
+ safe = 'YES' if f.get('safe_to_remove') else 'NO'
8857
+ cls = f.get('classification', 'unknown')
8858
+ print(f\" [{f.get('id', '?')}] {f.get('location', '?')}\")
8859
+ print(f\" Behavior: {f.get('behavior', '?')}\")
8860
+ print(f\" Classification: {cls}\")
8861
+ print(f\" Safe to remove: {safe}\")
8862
+ if f.get('evidence'):
8863
+ print(f\" Evidence: {f.get('evidence')}\")
8864
+ print()
8865
+ " "$friction_file" 2>/dev/null || echo " Error reading friction map."
8866
+ return 0
8867
+ fi
8868
+
8869
+ if [ "$do_report" = "true" ]; then
8870
+ local heal_dir="${codebase_path:-.}/.loki/healing"
8871
+ if [[ ! -d "$heal_dir" ]]; then
8872
+ echo -e "${YELLOW}No healing session found.${NC}"
8873
+ return 0
8874
+ fi
8875
+ echo -e "${BOLD}Healing Report${NC}"
8876
+ echo ""
8877
+ echo " Friction map: $(python3 -c "import json; print(len(json.load(open('$heal_dir/friction-map.json')).get('frictions', [])))" 2>/dev/null || echo '0') points"
8878
+ echo " Failure modes: $(python3 -c "import json; print(len(json.load(open('$heal_dir/failure-modes.json')).get('modes', [])))" 2>/dev/null || echo '0') cataloged"
8879
+ echo " Institutional knowledge: $(wc -l < "$heal_dir/institutional-knowledge.md" 2>/dev/null || echo '0') lines"
8880
+ echo " Characterization tests: $(find "$heal_dir/characterization-tests/" -name "*.json" 2>/dev/null | wc -l | tr -d ' ') tests"
8881
+ echo ""
8882
+ return 0
8883
+ fi
8884
+
8885
+ # Validate codebase path
8886
+ if [ -z "$codebase_path" ]; then
8887
+ echo -e "${RED}Error: Codebase path is required${NC}"
8888
+ echo "Usage: loki heal <path-to-codebase> [options]"
8889
+ return 1
8890
+ fi
8891
+
8892
+ if [[ ! -d "$codebase_path" ]]; then
8893
+ echo -e "${RED}Error: Directory not found: $codebase_path${NC}"
8894
+ return 1
8895
+ fi
8896
+
8897
+ # Validate phase
8898
+ case "$phase" in
8899
+ archaeology|stabilize|isolate|modernize|validate) ;;
8900
+ *)
8901
+ echo -e "${RED}Error: Invalid phase: $phase${NC}"
8902
+ echo "Valid phases: archaeology, stabilize, isolate, modernize, validate"
8903
+ return 1
8904
+ ;;
8905
+ esac
8906
+
8907
+ # Initialize healing directory
8908
+ local heal_dir="$codebase_path/.loki/healing"
8909
+ mkdir -p "$heal_dir"/{behavioral-baseline,characterization-tests}
8910
+
8911
+ # Initialize healing state files if they don't exist
8912
+ [[ ! -f "$heal_dir/friction-map.json" ]] && echo '{"frictions":[]}' > "$heal_dir/friction-map.json"
8913
+ [[ ! -f "$heal_dir/failure-modes.json" ]] && echo '{"modes":[]}' > "$heal_dir/failure-modes.json"
8914
+ [[ ! -f "$heal_dir/institutional-knowledge.md" ]] && echo "# Institutional Knowledge Registry" > "$heal_dir/institutional-knowledge.md"
8915
+
8916
+ # Initialize or update healing progress
8917
+ if [[ ! -f "$heal_dir/healing-progress.json" ]] || [ "$do_resume" != "true" ]; then
8918
+ python3 -c "
8919
+ import json
8920
+ from datetime import datetime
8921
+ progress = {
8922
+ 'codebase': '$codebase_path',
8923
+ 'started': datetime.now().isoformat(),
8924
+ 'current_phase': '$phase',
8925
+ 'strict_mode': $( [ "$strict" = "true" ] && echo "True" || echo "False" ),
8926
+ 'components': [],
8927
+ 'overall_health': 0.0
8928
+ }
8929
+ with open('$heal_dir/healing-progress.json', 'w') as f:
8930
+ json.dump(progress, f, indent=2)
8931
+ " || true
8932
+ fi
8933
+
8934
+ emit_event healing cli start "phase=$phase" "codebase=$codebase_path" "strict=$strict" 2>/dev/null || true
8935
+
8936
+ echo -e "${BOLD}Legacy System Healing${NC}"
8937
+ echo ""
8938
+ echo -e " Codebase: $codebase_path"
8939
+ echo -e " Phase: $phase"
8940
+ echo -e " Strict: $strict"
8941
+ echo -e " Provider: $provider"
8942
+ if [ "$archaeology_only" = "true" ]; then
8943
+ echo -e " Mode: archaeology-only (no modifications)"
8944
+ fi
8945
+ echo ""
8946
+
8947
+ if [ "$dry_run" = "true" ]; then
8948
+ echo -e "${CYAN}Dry run: Showing healing plan without executing${NC}"
8949
+ echo ""
8950
+ echo " Phase 1: Archaeology"
8951
+ echo " - Map dependency graph"
8952
+ echo " - Scan for friction points (sleeps, retries, magic values)"
8953
+ echo " - Extract institutional knowledge from comments"
8954
+ echo " - Write characterization tests for critical paths"
8955
+ echo ""
8956
+ echo " Phase 2: Stabilize"
8957
+ echo " - Add logging/observability without behavior changes"
8958
+ echo " - Extract hardcoded config values"
8959
+ echo " - Add type annotations where possible"
8960
+ echo ""
8961
+ echo " Phase 3: Isolate"
8962
+ echo " - Define component boundaries"
8963
+ echo " - Create adapter interfaces"
8964
+ echo " - Add integration tests at boundaries"
8965
+ echo ""
8966
+ echo " Phase 4: Modernize"
8967
+ echo " - Replace components one at a time behind adapters"
8968
+ echo " - Verify characterization tests after each replacement"
8969
+ echo ""
8970
+ echo " Phase 5: Validate"
8971
+ echo " - Compare outputs with pre-healing baseline"
8972
+ echo " - Generate healing report"
8973
+ return 0
8974
+ fi
8975
+
8976
+ # Build healing prompt for the AI provider
8977
+ local heal_prompt="You are performing legacy system healing on the codebase at '${codebase_path}'.
8978
+
8979
+ IMPORTANT: Read skills/healing.md for the full healing protocol.
8980
+
8981
+ Current phase: ${phase}
8982
+ Strict mode: ${strict}
8983
+ Archaeology only: ${archaeology_only}
8984
+
8985
+ HEALING RULES:
8986
+ 1. FRICTION IS SEMANTICS: Before removing any 'quirky' code (sleeps, retries, magic values), verify it is not an undocumented business rule. Document all friction in .loki/healing/friction-map.json.
8987
+ 2. LEARN THROUGH FAILURE: Deliberately probe error paths and edge cases. Catalog every failure mode in .loki/healing/failure-modes.json. Each failure teaches you about the system.
8988
+ 3. CHARACTERIZE BEFORE MODIFYING: Write tests that capture CURRENT behavior (not intended behavior) before changing anything.
8989
+ 4. PRESERVE INSTITUTIONAL LOGIC: Extract business rules from comments, git history, error messages, and test fixtures into .loki/healing/institutional-knowledge.md.
8990
+ 5. INCREMENTAL ONLY: Change ONE component at a time. Verify ALL characterization tests pass after each change.
8991
+
8992
+ Phase-specific instructions:
8993
+ - archaeology: Map dependencies, catalog friction, extract knowledge, write characterization tests. Do NOT modify source code.
8994
+ - stabilize: Add logging and observability. Extract config. Add type hints. Do NOT change behavior.
8995
+ - isolate: Create adapter interfaces at component boundaries. Add integration tests.
8996
+ - modernize: Replace components behind adapters. Verify characterization tests pass.
8997
+ - validate: Compare outputs with behavioral baseline. Generate healing report.
8998
+
8999
+ $([ "$strict" = "true" ] && echo "STRICT MODE: You MUST NOT change any observable behavior without creating a .loki/signals/HUMAN_REVIEW_NEEDED signal and waiting for approval.")
9000
+ $([ "$archaeology_only" = "true" ] && echo "ARCHAEOLOGY ONLY: Do NOT modify any source files. Only create files in .loki/healing/.")
9001
+
9002
+ Begin healing now. Follow the RARV cycle for each action."
9003
+
9004
+ # Execute with the appropriate provider
9005
+ local heal_exit=0
9006
+ case "$provider" in
9007
+ claude)
9008
+ local run_args=(--dangerously-skip-permissions -p "$heal_prompt" --output-format stream-json --verbose)
9009
+ (cd "$codebase_path" && claude "${run_args[@]}" 2>&1) | \
9010
+ while IFS= read -r line; do
9011
+ if echo "$line" | python3 -c "
9012
+ import sys, json
9013
+ try:
9014
+ d = json.loads(sys.stdin.read())
9015
+ if d.get('type') == 'assistant':
9016
+ for item in d.get('message', {}).get('content', []):
9017
+ if item.get('type') == 'text':
9018
+ print(item.get('text', ''), end='')
9019
+ except Exception: pass
9020
+ " 2>/dev/null; then
9021
+ true
9022
+ fi
9023
+ done && heal_exit=0 || heal_exit=$?
9024
+ ;;
9025
+ codex)
9026
+ (cd "$codebase_path" && codex exec --full-auto "$heal_prompt" 2>&1) || heal_exit=$?
9027
+ ;;
9028
+ gemini)
9029
+ (cd "$codebase_path" && gemini --approval-mode=yolo "$heal_prompt" 2>&1) || heal_exit=$?
9030
+ ;;
9031
+ cline)
9032
+ (cd "$codebase_path" && cline -y "$heal_prompt" 2>&1) || heal_exit=$?
9033
+ ;;
9034
+ aider)
9035
+ local aider_model="${LOKI_AIDER_MODEL:-claude-3.7-sonnet}"
9036
+ local aider_flags="${LOKI_AIDER_FLAGS:-}"
9037
+ # shellcheck disable=SC2086
9038
+ (cd "$codebase_path" && aider --message "$heal_prompt" --yes-always --no-auto-commits --model "$aider_model" $aider_flags 2>&1) || heal_exit=$?
9039
+ ;;
9040
+ esac
9041
+
9042
+ emit_event healing cli complete "phase=$phase" "exit=$heal_exit" 2>/dev/null || true
9043
+
9044
+ if [ "$heal_exit" -eq 0 ]; then
9045
+ echo ""
9046
+ echo -e "${GREEN}Healing phase '${phase}' complete.${NC}"
9047
+ echo -e "View progress: loki heal --status ${codebase_path}"
9048
+ echo -e "View friction: loki heal --friction-map ${codebase_path}"
9049
+ else
9050
+ echo ""
9051
+ echo -e "${YELLOW}Healing phase '${phase}' finished with warnings (exit: $heal_exit).${NC}"
9052
+ echo -e "Check: loki heal --status ${codebase_path}"
9053
+ fi
9054
+ }
9055
+
8603
9056
  cmd_migrate() {
8604
9057
  local codebase_path=""
8605
9058
  local target=""
@@ -10149,6 +10602,9 @@ main() {
10149
10602
  optimize)
10150
10603
  cmd_optimize "$@"
10151
10604
  ;;
10605
+ heal)
10606
+ cmd_heal "$@"
10607
+ ;;
10152
10608
  migrate)
10153
10609
  cmd_migrate "$@"
10154
10610
  ;;
@@ -10203,6 +10659,12 @@ main() {
10203
10659
  share)
10204
10660
  cmd_share "$@"
10205
10661
  ;;
10662
+ context|ctx)
10663
+ cmd_context "$@"
10664
+ ;;
10665
+ code)
10666
+ cmd_code "$@"
10667
+ ;;
10206
10668
  version|--version|-v)
10207
10669
  cmd_version
10208
10670
  ;;
@@ -14949,6 +15411,561 @@ METRICS_SCRIPT
14949
15411
  fi
14950
15412
  }
14951
15413
 
15414
+ # Context window management (inspired by Kiro CLI /context command)
15415
+ # Shows token usage breakdown and context window utilization
15416
+ cmd_context() {
15417
+ local subcommand="${1:-show}"
15418
+ shift 2>/dev/null || true
15419
+
15420
+ case "$subcommand" in
15421
+ --help|-h|help)
15422
+ echo -e "${BOLD}loki context${NC} - Context window management"
15423
+ echo ""
15424
+ echo "Usage: loki context <subcommand>"
15425
+ echo ""
15426
+ echo "Subcommands:"
15427
+ echo " show Show context window usage breakdown (default)"
15428
+ echo " files List files currently in context with token estimates"
15429
+ echo " tools Show tool token usage per origin (MCP, native)"
15430
+ echo " add Add file to context (@file reference expansion)"
15431
+ echo " clear Clear accumulated context (start fresh conversation)"
15432
+ echo ""
15433
+ echo "Examples:"
15434
+ echo " loki context # Show context usage"
15435
+ echo " loki context files # List context files"
15436
+ echo " loki context tools # Show tool token costs"
15437
+ echo " loki context add src/main.py # Add file to context"
15438
+ echo ""
15439
+ echo "Inspired by: Kiro CLI /context command (kiro.dev)"
15440
+ return 0
15441
+ ;;
15442
+ show)
15443
+ _context_show "$@"
15444
+ ;;
15445
+ files)
15446
+ _context_files "$@"
15447
+ ;;
15448
+ tools)
15449
+ _context_tools "$@"
15450
+ ;;
15451
+ add)
15452
+ _context_add "$@"
15453
+ ;;
15454
+ clear)
15455
+ _context_clear "$@"
15456
+ ;;
15457
+ *)
15458
+ echo -e "${RED}Unknown subcommand: $subcommand${NC}"
15459
+ echo "Run 'loki context help' for usage."
15460
+ return 1
15461
+ ;;
15462
+ esac
15463
+ }
15464
+
15465
+ _context_show() {
15466
+ local loki_dir="${LOKI_DIR:-.loki}"
15467
+
15468
+ if [ ! -d "$loki_dir" ]; then
15469
+ echo -e "${YELLOW}No active session.${NC}"
15470
+ return 0
15471
+ fi
15472
+
15473
+ section_header "Context Window Usage" 2>/dev/null || echo -e "\n${BOLD}Context Window Usage${NC}"
15474
+
15475
+ # Read context tracker data if available
15476
+ local ctx_file="$loki_dir/state/context-usage.json"
15477
+ if [ -f "$ctx_file" ]; then
15478
+ local total_tokens used_tokens input_tokens output_tokens cache_tokens
15479
+ total_tokens=$(python3 -c "import json; d=json.load(open('$ctx_file')); print(d.get('window_size', 200000))" 2>/dev/null || echo "200000")
15480
+ used_tokens=$(python3 -c "import json; d=json.load(open('$ctx_file')); print(d.get('used_tokens', 0))" 2>/dev/null || echo "0")
15481
+ input_tokens=$(python3 -c "import json; d=json.load(open('$ctx_file')); print(d.get('input_tokens', 0))" 2>/dev/null || echo "0")
15482
+ output_tokens=$(python3 -c "import json; d=json.load(open('$ctx_file')); print(d.get('output_tokens', 0))" 2>/dev/null || echo "0")
15483
+ cache_tokens=$(python3 -c "import json; d=json.load(open('$ctx_file')); print(d.get('cache_read_tokens', 0))" 2>/dev/null || echo "0")
15484
+
15485
+ # Display gauge
15486
+ if type context_gauge &>/dev/null; then
15487
+ context_gauge "$used_tokens" "$total_tokens" "Window"
15488
+ else
15489
+ local pct=$((used_tokens * 100 / total_tokens))
15490
+ echo -e " ${CYAN}Context:${NC} ${pct}% used (${used_tokens} / ${total_tokens} tokens)"
15491
+ fi
15492
+ echo ""
15493
+
15494
+ # Breakdown
15495
+ echo -e " ${BOLD}Breakdown${NC} ${DIM}(estimated)${NC}"
15496
+ echo -e " ${DIM}Input tokens:${NC} $(printf '%8s' "$input_tokens")"
15497
+ echo -e " ${DIM}Output tokens:${NC} $(printf '%8s' "$output_tokens")"
15498
+ echo -e " ${DIM}Cache reads:${NC} $(printf '%8s' "$cache_tokens")"
15499
+ else
15500
+ echo -e " ${DIM}No context tracking data yet.${NC}"
15501
+ echo -e " ${DIM}Context usage is tracked during active sessions.${NC}"
15502
+ fi
15503
+
15504
+ # Show token costs if budget file exists
15505
+ local budget_file="$loki_dir/metrics/budget.json"
15506
+ if [ -f "$budget_file" ]; then
15507
+ echo ""
15508
+ echo -e " ${BOLD}Cost${NC}"
15509
+ local budget_used budget_limit
15510
+ budget_used=$(python3 -c "import json; d=json.load(open('$budget_file')); print(round(d.get('budget_used', 0), 4))" 2>/dev/null || echo "0")
15511
+ budget_limit=$(python3 -c "import json; d=json.load(open('$budget_file')); print(d.get('budget_limit', 0))" 2>/dev/null || echo "0")
15512
+
15513
+ if [ "$budget_limit" != "0" ]; then
15514
+ echo -e " ${DIM}Spent:${NC} \$${budget_used} / \$${budget_limit}"
15515
+ local remaining
15516
+ remaining=$(echo "scale=4; $budget_limit - $budget_used" | bc 2>/dev/null || echo "0")
15517
+ echo -e " ${DIM}Remaining:${NC} \$${remaining}"
15518
+ else
15519
+ echo -e " ${DIM}Spent:${NC} \$${budget_used} (no budget limit set)"
15520
+ fi
15521
+ fi
15522
+ echo ""
15523
+ }
15524
+
15525
+ _context_files() {
15526
+ local loki_dir="${LOKI_DIR:-.loki}"
15527
+ local ctx_files="$loki_dir/state/context-files.json"
15528
+
15529
+ section_header "Context Files" 2>/dev/null || echo -e "\n${BOLD}Context Files${NC}"
15530
+
15531
+ if [ -f "$ctx_files" ]; then
15532
+ python3 << 'PYEOF'
15533
+ import json, os
15534
+
15535
+ ctx_file = os.environ.get("LOKI_DIR", ".loki") + "/state/context-files.json"
15536
+ try:
15537
+ with open(ctx_file) as f:
15538
+ files = json.load(f)
15539
+ total = 0
15540
+ for entry in files:
15541
+ name = entry.get("path", "unknown")
15542
+ tokens = entry.get("estimated_tokens", 0)
15543
+ total += tokens
15544
+ print(f" {tokens:>8} tokens {name}")
15545
+ print(f"\n {'Total:':>8} {total} tokens")
15546
+ except Exception:
15547
+ print(" No context files tracked yet.")
15548
+ PYEOF
15549
+ else
15550
+ echo -e " ${DIM}No context files tracked.${NC}"
15551
+ echo ""
15552
+ echo " Add files with: loki context add <file>"
15553
+ echo " Or use @path syntax in your prompt:"
15554
+ echo " @src/main.py - inject file contents"
15555
+ echo " @src/ - inject directory tree"
15556
+ fi
15557
+ echo ""
15558
+ }
15559
+
15560
+ _context_tools() {
15561
+ local loki_dir="${LOKI_DIR:-.loki}"
15562
+
15563
+ section_header "Tool Token Usage" 2>/dev/null || echo -e "\n${BOLD}Tool Token Usage${NC}"
15564
+
15565
+ # Read MCP config and estimate tool token usage
15566
+ local mcp_config="$loki_dir/mcp/config.json"
15567
+ if [ -f "$mcp_config" ]; then
15568
+ python3 << 'PYEOF'
15569
+ import json, os
15570
+
15571
+ config_file = os.environ.get("LOKI_DIR", ".loki") + "/mcp/config.json"
15572
+ try:
15573
+ with open(config_file) as f:
15574
+ config = json.load(f)
15575
+ servers = config.get("mcpServers", {})
15576
+ total = 0
15577
+ for name, server in servers.items():
15578
+ tools = server.get("tools", [])
15579
+ # Estimate ~500 tokens per tool definition
15580
+ est = len(tools) * 500 if tools else 500
15581
+ total += est
15582
+ print(f" {est:>6} tokens {name} ({len(tools) if tools else '?'} tools)")
15583
+ print(f"\n Total MCP tool overhead: ~{total} tokens per request")
15584
+ except Exception:
15585
+ print(" No MCP servers configured.")
15586
+ PYEOF
15587
+ else
15588
+ echo -e " ${DIM}Native tools only (no MCP servers configured).${NC}"
15589
+ fi
15590
+
15591
+ echo ""
15592
+ echo -e " ${DIM}Native tools: ~2000 tokens (bash, read, write, glob, grep)${NC}"
15593
+ echo -e " ${DIM}Tip: Large MCP tool descriptions impact performance.${NC}"
15594
+ echo ""
15595
+ }
15596
+
15597
+ _context_add() {
15598
+ local file_path="$1"
15599
+ if [ -z "$file_path" ]; then
15600
+ echo -e "${RED}Usage: loki context add <file-or-dir>${NC}"
15601
+ echo ""
15602
+ echo "Adds a file's content (or directory tree) to the context."
15603
+ echo "Equivalent to @path inline reference syntax."
15604
+ return 1
15605
+ fi
15606
+
15607
+ # Strip @ prefix if user types it
15608
+ file_path="${file_path#@}"
15609
+
15610
+ if [ -d "$file_path" ]; then
15611
+ echo -e "${BOLD}Directory tree: ${file_path}${NC}"
15612
+ echo ""
15613
+ if type tree_display &>/dev/null; then
15614
+ tree_display "$file_path"
15615
+ else
15616
+ find "$file_path" -maxdepth 3 -not -path '*/\.*' | head -50
15617
+ fi
15618
+ echo ""
15619
+
15620
+ # Count files and estimate tokens
15621
+ local file_count
15622
+ file_count=$(find "$file_path" -type f -not -path '*/\.*' | wc -l)
15623
+ echo -e "${DIM}$file_count files in directory${NC}"
15624
+ elif [ -f "$file_path" ]; then
15625
+ local size
15626
+ size=$(wc -c < "$file_path")
15627
+ # Rough estimate: 1 token per 4 bytes
15628
+ local est_tokens=$((size / 4))
15629
+ local lines
15630
+ lines=$(wc -l < "$file_path")
15631
+
15632
+ echo -e "${BOLD}File: ${file_path}${NC}"
15633
+ echo -e " ${DIM}Size:${NC} $size bytes"
15634
+ echo -e " ${DIM}Lines:${NC} $lines"
15635
+ echo -e " ${DIM}Tokens:${NC} ~$est_tokens (estimated)"
15636
+
15637
+ # Store in context files list
15638
+ local loki_dir="${LOKI_DIR:-.loki}"
15639
+ mkdir -p "$loki_dir/state"
15640
+ local ctx_files="$loki_dir/state/context-files.json"
15641
+ python3 -c "
15642
+ import json, os
15643
+ path = '$file_path'
15644
+ tokens = $est_tokens
15645
+ ctx_file = '$ctx_files'
15646
+ try:
15647
+ with open(ctx_file) as f:
15648
+ files = json.load(f)
15649
+ except:
15650
+ files = []
15651
+ # Avoid duplicates
15652
+ files = [f for f in files if f.get('path') != path]
15653
+ files.append({'path': path, 'estimated_tokens': tokens, 'size': $size, 'lines': $lines})
15654
+ with open(ctx_file, 'w') as f:
15655
+ json.dump(files, f, indent=2)
15656
+ " 2>/dev/null
15657
+
15658
+ echo -e " ${GREEN}Added to context.${NC}"
15659
+ else
15660
+ echo -e "${RED}Not found: $file_path${NC}"
15661
+ return 1
15662
+ fi
15663
+ }
15664
+
15665
+ _context_clear() {
15666
+ local loki_dir="${LOKI_DIR:-.loki}"
15667
+ rm -f "$loki_dir/state/context-files.json"
15668
+ rm -f "$loki_dir/state/context-usage.json"
15669
+ echo -e "${GREEN}Context cleared.${NC}"
15670
+ echo -e "${DIM}A new conversation will start fresh.${NC}"
15671
+ }
15672
+
15673
+ # Codebase intelligence (inspired by Kiro CLI /code command)
15674
+ # Provides workspace overview, symbol search, and pattern matching
15675
+ cmd_code() {
15676
+ local subcommand="${1:-help}"
15677
+ shift 2>/dev/null || true
15678
+
15679
+ case "$subcommand" in
15680
+ --help|-h|help)
15681
+ echo -e "${BOLD}loki code${NC} - Codebase intelligence"
15682
+ echo ""
15683
+ echo "Usage: loki code <subcommand>"
15684
+ echo ""
15685
+ echo "Subcommands:"
15686
+ echo " overview Generate workspace overview (file types, LOC, structure)"
15687
+ echo " symbols Search for symbols (functions, classes, variables)"
15688
+ echo " deps Show dependency graph for a file or module"
15689
+ echo " hotspots Find code hotspots (most-changed files, complexity)"
15690
+ echo " diff Show colored diff of recent changes"
15691
+ echo ""
15692
+ echo "Examples:"
15693
+ echo " loki code overview # Full codebase overview"
15694
+ echo " loki code overview --silent # Compact output"
15695
+ echo " loki code symbols 'class.*Service' # Find service classes"
15696
+ echo " loki code deps src/main.py # Show dependencies"
15697
+ echo " loki code hotspots --top 10 # Top 10 changed files"
15698
+ echo " loki code diff # Colored diff of uncommitted changes"
15699
+ echo ""
15700
+ echo "Inspired by: Kiro CLI /code command (kiro.dev)"
15701
+ return 0
15702
+ ;;
15703
+ overview)
15704
+ _code_overview "$@"
15705
+ ;;
15706
+ symbols)
15707
+ _code_symbols "$@"
15708
+ ;;
15709
+ deps)
15710
+ _code_deps "$@"
15711
+ ;;
15712
+ hotspots)
15713
+ _code_hotspots "$@"
15714
+ ;;
15715
+ diff)
15716
+ _code_diff "$@"
15717
+ ;;
15718
+ *)
15719
+ echo -e "${RED}Unknown subcommand: $subcommand${NC}"
15720
+ echo "Run 'loki code help' for usage."
15721
+ return 1
15722
+ ;;
15723
+ esac
15724
+ }
15725
+
15726
+ _code_overview() {
15727
+ local silent=false
15728
+ local target_dir="."
15729
+ while [[ $# -gt 0 ]]; do
15730
+ case "$1" in
15731
+ --silent|-s) silent=true; shift ;;
15732
+ *) target_dir="$1"; shift ;;
15733
+ esac
15734
+ done
15735
+
15736
+ if $silent; then
15737
+ echo -e "${BOLD}$(basename "$(cd "$target_dir" && pwd)")${NC}"
15738
+ else
15739
+ section_header "Codebase Overview: $(basename "$(cd "$target_dir" && pwd)")" 2>/dev/null || echo -e "\n${BOLD}Codebase Overview${NC}"
15740
+ fi
15741
+
15742
+ # Language breakdown
15743
+ python3 << PYEOF
15744
+ import os, collections, sys
15745
+
15746
+ target = "$target_dir"
15747
+ silent = $( $silent && echo "True" || echo "False" )
15748
+
15749
+ # File extension to language mapping
15750
+ EXT_MAP = {
15751
+ '.py': 'Python', '.js': 'JavaScript', '.ts': 'TypeScript', '.tsx': 'TypeScript',
15752
+ '.jsx': 'JavaScript', '.go': 'Go', '.rs': 'Rust', '.java': 'Java',
15753
+ '.rb': 'Ruby', '.php': 'PHP', '.c': 'C', '.cpp': 'C++', '.h': 'C/C++ Header',
15754
+ '.cs': 'C#', '.swift': 'Swift', '.kt': 'Kotlin', '.scala': 'Scala',
15755
+ '.sh': 'Shell', '.bash': 'Shell', '.zsh': 'Shell',
15756
+ '.html': 'HTML', '.css': 'CSS', '.scss': 'SCSS', '.vue': 'Vue',
15757
+ '.md': 'Markdown', '.json': 'JSON', '.yaml': 'YAML', '.yml': 'YAML',
15758
+ '.toml': 'TOML', '.xml': 'XML', '.sql': 'SQL',
15759
+ '.dockerfile': 'Docker', '.tf': 'Terraform', '.proto': 'Protobuf',
15760
+ }
15761
+
15762
+ SKIP_DIRS = {'.git', 'node_modules', '.loki', '__pycache__', '.venv', 'venv',
15763
+ 'dist', 'build', '.next', 'target', '.tox', '.mypy_cache',
15764
+ 'vendor', '.cargo', 'coverage', '.pytest_cache'}
15765
+
15766
+ lang_files = collections.Counter()
15767
+ lang_lines = collections.Counter()
15768
+ total_files = 0
15769
+ total_lines = 0
15770
+ dir_count = 0
15771
+
15772
+ for root, dirs, files in os.walk(target):
15773
+ # Skip hidden and build directories
15774
+ dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith('.')]
15775
+ dir_count += 1
15776
+ for f in files:
15777
+ ext = os.path.splitext(f)[1].lower()
15778
+ if ext in EXT_MAP:
15779
+ lang = EXT_MAP[ext]
15780
+ lang_files[lang] += 1
15781
+ total_files += 1
15782
+ try:
15783
+ fp = os.path.join(root, f)
15784
+ lines = sum(1 for _ in open(fp, 'rb'))
15785
+ lang_lines[lang] += lines
15786
+ total_lines += lines
15787
+ except:
15788
+ pass
15789
+
15790
+ if not silent:
15791
+ print(f"\n Files: {total_files} Lines: {total_lines:,} Dirs: {dir_count}")
15792
+ print()
15793
+
15794
+ # Language table
15795
+ print(" Language Files Lines %")
15796
+ print(" " + "-" * 40)
15797
+ for lang, count in lang_lines.most_common(15):
15798
+ pct = (count / total_lines * 100) if total_lines > 0 else 0
15799
+ files = lang_files[lang]
15800
+ bar_len = int(pct / 5)
15801
+ bar = "=" * bar_len
15802
+ print(f" {lang:<16} {files:>5} {count:>7,} {pct:>4.1f}% {bar}")
15803
+
15804
+ if not silent:
15805
+ # Show directory structure (top-level)
15806
+ print()
15807
+ print(" Top-level structure:")
15808
+ entries = sorted(os.listdir(target))
15809
+ for entry in entries:
15810
+ if entry.startswith('.') and entry not in ('.github',):
15811
+ continue
15812
+ full = os.path.join(target, entry)
15813
+ if os.path.isdir(full) and entry not in SKIP_DIRS:
15814
+ subcount = sum(1 for _, _, fs in os.walk(full) for f in fs)
15815
+ print(f" {entry + '/':30s} ({subcount} files)")
15816
+ elif os.path.isfile(full):
15817
+ size = os.path.getsize(full)
15818
+ if size > 1024:
15819
+ print(f" {entry:30s} ({size // 1024}KB)")
15820
+ PYEOF
15821
+ echo ""
15822
+ }
15823
+
15824
+ _code_symbols() {
15825
+ local pattern="${1:-.}"
15826
+ local file_type="${2:-}"
15827
+
15828
+ section_header "Symbol Search: $pattern" 2>/dev/null || echo -e "\n${BOLD}Symbol Search${NC}"
15829
+
15830
+ # Search for function/class definitions using ripgrep or grep
15831
+ local search_patterns=(
15832
+ "^(def|function|func|fn|pub fn|async def|class|interface|struct|enum|type|export (const|function|class|interface))\s+.*${pattern}"
15833
+ )
15834
+
15835
+ if command -v rg &>/dev/null; then
15836
+ rg -n --no-heading -e "^(def |function |func |fn |pub fn |async def |class |interface |struct |enum |type |export (const|function|class|interface) ).*${pattern}" \
15837
+ --type-add 'code:*.{py,js,ts,tsx,go,rs,java,rb,php,c,cpp,h,cs,swift,kt}' \
15838
+ -t code \
15839
+ --glob '!node_modules' --glob '!.git' --glob '!dist' --glob '!build' \
15840
+ . 2>/dev/null | head -50
15841
+ else
15842
+ grep -rn "^\(def\|function\|func\|fn\|class\|interface\|struct\|enum\|type\).*${pattern}" \
15843
+ --include='*.py' --include='*.js' --include='*.ts' --include='*.go' \
15844
+ --include='*.rs' --include='*.java' --include='*.rb' \
15845
+ --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist \
15846
+ . 2>/dev/null | head -50
15847
+ fi
15848
+ echo ""
15849
+ }
15850
+
15851
+ _code_deps() {
15852
+ local target_file="$1"
15853
+ if [ -z "$target_file" ]; then
15854
+ echo -e "${RED}Usage: loki code deps <file>${NC}"
15855
+ return 1
15856
+ fi
15857
+
15858
+ section_header "Dependencies: $target_file" 2>/dev/null || echo -e "\n${BOLD}Dependencies${NC}"
15859
+
15860
+ if [ ! -f "$target_file" ]; then
15861
+ echo -e "${RED}File not found: $target_file${NC}"
15862
+ return 1
15863
+ fi
15864
+
15865
+ local ext="${target_file##*.}"
15866
+ echo -e " ${BOLD}Imports/Requires:${NC}"
15867
+
15868
+ case "$ext" in
15869
+ py)
15870
+ grep -n "^import\|^from.*import" "$target_file" 2>/dev/null | sed 's/^/ /'
15871
+ ;;
15872
+ js|ts|tsx|jsx)
15873
+ grep -n "import\|require(" "$target_file" 2>/dev/null | sed 's/^/ /'
15874
+ ;;
15875
+ go)
15876
+ grep -n "\"" "$target_file" 2>/dev/null | grep -v "//" | sed 's/^/ /'
15877
+ ;;
15878
+ rs)
15879
+ grep -n "^use\|^extern crate" "$target_file" 2>/dev/null | sed 's/^/ /'
15880
+ ;;
15881
+ java)
15882
+ grep -n "^import" "$target_file" 2>/dev/null | sed 's/^/ /'
15883
+ ;;
15884
+ rb)
15885
+ grep -n "^require\|^require_relative" "$target_file" 2>/dev/null | sed 's/^/ /'
15886
+ ;;
15887
+ *)
15888
+ echo -e " ${DIM}Unsupported file type: .$ext${NC}"
15889
+ ;;
15890
+ esac
15891
+
15892
+ echo ""
15893
+ echo -e " ${BOLD}Referenced by:${NC}"
15894
+ local basename_file
15895
+ basename_file=$(basename "$target_file" ".$ext")
15896
+ if command -v rg &>/dev/null; then
15897
+ rg -l "$basename_file" --glob '!node_modules' --glob '!.git' --glob '!dist' . 2>/dev/null | \
15898
+ grep -v "^${target_file}$" | head -20 | sed 's/^/ /'
15899
+ else
15900
+ grep -rl "$basename_file" --exclude-dir=node_modules --exclude-dir=.git . 2>/dev/null | \
15901
+ grep -v "^${target_file}$" | head -20 | sed 's/^/ /'
15902
+ fi
15903
+ echo ""
15904
+ }
15905
+
15906
+ _code_hotspots() {
15907
+ local top_n=10
15908
+ while [[ $# -gt 0 ]]; do
15909
+ case "$1" in
15910
+ --top) top_n="${2:-10}"; shift 2 ;;
15911
+ --top=*) top_n="${1#*=}"; shift ;;
15912
+ *) shift ;;
15913
+ esac
15914
+ done
15915
+
15916
+ section_header "Code Hotspots (top $top_n)" 2>/dev/null || echo -e "\n${BOLD}Code Hotspots${NC}"
15917
+
15918
+ if ! git rev-parse --is-inside-work-tree &>/dev/null; then
15919
+ echo -e " ${RED}Not a git repository.${NC}"
15920
+ return 1
15921
+ fi
15922
+
15923
+ echo -e " ${BOLD}Most Changed Files (last 90 days):${NC}"
15924
+ echo ""
15925
+ git log --since="90 days ago" --pretty=format: --name-only 2>/dev/null | \
15926
+ sort | uniq -c | sort -rn | head -"$top_n" | \
15927
+ while read -r count file; do
15928
+ [ -z "$file" ] && continue
15929
+ printf " %4d changes %s\n" "$count" "$file"
15930
+ done
15931
+
15932
+ echo ""
15933
+ echo -e " ${BOLD}Largest Files by LOC:${NC}"
15934
+ echo ""
15935
+ find . -type f \( -name '*.py' -o -name '*.js' -o -name '*.ts' -o -name '*.go' \
15936
+ -o -name '*.rs' -o -name '*.java' -o -name '*.sh' \) \
15937
+ -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' \
15938
+ -exec wc -l {} \; 2>/dev/null | sort -rn | head -"$top_n" | \
15939
+ while read -r lines file; do
15940
+ printf " %6d lines %s\n" "$lines" "$file"
15941
+ done
15942
+ echo ""
15943
+ }
15944
+
15945
+ _code_diff() {
15946
+ if ! git rev-parse --is-inside-work-tree &>/dev/null; then
15947
+ echo -e "${RED}Not a git repository.${NC}"
15948
+ return 1
15949
+ fi
15950
+
15951
+ # Use delta if available, otherwise colored diff
15952
+ if command -v delta &>/dev/null; then
15953
+ git diff "$@" | delta
15954
+ else
15955
+ git diff "$@" | while IFS= read -r line; do
15956
+ case "$line" in
15957
+ diff*) echo -e "${BOLD}${line}${NC}" ;;
15958
+ ---*) echo -e "${BOLD}${line}${NC}" ;;
15959
+ +++*) echo -e "${BOLD}${line}${NC}" ;;
15960
+ @@*) echo -e "${CYAN}${line}${NC}" ;;
15961
+ +*) echo -e "${GREEN}${line}${NC}" ;;
15962
+ -*) echo -e "${RED}${line}${NC}" ;;
15963
+ *) echo "$line" ;;
15964
+ esac
15965
+ done
15966
+ fi
15967
+ }
15968
+
14952
15969
  # Output shell completion scripts
14953
15970
  cmd_completions() {
14954
15971
  local shell="${1:-bash}"