loki-mode 9.25.2 → 9.26.3
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/lib/config-map.sh +126 -0
- package/autonomy/verify.sh +195 -5
- package/dashboard/__init__.py +1 -1
- package/docs/COMPETITIVE-NEXT-10.md +193 -0
- 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/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.26.3
|
|
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.26.3 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.
|
|
1
|
+
9.26.3
|
|
@@ -838,6 +838,106 @@ loki_config_generate_schema() {
|
|
|
838
838
|
# refs, raw-secret literals (ERROR), and per-value validation failures. Returns
|
|
839
839
|
# non-zero on ANY failure. Reads the file directly (format-aware) WITHOUT
|
|
840
840
|
# exporting anything into the environment.
|
|
841
|
+
# Walk a JSON/YAML config's OWN key set and echo any dotted key that is not a
|
|
842
|
+
# member of LOKI_CONFIG_MAP, one per line. Used by validate only.
|
|
843
|
+
#
|
|
844
|
+
# Inert descriptive fields written by `loki init` (version/template/created) are
|
|
845
|
+
# allowlisted: they carry no behavior, so erroring on them would fail a file the
|
|
846
|
+
# product itself generated. Every other unmapped key is reported -- those are the
|
|
847
|
+
# ones a user expects to change behavior and that are silently dropped instead.
|
|
848
|
+
#
|
|
849
|
+
# Container keys are not reported: in {"dashboard":{"port":1}} the key
|
|
850
|
+
# "dashboard" is a parent of the mapped "dashboard.port", never a typo itself.
|
|
851
|
+
# A leaf is what a user actually mistypes.
|
|
852
|
+
loki_config_unknown_keys() {
|
|
853
|
+
local file="$1" fmt="$2"
|
|
854
|
+
command -v python3 >/dev/null 2>&1 || return 0
|
|
855
|
+
|
|
856
|
+
# YAML needs a parser. The rest of this file reaches for yq first, so do the
|
|
857
|
+
# same: convert to JSON via yq and let the JSON walk below handle it. That
|
|
858
|
+
# keeps detection working on a host with yq but no pyyaml (CI installs
|
|
859
|
+
# neither by default, and yq is the more common of the two here). Without
|
|
860
|
+
# either parser the walk no-ops and YAML validates as it did before -- a
|
|
861
|
+
# missing parser must not invent a verdict.
|
|
862
|
+
local scratch_json=""
|
|
863
|
+
if [ "$fmt" = "yaml" ] && ! python3 -c "import yaml" >/dev/null 2>&1; then
|
|
864
|
+
command -v yq >/dev/null 2>&1 || return 0
|
|
865
|
+
scratch_json="$(mktemp "${TMPDIR:-/tmp}/loki-cfg-uk.XXXXXX")" || return 0
|
|
866
|
+
if ! yq eval -o=json '.' "$file" > "$scratch_json" 2>/dev/null; then
|
|
867
|
+
rm -f "$scratch_json"; return 0
|
|
868
|
+
fi
|
|
869
|
+
file="$scratch_json"; fmt="json"
|
|
870
|
+
fi
|
|
871
|
+
|
|
872
|
+
local map_str="" mapping
|
|
873
|
+
for mapping in "${LOKI_CONFIG_MAP[@]}"; do map_str+="${mapping%%:*}"$'\n'; done
|
|
874
|
+
|
|
875
|
+
_LOKI_UK_FILE="$file" _LOKI_UK_FMT="$fmt" _LOKI_UK_MAP="$map_str" python3 -c '
|
|
876
|
+
import json, os, sys
|
|
877
|
+
|
|
878
|
+
path = os.environ["_LOKI_UK_FILE"]
|
|
879
|
+
fmt = os.environ["_LOKI_UK_FMT"]
|
|
880
|
+
|
|
881
|
+
known = set()
|
|
882
|
+
for line in os.environ.get("_LOKI_UK_MAP", "").splitlines():
|
|
883
|
+
line = line.strip()
|
|
884
|
+
if line:
|
|
885
|
+
known.add(line)
|
|
886
|
+
|
|
887
|
+
# Parents of a mapped key are containers, not typos.
|
|
888
|
+
containers = set()
|
|
889
|
+
for k in known:
|
|
890
|
+
parts = k.split(".")
|
|
891
|
+
for i in range(1, len(parts)):
|
|
892
|
+
containers.add(".".join(parts[:i]))
|
|
893
|
+
|
|
894
|
+
# Inert metadata emitted by `loki init` -- descriptive, never behavioral.
|
|
895
|
+
ALLOW = {"version", "template", "created", "name", "description"}
|
|
896
|
+
|
|
897
|
+
try:
|
|
898
|
+
if fmt == "json":
|
|
899
|
+
with open(path) as f:
|
|
900
|
+
data = json.load(f)
|
|
901
|
+
else:
|
|
902
|
+
try:
|
|
903
|
+
import yaml
|
|
904
|
+
except ImportError:
|
|
905
|
+
sys.exit(0)
|
|
906
|
+
with open(path) as f:
|
|
907
|
+
data = yaml.safe_load(f)
|
|
908
|
+
except Exception:
|
|
909
|
+
# A malformed file is out of scope here -- the parsers report it.
|
|
910
|
+
sys.exit(0)
|
|
911
|
+
|
|
912
|
+
if not isinstance(data, dict):
|
|
913
|
+
sys.exit(0)
|
|
914
|
+
|
|
915
|
+
unknown = []
|
|
916
|
+
|
|
917
|
+
def walk(node, prefix):
|
|
918
|
+
for key, val in node.items():
|
|
919
|
+
dotted = prefix + key if not prefix else prefix + "." + key
|
|
920
|
+
if isinstance(val, dict) and val:
|
|
921
|
+
# Recurse into containers; report the leaves inside them.
|
|
922
|
+
walk(val, dotted)
|
|
923
|
+
continue
|
|
924
|
+
if dotted in known or dotted in containers or dotted in ALLOW:
|
|
925
|
+
continue
|
|
926
|
+
unknown.append(dotted)
|
|
927
|
+
|
|
928
|
+
walk(data, "")
|
|
929
|
+
for u in unknown:
|
|
930
|
+
print(u)
|
|
931
|
+
' 2>/dev/null
|
|
932
|
+
# Always succeed: this helper reports keys on stdout, and a parser that
|
|
933
|
+
# cannot run must degrade to "nothing to report" rather than failing the
|
|
934
|
+
# caller. The `[ -n ... ] && rm` form would return non-zero on the common
|
|
935
|
+
# empty-scratch path and discard the captured output, so clean up with an
|
|
936
|
+
# unconditional rm on a possibly-empty path instead.
|
|
937
|
+
rm -f "${scratch_json:-/dev/null}" 2>/dev/null
|
|
938
|
+
return 0
|
|
939
|
+
}
|
|
940
|
+
|
|
841
941
|
loki_config_validate_file() {
|
|
842
942
|
local path="$1"
|
|
843
943
|
local rc=0
|
|
@@ -907,6 +1007,32 @@ loki_config_validate_file() {
|
|
|
907
1007
|
_loki_cfg_collect_pairs "$path" "$fmt"
|
|
908
1008
|
)"
|
|
909
1009
|
|
|
1010
|
+
# Unknown-key detection for JSON/YAML.
|
|
1011
|
+
#
|
|
1012
|
+
# The extraction above walks LOKI_CONFIG_MAP and pulls each KNOWN path out of
|
|
1013
|
+
# the file, so a key the map does not contain is never emitted and cannot
|
|
1014
|
+
# reach the pair loop below -- a misspelled key validated clean while the
|
|
1015
|
+
# same typo in .env format was correctly rejected. Detection therefore has to
|
|
1016
|
+
# walk the FILE's own key set and diff it against the map, which is what this
|
|
1017
|
+
# block does. Kept in validate only: the load/emit paths are unchanged, so a
|
|
1018
|
+
# config that runs today still runs.
|
|
1019
|
+
case "$fmt" in
|
|
1020
|
+
(json|yaml)
|
|
1021
|
+
local unknown_keys
|
|
1022
|
+
unknown_keys="$(loki_config_unknown_keys "$path" "$fmt")" || unknown_keys=""
|
|
1023
|
+
if [ -n "$unknown_keys" ]; then
|
|
1024
|
+
local ukey
|
|
1025
|
+
while IFS= read -r ukey; do
|
|
1026
|
+
[ -n "$ukey" ] || continue
|
|
1027
|
+
printf 'loki: config validate: ERROR unknown key %s (not a recognized config key -- typo?)\n' "$ukey" >&2
|
|
1028
|
+
rc=1
|
|
1029
|
+
done <<UNKNOWN_KEYS
|
|
1030
|
+
$unknown_keys
|
|
1031
|
+
UNKNOWN_KEYS
|
|
1032
|
+
fi
|
|
1033
|
+
;;
|
|
1034
|
+
esac
|
|
1035
|
+
|
|
910
1036
|
local env_var value expanded
|
|
911
1037
|
while IFS=$'\t' read -r env_var value; do
|
|
912
1038
|
[ -n "$env_var" ] || continue
|
package/autonomy/verify.sh
CHANGED
|
@@ -69,6 +69,151 @@ VERIFY_EXIT_ERROR=3
|
|
|
69
69
|
VERIFY_SCHEMA_VERSION="1.0"
|
|
70
70
|
|
|
71
71
|
# Resolve tool version from the VERSION file shipped alongside the repo.
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# LLM review stage (v9.26.0). Phase 2 of the spec; the deterministic MVP shipped
|
|
74
|
+
# without it and the evidence document said so honestly.
|
|
75
|
+
#
|
|
76
|
+
# WHY IT LIVES HERE AND NOT IN THE COUNCIL. This module deliberately does not
|
|
77
|
+
# source completion-council.sh (see the header): those functions are welded to
|
|
78
|
+
# the iteration loop's globals and diff base. Instead this calls the same
|
|
79
|
+
# raw-SDK bridge the council uses under the hood -- `loki internal sdk-judge`,
|
|
80
|
+
# a pure-HTTPS judge that prints JSON on exit 0 and NOTHING on any failure.
|
|
81
|
+
#
|
|
82
|
+
# FAIL-CLOSED, NEVER FAIL-QUIET. Every failure path records an honest status
|
|
83
|
+
# (unavailable, with the reason) rather than a pass. A verifier that reports
|
|
84
|
+
# "reviewed, no issues" when the reviewer never ran is worse than one that
|
|
85
|
+
# never had a reviewer.
|
|
86
|
+
#
|
|
87
|
+
# ADVISORY IN THIS RELEASE. The verdict and exit code are computed exactly as
|
|
88
|
+
# before; this only adds a section to the evidence document. `loki verify`
|
|
89
|
+
# exits 0/1/2 on the same conditions it always did, so a CI job gating on exit
|
|
90
|
+
# 0 sees no behavior change. Verdict influence is a separate, later flag.
|
|
91
|
+
_verify_llm_review() {
|
|
92
|
+
local out_dir="$1" merge_base="$2" head_sha="$3"
|
|
93
|
+
|
|
94
|
+
if [ "${VERIFY_NO_LLM:-0}" = "1" ]; then
|
|
95
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "skipped" "--no-llm requested" "" "0" ""
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
local loki_bin
|
|
100
|
+
loki_bin="$(command -v loki 2>/dev/null || true)"
|
|
101
|
+
if [ -z "$loki_bin" ]; then
|
|
102
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the loki CLI is not on PATH, so the SDK judge bridge cannot be reached" "" "0" ""
|
|
103
|
+
return 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Bound the diff. A judge prompt is an input cost and an unbounded diff is
|
|
107
|
+
# both expensive and useless -- past some size the model cannot reason about
|
|
108
|
+
# it anyway. The cap is explicit in the reason string when it bites, so a
|
|
109
|
+
# truncated review is never silently presented as a whole-diff review.
|
|
110
|
+
local diff_cap="${LOKI_VERIFY_LLM_DIFF_BYTES:-200000}"
|
|
111
|
+
local diff_file; diff_file="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-diff.XXXXXX")" || {
|
|
112
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "could not create a temp file for the diff" "" "0" ""
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
git diff --function-context "${merge_base}..${head_sha}" > "$diff_file" 2>/dev/null || true
|
|
116
|
+
local diff_bytes; diff_bytes=$(wc -c < "$diff_file" 2>/dev/null | tr -d ' ')
|
|
117
|
+
diff_bytes="${diff_bytes:-0}"
|
|
118
|
+
local truncated=""
|
|
119
|
+
if [ "$diff_bytes" -gt "$diff_cap" ]; then
|
|
120
|
+
head -c "$diff_cap" "$diff_file" > "${diff_file}.cut" 2>/dev/null && mv -f "${diff_file}.cut" "$diff_file"
|
|
121
|
+
truncated=" (diff truncated to ${diff_cap} bytes of ${diff_bytes})"
|
|
122
|
+
fi
|
|
123
|
+
if [ "$diff_bytes" -eq 0 ]; then
|
|
124
|
+
rm -f "$diff_file" 2>/dev/null || true
|
|
125
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "skipped" "no diff to review between the merge base and HEAD" "" "0" ""
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local pf sf
|
|
130
|
+
pf="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-prompt.XXXXXX")" || { rm -f "$diff_file"; return 0; }
|
|
131
|
+
sf="$(mktemp "${TMPDIR:-/tmp}/loki-verify-llm-schema.XXXXXX")" || { rm -f "$diff_file" "$pf"; return 0; }
|
|
132
|
+
|
|
133
|
+
# Context first: the diff is the bulk of the prompt and identical across any
|
|
134
|
+
# retry, so leading with it keeps the cacheable prefix stable.
|
|
135
|
+
{
|
|
136
|
+
printf 'Review this diff for correctness defects only.\n\n'
|
|
137
|
+
cat "$diff_file"
|
|
138
|
+
printf '\n\nYou are a code reviewer on a verification service. Report ONLY defects you can point at in this diff:\n'
|
|
139
|
+
printf -- '- a bug that produces a wrong result or a crash, with the input that triggers it\n'
|
|
140
|
+
printf -- '- a security hole reachable from untrusted input\n'
|
|
141
|
+
printf -- '- data loss or corruption\n\n'
|
|
142
|
+
printf 'Do NOT report style, naming, formatting, test coverage, or speculative refactors.\n'
|
|
143
|
+
printf 'If the diff has no such defect, return an empty findings array. An empty result is a\n'
|
|
144
|
+
printf 'legitimate and common answer; do not invent a finding to appear useful.\n'
|
|
145
|
+
} > "$pf"
|
|
146
|
+
|
|
147
|
+
cat > "$sf" <<'SCHEMA'
|
|
148
|
+
{
|
|
149
|
+
"type": "object",
|
|
150
|
+
"properties": {
|
|
151
|
+
"summary": { "type": "string" },
|
|
152
|
+
"findings": {
|
|
153
|
+
"type": "array",
|
|
154
|
+
"items": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"properties": {
|
|
157
|
+
"severity": { "type": "string", "enum": ["critical", "high", "medium", "low"] },
|
|
158
|
+
"file": { "type": "string" },
|
|
159
|
+
"message": { "type": "string" },
|
|
160
|
+
"why_it_breaks": { "type": "string" }
|
|
161
|
+
},
|
|
162
|
+
"required": ["severity", "message", "why_it_breaks"]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": ["summary", "findings"]
|
|
167
|
+
}
|
|
168
|
+
SCHEMA
|
|
169
|
+
|
|
170
|
+
model="${LOKI_VERIFY_LLM_MODEL:-claude-sonnet-5}"
|
|
171
|
+
local to_s="${LOKI_VERIFY_LLM_TIMEOUT_S:-120}"
|
|
172
|
+
local wrap=""
|
|
173
|
+
if command -v timeout >/dev/null 2>&1; then wrap="timeout $(( to_s + 15 ))"
|
|
174
|
+
elif command -v gtimeout >/dev/null 2>&1; then wrap="gtimeout $(( to_s + 15 ))"; fi
|
|
175
|
+
|
|
176
|
+
local out rc=0
|
|
177
|
+
out="$($wrap "$loki_bin" internal sdk-judge \
|
|
178
|
+
--prompt-file "$pf" --schema-file "$sf" \
|
|
179
|
+
--model "$model" --effort high \
|
|
180
|
+
--timeout-ms "$(( to_s * 1000 ))" 2>/dev/null)" || rc=$?
|
|
181
|
+
rm -f "$diff_file" "$pf" "$sf" 2>/dev/null || true
|
|
182
|
+
|
|
183
|
+
if [ "$rc" -ne 0 ] || [ -z "$out" ]; then
|
|
184
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the SDK judge returned no result (no API key, transport failure, or timeout)${truncated}" "" "0" "$model"
|
|
185
|
+
return 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Parse defensively: a malformed payload is "unavailable", never a pass.
|
|
189
|
+
local parsed
|
|
190
|
+
parsed="$(printf '%s' "$out" | python3 -c '
|
|
191
|
+
import json, sys
|
|
192
|
+
try:
|
|
193
|
+
d = json.load(sys.stdin)
|
|
194
|
+
fs = d.get("findings") or []
|
|
195
|
+
if not isinstance(fs, list):
|
|
196
|
+
raise ValueError("findings is not a list")
|
|
197
|
+
print("%d\t%s" % (len(fs), (d.get("summary") or "").replace("\t", " ").replace("\n", " ")[:300]))
|
|
198
|
+
except Exception as exc:
|
|
199
|
+
print("ERR\t%s" % type(exc).__name__)
|
|
200
|
+
' 2>/dev/null)" || parsed="ERR\tparse"
|
|
201
|
+
|
|
202
|
+
case "$parsed" in
|
|
203
|
+
ERR*)
|
|
204
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "unavailable" "the SDK judge returned a payload that did not parse${truncated}" "" "0" "$model" ;;
|
|
205
|
+
*)
|
|
206
|
+
findings_n="${parsed%%\t*}"
|
|
207
|
+
summary="${parsed#*\t}"
|
|
208
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "reviewed" "" "$summary" "$findings_n" "$model"
|
|
209
|
+
# Keep the raw payload beside the evidence document so a reader can
|
|
210
|
+
# check the review rather than take the summary on faith.
|
|
211
|
+
printf '%s' "$out" > "${out_dir}/llm-review.json" 2>/dev/null || true
|
|
212
|
+
;;
|
|
213
|
+
esac
|
|
214
|
+
return 0
|
|
215
|
+
}
|
|
216
|
+
|
|
72
217
|
_verify_tool_version() {
|
|
73
218
|
local here
|
|
74
219
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -1917,6 +2062,23 @@ verify_emit_evidence() {
|
|
|
1917
2062
|
repo_name="$(git config --get remote.origin.url 2>/dev/null | sed -E 's#.*[:/]([^/]+/[^/]+)(\.git)?$#\1#' || echo "local")"
|
|
1918
2063
|
[ -z "$repo_name" ] && repo_name="local"
|
|
1919
2064
|
|
|
2065
|
+
# LLM review (advisory this release: it does not change VERIFY_VERDICT or
|
|
2066
|
+
# VERIFY_EXIT, both already computed above).
|
|
2067
|
+
local _llm_line _llm_status _llm_reason _llm_summary _llm_n _llm_model
|
|
2068
|
+
_llm_line="$(_verify_llm_review "$out_dir" "${VERIFY_MERGE_BASE:-}" "${VERIFY_HEAD_SHA:-HEAD}" 2>/dev/null || true)"
|
|
2069
|
+
_llm_status="$(printf '%s' "$_llm_line" | cut -f1)"
|
|
2070
|
+
_llm_reason="$(printf '%s' "$_llm_line" | cut -f2)"
|
|
2071
|
+
_llm_summary="$(printf '%s' "$_llm_line" | cut -f3)"
|
|
2072
|
+
_llm_n="$(printf '%s' "$_llm_line" | cut -f4)"
|
|
2073
|
+
_llm_model="$(printf '%s' "$_llm_line" | cut -f5)"
|
|
2074
|
+
[ -n "$_llm_status" ] || _llm_status="unavailable"
|
|
2075
|
+
[ -n "$_llm_n" ] || _llm_n=0
|
|
2076
|
+
|
|
2077
|
+
_V_LLM_STATUS="$_llm_status" \
|
|
2078
|
+
_V_LLM_REASON="$_llm_reason" \
|
|
2079
|
+
_V_LLM_SUMMARY="$_llm_summary" \
|
|
2080
|
+
_V_LLM_N="$_llm_n" \
|
|
2081
|
+
_V_LLM_MODEL="$_llm_model" \
|
|
1920
2082
|
_VERIFY_OUT_DIR="$out_dir" \
|
|
1921
2083
|
_VERIFY_FINDINGS="$_VERIFY_FINDINGS_FILE" \
|
|
1922
2084
|
_VERIFY_GATES="$_VERIFY_GATES_FILE" \
|
|
@@ -2019,9 +2181,21 @@ doc = {
|
|
|
2019
2181
|
},
|
|
2020
2182
|
"deterministic_gates": gates,
|
|
2021
2183
|
"llm_review": {
|
|
2022
|
-
|
|
2023
|
-
"
|
|
2184
|
+
# status is one of: reviewed | skipped | unavailable.
|
|
2185
|
+
# "unavailable" is deliberately NOT "skipped": a reviewer that could not
|
|
2186
|
+
# run is a different fact from one that was not asked to, and collapsing
|
|
2187
|
+
# them would let a broken key read as a clean pass.
|
|
2188
|
+
"status": os.environ.get("_V_LLM_STATUS", "unavailable"),
|
|
2189
|
+
"reason": os.environ.get("_V_LLM_REASON", "") or None,
|
|
2190
|
+
"summary": os.environ.get("_V_LLM_SUMMARY", "") or None,
|
|
2191
|
+
"finding_count": int(os.environ.get("_V_LLM_N", "0") or 0),
|
|
2192
|
+
"model": os.environ.get("_V_LLM_MODEL", "") or None,
|
|
2193
|
+
# An LLM review is not reproducible the way a runner exit code is, and
|
|
2194
|
+
# the document says so rather than implying determinism it lacks.
|
|
2024
2195
|
"reproducible": False,
|
|
2196
|
+
# Advisory in this release: recorded, but never folded into the verdict
|
|
2197
|
+
# or the exit code. Promoting it is a separate, flagged change.
|
|
2198
|
+
"affects_verdict": False,
|
|
2025
2199
|
},
|
|
2026
2200
|
"findings": findings,
|
|
2027
2201
|
"suppressed": [],
|
|
@@ -2102,7 +2276,15 @@ lines.append("# Autonomi Verify report")
|
|
|
2102
2276
|
lines.append("")
|
|
2103
2277
|
lines.append("Verdict: **%s** (exit %d)" % (doc["verdict"], doc["exit_code"]))
|
|
2104
2278
|
lines.append("")
|
|
2105
|
-
|
|
2279
|
+
_llm = doc.get("llm_review") or {}
|
|
2280
|
+
_llm_status = _llm.get("status", "unavailable")
|
|
2281
|
+
if _llm_status == "reviewed":
|
|
2282
|
+
_llm_note = "LLM review: %d finding(s)" % _llm.get("finding_count", 0)
|
|
2283
|
+
elif _llm_status == "skipped":
|
|
2284
|
+
_llm_note = "LLM review: skipped"
|
|
2285
|
+
else:
|
|
2286
|
+
_llm_note = "LLM review: unavailable"
|
|
2287
|
+
lines.append("Tool: loki verify %s | %s" % (doc["produced_by"]["tool_version"], _llm_note))
|
|
2106
2288
|
lines.append("")
|
|
2107
2289
|
s = doc["subject"]
|
|
2108
2290
|
lines.append("## Subject")
|
|
@@ -2140,7 +2322,10 @@ else:
|
|
|
2140
2322
|
lines.append("")
|
|
2141
2323
|
lines.append("## LLM review")
|
|
2142
2324
|
lines.append("")
|
|
2143
|
-
|
|
2325
|
+
if _llm.get("reason"):
|
|
2326
|
+
lines.append("LLM review not run: %s" % _llm["reason"])
|
|
2327
|
+
elif _llm_status == "reviewed" and _llm.get("summary"):
|
|
2328
|
+
lines.append("LLM review: %s" % _llm["summary"])
|
|
2144
2329
|
lines.append("")
|
|
2145
2330
|
lines.append("Evidence JSON: %s" % ev_path)
|
|
2146
2331
|
lines.append("")
|
|
@@ -2183,7 +2368,9 @@ OPTIONS:
|
|
|
2183
2368
|
--block-on <list> Comma list of severities that BLOCK.
|
|
2184
2369
|
Default: critical,high (one notch looser than the
|
|
2185
2370
|
Loki build loop, which also blocks on medium).
|
|
2186
|
-
--no-llm
|
|
2371
|
+
--no-llm Skip the LLM review stage. The deterministic gates and the
|
|
2372
|
+
verdict are unchanged either way -- the review is advisory
|
|
2373
|
+
in this release and never alters the exit code.
|
|
2187
2374
|
--json Emit the evidence document to stdout so it can be piped
|
|
2188
2375
|
(`loki verify --json | jq .verdict`). The same document
|
|
2189
2376
|
is still written to <out>/evidence.json. The human
|
|
@@ -2793,6 +2980,9 @@ verify_main() {
|
|
|
2793
2980
|
--block-on)
|
|
2794
2981
|
block_on="$(printf '%s' "${2:-}" | tr '[:upper:]' '[:lower:]')"; shift 2 ;;
|
|
2795
2982
|
--no-llm)
|
|
2983
|
+
# Was a no-op accepted "for forward-compat" while no LLM stage
|
|
2984
|
+
# existed. Now it does what its name always implied.
|
|
2985
|
+
VERIFY_NO_LLM=1
|
|
2796
2986
|
shift ;;
|
|
2797
2987
|
--json)
|
|
2798
2988
|
# Emit the evidence document to STDOUT so a caller can pipe it.
|
package/dashboard/__init__.py
CHANGED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# The next items vs Factory.ai and 8090
|
|
2
|
+
|
|
3
|
+
Written 2026-09-09. Every item below cites a file:line, a command output, or a
|
|
4
|
+
fetched URL. Items that research proposed but that turned out to be **already
|
|
5
|
+
shipped** are listed at the bottom under "Not items" with the evidence, because
|
|
6
|
+
a plan that re-builds working code is worse than a shorter plan.
|
|
7
|
+
|
|
8
|
+
There are **seven** real items, not ten. Three of the research's candidates were
|
|
9
|
+
already implemented, and two more are architecturally unavailable to Loki as
|
|
10
|
+
designed. Padding to ten would mean inventing work.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What the market actually rewards
|
|
15
|
+
|
|
16
|
+
Both competitors lead with **governance, not speed**. Factory.ai ($150M Series C,
|
|
17
|
+
Apr 2026) sells fleets of droids under "engineers governing how much autonomy
|
|
18
|
+
each workflow receives". 8090 ($135M Series A, EY partnership) sells a "governed
|
|
19
|
+
multiplayer platform under human-led oversight".
|
|
20
|
+
|
|
21
|
+
Loki's wedge is the same shape and sharper: **the only agent that hands you a
|
|
22
|
+
receipt you can check yourself.** The items below are ranked by how much each
|
|
23
|
+
one strengthens a claim a buyer can verify without trusting us.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. Surface the evidence the receipt already holds
|
|
28
|
+
|
|
29
|
+
**Status: the computation exists; the presentation does not.**
|
|
30
|
+
|
|
31
|
+
`autonomy/lib/proof-generator.py` already computes, per run:
|
|
32
|
+
|
|
33
|
+
- the exogenous-vs-advisory gate split (`:291-307`) -- which gates are
|
|
34
|
+
agent-independent and which are model-authored, fail-closed on unknown gates
|
|
35
|
+
- files changed and a stat-level diff hash (`:910-917`)
|
|
36
|
+
- per-run efficiency cost, shared with the benchmark adapters so both compute it
|
|
37
|
+
identically (`:39`, `:93-94`)
|
|
38
|
+
|
|
39
|
+
None of it reaches the top of the receipt. A reader has to know it is in there.
|
|
40
|
+
|
|
41
|
+
This is the highest-value item because the research validated it from the
|
|
42
|
+
outside: Factory users complain about false-green runs and untrustworthy model
|
|
43
|
+
attribution. Those complaints describe exactly the fields Loki already has and
|
|
44
|
+
does not show.
|
|
45
|
+
|
|
46
|
+
**Do:** promote to the receipt header -- gate counts split exogenous/advisory,
|
|
47
|
+
files changed, tokens and turns, dispatched model per iteration, per-run cost.
|
|
48
|
+
No new measurement, no new dependency.
|
|
49
|
+
|
|
50
|
+
**Verify:** a receipt from a real run displays all six without opening the JSON.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. Make the machine contract discoverable
|
|
55
|
+
|
|
56
|
+
**Status: the contract exists and is documented; it is invisible where a user
|
|
57
|
+
would look.**
|
|
58
|
+
|
|
59
|
+
`docs/exit-codes.md` documents a genuinely good tiered contract: with
|
|
60
|
+
`LOKI_DURABLE_STATE=1`, `loki start` distinguishes "failed the quality gate"
|
|
61
|
+
from "crashed" -- written for a Kubernetes Job or an ECS task. `loki verify` has
|
|
62
|
+
its own documented contract (`docs/exit-codes.md:53`).
|
|
63
|
+
|
|
64
|
+
But `loki start --help` mentions `LOKI_DURABLE_STATE` **zero times** (measured).
|
|
65
|
+
A CI author reads `--help`, sees "0 on success, nonzero on failure", and builds
|
|
66
|
+
the coarse gate. Factory's `droid exec` advertises its exit codes in its own help
|
|
67
|
+
output; ours are a doc you have to already know exists.
|
|
68
|
+
|
|
69
|
+
**Do:** surface the durable contract in `loki start --help` and `loki verify
|
|
70
|
+
--help`, with a one-line pointer to `docs/exit-codes.md`.
|
|
71
|
+
|
|
72
|
+
**Note:** the research framed this as "Loki has no headless one-shot contract".
|
|
73
|
+
That framing was wrong -- the contract exists. The defect is discoverability,
|
|
74
|
+
which is a much cheaper fix.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Publish a measured kill-switch latency
|
|
79
|
+
|
|
80
|
+
**Status: mechanism exists, number does not.**
|
|
81
|
+
|
|
82
|
+
`check_human_intervention()` (`autonomy/run.sh`) implements PAUSE/STOP/INPUT.
|
|
83
|
+
There is no stated termination window anywhere in `docs/` (measured: zero
|
|
84
|
+
matches for "termination window" or "kill switch").
|
|
85
|
+
|
|
86
|
+
An enterprise buyer asks "how fast can I stop it?" Factory answers with a number.
|
|
87
|
+
"There is a stop signal" is not an answer.
|
|
88
|
+
|
|
89
|
+
**Do:** measure worst-case latency from signal to process exit across the bash
|
|
90
|
+
and Bun routes, publish the number, and add a test that fails if it regresses
|
|
91
|
+
past the published bound.
|
|
92
|
+
|
|
93
|
+
**Care:** publish the measured worst case, not the median. A number we beat 50%
|
|
94
|
+
of the time is worse than no number.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 4. Close the config-diagnostic gap for the remaining format
|
|
99
|
+
|
|
100
|
+
**Status: shipped for JSON and .env in v9.26.1-9.26.2; YAML is parser-dependent.**
|
|
101
|
+
|
|
102
|
+
`loki config validate` now reports unknown keys in JSON and `.env`. YAML
|
|
103
|
+
detection requires pyyaml or `yq`; with neither installed it degrades quietly
|
|
104
|
+
(correct -- a missing parser must not invent a verdict) but silently provides no
|
|
105
|
+
detection.
|
|
106
|
+
|
|
107
|
+
**Do:** either vendor a minimal YAML key-path scanner (the file already has
|
|
108
|
+
`loki_yaml_fallback_extract` for the read path, so the precedent exists), or
|
|
109
|
+
state the dependency in `loki config validate --help` so the gap is visible
|
|
110
|
+
rather than silent.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 5. `loki init` writes a config nothing reads
|
|
115
|
+
|
|
116
|
+
**Status: confirmed defect, low blast radius.**
|
|
117
|
+
|
|
118
|
+
`loki init` writes `.loki/loki.config.json` with six behavioral-looking keys --
|
|
119
|
+
`provider`, `complexity`, `quality_gates`, `parallel_mode`, `dashboard` -- and
|
|
120
|
+
labels it "project configuration" (`autonomy/loki:16598`). **Nothing reads any
|
|
121
|
+
of them** (measured: 0 read sites for all five; the only `template` match is a
|
|
122
|
+
comment). A user who sets `"quality_gates": false` is silently ignored.
|
|
123
|
+
|
|
124
|
+
Note this is a *different file* from `.loki/config.json`, which is live and is
|
|
125
|
+
read for `memory.disabled` and `otel_endpoint`.
|
|
126
|
+
|
|
127
|
+
**Mitigated already:** as of v9.26.1, `loki config validate` on that file
|
|
128
|
+
reports each dead key by name. The gap is now diagnosable rather than silent.
|
|
129
|
+
|
|
130
|
+
**Do:** the honest minimum is to stop describing it as "project configuration".
|
|
131
|
+
Removing the keys outright flips `tests/test-init-command.sh:141`, which asserts
|
|
132
|
+
`'provider' in d` -- so that is a deliberate contract change, not a cleanup.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 6. Verdict influence for the LLM review stage
|
|
137
|
+
|
|
138
|
+
**Status: stage ships in v9.26.0/9.26.1; verdict influence deliberately deferred.**
|
|
139
|
+
|
|
140
|
+
`llm_review` now runs by default and is recorded, but `affects_verdict` is
|
|
141
|
+
`false`. That was the right call for the release -- flipping it would silently
|
|
142
|
+
break anyone gating CI on exit 0.
|
|
143
|
+
|
|
144
|
+
**Do:** measure the reviewer on real diffs, then promote verdict influence behind
|
|
145
|
+
an explicit flag (`--llm-blocks`) before considering it as a default. The
|
|
146
|
+
sequencing matters more than the speed: a reviewer that returns CONCERNS where
|
|
147
|
+
deterministic-only returned VERIFIED is a breaking change to a published exit
|
|
148
|
+
contract.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7. Make the deferred-suite gap structural
|
|
153
|
+
|
|
154
|
+
**Status: process defect, cost two broken releases this cycle.**
|
|
155
|
+
|
|
156
|
+
v9.25.0 and v9.25.1 both failed to publish because a check that guards the
|
|
157
|
+
shipped artifact was **deferred by the fast tier** -- the only tier that runs
|
|
158
|
+
before every push. v9.26.0 failed the same way on repo-wide ShellCheck.
|
|
159
|
+
|
|
160
|
+
CLAUDE.md already states the rule ("a check that guards the shipped artifact must
|
|
161
|
+
run in the FAST tier"). The rule is not enforced.
|
|
162
|
+
|
|
163
|
+
**Do:** when a change touches a file, run the deferred suites that grep that file
|
|
164
|
+
before pushing. This session did it by hand (16 suites for `verify.sh`, then
|
|
165
|
+
repo-wide ShellCheck) and it caught the failure locally instead of one 25-minute
|
|
166
|
+
CI cycle at a time. Make it a script rather than a habit.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Not items (research proposed these; they are already shipped)
|
|
171
|
+
|
|
172
|
+
- **Signed receipts default-off is a moat gated behind an env var.** The receipt
|
|
173
|
+
already states signature status explicitly in both directions
|
|
174
|
+
(`proof-generator.py:1808-1820`): SIGNED, or UNSIGNED with the honest line that
|
|
175
|
+
the integrity hash "does NOT prove who produced them, so this receipt trusts
|
|
176
|
+
its generator." That is the shippable version. Do **not** flip
|
|
177
|
+
`LOKI_PROOF_GPG_KEY` to default-on: with no key present it would either fail
|
|
178
|
+
the run or silently emit no signature, and the second is the false-green this
|
|
179
|
+
project exists to prevent. Key distribution is a founder decision.
|
|
180
|
+
- **OTEL to a customer-owned collector.** Already implemented -- `otel_endpoint`
|
|
181
|
+
is persisted and read (`autonomy/loki:26755`, `:26828-26837`).
|
|
182
|
+
- **A headless exec contract with documented exit codes.** Already exists via
|
|
183
|
+
`LOKI_DURABLE_STATE=1`; see item 2, which is the real (smaller) gap.
|
|
184
|
+
|
|
185
|
+
## Architecturally unavailable, and worth saying so
|
|
186
|
+
|
|
187
|
+
- **Per-command risk tiers** and a **hard command blocklist** ("cannot be
|
|
188
|
+
bypassed by approval", per Factory's docs). `autonomy/run.sh:515` documents
|
|
189
|
+
that `LOKI_ALLOWED_PATHS` "does NOT restrict provider-driven agent writes
|
|
190
|
+
(run.sh never sees them)". You cannot classify a command you never observe.
|
|
191
|
+
The only honest form is a sandbox-boundary blocklist, which is a different and
|
|
192
|
+
much larger piece of work. Attempting a partial version would ship a security
|
|
193
|
+
claim we cannot keep.
|
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.26.3
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var St=Object.create;var{getPrototypeOf:yt,defineProperty:jG,getOwnPropertyNames:bt}=Object;var ft=Object.prototype.hasOwnProperty;function _t($){return this[$]}var vt,ht,gt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?vt??=new WeakMap:ht??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?St(yt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of bt($))if(!ft.call(J,q))jG(J,q,{get:_t.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var mt=($)=>$;function ut($,X){this[$]=mt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:ut.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var YR={};B1(YR,{lokiDir:()=>h0,homeLokiDir:()=>HQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as dt}from"url";import{existsSync as Zq}from"fs";import{homedir as pt}from"os";function ct(){let $=VR;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(VR,"..","..","..")}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 HQ(){return h2(pt(),".loki")}var VR,L1;var k1=s(()=>{VR=LG(dt(import.meta.url));L1=ct()});import{readFileSync as lt}from"fs";import{resolve as it,dirname as at}from"path";import{fileURLToPath as ot}from"url";function j9(){if(n3!==null)return n3;let $="9.
|
|
2
|
+
var St=Object.create;var{getPrototypeOf:yt,defineProperty:jG,getOwnPropertyNames:bt}=Object;var ft=Object.prototype.hasOwnProperty;function _t($){return this[$]}var vt,ht,gt=($,X,Q)=>{var z=$!=null&&typeof $==="object";if(z){var Z=X?vt??=new WeakMap:ht??=new WeakMap,K=Z.get($);if(K)return K}Q=$!=null?St(yt($)):{};let J=X||!$||!$.__esModule?jG(Q,"default",{value:$,enumerable:!0}):Q;for(let q of bt($))if(!ft.call(J,q))jG(J,q,{get:_t.bind($,q),enumerable:!0});if(z)Z.set($,J);return J};var zq=($,X)=>()=>(X||$((X={exports:{}}).exports,X),X.exports);var mt=($)=>$;function ut($,X){this[$]=mt.bind(null,X)}var B1=($,X)=>{for(var Q in X)jG($,Q,{get:X[Q],enumerable:!0,configurable:!0,set:ut.bind(X,Q)})};var s=($,X)=>()=>($&&(X=$($=0)),X);var w5=import.meta.require;var YR={};B1(YR,{lokiDir:()=>h0,homeLokiDir:()=>HQ,findRepoRootForVersion:()=>AG,REPO_ROOT:()=>L1});import{resolve as h2,dirname as LG}from"path";import{fileURLToPath as dt}from"url";import{existsSync as Zq}from"fs";import{homedir as pt}from"os";function ct(){let $=VR;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(VR,"..","..","..")}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 HQ(){return h2(pt(),".loki")}var VR,L1;var k1=s(()=>{VR=LG(dt(import.meta.url));L1=ct()});import{readFileSync as lt}from"fs";import{resolve as it,dirname as at}from"path";import{fileURLToPath as ot}from"url";function j9(){if(n3!==null)return n3;let $="9.26.3";if(typeof $==="string"&&$.length>0)return n3=$,n3;try{let X=at(ot(import.meta.url)),Q=AG(X);n3=lt(it(Q,"VERSION"),"utf-8").trim()}catch{n3="unknown"}return n3}var n3=null;var Kq=s(()=>{k1()});var GR={};B1(GR,{runOrThrow:()=>Ue,run:()=>$1,readStreamCapped:()=>Jq,commandVersion:()=>We,commandExists:()=>g5,ShellError:()=>CG,MAX_STDOUT_BYTES:()=>WR});async function Jq($,X=WR){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 Ue($,X={}){let Q=await $1($,X);if(Q.exitCode!==0)throw new CG(`command failed (${Q.exitCode}): ${$.join(" ")}`,Q.exitCode,Q.stdout,Q.stderr);return Q}async function g5($){let X=He($),Q=await $1(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function He($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function We($,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 WR=16777216,CG;var y8=s(()=>{CG=class CG 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 Ge?"":$}var Ge,p0,$5,q1,L61,A1,f1,m5,r;var t7=s(()=>{Ge=(process.env.NO_COLOR??"").length>0;p0=g2("\x1B[0;31m"),$5=g2("\x1B[0;32m"),q1=g2("\x1B[1;33m"),L61=g2("\x1B[0;34m"),A1=g2("\x1B[0;36m"),f1=g2("\x1B[1m"),m5=g2("\x1B[2m"),r=g2("\x1B[0m")});import{existsSync as De}from"fs";async function Z2(){if(GQ!==void 0)return GQ;let $="/opt/homebrew/bin/python3.12";if(De($))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 yR={};B1(yR,{runStatus:()=>oe});import{existsSync as u5,readFileSync as A9,readdirSync as RR,statSync as IR}from"fs";import{resolve as A5,basename as ge}from"path";import{homedir as me}from"os";function wR($){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 PR($,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=wR($),Y=wR(X);return` ${f1}${Q}${r} ${J}[${q}]${r} ${z}% (${V} / ${Y})`}async function de(){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)
|
|
@@ -1334,4 +1334,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
1334
1334
|
`),2}case"start":{let{runStart:z}=await Promise.resolve().then(() => (Et(),Pt));return z(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1335
1335
|
`),process.stderr.write(xt),2}}DR();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var X61=await $61(Bun.argv.slice(2));process.exit(X61);
|
|
1336
1336
|
|
|
1337
|
-
//# debugId=
|
|
1337
|
+
//# debugId=9A71B209805E0B772494C9C8FABE742E
|
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.26.3",
|
|
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",
|
|
@@ -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.26.3",
|
|
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",
|