loki-mode 6.26.1 → 6.26.3
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 +16 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/run.sh +0 -3
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +25 -53
- package/dashboard/static/index.html +3 -3
- package/docs/INSTALLATION.md +1 -1
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[]()
|
|
10
10
|
[](https://www.autonomi.dev/)
|
|
11
|
+
[](https://hub.docker.com/r/asklokesh/loki-mode)
|
|
11
12
|
|
|
12
|
-
**Current Version: v6.
|
|
13
|
+
**Current Version: v6.26.2**
|
|
14
|
+
|
|
15
|
+
### Traction
|
|
16
|
+
|
|
17
|
+
**737 stars** | **150 forks** | **10,200+ Docker pulls** | **18,000+ npm downloads** | **571+ commits** | **12 releases in a single day (March 18, 2026)**
|
|
13
18
|
|
|
14
19
|
---
|
|
15
20
|
|
|
@@ -133,6 +138,9 @@ See [full architecture documentation](docs/enterprise/architecture.md) for the d
|
|
|
133
138
|
| **Enterprise** | TLS, OIDC/SSO, RBAC, OTEL, policy engine, audit trails | [Enterprise Guide](docs/enterprise/architecture.md) |
|
|
134
139
|
| **Integrations** | Jira, Slack, Teams, GitHub Actions (Linear: partial) | [Integration Cookbook](docs/enterprise/integration-cookbook.md) |
|
|
135
140
|
| **Deployment** | Helm, Docker Compose, Terraform configs (AWS/Azure/GCP) | [Deployment Guide](deploy/helm/README.md) |
|
|
141
|
+
| **Web App** | Replit-like UI with 10 React components, PRD input, agent dashboard, file browser, memory viewer | [Dashboard Guide](docs/dashboard-guide.md) |
|
|
142
|
+
| **Cost Estimation** | Pre-execution analysis with complexity scoring, token/cost projection | [Memory System](references/memory-system.md) |
|
|
143
|
+
| **Auto-Failover** | Cross-provider failover (Claude -> Codex -> Gemini) when rate limited | [Provider Guide](skills/providers.md) |
|
|
136
144
|
| **SDKs** | Python (`loki-mode-sdk`), TypeScript (`loki-mode-sdk`) | [SDK Guide](docs/enterprise/sdk-guide.md) |
|
|
137
145
|
|
|
138
146
|
### Multi-Provider Support
|
|
@@ -162,6 +170,13 @@ Claude gets full features (subagents, parallelization, MCP, Task tool). All othe
|
|
|
162
170
|
| `loki import` | Import GitHub issues as tasks |
|
|
163
171
|
| `loki memory <cmd>` | Memory system CLI (index, timeline, search, consolidate) |
|
|
164
172
|
| `loki enterprise` | Enterprise feature management (tokens, OIDC) |
|
|
173
|
+
| `loki plan [PRD]` | Pre-execution analysis: complexity scoring, cost estimation, iteration prediction |
|
|
174
|
+
| `loki review [--staged\|--diff]` | AI-powered code review with 4 quality gates, severity filtering, CI output |
|
|
175
|
+
| `loki onboard [path]` | Instant project analysis and CLAUDE.md generation (12+ config types, 3 depth levels) |
|
|
176
|
+
| `loki ci` | CI/CD quality gate integration (GitHub Actions, GitLab CI, Jenkins, CircleCI) |
|
|
177
|
+
| `loki test [--file\|--dir\|--changed]` | AI-powered test generation (8 languages, 9 frameworks) |
|
|
178
|
+
| `loki failover [status\|--enable\|--chain]` | Cross-provider auto-failover when primary hits rate limits |
|
|
179
|
+
| `loki web` | Launch the web app (Replit-like UI for visual PRD-to-code workflow) |
|
|
165
180
|
| `loki version` | Show version |
|
|
166
181
|
|
|
167
182
|
Run `loki --help` for all commands. Full reference: [CLI Reference](wiki/CLI-Reference.md) | Configuration: [config.example.yaml](autonomy/config.example.yaml)
|
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.3
|
|
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.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.26.
|
|
1
|
+
6.26.3
|
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():
|
|
@@ -4825,8 +4793,12 @@ def start_migration_phase(migration_id: str, request_body: dict):
|
|
|
4825
4793
|
async def serve_spa_catchall(full_path: str):
|
|
4826
4794
|
"""Serve static files or fall back to index.html for SPA routing."""
|
|
4827
4795
|
if STATIC_DIR:
|
|
4796
|
+
static_root = os.path.realpath(STATIC_DIR)
|
|
4828
4797
|
# Try to serve the exact file first (e.g. /vite.svg, /manifest.json)
|
|
4829
|
-
|
|
4798
|
+
# Use realpath to prevent path traversal attacks
|
|
4799
|
+
candidate = os.path.realpath(os.path.join(STATIC_DIR, full_path))
|
|
4800
|
+
if not candidate.startswith(static_root + os.sep) and candidate != static_root:
|
|
4801
|
+
return Response(status_code=404)
|
|
4830
4802
|
if os.path.isfile(candidate):
|
|
4831
4803
|
return FileResponse(candidate)
|
|
4832
4804
|
# Fall back to index.html for client-side routes
|
|
@@ -5297,8 +5297,8 @@ var LokiDashboard=(()=>{var pt=Object.defineProperty;var Pt=Object.getOwnPropert
|
|
|
5297
5297
|
</div>
|
|
5298
5298
|
`}).join(""):'<div class="item" style="color:var(--loki-text-muted)">No items</div>'}_renderEmpty(){return`
|
|
5299
5299
|
<div class="empty-state">
|
|
5300
|
-
<p>
|
|
5301
|
-
<p class="hint">The PRD checklist
|
|
5300
|
+
<p><strong>No checklist data yet.</strong></p>
|
|
5301
|
+
<p class="hint">The PRD checklist is generated during the first iteration. Start a session with <code>loki start ./prd.md</code> -- groups and items will appear here as the session progresses and can be expanded for details.</p>
|
|
5302
5302
|
</div>
|
|
5303
5303
|
`}_attachEventListeners(){let t=this.shadowRoot;t&&(t.querySelectorAll(".category-header[data-category]").forEach(e=>{e.addEventListener("click",()=>this._toggleCategory(e.dataset.category))}),t.querySelectorAll("button[data-waive-id]").forEach(e=>{e.addEventListener("click",i=>{i.stopPropagation(),this._waiveItem(e.dataset.waiveId)})}),t.querySelectorAll("button[data-unwaive-id]").forEach(e=>{e.addEventListener("click",i=>{i.stopPropagation(),this._unwaiveItem(e.dataset.unwaiveId)})}))}_escapeHtml(t){return t?String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""):""}};customElements.define("loki-checklist-viewer",K);var St={not_initialized:{color:"var(--loki-text-muted, #71717a)",label:"Not Started",pulse:!1},starting:{color:"var(--loki-yellow, #ca8a04)",label:"Starting...",pulse:!0},running:{color:"var(--loki-green, #16a34a)",label:"Running",pulse:!0},stale:{color:"var(--loki-yellow, #ca8a04)",label:"Stale",pulse:!1},completed:{color:"var(--loki-text-muted, #a1a1aa)",label:"Completed",pulse:!1},failed:{color:"var(--loki-red, #dc2626)",label:"Failed",pulse:!1},crashed:{color:"var(--loki-red, #dc2626)",label:"Crashed",pulse:!1},stopped:{color:"var(--loki-text-muted, #a1a1aa)",label:"Stopped",pulse:!1},unknown:{color:"var(--loki-text-muted, #71717a)",label:"Unknown",pulse:!1}},V=class extends h{static get observedAttributes(){return["api-url","theme"]}constructor(){super(),this._loading=!1,this._error=null,this._api=null,this._pollInterval=null,this._status=null,this._logs=[],this._lastDataHash=null,this._lastLogsHash=null}connectedCallback(){super.connectedCallback(),this._setupApi(),this._loadData(),this._startPolling()}disconnectedCallback(){super.disconnectedCallback(),this._stopPolling()}attributeChangedCallback(t,e,i){e!==i&&(t==="api-url"&&this._api&&(this._api.baseUrl=i,this._loadData()),t==="theme"&&this._applyTheme())}_setupApi(){let t=this.getAttribute("api-url")||window.location.origin;this._api=g({baseUrl:t})}_startPolling(){this._pollInterval=setInterval(()=>this._loadData(),3e3),this._visibilityHandler=()=>{document.hidden?this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null):this._pollInterval||(this._loadData(),this._pollInterval=setInterval(()=>this._loadData(),3e3))},document.addEventListener("visibilitychange",this._visibilityHandler)}_stopPolling(){this._pollInterval&&(clearInterval(this._pollInterval),this._pollInterval=null),this._visibilityHandler&&(document.removeEventListener("visibilitychange",this._visibilityHandler),this._visibilityHandler=null)}async _loadData(){try{let[t,e]=await Promise.all([this._api.getAppRunnerStatus(),this._api.getAppRunnerLogs()]),i=JSON.stringify({status:t?.status,port:t?.port,restarts:t?.restart_count,url:t?.url}),a=JSON.stringify(e?.lines?.slice(-5)||[]),s=a!==this._lastLogsHash;if(i===this._lastDataHash&&!s)return;this._lastDataHash=i,this._lastLogsHash=a,this._status=t,this._logs=e?.lines||[],this._error=null,this.render(),this._scrollLogsToBottom()}catch(t){this._error||(this._error=`Failed to load app status: ${t.message}`,this.render())}}_scrollLogsToBottom(){let t=this.shadowRoot;if(!t)return;let e=t.querySelector(".log-area");e&&(e.scrollTop=e.scrollHeight)}async _handleRestart(){try{await this._api.restartApp(),this._loadData()}catch(t){this._error=`Restart failed: ${t.message}`,this.render()}}async _handleStop(){try{await this._api.stopApp(),this._loadData()}catch(t){this._error=`Stop failed: ${t.message}`,this.render()}}_formatUptime(t){if(!t)return"--";let e=new Date(t),a=Math.floor((new Date-e)/1e3);if(a<60)return`${a}s`;if(a<3600)return`${Math.floor(a/60)}m ${a%60}s`;let s=Math.floor(a/3600),r=Math.floor(a%3600/60);return`${s}h ${r}m`}_isValidUrl(t){if(!t)return!1;try{let e=new URL(t);return e.protocol==="http:"||e.protocol==="https:"}catch{return!1}}_getStyles(){return`
|
|
5304
5304
|
.app-status {
|
|
@@ -8991,7 +8991,7 @@ var LokiDashboard=(()=>{var pt=Object.defineProperty;var Pt=Object.getOwnPropert
|
|
|
8991
8991
|
color: var(--loki-text-muted, #939084);
|
|
8992
8992
|
font-size: 13px;
|
|
8993
8993
|
}
|
|
8994
|
-
`}render(){let t=this.shadowRoot;if(!t)return;let e=this._gates,i=ee(e),a;this._loading&&e.length===0?a='<div class="loading">Loading quality gates...</div>':e.length===0?a='<div class="empty-state">Quality gates
|
|
8994
|
+
`}render(){let t=this.shadowRoot;if(!t)return;let e=this._gates,i=ee(e),a;this._loading&&e.length===0?a='<div class="loading">Loading quality gates...</div>':e.length===0?a='<div class="empty-state"><strong>No gate results yet.</strong> Quality gates run automatically between RARV iterations during an active session. Start a session with <code>loki start ./prd.md</code> to see results here. You can also run gates manually with <code>loki review</code>.</div>':a=`<div class="gates-grid">${e.map(o=>{let n=(o.status||"pending").toLowerCase(),l=Lt[n]||Lt.pending;return`
|
|
8995
8995
|
<div class="gate-card status-${n}">
|
|
8996
8996
|
<div class="gate-header">
|
|
8997
8997
|
<span class="gate-name">${this._escapeHtml(o.name||"Unnamed Gate")}</span>
|
package/docs/INSTALLATION.md
CHANGED
package/mcp/__init__.py
CHANGED