loki-mode 6.26.0 → 6.26.2
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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +136 -0
- package/autonomy/run.sh +0 -3
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +43 -52
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +3 -2
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 minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v6.26.
|
|
6
|
+
# Loki Mode v6.26.2
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
267
267
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
268
268
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
269
269
|
|
|
270
|
-
**v6.26.
|
|
270
|
+
**v6.26.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.26.
|
|
1
|
+
6.26.2
|
package/autonomy/loki
CHANGED
|
@@ -404,6 +404,7 @@ show_help() {
|
|
|
404
404
|
echo " stats [flags] Session statistics (--json, --efficiency)"
|
|
405
405
|
echo " logs Show recent log output"
|
|
406
406
|
echo " dashboard [cmd] Dashboard server (start|stop|status|url|open)"
|
|
407
|
+
echo " web [cmd] Web app UI (start|stop|status) -- serves web-app/dist/"
|
|
407
408
|
echo " provider [cmd] Manage AI provider (show|set|list|info)"
|
|
408
409
|
echo " serve Start dashboard/API server (alias for api start)"
|
|
409
410
|
echo " api [cmd] Dashboard/API server (start|stop|status)"
|
|
@@ -2905,6 +2906,138 @@ cmd_dashboard_open() {
|
|
|
2905
2906
|
fi
|
|
2906
2907
|
}
|
|
2907
2908
|
|
|
2909
|
+
# Web app management
|
|
2910
|
+
# The web app is served directly by the dashboard FastAPI server (port 57374).
|
|
2911
|
+
# No separate web server process is needed -- the dashboard mounts web-app/dist/
|
|
2912
|
+
# as static files and handles SPA catch-all routing.
|
|
2913
|
+
|
|
2914
|
+
cmd_web() {
|
|
2915
|
+
local subcommand="${1:-start}"
|
|
2916
|
+
|
|
2917
|
+
case "$subcommand" in
|
|
2918
|
+
start)
|
|
2919
|
+
shift || true
|
|
2920
|
+
cmd_web_start "$@"
|
|
2921
|
+
;;
|
|
2922
|
+
stop)
|
|
2923
|
+
cmd_web_stop
|
|
2924
|
+
;;
|
|
2925
|
+
status)
|
|
2926
|
+
cmd_web_status
|
|
2927
|
+
;;
|
|
2928
|
+
--help|-h|help)
|
|
2929
|
+
cmd_web_help
|
|
2930
|
+
;;
|
|
2931
|
+
*)
|
|
2932
|
+
# Treat unknown args as options to start (e.g., loki web --no-open)
|
|
2933
|
+
cmd_web_start "$subcommand" "$@"
|
|
2934
|
+
;;
|
|
2935
|
+
esac
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
cmd_web_help() {
|
|
2939
|
+
echo -e "${BOLD}Loki Mode Web App${NC}"
|
|
2940
|
+
echo ""
|
|
2941
|
+
echo "Usage: loki web [command] [options]"
|
|
2942
|
+
echo ""
|
|
2943
|
+
echo "Commands:"
|
|
2944
|
+
echo " start Start the web app (default)"
|
|
2945
|
+
echo " stop Stop the dashboard server (alias for 'loki dashboard stop')"
|
|
2946
|
+
echo " status Show dashboard server status"
|
|
2947
|
+
echo " help Show this help"
|
|
2948
|
+
echo ""
|
|
2949
|
+
echo "Options (for start):"
|
|
2950
|
+
echo " --no-open Don't open browser automatically"
|
|
2951
|
+
echo ""
|
|
2952
|
+
echo "The web app is served by the dashboard API at localhost:${DASHBOARD_DEFAULT_PORT}."
|
|
2953
|
+
echo "Both API endpoints and static files are served from the same origin."
|
|
2954
|
+
echo ""
|
|
2955
|
+
echo "Examples:"
|
|
2956
|
+
echo " loki web Start dashboard and open web app in browser"
|
|
2957
|
+
echo " loki web --no-open Start dashboard without opening browser"
|
|
2958
|
+
echo " loki web stop Stop the dashboard server"
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
cmd_web_start() {
|
|
2962
|
+
local open_browser=true
|
|
2963
|
+
|
|
2964
|
+
# Parse arguments
|
|
2965
|
+
while [[ $# -gt 0 ]]; do
|
|
2966
|
+
case "$1" in
|
|
2967
|
+
--no-open)
|
|
2968
|
+
open_browser=false
|
|
2969
|
+
shift
|
|
2970
|
+
;;
|
|
2971
|
+
--help|-h)
|
|
2972
|
+
cmd_web_help
|
|
2973
|
+
exit 0
|
|
2974
|
+
;;
|
|
2975
|
+
*)
|
|
2976
|
+
echo -e "${RED}Unknown option: $1${NC}"
|
|
2977
|
+
echo "Run 'loki web --help' for usage."
|
|
2978
|
+
exit 1
|
|
2979
|
+
;;
|
|
2980
|
+
esac
|
|
2981
|
+
done
|
|
2982
|
+
|
|
2983
|
+
# Check that web-app dist exists
|
|
2984
|
+
local web_dist="${SKILL_DIR}/web-app/dist"
|
|
2985
|
+
if [ ! -d "$web_dist" ] || [ ! -f "$web_dist/index.html" ]; then
|
|
2986
|
+
echo -e "${RED}Error: Web app not built${NC}"
|
|
2987
|
+
echo "Expected files at: $web_dist"
|
|
2988
|
+
echo ""
|
|
2989
|
+
echo "Build the web app first:"
|
|
2990
|
+
echo " cd web-app && npm install && npm run build"
|
|
2991
|
+
exit 1
|
|
2992
|
+
fi
|
|
2993
|
+
|
|
2994
|
+
# Ensure dashboard server is running (it serves both API and web app)
|
|
2995
|
+
local api_running=false
|
|
2996
|
+
if [ -f "$DASHBOARD_PID_FILE" ]; then
|
|
2997
|
+
local api_pid
|
|
2998
|
+
api_pid=$(cat "$DASHBOARD_PID_FILE" 2>/dev/null)
|
|
2999
|
+
if [ -n "$api_pid" ] && kill -0 "$api_pid" 2>/dev/null; then
|
|
3000
|
+
api_running=true
|
|
3001
|
+
fi
|
|
3002
|
+
fi
|
|
3003
|
+
|
|
3004
|
+
if [ "$api_running" = false ]; then
|
|
3005
|
+
echo -e "${CYAN}Starting dashboard server...${NC}"
|
|
3006
|
+
cmd_dashboard_start
|
|
3007
|
+
echo ""
|
|
3008
|
+
fi
|
|
3009
|
+
|
|
3010
|
+
local url="http://127.0.0.1:${DASHBOARD_DEFAULT_PORT}"
|
|
3011
|
+
|
|
3012
|
+
echo -e "${GREEN}Web app available at: $url${NC}"
|
|
3013
|
+
echo ""
|
|
3014
|
+
echo -e "Stop with: ${CYAN}loki web stop${NC} or ${CYAN}loki dashboard stop${NC}"
|
|
3015
|
+
|
|
3016
|
+
# Open browser
|
|
3017
|
+
if [ "$open_browser" = true ]; then
|
|
3018
|
+
echo ""
|
|
3019
|
+
if command -v open &> /dev/null; then
|
|
3020
|
+
open "$url"
|
|
3021
|
+
elif command -v xdg-open &> /dev/null; then
|
|
3022
|
+
xdg-open "$url"
|
|
3023
|
+
elif command -v start &> /dev/null; then
|
|
3024
|
+
start "$url"
|
|
3025
|
+
else
|
|
3026
|
+
echo "Please open in browser: $url"
|
|
3027
|
+
fi
|
|
3028
|
+
fi
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
cmd_web_stop() {
|
|
3032
|
+
# Web app is served by the dashboard -- stopping dashboard stops the web app
|
|
3033
|
+
cmd_dashboard_stop
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
cmd_web_status() {
|
|
3037
|
+
# Web app is served by the dashboard -- show dashboard status
|
|
3038
|
+
cmd_dashboard_status
|
|
3039
|
+
}
|
|
3040
|
+
|
|
2908
3041
|
# Import GitHub issues
|
|
2909
3042
|
cmd_import() {
|
|
2910
3043
|
if [ ! -d "$LOKI_DIR" ]; then
|
|
@@ -9072,6 +9205,9 @@ main() {
|
|
|
9072
9205
|
dashboard)
|
|
9073
9206
|
cmd_dashboard "$@"
|
|
9074
9207
|
;;
|
|
9208
|
+
web)
|
|
9209
|
+
cmd_web "$@"
|
|
9210
|
+
;;
|
|
9075
9211
|
logs)
|
|
9076
9212
|
cmd_logs "$@"
|
|
9077
9213
|
;;
|
package/autonomy/run.sh
CHANGED
|
@@ -9347,7 +9347,6 @@ cleanup() {
|
|
|
9347
9347
|
if type app_runner_cleanup &>/dev/null; then
|
|
9348
9348
|
app_runner_cleanup
|
|
9349
9349
|
fi
|
|
9350
|
-
stop_dashboard
|
|
9351
9350
|
stop_status_monitor
|
|
9352
9351
|
kill_all_registered
|
|
9353
9352
|
rm -f "$loki_dir/loki.pid" 2>/dev/null
|
|
@@ -9379,7 +9378,6 @@ except (json.JSONDecodeError, OSError): pass
|
|
|
9379
9378
|
if type app_runner_cleanup &>/dev/null; then
|
|
9380
9379
|
app_runner_cleanup
|
|
9381
9380
|
fi
|
|
9382
|
-
stop_dashboard
|
|
9383
9381
|
stop_status_monitor
|
|
9384
9382
|
kill_all_registered
|
|
9385
9383
|
rm -f "$loki_dir/loki.pid" "$loki_dir/PAUSE" 2>/dev/null
|
|
@@ -9974,7 +9972,6 @@ main() {
|
|
|
9974
9972
|
if type app_runner_cleanup &>/dev/null; then
|
|
9975
9973
|
app_runner_cleanup
|
|
9976
9974
|
fi
|
|
9977
|
-
stop_dashboard
|
|
9978
9975
|
stop_status_monitor
|
|
9979
9976
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
9980
9977
|
rm -f "$loki_dir/loki.pid" 2>/dev/null
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -286,43 +286,6 @@ manager = ConnectionManager()
|
|
|
286
286
|
start_time = datetime.now(timezone.utc)
|
|
287
287
|
|
|
288
288
|
|
|
289
|
-
async def _orphan_watchdog():
|
|
290
|
-
"""Background task that shuts down dashboard if the parent session dies.
|
|
291
|
-
|
|
292
|
-
When the session process is killed (SIGKILL), the cleanup trap never runs
|
|
293
|
-
and the dashboard is left orphaned. This watchdog checks the session PID
|
|
294
|
-
every 30 seconds and initiates shutdown if the session is gone.
|
|
295
|
-
"""
|
|
296
|
-
loki_dir = _get_loki_dir()
|
|
297
|
-
pid_file = loki_dir / "loki.pid"
|
|
298
|
-
# Wait 60s before first check to let session fully start
|
|
299
|
-
await asyncio.sleep(60)
|
|
300
|
-
while True:
|
|
301
|
-
try:
|
|
302
|
-
if pid_file.exists():
|
|
303
|
-
pid = int(pid_file.read_text().strip())
|
|
304
|
-
try:
|
|
305
|
-
os.kill(pid, 0) # Check if process exists
|
|
306
|
-
except OSError:
|
|
307
|
-
# Session PID is dead -- we're orphaned
|
|
308
|
-
logger.warning(
|
|
309
|
-
"Session PID %d is gone. Dashboard shutting down to avoid orphan.", pid
|
|
310
|
-
)
|
|
311
|
-
# Clean up our own PID file
|
|
312
|
-
dash_pid = loki_dir / "dashboard" / "dashboard.pid"
|
|
313
|
-
dash_pid.unlink(missing_ok=True)
|
|
314
|
-
# Give a moment for any in-flight requests
|
|
315
|
-
await asyncio.sleep(2)
|
|
316
|
-
os._exit(0)
|
|
317
|
-
# If PID file doesn't exist and we've been running >2 min, also shut down
|
|
318
|
-
elif time.time() - _dashboard_start_time > 120:
|
|
319
|
-
logger.warning("No session PID file found. Dashboard shutting down.")
|
|
320
|
-
os._exit(0)
|
|
321
|
-
except (ValueError, OSError):
|
|
322
|
-
pass
|
|
323
|
-
await asyncio.sleep(30)
|
|
324
|
-
|
|
325
|
-
|
|
326
289
|
_dashboard_start_time = time.time()
|
|
327
290
|
|
|
328
291
|
|
|
@@ -332,11 +295,8 @@ async def lifespan(app: FastAPI):
|
|
|
332
295
|
# Startup
|
|
333
296
|
await init_db()
|
|
334
297
|
_telemetry.send_telemetry("dashboard_start")
|
|
335
|
-
# Start orphan watchdog
|
|
336
|
-
watchdog_task = asyncio.create_task(_orphan_watchdog())
|
|
337
298
|
yield
|
|
338
299
|
# Shutdown
|
|
339
|
-
watchdog_task.cancel()
|
|
340
300
|
await close_db()
|
|
341
301
|
|
|
342
302
|
|
|
@@ -436,6 +396,26 @@ async def get_status() -> StatusResponse:
|
|
|
436
396
|
loki_dir = _get_loki_dir()
|
|
437
397
|
uptime = (datetime.now(timezone.utc) - start_time).total_seconds()
|
|
438
398
|
|
|
399
|
+
# Read VERSION file early (independent of .loki/ dir)
|
|
400
|
+
version = "unknown"
|
|
401
|
+
dashboard_dir = os.path.dirname(__file__)
|
|
402
|
+
project_root = os.path.dirname(dashboard_dir)
|
|
403
|
+
version_file = os.path.join(project_root, "VERSION")
|
|
404
|
+
if os.path.isfile(version_file):
|
|
405
|
+
try:
|
|
406
|
+
with open(version_file) as vf:
|
|
407
|
+
version = vf.read().strip()
|
|
408
|
+
except (OSError, IOError) as e:
|
|
409
|
+
logger.warning(f"Failed to read VERSION file: {e}")
|
|
410
|
+
|
|
411
|
+
# If .loki/ directory doesn't exist, return idle status immediately
|
|
412
|
+
if not loki_dir.is_dir():
|
|
413
|
+
return StatusResponse(
|
|
414
|
+
status="idle",
|
|
415
|
+
version=version,
|
|
416
|
+
uptime_seconds=uptime,
|
|
417
|
+
)
|
|
418
|
+
|
|
439
419
|
# Read dashboard-state.json (written by run.sh every 2 seconds)
|
|
440
420
|
state_file = loki_dir / "dashboard-state.json"
|
|
441
421
|
pid_file = loki_dir / "loki.pid"
|
|
@@ -450,18 +430,6 @@ async def get_status() -> StatusResponse:
|
|
|
450
430
|
current_task = ""
|
|
451
431
|
pending_tasks = 0
|
|
452
432
|
running_agents = 0
|
|
453
|
-
version = "unknown"
|
|
454
|
-
|
|
455
|
-
# Read VERSION file
|
|
456
|
-
dashboard_dir = os.path.dirname(__file__)
|
|
457
|
-
project_root = os.path.dirname(dashboard_dir)
|
|
458
|
-
version_file = os.path.join(project_root, "VERSION")
|
|
459
|
-
if os.path.isfile(version_file):
|
|
460
|
-
try:
|
|
461
|
-
with open(version_file) as vf:
|
|
462
|
-
version = vf.read().strip()
|
|
463
|
-
except (OSError, IOError) as e:
|
|
464
|
-
logger.warning(f"Failed to read VERSION file: {e}")
|
|
465
433
|
|
|
466
434
|
# Read dashboard state
|
|
467
435
|
if state_file.exists():
|
|
@@ -4370,6 +4338,7 @@ PROJECT_ROOT = os.path.dirname(DASHBOARD_DIR)
|
|
|
4370
4338
|
# Possible static file locations (in order of preference)
|
|
4371
4339
|
# Resolves correctly regardless of PYTHONPATH, symlinks, or install method
|
|
4372
4340
|
STATIC_LOCATIONS = [
|
|
4341
|
+
os.path.join(PROJECT_ROOT, "web-app", "dist"), # web-app/dist/ (web app SPA)
|
|
4373
4342
|
os.path.join(DASHBOARD_DIR, "static"), # dashboard/static/ (production)
|
|
4374
4343
|
os.path.join(PROJECT_ROOT, "dashboard-ui", "dist"), # dashboard-ui/dist/ (development)
|
|
4375
4344
|
]
|
|
@@ -4377,12 +4346,14 @@ STATIC_LOCATIONS = [
|
|
|
4377
4346
|
# Add LOKI_SKILL_DIR env var fallback (set by loki CLI and run.sh)
|
|
4378
4347
|
_skill_dir = os.environ.get("LOKI_SKILL_DIR", "")
|
|
4379
4348
|
if _skill_dir:
|
|
4349
|
+
STATIC_LOCATIONS.append(os.path.join(_skill_dir, "web-app", "dist"))
|
|
4380
4350
|
STATIC_LOCATIONS.append(os.path.join(_skill_dir, "dashboard", "static"))
|
|
4381
4351
|
STATIC_LOCATIONS.append(os.path.join(_skill_dir, "dashboard-ui", "dist"))
|
|
4382
4352
|
|
|
4383
4353
|
# Add ~/.claude/skills/loki-mode fallback (installed skill location)
|
|
4384
4354
|
_home_skill = os.path.join(os.path.expanduser("~"), ".claude", "skills", "loki-mode")
|
|
4385
4355
|
if os.path.isdir(_home_skill):
|
|
4356
|
+
STATIC_LOCATIONS.append(os.path.join(_home_skill, "web-app", "dist"))
|
|
4386
4357
|
STATIC_LOCATIONS.append(os.path.join(_home_skill, "dashboard", "static"))
|
|
4387
4358
|
STATIC_LOCATIONS.append(os.path.join(_home_skill, "dashboard-ui", "dist"))
|
|
4388
4359
|
|
|
@@ -4813,6 +4784,26 @@ def start_migration_phase(migration_id: str, request_body: dict):
|
|
|
4813
4784
|
raise HTTPException(status_code=500, detail="Failed to start phase")
|
|
4814
4785
|
|
|
4815
4786
|
|
|
4787
|
+
# ---------------------------------------------------------------------------
|
|
4788
|
+
# SPA catch-all: serve index.html for any path not matched by API routes
|
|
4789
|
+
# or static asset mounts. This lets the web-app handle client-side routing.
|
|
4790
|
+
# Must be registered LAST so it never shadows an API endpoint.
|
|
4791
|
+
# ---------------------------------------------------------------------------
|
|
4792
|
+
@app.get("/{full_path:path}", include_in_schema=False)
|
|
4793
|
+
async def serve_spa_catchall(full_path: str):
|
|
4794
|
+
"""Serve static files or fall back to index.html for SPA routing."""
|
|
4795
|
+
if STATIC_DIR:
|
|
4796
|
+
# Try to serve the exact file first (e.g. /vite.svg, /manifest.json)
|
|
4797
|
+
candidate = os.path.join(STATIC_DIR, full_path)
|
|
4798
|
+
if os.path.isfile(candidate):
|
|
4799
|
+
return FileResponse(candidate)
|
|
4800
|
+
# Fall back to index.html for client-side routes
|
|
4801
|
+
index_path = os.path.join(STATIC_DIR, "index.html")
|
|
4802
|
+
if os.path.isfile(index_path):
|
|
4803
|
+
return FileResponse(index_path, media_type="text/html")
|
|
4804
|
+
return Response(status_code=404)
|
|
4805
|
+
|
|
4806
|
+
|
|
4816
4807
|
def run_server(host: str = None, port: int = None) -> None:
|
|
4817
4808
|
"""Run the dashboard server."""
|
|
4818
4809
|
import uvicorn
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "6.26.
|
|
3
|
+
"version": "6.26.2",
|
|
4
4
|
"description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -81,7 +81,8 @@
|
|
|
81
81
|
"dashboard/requirements.txt",
|
|
82
82
|
"mcp/",
|
|
83
83
|
"completions/",
|
|
84
|
-
"src/"
|
|
84
|
+
"src/",
|
|
85
|
+
"web-app/dist/"
|
|
85
86
|
],
|
|
86
87
|
"scripts": {
|
|
87
88
|
"postinstall": "node bin/postinstall.js",
|