loki-mode 7.7.4 → 7.7.6
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/run.sh +35 -6
- 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 +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.6
|
|
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.6 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.7.
|
|
1
|
+
7.7.6
|
package/autonomy/run.sh
CHANGED
|
@@ -3575,10 +3575,17 @@ except: print('{\"total\":0,\"unacknowledged\":0}')
|
|
|
3575
3575
|
" 2>/dev/null || echo '{"total":0,"unacknowledged":0}')
|
|
3576
3576
|
fi
|
|
3577
3577
|
|
|
3578
|
-
# Write comprehensive JSON state (atomic via temp file + mv)
|
|
3578
|
+
# Write comprehensive JSON state (atomic via temp file + mv).
|
|
3579
|
+
# v7.7.5 fix: previously used `${output_file}.tmp` (no PID suffix). When two
|
|
3580
|
+
# background processes both called write_dashboard_state concurrently, they
|
|
3581
|
+
# raced on the same .tmp filename -- one would clobber the other's content,
|
|
3582
|
+
# the loser's `mv` would fail with "No such file or directory" because the
|
|
3583
|
+
# winner already moved the shared .tmp away. This flooded the agent output
|
|
3584
|
+
# with `mv: rename .loki/dashboard-state.json.tmp ...` errors and made
|
|
3585
|
+
# Loki sessions appear broken. Now each process gets a unique tmp suffix.
|
|
3579
3586
|
local project_name=$(basename "$(pwd)")
|
|
3580
3587
|
local project_path=$(pwd)
|
|
3581
|
-
local _tmp_state="${output_file}.tmp"
|
|
3588
|
+
local _tmp_state="${output_file}.tmp.$$.$RANDOM"
|
|
3582
3589
|
|
|
3583
3590
|
# BUG #49 fix: Escape project path/name for JSON to handle special chars
|
|
3584
3591
|
# (spaces, quotes, backslashes in directory names)
|
|
@@ -3643,7 +3650,12 @@ except: print('null')
|
|
|
3643
3650
|
"notifications": $notification_summary
|
|
3644
3651
|
}
|
|
3645
3652
|
EOF
|
|
3646
|
-
mv
|
|
3653
|
+
# v7.7.5 fix: silence mv stderr so any residual race (different process
|
|
3654
|
+
# already swapped a newer file in) doesn't flood the agent output. The
|
|
3655
|
+
# PID-suffixed tmp eliminates the race; the 2>/dev/null is belt-and-
|
|
3656
|
+
# suspenders. `|| rm -f "$_tmp_state" 2>/dev/null` cleans up the tmp
|
|
3657
|
+
# on the rare failure rather than leaking it.
|
|
3658
|
+
mv "$_tmp_state" "$output_file" 2>/dev/null || rm -f "$_tmp_state" 2>/dev/null
|
|
3647
3659
|
}
|
|
3648
3660
|
|
|
3649
3661
|
#===============================================================================
|
|
@@ -11727,12 +11739,16 @@ LOKI_PROVIDER_ACTIVE=0
|
|
|
11727
11739
|
kill_provider_child() {
|
|
11728
11740
|
local killed=0
|
|
11729
11741
|
local protected_pids=""
|
|
11730
|
-
#
|
|
11731
|
-
#
|
|
11732
|
-
#
|
|
11742
|
+
# v7.7.5 follow-up: previously this only read `*.pid` files, but the
|
|
11743
|
+
# canonical registry (`register_pid` in run.sh:873) writes `*.json` files
|
|
11744
|
+
# named `<PID>.json`. The dashboard PID was registered as JSON and thus
|
|
11745
|
+
# not protected; provider kill cascade caught it. Now reads BOTH:
|
|
11746
|
+
# *.pid files (legacy + .loki/dashboard/dashboard.pid) AND *.json files
|
|
11747
|
+
# (the canonical pid registry, where the JSON filename IS the PID).
|
|
11733
11748
|
local pid_root="${TARGET_DIR:-.}/.loki/pids"
|
|
11734
11749
|
if [ -d "$pid_root" ]; then
|
|
11735
11750
|
local pid_file pid
|
|
11751
|
+
# Legacy / external `.pid` files: content is the PID
|
|
11736
11752
|
for pid_file in "$pid_root"/*.pid; do
|
|
11737
11753
|
[ -f "$pid_file" ] || continue
|
|
11738
11754
|
pid=$(cat "$pid_file" 2>/dev/null | head -1 | tr -d '[:space:]')
|
|
@@ -11740,6 +11756,19 @@ kill_provider_child() {
|
|
|
11740
11756
|
protected_pids="${protected_pids} ${pid}"
|
|
11741
11757
|
fi
|
|
11742
11758
|
done
|
|
11759
|
+
# Canonical `register_pid` registry: filename `<PID>.json` IS the PID.
|
|
11760
|
+
for pid_file in "$pid_root"/*.json; do
|
|
11761
|
+
[ -f "$pid_file" ] || continue
|
|
11762
|
+
pid=$(basename "$pid_file" .json)
|
|
11763
|
+
# Verify numeric + alive before adding (basename may be non-numeric
|
|
11764
|
+
# if some other consumer wrote a non-PID JSON file here).
|
|
11765
|
+
case "$pid" in
|
|
11766
|
+
''|*[!0-9]*) continue ;;
|
|
11767
|
+
esac
|
|
11768
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
11769
|
+
protected_pids="${protected_pids} ${pid}"
|
|
11770
|
+
fi
|
|
11771
|
+
done
|
|
11743
11772
|
fi
|
|
11744
11773
|
# Also protect the dashboard PID file at .loki/dashboard/dashboard.pid (older path).
|
|
11745
11774
|
local dash_pid_file="${TARGET_DIR:-.}/.loki/dashboard/dashboard.pid"
|
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 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.6";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)
|
|
@@ -550,4 +550,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
550
550
|
`),2}default:return process.stderr.write(`Unknown command: ${$}
|
|
551
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);
|
|
552
552
|
|
|
553
|
-
//# debugId=
|
|
553
|
+
//# debugId=496D49A157EA8A9064756E2164756E21
|
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.6",
|
|
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",
|