loki-mode 7.7.27 → 7.7.28
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/completion-council.sh +10 -1
- package/autonomy/hooks/migration-hooks.sh +4 -1
- package/autonomy/run.sh +14 -2
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/memory/retrieval.py +49 -0
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-to-product system. Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product via the RARV-C closure loop, with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v7.7.
|
|
6
|
+
# Loki Mode v7.7.28
|
|
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.28 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.7.
|
|
1
|
+
7.7.28
|
|
@@ -1199,7 +1199,16 @@ council_evaluate_member() {
|
|
|
1199
1199
|
for test_log in "$loki_dir"/logs/test-*.log "$loki_dir"/logs/*test*.log; do
|
|
1200
1200
|
if [ -f "$test_log" ]; then
|
|
1201
1201
|
local fail_count
|
|
1202
|
-
|
|
1202
|
+
# Detect REAL test failures, not any line containing "error".
|
|
1203
|
+
# The old blanket grep "(FAIL|ERROR|failed|error:)" counted
|
|
1204
|
+
# benign lines ("0 errors", "0 failed", a test named
|
|
1205
|
+
# test_error_handling, "no errors found"), which forced CONTINUE
|
|
1206
|
+
# forever on a fully-passing suite that merely mentions "error".
|
|
1207
|
+
# Match actual failure signals and exclude the zero-count forms.
|
|
1208
|
+
fail_count=$(grep -ciE \
|
|
1209
|
+
'([1-9][0-9]*[[:space:]]+(failed|errors?)|^FAILED|[[:space:]]FAILED[[:space:]]|tests? failed|assertionerror|traceback \(most recent)' \
|
|
1210
|
+
"$test_log" 2>/dev/null | tr -dc '0-9')
|
|
1211
|
+
fail_count=${fail_count:-0}
|
|
1203
1212
|
test_failures=$((test_failures + fail_count))
|
|
1204
1213
|
fi
|
|
1205
1214
|
done
|
|
@@ -362,7 +362,10 @@ import json, sys
|
|
|
362
362
|
from datetime import datetime
|
|
363
363
|
with open(sys.argv[1]) as f:
|
|
364
364
|
data = json.load(f)
|
|
365
|
-
|
|
365
|
+
# setdefault (not get): get('modes', []) appended to a THROWAWAY list when
|
|
366
|
+
# the key was missing, so the failure mode was silently lost on a fresh
|
|
367
|
+
# failure-modes.json. setdefault binds the list into data before appending.
|
|
368
|
+
data.setdefault('modes', []).append({
|
|
366
369
|
'mode_id': 'heal-fail-' + datetime.now().strftime('%Y%m%dT%H%M%S'),
|
|
367
370
|
'trigger': 'healing_modification',
|
|
368
371
|
'file': sys.argv[2],
|
package/autonomy/run.sh
CHANGED
|
@@ -8710,7 +8710,11 @@ try:
|
|
|
8710
8710
|
from memory.engine import MemoryEngine
|
|
8711
8711
|
from memory.schemas import EpisodeTrace
|
|
8712
8712
|
from datetime import datetime, timezone
|
|
8713
|
-
|
|
8713
|
+
# base_path= is required: MemoryEngine.__init__(self, storage=None, base_path=...)
|
|
8714
|
+
# takes `storage` first, so a bare positional path was assigned to
|
|
8715
|
+
# self.storage and engine.initialize() crashed on str.ensure_directory,
|
|
8716
|
+
# silently dropping every store_episode_trace into the except handler.
|
|
8717
|
+
engine = MemoryEngine(base_path=f'{target_dir}/.loki/memory')
|
|
8714
8718
|
engine.initialize()
|
|
8715
8719
|
trace = EpisodeTrace.create(
|
|
8716
8720
|
task_id=task_id,
|
|
@@ -9071,7 +9075,15 @@ try:
|
|
|
9071
9075
|
importance = float(getattr(trace, 'importance', 0.0) or 0.0)
|
|
9072
9076
|
except (TypeError, ValueError):
|
|
9073
9077
|
importance = 0.0
|
|
9074
|
-
|
|
9078
|
+
# Reconstruct the ACTUAL on-disk path. storage.save_episode writes to
|
|
9079
|
+
# episodic/<YYYY-MM-DD>/task-<id>.json (date from the trace timestamp),
|
|
9080
|
+
# NOT episodic/<id>.json. The old flat path never existed, so the
|
|
9081
|
+
# importance shadow-write guard in bash never fired.
|
|
9082
|
+
_ts = getattr(trace, 'timestamp', '') or ''
|
|
9083
|
+
_date_str = str(_ts)[:10] if _ts else __import__('datetime').datetime.now(
|
|
9084
|
+
__import__('datetime').timezone.utc).strftime('%Y-%m-%d')
|
|
9085
|
+
episode_file = (Path(f'{target_dir}/.loki/memory/episodic')
|
|
9086
|
+
/ _date_str / f'task-{trace.id}.json')
|
|
9075
9087
|
if path_out_file:
|
|
9076
9088
|
try:
|
|
9077
9089
|
with open(path_out_file, 'w', encoding='utf-8') as f:
|
package/dashboard/__init__.py
CHANGED
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 v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(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 Q=S1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function N1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=S1($);if(X===$)break;$=X}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as F7}from"fs";import{resolve as w7,dirname as x7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(o!==null)return o;let K="7.7.
|
|
2
|
+
var _7=Object.defineProperty;var I7=(K)=>K;function P7(K,$){this[K]=I7.bind(null,$)}var v=(K,$)=>{for(var Q in $)_7(K,Q,{get:$[Q],enumerable:!0,configurable:!0,set:P7.bind($,Q)})};var R=(K,$)=>()=>(K&&($=K(K=0)),$);var t=import.meta.require;var e1={};v(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 Q=S1(K);if(Q===K)break;K=Q}return u(i1,"..","..","..")}function N1(K){let $=K;for(let Q=0;Q<6;Q++){if(J1(u($,"VERSION"))&&J1(u($,"autonomy/run.sh")))return $;let X=S1($);if(X===$)break;$=X}return u(K,"..","..","..")}function P(){return process.env.LOKI_DIR??u(process.cwd(),".loki")}function k1(){return u(R7(),".loki")}var i1,p;var g=R(()=>{i1=S1(L7(import.meta.url));p=E7()});import{readFileSync as F7}from"fs";import{resolve as w7,dirname as x7}from"path";import{fileURLToPath as S7}from"url";function G1(){if(o!==null)return o;let K="7.7.28";if(typeof K==="string"&&K.length>0)return o=K,o;try{let $=x7(S7(import.meta.url)),Q=N1($);o=F7(w7(Q,"VERSION"),"utf-8").trim()}catch{o="unknown"}return o}var o=null;var D1=R(()=>{g()});var $0={};v($0,{runOrThrow:()=>N7,run:()=>k,commandVersion:()=>D7,commandExists:()=>h,ShellError:()=>C1});async function k(K,$={}){let Q=Bun.spawn({cmd:[...K],stdout:"pipe",stderr:"pipe",env:$.env?{...process.env,...$.env}:process.env,cwd:$.cwd}),X,Z;if($.timeoutMs&&$.timeoutMs>0)X=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},$.timeoutMs);try{let[W,z,q]=await Promise.all([new Response(Q.stdout).text(),new Response(Q.stderr).text(),Q.exited]);return{stdout:W,stderr:z,exitCode:q}}finally{if(X)clearTimeout(X);if(Z)clearTimeout(Z)}}async function N7(K,$={}){let Q=await k(K,$);if(Q.exitCode!==0)throw new C1(`command failed (${Q.exitCode}): ${K.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function h(K){let $=k7(K),Q=await k(["sh","-c",`command -v ${$}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.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 h(K))return null;let X=await k([K,$],{timeoutMs:5000});if(X.exitCode!==0)return null;return((X.stdout||X.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var C1;var n=R(()=>{C1=class C1 extends Error{message;exitCode;stdout;stderr;constructor(K,$,Q,X){super(K);this.message=K;this.exitCode=$;this.stdout=Q;this.stderr=X;this.name="ShellError"}}});function c(K){return C7?"":K}var C7,E,b,F,T6,O,D,w,H;var a=R(()=>{C7=(process.env.NO_COLOR??"").length>0;E=c("\x1B[0;31m"),b=c("\x1B[0;32m"),F=c("\x1B[1;33m"),T6=c("\x1B[0;34m"),O=c("\x1B[0;36m"),D=c("\x1B[1m"),w=c("\x1B[2m"),H=c("\x1B[0m")});import{existsSync as c7}from"fs";async function i(){if(X1!==void 0)return X1;let K="/opt/homebrew/bin/python3.12";if(c7(K))return X1=K,K;let $=await h("python3.12");if($)return X1=$,$;let Q=await h("python3");return X1=Q,Q}async function s(K,$={}){let Q=await i();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return k([Q,"-c",K],$)}var X1;var Z1=R(()=>{n()});var G0={};v(G0,{runStatus:()=>Q5});import{existsSync as N,readFileSync as W1,readdirSync as W0,statSync as H0}from"fs";import{resolve as x,basename as a7}from"path";async function r7(){if(await h("jq"))return!0;return process.stdout.write(`${E}Error: jq is required but not installed.${H}
|
|
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)
|
|
@@ -585,4 +585,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
585
585
|
`),2}default:return process.stderr.write(`Unknown command: ${$}
|
|
586
586
|
`),process.stderr.write(j7),2}}process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var X6=await Q6(Bun.argv.slice(2));process.exit(X6);
|
|
587
587
|
|
|
588
|
-
//# debugId=
|
|
588
|
+
//# debugId=FB1EB87ADC30E73E64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/memory/retrieval.py
CHANGED
|
@@ -1558,6 +1558,35 @@ class MemoryRetrieval:
|
|
|
1558
1558
|
if self._belongs_to_namespace(anti_copy):
|
|
1559
1559
|
results.append(anti_copy)
|
|
1560
1560
|
|
|
1561
|
+
# Consolidation writes anti-patterns as SemanticPattern objects with
|
|
1562
|
+
# category="anti-pattern" into semantic/patterns.json (not the legacy
|
|
1563
|
+
# anti-patterns.json above), with fields incorrect_approach /
|
|
1564
|
+
# description / correct_approach. Without this bridge, consolidated
|
|
1565
|
+
# anti-patterns were never retrievable. Map them onto the same
|
|
1566
|
+
# what_fails / why / prevention scoring shape.
|
|
1567
|
+
patterns_data = self.storage.read_json("semantic/patterns.json") or {}
|
|
1568
|
+
for pat in patterns_data.get("patterns", []):
|
|
1569
|
+
if pat.get("category") != "anti-pattern":
|
|
1570
|
+
continue
|
|
1571
|
+
what_fails = (pat.get("incorrect_approach", "")
|
|
1572
|
+
or pat.get("pattern", "")).lower()
|
|
1573
|
+
why = pat.get("description", "").lower()
|
|
1574
|
+
prevention = pat.get("correct_approach", "").lower()
|
|
1575
|
+
|
|
1576
|
+
score = sum(2 for kw in keywords if kw in what_fails)
|
|
1577
|
+
score += sum(1 for kw in keywords if kw in why)
|
|
1578
|
+
score += sum(1 for kw in keywords if kw in prevention)
|
|
1579
|
+
|
|
1580
|
+
if score > 0:
|
|
1581
|
+
anti_copy = dict(pat)
|
|
1582
|
+
anti_copy["_score"] = score
|
|
1583
|
+
anti_copy["_source"] = "anti_patterns"
|
|
1584
|
+
anti_copy.setdefault("what_fails", pat.get("incorrect_approach", "") or pat.get("pattern", ""))
|
|
1585
|
+
anti_copy.setdefault("why", pat.get("description", ""))
|
|
1586
|
+
anti_copy.setdefault("prevention", pat.get("correct_approach", ""))
|
|
1587
|
+
if self._belongs_to_namespace(anti_copy):
|
|
1588
|
+
results.append(anti_copy)
|
|
1589
|
+
|
|
1561
1590
|
return results
|
|
1562
1591
|
|
|
1563
1592
|
def _build_episodic_index(self) -> None:
|
|
@@ -1654,3 +1683,23 @@ class MemoryRetrieval:
|
|
|
1654
1683
|
# Add to index with ID
|
|
1655
1684
|
item_id = anti.get("id", anti.get("source", f"anti-{hash(text) % 10000}"))
|
|
1656
1685
|
index.add(item_id, embedding, anti)
|
|
1686
|
+
|
|
1687
|
+
# Parity with the keyword path: consolidation writes anti-patterns as
|
|
1688
|
+
# category="anti-pattern" entries in semantic/patterns.json, not the
|
|
1689
|
+
# legacy anti-patterns.json above. Bridge those into the vector index
|
|
1690
|
+
# too so embedding-based retrieval sees consolidated anti-patterns.
|
|
1691
|
+
patterns_data = self.storage.read_json("semantic/patterns.json") or {}
|
|
1692
|
+
for pat in patterns_data.get("patterns", []):
|
|
1693
|
+
if pat.get("category") != "anti-pattern":
|
|
1694
|
+
continue
|
|
1695
|
+
what_fails = pat.get("incorrect_approach", "") or pat.get("pattern", "")
|
|
1696
|
+
why = pat.get("description", "")
|
|
1697
|
+
prevention = pat.get("correct_approach", "")
|
|
1698
|
+
text = f"{what_fails} {why} {prevention}"
|
|
1699
|
+
embedding = self.embedding_engine.embed(text)
|
|
1700
|
+
item_id = pat.get("id", f"anti-{hash(text) % 10000}")
|
|
1701
|
+
bridged = dict(pat)
|
|
1702
|
+
bridged.setdefault("what_fails", what_fails)
|
|
1703
|
+
bridged.setdefault("why", why)
|
|
1704
|
+
bridged.setdefault("prevention", prevention)
|
|
1705
|
+
index.add(item_id, embedding, bridged)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
|
-
"version": "7.7.
|
|
3
|
+
"version": "7.7.28",
|
|
4
4
|
"description": "Loki Mode by Autonomi. Autonomous spec-to-product system: takes a PRD, GitHub issue, OpenAPI/JSON/YAML, or one-line brief to a deployed app via the RARV-C closure loop with 11 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|