loki-mode 11.77.0 → 11.79.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 +3 -3
- package/VERSION +1 -1
- package/autonomy/lib/cockpit-render.sh +1 -1
- package/autonomy/lib/proof-generator.py +48 -0
- package/autonomy/lib/proof-pr.sh +13 -0
- package/autonomy/lib/telemetry-schema.json +4 -0
- package/autonomy/loki +280 -13
- package/autonomy/run.sh +126 -8
- package/autonomy/telemetry.sh +13 -0
- package/autonomy/verify.sh +2 -2
- package/bin/loki +17 -12
- package/completions/_loki +24 -0
- package/completions/loki.bash +22 -1
- package/dashboard/__init__.py +1 -1
- package/docs/{v10/CLI-MODERN.md → CLI-INVENTORY.md} +9 -8
- package/docs/CLI-REFERENCE.md +3 -2
- package/docs/INSTALLATION.md +1 -1
- package/docs/PRIVACY.md +1 -0
- package/docs/dev/architecture-reference.md +3 -2
- package/docs/dev/project-structure.md +2 -1
- package/loki-ts/data/child-env.json +1 -0
- package/loki-ts/dist/loki.js +1657 -1645
- package/loki-ts/package.json +2 -2
- package/mcp/__init__.py +1 -1
- package/mcp/requirements.txt +1 -1
- package/package.json +3 -3
- package/packages/control-plane/dist/server.js +19608 -19067
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/quality-gates.md +6 -0
- package/web-app/dist/assets/{AdminPage-DrlEpTKN.js → AdminPage-Bph4VGNm.js} +1 -1
- package/web-app/dist/assets/{Avatar-CJ9juG_K.js → Avatar-Btrppoor.js} +1 -1
- package/web-app/dist/assets/{Badge-DcttGvjK.js → Badge-fJFe3CEH.js} +1 -1
- package/web-app/dist/assets/{Button-b5O9YocE.js → Button-B_cYY4Me.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-Bx6KwmVB.js → CockpitPage-26oBcuYy.js} +1 -1
- package/web-app/dist/assets/{ComparePage-BaevXGrD.js → ComparePage-CSuQZUm2.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-2sZmLblz.js → ErrorBoundary-Cf7DQMz0.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-C2XY1fMD.js → EvidenceReceiptPanel-BMZszYBV.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DwHuq31X.js → GitHubIssuesPanel-Bt5dkJwW.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BQMn-YnD.js → GitHubPRsPanel-CL4NbWJ3.js} +1 -1
- package/web-app/dist/assets/{HomePage-Dpo55VLr.js → HomePage-Bqugxf45.js} +2 -2
- package/web-app/dist/assets/{LoginPage-Dfnw1PTO.js → LoginPage-ENM9QyG8.js} +1 -1
- package/web-app/dist/assets/{MagicPage-DoR3K8ef.js → MagicPage-D-7g-mew.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-Brf8VAOe.js → MetricsPage-CelqK13G.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CepMv0TW.js → NotFoundPage-EQ3Aq4Xg.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-DKGi8lof.js → ProjectPage-D3Jls_Oh.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-BxPCmo7e.js → ProjectsPage-CgGRkJ3X.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-CriniWvU.js → SettingsPage-B79K8xg2.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-B2PV4GFW.js → ShowcasePage-DxSsaGGx.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-CQUapqwh.js → SystemSettingsPage-CYNKB-5I.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BkLyYavH.js → TeamsPage-Bit01iEC.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-CeU_rnJk.js → TemplatesPage-DQ3fPi7x.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-oLOjcbqL.js → TerminalOutput-By137hB-.js} +1 -1
- package/web-app/dist/assets/{activity-DEHJPfgq.js → activity-rbRWrIjS.js} +1 -1
- package/web-app/dist/assets/{bell-BmXaknK2.js → bell-DIiSalJU.js} +1 -1
- package/web-app/dist/assets/{bot-B-JWrH_p.js → bot-BQyrbJaf.js} +1 -1
- package/web-app/dist/assets/{check-BaspF93s.js → check-BJ_PqlU2.js} +1 -1
- package/web-app/dist/assets/{chevron-left-M6VCet0K.js → chevron-left-_QsMbFBT.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DVLpodrK.js → circle-alert-DEovFrER.js} +1 -1
- package/web-app/dist/assets/{clock-BRy8KpHK.js → clock-Dmrl0gvN.js} +1 -1
- package/web-app/dist/assets/{cloud-jTlY1qAB.js → cloud-Bs2a7FCR.js} +1 -1
- package/web-app/dist/assets/{code-xml-73Xelkkd.js → code-xml-D2WdclgQ.js} +1 -1
- package/web-app/dist/assets/{database-DpMqKrsA.js → database-kbYbPMS5.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-Bcw89Epb.js → dollar-sign-BoOAvw6B.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-5Drrk2XB.js → file-code-corner-ew5NSFtR.js} +1 -1
- package/web-app/dist/assets/{file-plus-D1PmiWXN.js → file-plus-CNV2nXNx.js} +1 -1
- package/web-app/dist/assets/{globe-Dq8rgYwT.js → globe-CZo7LjYX.js} +1 -1
- package/web-app/dist/assets/{hammer-Duyrm7AU.js → hammer-BedES-IA.js} +1 -1
- package/web-app/dist/assets/{index-Dg4Wvsep.js → index-C_uRanCX.js} +3 -3
- package/web-app/dist/assets/{layers-zbNbb_Hz.js → layers-B-OfB6O0.js} +1 -1
- package/web-app/dist/assets/{loader-circle-vOKp3Yno.js → loader-circle-C8hx3EE8.js} +1 -1
- package/web-app/dist/assets/{lock-AsdJPlwj.js → lock-Cyi_Tc3f.js} +1 -1
- package/web-app/dist/assets/{package-Dt8-zN2y.js → package-Cfps-Gdr.js} +1 -1
- package/web-app/dist/assets/{plus-md8Hlau_.js → plus-CjSNRaN2.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-BSTU8fSw.js → refresh-cw-pEhkaiQh.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-D5Wn5NCl.js → rotate-ccw-twl6HuT5.js} +1 -1
- package/web-app/dist/assets/{scroll-text-ChmR_jcw.js → scroll-text-B9XWtVcd.js} +1 -1
- package/web-app/dist/assets/{server-NY1F729P.js → server-DeBbwC8T.js} +1 -1
- package/web-app/dist/assets/{shield-alert-yhSn1RwF.js → shield-alert-BGQQ_YfF.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-GEJKgG1X.js → thumbs-up-Dy9nEa04.js} +1 -1
- package/web-app/dist/assets/{trash-2-elTR7osS.js → trash-2-DIftKB8Y.js} +1 -1
- package/web-app/dist/assets/{trending-down-ptCvIKeg.js → trending-down-Cjh8EsVU.js} +1 -1
- package/web-app/dist/assets/{trending-up-DbdbNpEl.js → trending-up-xWyMrayW.js} +1 -1
- package/web-app/dist/assets/{upload-D61Rwawf.js → upload-Bh3moncl.js} +1 -1
- package/web-app/dist/assets/{usePolling-DNF7DO1q.js → usePolling-C2sF90ff.js} +1 -1
- package/web-app/dist/assets/{user-BEYk6Un_.js → user-CtBrnwfC.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Runs an autonomous software factory that knows what it is supposed to deliver and proves it did, with a signed receipt. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v11.
|
|
6
|
+
# Loki Mode v11.79.0
|
|
7
7
|
|
|
8
8
|
**Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
|
|
9
9
|
|
|
@@ -227,7 +227,7 @@ Pick modules from this table, one hop from here. Each module over 100 lines open
|
|
|
227
227
|
<!-- generated:commands -->
|
|
228
228
|
Commands (generated from `loki-ts/src/cli/registry.ts`; full reference in `docs/CLI-REFERENCE.md`):
|
|
229
229
|
|
|
230
|
-
`version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `backup`, `restore`, `proof`, `wiki`, `control`, `runs`, `intent`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `issues`, `undo`, `verify-pr`, `answer`, `engine10`, `help`, `chat`, `edit`, `diff`, `models`, `setup`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `cancel`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `wow`, `factory`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`, `steer`
|
|
230
|
+
`version`, `status`, `doctor`, `provider`, `memory`, `rollback`, `backup`, `restore`, `proof`, `wiki`, `control`, `runs`, `intent`, `kpis`, `report`, `trust`, `crash`, `contract`, `start`, `slack`, `queue`, `issues`, `undo`, `verify-pr`, `answer`, `engine10`, `help`, `chat`, `edit`, `diff`, `models`, `setup`, `quick`, `quickstart`, `init`, `template`, `verify`, `keys`, `review`, `dashboard`, `config`, `mcp`, `acp`, `stop`, `pause`, `resume`, `cancel`, `why`, `next`, `logs`, `ship`, `deploy`, `import`, `github`, `issue`, `ci`, `modernize`, `share`, `assets`, `export`, `notify`, `tour`, `wow`, `try`, `factory`, `welcome`, `onboard`, `setup-skill`, `self-update`, `remote`, `cockpit`, `code`, `context`, `secrets`, `api`, `sandbox`, `docker`, `web`, `preview`, `telemetry`, `syslog`, `explain`, `docs`, `test`, `bench`, `voice`, `own`, `secure`, `compliance`, `enterprise`, `projects`, `audit`, `cost`, `metrics`, `sentrux`, `magic`, `steer`
|
|
231
231
|
<!-- /generated -->
|
|
232
232
|
|
|
233
233
|
---
|
|
@@ -475,4 +475,4 @@ Implemented, planned and deprecated items, with the version each landed in: `ski
|
|
|
475
475
|
|
|
476
476
|
---
|
|
477
477
|
|
|
478
|
-
**v11.
|
|
478
|
+
**v11.79.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.
|
|
1
|
+
11.79.0
|
|
@@ -235,7 +235,7 @@ cockpit_render() {
|
|
|
235
235
|
[ "$no_image" = "1" ] && args+=(--no-image)
|
|
236
236
|
|
|
237
237
|
# Gather -> render. Preserve the TS exit code (0 image / 3 fallback).
|
|
238
|
-
cockpit_gather_state "$skill_dir" "$focus_repo" | bun "$entry" "${args[@]}"
|
|
238
|
+
cockpit_gather_state "$skill_dir" "$focus_repo" | bun --no-env-file --config=/dev/null "$entry" "${args[@]}"
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
#===============================================================================
|
|
@@ -361,6 +361,51 @@ def _test_results_fresh(quality_dir):
|
|
|
361
361
|
return False
|
|
362
362
|
|
|
363
363
|
|
|
364
|
+
def _collect_advisory_review(loki_dir):
|
|
365
|
+
"""Advisory reviewers' Critical/High counts from the latest code review.
|
|
366
|
+
|
|
367
|
+
architecture-strategist, maintainer-mergeability and test-coverage-auditor
|
|
368
|
+
run but hold no veto, so their findings must not vanish: they are recorded
|
|
369
|
+
here (receipt) and rendered in the PR report. DISPLAY ONLY: this never feeds
|
|
370
|
+
the headline. Returns None when no aggregate carries the advisory fields
|
|
371
|
+
(older run, or no review), so existing receipts are byte-identical.
|
|
372
|
+
"""
|
|
373
|
+
reviews_dir = os.path.join(loki_dir, "quality", "reviews")
|
|
374
|
+
try:
|
|
375
|
+
ids = [d for d in os.listdir(reviews_dir)
|
|
376
|
+
if os.path.isfile(os.path.join(reviews_dir, d, "aggregate.json"))]
|
|
377
|
+
except OSError:
|
|
378
|
+
return None
|
|
379
|
+
if not ids:
|
|
380
|
+
return None
|
|
381
|
+
latest = max(ids, key=lambda d: (
|
|
382
|
+
os.path.getmtime(os.path.join(reviews_dir, d, "aggregate.json")), d))
|
|
383
|
+
agg = _read_json(os.path.join(reviews_dir, latest, "aggregate.json"), default=None)
|
|
384
|
+
if not isinstance(agg, dict) or "advisory_critical" not in agg:
|
|
385
|
+
return None
|
|
386
|
+
rows = agg.get("reviewers")
|
|
387
|
+
rows = rows if isinstance(rows, list) else []
|
|
388
|
+
reviewers = [
|
|
389
|
+
{"name": str(r.get("name") or ""),
|
|
390
|
+
"critical": _as_int(r.get("critical")), "high": _as_int(r.get("high"))}
|
|
391
|
+
for r in rows if isinstance(r, dict) and r.get("advisory") is True
|
|
392
|
+
]
|
|
393
|
+
return {
|
|
394
|
+
"review_id": str(agg.get("review_id") or latest),
|
|
395
|
+
"critical": _as_int(agg.get("advisory_critical")),
|
|
396
|
+
"high": _as_int(agg.get("advisory_high")),
|
|
397
|
+
"reviewers": reviewers,
|
|
398
|
+
"veto": False,
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _as_int(v):
|
|
403
|
+
try:
|
|
404
|
+
return int(v)
|
|
405
|
+
except Exception:
|
|
406
|
+
return 0
|
|
407
|
+
|
|
408
|
+
|
|
364
409
|
def _collect_quality_gates(loki_dir):
|
|
365
410
|
gates_raw = _read_json(
|
|
366
411
|
os.path.join(loki_dir, "state", "quality-gates.json"), default=None
|
|
@@ -523,7 +568,10 @@ def _collect_quality_gates(loki_dir):
|
|
|
523
568
|
if name.startswith("LOKI_PHASE_")
|
|
524
569
|
and str(value).strip().lower() in ("false", "0", "no", "off")
|
|
525
570
|
)
|
|
571
|
+
_adv_review = _collect_advisory_review(loki_dir)
|
|
572
|
+
_adv_extra = {"advisory_review": _adv_review} if _adv_review else {}
|
|
526
573
|
return {
|
|
574
|
+
**_adv_extra,
|
|
527
575
|
"passed": passed,
|
|
528
576
|
"total": total,
|
|
529
577
|
"gates": gates,
|
package/autonomy/lib/proof-pr.sh
CHANGED
|
@@ -235,6 +235,19 @@ def main():
|
|
|
235
235
|
_off = [str(x) for x in _off] if isinstance(_off, list) else []
|
|
236
236
|
if _off:
|
|
237
237
|
_line("| Gates disabled | " + ", ".join(sorted(_off)) + " |")
|
|
238
|
+
# Advisory reviewers run without a veto; their Critical/High findings are
|
|
239
|
+
# shown here so a removed veto is never a hidden one. Display only: this row
|
|
240
|
+
# never feeds the headline. Emitted only when something was recorded.
|
|
241
|
+
_adv = _qg.get("advisory_review")
|
|
242
|
+
if isinstance(_adv, dict):
|
|
243
|
+
try:
|
|
244
|
+
_ac = int(_adv.get("critical") or 0)
|
|
245
|
+
_ah = int(_adv.get("high") or 0)
|
|
246
|
+
except Exception:
|
|
247
|
+
_ac = _ah = 0
|
|
248
|
+
if _ac + _ah > 0:
|
|
249
|
+
_line("| Advisory review findings | Critical " + str(_ac) + ", High "
|
|
250
|
+
+ str(_ah) + " (recorded, no veto; see the review aggregate) |")
|
|
238
251
|
_line("| Base sha | `" + (base_sha or "(none)") + "` |")
|
|
239
252
|
_line("| Head sha | `" + (head_sha or "(none)") + "` |")
|
|
240
253
|
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"first_run_blocked": {
|
|
37
37
|
"blocker": {"type": "enum", "values": ["no_provider", "not_logged_in", "not_a_repo", "git_identity", "node", "python3", "jq", "git", "curl", "disk", "skill_symlink", "other"]}
|
|
38
38
|
},
|
|
39
|
+
"seal_hook_decision": {
|
|
40
|
+
"decision": {"type": "enum", "values": ["allow", "block"]},
|
|
41
|
+
"reason": {"type": "enum", "values": ["none", "tests_failed", "tests_weakened", "no_tests", "contract_unverified", "released", "error"]}
|
|
42
|
+
},
|
|
39
43
|
"build_verified": {
|
|
40
44
|
"headline": {"type": "enum", "values": ["VERIFIED", "VERIFIED WITH GAPS", "NOT VERIFIED"]},
|
|
41
45
|
"route": {"type": "enum", "values": ["engine10"]},
|
package/autonomy/loki
CHANGED
|
@@ -1864,6 +1864,7 @@ show_help_all() {
|
|
|
1864
1864
|
echo " workspace list | infer <name> | run <name> <ref> Multi-repo runs from loki.yaml workspaces (LOKI_WORKSPACES=0 disables)"
|
|
1865
1865
|
echo " start --workspace <name> \"<brief>\" Run a workspace; start --deploy [--health-url U] deploys after a VERIFIED run"
|
|
1866
1866
|
echo " quickstart [idea] Guided first build: setup, idea, template, plan, go"
|
|
1867
|
+
echo " try <repo-url> Try Loki on a public repo in a temp clone (--dry-run previews); only the receipt is kept"
|
|
1867
1868
|
echo ""
|
|
1868
1869
|
echo "Session:"
|
|
1869
1870
|
echo " status [--json|--badge|--probe] Show current status (--json machine-readable, --badge deploy badge, --probe re-check deploy health)"
|
|
@@ -2023,7 +2024,7 @@ show_help_all() {
|
|
|
2023
2024
|
echo " projects proof (receipt) provider quick quickstart rc remote report reset"
|
|
2024
2025
|
echo " merge restore resume review rollback run runs sandbox secrets secure self-update sentrux"
|
|
2025
2026
|
echo " serve setup setup-skill share ship spec start state stats status steer stop"
|
|
2026
|
-
echo " syslog telemetry template test tour trigger trust trust-metrics"
|
|
2027
|
+
echo " syslog telemetry template test tour trigger trust trust-metrics try"
|
|
2027
2028
|
echo " ultracode undo update verify version voice watch watchdog web welcome why"
|
|
2028
2029
|
echo " wiki workspace worktree (wt) wow"
|
|
2029
2030
|
echo " control"
|
|
@@ -2186,7 +2187,7 @@ _loki_control_ui() {
|
|
|
2186
2187
|
local cli="$SKILL_DIR/loki-ts/dist/loki.js" cdir="${HOME}/.loki/control" spid
|
|
2187
2188
|
[ -f "$cli" ] || cli="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
2188
2189
|
mkdir -p "$cdir" && chmod 700 "$cdir" 2>/dev/null || true
|
|
2189
|
-
LOKI_CONTROL=1 LOKI_NO_BROWSER=1 nohup bun "$cli" control serve >"$cdir/serve.log" 2>&1 </dev/null &
|
|
2190
|
+
LOKI_CONTROL=1 LOKI_NO_BROWSER=1 nohup bun --no-env-file --config=/dev/null "$cli" control serve >"$cdir/serve.log" 2>&1 </dev/null &
|
|
2190
2191
|
spid=$!
|
|
2191
2192
|
echo "$spid" > "$cdir/serve.pid" 2>/dev/null || true
|
|
2192
2193
|
for i in $(seq 1 100); do
|
|
@@ -5869,8 +5870,27 @@ try:
|
|
|
5869
5870
|
except Exception:
|
|
5870
5871
|
sys.exit(1)
|
|
5871
5872
|
PYEOF
|
|
5872
|
-
local est_json
|
|
5873
|
-
|
|
5873
|
+
local est_json="" est_pid="" est_ticks=0 est_max est_kid
|
|
5874
|
+
est_max=$(( ${LOKI_NEXT_ESTIMATE_TIMEOUT:-20} * 4 ))
|
|
5875
|
+
# The estimate runs in the background and is polled, so a slow or hung
|
|
5876
|
+
# estimator cannot hang the command (no timeout binary is assumed: macOS
|
|
5877
|
+
# bash 3.2 may lack one). Only the PID started here, and its direct
|
|
5878
|
+
# children, are ever signalled.
|
|
5879
|
+
( cmd_estimate --json > "$tmp/est" 2>/dev/null ) &
|
|
5880
|
+
est_pid=$!
|
|
5881
|
+
while kill -0 "$est_pid" 2>/dev/null; do
|
|
5882
|
+
if [ "$est_ticks" -ge "$est_max" ]; then
|
|
5883
|
+
for est_kid in $(pgrep -P "$est_pid" 2>/dev/null); do kill "$est_kid" 2>/dev/null || true; done
|
|
5884
|
+
kill "$est_pid" 2>/dev/null || true
|
|
5885
|
+
note="${note:+$note; }estimate unavailable: timed out after ${LOKI_NEXT_ESTIMATE_TIMEOUT:-20}s"
|
|
5886
|
+
: > "$tmp/est"
|
|
5887
|
+
break
|
|
5888
|
+
fi
|
|
5889
|
+
sleep 0.25
|
|
5890
|
+
est_ticks=$((est_ticks + 1))
|
|
5891
|
+
done
|
|
5892
|
+
wait "$est_pid" 2>/dev/null || true
|
|
5893
|
+
est_json="$(cat "$tmp/est" 2>/dev/null)" || est_json=""
|
|
5874
5894
|
{ git grep -nIE 'TODO|FIXME' -- . ':!.loki' 2>/dev/null \
|
|
5875
5895
|
|| grep -rnIE 'TODO|FIXME' --exclude-dir=.git --exclude-dir=.loki --exclude-dir=node_modules . 2>/dev/null; } \
|
|
5876
5896
|
| head -20 > "$tmp/todos" || true
|
|
@@ -15922,9 +15942,9 @@ _loki_bun_delegate() {
|
|
|
15922
15942
|
|
|
15923
15943
|
t="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)"
|
|
15924
15944
|
if [ -n "$t" ]; then
|
|
15925
|
-
"$t" -k 1 5 "$bun_bin" "$cli" version >/dev/null 2>&1
|
|
15945
|
+
"$t" -k 1 5 "$bun_bin" --no-env-file --config=/dev/null "$cli" version >/dev/null 2>&1
|
|
15926
15946
|
else
|
|
15927
|
-
"$bun_bin" "$cli" version >/dev/null 2>&1
|
|
15947
|
+
"$bun_bin" --no-env-file --config=/dev/null "$cli" version >/dev/null 2>&1
|
|
15928
15948
|
fi
|
|
15929
15949
|
rc=$?
|
|
15930
15950
|
if [ "$rc" -ne 0 ]; then
|
|
@@ -15933,7 +15953,7 @@ _loki_bun_delegate() {
|
|
|
15933
15953
|
return 1
|
|
15934
15954
|
fi
|
|
15935
15955
|
|
|
15936
|
-
exec "$bun_bin" "$cli" "$cmd" "$@"
|
|
15956
|
+
exec "$bun_bin" --no-env-file --config=/dev/null "$cli" "$cmd" "$@"
|
|
15937
15957
|
}
|
|
15938
15958
|
|
|
15939
15959
|
# One-line fix for a requirement id (a provider name is an id), read from the shared
|
|
@@ -17166,13 +17186,20 @@ PY
|
|
|
17166
17186
|
return 0
|
|
17167
17187
|
}
|
|
17168
17188
|
|
|
17169
|
-
# Removes only a directory
|
|
17189
|
+
# Removes only a directory the caller created: a direct child of the temp root named
|
|
17190
|
+
# <prefix>.* (default loki-wow), not a symlink. With a marker file name as $3, the directory
|
|
17191
|
+
# must also hold that file (not a symlink) whose first line equals the directory's own path.
|
|
17170
17192
|
wow_cleanup_dir() {
|
|
17171
|
-
local d="${1:-}" root
|
|
17193
|
+
local d="${1:-}" prefix="${2:-loki-wow}" marker="${3:-}" root marker_value=""
|
|
17172
17194
|
[ -n "$d" ] && [ -d "$d" ] && [ ! -L "$d" ] || return 0
|
|
17173
17195
|
root="$(cd "${TMPDIR:-/tmp}" 2>/dev/null && pwd -P)" || return 0
|
|
17174
|
-
case "$d" in "$root"
|
|
17196
|
+
case "$d" in "$root/$prefix".*) ;; *) return 0 ;; esac
|
|
17175
17197
|
[ "$(dirname -- "$d")" = "$root" ] || return 0
|
|
17198
|
+
if [ -n "$marker" ]; then
|
|
17199
|
+
[ -f "$d/$marker" ] && [ ! -L "$d/$marker" ] || return 0
|
|
17200
|
+
IFS= read -r marker_value <"$d/$marker" || true
|
|
17201
|
+
[ "$marker_value" = "$d" ] || return 0
|
|
17202
|
+
fi
|
|
17176
17203
|
rm -rf -- "$d"
|
|
17177
17204
|
}
|
|
17178
17205
|
|
|
@@ -23005,6 +23032,243 @@ for r in reqs:
|
|
|
23005
23032
|
"
|
|
23006
23033
|
}
|
|
23007
23034
|
|
|
23035
|
+
# loki try: clone a public repo into a run-owned temp dir, pick a starter task, run it
|
|
23036
|
+
# there, keep only the receipt. The caller's checkout is never touched. The temp helpers
|
|
23037
|
+
# below follow the loki_run_tmp_create/cleanup contract (the repo's own copies live under
|
|
23038
|
+
# eval/, which the npm package does not ship).
|
|
23039
|
+
try_tmp_create() {
|
|
23040
|
+
local temp_root d
|
|
23041
|
+
temp_root="$(cd "${TMPDIR:-/tmp}" 2>/dev/null && pwd -P)" || return 1
|
|
23042
|
+
d="$(mktemp -d "${temp_root}/loki-run.XXXXXXXX")" || return 1
|
|
23043
|
+
chmod 700 "$d" || { rmdir "$d" 2>/dev/null; return 1; }
|
|
23044
|
+
if ! printf '%s\n' "$d" >"$d/.loki-run-owned"; then rm -rf -- "$d"; return 1; fi
|
|
23045
|
+
printf '%s\n' "$d"
|
|
23046
|
+
}
|
|
23047
|
+
|
|
23048
|
+
# Removes only a directory try_tmp_create made (loki-run.* child of the temp root whose
|
|
23049
|
+
# .loki-run-owned marker holds its own path); the checks live in wow_cleanup_dir.
|
|
23050
|
+
try_tmp_cleanup() {
|
|
23051
|
+
wow_cleanup_dir "${1:-}" loki-run .loki-run-owned
|
|
23052
|
+
}
|
|
23053
|
+
|
|
23054
|
+
# Prints the starter task for the clone at $1 (source URL $2): the first open GitHub
|
|
23055
|
+
# issue title when gh can read it, else the first TODO line. Prints nothing when neither exists.
|
|
23056
|
+
try_pick_task() {
|
|
23057
|
+
local dest="$1" url="$2" slug title first p l t todo_file
|
|
23058
|
+
case "$url" in
|
|
23059
|
+
https://github.com/*)
|
|
23060
|
+
slug="${url#https://github.com/}"
|
|
23061
|
+
slug="${slug%.git}"
|
|
23062
|
+
slug="${slug%/}"
|
|
23063
|
+
if command -v gh >/dev/null 2>&1; then
|
|
23064
|
+
title="$(gh issue list --repo "$slug" --state open --limit 1 --json title --jq '.[0].title // empty' 2>/dev/null)" || title=""
|
|
23065
|
+
title="$(printf '%s' "$title" | LC_ALL=C tr -d '\000-\011\013-\037\177' | LC_ALL=C tr '\n' ' ')"
|
|
23066
|
+
if [ -n "$title" ]; then
|
|
23067
|
+
printf '%s\n' "Resolve the open issue: ${title:0:200}"
|
|
23068
|
+
return 0
|
|
23069
|
+
fi
|
|
23070
|
+
fi
|
|
23071
|
+
;;
|
|
23072
|
+
esac
|
|
23073
|
+
todo_file="$(dirname -- "$dest")/todo.txt"
|
|
23074
|
+
git -C "$dest" grep -I -n -e TODO >"$todo_file" 2>/dev/null || true
|
|
23075
|
+
first="$(sed -n 1p "$todo_file" 2>/dev/null)"
|
|
23076
|
+
[ -n "$first" ] || return 0
|
|
23077
|
+
p="${first%%:*}"
|
|
23078
|
+
first="${first#*:}"
|
|
23079
|
+
l="${first%%:*}"
|
|
23080
|
+
t="${first#*:}"
|
|
23081
|
+
t="$(printf '%s' "$t" | LC_ALL=C tr -d '\000-\037\177' | sed -e 's/^[[:space:]]*//' | cut -c1-160)"
|
|
23082
|
+
p="$(printf '%s' "$p" | LC_ALL=C tr -d '\000-\037\177')"
|
|
23083
|
+
printf '%s\n' "Resolve the TODO in ${p} (line ${l}): ${t}"
|
|
23084
|
+
}
|
|
23085
|
+
|
|
23086
|
+
# Copies the newest receipt.json under $1/.loki/runs/<run>/ to a kept location and prints its
|
|
23087
|
+
# path. The clone is untrusted: every level (.loki, runs, the run dir, the file) is opened
|
|
23088
|
+
# without following symlinks, and only a receipt modified after the stamp file $2 (touched
|
|
23089
|
+
# right before loki start) is accepted, so a receipt committed in the repo is never reported.
|
|
23090
|
+
try_keep_receipt() {
|
|
23091
|
+
local dest="$1" stamp="$2" out_root
|
|
23092
|
+
out_root="${LOKI_TRY_RECEIPT_DIR:-${HOME}/.loki/try-receipts}"
|
|
23093
|
+
python3 -I - "$dest" "$stamp" "$out_root" <<'PY'
|
|
23094
|
+
import os, re, stat, sys
|
|
23095
|
+
dest, stamp, out_root = sys.argv[1:4]
|
|
23096
|
+
D = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW
|
|
23097
|
+
try:
|
|
23098
|
+
start = os.stat(stamp).st_mtime_ns
|
|
23099
|
+
root = os.open(dest, D)
|
|
23100
|
+
loki = os.open(".loki", D, dir_fd=root)
|
|
23101
|
+
runs = os.open("runs", D, dir_fd=loki)
|
|
23102
|
+
names = os.listdir(runs)
|
|
23103
|
+
except OSError:
|
|
23104
|
+
sys.exit(1)
|
|
23105
|
+
best = None
|
|
23106
|
+
for name in names:
|
|
23107
|
+
if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", name):
|
|
23108
|
+
continue
|
|
23109
|
+
try:
|
|
23110
|
+
rd = os.open(name, D, dir_fd=runs)
|
|
23111
|
+
except OSError:
|
|
23112
|
+
continue
|
|
23113
|
+
try:
|
|
23114
|
+
fd = os.open("receipt.json", os.O_RDONLY | os.O_NOFOLLOW, dir_fd=rd)
|
|
23115
|
+
except OSError:
|
|
23116
|
+
os.close(rd)
|
|
23117
|
+
continue
|
|
23118
|
+
try:
|
|
23119
|
+
st = os.fstat(fd)
|
|
23120
|
+
if stat.S_ISREG(st.st_mode) and st.st_size <= 16 * 1024 * 1024 and st.st_mtime_ns > start:
|
|
23121
|
+
if best is None or st.st_mtime_ns > best[0]:
|
|
23122
|
+
with os.fdopen(os.dup(fd), "rb") as f:
|
|
23123
|
+
best = (st.st_mtime_ns, name, f.read())
|
|
23124
|
+
finally:
|
|
23125
|
+
os.close(fd)
|
|
23126
|
+
os.close(rd)
|
|
23127
|
+
if best is None:
|
|
23128
|
+
sys.exit(1)
|
|
23129
|
+
out = os.path.join(out_root, best[1])
|
|
23130
|
+
os.makedirs(out, exist_ok=True)
|
|
23131
|
+
target = os.path.join(out, "receipt.json")
|
|
23132
|
+
with open(target, "wb") as f:
|
|
23133
|
+
f.write(best[2])
|
|
23134
|
+
print(target)
|
|
23135
|
+
PY
|
|
23136
|
+
}
|
|
23137
|
+
|
|
23138
|
+
_cmd_try_body() {
|
|
23139
|
+
local tmp="$1" url="$2" dry_run="$3" provider="$4" max_cost="$5"
|
|
23140
|
+
local dest="$tmp/repo" task
|
|
23141
|
+
if ! (
|
|
23142
|
+
unset GIT_DIR GIT_WORK_TREE GIT_COMMON_DIR GIT_INDEX_FILE GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_PREFIX
|
|
23143
|
+
GIT_TERMINAL_PROMPT=0 git -c protocol.ext.allow=never -c core.hooksPath=/dev/null \
|
|
23144
|
+
clone --depth 1 --quiet -- "$url" "$dest"
|
|
23145
|
+
); then
|
|
23146
|
+
echo "loki try: could not clone $url" >&2
|
|
23147
|
+
return 1
|
|
23148
|
+
fi
|
|
23149
|
+
task="$(try_pick_task "$dest" "$url")"
|
|
23150
|
+
if [ -z "$task" ]; then
|
|
23151
|
+
echo "loki try: no starter task found (no open issue readable, no TODO line) in $url" >&2
|
|
23152
|
+
return 1
|
|
23153
|
+
fi
|
|
23154
|
+
echo "Clone path: $dest"
|
|
23155
|
+
echo "Task: $task"
|
|
23156
|
+
echo "Estimate: one task on the Loki 10 engine, provider $provider, cost capped at \$$max_cost (--max-cost); the receipt records the actual spend."
|
|
23157
|
+
if [ "$dry_run" = true ]; then
|
|
23158
|
+
echo "[dry-run] No model call was made. A real run would execute:"
|
|
23159
|
+
echo " loki start \"<task>\" --no-pr --provider $provider --max-cost $max_cost"
|
|
23160
|
+
return 0
|
|
23161
|
+
fi
|
|
23162
|
+
local rc=0 receipt stamp="$tmp/started.stamp"
|
|
23163
|
+
: >"$stamp"
|
|
23164
|
+
(
|
|
23165
|
+
cd "$dest" && LOKI_ASSUME_YES=1 "$SKILL_DIR/bin/loki" start "$task" --no-pr --provider "$provider" --max-cost "$max_cost"
|
|
23166
|
+
) || rc=$?
|
|
23167
|
+
if receipt="$(try_keep_receipt "$dest" "$stamp")"; then
|
|
23168
|
+
echo ""
|
|
23169
|
+
echo "Receipt: $receipt"
|
|
23170
|
+
echo "Check it: loki verify $receipt"
|
|
23171
|
+
else
|
|
23172
|
+
echo ""
|
|
23173
|
+
echo "No receipt was written (run exited $rc)."
|
|
23174
|
+
[ "$rc" != "0" ] || rc=1
|
|
23175
|
+
fi
|
|
23176
|
+
return "$rc"
|
|
23177
|
+
}
|
|
23178
|
+
|
|
23179
|
+
cmd_try() {
|
|
23180
|
+
local url="" dry_run=false provider="" max_cost="2"
|
|
23181
|
+
while [[ $# -gt 0 ]]; do
|
|
23182
|
+
case "$1" in
|
|
23183
|
+
--help|-h)
|
|
23184
|
+
echo -e "${BOLD}loki try${NC} - Try Loki on a public repo without touching your checkout"
|
|
23185
|
+
echo ""
|
|
23186
|
+
echo "Usage: loki try <public-repo-url> [--dry-run] [--provider P] [--max-cost N]"
|
|
23187
|
+
echo ""
|
|
23188
|
+
echo "Clones the repo (depth 1) into a run-owned temp directory, picks a starter"
|
|
23189
|
+
echo "task (the first open GitHub issue title, else the first TODO line), runs it"
|
|
23190
|
+
echo "on the Loki 10 engine with no PR, keeps only the receipt, and always removes"
|
|
23191
|
+
echo "the temp clone. Your current checkout is never read or changed."
|
|
23192
|
+
echo ""
|
|
23193
|
+
echo "Options:"
|
|
23194
|
+
echo " --dry-run Clone, then print the clone path, the task and an estimate. No model call, no spend"
|
|
23195
|
+
echo " --provider P claude (default) or opencode, codex, cline, aider"
|
|
23196
|
+
echo " --max-cost N Hard per-run cap in USD, a positive number (default 2)"
|
|
23197
|
+
echo " --help, -h Show this help"
|
|
23198
|
+
echo ""
|
|
23199
|
+
echo "URL schemes: https://, http://, git://, file://"
|
|
23200
|
+
echo "The receipt is kept under \${LOKI_TRY_RECEIPT_DIR:-~/.loki/try-receipts}/<run>/receipt.json;"
|
|
23201
|
+
echo "re-check it with: loki verify <receipt path>"
|
|
23202
|
+
echo ""
|
|
23203
|
+
echo "Examples:"
|
|
23204
|
+
echo " loki try https://github.com/owner/repo --dry-run"
|
|
23205
|
+
echo " loki try https://github.com/owner/repo"
|
|
23206
|
+
return 0
|
|
23207
|
+
;;
|
|
23208
|
+
--dry-run) dry_run=true; shift ;;
|
|
23209
|
+
--provider)
|
|
23210
|
+
provider="${2:-}"
|
|
23211
|
+
if [ -z "$provider" ]; then echo "loki try: --provider requires a value" >&2; return 2; fi
|
|
23212
|
+
shift 2
|
|
23213
|
+
;;
|
|
23214
|
+
--provider=*) provider="${1#*=}"; shift ;;
|
|
23215
|
+
--max-cost)
|
|
23216
|
+
max_cost="${2:-}"
|
|
23217
|
+
if [ -z "$max_cost" ]; then echo "loki try: --max-cost requires a value" >&2; return 2; fi
|
|
23218
|
+
shift 2
|
|
23219
|
+
;;
|
|
23220
|
+
--max-cost=*) max_cost="${1#*=}"; shift ;;
|
|
23221
|
+
-*)
|
|
23222
|
+
echo "loki try: unknown option: $1" >&2
|
|
23223
|
+
echo "Run 'loki try --help'." >&2
|
|
23224
|
+
return 2
|
|
23225
|
+
;;
|
|
23226
|
+
*)
|
|
23227
|
+
if [ -n "$url" ]; then
|
|
23228
|
+
echo "loki try: only one repo URL is accepted" >&2
|
|
23229
|
+
return 2
|
|
23230
|
+
fi
|
|
23231
|
+
url="$1"
|
|
23232
|
+
shift
|
|
23233
|
+
;;
|
|
23234
|
+
esac
|
|
23235
|
+
done
|
|
23236
|
+
if [ -z "$url" ]; then
|
|
23237
|
+
echo "Usage: loki try <public-repo-url> [--dry-run]" >&2
|
|
23238
|
+
return 2
|
|
23239
|
+
fi
|
|
23240
|
+
case "$url" in
|
|
23241
|
+
https://*|http://*|git://*|file://*) ;;
|
|
23242
|
+
*) echo "loki try: unsupported URL '$url' (use https://, http://, git:// or file://)" >&2; return 2 ;;
|
|
23243
|
+
esac
|
|
23244
|
+
case "$url" in *[[:space:]]*) echo "loki try: the URL must not contain whitespace" >&2; return 2 ;; esac
|
|
23245
|
+
if ! [[ "$max_cost" =~ ^[0-9]+(\.[0-9]+)?$ ]] || ! awk -v c="$max_cost" 'BEGIN { exit !(c > 0) }'; then
|
|
23246
|
+
echo "loki try: --max-cost must be a positive number of USD, got: $max_cost" >&2
|
|
23247
|
+
return 2
|
|
23248
|
+
fi
|
|
23249
|
+
local use_provider="${provider:-${LOKI_PROVIDER:-claude}}"
|
|
23250
|
+
case "$use_provider" in
|
|
23251
|
+
claude|opencode|codex|cline|aider) ;;
|
|
23252
|
+
*) echo "loki try: unknown provider '$use_provider' (use claude, opencode, codex, cline or aider)" >&2; return 2 ;;
|
|
23253
|
+
esac
|
|
23254
|
+
if [ "$dry_run" != true ] && ! wow_provider_ready "$use_provider"; then
|
|
23255
|
+
echo "loki try: no provider ready for '$use_provider'. Run 'loki doctor', or preview with --dry-run." >&2
|
|
23256
|
+
return 1
|
|
23257
|
+
fi
|
|
23258
|
+
|
|
23259
|
+
local tmp rc=0
|
|
23260
|
+
tmp="$(try_tmp_create)" || { echo "loki try: cannot create a temp directory" >&2; return 1; }
|
|
23261
|
+
# shellcheck disable=SC2064 # expand tmp now; it is local to this call
|
|
23262
|
+
trap "try_tmp_cleanup '$tmp'; trap - INT TERM HUP; exit 130" INT
|
|
23263
|
+
trap "try_tmp_cleanup '$tmp'; trap - INT TERM HUP; exit 143" TERM
|
|
23264
|
+
trap "try_tmp_cleanup '$tmp'; trap - INT TERM HUP; exit 129" HUP
|
|
23265
|
+
_cmd_try_body "$tmp" "$url" "$dry_run" "$use_provider" "$max_cost" || rc=$?
|
|
23266
|
+
trap - INT TERM HUP
|
|
23267
|
+
try_tmp_cleanup "$tmp"
|
|
23268
|
+
if [ ! -e "$tmp" ]; then echo "Removed the temp clone."; fi
|
|
23269
|
+
return "$rc"
|
|
23270
|
+
}
|
|
23271
|
+
|
|
23008
23272
|
cmd_plan() {
|
|
23009
23273
|
local prd_file=""
|
|
23010
23274
|
local show_requirements=false
|
|
@@ -23130,7 +23394,7 @@ _suggest_command() {
|
|
|
23130
23394
|
ultracode council demo dogfood heal modernize import issue github init template \
|
|
23131
23395
|
status stop pause resume monitor watch watchdog dashboard web open preview share \
|
|
23132
23396
|
memory context ctx checkpoint state report kpis cost metrics stats logs otel \
|
|
23133
|
-
syslog telemetry crash config provider doctor setup setup-skill onboard quickstart \
|
|
23397
|
+
syslog telemetry crash config provider doctor setup setup-skill onboard quickstart try \
|
|
23134
23398
|
welcome update self-update rollback merge failover cluster enterprise remote deploy docker \
|
|
23135
23399
|
sandbox api mcp magic compound assets export wiki control docs explain why audit compliance \
|
|
23136
23400
|
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
@@ -23703,6 +23967,9 @@ main() {
|
|
|
23703
23967
|
plan)
|
|
23704
23968
|
cmd_plan "$@"
|
|
23705
23969
|
;;
|
|
23970
|
+
try)
|
|
23971
|
+
cmd_try "$@"
|
|
23972
|
+
;;
|
|
23706
23973
|
failover)
|
|
23707
23974
|
cmd_failover "$@"
|
|
23708
23975
|
;;
|
|
@@ -30477,7 +30744,7 @@ cmd_estimate() {
|
|
|
30477
30744
|
echo "Install Bun (https://bun.sh) or remove LOKI_LEGACY_BASH=1." >&2
|
|
30478
30745
|
return 1
|
|
30479
30746
|
fi
|
|
30480
|
-
bun "$entry" estimate "$@"
|
|
30747
|
+
bun --no-env-file --config=/dev/null "$entry" estimate "$@"
|
|
30481
30748
|
}
|
|
30482
30749
|
|
|
30483
30750
|
# Transparent cost view (R3): per-run + per-project spend, model routing, and
|
|
@@ -36052,7 +36319,7 @@ cmd_control() {
|
|
|
36052
36319
|
return 1
|
|
36053
36320
|
fi
|
|
36054
36321
|
[ -f "$cli" ] || cli="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
36055
|
-
exec bun "$cli" control "$@"
|
|
36322
|
+
exec bun --no-env-file --config=/dev/null "$cli" control "$@"
|
|
36056
36323
|
}
|
|
36057
36324
|
|
|
36058
36325
|
cmd_wiki() {
|