loki-mode 11.78.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 +259 -11
- 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 +15 -0
- package/completions/loki.bash +15 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-INVENTORY.md +4 -3
- package/docs/CLI-REFERENCE.md +2 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/PRIVACY.md +1 -0
- package/docs/dev/architecture-reference.md +1 -0
- package/loki-ts/data/child-env.json +1 -0
- package/loki-ts/dist/loki.js +1334 -1324
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +1453 -1267
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/quality-gates.md +6 -0
- package/web-app/dist/assets/{AdminPage-BPF2Xehf.js → AdminPage-Bph4VGNm.js} +1 -1
- package/web-app/dist/assets/{Avatar-CSW9BhBU.js → Avatar-Btrppoor.js} +1 -1
- package/web-app/dist/assets/{Badge-wgm79wim.js → Badge-fJFe3CEH.js} +1 -1
- package/web-app/dist/assets/{Button-BcIAcPKr.js → Button-B_cYY4Me.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BFi-0Kwj.js → CockpitPage-26oBcuYy.js} +1 -1
- package/web-app/dist/assets/{ComparePage-B7Wibrfn.js → ComparePage-CSuQZUm2.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-BYyOVh2c.js → ErrorBoundary-Cf7DQMz0.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-C9QcrjNl.js → EvidenceReceiptPanel-BMZszYBV.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-CWbuBxVZ.js → GitHubIssuesPanel-Bt5dkJwW.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-qcBHK615.js → GitHubPRsPanel-CL4NbWJ3.js} +1 -1
- package/web-app/dist/assets/{HomePage-B1mYFU4t.js → HomePage-Bqugxf45.js} +2 -2
- package/web-app/dist/assets/{LoginPage-CmIO1xL7.js → LoginPage-ENM9QyG8.js} +1 -1
- package/web-app/dist/assets/{MagicPage-L7UP4qKP.js → MagicPage-D-7g-mew.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-DVPCjN1U.js → MetricsPage-CelqK13G.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D3XZmCH5.js → NotFoundPage-EQ3Aq4Xg.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BlaMzlVi.js → ProjectPage-D3Jls_Oh.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DN7W28Zq.js → ProjectsPage-CgGRkJ3X.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-2u9ODYB5.js → SettingsPage-B79K8xg2.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CoKuTfab.js → ShowcasePage-DxSsaGGx.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-Crhj2XAU.js → SystemSettingsPage-CYNKB-5I.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DehMgnKh.js → TeamsPage-Bit01iEC.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-DGtwfZgU.js → TemplatesPage-DQ3fPi7x.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-7HZRtXoC.js → TerminalOutput-By137hB-.js} +1 -1
- package/web-app/dist/assets/{activity-DAp5wswo.js → activity-rbRWrIjS.js} +1 -1
- package/web-app/dist/assets/{bell-Ncvnrz7d.js → bell-DIiSalJU.js} +1 -1
- package/web-app/dist/assets/{bot-BWob3K3E.js → bot-BQyrbJaf.js} +1 -1
- package/web-app/dist/assets/{check-DReWA2vm.js → check-BJ_PqlU2.js} +1 -1
- package/web-app/dist/assets/{chevron-left-CBJpzVU1.js → chevron-left-_QsMbFBT.js} +1 -1
- package/web-app/dist/assets/{circle-alert-2c_vs5PW.js → circle-alert-DEovFrER.js} +1 -1
- package/web-app/dist/assets/{clock-BrS_ZKKi.js → clock-Dmrl0gvN.js} +1 -1
- package/web-app/dist/assets/{cloud-Czvuo51R.js → cloud-Bs2a7FCR.js} +1 -1
- package/web-app/dist/assets/{code-xml-BWEqJ2I8.js → code-xml-D2WdclgQ.js} +1 -1
- package/web-app/dist/assets/{database-CJ6M-oK-.js → database-kbYbPMS5.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DSObKmVv.js → dollar-sign-BoOAvw6B.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CVEW-gF5.js → file-code-corner-ew5NSFtR.js} +1 -1
- package/web-app/dist/assets/{file-plus-CAK0I0tE.js → file-plus-CNV2nXNx.js} +1 -1
- package/web-app/dist/assets/{globe-DeUgcsSZ.js → globe-CZo7LjYX.js} +1 -1
- package/web-app/dist/assets/{hammer-B74PyjS0.js → hammer-BedES-IA.js} +1 -1
- package/web-app/dist/assets/{index-DW3_HRqp.js → index-C_uRanCX.js} +3 -3
- package/web-app/dist/assets/{layers-Dw7kaGyJ.js → layers-B-OfB6O0.js} +1 -1
- package/web-app/dist/assets/{loader-circle-B6xzlAjw.js → loader-circle-C8hx3EE8.js} +1 -1
- package/web-app/dist/assets/{lock-5ncCPzIG.js → lock-Cyi_Tc3f.js} +1 -1
- package/web-app/dist/assets/{package-x59_dTx0.js → package-Cfps-Gdr.js} +1 -1
- package/web-app/dist/assets/{plus-BcVBzjpS.js → plus-CjSNRaN2.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CNrJhks6.js → refresh-cw-pEhkaiQh.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-BpmS_ezI.js → rotate-ccw-twl6HuT5.js} +1 -1
- package/web-app/dist/assets/{scroll-text-CwBYNyoj.js → scroll-text-B9XWtVcd.js} +1 -1
- package/web-app/dist/assets/{server-C4hnxkk6.js → server-DeBbwC8T.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CSVUyWCM.js → shield-alert-BGQQ_YfF.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DEU9fmDX.js → thumbs-up-Dy9nEa04.js} +1 -1
- package/web-app/dist/assets/{trash-2-DJKMevAm.js → trash-2-DIftKB8Y.js} +1 -1
- package/web-app/dist/assets/{trending-down-Dq3wAM59.js → trending-down-Cjh8EsVU.js} +1 -1
- package/web-app/dist/assets/{trending-up-DribptN4.js → trending-up-xWyMrayW.js} +1 -1
- package/web-app/dist/assets/{upload-DUlZxsH1.js → upload-Bh3moncl.js} +1 -1
- package/web-app/dist/assets/{usePolling-PecqVZ0k.js → usePolling-C2sF90ff.js} +1 -1
- package/web-app/dist/assets/{user-BcLYLDVU.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
|
|
@@ -15941,9 +15942,9 @@ _loki_bun_delegate() {
|
|
|
15941
15942
|
|
|
15942
15943
|
t="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)"
|
|
15943
15944
|
if [ -n "$t" ]; then
|
|
15944
|
-
"$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
|
|
15945
15946
|
else
|
|
15946
|
-
"$bun_bin" "$cli" version >/dev/null 2>&1
|
|
15947
|
+
"$bun_bin" --no-env-file --config=/dev/null "$cli" version >/dev/null 2>&1
|
|
15947
15948
|
fi
|
|
15948
15949
|
rc=$?
|
|
15949
15950
|
if [ "$rc" -ne 0 ]; then
|
|
@@ -15952,7 +15953,7 @@ _loki_bun_delegate() {
|
|
|
15952
15953
|
return 1
|
|
15953
15954
|
fi
|
|
15954
15955
|
|
|
15955
|
-
exec "$bun_bin" "$cli" "$cmd" "$@"
|
|
15956
|
+
exec "$bun_bin" --no-env-file --config=/dev/null "$cli" "$cmd" "$@"
|
|
15956
15957
|
}
|
|
15957
15958
|
|
|
15958
15959
|
# One-line fix for a requirement id (a provider name is an id), read from the shared
|
|
@@ -17185,13 +17186,20 @@ PY
|
|
|
17185
17186
|
return 0
|
|
17186
17187
|
}
|
|
17187
17188
|
|
|
17188
|
-
# 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.
|
|
17189
17192
|
wow_cleanup_dir() {
|
|
17190
|
-
local d="${1:-}" root
|
|
17193
|
+
local d="${1:-}" prefix="${2:-loki-wow}" marker="${3:-}" root marker_value=""
|
|
17191
17194
|
[ -n "$d" ] && [ -d "$d" ] && [ ! -L "$d" ] || return 0
|
|
17192
17195
|
root="$(cd "${TMPDIR:-/tmp}" 2>/dev/null && pwd -P)" || return 0
|
|
17193
|
-
case "$d" in "$root"
|
|
17196
|
+
case "$d" in "$root/$prefix".*) ;; *) return 0 ;; esac
|
|
17194
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
|
|
17195
17203
|
rm -rf -- "$d"
|
|
17196
17204
|
}
|
|
17197
17205
|
|
|
@@ -23024,6 +23032,243 @@ for r in reqs:
|
|
|
23024
23032
|
"
|
|
23025
23033
|
}
|
|
23026
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
|
+
|
|
23027
23272
|
cmd_plan() {
|
|
23028
23273
|
local prd_file=""
|
|
23029
23274
|
local show_requirements=false
|
|
@@ -23149,7 +23394,7 @@ _suggest_command() {
|
|
|
23149
23394
|
ultracode council demo dogfood heal modernize import issue github init template \
|
|
23150
23395
|
status stop pause resume monitor watch watchdog dashboard web open preview share \
|
|
23151
23396
|
memory context ctx checkpoint state report kpis cost metrics stats logs otel \
|
|
23152
|
-
syslog telemetry crash config provider doctor setup setup-skill onboard quickstart \
|
|
23397
|
+
syslog telemetry crash config provider doctor setup setup-skill onboard quickstart try \
|
|
23153
23398
|
welcome update self-update rollback merge failover cluster enterprise remote deploy docker \
|
|
23154
23399
|
sandbox api mcp magic compound assets export wiki control docs explain why audit compliance \
|
|
23155
23400
|
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
@@ -23722,6 +23967,9 @@ main() {
|
|
|
23722
23967
|
plan)
|
|
23723
23968
|
cmd_plan "$@"
|
|
23724
23969
|
;;
|
|
23970
|
+
try)
|
|
23971
|
+
cmd_try "$@"
|
|
23972
|
+
;;
|
|
23725
23973
|
failover)
|
|
23726
23974
|
cmd_failover "$@"
|
|
23727
23975
|
;;
|
|
@@ -30496,7 +30744,7 @@ cmd_estimate() {
|
|
|
30496
30744
|
echo "Install Bun (https://bun.sh) or remove LOKI_LEGACY_BASH=1." >&2
|
|
30497
30745
|
return 1
|
|
30498
30746
|
fi
|
|
30499
|
-
bun "$entry" estimate "$@"
|
|
30747
|
+
bun --no-env-file --config=/dev/null "$entry" estimate "$@"
|
|
30500
30748
|
}
|
|
30501
30749
|
|
|
30502
30750
|
# Transparent cost view (R3): per-run + per-project spend, model routing, and
|
|
@@ -36071,7 +36319,7 @@ cmd_control() {
|
|
|
36071
36319
|
return 1
|
|
36072
36320
|
fi
|
|
36073
36321
|
[ -f "$cli" ] || cli="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
36074
|
-
exec bun "$cli" control "$@"
|
|
36322
|
+
exec bun --no-env-file --config=/dev/null "$cli" control "$@"
|
|
36075
36323
|
}
|
|
36076
36324
|
|
|
36077
36325
|
cmd_wiki() {
|