loki-mode 7.7.0 → 7.7.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 +28 -4
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +40 -0
- package/dashboard/static/index.html +34 -0
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +20 -6
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.7.
|
|
6
|
+
# Loki Mode v7.7.2
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -381,4 +381,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
381
381
|
|
|
382
382
|
---
|
|
383
383
|
|
|
384
|
-
**v7.7.
|
|
384
|
+
**v7.7.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.7.
|
|
1
|
+
7.7.2
|
package/autonomy/loki
CHANGED
|
@@ -2285,7 +2285,8 @@ if not os.path.isdir(loki_dir):
|
|
|
2285
2285
|
result['status'] = 'inactive'
|
|
2286
2286
|
result['phase'] = None
|
|
2287
2287
|
result['iteration'] = 0
|
|
2288
|
-
result['provider'] = env_provider
|
|
2288
|
+
result['provider'] = env_provider if os.environ.get('LOKI_PROVIDER', '') else 'claude'
|
|
2289
|
+
result['provider_source'] = 'env' if os.environ.get('LOKI_PROVIDER', '') else 'default'
|
|
2289
2290
|
result['dashboard_url'] = None
|
|
2290
2291
|
result['pid'] = None
|
|
2291
2292
|
result['elapsed_time'] = 0
|
|
@@ -2343,13 +2344,27 @@ else:
|
|
|
2343
2344
|
result['phase'] = None
|
|
2344
2345
|
result['iteration'] = 0
|
|
2345
2346
|
|
|
2346
|
-
# Provider
|
|
2347
|
+
# Provider + provider_source (v7.7.2 B-5 clarity: surface WHY this
|
|
2348
|
+
# value won the resolution race: 'saved' vs 'env' vs 'default').
|
|
2347
2349
|
provider_file = os.path.join(loki_dir, 'state', 'provider')
|
|
2348
2350
|
if os.path.isfile(provider_file):
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
+
try:
|
|
2352
|
+
with open(provider_file) as f:
|
|
2353
|
+
saved = f.read().strip()
|
|
2354
|
+
except OSError:
|
|
2355
|
+
saved = ''
|
|
2351
2356
|
else:
|
|
2357
|
+
saved = ''
|
|
2358
|
+
env_set = os.environ.get('LOKI_PROVIDER', '') != ''
|
|
2359
|
+
if saved:
|
|
2360
|
+
result['provider'] = saved
|
|
2361
|
+
result['provider_source'] = 'saved'
|
|
2362
|
+
elif env_set:
|
|
2352
2363
|
result['provider'] = env_provider
|
|
2364
|
+
result['provider_source'] = 'env'
|
|
2365
|
+
else:
|
|
2366
|
+
result['provider'] = 'claude'
|
|
2367
|
+
result['provider_source'] = 'default'
|
|
2353
2368
|
|
|
2354
2369
|
# PID
|
|
2355
2370
|
pid_file = os.path.join(loki_dir, 'loki.pid')
|
|
@@ -2817,6 +2832,15 @@ cmd_provider() {
|
|
|
2817
2832
|
echo " info Show provider details"
|
|
2818
2833
|
echo " models Show resolved model configuration for all providers"
|
|
2819
2834
|
echo ""
|
|
2835
|
+
echo "Precedence (v7.7.2 -- highest wins):"
|
|
2836
|
+
echo " 1. loki start --provider NAME CLI flag (per-invocation)"
|
|
2837
|
+
echo " 2. .loki/state/provider saved value (per-project)"
|
|
2838
|
+
echo " 3. LOKI_PROVIDER env var shell session default"
|
|
2839
|
+
echo " 4. claude built-in default"
|
|
2840
|
+
echo ""
|
|
2841
|
+
echo " Note: 'loki status' reflects the SAVED provider, not the env var."
|
|
2842
|
+
echo " Run 'loki provider set <name>' to make a project-level choice persist."
|
|
2843
|
+
echo ""
|
|
2820
2844
|
echo "Examples:"
|
|
2821
2845
|
echo " loki provider show"
|
|
2822
2846
|
echo " loki provider set claude"
|
package/dashboard/__init__.py
CHANGED
package/dashboard/server.py
CHANGED
|
@@ -5207,6 +5207,46 @@ async def get_checklist():
|
|
|
5207
5207
|
return {"status": "error", "categories": [], "summary": {"total": 0, "verified": 0, "failing": 0, "pending": 0}}
|
|
5208
5208
|
|
|
5209
5209
|
|
|
5210
|
+
@app.get("/api/usage")
|
|
5211
|
+
async def get_usage_doc():
|
|
5212
|
+
"""v7.7.1 F-1 follow-up: return the auto-generated USAGE.md from the
|
|
5213
|
+
project root so Dashboard + Lab can surface "how to run / test the app".
|
|
5214
|
+
|
|
5215
|
+
Returns {exists: bool, content: str|None, path: str, size: int, mtime: float}.
|
|
5216
|
+
Path-traversal hardened: resolves to PROJECT_ROOT/USAGE.md verbatim, no
|
|
5217
|
+
user-controlled path component. Reads up to 256 KiB; truncates with a
|
|
5218
|
+
`truncated: true` flag if larger.
|
|
5219
|
+
"""
|
|
5220
|
+
loki_dir = _get_loki_dir()
|
|
5221
|
+
project_root = loki_dir.parent
|
|
5222
|
+
usage_path = project_root / "USAGE.md"
|
|
5223
|
+
out = {
|
|
5224
|
+
"exists": False,
|
|
5225
|
+
"content": None,
|
|
5226
|
+
"path": str(usage_path),
|
|
5227
|
+
"size": 0,
|
|
5228
|
+
"mtime": 0.0,
|
|
5229
|
+
"truncated": False,
|
|
5230
|
+
}
|
|
5231
|
+
if not usage_path.is_file():
|
|
5232
|
+
return out
|
|
5233
|
+
try:
|
|
5234
|
+
st = usage_path.stat()
|
|
5235
|
+
out["exists"] = True
|
|
5236
|
+
out["size"] = st.st_size
|
|
5237
|
+
out["mtime"] = st.st_mtime
|
|
5238
|
+
MAX_BYTES = 256 * 1024 # 256 KiB cap
|
|
5239
|
+
if st.st_size > MAX_BYTES:
|
|
5240
|
+
out["truncated"] = True
|
|
5241
|
+
with usage_path.open("r", encoding="utf-8", errors="replace") as f:
|
|
5242
|
+
out["content"] = f.read(MAX_BYTES) + "\n\n... [truncated]"
|
|
5243
|
+
else:
|
|
5244
|
+
out["content"] = usage_path.read_text(encoding="utf-8", errors="replace")
|
|
5245
|
+
except OSError as e:
|
|
5246
|
+
out["error"] = str(e)
|
|
5247
|
+
return out
|
|
5248
|
+
|
|
5249
|
+
|
|
5210
5250
|
@app.get("/api/checklist/summary")
|
|
5211
5251
|
async def get_checklist_summary():
|
|
5212
5252
|
"""Get checklist verification summary."""
|
|
@@ -702,6 +702,40 @@
|
|
|
702
702
|
<h3 style="font-family: 'DM Serif Display', Georgia, serif; font-size: 1.15rem; font-weight: 400; color: var(--loki-text-primary); margin-bottom: 12px;">Learning Metrics</h3>
|
|
703
703
|
<loki-learning-dashboard id="learning-dashboard" time-range="7d"></loki-learning-dashboard>
|
|
704
704
|
</div>
|
|
705
|
+
<!-- v7.7.1 F-1 follow-up: How to Run (USAGE.md) -->
|
|
706
|
+
<div>
|
|
707
|
+
<h3 style="font-family: 'DM Serif Display', Georgia, serif; font-size: 1.15rem; font-weight: 400; color: var(--loki-text-primary); margin-bottom: 12px;">How to Run (USAGE.md)</h3>
|
|
708
|
+
<div id="usage-doc-panel" style="background: var(--loki-bg-card, #1a1a1a); border: 1px solid var(--loki-border, #333); border-radius: 5px; padding: 12px;">
|
|
709
|
+
<div id="usage-doc-meta" style="font-size: 11px; color: var(--loki-text-muted, #888); margin-bottom: 8px;">Loading...</div>
|
|
710
|
+
<pre id="usage-doc-content" style="margin: 0; padding: 12px; background: var(--loki-bg-secondary, #111); border-radius: 4px; font-family: 'JetBrains Mono', monospace; font-size: 12px; white-space: pre-wrap; word-break: break-word; color: var(--loki-text-primary, #eee); max-height: 480px; overflow: auto;"></pre>
|
|
711
|
+
</div>
|
|
712
|
+
<script>
|
|
713
|
+
(function(){
|
|
714
|
+
function loadUsage(){
|
|
715
|
+
fetch('/api/usage').then(function(r){ return r.json(); }).then(function(j){
|
|
716
|
+
var meta = document.getElementById('usage-doc-meta');
|
|
717
|
+
var body = document.getElementById('usage-doc-content');
|
|
718
|
+
if (!meta || !body) return;
|
|
719
|
+
if (!j || !j.exists){
|
|
720
|
+
meta.textContent = 'USAGE.md not generated yet. Loki writes it at the end of each session.';
|
|
721
|
+
body.textContent = '';
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
var size = j.size > 1024 ? (j.size/1024).toFixed(1) + ' KB' : j.size + ' B';
|
|
725
|
+
var when = j.mtime ? new Date(j.mtime*1000).toLocaleString() : '';
|
|
726
|
+
meta.textContent = j.path + ' (' + size + (j.truncated ? ', truncated' : '') + ', ' + when + ')';
|
|
727
|
+
body.textContent = j.content || '';
|
|
728
|
+
}).catch(function(e){
|
|
729
|
+
var meta = document.getElementById('usage-doc-meta');
|
|
730
|
+
if (meta) meta.textContent = 'Failed to load USAGE.md: ' + (e && e.message ? e.message : 'unknown');
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
loadUsage();
|
|
734
|
+
// Re-poll on interval so a fresh USAGE.md appears after a session.
|
|
735
|
+
setInterval(loadUsage, 15000);
|
|
736
|
+
})();
|
|
737
|
+
</script>
|
|
738
|
+
</div>
|
|
705
739
|
</div>
|
|
706
740
|
</div>
|
|
707
741
|
|
package/docs/INSTALLATION.md
CHANGED
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var b=(K,$)=>{for(var z in $)_7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:P7.bind($,z)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var V1=import.meta.require;var e1={};b(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let z=S1(K);if(z===K)break;K=z}return u(i1,"..","..","..")}function N1(K){let $=K;for(let z=0;z<6;z++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let Q=S1($);if(Q===$)break;$=Q}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var y=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as x7}from"fs";import{resolve as F7,dirname as w7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(n!==null)return n;let K="7.7.
|
|
2
|
+
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var b=(K,$)=>{for(var z in $)_7(K,z,{get:$[z],enumerable:!0,configurable:!0,set:P7.bind($,z)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var V1=import.meta.require;var e1={};b(e1,{lokiDir:()=>P,homeLokiDir:()=>k1,findRepoRootForVersion:()=>N1,REPO_ROOT:()=>p});import{resolve as u,dirname as S1}from"path";import{fileURLToPath as L7}from"url";import{existsSync as J1}from"fs";import{homedir as R7}from"os";function E7(){let K=i1;for(let $=0;$<6;$++){if(J1(u(K,"VERSION"))&&J1(u(K,"autonomy/run.sh")))return K;let z=S1(K);if(z===K)break;K=z}return u(i1,"..","..","..")}function N1(K){let $=K;for(let z=0;z<6;z++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let Q=S1($);if(Q===$)break;$=Q}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var y=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as x7}from"fs";import{resolve as F7,dirname as w7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(n!==null)return n;let K="7.7.2";if(typeof K==="string"&&K.length>0)return n=K,n;try{let $=w7(S7(import.meta.url)),z=N1($);n=x7(F7(z,"VERSION"),"utf-8").trim()}catch{n="unknown"}return n}var n=null;var D1=R(()=>{y()});var $0={};b($0,{runOrThrow:()=>N7,run:()=>S,commandVersion:()=>D7,commandExists:()=>D,ShellError:()=>C1});async function S(K,$={}){let z=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),Q,X;if($.timeoutMs&&$.timeoutMs>0)Q=setTimeout(()=>{try{z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{z.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[H,Z,q]=await Promise.all([new Response(z.stdout).text(),new Response(z.stderr).text(),z.exited]);return{stdout:H,stderr:Z,exitCode:q}}finally{if(Q)clearTimeout(Q);if(X)clearTimeout(X)}}async function N7(K,$={}){let z=await S(K,$);if(z.exitCode!==0)throw new C1(`command failed (${z.exitCode}): ${K.join(" ")}`,z.exitCode,z.stdout,z.stderr);return z}async function D(K){let $=k7(K),z=await S(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(z.exitCode===0)return z.stdout.trim()||null;return null}function k7(K){if(!/^[A-Za-z0-9._/-]+$/.test(K))throw Error(`refused to shell-escape suspect token: ${K}`);return K}async function D7(K,$="--version"){if(!await D(K))return null;let Q=await S([K,$],{timeoutMs:5000});if(Q.exitCode!==0)return null;return((Q.stdout||Q.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var c=R(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,z,Q){super(K);this.message=K;this.exitCode=$;this.stdout=z;this.stderr=Q;this.name="ShellError"}}});function l(K){return C7?"":K}var C7,E,C,x,O6,O,k,F,W;var a=R(()=>{C7=(process.env.NO_COLOR??"").length>0;E=l("\x1B[0;31m"),C=l("\x1B[0;32m"),x=l("\x1B[1;33m"),O6=l("\x1B[0;34m"),O=l("\x1B[0;36m"),k=l("\x1B[1m"),F=l("\x1B[2m"),W=l("\x1B[0m")});import{existsSync as c7}from"fs";async function t(){if(z1!==void 0)return z1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return z1=K,K;let $=await D("python3.12");if($)return z1=$,$;let z=await D("python3");return z1=z,z}async function s(K,$={}){let z=await t();if(!z)return{stdout:"",stderr:"python3 not found",exitCode:127};return S([z,"-c",K],$)}var z1;var Q1=R(()=>{c()});var G0={};b(G0,{runStatus:()=>z5});import{existsSync as N,readFileSync as Z1,readdirSync as H0,statSync as W0}from"fs";import{resolve as w,basename as a7}from"path";async function r7(){if(await D("jq"))return!0;return process.stdout.write(`${E}Error: jq is required but not installed.${W}
|
|
3
3
|
`),process.stdout.write(`Install with:
|
|
4
4
|
`),process.stdout.write(` brew install jq (macOS)
|
|
5
5
|
`),process.stdout.write(` apt install jq (Debian/Ubuntu)
|
|
@@ -76,7 +76,8 @@ if not os.path.isdir(loki_dir):
|
|
|
76
76
|
result['status'] = 'inactive'
|
|
77
77
|
result['phase'] = None
|
|
78
78
|
result['iteration'] = 0
|
|
79
|
-
result['provider'] = env_provider
|
|
79
|
+
result['provider'] = env_provider if os.environ.get('LOKI_PROVIDER', '') else 'claude'
|
|
80
|
+
result['provider_source'] = 'env' if os.environ.get('LOKI_PROVIDER', '') else 'default'
|
|
80
81
|
result['dashboard_url'] = None
|
|
81
82
|
result['pid'] = None
|
|
82
83
|
result['elapsed_time'] = 0
|
|
@@ -134,13 +135,26 @@ else:
|
|
|
134
135
|
result['phase'] = None
|
|
135
136
|
result['iteration'] = 0
|
|
136
137
|
|
|
137
|
-
# Provider
|
|
138
|
+
# Provider + provider_source (v7.7.2 B-5 clarity, parity with bash)
|
|
138
139
|
provider_file = os.path.join(loki_dir, 'state', 'provider')
|
|
139
140
|
if os.path.isfile(provider_file):
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
try:
|
|
142
|
+
with open(provider_file) as f:
|
|
143
|
+
saved = f.read().strip()
|
|
144
|
+
except OSError:
|
|
145
|
+
saved = ''
|
|
142
146
|
else:
|
|
147
|
+
saved = ''
|
|
148
|
+
env_set = os.environ.get('LOKI_PROVIDER', '') != ''
|
|
149
|
+
if saved:
|
|
150
|
+
result['provider'] = saved
|
|
151
|
+
result['provider_source'] = 'saved'
|
|
152
|
+
elif env_set:
|
|
143
153
|
result['provider'] = env_provider
|
|
154
|
+
result['provider_source'] = 'env'
|
|
155
|
+
else:
|
|
156
|
+
result['provider'] = 'claude'
|
|
157
|
+
result['provider_source'] = 'default'
|
|
144
158
|
|
|
145
159
|
# PID
|
|
146
160
|
pid_file = os.path.join(loki_dir, 'loki.pid')
|
|
@@ -536,4 +550,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
536
550
|
`),2}default:return process.stderr.write(`Unknown command: ${$}
|
|
537
551
|
`),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var z6=await $6(Bun.argv.slice(2));process.exit(z6);
|
|
538
552
|
|
|
539
|
-
//# debugId=
|
|
553
|
+
//# debugId=031A27C701A5C40664756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.2",
|
|
4
4
|
"description": "Loki Mode by Autonomi. Multi-agent autonomous SDLC framework. Spec to deployed app: PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief. 4 AI providers (Claude Code, OpenAI Codex, Cline, Aider). 11 quality gates.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|