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 +40 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +1 -0
- package/autonomy/loki +10 -51
- package/autonomy/run.sh +65 -8
- package/autonomy/sandbox.sh +296 -21
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +152 -49
- package/dashboard/static/index.html +109 -102
- package/docs/INSTALLATION.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/loki-mode)
|
|
7
7
|
[](https://github.com/asklokesh/loki-mode)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://github.com/marketplace/actions/loki-mode-code-review)
|
|
9
10
|
[](https://claude.ai)
|
|
10
11
|
[]()
|
|
11
12
|
[](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
|
-
|
|
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.
|
|
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.
|
|
263
|
+
**v5.29.0 | Demo, quick mode, init, cost dashboard, 12 templates, GitHub Action | ~270 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.29.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
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
3857
|
-
|
|
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 = '
|
|
3862
|
-
query = '
|
|
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
|
-
#
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
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
|