loki-mode 7.65.0 → 7.66.1
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 +38 -11
- package/autonomy/run.sh +17 -0
- package/dashboard/__init__.py +1 -1
- package/docs/INSTALLATION.md +2 -2
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- package/memory/storage.py +10 -4
- package/package.json +1 -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 v7.
|
|
6
|
+
# Loki Mode v7.66.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -406,4 +406,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
406
406
|
|
|
407
407
|
---
|
|
408
408
|
|
|
409
|
-
**v7.
|
|
409
|
+
**v7.66.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.66.1
|
|
@@ -75,6 +75,12 @@ COUNCIL_CONSECUTIVE_NO_CHANGE=0
|
|
|
75
75
|
COUNCIL_DONE_SIGNALS=0
|
|
76
76
|
COUNCIL_TOTAL_DONE_SIGNALS=0
|
|
77
77
|
COUNCIL_LAST_DIFF_HASH=""
|
|
78
|
+
# bash-F1: distinguishes a genuine council approval from a force-stop safety
|
|
79
|
+
# valve (stagnation / done-signal flood). council_should_stop sets this to 1
|
|
80
|
+
# ONLY on the force-stop paths; run.sh reads it (same sourced shell) to avoid
|
|
81
|
+
# reporting a non-approved force-stop as a verified-complete product. Guarded
|
|
82
|
+
# default so set -u never trips before the function runs.
|
|
83
|
+
: "${COUNCIL_FORCE_STOPPED:=0}"
|
|
78
84
|
|
|
79
85
|
#===============================================================================
|
|
80
86
|
# v6.83.0 Phase 1: Managed Agents memory augmentation (opt-in).
|
|
@@ -2051,27 +2057,32 @@ ISSUES: CRITICAL:description (optional, one per line per issue)"
|
|
|
2051
2057
|
# no VOTE and default a real APPROVE to REJECT. Capture the full
|
|
2052
2058
|
# output; the downstream parse already greps VOTE/REASON/ISSUES.
|
|
2053
2059
|
# CAVEMAN_DEFAULT_MODE=off suppression is preserved (see above).
|
|
2054
|
-
|
|
2060
|
+
# bash-F3: timeout-guard the provider subcall so a hung CLI can
|
|
2061
|
+
# not stall the whole council. Default 600s matches the Bun route
|
|
2062
|
+
# (council.ts LOKI_COUNCIL_TIMEOUT_MS=600000). A timeout yields
|
|
2063
|
+
# empty output, which the [ -z "$verdict" ] fallback below turns
|
|
2064
|
+
# into a conservative heuristic review.
|
|
2065
|
+
verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" env CAVEMAN_DEFAULT_MODE=off claude "${_cm_argv[@]}" -p 2>/dev/null)
|
|
2055
2066
|
fi
|
|
2056
2067
|
;;
|
|
2057
2068
|
codex)
|
|
2058
2069
|
if command -v codex &>/dev/null; then
|
|
2059
|
-
verdict=$(codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
|
|
2070
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
|
|
2060
2071
|
fi
|
|
2061
2072
|
;;
|
|
2062
2073
|
gemini)
|
|
2063
2074
|
if command -v gemini &>/dev/null; then
|
|
2064
|
-
verdict=$(echo "$prompt" | gemini 2>/dev/null)
|
|
2075
|
+
verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" gemini 2>/dev/null)
|
|
2065
2076
|
fi
|
|
2066
2077
|
;;
|
|
2067
2078
|
cline)
|
|
2068
2079
|
if command -v cline &>/dev/null; then
|
|
2069
|
-
verdict=$(cline -y "$prompt" 2>/dev/null)
|
|
2080
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" cline -y "$prompt" 2>/dev/null)
|
|
2070
2081
|
fi
|
|
2071
2082
|
;;
|
|
2072
2083
|
aider)
|
|
2073
2084
|
if command -v aider &>/dev/null; then
|
|
2074
|
-
verdict=$(aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
|
|
2085
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
|
|
2075
2086
|
fi
|
|
2076
2087
|
;;
|
|
2077
2088
|
esac
|
|
@@ -2152,27 +2163,30 @@ REASON: your reasoning"
|
|
|
2152
2163
|
# Inlined on `claude` only (does not cross the pipe). No-op absent.
|
|
2153
2164
|
# v7.41.3 BUG A: full capture, no tail-truncation (see member
|
|
2154
2165
|
# subcall note). CAVEMAN_DEFAULT_MODE=off suppression preserved.
|
|
2155
|
-
|
|
2166
|
+
# bash-F3: timeout-guard the devil's-advocate subcall (same 600s
|
|
2167
|
+
# default as the member subcalls / Bun council.ts). An empty
|
|
2168
|
+
# verdict on timeout hits the conservative REJECT fallback below.
|
|
2169
|
+
verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" env CAVEMAN_DEFAULT_MODE=off claude "${_co_argv[@]}" -p 2>/dev/null)
|
|
2156
2170
|
fi
|
|
2157
2171
|
;;
|
|
2158
2172
|
codex)
|
|
2159
2173
|
if command -v codex &>/dev/null; then
|
|
2160
|
-
verdict=$(codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
|
|
2174
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" codex exec --sandbox workspace-write "$prompt" 2>/dev/null)
|
|
2161
2175
|
fi
|
|
2162
2176
|
;;
|
|
2163
2177
|
gemini)
|
|
2164
2178
|
if command -v gemini &>/dev/null; then
|
|
2165
|
-
verdict=$(echo "$prompt" | gemini 2>/dev/null)
|
|
2179
|
+
verdict=$(echo "$prompt" | timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" gemini 2>/dev/null)
|
|
2166
2180
|
fi
|
|
2167
2181
|
;;
|
|
2168
2182
|
cline)
|
|
2169
2183
|
if command -v cline &>/dev/null; then
|
|
2170
|
-
verdict=$(cline -y "$prompt" 2>/dev/null)
|
|
2184
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" cline -y "$prompt" 2>/dev/null)
|
|
2171
2185
|
fi
|
|
2172
2186
|
;;
|
|
2173
2187
|
aider)
|
|
2174
2188
|
if command -v aider &>/dev/null; then
|
|
2175
|
-
verdict=$(aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
|
|
2189
|
+
verdict=$(timeout "${LOKI_COUNCIL_REVIEW_TIMEOUT:-600}" aider --message "$prompt" --yes-always --no-auto-commits --no-git 2>/dev/null)
|
|
2176
2190
|
fi
|
|
2177
2191
|
;;
|
|
2178
2192
|
esac
|
|
@@ -2753,7 +2767,13 @@ council_evaluate() {
|
|
|
2753
2767
|
fi
|
|
2754
2768
|
fi
|
|
2755
2769
|
if [ -z "$aggregate_result" ]; then
|
|
2756
|
-
|
|
2770
|
+
# bash-F2: council_aggregate_votes emits log_info/log_warn lines on
|
|
2771
|
+
# stdout (run.sh log_* helpers do not redirect to stderr) before its
|
|
2772
|
+
# terminal `echo "$verdict"`. Capturing the whole stream and exact-
|
|
2773
|
+
# matching "COMPLETE" never succeeds, so the heuristic path could never
|
|
2774
|
+
# return COMPLETE. Take only the last line (the verdict token); any
|
|
2775
|
+
# degenerate empty output falls through to the safe not-COMPLETE default.
|
|
2776
|
+
aggregate_result=$(council_aggregate_votes | tail -n1)
|
|
2757
2777
|
fi
|
|
2758
2778
|
|
|
2759
2779
|
if [ "$aggregate_result" = "COMPLETE" ]; then
|
|
@@ -3011,6 +3031,11 @@ PYEOF
|
|
|
3011
3031
|
#===============================================================================
|
|
3012
3032
|
|
|
3013
3033
|
council_should_stop() {
|
|
3034
|
+
# bash-F1: reset the force-stop sentinel at entry. A return-0 from the
|
|
3035
|
+
# genuine approval path leaves this 0; only the safety-valve paths below
|
|
3036
|
+
# set it to 1 before their return-0.
|
|
3037
|
+
COUNCIL_FORCE_STOPPED=0
|
|
3038
|
+
|
|
3014
3039
|
if [ "$COUNCIL_ENABLED" != "true" ]; then
|
|
3015
3040
|
return 1 # Council disabled, don't stop
|
|
3016
3041
|
fi
|
|
@@ -3101,6 +3126,7 @@ council_should_stop() {
|
|
|
3101
3126
|
if [ "$COUNCIL_CONSECUTIVE_NO_CHANGE" -ge "$safety_limit" ]; then
|
|
3102
3127
|
log_error "Safety valve: ${COUNCIL_CONSECUTIVE_NO_CHANGE} iterations with no changes exceeds safety limit ($safety_limit)"
|
|
3103
3128
|
log_error "Forcing stop to prevent resource waste"
|
|
3129
|
+
COUNCIL_FORCE_STOPPED=1 # bash-F1: force-stop, NOT a council approval
|
|
3104
3130
|
return 0 # FORCE STOP
|
|
3105
3131
|
fi
|
|
3106
3132
|
fi
|
|
@@ -3109,6 +3135,7 @@ council_should_stop() {
|
|
|
3109
3135
|
if [ "$COUNCIL_TOTAL_DONE_SIGNALS" -ge "$COUNCIL_DONE_SIGNAL_LIMIT" ]; then
|
|
3110
3136
|
log_error "Safety valve: Agent signaled 'done' $COUNCIL_TOTAL_DONE_SIGNALS times (limit: $COUNCIL_DONE_SIGNAL_LIMIT)"
|
|
3111
3137
|
log_error "Forcing stop - agent believes work is complete"
|
|
3138
|
+
COUNCIL_FORCE_STOPPED=1 # bash-F1: force-stop, NOT a council approval
|
|
3112
3139
|
return 0 # FORCE STOP
|
|
3113
3140
|
fi
|
|
3114
3141
|
|
package/autonomy/run.sh
CHANGED
|
@@ -15728,6 +15728,23 @@ else:
|
|
|
15728
15728
|
ensure_completion_test_evidence || true
|
|
15729
15729
|
fi
|
|
15730
15730
|
if type council_should_stop &>/dev/null && council_should_stop; then
|
|
15731
|
+
# bash-F1: council_should_stop returns 0 from a genuine approval
|
|
15732
|
+
# AND from two force-stop safety valves (stagnation flood /
|
|
15733
|
+
# repeated done-signals). A force-stop is NOT a verified-complete
|
|
15734
|
+
# product, so it must not claim "PROJECT COMPLETE" or open a PR.
|
|
15735
|
+
# The sentinel set inside council_should_stop disambiguates.
|
|
15736
|
+
if [ "${COUNCIL_FORCE_STOPPED:-0}" = "1" ]; then
|
|
15737
|
+
echo ""
|
|
15738
|
+
log_header "COMPLETION COUNCIL: STOPPED WITHOUT APPROVAL"
|
|
15739
|
+
log_warn "Council force-stopped (stagnation or repeated done-signals); work is NOT verified-complete"
|
|
15740
|
+
log_info "Running memory consolidation..."
|
|
15741
|
+
run_memory_consolidation
|
|
15742
|
+
# No on_run_complete: a force-stop must never open a "done" PR.
|
|
15743
|
+
emit_completion_summary force_stopped
|
|
15744
|
+
save_state $retry "force_stopped" 0
|
|
15745
|
+
rm -f "$iter_output" 2>/dev/null
|
|
15746
|
+
return 0
|
|
15747
|
+
fi
|
|
15731
15748
|
echo ""
|
|
15732
15749
|
log_header "COMPLETION COUNCIL: PROJECT COMPLETE"
|
|
15733
15750
|
log_info "Council voted to stop (convergence detected + requirements verified)"
|
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:** v7.
|
|
5
|
+
**Version:** v7.66.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -395,7 +395,7 @@ provider works inside the container. Provide auth with your Anthropic API key:
|
|
|
395
395
|
# Run Loki Mode in Docker (Claude provider, API-key auth)
|
|
396
396
|
docker run --rm -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
|
|
397
397
|
-v $(pwd):/workspace -w /workspace \
|
|
398
|
-
asklokesh/loki-mode:7.
|
|
398
|
+
asklokesh/loki-mode:7.66.1 start ./my-spec.md
|
|
399
399
|
```
|
|
400
400
|
|
|
401
401
|
##### docker compose + .env (no host install)
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var r6=Object.defineProperty;var t6=($)=>$;function i6($,Q){this[$]=t6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:i6.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>j,homeLokiDir:()=>a$,findRepoRootForVersion:()=>n$,REPO_ROOT:()=>g});import{resolve as a,dirname as o$}from"path";import{fileURLToPath as e6}from"url";import{existsSync as j$}from"fs";import{homedir as $Q}from"os";function QQ(){let $=S1;for(let Q=0;Q<6;Q++){if(j$(a($,"VERSION"))&&j$(a($,"autonomy/run.sh")))return $;let Z=o$($);if(Z===$)break;$=Z}return a(S1,"..","..","..")}function n$($){let Q=$;for(let Z=0;Z<6;Z++){if(j$(a(Q,"VERSION"))&&j$(a(Q,"autonomy/run.sh")))return Q;let z=o$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function j(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function a$(){return a($Q(),".loki")}var S1,g;var C=P(()=>{S1=o$(e6(import.meta.url));g=QQ()});import{readFileSync as ZQ}from"fs";import{resolve as zQ,dirname as XQ}from"path";import{fileURLToPath as KQ}from"url";function F$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var r6=Object.defineProperty;var t6=($)=>$;function i6($,Q){this[$]=t6.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)r6($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:i6.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var q$=import.meta.require;var D1={};b(D1,{lokiDir:()=>j,homeLokiDir:()=>a$,findRepoRootForVersion:()=>n$,REPO_ROOT:()=>g});import{resolve as a,dirname as o$}from"path";import{fileURLToPath as e6}from"url";import{existsSync as j$}from"fs";import{homedir as $Q}from"os";function QQ(){let $=S1;for(let Q=0;Q<6;Q++){if(j$(a($,"VERSION"))&&j$(a($,"autonomy/run.sh")))return $;let Z=o$($);if(Z===$)break;$=Z}return a(S1,"..","..","..")}function n$($){let Q=$;for(let Z=0;Z<6;Z++){if(j$(a(Q,"VERSION"))&&j$(a(Q,"autonomy/run.sh")))return Q;let z=o$(Q);if(z===Q)break;Q=z}return a($,"..","..","..")}function j(){return process.env.LOKI_DIR??a(process.cwd(),".loki")}function a$(){return a($Q(),".loki")}var S1,g;var C=P(()=>{S1=o$(e6(import.meta.url));g=QQ()});import{readFileSync as ZQ}from"fs";import{resolve as zQ,dirname as XQ}from"path";import{fileURLToPath as KQ}from"url";function F$(){if(Q$!==null)return Q$;let $="7.66.1";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=XQ(KQ(import.meta.url)),Z=n$(Q);Q$=ZQ(zQ(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var s$=P(()=>{C()});var b1={};b(b1,{runOrThrow:()=>qQ,run:()=>k,commandVersion:()=>JQ,commandExists:()=>f,ShellError:()=>r$});async function k($,Q={}){let Z=Bun.spawn({cmd:[...$],stdout:"pipe",stderr:"pipe",env:Q.env?{...process.env,...Q.env}:process.env,cwd:Q.cwd}),z,X;if(Q.timeoutMs&&Q.timeoutMs>0)z=setTimeout(()=>{try{Z.kill("SIGTERM")}catch{}X=setTimeout(()=>{try{Z.kill("SIGKILL")}catch{}},2000)},Q.timeoutMs);try{let[q,K,W]=await Promise.all([new Response(Z.stdout).text(),new Response(Z.stderr).text(),Z.exited]);return{stdout:q,stderr:K,exitCode:W}}finally{if(z)clearTimeout(z);if(X)clearTimeout(X)}}async function qQ($,Q={}){let Z=await k($,Q);if(Z.exitCode!==0)throw new r$(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function f($){let Q=VQ($),Z=await k(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function VQ($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function JQ($,Q="--version"){if(!await f($))return null;let z=await k([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var r$;var d=P(()=>{r$=class r$ extends Error{message;exitCode;stdout;stderr;constructor($,Q,Z,z){super($);this.message=$;this.exitCode=Q;this.stdout=Z;this.stderr=z;this.name="ShellError"}}});function s($){return WQ?"":$}var WQ,T,S,_,wZ,I,R,h,V;var c=P(()=>{WQ=(process.env.NO_COLOR??"").length>0;T=s("\x1B[0;31m"),S=s("\x1B[0;32m"),_=s("\x1B[1;33m"),wZ=s("\x1B[0;34m"),I=s("\x1B[0;36m"),R=s("\x1B[1m"),h=s("\x1B[2m"),V=s("\x1B[0m")});import{existsSync as wQ}from"fs";async function Z$(){if(Y$!==void 0)return Y$;let $="/opt/homebrew/bin/python3.12";if(wQ($))return Y$=$,$;let Q=await f("python3.12");if(Q)return Y$=Q,Q;let Z=await f("python3");return Y$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return k([Z,"-c",$],Q)}var Y$;var V$=P(()=>{d()});var e1={};b(e1,{runStatus:()=>uQ});import{existsSync as y,readFileSync as W$,readdirSync as d1,statSync as o1}from"fs";import{resolve as D,basename as DQ}from"path";import{homedir as CQ}from"os";function n1($){let Q=Math.trunc($);if(Q>=1e6)return`${(Math.trunc(Q/1e6*10)/10).toFixed(1)}M`;if(Q>=1000)return`${(Math.trunc(Q/1000*10)/10).toFixed(1)}K`;return String(Q)}function a1($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*R$/Q);if(X>R$)X=R$;let q=R$-X,K=S;if(z>=80)K=T;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),J=n1($),U=n1(Q);return` ${R}${Z}${V} ${K}[${W}]${V} ${z}% (${J} / ${U})`}async function hQ(){if(await f("jq"))return!0;return process.stdout.write(`${T}Error: jq is required but not installed.${V}
|
|
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)
|
|
@@ -791,4 +791,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
791
791
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
792
792
|
`),process.stderr.write(s6),2}}l1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var KZ=await XZ(Bun.argv.slice(2));process.exit(KZ);
|
|
793
793
|
|
|
794
|
-
//# debugId=
|
|
794
|
+
//# debugId=3E79E75C7B6CF7FD64756E2164756E21
|
package/mcp/__init__.py
CHANGED
package/memory/storage.py
CHANGED
|
@@ -317,10 +317,16 @@ class MemoryStorage:
|
|
|
317
317
|
if lock_file is not None:
|
|
318
318
|
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
|
|
319
319
|
lock_file.close()
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
# Do NOT os.remove(lock_path) here. Unlinking the lock file on
|
|
321
|
+
# release is a flock+unlink inode-replacement race: with 3+
|
|
322
|
+
# contenders, holder A unlinks inode-1 after B (blocked on it)
|
|
323
|
+
# acquires it, then C opens the path, finds it gone, creates
|
|
324
|
+
# inode-2, and flocks inode-2 -- entering the critical section
|
|
325
|
+
# while B is still inside. That dropped index.json topics under
|
|
326
|
+
# concurrent store_pattern/store_episode (reproduced on Linux
|
|
327
|
+
# py3.13, 16 threads). Persistent lock files are the standard
|
|
328
|
+
# flock pattern; stale ones are GC'd by _cleanup_stale_locks,
|
|
329
|
+
# which is itself flock-safe (probe-before-unlink, wave-6).
|
|
324
330
|
|
|
325
331
|
def _atomic_write(self, path: Path, data: dict) -> None:
|
|
326
332
|
"""
|
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": "7.
|
|
4
|
+
"version": "7.66.1",
|
|
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).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"agent",
|
|
@@ -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": "7.
|
|
5
|
+
"version": "7.66.1",
|
|
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",
|