loki-mode 7.120.0 → 7.121.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/checklist-verify.py +184 -28
- package/autonomy/verify.sh +8 -1
- 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/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.121.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
408
408
|
|
|
409
409
|
---
|
|
410
410
|
|
|
411
|
-
**v7.
|
|
411
|
+
**v7.121.1 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.121.1
|
|
@@ -53,12 +53,19 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
|
|
|
53
53
|
|
|
54
54
|
try:
|
|
55
55
|
if check_type == "file_exists":
|
|
56
|
-
|
|
56
|
+
# The checklist is LLM-emitted (generated_from a PRD), so the path
|
|
57
|
+
# field name is not pinnable: real checks in the wild carry the path
|
|
58
|
+
# under "path" OR "target" OR "file". Reading only "path" made a valid
|
|
59
|
+
# file_exists on a present file (target="package.json") raise
|
|
60
|
+
# "Invalid path characters: ''" -> None -> the item stuck 'pending' ->
|
|
61
|
+
# a correct build never reached a verified card. Alias the variants
|
|
62
|
+
# (same fake-inconclusive class as the grep/tests_pass runner bugs).
|
|
63
|
+
path = check.get("path") or check.get("target") or check.get("file") or ""
|
|
57
64
|
full_path = _validate_path(path, project_dir)
|
|
58
65
|
result["passed"] = os.path.exists(full_path)
|
|
59
66
|
|
|
60
67
|
elif check_type == "file_contains":
|
|
61
|
-
path = check.get("path"
|
|
68
|
+
path = check.get("path") or check.get("target") or check.get("file") or ""
|
|
62
69
|
pattern = check.get("pattern", "")
|
|
63
70
|
if pattern and not _SAFE_PATTERN_RE.match(pattern):
|
|
64
71
|
result["passed"] = None
|
|
@@ -84,11 +91,47 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
|
|
|
84
91
|
result["passed"] = None
|
|
85
92
|
result["output"] = f"Unsafe pattern rejected: {pattern!r}"
|
|
86
93
|
elif pattern:
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
# Runner-agnostic: use the PROJECT'S OWN declared test command
|
|
95
|
+
# rather than hardcoding a runner. A project's package.json
|
|
96
|
+
# "scripts.test" may be vitest / jest / mocha / node:test / ava /
|
|
97
|
+
# etc. Hardcoding `npx jest` was a fake-RED: on a vitest project
|
|
98
|
+
# (cdbbb5af, "test":"vitest run", jest not installed) `npx jest`
|
|
99
|
+
# either errored on the drifted --testPathPattern flag or npx tried
|
|
100
|
+
# to FETCH jest and timed out -> a genuinely-passing 26/26 suite read
|
|
101
|
+
# False/None ("tests not run") -> the item never reaches 'verified'
|
|
102
|
+
# -> a correct build never shows a verified card. Running the
|
|
103
|
+
# declared command runs the runner the build actually chose, so it
|
|
104
|
+
# is model-/complexity-agnostic. NB: we run the WHOLE declared suite
|
|
105
|
+
# (the per-item `pattern` is not a runner filter -- a mismatched
|
|
106
|
+
# vitest/jest filter can exit 0 with zero tests run = a fake-green).
|
|
107
|
+
pkg_path = os.path.join(project_dir, "package.json")
|
|
108
|
+
test_script = None
|
|
109
|
+
if os.path.isfile(pkg_path):
|
|
110
|
+
try:
|
|
111
|
+
with open(pkg_path) as _pf:
|
|
112
|
+
_pkg = json.load(_pf)
|
|
113
|
+
test_script = (_pkg.get("scripts") or {}).get("test")
|
|
114
|
+
except (json.JSONDecodeError, OSError):
|
|
115
|
+
test_script = None
|
|
116
|
+
if os.path.isfile(pkg_path):
|
|
117
|
+
if test_script and test_script.strip():
|
|
118
|
+
# `npm test` runs scripts.test with the project's local
|
|
119
|
+
# runner on PATH; list-form + shell=False + cwd = same
|
|
120
|
+
# posture as before (the engine already ran this exact
|
|
121
|
+
# command to build the workspace -- not a new surface).
|
|
122
|
+
cmd = ["npm", "test", "--silent"]
|
|
123
|
+
else:
|
|
124
|
+
# package.json but no test script -> nothing declared to
|
|
125
|
+
# run. Do NOT invent a runner (that reintroduces the
|
|
126
|
+
# hardcoded-runner fake-RED). Inconclusive, not a failure.
|
|
127
|
+
result["passed"] = None
|
|
128
|
+
result["output"] = (
|
|
129
|
+
"No 'scripts.test' declared in package.json; cannot run "
|
|
130
|
+
"the project's tests (inconclusive, not a failure)."
|
|
131
|
+
)
|
|
132
|
+
return result
|
|
90
133
|
else:
|
|
91
|
-
cmd = ["python3", "-m", "pytest", "-q"
|
|
134
|
+
cmd = ["python3", "-m", "pytest", "-q"]
|
|
92
135
|
try:
|
|
93
136
|
proc = subprocess.run(
|
|
94
137
|
cmd,
|
|
@@ -98,34 +141,74 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
|
|
|
98
141
|
timeout=timeout,
|
|
99
142
|
)
|
|
100
143
|
combined = proc.stdout + proc.stderr
|
|
101
|
-
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
144
|
+
_low = combined.lower()
|
|
145
|
+
# Trust gate: a tests_pass check REQUIRES POSITIVE PROOF that at
|
|
146
|
+
# least one test actually ran and passed. rc==0 alone is NOT
|
|
147
|
+
# proof: a no-op test script (`echo done`, `exit 0`, `true`, `:`)
|
|
148
|
+
# exits 0 having run ZERO tests -> passing on rc==0 would be a
|
|
149
|
+
# FAKE-GREEN (a required verification green with nothing tested).
|
|
150
|
+
# So we require a runner's "N passed" signal. Absence of that
|
|
151
|
+
# signal on rc==0 is INCONCLUSIVE (None -> pending), never True
|
|
152
|
+
# (fake-green) and never False (that would fake-RED an exotic
|
|
153
|
+
# passing runner). rc!=0 with real failures -> honest False. This
|
|
154
|
+
# is the same inconclusive-never-false moat as grep_codebase.
|
|
108
155
|
no_tests = (
|
|
109
|
-
"
|
|
110
|
-
or "no
|
|
111
|
-
or "no tests
|
|
112
|
-
or
|
|
156
|
+
"no tests found" in _low # jest
|
|
157
|
+
or "no test files found" in _low # vitest
|
|
158
|
+
or "no tests ran" in _low # pytest/jest phrasing
|
|
159
|
+
or "no tests to run" in _low
|
|
160
|
+
or proc.returncode == 5 # pytest: none collected
|
|
161
|
+
)
|
|
162
|
+
# Positive "tests ran and passed" signals across common runners.
|
|
163
|
+
# jest: "Tests: 3 passed" vitest: "Tests 26 passed (26)"
|
|
164
|
+
# pytest:"3 passed in 0.05s" mocha: "3 passing"
|
|
165
|
+
# node:test: "# pass 3" tap: "pass 3"
|
|
166
|
+
# The count MUST be >=1 (`[1-9]\d*`, never a literal 0): a
|
|
167
|
+
# runner can print "0 passed" / "0 passing" (mocha over an
|
|
168
|
+
# empty describe, all-.skip, node:test "# pass 0") on rc=0 with
|
|
169
|
+
# ZERO tests run -- matching a bare `\d+` would re-open the
|
|
170
|
+
# fake-green. A zero count falls through to the inconclusive
|
|
171
|
+
# else -> None (pending), correctly never green.
|
|
172
|
+
_ran_and_passed = bool(
|
|
173
|
+
re.search(r'tests?:\s*[1-9]\d*\s+passed', _low) # jest
|
|
174
|
+
or re.search(r'tests?\s+[1-9]\d*\s+passed', _low) # vitest
|
|
175
|
+
or re.search(r'\b[1-9]\d*\s+passed\b', _low) # pytest/vitest
|
|
176
|
+
or re.search(r'\b[1-9]\d*\s+passing\b', _low) # mocha
|
|
177
|
+
or re.search(r'#\s*pass\s+[1-9]\d*', _low) # node:test
|
|
178
|
+
or re.search(r'\bpass\s+[1-9]\d*\b', _low) # tap
|
|
113
179
|
)
|
|
114
180
|
if no_tests:
|
|
115
181
|
result["passed"] = False
|
|
116
182
|
result["output"] = (
|
|
117
183
|
"No tests discovered for required check "
|
|
118
|
-
|
|
119
|
-
"
|
|
184
|
+
"(a tests_pass check must run at least one test). "
|
|
185
|
+
"Output: "
|
|
120
186
|
) + combined[:400]
|
|
121
|
-
|
|
122
|
-
|
|
187
|
+
elif proc.returncode != 0:
|
|
188
|
+
# Ran and FAILED -> honest False (blocks). A non-zero exit
|
|
189
|
+
# from a real runner is a genuine negative, not inconclusive.
|
|
190
|
+
result["passed"] = False
|
|
123
191
|
result["output"] = combined[:500]
|
|
192
|
+
elif _ran_and_passed:
|
|
193
|
+
# rc==0 WITH positive "N passed" proof -> True.
|
|
194
|
+
result["passed"] = True
|
|
195
|
+
result["output"] = combined[:500]
|
|
196
|
+
else:
|
|
197
|
+
# rc==0 but NO positive "tests ran" signal (a no-op script,
|
|
198
|
+
# or a runner whose output we do not recognise). We cannot
|
|
199
|
+
# prove tests ran -> INCONCLUSIVE (None -> pending), never a
|
|
200
|
+
# fake-green True and never a fake-RED False.
|
|
201
|
+
result["passed"] = None
|
|
202
|
+
result["output"] = (
|
|
203
|
+
"Test command exited 0 but produced no recognisable "
|
|
204
|
+
"'N passed' signal -- cannot confirm any test ran "
|
|
205
|
+
"(inconclusive, not a pass). Output: "
|
|
206
|
+
) + combined[:350]
|
|
124
207
|
except subprocess.TimeoutExpired:
|
|
125
|
-
result["passed"] = None # timeout = pending
|
|
208
|
+
result["passed"] = None # timeout = pending (couldn't run)
|
|
126
209
|
result["output"] = f"Timed out after {timeout}s"
|
|
127
210
|
except FileNotFoundError:
|
|
128
|
-
result["passed"] = None
|
|
211
|
+
result["passed"] = None # runner not found = couldn't run
|
|
129
212
|
result["output"] = "Test runner not found"
|
|
130
213
|
else:
|
|
131
214
|
result["passed"] = None
|
|
@@ -169,9 +252,17 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
|
|
|
169
252
|
elif pattern:
|
|
170
253
|
try:
|
|
171
254
|
# grep with --exclude-dir for safety (no .git, node_modules)
|
|
172
|
-
# Use '--' to prevent pattern being interpreted as flags
|
|
255
|
+
# Use '--' to prevent pattern being interpreted as flags.
|
|
256
|
+
# Use -E (ERE) NOT the default BRE: LLM-emitted patterns are
|
|
257
|
+
# ERE/PCRE-flavoured (e.g. app\.get\('/api/tasks'). In BRE an
|
|
258
|
+
# escaped `\(` is a GROUP-OPEN, so an unmatched one makes grep
|
|
259
|
+
# error 'parentheses not balanced' (rc=2) -- a fake-RED that
|
|
260
|
+
# marked 3 present endpoints failing -> a 64-min non-converging
|
|
261
|
+
# build (#142/#124). Under -E, `\(` is a LITERAL paren (no
|
|
262
|
+
# error) AND quantifiers `.` `*` `+` keep their regex meaning,
|
|
263
|
+
# so a present-as-regex endpoint matches instead of erroring.
|
|
173
264
|
proc = subprocess.run(
|
|
174
|
-
["grep", "-r", "-l",
|
|
265
|
+
["grep", "-r", "-l", "-E",
|
|
175
266
|
"--exclude-dir=.git", "--exclude-dir=node_modules",
|
|
176
267
|
"--exclude-dir=.loki", "--exclude-dir=__pycache__",
|
|
177
268
|
"--", pattern, "."],
|
|
@@ -180,9 +271,74 @@ def run_check(check: dict, project_dir: str, timeout: int) -> dict:
|
|
|
180
271
|
text=True,
|
|
181
272
|
timeout=timeout,
|
|
182
273
|
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
274
|
+
# grep exit codes: 0=match found, 1=no match, >=2=ERROR (bad
|
|
275
|
+
# regex, unreadable file, or a grep variant -- e.g. ugrep on
|
|
276
|
+
# macOS -- that rejects a pattern GNU grep accepts). A grep that
|
|
277
|
+
# ERRORED has NOT proven the pattern is absent, so it must be
|
|
278
|
+
# INCONCLUSIVE (passed=None), NOT a hard False. Collapsing an
|
|
279
|
+
# error to False is a fake-RED: it marks a genuinely-present
|
|
280
|
+
# endpoint 'failing' -> the council hard-gate blocks a correct
|
|
281
|
+
# build forever (observed: an LLM-emitted pattern app\.get\('...'
|
|
282
|
+
# made ugrep error position-26 mismatched-paren -> returncode 2
|
|
283
|
+
# -> 3 working endpoints read failing -> a 64-min non-converging
|
|
284
|
+
# build). Only a clean 1 (real no-match) is an honest False.
|
|
285
|
+
if proc.returncode == 0:
|
|
286
|
+
result["passed"] = True
|
|
287
|
+
files_found = proc.stdout.strip().split("\n") if proc.stdout.strip() else []
|
|
288
|
+
result["output"] = f"Found in {len(files_found)} file(s)"
|
|
289
|
+
elif proc.returncode == 1:
|
|
290
|
+
result["passed"] = False
|
|
291
|
+
result["output"] = "Found in 0 file(s)"
|
|
292
|
+
else:
|
|
293
|
+
# rc>=2 = grep ERROR even under -E (e.g. a genuinely
|
|
294
|
+
# malformed pattern, or an unreadable file). Do NOT collapse
|
|
295
|
+
# to False (fake-RED) and do NOT punt straight to
|
|
296
|
+
# inconclusive (that opens a fake-green: an ABSENT endpoint
|
|
297
|
+
# whose pattern also errors would read pending, not failing).
|
|
298
|
+
# RECOVER real signal by retrying as a FIXED string (grep -F,
|
|
299
|
+
# escapes stripped to the intended literal), which cannot
|
|
300
|
+
# error on metacharacters. A fixed-string retry is a strict
|
|
301
|
+
# subset match: rc=0 (literal present) is a sound True; a
|
|
302
|
+
# literal-absent rc=1 keeps the honest-False moat for the
|
|
303
|
+
# common case. Only if -F ALSO errors is the check truly
|
|
304
|
+
# unrecoverable -> inconclusive (None), never a hard fail.
|
|
305
|
+
# NB: the -E primary already resolves the whole LLM
|
|
306
|
+
# escaped-paren class, so this branch is now rarely reached.
|
|
307
|
+
literal = re.sub(r'\\(.)', r'\1', pattern)
|
|
308
|
+
try:
|
|
309
|
+
proc2 = subprocess.run(
|
|
310
|
+
["grep", "-r", "-l", "-F",
|
|
311
|
+
"--exclude-dir=.git", "--exclude-dir=node_modules",
|
|
312
|
+
"--exclude-dir=.loki", "--exclude-dir=__pycache__",
|
|
313
|
+
"--", literal, "."],
|
|
314
|
+
cwd=project_dir, capture_output=True,
|
|
315
|
+
text=True, timeout=timeout,
|
|
316
|
+
)
|
|
317
|
+
except subprocess.TimeoutExpired:
|
|
318
|
+
proc2 = None
|
|
319
|
+
if proc2 is not None and proc2.returncode == 0:
|
|
320
|
+
files_found = proc2.stdout.strip().split("\n") if proc2.stdout.strip() else []
|
|
321
|
+
result["passed"] = True
|
|
322
|
+
result["output"] = f"Found in {len(files_found)} file(s) (fixed-string retry after regex error)"
|
|
323
|
+
else:
|
|
324
|
+
# -F rc=1 (literal absent) or rc>=2 (also errored) ->
|
|
325
|
+
# INCONCLUSIVE (None), never False. Once -E has failed to
|
|
326
|
+
# parse the pattern, the escape-stripped literal cannot
|
|
327
|
+
# distinguish "present only via a regex quantifier" from
|
|
328
|
+
# "genuinely absent" -- so a literal-absent here is NOT
|
|
329
|
+
# proof of absence. Mapping it to False would re-open the
|
|
330
|
+
# narrowed fake-RED. None -> item 'pending' (never blocks
|
|
331
|
+
# a correct build); a genuinely-absent endpoint is caught
|
|
332
|
+
# by the -E primary's honest rc=1 False above, not here.
|
|
333
|
+
result["passed"] = None
|
|
334
|
+
_err = (proc.stderr or "").strip().splitlines()
|
|
335
|
+
_why = ("literal-absent, cannot prove absence"
|
|
336
|
+
if (proc2 is not None and proc2.returncode == 1)
|
|
337
|
+
else (_err[0] if _err else f"exit {proc.returncode}"))
|
|
338
|
+
result["output"] = (
|
|
339
|
+
"grep regex error, unrecoverable (inconclusive, not a failure): "
|
|
340
|
+
+ _why
|
|
341
|
+
)[:200]
|
|
186
342
|
except subprocess.TimeoutExpired:
|
|
187
343
|
result["passed"] = None
|
|
188
344
|
result["output"] = f"Timed out after {timeout}s"
|
package/autonomy/verify.sh
CHANGED
|
@@ -423,8 +423,15 @@ verify_gate_tests() {
|
|
|
423
423
|
# them so the gate runs (or goes inconclusive -> CONCERNS when no
|
|
424
424
|
# runner is installed), never silently skipped.
|
|
425
425
|
if [ "$has_python" = "false" ]; then
|
|
426
|
-
|
|
426
|
+
# maxdepth 2 (was 1) + exclusions to MIRROR run.sh's enforce_test_coverage
|
|
427
|
+
# detection exactly (#139 parity): a shallow-but-not-root test_*.py (e.g.
|
|
428
|
+
# a src/-adjacent test) is now seen by BOTH gates, not just run.sh. Still
|
|
429
|
+
# the safe direction (under-detect only, never a fake-green): worst case a
|
|
430
|
+
# deeper test dir is inconclusive, never falsely VERIFIED.
|
|
431
|
+
if find "$tree" -maxdepth 2 -type f \
|
|
427
432
|
\( -name 'test_*.py' -o -name '*_test.py' \) \
|
|
433
|
+
-not -path '*/.loki/*' -not -path '*/.git/*' -not -path '*/node_modules/*' \
|
|
434
|
+
-not -path '*/.venv/*' -not -path '*/venv/*' \
|
|
428
435
|
-print -quit 2>/dev/null | grep -q .; then
|
|
429
436
|
has_python=true
|
|
430
437
|
fi
|
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.121.1
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.
|
|
2
|
+
var q8=Object.defineProperty;var J8=($)=>$;function V8($,Q){this[$]=J8.bind(null,Q)}var b=($,Q)=>{for(var Z in Q)q8($,Z,{get:Q[Z],enumerable:!0,configurable:!0,set:V8.bind(Q,Z)})};var P=($,Q)=>()=>($&&(Q=$($=0)),Q);var J$=import.meta.require;var m1={};b(m1,{lokiDir:()=>j,homeLokiDir:()=>T$,findRepoRootForVersion:()=>$1,REPO_ROOT:()=>h});import{resolve as t,dirname as e$}from"path";import{fileURLToPath as W8}from"url";import{existsSync as N$}from"fs";import{homedir as H8}from"os";function G8(){let $=v1;for(let Q=0;Q<6;Q++){if(N$(t($,"VERSION"))&&N$(t($,"autonomy/run.sh")))return $;let Z=e$($);if(Z===$)break;$=Z}return t(v1,"..","..","..")}function $1($){let Q=$;for(let Z=0;Z<6;Z++){if(N$(t(Q,"VERSION"))&&N$(t(Q,"autonomy/run.sh")))return Q;let z=e$(Q);if(z===Q)break;Q=z}return t($,"..","..","..")}function j(){return process.env.LOKI_DIR??t(process.cwd(),".loki")}function T$(){return t(H8(),".loki")}var v1,h;var C=P(()=>{v1=e$(W8(import.meta.url));h=G8()});import{readFileSync as U8}from"fs";import{resolve as Y8,dirname as B8}from"path";import{fileURLToPath as M8}from"url";function S$(){if(Q$!==null)return Q$;let $="7.121.1";if(typeof $==="string"&&$.length>0)return Q$=$,Q$;try{let Q=B8(M8(import.meta.url)),Z=$1(Q);Q$=U8(Y8(Z,"VERSION"),"utf-8").trim()}catch{Q$="unknown"}return Q$}var Q$=null;var Q1=P(()=>{C()});var p1={};b(p1,{runOrThrow:()=>S8,run:()=>R,readStreamCapped:()=>c1,commandVersion:()=>C8,commandExists:()=>u,ShellError:()=>Z1,MAX_STDOUT_BYTES:()=>u1});async function c1($,Q=u1){let Z=$.getReader(),z=new TextDecoder,X="",q=0;try{while(q<Q){let{done:K,value:W}=await Z.read();if(K)break;if(!W)continue;if(q+=W.byteLength,q>Q){let V=W.byteLength-(q-Q);X+=z.decode(W.subarray(0,V),{stream:!0});break}X+=z.decode(W,{stream:!0})}X+=z.decode()}finally{try{await Z.cancel()}catch{}Z.releaseLock()}return X}async function R($,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([c1(Z.stdout),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 S8($,Q={}){let Z=await R($,Q);if(Z.exitCode!==0)throw new Z1(`command failed (${Z.exitCode}): ${$.join(" ")}`,Z.exitCode,Z.stdout,Z.stderr);return Z}async function u($){let Q=D8($),Z=await R(["sh","-c",`command -v ${Q}`],{timeoutMs:5000});if(Z.exitCode===0)return Z.stdout.trim()||null;return null}function D8($){if(!/^[A-Za-z0-9._/-]+$/.test($))throw Error(`refused to shell-escape suspect token: ${$}`);return $}async function C8($,Q="--version"){if(!await u($))return null;let z=await R([$,Q],{timeoutMs:5000});if(z.exitCode!==0)return null;return((z.stdout||z.stderr).split(/\r?\n/)[0]?.trim()??"")||null}var u1=16777216,Z1;var o=P(()=>{Z1=class Z1 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 r($){return b8?"":$}var b8,M,S,_,tZ,L,k,y,J;var p=P(()=>{b8=(process.env.NO_COLOR??"").length>0;M=r("\x1B[0;31m"),S=r("\x1B[0;32m"),_=r("\x1B[1;33m"),tZ=r("\x1B[0;34m"),L=r("\x1B[0;36m"),k=r("\x1B[1m"),y=r("\x1B[2m"),J=r("\x1B[0m")});import{existsSync as l8}from"fs";async function Z$(){if(A$!==void 0)return A$;let $="/opt/homebrew/bin/python3.12";if(l8($))return A$=$,$;let Q=await u("python3.12");if(Q)return A$=Q,Q;let Z=await u("python3");return A$=Z,Z}async function z$($,Q={}){let Z=await Z$();if(!Z)return{stdout:"",stderr:"python3 not found",exitCode:127};return R([Z,"-c",$],Q)}var A$;var V$=P(()=>{o()});var V0={};b(V0,{runStatus:()=>U3});import{existsSync as v,readFileSync as H$,readdirSync as $0,statSync as Q0}from"fs";import{resolve as D,basename as z3}from"path";import{homedir as X3}from"os";function Z0($){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 z0($,Q,Z){if(Q===0)return null;let z=Math.trunc($*100/Q),X=Math.trunc($*C$/Q);if(X>C$)X=C$;let q=C$-X,K=S;if(z>=80)K=M;else if(z>=50)K=_;let W="=".repeat(Math.max(0,X))+" ".repeat(Math.max(0,q)),V=Z0($),H=Z0(Q);return` ${k}${Z}${J} ${K}[${W}]${J} ${z}% (${V} / ${H})`}async function q3(){if(await u("jq"))return!0;return process.stdout.write(`${M}Error: jq is required but not installed.${J}
|
|
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)
|
|
@@ -814,4 +814,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
814
814
|
`),2}default:return process.stderr.write(`Unknown command: ${Q}
|
|
815
815
|
`),process.stderr.write(K8),2}}e1();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var SZ=await NZ(Bun.argv.slice(2));process.exit(SZ);
|
|
816
816
|
|
|
817
|
-
//# debugId=
|
|
817
|
+
//# debugId=18E173D8ADB8057464756E2164756E21
|
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": "7.
|
|
4
|
+
"version": "7.121.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.121.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",
|