loki-mode 11.89.0 → 11.91.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.
Files changed (72) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/backlog.py +9 -0
  4. package/autonomy/loki +120 -14
  5. package/completions/_loki +7 -4
  6. package/completions/loki.bash +3 -3
  7. package/dashboard/__init__.py +1 -1
  8. package/docs/CLI-REFERENCE.md +1 -1
  9. package/docs/INSTALLATION.md +1 -1
  10. package/docs/v11/VERIFY-PR-SANDBOX.md +2 -0
  11. package/loki-ts/dist/loki.js +1390 -1381
  12. package/mcp/__init__.py +1 -1
  13. package/package.json +2 -2
  14. package/packages/control-plane/dist/server.js +14664 -14373
  15. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  16. package/web-app/dist/assets/{AdminPage-BQLmlBoU.js → AdminPage-9ggwVri1.js} +1 -1
  17. package/web-app/dist/assets/{Avatar-sPMrfkwo.js → Avatar-B8FkIL3q.js} +1 -1
  18. package/web-app/dist/assets/{Badge-D6bf5ZNR.js → Badge-DUwOrMSE.js} +1 -1
  19. package/web-app/dist/assets/{Button-BKMmzs4p.js → Button-CfNOtUNu.js} +1 -1
  20. package/web-app/dist/assets/{CockpitPage-Cobt_89E.js → CockpitPage-Tp4zfLg3.js} +1 -1
  21. package/web-app/dist/assets/{ComparePage-D3_PQ05B.js → ComparePage-CazDogm2.js} +1 -1
  22. package/web-app/dist/assets/{ErrorBoundary-YMrMfXT1.js → ErrorBoundary-CSOUEOuI.js} +1 -1
  23. package/web-app/dist/assets/{EvidenceReceiptPanel-wFjp7S3Y.js → EvidenceReceiptPanel-BPny501Q.js} +1 -1
  24. package/web-app/dist/assets/{GitHubIssuesPanel-DpkjZRyt.js → GitHubIssuesPanel-BhtfBprx.js} +1 -1
  25. package/web-app/dist/assets/{GitHubPRsPanel-AVnL3HWL.js → GitHubPRsPanel-Bx2bqoiA.js} +1 -1
  26. package/web-app/dist/assets/{HomePage-B0MtpdPg.js → HomePage-Bgn9XzXY.js} +2 -2
  27. package/web-app/dist/assets/{LoginPage-CC80bz-e.js → LoginPage-DEoEr8Qo.js} +1 -1
  28. package/web-app/dist/assets/{MagicPage-DVV83HJy.js → MagicPage-qVEnzwnK.js} +1 -1
  29. package/web-app/dist/assets/{MetricsPage-C5BkFI0J.js → MetricsPage-BCru8Vw7.js} +1 -1
  30. package/web-app/dist/assets/{NotFoundPage-s1rzn42S.js → NotFoundPage-BfyOjcyd.js} +1 -1
  31. package/web-app/dist/assets/{ProjectPage-B0agoWbD.js → ProjectPage-B6ofKWxG.js} +3 -3
  32. package/web-app/dist/assets/{ProjectsPage-BOm_XFI3.js → ProjectsPage-hEPpeCXF.js} +1 -1
  33. package/web-app/dist/assets/{SettingsPage-DQz1M_vu.js → SettingsPage-xnOPbkyg.js} +1 -1
  34. package/web-app/dist/assets/{ShowcasePage-B2TVXqEE.js → ShowcasePage-lhQPmInF.js} +1 -1
  35. package/web-app/dist/assets/{SystemSettingsPage-CrfoTgPb.js → SystemSettingsPage-B5UxUTUH.js} +1 -1
  36. package/web-app/dist/assets/{TeamsPage-BLGlLcfL.js → TeamsPage-D_BLZ_R6.js} +1 -1
  37. package/web-app/dist/assets/{TemplatesPage-iipnnIUw.js → TemplatesPage-CylVy_1x.js} +1 -1
  38. package/web-app/dist/assets/{TerminalOutput-B5pOWLP1.js → TerminalOutput-Brs2zmhu.js} +1 -1
  39. package/web-app/dist/assets/{activity-DNQZDHv_.js → activity-CKKwgBww.js} +1 -1
  40. package/web-app/dist/assets/{bell-BcOHxEZp.js → bell-Bg8nyAMu.js} +1 -1
  41. package/web-app/dist/assets/{bot-DJHk8DIS.js → bot-BDbOv-DW.js} +1 -1
  42. package/web-app/dist/assets/{check-DLcpqjjW.js → check-Coz1WWGB.js} +1 -1
  43. package/web-app/dist/assets/{chevron-left-BBmSCXx7.js → chevron-left-CeLweRux.js} +1 -1
  44. package/web-app/dist/assets/{circle-alert-B2CgD1z3.js → circle-alert-Cey6q-cN.js} +1 -1
  45. package/web-app/dist/assets/{clock-CLTNw-dw.js → clock-XtA5Pfpw.js} +1 -1
  46. package/web-app/dist/assets/{cloud-BMJEQNqp.js → cloud-FQ28jZRb.js} +1 -1
  47. package/web-app/dist/assets/{code-xml-BmFkHUyF.js → code-xml-vDF0KSYX.js} +1 -1
  48. package/web-app/dist/assets/{database-Bq9o-0ff.js → database-D_3JabMZ.js} +1 -1
  49. package/web-app/dist/assets/{dollar-sign-CPvl4BSY.js → dollar-sign-DuF13H12.js} +1 -1
  50. package/web-app/dist/assets/{file-code-corner-Ok780J5c.js → file-code-corner-DGQ1fKi8.js} +1 -1
  51. package/web-app/dist/assets/{file-plus-C-mvuzqz.js → file-plus-ClQjx4PE.js} +1 -1
  52. package/web-app/dist/assets/{globe-7nchOhax.js → globe-D_3mXRWy.js} +1 -1
  53. package/web-app/dist/assets/{hammer--C-eV19O.js → hammer-CuUomfVV.js} +1 -1
  54. package/web-app/dist/assets/{index-BY0d2rJT.js → index-D9-QtcQt.js} +3 -3
  55. package/web-app/dist/assets/{layers-B3T1id14.js → layers-B5R8eQiI.js} +1 -1
  56. package/web-app/dist/assets/{loader-circle-CtsL9AIK.js → loader-circle-B6mERdsr.js} +1 -1
  57. package/web-app/dist/assets/{lock-C5gtyC-I.js → lock-B4ySRbcK.js} +1 -1
  58. package/web-app/dist/assets/{package-BWa9VJ_J.js → package-MHaPM4mF.js} +1 -1
  59. package/web-app/dist/assets/{plus-CbrYo3zc.js → plus-Cj08kEEm.js} +1 -1
  60. package/web-app/dist/assets/{refresh-cw-DpSr1VHS.js → refresh-cw-DThqwXZE.js} +1 -1
  61. package/web-app/dist/assets/{rotate-ccw-CGfdW9Iq.js → rotate-ccw-D6FVAHZp.js} +1 -1
  62. package/web-app/dist/assets/{scroll-text-DJQaMueH.js → scroll-text-1tS_QOZn.js} +1 -1
  63. package/web-app/dist/assets/{server-gW2YYTQK.js → server-CtMpOUbw.js} +1 -1
  64. package/web-app/dist/assets/{shield-alert-Q8pgbz-s.js → shield-alert-BlC4wmkJ.js} +1 -1
  65. package/web-app/dist/assets/{thumbs-up-Du9_EC4t.js → thumbs-up-B_AuhFxq.js} +1 -1
  66. package/web-app/dist/assets/{trash-2-D_P45JH4.js → trash-2-CBj_pRfh.js} +1 -1
  67. package/web-app/dist/assets/{trending-down-B4tyujmD.js → trending-down-BZ9AZ6D0.js} +1 -1
  68. package/web-app/dist/assets/{trending-up-CkSU2NRR.js → trending-up-BdO4ANqU.js} +1 -1
  69. package/web-app/dist/assets/{upload-Mh6nzuC2.js → upload-D1Cg5cAS.js} +1 -1
  70. package/web-app/dist/assets/{usePolling-BWRlQ0Ir.js → usePolling-IHqzRFVn.js} +1 -1
  71. package/web-app/dist/assets/{user-BF6I3BoG.js → user-1UKh3VZH.js} +1 -1
  72. 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.89.0
