loki-mode 8.68.0 → 8.69.0
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/cost-summary.py +308 -0
- package/autonomy/lib/efficiency_cost.py +46 -6
- 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 v8.
|
|
6
|
+
# Loki Mode v8.69.0
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -469,4 +469,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
469
469
|
|
|
470
470
|
---
|
|
471
471
|
|
|
472
|
-
**v8.
|
|
472
|
+
**v8.69.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
8.
|
|
1
|
+
8.69.0
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Per-run cost summary: what this run spent, and what we failed to measure.
|
|
3
|
+
|
|
4
|
+
WHY THIS EXISTS. The engine writes .loki/metrics/efficiency/iteration-N.json
|
|
5
|
+
every iteration, and the only ways to read it back are a dashboard HTTP
|
|
6
|
+
endpoint and a prompt-injection block. Neither answers the question an operator
|
|
7
|
+
actually asks between runs: what did that cost, is it climbing, and is the
|
|
8
|
+
cache working. Cache hit ratio is the number that moves cost most -- a run
|
|
9
|
+
reading 90% from cache costs roughly a tenth of the same run reading fresh --
|
|
10
|
+
and nothing on the CLI surfaced it.
|
|
11
|
+
|
|
12
|
+
THE HONESTY RULE THIS INHERITS. Unmeasured is not free. That confusion shipped
|
|
13
|
+
to a user on four separate surfaces (v8.51.0 through v8.54.0: the codex
|
|
14
|
+
dispatch recorded no tokens, the receipt said {"usd": 0.0, "available": true},
|
|
15
|
+
the PROMPT said "$0.00" per iteration, and the verifier never checked). Each
|
|
16
|
+
was fixed in isolation. This is a fifth surface reading the same records, so it
|
|
17
|
+
obeys the same rule, by importing the same predicate rather than restating it:
|
|
18
|
+
|
|
19
|
+
- An unmeasured iteration prints UNKNOWN and is EXCLUDED from totals. Adding
|
|
20
|
+
it as zero would be indistinguishable from a real measurement of zero.
|
|
21
|
+
- When NO iteration is measured, the total reads UNKNOWN, never $0.00.
|
|
22
|
+
- measured/exists is always stated, so a partial measurement can never be
|
|
23
|
+
mistaken for a complete one. Half the iterations measured is half a number,
|
|
24
|
+
and a total presented without that ratio silently claims to be whole.
|
|
25
|
+
- A ratio over a zero denominator is UNKNOWN, not 0%. "The cache missed
|
|
26
|
+
everything" is an expensive, actionable claim; we have not earned it.
|
|
27
|
+
- A trend needs two measured points. "Flat" from one point is fabrication of
|
|
28
|
+
the same family.
|
|
29
|
+
|
|
30
|
+
Definitions, stated because an unstated denominator is its own dishonesty:
|
|
31
|
+
cache hit ratio = cache_read / (input_tokens + cache_read)
|
|
32
|
+
i.e. share of everything read IN that came from cache. Same denominator as
|
|
33
|
+
iteration_attribution.prompt_block, so both surfaces report one number.
|
|
34
|
+
measured = the record carries a non-zero cost or token count
|
|
35
|
+
(efficiency_cost.record_is_measured -- the single definition).
|
|
36
|
+
|
|
37
|
+
Usage:
|
|
38
|
+
python3 autonomy/lib/cost-summary.py [WORKSPACE] [--json]
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
import argparse
|
|
44
|
+
import importlib.util
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import sys
|
|
48
|
+
|
|
49
|
+
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
50
|
+
if _HERE not in sys.path:
|
|
51
|
+
sys.path.insert(0, _HERE)
|
|
52
|
+
|
|
53
|
+
from efficiency_cost import record_is_measured # noqa: E402
|
|
54
|
+
|
|
55
|
+
# iteration_attribution.py already reads and sorts the efficiency dir, skipping
|
|
56
|
+
# malformed records. Importing it keeps ONE reader of that directory; a third
|
|
57
|
+
# copy would drift the same way a second honesty predicate would.
|
|
58
|
+
_ia_spec = importlib.util.spec_from_file_location(
|
|
59
|
+
"iteration_attribution", os.path.join(_HERE, "iteration_attribution.py"))
|
|
60
|
+
_ia = importlib.util.module_from_spec(_ia_spec)
|
|
61
|
+
_ia_spec.loader.exec_module(_ia)
|
|
62
|
+
|
|
63
|
+
TOKEN_FIELDS = (
|
|
64
|
+
"input_tokens",
|
|
65
|
+
"output_tokens",
|
|
66
|
+
"cache_read_tokens",
|
|
67
|
+
"cache_creation_tokens",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _num(v):
|
|
72
|
+
"""Non-bool int/float, else None. Never coerces junk to 0."""
|
|
73
|
+
if isinstance(v, bool) or not isinstance(v, (int, float)):
|
|
74
|
+
return None
|
|
75
|
+
return v
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _count_iteration_files(loki_dir):
|
|
79
|
+
"""How many iteration-*.json files EXIST, parseable or not.
|
|
80
|
+
|
|
81
|
+
A corrupt or partial record is skipped by the reader but still happened, so
|
|
82
|
+
it counts toward "exists". Dropping it from the denominator would shrink a
|
|
83
|
+
partial run into a complete-looking one -- exactly the mistake the
|
|
84
|
+
measured/exists ratio is here to prevent.
|
|
85
|
+
"""
|
|
86
|
+
eff_dir = os.path.join(loki_dir, "metrics", "efficiency")
|
|
87
|
+
try:
|
|
88
|
+
names = os.listdir(eff_dir)
|
|
89
|
+
except OSError:
|
|
90
|
+
return 0
|
|
91
|
+
return sum(
|
|
92
|
+
1 for n in names if n.startswith("iteration-") and n.endswith(".json"))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _ratio(cache_read, fresh_input):
|
|
96
|
+
"""cache_read / (fresh_input + cache_read), or None when nothing was read."""
|
|
97
|
+
denom = fresh_input + cache_read
|
|
98
|
+
if denom <= 0:
|
|
99
|
+
return None
|
|
100
|
+
return round(cache_read / denom, 4)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def summarize(workspace="."):
|
|
104
|
+
"""Build the summary dict. Pure derivation, no guessing."""
|
|
105
|
+
loki_dir = os.path.join(workspace, ".loki")
|
|
106
|
+
recs = _ia._iteration_records(loki_dir)
|
|
107
|
+
exists = max(_count_iteration_files(loki_dir), len(recs))
|
|
108
|
+
|
|
109
|
+
iterations = []
|
|
110
|
+
totals = dict.fromkeys(TOKEN_FIELDS, 0)
|
|
111
|
+
total_usd = 0.0
|
|
112
|
+
measured = 0
|
|
113
|
+
cost_points = [] # (iteration, usd) for measured costs only, for the trend
|
|
114
|
+
|
|
115
|
+
for rec in recs:
|
|
116
|
+
it = rec.get("iteration", "?")
|
|
117
|
+
is_measured = record_is_measured(rec)
|
|
118
|
+
row = {"iteration": it, "measured": is_measured}
|
|
119
|
+
|
|
120
|
+
if not is_measured:
|
|
121
|
+
# EXCLUDED from totals, not added as zero. These two produce an
|
|
122
|
+
# identical total on a partial run, which is why the measured/exists
|
|
123
|
+
# count below (not the total) is what proves the difference.
|
|
124
|
+
row["cost_usd"] = None
|
|
125
|
+
for f in TOKEN_FIELDS:
|
|
126
|
+
row[f] = None
|
|
127
|
+
row["cache_hit_ratio"] = None
|
|
128
|
+
iterations.append(row)
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
measured += 1
|
|
132
|
+
usd = _num(rec.get("cost_usd"))
|
|
133
|
+
# UNPRICED IS NOT FREE. record_is_measured() is field-agnostic: a record
|
|
134
|
+
# with real tokens but cost_usd 0 is "measured" on the strength of its
|
|
135
|
+
# tokens, and the cost slot would then render $0.0000 -- the headline
|
|
136
|
+
# rule inverted, on a shape that actually ships. Codex tiers can record
|
|
137
|
+
# tokens with no priced cost, and the real FireLater records wrote an
|
|
138
|
+
# explicit "cost_usd": 0 rather than omitting the key.
|
|
139
|
+
#
|
|
140
|
+
# An exact zero is a reliable unpriced signal because real costs are
|
|
141
|
+
# stored raw (0.018719), so a sub-cent charge is 0.0001, never 0. Same
|
|
142
|
+
# truthiness rule record_is_measured applies to the aggregate, applied
|
|
143
|
+
# one level down. Tokens still render; only cost reads UNKNOWN.
|
|
144
|
+
if usd == 0:
|
|
145
|
+
usd = None
|
|
146
|
+
row["cost_usd"] = usd
|
|
147
|
+
if usd is not None:
|
|
148
|
+
total_usd += float(usd)
|
|
149
|
+
cost_points.append((it, float(usd)))
|
|
150
|
+
for f in TOKEN_FIELDS:
|
|
151
|
+
v = _num(rec.get(f))
|
|
152
|
+
row[f] = v
|
|
153
|
+
if v is not None:
|
|
154
|
+
totals[f] += int(v)
|
|
155
|
+
row["cache_hit_ratio"] = _ratio(
|
|
156
|
+
row.get("cache_read_tokens") or 0, row.get("input_tokens") or 0)
|
|
157
|
+
iterations.append(row)
|
|
158
|
+
|
|
159
|
+
# A measured iteration can still carry tokens but no cost (an unpriced
|
|
160
|
+
# model). Cost totals therefore key on cost_points, not on `measured`.
|
|
161
|
+
have_cost = bool(cost_points)
|
|
162
|
+
|
|
163
|
+
out = {
|
|
164
|
+
"workspace": os.path.abspath(workspace),
|
|
165
|
+
"iterations_found": exists,
|
|
166
|
+
"iterations_measured": measured,
|
|
167
|
+
"fully_measured": exists > 0 and measured == exists,
|
|
168
|
+
"total_cost_usd": round(total_usd, 4) if have_cost else None,
|
|
169
|
+
"cost_iterations_counted": len(cost_points),
|
|
170
|
+
"avg_cost_per_iteration": (
|
|
171
|
+
round(total_usd / len(cost_points), 4) if have_cost else None),
|
|
172
|
+
"cache_hit_ratio": _ratio(
|
|
173
|
+
totals["cache_read_tokens"], totals["input_tokens"]),
|
|
174
|
+
"cost_trend": _trend(cost_points),
|
|
175
|
+
"iterations": iterations,
|
|
176
|
+
"notes": [],
|
|
177
|
+
}
|
|
178
|
+
for f in TOKEN_FIELDS:
|
|
179
|
+
out["total_" + f] = totals[f] if measured else None
|
|
180
|
+
|
|
181
|
+
n = out["notes"]
|
|
182
|
+
if exists == 0:
|
|
183
|
+
n.append("no efficiency records found: nothing to summarize")
|
|
184
|
+
if not have_cost:
|
|
185
|
+
n.append(
|
|
186
|
+
"cost not measured for any iteration: total reads UNKNOWN, "
|
|
187
|
+
"not $0.00 (unmeasured is not free)")
|
|
188
|
+
if measured:
|
|
189
|
+
n.append(
|
|
190
|
+
"tokens WERE recorded but no cost was: the model is likely "
|
|
191
|
+
"unpriced, so spend is unknown rather than zero")
|
|
192
|
+
elif len(cost_points) < measured:
|
|
193
|
+
n.append(
|
|
194
|
+
"%d of %d measured iterations carried tokens but no cost "
|
|
195
|
+
"(unpriced model): the cost total excludes them"
|
|
196
|
+
% (measured - len(cost_points), measured))
|
|
197
|
+
elif measured < exists:
|
|
198
|
+
n.append(
|
|
199
|
+
"PARTIAL: %d of %d iterations measured. The total covers only the "
|
|
200
|
+
"measured ones; unmeasured iterations are excluded, not counted as "
|
|
201
|
+
"zero, so the real cost is HIGHER than shown."
|
|
202
|
+
% (measured, exists))
|
|
203
|
+
if out["cache_hit_ratio"] is None and measured:
|
|
204
|
+
n.append(
|
|
205
|
+
"cache hit ratio UNKNOWN: no input or cache-read tokens recorded "
|
|
206
|
+
"(a 0% ratio would claim a cold cache we did not observe)")
|
|
207
|
+
return out
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _trend(points):
|
|
211
|
+
"""Is cost per iteration climbing? Needs two measured points to say.
|
|
212
|
+
|
|
213
|
+
Compares the mean of the first half against the mean of the second half.
|
|
214
|
+
Deliberately coarse: the useful signal is direction, and a regression slope
|
|
215
|
+
over four noisy points would look more precise than it is.
|
|
216
|
+
"""
|
|
217
|
+
if len(points) < 2:
|
|
218
|
+
return {
|
|
219
|
+
"direction": "unknown",
|
|
220
|
+
"points": len(points),
|
|
221
|
+
"detail": "need at least 2 measured iterations to compare",
|
|
222
|
+
}
|
|
223
|
+
vals = [v for _, v in points]
|
|
224
|
+
half = len(vals) // 2
|
|
225
|
+
first = sum(vals[:half]) / half
|
|
226
|
+
second = sum(vals[half:]) / len(vals[half:])
|
|
227
|
+
if first <= 0:
|
|
228
|
+
direction = "unknown"
|
|
229
|
+
elif second > first * 1.1:
|
|
230
|
+
direction = "climbing"
|
|
231
|
+
elif second < first * 0.9:
|
|
232
|
+
direction = "falling"
|
|
233
|
+
else:
|
|
234
|
+
direction = "flat"
|
|
235
|
+
return {
|
|
236
|
+
"direction": direction,
|
|
237
|
+
"points": len(vals),
|
|
238
|
+
"first_half_avg_usd": round(first, 4),
|
|
239
|
+
"second_half_avg_usd": round(second, 4),
|
|
240
|
+
"detail": "mean of first half vs second half of measured iterations",
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
UNKNOWN = "UNKNOWN"
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _usd(v):
|
|
248
|
+
return UNKNOWN if v is None else "$%.4f" % v
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _pct(v):
|
|
252
|
+
return UNKNOWN if v is None else "%.1f%%" % (v * 100)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _tok(v):
|
|
256
|
+
return UNKNOWN if v is None else "{:,}".format(v)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def render(s):
|
|
260
|
+
L = ["Cost summary", "============", "", "Workspace: " + s["workspace"], ""]
|
|
261
|
+
L.append("Iterations: %d found, %d measured%s" % (
|
|
262
|
+
s["iterations_found"], s["iterations_measured"],
|
|
263
|
+
"" if s["fully_measured"] else " <- PARTIAL" if s["iterations_found"]
|
|
264
|
+
else ""))
|
|
265
|
+
L.append("Total cost: " + _usd(s["total_cost_usd"]))
|
|
266
|
+
L.append("Avg / iter: " + _usd(s["avg_cost_per_iteration"]))
|
|
267
|
+
L.append("Cache ratio: " + _pct(s["cache_hit_ratio"])
|
|
268
|
+
+ " (cache_read / (input + cache_read))")
|
|
269
|
+
t = s["cost_trend"]
|
|
270
|
+
L.append("Cost trend: %s (%d measured point(s))" % (
|
|
271
|
+
t["direction"].upper(), t["points"]))
|
|
272
|
+
L.append("")
|
|
273
|
+
L.append("Tokens:")
|
|
274
|
+
L.append(" input: " + _tok(s["total_input_tokens"]))
|
|
275
|
+
L.append(" output: " + _tok(s["total_output_tokens"]))
|
|
276
|
+
L.append(" cache read: " + _tok(s["total_cache_read_tokens"]))
|
|
277
|
+
L.append(" cache creation: " + _tok(s["total_cache_creation_tokens"]))
|
|
278
|
+
|
|
279
|
+
if s["iterations"]:
|
|
280
|
+
L += ["", "Per iteration:"]
|
|
281
|
+
for r in s["iterations"]:
|
|
282
|
+
if not r["measured"]:
|
|
283
|
+
L.append(" iter %s: %s (excluded from totals)"
|
|
284
|
+
% (r["iteration"], UNKNOWN))
|
|
285
|
+
continue
|
|
286
|
+
L.append(" iter %s: %s, in %s, out %s, cache %s" % (
|
|
287
|
+
r["iteration"], _usd(r["cost_usd"]), _tok(r["input_tokens"]),
|
|
288
|
+
_tok(r["output_tokens"]), _pct(r["cache_hit_ratio"])))
|
|
289
|
+
if s["notes"]:
|
|
290
|
+
L.append("")
|
|
291
|
+
for note in s["notes"]:
|
|
292
|
+
L.append("note: " + note)
|
|
293
|
+
return "\n".join(L)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def main(argv=None):
|
|
297
|
+
ap = argparse.ArgumentParser(description="Per-run cost summary for a Loki workspace.")
|
|
298
|
+
ap.add_argument("workspace", nargs="?", default=".",
|
|
299
|
+
help="workspace containing .loki/ (default: .)")
|
|
300
|
+
ap.add_argument("--json", action="store_true", help="machine-readable output")
|
|
301
|
+
args = ap.parse_args(argv)
|
|
302
|
+
s = summarize(args.workspace)
|
|
303
|
+
print(json.dumps(s, indent=2) if args.json else render(s))
|
|
304
|
+
return 0
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
if __name__ == "__main__":
|
|
308
|
+
sys.exit(main())
|
|
@@ -28,6 +28,7 @@ import os
|
|
|
28
28
|
|
|
29
29
|
__all__ = [
|
|
30
30
|
"collect_efficiency",
|
|
31
|
+
"record_is_measured",
|
|
31
32
|
"load_prices",
|
|
32
33
|
"price_from_tokens",
|
|
33
34
|
"DEFAULT_PRICES_PATH",
|
|
@@ -68,6 +69,40 @@ def _to_float(v, default=0.0):
|
|
|
68
69
|
return default
|
|
69
70
|
|
|
70
71
|
|
|
72
|
+
_MEASURED_FIELDS = (
|
|
73
|
+
"cost_usd",
|
|
74
|
+
"input_tokens",
|
|
75
|
+
"output_tokens",
|
|
76
|
+
"cache_read_tokens",
|
|
77
|
+
"cache_creation_tokens",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def record_is_measured(rec):
|
|
82
|
+
"""True when ONE efficiency record actually carries an observed value.
|
|
83
|
+
|
|
84
|
+
THE SINGLE DEFINITION of "measured" for a per-iteration record. collect_
|
|
85
|
+
efficiency() applies the same rule to the SUM; anything wanting the rule
|
|
86
|
+
per iteration (cost-summary.py) must import this rather than restate it.
|
|
87
|
+
A second copy of this predicate is how the honesty rule drifts: the four
|
|
88
|
+
surfaces that once rendered an unmeasured run as "$0.00" each had their
|
|
89
|
+
own idea of what counted as measured.
|
|
90
|
+
|
|
91
|
+
A present file is not a measurement. A run that did work necessarily
|
|
92
|
+
consumed tokens, so all-zeros means we FAILED TO MEASURE, and unmeasured
|
|
93
|
+
must read as unknown, never as free.
|
|
94
|
+
"""
|
|
95
|
+
if not isinstance(rec, dict):
|
|
96
|
+
return False
|
|
97
|
+
for key in _MEASURED_FIELDS:
|
|
98
|
+
v = rec.get(key)
|
|
99
|
+
if isinstance(v, bool):
|
|
100
|
+
continue
|
|
101
|
+
if isinstance(v, (int, float)) and v:
|
|
102
|
+
return True
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
|
|
71
106
|
# ---------------------------------------------------------------------------
|
|
72
107
|
# efficiency collection (extracted verbatim from proof-generator.py)
|
|
73
108
|
# ---------------------------------------------------------------------------
|
|
@@ -125,12 +160,17 @@ def collect_efficiency(loki_dir):
|
|
|
125
160
|
# at least one record carried a non-zero token count or cost -- an OBSERVED
|
|
126
161
|
# value, not a present file. Zero everywhere means we failed to measure, and
|
|
127
162
|
# unmeasured must read as unknown.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
#
|
|
164
|
+
# Applied through record_is_measured() so the aggregate rule and the
|
|
165
|
+
# per-iteration rule are literally the same code (see that docstring).
|
|
166
|
+
# The cost dict keys "usd" where a record says "cost_usd", so map across.
|
|
167
|
+
_observed = record_is_measured({
|
|
168
|
+
"cost_usd": cost["usd"],
|
|
169
|
+
"input_tokens": cost["input_tokens"],
|
|
170
|
+
"output_tokens": cost["output_tokens"],
|
|
171
|
+
"cache_read_tokens": cost["cache_read_tokens"],
|
|
172
|
+
"cache_creation_tokens": cost["cache_creation_tokens"],
|
|
173
|
+
})
|
|
134
174
|
if collected and _observed:
|
|
135
175
|
# Round usd to a sane precision but keep it precise (anti-pattern:
|
|
136
176
|
# round suspiciously-clean numbers). 4 decimals preserves odd values.
|
package/dashboard/__init__.py
CHANGED
package/loki-ts/dist/loki.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var m_=Object.create;var{getPrototypeOf:u_,defineProperty:eK,getOwnPropertyNames:p_}=Object;var d_=Object.prototype.hasOwnProperty;function c_(Z){return this[Z]}var l_,i_,a_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?l_??=new WeakMap:i_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?m_(u_(Z)):{};let K=X||!Z||!Z.__esModule?eK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of p_(Z))if(!d_.call(K,$))eK(K,$,{get:c_.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 s_=(Z)=>Z;function n_(Z,X){this[Z]=s_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)eK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:n_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var e0=import.meta.require;var kO={};l0(kO,{lokiDir:()=>j0,homeLokiDir:()=>R4,findRepoRootForVersion:()=>X$,REPO_ROOT:()=>i0});import{resolve as n7,dirname as Z$}from"path";import{fileURLToPath as o_}from"url";import{existsSync as UQ}from"fs";import{homedir as r_}from"os";function t_(){let Z=RO;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(RO,"..","..","..")}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(r_(),".loki")}var RO,i0;var H8=p(()=>{RO=Z$(o_(import.meta.url));i0=t_()});import{readFileSync as e_}from"fs";import{resolve as Zf,dirname as Xf}from"path";import{fileURLToPath as Qf}from"url";function h3(){if(h5!==null)return h5;let Z="8.
|
|
2
|
+
var m_=Object.create;var{getPrototypeOf:u_,defineProperty:eK,getOwnPropertyNames:p_}=Object;var d_=Object.prototype.hasOwnProperty;function c_(Z){return this[Z]}var l_,i_,a_=(Z,X,Q)=>{var Y=Z!=null&&typeof Z==="object";if(Y){var J=X?l_??=new WeakMap:i_??=new WeakMap,z=J.get(Z);if(z)return z}Q=Z!=null?m_(u_(Z)):{};let K=X||!Z||!Z.__esModule?eK(Q,"default",{value:Z,enumerable:!0}):Q;for(let $ of p_(Z))if(!d_.call(K,$))eK(K,$,{get:c_.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 s_=(Z)=>Z;function n_(Z,X){this[Z]=s_.bind(null,X)}var l0=(Z,X)=>{for(var Q in X)eK(Z,Q,{get:X[Q],enumerable:!0,configurable:!0,set:n_.bind(X,Q)})};var p=(Z,X)=>()=>(Z&&(X=Z(Z=0)),X);var e0=import.meta.require;var kO={};l0(kO,{lokiDir:()=>j0,homeLokiDir:()=>R4,findRepoRootForVersion:()=>X$,REPO_ROOT:()=>i0});import{resolve as n7,dirname as Z$}from"path";import{fileURLToPath as o_}from"url";import{existsSync as UQ}from"fs";import{homedir as r_}from"os";function t_(){let Z=RO;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(RO,"..","..","..")}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(r_(),".loki")}var RO,i0;var H8=p(()=>{RO=Z$(o_(import.meta.url));i0=t_()});import{readFileSync as e_}from"fs";import{resolve as Zf,dirname as Xf}from"path";import{fileURLToPath as Qf}from"url";function h3(){if(h5!==null)return h5;let Z="8.69.0";if(typeof Z==="string"&&Z.length>0)return h5=Z,h5;try{let X=Xf(Qf(import.meta.url)),Q=X$(X);h5=e_(Zf(Q,"VERSION"),"utf-8").trim()}catch{h5="unknown"}return h5}var h5=null;var BQ=p(()=>{H8()});var bO={};l0(bO,{runOrThrow:()=>jf,run:()=>E0,readStreamCapped:()=>NQ,commandVersion:()=>Tf,commandExists:()=>X9,ShellError:()=>Q$,MAX_STDOUT_BYTES:()=>yO});async function NQ(Z,X=yO){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 W=$.byteLength-(z-X);J+=Y.decode($.subarray(0,W),{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 jf(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=Mf(Z),Q=await E0(["sh","-c",`command -v ${X}`],{timeoutMs:5000});if(Q.exitCode===0)return Q.stdout.trim()||null;return null}function Mf(Z){if(!/^[A-Za-z0-9._/-]+$/.test(Z))throw Error(`refused to shell-escape suspect token: ${Z}`);return Z}async function Tf(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 yO=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 wf?"":Z}var wf,L0,F8,p0,YV0,a0,W8,Q9,v;var S6=p(()=>{wf=(process.env.NO_COLOR??"").length>0;L0=o7("\x1B[0;31m"),F8=o7("\x1B[0;32m"),p0=o7("\x1B[1;33m"),YV0=o7("\x1B[0;34m"),a0=o7("\x1B[0;36m"),W8=o7("\x1B[1m"),Q9=o7("\x1B[2m"),v=o7("\x1B[0m")});import{existsSync as bf}from"fs";async function E7(){if(x4!==void 0)return x4;let Z="/opt/homebrew/bin/python3.12";if(bf(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 ZL={};l0(ZL,{runStatus:()=>Kh});import{existsSync as Y9,readFileSync as g3,readdirSync as iO,statSync as aO}from"fs";import{resolve as h8,basename as rf}from"path";import{homedir as tf}from"os";function sO(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 nO(Z,X,Q){if(X===0)return null;let Y=Math.trunc(Z*100/X),J=Math.trunc(Z*LQ/X);if(J>LQ)J=LQ;let z=LQ-J,K=F8;if(Y>=80)K=L0;else if(Y>=50)K=p0;let $="=".repeat(Math.max(0,J))+" ".repeat(Math.max(0,z)),W=sO(Z),V=sO(X);return` ${W8}${Q}${v} ${K}[${$}]${v} ${Y}% (${W} / ${V})`}async function Zh(){if(await X9("jq"))return!0;return process.stdout.write(`${L0}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)
|
|
@@ -1232,4 +1232,4 @@ Set LOKI_LEGACY_BASH=1 to force the bash CLI for every command.
|
|
|
1232
1232
|
`),2}case"start":{let{runStart:Y}=await Promise.resolve().then(() => (h_(),f_));return Y(Q)}default:return process.stderr.write(`Unknown command: ${X}
|
|
1233
1233
|
`),process.stderr.write(v_),2}}cO();process.on("SIGINT",()=>process.exit(130));process.on("SIGTERM",()=>process.exit(143));var gW0=await vW0(Bun.argv.slice(2));process.exit(gW0);
|
|
1234
1234
|
|
|
1235
|
-
//# debugId=
|
|
1235
|
+
//# debugId=EA083A7FA264315B64756E2164756E21
|
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": "8.
|
|
4
|
+
"version": "8.69.0",
|
|
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": "8.
|
|
5
|
+
"version": "8.69.0",
|
|
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",
|