loki-mode 9.18.2 → 9.18.4
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 +27 -10
- package/autonomy/trigger-server.py +74 -24
- package/dashboard/__init__.py +1 -1
- package/loki-ts/dist/loki.js +2 -2
- package/mcp/__init__.py +1 -1
- 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 v9.18.
|
|
6
|
+
# Loki Mode v9.18.4
|
|
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.18.
|
|
473
|
+
**v9.18.4 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.18.
|
|
1
|
+
9.18.4
|
package/autonomy/loki
CHANGED
|
@@ -684,7 +684,7 @@ loki_remote_submit() {
|
|
|
684
684
|
last="$status"
|
|
685
685
|
fi
|
|
686
686
|
case "$status" in
|
|
687
|
-
|
|
687
|
+
passed|failed|unknown|error|rejected*)
|
|
688
688
|
break
|
|
689
689
|
;;
|
|
690
690
|
esac
|
|
@@ -701,16 +701,22 @@ loki_remote_submit() {
|
|
|
701
701
|
return 1
|
|
702
702
|
fi
|
|
703
703
|
|
|
704
|
-
#
|
|
705
|
-
#
|
|
706
|
-
#
|
|
707
|
-
#
|
|
708
|
-
#
|
|
709
|
-
#
|
|
704
|
+
# Gate on the BUILD's outcome, not on "a build was launched". The server
|
|
705
|
+
# reports passed / failed / unknown as distinct terminal statuses (see
|
|
706
|
+
# JOB_TERMINAL_STATUSES in trigger-server.py). Anything that is not
|
|
707
|
+
# "passed" exits non-zero so a CI pipeline cannot go green on a failed
|
|
708
|
+
# build. "unknown" -- the build detached and its exit code was never
|
|
709
|
+
# observed -- fails CLOSED: an outcome nobody observed is not a pass.
|
|
710
710
|
case "$status" in
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
711
|
+
passed) return 0 ;;
|
|
712
|
+
unknown)
|
|
713
|
+
echo -e "${RED}loki: remote job $job_id outcome UNKNOWN${NC}" >&2
|
|
714
|
+
echo " The build detached and its exit code was never observed." >&2
|
|
715
|
+
echo " An unobserved outcome is not a pass; treating it as a failure." >&2
|
|
716
|
+
return 1
|
|
717
|
+
;;
|
|
718
|
+
"") echo -e "${RED}loki: job $job_id did not reach a terminal status${NC}" >&2; return 1 ;;
|
|
719
|
+
*) echo -e "${RED}loki: remote job $job_id failed: $status${NC}" >&2; return 1 ;;
|
|
714
720
|
esac
|
|
715
721
|
}
|
|
716
722
|
|
|
@@ -832,6 +838,17 @@ loki_remote_verify_receipt() {
|
|
|
832
838
|
return 1
|
|
833
839
|
fi
|
|
834
840
|
|
|
841
|
+
# Drift is REPORTED, never a verdict. This receipt was produced in a cluster
|
|
842
|
+
# pod and is being checked against a different tree, so drift is the normal
|
|
843
|
+
# result and says nothing about tampering -- docs/VERIFICATION-COST.md:
|
|
844
|
+
# hash_ok is integrity, `ok` folds in tree_drift and false-alarms here.
|
|
845
|
+
# Stated rather than suppressed: silently dropping a true fact because it is
|
|
846
|
+
# inconvenient is the same error as folding UNCHECKED into UNSIGNED.
|
|
847
|
+
if [ "$(printf '%s' "$out" | jq -r '[.diff_drift,.tree_drift]|index(true)!=null' 2>/dev/null)" = "true" ]; then
|
|
848
|
+
echo " Drift: this tree differs from the one the receipt records. Expected --"
|
|
849
|
+
echo " the build ran elsewhere. Not tampering, and not part of the verdict."
|
|
850
|
+
fi
|
|
851
|
+
|
|
835
852
|
# gpg_ok=false covers TWO facts that must never be conflated: the signature
|
|
836
853
|
# is cryptographically bad over this content (the receipt was ALTERED), and
|
|
837
854
|
# gpg could not evaluate it at all (we do not hold the public key). Only the
|
|
@@ -105,6 +105,19 @@ JOB_EVENT = "loki_job"
|
|
|
105
105
|
# storm cannot grow memory without limit.
|
|
106
106
|
DEFAULT_JOB_HISTORY = 1024
|
|
107
107
|
|
|
108
|
+
# Terminal job statuses a remote client can gate on. "queued" and "running"
|
|
109
|
+
# are deliberately absent: a client must be able to tell "not done yet" from
|
|
110
|
+
# "done and failed", which a single non-passed value could not express.
|
|
111
|
+
#
|
|
112
|
+
# Only JOB_STATUS_PASSED means the build itself succeeded. JOB_STATUS_UNKNOWN
|
|
113
|
+
# means the build detached past our wait window and its exit code was never
|
|
114
|
+
# observed -- an unobserved outcome is NOT a pass, and the client exits
|
|
115
|
+
# non-zero on it (fail closed).
|
|
116
|
+
JOB_STATUS_PASSED = "passed"
|
|
117
|
+
JOB_STATUS_FAILED = "failed"
|
|
118
|
+
JOB_STATUS_UNKNOWN = "unknown"
|
|
119
|
+
JOB_TERMINAL_STATUSES = (JOB_STATUS_PASSED, JOB_STATUS_FAILED, JOB_STATUS_UNKNOWN)
|
|
120
|
+
|
|
108
121
|
# A run id names a directory under .loki/proofs/. run.sh mints it as
|
|
109
122
|
# "run-<utc>-<pid>-<rand>" or "proof-<utc>-<pid>-<rand>"; this is the alphabet
|
|
110
123
|
# those forms use. Applied to the pointer file's contents (never to a request
|
|
@@ -338,21 +351,30 @@ def _reap_child(proc):
|
|
|
338
351
|
pass
|
|
339
352
|
|
|
340
353
|
|
|
341
|
-
|
|
342
|
-
|
|
354
|
+
# Outcome of a dispatch, as distinct from "did it launch".
|
|
355
|
+
#
|
|
356
|
+
# run_loki_command already distinguished all three of these and then threw two
|
|
357
|
+
# of them away by returning a bool. Collapsing "exited 0" and "still detached"
|
|
358
|
+
# into True is what made a remotely-submitted build that STARTS and then FAILS
|
|
359
|
+
# report success: the client had no value to gate on. UNKNOWN is not a pass --
|
|
360
|
+
# an outcome we could not observe must fail closed.
|
|
361
|
+
# ponytail: the outcome IS the terminal job status, so they are the same three
|
|
362
|
+
# strings rather than two enums plus a mapping table.
|
|
363
|
+
OUTCOME_PASSED = JOB_STATUS_PASSED # ran to completion and exited 0
|
|
364
|
+
OUTCOME_FAILED = JOB_STATUS_FAILED # launch failed, or exited non-zero
|
|
365
|
+
OUTCOME_UNKNOWN = JOB_STATUS_UNKNOWN # detached; exit code never observed
|
|
343
366
|
|
|
344
|
-
Returns True if the command was launched and exited 0 (or backgrounded
|
|
345
|
-
cleanly within the wait window), False if the launch failed or it exited
|
|
346
|
-
non-zero. The child is always waited on, so no zombies accumulate. stderr
|
|
347
|
-
is captured on failure so a broken dispatch is diagnosable.
|
|
348
367
|
|
|
349
|
-
|
|
350
|
-
|
|
368
|
+
def run_loki_outcome(args, dry_run=False):
|
|
369
|
+
"""Run a loki command and report WHICH of the three outcomes occurred.
|
|
370
|
+
|
|
371
|
+
Returns one of OUTCOME_PASSED / OUTCOME_FAILED / OUTCOME_UNKNOWN. This is
|
|
372
|
+
the honest version of run_loki_command, which answers only "did it launch".
|
|
351
373
|
"""
|
|
352
374
|
cmd = ["loki"] + args
|
|
353
375
|
if dry_run:
|
|
354
376
|
logging.info("[DRY-RUN] Would run: %s", " ".join(cmd))
|
|
355
|
-
return
|
|
377
|
+
return OUTCOME_PASSED
|
|
356
378
|
logging.info("Running: %s", " ".join(cmd))
|
|
357
379
|
try:
|
|
358
380
|
proc = subprocess.Popen(
|
|
@@ -362,18 +384,14 @@ def run_loki_command(args, dry_run=False):
|
|
|
362
384
|
)
|
|
363
385
|
except (FileNotFoundError, OSError) as e:
|
|
364
386
|
logging.error("Failed to launch %s: %s", " ".join(cmd), e)
|
|
365
|
-
return
|
|
387
|
+
return OUTCOME_FAILED
|
|
366
388
|
|
|
367
389
|
try:
|
|
368
|
-
# Wait (and thereby reap) the child. A --detach launch returns quickly;
|
|
369
|
-
# this bound only guards against a wedged launch.
|
|
370
390
|
_, stderr = proc.communicate(timeout=DISPATCH_WAIT_SECONDS)
|
|
371
391
|
except subprocess.TimeoutExpired:
|
|
372
|
-
#
|
|
373
|
-
#
|
|
374
|
-
#
|
|
375
|
-
# THIS process is alive, and the OS reparents it after we exit. This
|
|
376
|
-
# keeps the "always waited on, no zombies" guarantee honest.
|
|
392
|
+
# Detached past the wait window. The reaper collects the child, but we
|
|
393
|
+
# never see its exit code, so the outcome is genuinely UNKNOWN -- NOT a
|
|
394
|
+
# pass. Reporting success here is exactly the defect this replaces.
|
|
377
395
|
logging.info(
|
|
378
396
|
"Dispatch pid=%d still running after %ds; reaping in background",
|
|
379
397
|
proc.pid,
|
|
@@ -385,11 +403,11 @@ def run_loki_command(args, dry_run=False):
|
|
|
385
403
|
name="loki-trigger-reaper-%d" % proc.pid,
|
|
386
404
|
daemon=True,
|
|
387
405
|
).start()
|
|
388
|
-
return
|
|
406
|
+
return OUTCOME_UNKNOWN
|
|
389
407
|
|
|
390
408
|
if proc.returncode == 0:
|
|
391
409
|
logging.info("Dispatch pid=%d completed (exit 0)", proc.pid)
|
|
392
|
-
return
|
|
410
|
+
return OUTCOME_PASSED
|
|
393
411
|
|
|
394
412
|
stderr_text = ""
|
|
395
413
|
if stderr:
|
|
@@ -400,7 +418,28 @@ def run_loki_command(args, dry_run=False):
|
|
|
400
418
|
proc.returncode,
|
|
401
419
|
stderr_text or "(no stderr)",
|
|
402
420
|
)
|
|
403
|
-
return
|
|
421
|
+
return OUTCOME_FAILED
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def run_loki_command(args, dry_run=False):
|
|
425
|
+
"""Run a loki command synchronously and reap it; or print it if dry_run.
|
|
426
|
+
|
|
427
|
+
Returns True if the command was launched and exited 0 (or backgrounded
|
|
428
|
+
cleanly within the wait window), False if the launch failed or it exited
|
|
429
|
+
non-zero. The child is always waited on, so no zombies accumulate. stderr
|
|
430
|
+
is captured on failure so a broken dispatch is diagnosable.
|
|
431
|
+
|
|
432
|
+
This is invoked from worker threads, so blocking here does not block the
|
|
433
|
+
HTTP listener.
|
|
434
|
+
|
|
435
|
+
Kept as the bool view for the GitHub webhook handlers, which only care
|
|
436
|
+
whether a dispatch started. A caller that must know whether the BUILD
|
|
437
|
+
passed wants run_loki_outcome instead. Behaviour is unchanged: a detached
|
|
438
|
+
dispatch (UNKNOWN) still reads as True here, exactly as before.
|
|
439
|
+
"""
|
|
440
|
+
return run_loki_outcome(args, dry_run=dry_run) in (
|
|
441
|
+
OUTCOME_PASSED, OUTCOME_UNKNOWN,
|
|
442
|
+
)
|
|
404
443
|
|
|
405
444
|
|
|
406
445
|
def handle_issues_event(payload, dry_run=False):
|
|
@@ -496,9 +535,11 @@ def handle_job_event(payload, dry_run=False):
|
|
|
496
535
|
return None, "rejected (invalid spec)"
|
|
497
536
|
args = ["start", spec.strip(), "--detach"]
|
|
498
537
|
summary = "job %s: %s" % (payload.get("job_id", "?"), spec.strip())
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
538
|
+
# A remote submitter gates CI on this, so report the BUILD's outcome, not
|
|
539
|
+
# merely that a build was launched. "fired" (launched) and "passed" must
|
|
540
|
+
# never share a value.
|
|
541
|
+
status = run_loki_outcome(args, dry_run=dry_run)
|
|
542
|
+
if status != OUTCOME_FAILED:
|
|
502
543
|
send_notification("Trigger fired: %s" % summary)
|
|
503
544
|
return summary, status
|
|
504
545
|
|
|
@@ -732,7 +773,16 @@ class Dispatcher:
|
|
|
732
773
|
continue
|
|
733
774
|
try:
|
|
734
775
|
event_type, payload = item
|
|
735
|
-
|
|
776
|
+
# Honour job_id ONLY for a remotely-submitted job. A GitHub
|
|
777
|
+
# payload carries no job_id of its own, so accepting one from
|
|
778
|
+
# any payload let a holder of the WEBHOOK HMAC write into the
|
|
779
|
+
# /jobs status store -- overwriting a real job's terminal
|
|
780
|
+
# status (e.g. "passed" -> "fired") and re-introducing the
|
|
781
|
+
# false-green. That is a webhook credential reaching a /jobs
|
|
782
|
+
# capability, which the separate-credential design forbids.
|
|
783
|
+
job_id = (payload.get("job_id")
|
|
784
|
+
if event_type == JOB_EVENT and isinstance(payload, dict)
|
|
785
|
+
else None)
|
|
736
786
|
if job_id:
|
|
737
787
|
self.record_job(job_id, "running")
|
|
738
788
|
# Counted for every dispatch, including webhook builds that
|
package/dashboard/__init__.py
CHANGED
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var p_=Object.create;var{getPrototypeOf:d_,defineProperty:eK,getOwnPropertyNames:c_}=Object;var l_=Object.prototype.hasOwnProperty;function i_(Z){return this[Z]}var a_,s_,n_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?a_??=new WeakMap:s_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?p_(d_(Z)):{};let K=X||!Z||!Z.__esModule?eK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of c_(Z))if(!l_.call(K,$))eK(K,$,{get:i_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var HQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var o_=(Z)=>Z;function r_(Z,X){this[Z]=o_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)eK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:r_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var xO={};l0(xO,{lokiDir:()=>j0,homeLokiDir:()=>R4,findRepoRootForVersion:()=>X$,REPO_ROOT:()=>i0});import{resolve as n7,dirname as Z$}from"path";import{fileURLToPath as t_}from"url";import{existsSync as UQ}from"fs";import{homedir as e_}from"os";function Zf(){let Z=kO;for(let X=0;X<6;X++){if(UQ(n7(Z,"VERSION"))&&UQ(n7(Z,"autonomy/run.sh")))return Z;let Q=Z$(Z);if(Q===Z)break;Z=Q}return n7(kO,"..","..","..")}function X$(Z){let X=Z;for(let Q=0;Q<6;Q++){if(UQ(n7(X,"VERSION"))&&UQ(n7(X,"autonomy/run.sh")))return X;let Y=Z$(X);if(Y===X)break;X=Y}return n7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??n7(process.cwd(),".loki")}function R4(){return n7(e_(),".loki")}var kO,i0;var H8=p(()=>{kO=Z$(t_(import.meta.url));i0=Zf()});import{readFileSync as Xf}from"fs";import{resolve as Qf,dirname as Yf}from"path";import{fileURLToPath as Jf}from"url";function h3(){if(h5!==null)return h5;let Z="9.18.
|
|
2
|
+
var p_=Object.create;var{getPrototypeOf:d_,defineProperty:eK,getOwnPropertyNames:c_}=Object;var l_=Object.prototype.hasOwnProperty;function i_(Z){return this[Z]}var a_,s_,n_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?a_??=new WeakMap:s_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?p_(d_(Z)):{};let K=X||!Z||!Z.__esModule?eK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of c_(Z))if(!l_.call(K,$))eK(K,$,{get:i_.bind(Z,$),enumerable:!0});if(Y)J.set(Z,K);return K};var HQ=(Z,X)=>()=>(X||Z((X={exports:{}}).exports,X),X.exports);var o_=(Z)=>Z;function r_(Z,X){this[Z]=o_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)eK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:r_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var t0=import.meta.require;var xO={};l0(xO,{lokiDir:()=>j0,homeLokiDir:()=>R4,findRepoRootForVersion:()=>X$,REPO_ROOT:()=>i0});import{resolve as n7,dirname as Z$}from"path";import{fileURLToPath as t_}from"url";import{existsSync as UQ}from"fs";import{homedir as e_}from"os";function Zf(){let Z=kO;for(let X=0;X<6;X++){if(UQ(n7(Z,"VERSION"))&&UQ(n7(Z,"autonomy/run.sh")))return Z;let Q=Z$(Z);if(Q===Z)break;Z=Q}return n7(kO,"..","..","..")}function X$(Z){let X=Z;for(let Q=0;Q<6;Q++){if(UQ(n7(X,"VERSION"))&&UQ(n7(X,"autonomy/run.sh")))return X;let Y=Z$(X);if(Y===X)break;X=Y}return n7(Z,"..","..","..")}function j0(){return process.env.LOKI_DIR??n7(process.cwd(),".loki")}function R4(){return n7(e_(),".loki")}var kO,i0;var H8=p(()=>{kO=Z$(t_(import.meta.url));i0=Zf()});import{readFileSync as Xf}from"fs";import{resolve as Qf,dirname as Yf}from"path";import{fileURLToPath as Jf}from"url";function h3(){if(h5!==null)return h5;let Z="9.18.4";if(typeof Z==="string"&&Z.length>0)return h5=Z,h5;try{let X=Yf(Jf(import.meta.url)),Q=X$(X);h5=Xf(Qf(Q,"VERSION"),"utf-8").trim()}catch{h5="unknown"}return h5}var h5=null;var BQ=p(()=>{H8()});var _O={};l0(_O,{runOrThrow:()=>Tf,run:()=>E0,readStreamCapped:()=>NQ,commandVersion:()=>Cf,commandExists:()=>X9,ShellError:()=>Q$,MAX_STDOUT_BYTES:()=>bO});async function NQ(Z,X=bO){let Q=Z.getReader(),Y=new TextDecoder,J="",z=0;try{while(z<X){let{done:K,value:$}=await Q.read();if(K)break;if(!$)continue;if(z+=$.byteLength,z>X){let V=$.byteLength-(z-X);J+=Y.decode($.subarray(0,V),{stream:!0});break}J+=Y.decode($,{stream:!0})}J+=Y.decode()}finally{try{await Q.cancel()}catch{}Q.releaseLock()}return J}async function E0(Z,X={}){let Q=Bun.spawn({cmd:[...Z],stdout:"pipe",stderr:"pipe",env:X.env?{...process.env,...X.env}:process.env,cwd:X.cwd}),Y,J;if(X.timeoutMs&&X.timeoutMs>0)Y=setTimeout(()=>{try{Q.kill("SIGTERM")}catch{}J=setTimeout(()=>{try{Q.kill("SIGKILL")}catch{}},2000)},X.timeoutMs);try{let[z,K,$]=await Promise.all([NQ(Q.stdout),new Response(Q.stderr).text(),Q.exited]);return{stdout:z,stderr:K,exitCode:$}}finally{if(Y)clearTimeout(Y);if(J)clearTimeout(J)}}async function Tf(Z,X={}){let Q=await E0(Z,X);if(Q.exitCode!==0)throw new Q$(`command failed (${Q.exitCode}): ${Z.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function X9(Z){let X=wf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function wf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Cf(Z,X="--version"){if(!await X9(Z))return null;let Y=await E0([Z,X],{timeoutMs:5000});if(Y.exitCode!==0)return null;return((Y.stdout||Y.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var bO=16777216,Q$;var x9=p(()=>{Q$=class Q$ extends Error{message;exitCode;stdout;stderr;constructor(Z,X,Q,Y){super(Z);this.message=Z;this.exitCode=X;this.stdout=Q;this.stderr=Y;this.name="ShellError"}}});function o7(Z){return Ff?"":Z}var Ff,A0,F8,_0,$W0,a0,V8,Q9,h;var S6=p(()=>{Ff=(process.env.NO_COLOR??"").length>0;A0=o7("\x1B[0;31m"),F8=o7("\x1B[0;32m"),_0=o7("\x1B[1;33m"),$W0=o7("\x1B[0;34m"),a0=o7("\x1B[0;36m"),V8=o7("\x1B[1m"),Q9=o7("\x1B[2m"),h=o7("\x1B[0m")});import{existsSync as ff}from"fs";async function E7(){if(x4!==void 0)return x4;let Z="/opt/homebrew/bin/python3.12";if(ff(Z))return x4=Z,Z;let X=await X9("python3.12");if(X)return x4=X,X;let Q=await X9("python3");return x4=Q,Q}async function Y7(Z,X={}){let Q=await E7();if(!Q)return{stdout:"",stderr:"python3 not found",exitCode:127};return E0([Q,"-c",Z],X)}var x4;var r7=p(()=>{x9()});var XA={};l0(XA,{runStatus:()=>Vh});import{existsSync as Y9,readFileSync as g3,readdirSync as aO,statSync as sO}from"fs";import{resolve as h8,basename as ef}from"path";import{homedir as Zh}from"os";function nO(Z){let X=Math.trunc(Z);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 oO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*AQ/X);if(J>AQ)J=AQ;let z=AQ-J,K=F8;if(Y>=80)K=A0;else if(Y>=50)K=_0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),V=nO(Z),W=nO(X);return` ${V8}${Q}${h} ${K}[${$}]${h} ${Y}% (${V} / ${W})`}async function Qh(){if(await X9("jq"))return!0;return process.stdout.write(`${A0}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)
|
|
@@ -1236,4 +1236,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
1236
1236
|
`),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (g_(),v_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1237
1237
|
`),process.stderr.write(m_),2}}lO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var dV0=await pV0(Bun.argv.slice(2));process.exit(dV0);
|
|
1238
1238
|
|
|
1239
|
-
//# debugId=
|
|
1239
|
+
//# debugId=FEE1C42CA5DB4B5764756E2164756E21
|
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.18.
|
|
4
|
+
"version": "9.18.4",
|
|
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": "9.18.
|
|
5
|
+
"version": "9.18.4",
|
|
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",
|