6
+ # Loki Mode v11.91.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
 
@@ -475,4 +475,4 @@ Implemented, planned and deprecated items, with the version each landed in: `ski
475
475
 
476
476
  ---
477
477
 
478
- **v11.89.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
478
+ **v11.91.0 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 11.89.0
1
+ 11.91.0
@@ -378,6 +378,15 @@ def main(argv):
378
378
  tmp = dst + ".tmp%d" % os.getpid()
379
379
  shutil.copyfile(src, tmp)
380
380
  os.replace(tmp, dst)
381
+ # The sealed Wall copies travel with the receipt: integrate reruns them on the merged tree after the worktree is gone.
382
+ wsrc, wdst = os.path.join(src_dir, "wall"), os.path.join(dst_dir, "wall")
383
+ if os.path.isdir(wsrc) and not os.path.islink(wsrc) and not os.path.islink(wdst):
384
+ shutil.rmtree(wdst, ignore_errors=True)
385
+ os.makedirs(wdst, mode=0o700, exist_ok=True)
386
+ for name in os.listdir(wsrc):
387
+ f = os.path.join(wsrc, name)
388
+ if os.path.isfile(f) and not os.path.islink(f):
389
+ shutil.copyfile(f, os.path.join(wdst, name))
381
390
  except OSError:
382
391
  pass
383
392
 
package/autonomy/loki CHANGED
@@ -28170,13 +28170,98 @@ _pr_record_merge() {
28170
28170
  return 0
28171
28171
  }
28172
28172
 
28173
+ # Owner opt-in earned merge (LOKI_EARNED_MERGE=1, off by default). Every path
28174
+ # the PR changes (the new name and, for a rename, the previous name, listed by
28175
+ # paginated REST and required to match the PR's changedFiles count) must have a
28176
+ # Seal record from `loki trust --json` (path_seals) with at least
28177
+ # LOKI_EARNED_MERGE_MIN_VERIFIED (default 20, floor 1) VERIFIED receipts and
28178
+ # exactly 0 reverts in git history. A revert count of null (history
28179
+ # unreadable), a missing record, an unreadable trust output or an unreadable or
28180
+ # truncated file list or an unreadable diff all refuse, as does any PR whose
28181
+ # diff touches a symlink (120000) or submodule (160000) mode, including a pure
28182
+ # rename of one (modes read from git on the base and head). Protected paths (.github/, .loki/, .env*,
28183
+ # .envrc, CODEOWNERS, plus the space-separated globs in
28184
+ # LOKI_EARNED_MERGE_PROTECTED), matched case-insensitively, always stay
28185
+ # PR-only. The reason for a refusal goes to stdout; the return code is 1.
28186
+ _pr_earned_merge_check() {
28187
+ local pr="$1" min="${LOKI_EARNED_MERGE_MIN_VERIFIED:-20}" meta="" num="" want="" lines="" seals=""
28188
+ local kind p lp g row v r got=0 globs=() diff="" bad_mode="" renames="" refs="" base="" head="" from to m_old m_new
28189
+ case "$min" in ''|*[!0-9]*) min=20 ;; esac
28190
+ [ "$min" -ge 1 ] || min=1
28191
+ command -v jq >/dev/null 2>&1 || { echo "jq is required to read the Seal record"; return 1; }
28192
+ meta="$(gh pr view "$pr" --json number,changedFiles -q '"\(.number) \(.changedFiles)"' 2>/dev/null)" || meta=""
28193
+ num="${meta%% *}"; want="${meta#* }"
28194
+ case "$num$want" in ''|*[!0-9]*) echo "the PR's changed files are unreadable"; return 1 ;; esac
28195
+ lines="$(gh api --paginate "repos/{owner}/{repo}/pulls/${num}/files" -q '.[] | ("F\t" + .filename), (select(.previous_filename != null) | "P\t" + .previous_filename)' 2>/dev/null)" || lines=""
28196
+ [ -n "$lines" ] || { echo "the PR's changed files are unreadable"; return 1; }
28197
+ seals="$("$_LOKI_SCRIPT_DIR/../bin/loki" trust --json 2>/dev/null)" || seals=""
28198
+ printf '%s' "$seals" | jq -e '.path_seals | type == "array"' >/dev/null 2>&1 || { echo "the Seal record is unreadable"; return 1; }
28199
+ # read -a splits on whitespace without expanding globs against the cwd.
28200
+ read -r -a globs <<EOF_GLOBS
28201
+ $(printf '%s' "${LOKI_EARNED_MERGE_PROTECTED:-}" | tr '[:upper:]' '[:lower:]')
28202
+ EOF_GLOBS
28203
+ while IFS="$(printf '\t')" read -r kind p; do
28204
+ [ -n "$p" ] || continue
28205
+ [ "$kind" = "F" ] && got=$((got + 1))
28206
+ lp="$(printf '%s' "$p" | tr '[:upper:]' '[:lower:]')"
28207
+ case "$lp" in .github/*|.loki/*|.env|.env.*|*/.env|*/.env.*|.envrc|*/.envrc|codeowners|docs/codeowners)
28208
+ echo "$p is a protected path"; return 1 ;;
28209
+ esac
28210
+ for g in ${globs[@]+"${globs[@]}"}; do
28211
+ # shellcheck disable=SC2254
28212
+ case "$lp" in $g) echo "$p is a protected path"; return 1 ;; esac
28213
+ done
28214
+ row="$(printf '%s' "$seals" | jq -r --arg p "$p" '[.path_seals[] | select(.path == $p)] | if length == 0 then "none" else "\(.[0].verified) \(.[0].reverts)" end')" || row="none"
28215
+ [ "$row" != "none" ] || { echo "$p has no Seal record"; return 1; }
28216
+ v="${row%% *}"; r="${row#* }"
28217
+ case "$r" in
28218
+ 0) ;;
28219
+ null) echo "$p revert status is unknown"; return 1 ;;
28220
+ *) echo "$p has $r recorded revert(s)"; return 1 ;;
28221
+ esac
28222
+ [ "$v" -ge "$min" ] 2>/dev/null || { echo "$p has $v VERIFIED receipts, need $min"; return 1; }
28223
+ done <<EOF_FILES
28224
+ $lines
28225
+ EOF_FILES
28226
+ [ "$got" = "$want" ] || { echo "the PR lists $want changed files but $got were readable"; return 1; }
28227
+ # A symlink (120000) or submodule (160000) can point an earned path anywhere:
28228
+ # refuse any PR whose diff adds, removes, changes or re-modes one.
28229
+ diff="$(gh pr diff "$pr" 2>/dev/null)" || diff=""
28230
+ [ -n "$diff" ] || { echo "the PR's file modes are unreadable"; return 1; }
28231
+ bad_mode="$(printf '%s\n' "$diff" | awk '/^diff --git /{h=$0} /^(new file mode|deleted file mode|old mode|new mode) (120000|160000)$/{print h; exit} /^index [0-9a-f.]+ (120000|160000)$/{print h; exit}')"
28232
+ [ -z "$bad_mode" ] || { echo "${bad_mode#diff --git } involves a symlink or submodule"; return 1; }
28233
+ # A pure rename (100% similarity) carries no mode or index line, so the scan
28234
+ # above cannot see a symlink moving. Read both sides' modes from git.
28235
+ renames="$(printf '%s\n' "$diff" | awk '/^rename from /{f=substr($0,13)} /^rename to /{print f "\t" substr($0,11); f=""}')"
28236
+ if [ -n "$renames" ]; then
28237
+ refs="$(gh pr view "$pr" --json baseRefOid,headRefOid -q '.baseRefOid + " " + .headRefOid' 2>/dev/null)" || refs=""
28238
+ base="${refs%% *}"; head="${refs#* }"
28239
+ case "$base$head" in ''|*[!0-9a-f]*) echo "the PR's rename modes are unreadable"; return 1 ;; esac
28240
+ if ! git cat-file -e "${base}^{commit}" 2>/dev/null || ! git cat-file -e "${head}^{commit}" 2>/dev/null; then
28241
+ GIT_TERMINAL_PROMPT=0 git fetch -q origin "pull/${num}/head" >/dev/null 2>&1 || true
28242
+ fi
28243
+ while IFS="$(printf '\t')" read -r from to; do
28244
+ [ -n "$to" ] || continue
28245
+ m_old="$(git ls-tree --full-tree "$base" -- "$from" 2>/dev/null | awk '{print $1; exit}')"
28246
+ m_new="$(git ls-tree --full-tree "$head" -- "$to" 2>/dev/null | awk '{print $1; exit}')"
28247
+ case "$m_old:$m_new" in
28248
+ 120000:*|160000:*|*:120000|*:160000) echo "rename from $from to $to involves a symlink or submodule"; return 1 ;;
28249
+ '':*|*:'') echo "rename from $from to $to has unreadable file modes"; return 1 ;;
28250
+ esac
28251
+ done <<EOF_RENAMES
28252
+ $renames
28253
+ EOF_RENAMES
28254
+ fi
28255
+ return 0
28256
+ }
28257
+
28173
28258
  # Shared merge gate. One PR (number or branch) merges only when it is not a
28174
28259
  # draft, `gh pr checks` exits 0 after polling pending (rc=8), and, with
28175
28260
  # --require-verified, every clean proof recorded for the PR's head commit
28176
28261
  # (facts.git.head_sha == headRefOid, dirty-tree proofs ignored) says VERIFIED; a stale or missing proof
28177
28262
  # refuses ("gate: head"). The merge passes --match-head-commit so a push after
28178
28263
  # the gate cannot land unjudged. On refusal it prints the exact blocking gate
28179
- # ("gate: draft|head|verdict|checks") and leaves the PR open.
28264
+ # ("gate: draft|head|verdict|trust|checks") and leaves the PR open.
28180
28265
  # Usage: _merge_pr_gated <pr> [--require-verified] [--delete-branch]
28181
28266
  _merge_pr_gated() {
28182
28267
  local pr="$1" need_verified=false margs=(--squash) rc d
@@ -28252,6 +28337,14 @@ _merge_pr_gated() {
28252
28337
  return 1
28253
28338
  fi
28254
28339
  fi
28340
+ if [ "${LOKI_EARNED_MERGE:-0}" = "1" ]; then
28341
+ local trust_why=""
28342
+ trust_why="$(_pr_earned_merge_check "$pr")" || {
28343
+ echo "$pr: not merged, gate: trust ($trust_why); PR left open" >&2
28344
+ _pr_record_merge "$pr" "blocked:trust" "trust"
28345
+ return 1
28346
+ }
28347
+ fi
28255
28348
  rc=0; _pr_checks_wait "$pr" || rc=$?
28256
28349
  if [ "$rc" != "0" ]; then
28257
28350
  echo "$pr: not merged, gate: checks (gh pr checks rc=$rc); PR left open" >&2
@@ -31180,6 +31273,7 @@ cmd_metrics() {
31180
31273
  local last_n=0
31181
31274
  local save_file=false
31182
31275
  local share_flag=false
31276
+ local share_visibility=""
31183
31277
 
31184
31278
  while [[ $# -gt 0 ]]; do
31185
31279
  case "$1" in
@@ -31196,7 +31290,8 @@ cmd_metrics() {
31196
31290
  echo " --json Machine-readable JSON output"
31197
31291
  echo " --last N Analyze only the last N sessions (default: all)"
31198
31292
  echo " --save Write report to METRICS.md in project root"
31199
- echo " --share Upload report as GitHub Gist (via loki share)"
31293
+ echo " --share Upload report as a secret GitHub Gist (via loki share)"
31294
+ echo " --public With --share, create a PUBLIC gist instead"
31200
31295
  echo " --help, -h Show this help"
31201
31296
  echo ""
31202
31297
  echo "Subcommands:"
@@ -31207,7 +31302,7 @@ cmd_metrics() {
31207
31302
  echo " loki metrics --json # Machine-readable output"
31208
31303
  echo " loki metrics --last 5 # Last 5 sessions only"
31209
31304
  echo " loki metrics --save # Write METRICS.md"
31210
- echo " loki metrics --share # Share as GitHub Gist"
31305
+ echo " loki metrics --share # Share as a secret GitHub Gist"
31211
31306
  echo " loki metrics prometheus # Prometheus metrics from dashboard"
31212
31307
  exit 0
31213
31308
  ;;
@@ -31216,6 +31311,7 @@ cmd_metrics() {
31216
31311
  --last=*) last_n="${1#*=}"; shift ;;
31217
31312
  --save) save_file=true; shift ;;
31218
31313
  --share) share_flag=true; shift ;;
31314
+ --public) share_visibility="--public"; shift ;;
31219
31315
  prometheus)
31220
31316
  # Legacy Prometheus metrics subcommand
31221
31317
  shift
@@ -31638,7 +31734,10 @@ METRICS_SCRIPT
31638
31734
  project_name=$(basename "$(pwd)")
31639
31735
  local gist_desc="Loki Mode productivity report - ${project_name:-project} ($(date +%Y-%m-%d))"
31640
31736
  local gist_url
31641
- gist_url=$(gh gist create "$tmpfile" --desc "$gist_desc" --public 2>&1)
31737
+ if [ "$share_visibility" = "--public" ]; then
31738
+ echo -e "${YELLOW}Warning: this gist is PUBLIC and visible to anyone with the URL.${NC}" >&2
31739
+ fi
31740
+ gist_url=$(gh gist create "$tmpfile" --desc "$gist_desc" $share_visibility 2>&1)
31642
31741
  local gist_exit=$?
31643
31742
  rm -f "$tmpfile"
31644
31743
 
@@ -38845,7 +38944,8 @@ REPORT_SCRIPT
38845
38944
 
38846
38945
  cmd_share() {
38847
38946
  local format="markdown"
38848
- local visibility="--public"
38947
+ # Secret by default: --public is passed to gh only on an explicit --public.
38948
+ local visibility=""
38849
38949
 
38850
38950
  while [[ $# -gt 0 ]]; do
38851
38951
  case "$1" in
@@ -38858,20 +38958,22 @@ cmd_share() {
38858
38958
  echo "returning a shareable URL."
38859
38959
  echo ""
38860
38960
  echo "Options:"
38861
- echo " --private Create a secret gist (default: public)"
38961
+ echo " --public Create a PUBLIC gist, visible to anyone with the URL"
38962
+ echo " --private Create a secret gist (default: secret; accepted for compatibility)"
38862
38963
  echo " --format <fmt> Report format: text, markdown, html (default: markdown)"
38863
38964
  echo " --help, -h Show this help"
38864
38965
  echo ""
38865
38966
  echo "Examples:"
38866
- echo " loki share # Public markdown gist"
38867
- echo " loki share --private # Secret gist"
38868
- echo " loki share --format html # Public HTML gist"
38869
- echo " loki share --private --format text # Secret text gist"
38967
+ echo " loki share # Secret markdown gist (default)"
38968
+ echo " loki share --public # Public gist"
38969
+ echo " loki share --format html # Secret HTML gist"
38970
+ echo " loki share --public --format text # Public text gist"
38870
38971
  echo ""
38871
38972
  echo "Requires: gh CLI (https://cli.github.com)"
38872
38973
  exit 0
38873
38974
  ;;
38874
38975
  --private) visibility=""; shift ;;
38976
+ --public) visibility="--public"; shift ;;
38875
38977
  --format) format="${2:-markdown}"; shift 2 ;;
38876
38978
  --format=*) format="${1#*=}"; shift ;;
38877
38979
  *) echo -e "${RED}Unknown option: $1${NC}" >&2; echo "Run 'loki share --help' for usage." >&2; exit 1 ;;
@@ -38945,8 +39047,8 @@ cmd_share() {
38945
39047
  }
38946
39048
 
38947
39049
  # Internal helper: upload a single file as a GitHub Gist.
38948
- # Args: $1=file path, $2=description, $3=visibility ("--public", "--private",
38949
- # or "" for gh's default). NOTE: $3 is intentionally left unquoted at the
39050
+ # Args: $1=file path, $2=description, $3=visibility ("--public" for a public
39051
+ # gist, or "" for gh's default, which is a secret gist). NOTE: $3 is intentionally left unquoted at the
38950
39052
  # gh call site so an empty value collapses (the --private case sets it to "").
38951
39053
  # Removes the input file after the attempt. Prints "Shared: <url>" on success;
38952
39054
  # on failure prints the gh error and exits 1.
@@ -38961,6 +39063,9 @@ _loki_gist_upload() {
38961
39063
  # "Shared:" line and the ready-to-post hook with multi-line gh chatter.
38962
39064
  local gist_out gist_err gist_exit
38963
39065
  local errfile
39066
+ if [ "$visibility" = "--public" ]; then
39067
+ echo -e "${YELLOW}Warning: this gist is PUBLIC and visible to anyone with the URL.${NC}" >&2
39068
+ fi
38964
39069
  # Fall back to /dev/null if mktemp fails (disk full / unwritable /tmp) so the
38965
39070
  # redirect target is always a real path -- never an empty string (2>"" is a
38966
39071
  # bash error). We lose the captured stderr text in that rare case, not the
@@ -39425,7 +39530,8 @@ cmd_proof() {
39425
39530
  echo ""
39426
39531
  echo "Options for 'share':"
39427
39532
  echo " --yes Skip the redaction-preview confirmation prompt"
39428
- echo " --private Create a secret gist (default: public)"
39533
+ echo " --public Create a PUBLIC gist, visible to anyone with the URL"
39534
+ echo " --private Create a secret gist (default: secret; accepted for compatibility)"
39429
39535
  echo " --hosted Publish to LOKI_HOSTED_ENDPOINT (open-core seam; no official backend yet)"
39430
39536
  echo ""
39431
39537
  echo "Proofs are generated automatically at run completion (LOKI_PROOF=0 to opt out)."
@@ -40083,7 +40189,7 @@ print(json.dumps(doc, indent=2))
40083
40189
  share)
40084
40190
  local id=""
40085
40191
  local skip_confirm=0
40086
- local visibility="--public"
40192
+ local visibility=""
40087
40193
  local hosted=0
40088
40194
  # every python3 invocation below reads proof.json from inside
40089
40195
  # the checkout being shared. Resolve an absolute path once so an
package/completions/_loki CHANGED
@@ -100,7 +100,7 @@ function _loki {
100
100
  estimate)
101
101
  _arguments \
102
102
  '--json[Output machine-readable JSON]' \
103
- '--model[Model whose rough prior to use]:model:' \
103
+ '--model[Model whose first-run prior to use]:model:' \
104
104
  '--provider[Provider whose default model is used]:provider:' \
105
105
  '--help[Show help]'
106
106
  ;;
@@ -185,7 +185,8 @@ function _loki {
185
185
  '--json[Machine-readable JSON output]' \
186
186
  '--last[Analyze last N sessions]:number:' \
187
187
  '--save[Write report to METRICS.md]' \
188
- '--share[Upload report as GitHub Gist]' \
188
+ '--share[Upload report as a secret GitHub Gist]' \
189
+ '--public[With --share, create a PUBLIC gist]' \
189
190
  '--help[Show help]' \
190
191
  '1:subcommand:(prometheus)'
191
192
  ;;
@@ -199,14 +200,16 @@ function _loki {
199
200
  ;;
200
201
  share)
201
202
  _arguments \
202
- '--private[Create a secret gist]' \
203
+ '--public[Create a PUBLIC gist, visible to anyone with the URL]' \
204
+ '--private[Create a secret gist (default)]' \
203
205
  '--format[Report format]:format:(text markdown html)' \
204
206
  '--help[Show help]'
205
207
  ;;
206
208
  proof)
207
209
  _arguments \
208
210
  '--yes[Skip redaction-preview confirmation]' \
209
- '--private[Create a secret gist when sharing]' \
211
+ '--public[Create a PUBLIC gist when sharing]' \
212
+ '--private[Create a secret gist when sharing (default)]' \
210
213
  '--hosted[Hosted publishing (coming in R9)]' \
211
214
  '--dry-run[Scan and list what share would publish; publishes nothing]' \
212
215
  '--help[Show help]' \
@@ -355,7 +355,7 @@ _loki_completion() {
355
355
 
356
356
  metrics)
357
357
  if [[ "$cur" == -* ]]; then
358
- COMPREPLY=( $(compgen -W "--json --last --save --share --help" -- "$cur") )
358
+ COMPREPLY=( $(compgen -W "--json --last --save --share --public --help" -- "$cur") )
359
359
  return 0
360
360
  fi
361
361
  local metrics_cmds="prometheus"
@@ -373,7 +373,7 @@ _loki_completion() {
373
373
 
374
374
  share)
375
375
  if [[ "$cur" == -* ]]; then
376
- COMPREPLY=( $(compgen -W "--private --format --help" -- "$cur") )
376
+ COMPREPLY=( $(compgen -W "--public --private --format --help" -- "$cur") )
377
377
  return 0
378
378
  fi
379
379
  if [[ "$prev" == "--format" ]]; then
@@ -384,7 +384,7 @@ _loki_completion() {
384
384
 
385
385
  proof)
386
386
  if [[ "$cur" == -* ]]; then
387
- COMPREPLY=( $(compgen -W "--yes --private --hosted --dry-run --out --help" -- "$cur") )
387
+ COMPREPLY=( $(compgen -W "--yes --public --private --hosted --dry-run --out --help" -- "$cur") )
388
388
  return 0
389
389
  fi
390
390
  if [[ $cword -eq 2 ]]; then
@@ -7,6 +7,6 @@ Modules:
7
7
  control: Session control helpers (status, atomic writes, origin check)
8
8
  """
9
9
 
10
- __version__ = "11.89.0"
10
+ __version__ = "11.91.0"
11
11
 
12
12
  __all__ = ["__version__"]
@@ -1,6 +1,6 @@
1
1
  # CLI reference
2
2
 
3
- Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.89.0. Do not edit by hand.
3
+ Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.91.0. Do not edit by hand.
4
4
  Hidden and legacy-only commands are omitted; see `docs/CLI-INVENTORY.md` for the full inventory.
5
5
 
6
6
  | Command | Description |
@@ -2,7 +2,7 @@
2
2
 
3
3
  Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
4
4
 
5
- **Version:** v11.89.0
5
+ **Version:** v11.91.0
6
6
 
7
7
  **Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
8
8
 
@@ -25,6 +25,7 @@ verify-pr-sandbox.sh --repo DIR --out DIR --cmd 'SHELL CMD' --image IMG [--timeo
25
25
  | Env scrub | `docker run` inherits no host env; only PATH, HOME=/work, CI=1 are passed. GH_TOKEN, GITHUB_TOKEN, SSH_AUTH_SOCK never reach argv |
26
26
  | Mounts | exactly one: the repo, read-only. No HOME, no docker socket, no output mount |
27
27
  | Output | stdout is captured by the host, capped in-container (`--max-output`); the host writes result.json and output.txt |
28
+ | Report | `--report /work/NAME` returns the file the check's runner wrote there (up to 1 MiB, outside the output cap) between nonce-bearing markers; the host writes it as report.txt. verify-pr runs bun test, pytest, jest and vitest with a report flag and a random path, and reads pass/fail only from that file |
28
29
 
29
30
  Deviation from the card: there is no mounted output dir. A writable host mount would let the checked code fill the host disk, so the result travels on stdout and only the host writes `OUT`. Numeric flags are forced to base 10 (a leading zero is not octal) and every value flag requires a value. `--image` is restricted to `[A-Za-z0-9._/:@-]`.
30
31
 
@@ -35,6 +36,7 @@ Deviation from the card: there is no mounted output dir. A writable host mount w
35
36
  - Oversized output: capped by `--max-output`; `output_truncated` is set.
36
37
  - Fork bomb: `--pids-limit 256`. Memory bomb: `--memory`. Disk fill: tmpfs size caps.
37
38
  - Forged result: the checked code can print a fake `LOKI_VPR_END` line. A per-run 128-bit nonce is sent to the wrapper on stdin and the checked command runs with stdin from /dev/null; the host accepts only the last line carrying that nonce. Docker's default seccomp profile does NOT block ptrace or process_vm_readv on kernels >= 4.8, and the checked command runs as the same uid as the wrapper inside the container, so a hostile process can read the wrapper's memory and recover the nonce. The nonce therefore only stops accidental or naive forgery; it is not a security boundary. One cross-check is added: the wrapper exits with the check's code and the host rejects (status ERROR) a sentinel that disagrees with docker's own exit code. The PR code can still make its own check report success, which is why `result.json` is a signal about the check and not the final verdict (VPR-2 owns that, and it must not treat a bare COMPLETED exit 0 as proof). A custom seccomp profile denying ptrace is a possible hardening and is not shipped here.
39
+ - Forged report: the report path is a per-run random name given only on the runner's command line, so printed totals cannot forge it. The checked tests run inside the runner's process or its worker processes, so a hostile test that reads the path from the process command line can register an exit hook that rewrites the file after the runner has written it, and can still forge a pass; the same ptrace-class residual applies.
38
40
  - Docker pool exhaustion: reported as BLOCKED, never as a failing check.
39
41
 
40
42
  ## Not covered (residual, tracked for VPR-2 review)