loki-mode 5.28.0 → 5.29.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/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  [![npm downloads](https://img.shields.io/npm/dw/loki-mode)](https://www.npmjs.com/package/loki-mode)
7
7
  [![GitHub stars](https://img.shields.io/github/stars/asklokesh/loki-mode)](https://github.com/asklokesh/loki-mode)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Loki%20Mode-purple?logo=github)](https://github.com/marketplace/actions/loki-mode-code-review)
9
10
  [![Claude Code](https://img.shields.io/badge/Claude-Code-orange)](https://claude.ai)
10
11
  [![Agent Types](https://img.shields.io/badge/Agent%20Types-41-blue)]()
11
12
  [![Loki Mode](https://img.shields.io/badge/Loki%20Mode-98.78%25%20Pass%401-blueviolet)](benchmarks/results/)
@@ -55,7 +56,45 @@ claude --dangerously-skip-permissions
55
56
  # Then say: Loki Mode with PRD at ./my-prd.md
56
57
  ```
57
58
 
58
- Also available via **Homebrew**, **Docker**, **VS Code Extension**, and **direct shell script**. See the [Installation Guide](docs/INSTALLATION.md) for all 6 installation methods and detailed instructions.
59
+ ### Option 3: GitHub Action
60
+
61
+ Add automated AI code review to your pull requests:
62
+
63
+ ```yaml
64
+ # .github/workflows/loki-review.yml
65
+ name: Loki Code Review
66
+
67
+ on:
68
+ pull_request:
69
+ types: [opened, synchronize]
70
+
71
+ permissions:
72
+ contents: read
73
+ pull-requests: write
74
+
75
+ jobs:
76
+ review:
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@v4
80
+ - uses: asklokesh/loki-mode@v5
81
+ with:
82
+ github_token: ${{ secrets.GITHUB_TOKEN }}
83
+ mode: review # review, fix, or test
84
+ provider: claude # claude, codex, or gemini
85
+ max_iterations: 3 # higher = more thorough
86
+ budget_limit: '5.00' # max cost in USD
87
+ ```
88
+
89
+ **Modes:**
90
+
91
+ | Mode | Description |
92
+ |------|-------------|
93
+ | `review` | Analyze PR diff, post structured review as PR comment |
94
+ | `fix` | Automatically fix issues found in the codebase |
95
+ | `test` | Run autonomous test generation and validation |
96
+
97
+ Also available via **Homebrew**, **Docker**, **VS Code Extension**, and **direct shell script**. See the [Installation Guide](docs/INSTALLATION.md) for all 7 installation methods and detailed instructions.
59
98
 
60
99
  ### Multi-Provider Support (v5.0.0)
61
100
 
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with zero human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v5.28.0
6
+ # Loki Mode v5.29.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -260,4 +260,4 @@ Auto-detected or force with `LOKI_COMPLEXITY`:
260
260
 
261
261
  ---
262
262
 
263
- **v5.28.0 | Demo, quick mode, init, cost dashboard, 12 templates, GitHub Action | ~270 lines core**
263
+ **v5.29.0 | Demo, quick mode, init, cost dashboard, 12 templates, GitHub Action | ~270 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 5.28.0
1
+ 5.29.0
@@ -76,6 +76,7 @@ council_init() {
76
76
  cat > "$COUNCIL_STATE_DIR/state.json" << 'COUNCIL_EOF'
77
77
  {
78
78
  "initialized": true,
79
+ "enabled": true,
79
80
  "total_votes": 0,
80
81
  "approve_votes": 0,
81
82
  "reject_votes": 0,
package/autonomy/loki CHANGED
@@ -506,33 +506,14 @@ cmd_start() {
506
506
  local effective_provider="${provider:-${LOKI_PROVIDER:-claude}}"
507
507
 
508
508
  # Handle sandbox mode - delegate to sandbox.sh
509
- # Skip if we're already inside a container (/.dockerenv exists)
510
- if [[ "${LOKI_SANDBOX_MODE:-}" == "true" ]] && [[ ! -f /.dockerenv ]]; then
509
+ # Skip if we're already inside a sandbox (IS_SANDBOX=1 or /.dockerenv exists)
510
+ if [[ "${LOKI_SANDBOX_MODE:-}" == "true" ]] && [[ "${IS_SANDBOX:-}" != "1" ]] && [[ ! -f /.dockerenv ]]; then
511
511
  if [ ! -f "$SANDBOX_SH" ]; then
512
512
  echo -e "${RED}Error: sandbox.sh not found at $SANDBOX_SH${NC}"
513
513
  exit 1
514
514
  fi
515
515
 
516
- # Check Docker availability
517
- if ! command -v docker &> /dev/null; then
518
- echo -e "${RED}Error: Docker not found${NC}"
519
- echo ""
520
- echo "Docker is required for sandbox mode. Install it:"
521
- echo " macOS: brew install --cask docker"
522
- echo " Linux: curl -fsSL https://get.docker.com | sh"
523
- exit 1
524
- fi
525
-
526
- if ! docker info &> /dev/null 2>&1; then
527
- echo -e "${RED}Error: Docker daemon not running${NC}"
528
- echo ""
529
- echo "Start Docker:"
530
- echo " macOS: Open Docker Desktop app"
531
- echo " Linux: sudo systemctl start docker"
532
- exit 1
533
- fi
534
-
535
- echo -e "${GREEN}Starting Loki Mode in Docker sandbox...${NC}"
516
+ echo -e "${GREEN}Starting Loki Mode in sandbox...${NC}"
536
517
  # Load memory context before sandbox start (errors don't block startup)
537
518
  load_memory_context "$prd_file" || true
538
519
  emit_event session cli start "provider=$effective_provider" "sandbox=true" "prd_path=${prd_file:-}"
@@ -1280,7 +1261,7 @@ cmd_dashboard_start() {
1280
1261
 
1281
1262
  # Start the dashboard server in background
1282
1263
  # LOKI_SKILL_DIR tells server.py where to find static files
1283
- LOKI_SKILL_DIR="$SKILL_DIR" PYTHONPATH="$SKILL_DIR" LOKI_DASHBOARD_HOST="$host" LOKI_DASHBOARD_PORT="$port" \
1264
+ LOKI_DIR="$LOKI_DIR" LOKI_SKILL_DIR="$SKILL_DIR" PYTHONPATH="$SKILL_DIR" LOKI_DASHBOARD_HOST="$host" LOKI_DASHBOARD_PORT="$port" \
1284
1265
  nohup "$python_cmd" -m dashboard.server > "$log_file" 2>&1 &
1285
1266
  local new_pid=$!
1286
1267
 
@@ -2795,6 +2776,7 @@ broadcast_notification() {
2795
2776
  }
2796
2777
 
2797
2778
  # Sandbox management (delegates to sandbox.sh)
2779
+ # sandbox.sh handles detection: Docker Desktop Sandbox > Docker Container > Worktree
2798
2780
  cmd_sandbox() {
2799
2781
  local subcommand="${1:-help}"
2800
2782
  shift || true
@@ -2805,28 +2787,7 @@ cmd_sandbox() {
2805
2787
  exit 1
2806
2788
  fi
2807
2789
 
2808
- # Check Docker availability
2809
- if ! command -v docker &> /dev/null; then
2810
- echo -e "${RED}Error: Docker not found${NC}"
2811
- echo ""
2812
- echo "Docker is required for sandbox mode. Install it:"
2813
- echo " macOS: brew install --cask docker"
2814
- echo " Linux: curl -fsSL https://get.docker.com | sh"
2815
- echo ""
2816
- echo "After installation, start Docker Desktop or the Docker daemon."
2817
- exit 1
2818
- fi
2819
-
2820
- if ! docker info &> /dev/null 2>&1; then
2821
- echo -e "${RED}Error: Docker daemon not running${NC}"
2822
- echo ""
2823
- echo "Start Docker:"
2824
- echo " macOS: Open Docker Desktop app"
2825
- echo " Linux: sudo systemctl start docker"
2826
- exit 1
2827
- fi
2828
-
2829
- # Delegate to sandbox.sh
2790
+ # Delegate to sandbox.sh (it handles Docker/worktree detection internally)
2830
2791
  exec "$SANDBOX_SH" "$subcommand" "$@"
2831
2792
  }
2832
2793
 
@@ -3853,13 +3814,11 @@ if count == 0:
3853
3814
  echo -e "${BOLD}Search Results for: $query${NC}"
3854
3815
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
3855
3816
 
3856
- local query_escaped="${query//\'/\'\\\'\'}"
3857
- python3 -c "
3858
- import json
3859
- import os
3817
+ LOKI_SEARCH_QUERY="$query" LOKI_LEARNINGS_DIR="$learnings_dir" python3 -c "
3818
+ import json, os
3860
3819
 
3861
- learnings_dir = '$learnings_dir'
3862
- query = '${query_escaped}'.lower()
3820
+ learnings_dir = os.environ['LOKI_LEARNINGS_DIR']
3821
+ query = os.environ['LOKI_SEARCH_QUERY'].lower()
3863
3822
 
3864
3823
  for filename in ['patterns.jsonl', 'mistakes.jsonl', 'successes.jsonl']:
3865
3824
  filepath = os.path.join(learnings_dir, filename)
package/autonomy/run.sh CHANGED
@@ -1467,7 +1467,7 @@ create_worktree() {
1467
1467
 
1468
1468
  # Initialize environment (detect and run appropriate install)
1469
1469
  (
1470
- cd "$worktree_path"
1470
+ cd "$worktree_path" || exit 1
1471
1471
  if [ -f "package.json" ]; then
1472
1472
  npm install --silent 2>/dev/null || true
1473
1473
  elif [ -f "requirements.txt" ]; then
@@ -1558,7 +1558,7 @@ spawn_worktree_session() {
1558
1558
  log_step "Spawning ${PROVIDER_DISPLAY_NAME:-Claude} session: $stream_name"
1559
1559
 
1560
1560
  (
1561
- cd "$worktree_path"
1561
+ cd "$worktree_path" || exit 1
1562
1562
  # Provider-specific invocation for parallel sessions
1563
1563
  case "${PROVIDER_NAME:-claude}" in
1564
1564
  claude)
@@ -1695,7 +1695,7 @@ merge_feature() {
1695
1695
  log_step "Merging feature: $feature"
1696
1696
 
1697
1697
  (
1698
- cd "$TARGET_DIR"
1698
+ cd "$TARGET_DIR" || exit 1
1699
1699
 
1700
1700
  # Ensure we're on main
1701
1701
  git checkout main 2>/dev/null
@@ -2079,9 +2079,36 @@ init_loki_dir() {
2079
2079
  EOF
2080
2080
  fi
2081
2081
 
2082
+ # Write pricing.json with provider-specific model rates
2083
+ _write_pricing_json
2084
+
2082
2085
  log_info "Loki directory initialized: .loki/"
2083
2086
  }
2084
2087
 
2088
+ # Write .loki/pricing.json based on active provider
2089
+ _write_pricing_json() {
2090
+ local provider="${LOKI_PROVIDER:-claude}"
2091
+ local updated
2092
+ updated=$(date -u +%Y-%m-%d)
2093
+
2094
+ cat > ".loki/pricing.json" << PRICING_EOF
2095
+ {
2096
+ "provider": "${provider}",
2097
+ "updated": "${updated}",
2098
+ "source": "static",
2099
+ "models": {
2100
+ "opus": {"input": 5.00, "output": 25.00, "label": "Opus 4.6", "provider": "claude"},
2101
+ "sonnet": {"input": 3.00, "output": 15.00, "label": "Sonnet 4.5", "provider": "claude"},
2102
+ "haiku": {"input": 1.00, "output": 5.00, "label": "Haiku 4.5", "provider": "claude"},
2103
+ "gpt-5.3-codex": {"input": 1.50, "output": 12.00, "label": "GPT-5.3 Codex", "provider": "codex"},
2104
+ "gemini-3-pro": {"input": 1.25, "output": 10.00, "label": "Gemini 3 Pro", "provider": "gemini"},
2105
+ "gemini-3-flash": {"input": 0.10, "output": 0.40, "label": "Gemini 3 Flash", "provider": "gemini"}
2106
+ }
2107
+ }
2108
+ PRICING_EOF
2109
+ log_info "Pricing data written: .loki/pricing.json (provider: ${provider})"
2110
+ }
2111
+
2085
2112
  #===============================================================================
2086
2113
  # Gemini Invocation with Rate Limit Fallback
2087
2114
  #===============================================================================
@@ -2384,6 +2411,7 @@ write_dashboard_state() {
2384
2411
  "path": "$project_path"
2385
2412
  },
2386
2413
  "mode": "$mode",
2414
+ "provider": "${PROVIDER_NAME:-claude}",
2387
2415
  "phase": "$current_phase",
2388
2416
  "complexity": "$complexity",
2389
2417
  "iteration": $ITERATION_COUNT,
@@ -2539,6 +2567,30 @@ track_iteration_complete() {
2539
2567
  --context "{\"iteration\":$iteration,\"exit_code\":$exit_code}"
2540
2568
  fi
2541
2569
 
2570
+ # Write efficiency tracking file for /api/cost endpoint
2571
+ mkdir -p .loki/metrics/efficiency
2572
+ local model_tier="sonnet"
2573
+ if [ "${PROVIDER_NAME:-claude}" = "claude" ]; then
2574
+ model_tier="sonnet"
2575
+ elif [ "${PROVIDER_NAME:-claude}" = "codex" ]; then
2576
+ model_tier="gpt-5.3-codex"
2577
+ elif [ "${PROVIDER_NAME:-claude}" = "gemini" ]; then
2578
+ model_tier="gemini-3-pro"
2579
+ fi
2580
+ local phase="$current_phase"
2581
+ [ -z "$phase" ] && phase=$(python3 -c "import json; print(json.load(open('.loki/state/orchestrator.json')).get('currentPhase', 'unknown'))" 2>/dev/null || echo "unknown")
2582
+ cat > ".loki/metrics/efficiency/iteration-${iteration}.json" << EFF_EOF
2583
+ {
2584
+ "iteration": $iteration,
2585
+ "model": "$model_tier",
2586
+ "phase": "$phase",
2587
+ "duration_ms": $duration_ms,
2588
+ "provider": "${PROVIDER_NAME:-claude}",
2589
+ "status": "$status_str",
2590
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
2591
+ }
2592
+ EFF_EOF
2593
+
2542
2594
  # Get task from in-progress
2543
2595
  local in_progress_file=".loki/queue/in-progress.json"
2544
2596
  local completed_file=".loki/queue/completed.json"
@@ -3562,11 +3614,16 @@ start_dashboard() {
3562
3614
  # Check if it's our own dashboard
3563
3615
  local existing_pid=$(lsof -ti :$DASHBOARD_PORT 2>/dev/null | head -1)
3564
3616
  if [ -n "$existing_pid" ]; then
3565
- # Kill existing dashboard on this port
3566
- log_step "Killing existing dashboard on port $DASHBOARD_PORT..."
3567
- kill "$existing_pid" 2>/dev/null || true
3568
- sleep 1
3569
- break
3617
+ # Only kill if it's a Python/uvicorn dashboard process
3618
+ local proc_cmd=$(ps -p "$existing_pid" -o comm= 2>/dev/null || true)
3619
+ if [[ "$proc_cmd" == *python* ]] || [[ "$proc_cmd" == *uvicorn* ]]; then
3620
+ log_step "Killing existing dashboard on port $DASHBOARD_PORT (PID: $existing_pid)..."
3621
+ kill "$existing_pid" 2>/dev/null || true
3622
+ sleep 1
3623
+ break
3624
+ else
3625
+ log_info "Port $DASHBOARD_PORT in use by non-dashboard process ($proc_cmd), skipping..."
3626
+ fi
3570
3627
  fi
3571
3628
  ((DASHBOARD_PORT++))
3572
3629
  if [ "$DASHBOARD_PORT" -gt 65535 ]; then