loki-mode 9.38.0 → 9.39.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +24 -0
- package/claude/hooks/loki-session-end.sh +89 -0
- 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/package.json +2 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v9.
|
|
6
|
+
# Loki Mode v9.39.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
470
470
|
|
|
471
471
|
---
|
|
472
472
|
|
|
473
|
-
**v9.
|
|
473
|
+
**v9.39.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.
|
|
1
|
+
9.39.0
|
package/autonomy/loki
CHANGED
|
@@ -24582,6 +24582,20 @@ except Exception as e:
|
|
|
24582
24582
|
fi
|
|
24583
24583
|
local _settings="$HOME/.claude/settings.json"
|
|
24584
24584
|
local _hook_script="${SKILL_DIR:-$(pwd)}/claude/hooks/loki-session-end.sh"
|
|
24585
|
+
# FAIL CLOSED on a missing script. This path was built unguarded and
|
|
24586
|
+
# written into the user's REAL ~/.claude/settings.json, then reported
|
|
24587
|
+
# green "installed" -- while claude/ was absent from package.json
|
|
24588
|
+
# files[], so for every npm user the hook pointed at a file that did
|
|
24589
|
+
# not exist. The install claimed success and transcripts were never
|
|
24590
|
+
# ingested; the SessionEnd failure surfaces nowhere the user reads.
|
|
24591
|
+
# Writing a settings entry we cannot honor is worse than not writing
|
|
24592
|
+
# one, so refuse and say why instead of mutating their config.
|
|
24593
|
+
if [ ! -f "$_hook_script" ]; then
|
|
24594
|
+
echo -e "${RED}Cannot install the SessionEnd hook: script not found${NC}" >&2
|
|
24595
|
+
echo -e "${YELLOW} expected: $_hook_script${NC}" >&2
|
|
24596
|
+
echo -e "${YELLOW} ~/.claude/settings.json was NOT modified.${NC}" >&2
|
|
24597
|
+
return 1
|
|
24598
|
+
fi
|
|
24585
24599
|
mkdir -p "$(dirname "$_settings")" 2>/dev/null || true
|
|
24586
24600
|
[ ! -f "$_settings" ] && echo '{}' > "$_settings"
|
|
24587
24601
|
_LOKI_HOOK_SCRIPT="$_hook_script" _LOKI_SETTINGS="$_settings" python3 -c "
|
|
@@ -29440,6 +29454,14 @@ USER TASK: ${prompt}"
|
|
|
29440
29454
|
aider)
|
|
29441
29455
|
aider --message "$full_prompt" --yes-always --no-auto-commits < /dev/null 2>&1 || agent_exit=$?
|
|
29442
29456
|
;;
|
|
29457
|
+
opencode)
|
|
29458
|
+
# Mirrors the working arm in the phase dispatcher (see the
|
|
29459
|
+
# opencode case alongside aider there). opencode was listed
|
|
29460
|
+
# as an active provider and accepted by `loki provider set`,
|
|
29461
|
+
# but this case had no arm, so `loki agent run` printed the
|
|
29462
|
+
# persona banner and then died with "Unknown provider".
|
|
29463
|
+
(source "$_LOKI_SCRIPT_DIR/../providers/opencode.sh" && provider_invoke "$full_prompt" 2>&1) || agent_exit=$?
|
|
29464
|
+
;;
|
|
29443
29465
|
*)
|
|
29444
29466
|
echo -e "${RED}Unknown provider: $provider${NC}"
|
|
29445
29467
|
return 1
|
|
@@ -29565,6 +29587,8 @@ $diff"
|
|
|
29565
29587
|
claude) claude -p "$review_prompt" 2>&1 ;;
|
|
29566
29588
|
codex) codex exec --sandbox workspace-write "$review_prompt" 2>&1 ;;
|
|
29567
29589
|
cline) cline -y "$review_prompt" 2>&1 ;;
|
|
29590
|
+
aider) aider --message "$review_prompt" --yes-always --no-auto-commits < /dev/null 2>&1 ;;
|
|
29591
|
+
opencode) (source "$_LOKI_SCRIPT_DIR/../providers/opencode.sh" && provider_invoke "$review_prompt" 2>&1) ;;
|
|
29568
29592
|
*) echo -e "${RED}Unknown provider: $provider${NC}"; return 1 ;;
|
|
29569
29593
|
esac
|
|
29570
29594
|
;;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# v7.7.18 sample Claude Code SessionEnd hook (MANUAL install only).
|
|
3
|
+
#
|
|
4
|
+
# Pipes the Claude Code session transcript through `loki memory ingest`
|
|
5
|
+
# so the project's .loki/memory/ store accumulates real episodes from
|
|
6
|
+
# every Claude Code session (not just `loki start <prd>` sessions).
|
|
7
|
+
#
|
|
8
|
+
# v7.7.18 council fix (Opus 2): supports BOTH (a) the documented JSON-
|
|
9
|
+
# on-stdin payload format with `transcript_path` key, AND (b) the legacy
|
|
10
|
+
# $CLAUDE_TRANSCRIPT_PATH env var as fallback. Whichever Claude Code
|
|
11
|
+
# version is running, one of them works.
|
|
12
|
+
#
|
|
13
|
+
# Manual installation (recommended -- automated installer deferred to
|
|
14
|
+
# v7.7.19 pending empirical schema verification):
|
|
15
|
+
# 1. Add to your ~/.claude/settings.json:
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": {
|
|
18
|
+
# "SessionEnd": [
|
|
19
|
+
# {
|
|
20
|
+
# "matcher": "clear",
|
|
21
|
+
# "hooks": [
|
|
22
|
+
# {
|
|
23
|
+
# "type": "command",
|
|
24
|
+
# "command": "/absolute/path/to/loki-session-end.sh"
|
|
25
|
+
# }
|
|
26
|
+
# ]
|
|
27
|
+
# }
|
|
28
|
+
# ]
|
|
29
|
+
# }
|
|
30
|
+
# }
|
|
31
|
+
# 2. Reload Claude Code (or open a new session).
|
|
32
|
+
# 3. End a session with `/clear` to trigger the hook.
|
|
33
|
+
#
|
|
34
|
+
# Note: SessionEnd only fires on /clear, NOT on normal exits per the
|
|
35
|
+
# Claude Code documentation. To capture EVERY session, alternative
|
|
36
|
+
# event types may be needed (researched in v7.7.19+).
|
|
37
|
+
#
|
|
38
|
+
# Escape hatches:
|
|
39
|
+
# LOKI_MEMORY_CAPTURE_DISABLED=true # blocks ingest at the engine
|
|
40
|
+
#
|
|
41
|
+
# Privacy: the ingester scrubs credential keywords + high-entropy
|
|
42
|
+
# token shapes (sk-, ghp_/ghs_, xox*, AIza, AKIA) before writing.
|
|
43
|
+
# Path-aware scrubbing for sensitive directories added in v7.7.18.
|
|
44
|
+
set -u
|
|
45
|
+
|
|
46
|
+
if [ "${LOKI_MEMORY_CAPTURE_DISABLED:-}" = "true" ]; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Resolve transcript path from EITHER stdin JSON OR env var.
|
|
51
|
+
TRANSCRIPT=""
|
|
52
|
+
|
|
53
|
+
# 1. Try stdin JSON (documented Claude Code hook payload format).
|
|
54
|
+
# Only read stdin if it's not a TTY (i.e. there's actual data).
|
|
55
|
+
if [ ! -t 0 ]; then
|
|
56
|
+
# Read up to 16KB of stdin (JSON payloads are tiny)
|
|
57
|
+
STDIN_DATA=$(head -c 16384 2>/dev/null || true)
|
|
58
|
+
if [ -n "$STDIN_DATA" ] && command -v python3 >/dev/null 2>&1; then
|
|
59
|
+
TRANSCRIPT=$(printf '%s' "$STDIN_DATA" | python3 -c "
|
|
60
|
+
import json, sys
|
|
61
|
+
try:
|
|
62
|
+
d = json.loads(sys.stdin.read())
|
|
63
|
+
print(d.get('transcript_path', ''), end='')
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
66
|
+
" 2>/dev/null || true)
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# 2. Fallback to env var (legacy / undocumented variant).
|
|
71
|
+
if [ -z "$TRANSCRIPT" ]; then
|
|
72
|
+
TRANSCRIPT="${CLAUDE_TRANSCRIPT_PATH:-}"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
|
|
76
|
+
# Nothing to ingest -- silent exit so we never block SessionEnd.
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Find the loki binary; silent skip if not installed.
|
|
81
|
+
if ! command -v loki >/dev/null 2>&1; then
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Fire-and-forget ingest. Backgrounded so SessionEnd is not blocked.
|
|
86
|
+
loki memory ingest --from-claude-transcript "$TRANSCRIPT" >/dev/null 2>&1 &
|
|
87
|
+
disown 2>/dev/null || true
|
|
88
|
+
|
|
89
|
+
exit 0
|
package/dashboard/__init__.py
CHANGED
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v9.
|
|
5
|
+
**Version:** v9.39.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var _t=Object.create;var{getPrototypeOf:vt,defineProperty:jG,getOwnPropertyNames:ht}=Object;var gt=Object.prototype.hasOwnProperty;function mt($){return this[$]}var ut,dt,pt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?ut??=new WeakMap:dt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?_t(vt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ht($))if(!gt.call(J,q))jG(J,q,{get:mt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var ct=($)=>$;function lt($,X){this[$]=ct.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:lt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var UR={};B1(UR,{lokiDir:()=>h0,homeLokiDir:()=>WQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as it}from"url";import{existsSync as Zq}from"fs";import{homedir as at}from"os";function ot(){let $=YR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(YR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function WQ(){return h2(at(),".loki")}var YR,L1;var k1=s(()=>{YR=LG(it(import.meta.url));L1=ot()});import{readFileSync as st}from"fs";import{resolve as nt,dirname as rt}from"path";import{fileURLToPath as tt}from"url";function j9(){if(n3!==null)return n3;let $="9.
|
|
2
|
+
var _t=Object.create;var{getPrototypeOf:vt,defineProperty:jG,getOwnPropertyNames:ht}=Object;var gt=Object.prototype.hasOwnProperty;function mt($){return this[$]}var ut,dt,pt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?ut??=new WeakMap:dt??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?_t(vt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of ht($))if(!gt.call(J,q))jG(J,q,{get:mt.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var ct=($)=>$;function lt($,X){this[$]=ct.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:lt.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var UR={};B1(UR,{lokiDir:()=>h0,homeLokiDir:()=>WQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as it}from"url";import{existsSync as Zq}from"fs";import{homedir as at}from"os";function ot(){let $=YR;for(let X=0;X<6;X++){if(Zq(h2($,"VERSION"))&&Zq(h2($,"autonomy/run.sh")))return $;let Q=LG($);if(Q===$)break;$=Q}return h2(YR,"..","..","..")}function AG($){let X=$;for(let Q=0;Q<6;Q++){if(Zq(h2(X,"VERSION"))&&Zq(h2(X,"autonomy/run.sh")))return X;let z=LG(X);if(z===X)break;X=z}return h2($,"..","..","..")}function h0(){return process.env.LOKI_DIR??h2(process.cwd(),".loki")}function WQ(){return h2(at(),".loki")}var YR,L1;var k1=s(()=>{YR=LG(it(import.meta.url));L1=ot()});import{readFileSync as st}from"fs";import{resolve as nt,dirname as rt}from"path";import{fileURLToPath as tt}from"url";function j9(){if(n3!==null)return n3;let $="9.39.0";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=rt(tt(import.meta.url)),Q=AG(X);n3=st(nt(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var OR={};B1(OR,{runOrThrow:()=>Be,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>Me,commandExists:()=>g5,ShellError:()=>TG,MAX_STDOUT_BYTES:()=>MR});async function Jq($,X=MR){let Q=$.getReader(),z=new TextDecoder,Z="",K=0;try{while(K<X){let{done:J,value:q}=await Q.read();if(J)break;if(!q)continue;if(K+=q.byteLength,K>X){let V=q.byteLength-(K-X);Z+=z.decode(q.subarray(0,V),{stream:!0});break}Z+=z.decode(q,{stream:!0})}Z+=z.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return Z}async function $1($,X={}){let Q=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),z,Z;if(X.timeoutMs&&X.timeoutMs>0)z=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}Z=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[K,J,q]=await Promise.all([Jq(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:K,stderr:J,exitCode:q}}finally{if(z)clearTimeout(z);if(Z)clearTimeout(Z)}}async function Be($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new TG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=Ne($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Ne($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function Me($,X="--version"){if(!await g5($))return null;let z=await $1([$,X],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var MR=16777216,TG;var y8=s(()=>{TG=class TG extends Error{message;exitCode;stdout;stderr;constructor($,X,Q,z){super($);this.message=$;this.exitCode=X;this.stdout=Q;this.stderr=z;this.name="ShellError"}}});function g2($){return Oe?"":$}var Oe,p0,$5,q1,D61,A1,f1,m5,r;var t7=s(()=>{Oe=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),D61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as we}from"fs";async function Z2(){if(GQ!==void 0)return GQ;let $="/opt/homebrew/bin/python3.12";if(we($))return GQ=$,$;let X=await g5("python3.12");if(X)return GQ=X,X;let Q=await g5("python3");return GQ=Q,Q}async function I4($,X={}){let Q=await Z2();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return $1([Q,"-c",$],X)}var GQ;var m2=s(()=>{y8()});var vR={};B1(vR,{runStatus:()=>te});import{existsSync as u5,readFileSync as C9,readdirSync as ER,statSync as xR}from"fs";import{resolve as A5,basename as pe}from"path";import{homedir as ce}from"os";function kR($){let X=Math.trunc($);if(X>=1e6)return`${(Math.trunc(X/1e6*10)/10).toFixed(1)}M`;if(X>=1000)return`${(Math.trunc(X/1000*10)/10).toFixed(1)}K`;return String(X)}function SR($,X,Q){if(X===0)return null;let z=Math.trunc($*100/X),Z=Math.trunc($*Vq/X);if(Z>Vq)Z=Vq;let K=Vq-Z,J=$5;if(z>=80)J=p0;else if(z>=50)J=q1;let q="=".repeat(Math.max(0,Z))+" ".repeat(Math.max(0,K)),V=kR($),Y=kR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function ie(){if(await g5("jq"))return!0;return process.stdout.write(`${p0}Error: jq is required but not installed.${r}
|
|
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)
|
|
@@ -1337,4 +1337,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
1337
1337
|
`),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (yt(),St));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1338
1338
|
`),process.stderr.write(bt),2}}wR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var K61=await Z61(Bun.argv.slice(2));process.exit(K61);
|
|
1339
1339
|
|
|
1340
|
-
//# debugId=
|
|
1340
|
+
//# debugId=F2D05F5DF5DCE17BC281BF37D76CAB57
|
package/mcp/__init__.py
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loki-mode",
|
|
3
3
|
"mcpName": "io.github.asklokesh/loki-mode",
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.39.0",
|
|
5
5
|
"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 8 quality gates. Provider-agnostic (Claude Code, OpenAI Codex, Cline, Aider, opencode).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"tools/",
|
|
70
70
|
"plugins/",
|
|
71
71
|
".claude-plugin/marketplace.json",
|
|
72
|
+
"claude/hooks/",
|
|
72
73
|
"autonomy/",
|
|
73
74
|
"providers/",
|
|
74
75
|
"vendor/autonomi-verify/",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "loki-mode",
|
|
4
4
|
"displayName": "Loki Mode",
|
|
5
|
-
"version": "9.
|
|
5
|
+
"version": "9.39.0",
|
|
6
6
|
"description": "Autonomous spec-to-product build system with a built-in trust layer (RARV-C closure loop, 8 quality gates, completion council). Ships Loki's spec-hardening, drift-detection, and deterministic PR verification commands plus the Loki MCP server.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Autonomi",
|