loki-mode 11.7.1 → 11.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/backlog.py +40 -4
- package/autonomy/lib/chat.sh +3 -2
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +185 -106
- package/autonomy/trigger-server.py +550 -5
- package/autonomy/verify.sh +5 -0
- package/bin/loki +1 -1
- package/completions/_loki +34 -1
- package/completions/loki.bash +10 -3
- package/dashboard/__init__.py +1 -1
- package/docs/ARCHITECTURE.md +0 -1
- package/docs/CLI-REFERENCE.md +1 -1
- package/docs/COMPONENTS.md +1 -1
- package/docs/DEPLOY-PLAN.md +13 -3
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +1 -1
- package/docs/dev/architecture-reference.md +1 -1
- package/docs/sentry-intake.md +36 -0
- package/docs/v10/D61-SPEED.md +14 -0
- package/docs/v10/PROGRESS.md +22 -0
- package/loki-ts/dist/loki.js +962 -879
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/ask-tools-server.js +13 -4
- package/packages/control-plane/dist/server.js +614 -456
- package/packages/control-plane/src/server/diffstat.ts +10 -2
- package/packages/control-plane/src/server/legacy/routes-control.ts +8 -4
- package/packages/control-plane/src/server/routes/checkpoints.ts +11 -2
- package/packages/control-plane/ui/dist/assets/index-u8cd6TRQ.js +190 -0
- package/packages/control-plane/ui/dist/index.html +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-S3BYeapm.js → AdminPage-BdH9iL2I.js} +1 -1
- package/web-app/dist/assets/{Avatar-C1rHwLgn.js → Avatar-C-WixxR6.js} +1 -1
- package/web-app/dist/assets/{Badge-CzlnN5aQ.js → Badge-BFWpRd_L.js} +1 -1
- package/web-app/dist/assets/{Button-B1w5AeGC.js → Button-CE5vG66Y.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-Dyeqjfkc.js → CockpitPage-BAVn10bY.js} +1 -1
- package/web-app/dist/assets/{ComparePage-C6y2GkGR.js → ComparePage-DOecIGfM.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-Dg2fcGkR.js → ErrorBoundary-1rNA5v7f.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CxbyjvTj.js → EvidenceReceiptPanel-DjOaWfdH.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Dpys1xDb.js → GitHubIssuesPanel-NNPBuRPD.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-CRVjWoFl.js → GitHubPRsPanel-CL1b--tV.js} +1 -1
- package/web-app/dist/assets/{HomePage-BKnv-oYw.js → HomePage-BsLDN6MW.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BGksmFRd.js → LoginPage-CVIv5bCS.js} +1 -1
- package/web-app/dist/assets/{MagicPage-OCvhp16M.js → MagicPage-BgMBB9lK.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-CJOJpXWC.js → MetricsPage-BHeJlqPC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-C04p3QvN.js → NotFoundPage-DDzwfyDK.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-BRB8txsY.js → ProjectPage-CuQWEYBF.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-CoUYeQEh.js → ProjectsPage-OoD0RFDZ.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-BkjmJGfM.js → SettingsPage-tMFMKkK5.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-B4vU9TjQ.js → ShowcasePage-DVfLoWdz.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-Z2jiVsni.js → SystemSettingsPage-CThV9N4l.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-Ulm8SISq.js → TeamsPage-CV0nvXI8.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C_7MM6Ey.js → TemplatesPage-D81EI1L8.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Cumd7Tgs.js → TerminalOutput-Bj7uKRIe.js} +1 -1
- package/web-app/dist/assets/{activity-CZAUkbiY.js → activity-DoNfNkOL.js} +1 -1
- package/web-app/dist/assets/{bell-BOvSXpEr.js → bell-BWO3OU50.js} +1 -1
- package/web-app/dist/assets/{bot-DOTZxBYj.js → bot-DcvWv_zD.js} +1 -1
- package/web-app/dist/assets/{check-BxpE0B5u.js → check-CfG09Z18.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D6KPvBOA.js → chevron-left-z0fh4x2h.js} +1 -1
- package/web-app/dist/assets/{circle-alert-Bajq86oe.js → circle-alert-C_7GlYul.js} +1 -1
- package/web-app/dist/assets/{clock-CnlHAVJc.js → clock-BsYUp070.js} +1 -1
- package/web-app/dist/assets/{cloud-DjaymjwM.js → cloud-BVWAJ7Rv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BADEw2Ld.js → code-xml-CdZT-6zu.js} +1 -1
- package/web-app/dist/assets/{database-B4aKb5Cq.js → database-Dbkm9t2D.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DrldXpja.js → dollar-sign-CfIUM5Pd.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-DVebDVR-.js → file-code-corner-CrUKhnj-.js} +1 -1
- package/web-app/dist/assets/{file-plus-BD-N_-Tl.js → file-plus-Cf6u0xHY.js} +1 -1
- package/web-app/dist/assets/{globe-C7ieInC-.js → globe-DsIqaI7w.js} +1 -1
- package/web-app/dist/assets/{hammer-BSugKIji.js → hammer-CA14BeMp.js} +1 -1
- package/web-app/dist/assets/{index-nLQgfvOn.js → index-DpJ_GJuZ.js} +3 -3
- package/web-app/dist/assets/{layers-CHXxU2on.js → layers-Dh5Oqhrj.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BEEz0LbD.js → loader-circle-B0WHIGqt.js} +1 -1
- package/web-app/dist/assets/{lock-CzQGfcnE.js → lock-DkgfEGYF.js} +1 -1
- package/web-app/dist/assets/{package-CcHYQLGd.js → package-LzjmwD1S.js} +1 -1
- package/web-app/dist/assets/{plus-DdzgFEXn.js → plus-BNjA0KSV.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-7x0HbH9Y.js → refresh-cw-k3IacuDX.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-DBahX1Sp.js → rotate-ccw-BFON_xv-.js} +1 -1
- package/web-app/dist/assets/{scroll-text-BB8y8a9Z.js → scroll-text-CaIlSPZB.js} +1 -1
- package/web-app/dist/assets/{server-CfkbW2MH.js → server-CKFUccCG.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DUgD8rBF.js → shield-alert-Q4-rPo5r.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DGoyY6QC.js → thumbs-up-BYMeh8oV.js} +1 -1
- package/web-app/dist/assets/{trash-2-B6xrSUMr.js → trash-2-BHSUEIHw.js} +1 -1
- package/web-app/dist/assets/{trending-down-B6RGKTOx.js → trending-down-BC_FR2VT.js} +1 -1
- package/web-app/dist/assets/{trending-up-B44DCrt1.js → trending-up-CmA-WmLW.js} +1 -1
- package/web-app/dist/assets/{upload-4kWm33ZY.js → upload-BxLgBa1B.js} +1 -1
- package/web-app/dist/assets/{usePolling-zkUtRQG6.js → usePolling-DKNgwt5n.js} +1 -1
- package/web-app/dist/assets/{user-04cEVIe2.js → user-CA2DlmLc.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/memory/rag_injector.py +0 -161
- package/packages/control-plane/ui/dist/assets/index-DbLzr87u.js +0 -190
package/autonomy/loki
CHANGED
|
@@ -2703,13 +2703,14 @@ cmd_start() {
|
|
|
2703
2703
|
echo " --log-level LEVEL debug | info | warn | error (default: info)"
|
|
2704
2704
|
echo " --max-duration DUR Wall-clock cap: seconds, or 90m / 2h (default: none)"
|
|
2705
2705
|
echo ""
|
|
2706
|
-
echo "Issue-mode options (
|
|
2706
|
+
echo "Issue-mode options (used when input is an ISSUE-REF or a --brief):"
|
|
2707
2707
|
echo " --dry-run Preview generated PRD without starting"
|
|
2708
2708
|
echo " --no-start Generate PRD but don't start execution"
|
|
2709
2709
|
echo " --output FILE Save generated PRD to custom path"
|
|
2710
2710
|
echo " --worktree, -w Git worktree isolation (separate branch)"
|
|
2711
2711
|
echo " --pr Worktree + auto-create PR"
|
|
2712
2712
|
echo " --ship Worktree + PR + auto-merge"
|
|
2713
|
+
echo " --detach, -d Run in background (implies --worktree)"
|
|
2713
2714
|
echo ""
|
|
2714
2715
|
echo "Preview without spending (any input mode):"
|
|
2715
2716
|
echo " loki plan <PRD> Analyze a spec WITHOUT executing: complexity,"
|
|
@@ -3443,6 +3444,24 @@ cmd_start() {
|
|
|
3443
3444
|
return $?
|
|
3444
3445
|
fi
|
|
3445
3446
|
|
|
3447
|
+
# BRIEF + isolation flags (--pr/--worktree/--detach/--prepare-pr/--ship):
|
|
3448
|
+
# the flags were collected above but only the issue path consumed them, so a
|
|
3449
|
+
# delegated brief ran in the caller's checkout with no PR. Route it through
|
|
3450
|
+
# cmd_run (worktree, detach, receipt-bearing PR) as a tracker-less issue.
|
|
3451
|
+
if [ "$detected_type" = "brief" ] && [ "$issue_use_worktree" = "true" ]; then
|
|
3452
|
+
local brief_run_args=("--brief-text" "$brief_text")
|
|
3453
|
+
for a in "${issue_mode_args[@]+"${issue_mode_args[@]}"}"; do
|
|
3454
|
+
brief_run_args+=("$a")
|
|
3455
|
+
done
|
|
3456
|
+
for a in "${args[@]+"${args[@]}"}"; do
|
|
3457
|
+
brief_run_args+=("$a")
|
|
3458
|
+
done
|
|
3459
|
+
brief_run_args+=("--no-plan")
|
|
3460
|
+
export LOKI_UNIFIED_START=1
|
|
3461
|
+
cmd_run "${brief_run_args[@]}"
|
|
3462
|
+
return $?
|
|
3463
|
+
fi
|
|
3464
|
+
|
|
3446
3465
|
# R7 (zero-config first run): BRIEF mode. The user gave a one-line brief
|
|
3447
3466
|
# (`loki start "build a todo app"` or `loki start --brief "..."`). Synthesize
|
|
3448
3467
|
# a forward-looking PRD, switch to the lightweight TTFV profile (honest fast:
|
|
@@ -9751,7 +9770,11 @@ _deploy_latest_receipt() {
|
|
|
9751
9770
|
return 0
|
|
9752
9771
|
}
|
|
9753
9772
|
|
|
9754
|
-
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha>
|
|
9773
|
+
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha> [state] [health_status] [health_url] [rb_attempted] [rb_exit] [receipt_sha256] [started_at] [finished_at] [target] [canary]
|
|
9774
|
+
# Writes the executed deploy's own record, then signs it (loki.deploy/1, Ed25519 via the
|
|
9775
|
+
# receipt signer in loki-ts/src/util/receipt_signer.ts) so it is bound to the receipt that
|
|
9776
|
+
# authorized it; `loki verify <record>` re-checks it offline. An unsigned record is reported
|
|
9777
|
+
# loudly and verifies as UNCHECKED, never VERIFIED.
|
|
9755
9778
|
# Writes the executed deploy's own record. A deploy with no record of itself
|
|
9756
9779
|
# defeats the entire premise of gating on evidence.
|
|
9757
9780
|
#
|
|
@@ -9769,17 +9792,27 @@ _deploy_record() {
|
|
|
9769
9792
|
# Post-deploy health and rollback facts. "unchecked" never implies healthy.
|
|
9770
9793
|
local health_status="${7:-unchecked}" health_url="${8:-}"
|
|
9771
9794
|
local rb_attempted="${9:-false}" rb_exit="${10:-}"
|
|
9795
|
+
local receipt_sha="${11:-}" started_at="${12:-}" finished_at="${13:-}" target="${14:-}" canary="${15:-false}"
|
|
9772
9796
|
local ddir="$dir/.loki/deploys"
|
|
9773
9797
|
[ -d "${LOKI_DIR:-}" ] && ddir="${LOKI_DIR}/deploys"
|
|
9774
9798
|
mkdir -p "$ddir" 2>/dev/null || return 0
|
|
9775
9799
|
local f="$ddir/$(date -u +%Y%m%dT%H%M%SZ)-$$.json"
|
|
9776
9800
|
DR_CMD="$command" DR_RC="$rc" DR_RUN="$run_id" DR_ANCHOR="$anchor" DR_OUT="$f" \
|
|
9777
9801
|
DR_STATE="$anchor_state" DR_HS="$health_status" DR_HU="$health_url" \
|
|
9778
|
-
DR_RBA="$rb_attempted" DR_RBX="$rb_exit" \
|
|
9802
|
+
DR_RBA="$rb_attempted" DR_RBX="$rb_exit" DR_RSHA="$receipt_sha" DR_T0="$started_at" \
|
|
9803
|
+
DR_T1="$finished_at" DR_TGT="$target" DR_CAN="$canary" \
|
|
9779
9804
|
python3 -c '
|
|
9780
9805
|
import json, os, datetime
|
|
9781
9806
|
state = os.environ.get("DR_STATE") or "anchored"
|
|
9807
|
+
now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
9782
9808
|
json.dump({
|
|
9809
|
+
"schema": "loki.deploy/1",
|
|
9810
|
+
"receipt_sha256": os.environ.get("DR_RSHA", ""),
|
|
9811
|
+
"head_sha": os.environ["DR_ANCHOR"],
|
|
9812
|
+
"target": os.environ.get("DR_TGT", ""),
|
|
9813
|
+
"started_at": os.environ.get("DR_T0") or now,
|
|
9814
|
+
"finished_at": os.environ.get("DR_T1") or now,
|
|
9815
|
+
"canary": os.environ.get("DR_CAN") == "true",
|
|
9783
9816
|
"recorded_at": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
9784
9817
|
"command": os.environ["DR_CMD"],
|
|
9785
9818
|
"exit_code": int(os.environ["DR_RC"]),
|
|
@@ -9795,6 +9828,10 @@ json.dump({
|
|
|
9795
9828
|
"rollback_exit": int(os.environ["DR_RBX"]) if os.environ.get("DR_RBX", "") != "" else None,
|
|
9796
9829
|
}, open(os.environ["DR_OUT"], "w"), indent=2)
|
|
9797
9830
|
' 2>/dev/null || return 0
|
|
9831
|
+
# Sign through the one Bun implementation. A subshell, because the delegate execs.
|
|
9832
|
+
if ! ( _loki_bun_delegate engine10 verify --sign-deploy "$f" ) >/dev/null 2>&1; then
|
|
9833
|
+
echo -e "${YELLOW}WARNING: the deploy record is UNSIGNED (no signing key or Bun route); it verifies as UNCHECKED.${NC}" >&2
|
|
9834
|
+
fi
|
|
9798
9835
|
printf '%s' "$f"
|
|
9799
9836
|
return 0
|
|
9800
9837
|
}
|
|
@@ -9946,6 +9983,20 @@ _deploy_try_execute() {
|
|
|
9946
9983
|
return 1
|
|
9947
9984
|
fi
|
|
9948
9985
|
|
|
9986
|
+
# --canary uses only documented staged verbs; anything else refuses, naming why.
|
|
9987
|
+
local canary="${_LOKI_DEPLOY_CANARY:-false}" exec_cmd="$command" canary_kind=""
|
|
9988
|
+
if [ "$canary" = true ]; then
|
|
9989
|
+
case "$command" in
|
|
9990
|
+
"vercel --prod") canary_kind="vercel"; exec_cmd="vercel" ;;
|
|
9991
|
+
"fly deploy") canary_kind="fly"; exec_cmd="fly deploy --strategy canary" ;;
|
|
9992
|
+
*)
|
|
9993
|
+
echo -e "${RED}REFUSED: --canary is not supported for: ${command}${NC}" >&2
|
|
9994
|
+
echo " This provider documents no staged rollout verb (Vercel: deploy then 'vercel promote'; Fly: 'fly deploy --strategy canary'). Nothing was run." >&2
|
|
9995
|
+
return 1
|
|
9996
|
+
;;
|
|
9997
|
+
esac
|
|
9998
|
+
fi
|
|
9999
|
+
|
|
9949
10000
|
local reasons
|
|
9950
10001
|
reasons="$(_deploy_gate "$dir" "$command")"
|
|
9951
10002
|
|
|
@@ -10002,7 +10053,7 @@ _deploy_try_execute() {
|
|
|
10002
10053
|
else
|
|
10003
10054
|
echo -e "${GREEN}Evidence gate PASSED.${NC} Receipt ${run_id:-unknown} VERIFIED this tree (${anchor:0:12})."
|
|
10004
10055
|
fi
|
|
10005
|
-
echo "Running: ${command}"
|
|
10056
|
+
if [ "$canary" = true ]; then echo "Running (canary): ${exec_cmd}"; else echo "Running: ${command}"; fi
|
|
10006
10057
|
echo ""
|
|
10007
10058
|
|
|
10008
10059
|
# Run in the project dir. No credential env var is read, set, or forwarded:
|
|
@@ -10032,13 +10083,40 @@ _deploy_try_execute() {
|
|
|
10032
10083
|
esac
|
|
10033
10084
|
fi
|
|
10034
10085
|
|
|
10035
|
-
local rc=0
|
|
10036
|
-
|
|
10086
|
+
local rc=0 started_at finished_at purl="" canary_out=""
|
|
10087
|
+
started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
10088
|
+
local health_status="unchecked" rb_attempted=false rb_exit="" hcode=""
|
|
10089
|
+
if [ "$canary_kind" = vercel ]; then
|
|
10090
|
+
# Staged: a preview deploy (no --prod), probe the preview URL, promote only if healthy.
|
|
10091
|
+
# Nothing reaches production before the probe, so there is nothing to roll back.
|
|
10092
|
+
rb_cmd=""; auto_rollback=false
|
|
10093
|
+
exec_cmd="vercel"
|
|
10094
|
+
canary_out="$( cd "$dir" && eval "$exec_cmd" )" || rc=$?
|
|
10095
|
+
printf '%s\n' "$canary_out"
|
|
10096
|
+
purl="$(grep -Eo 'https?://[^[:space:]]+' <<<"$canary_out" | tail -n 1)"
|
|
10097
|
+
if [ "$rc" -eq 0 ] && [ -n "$purl" ]; then
|
|
10098
|
+
health_url="$purl"
|
|
10099
|
+
echo "Probing canary health: ${health_url}"
|
|
10100
|
+
if hcode="$(_deploy_probe_health "$health_url")"; then
|
|
10101
|
+
health_status="healthy"
|
|
10102
|
+
echo "Promoting: vercel promote ${purl}"
|
|
10103
|
+
exec_cmd="vercel; vercel promote ${purl}"
|
|
10104
|
+
( cd "$dir" && vercel promote "$purl" ) || rc=$?
|
|
10105
|
+
else
|
|
10106
|
+
health_status="FAILED_HEALTH"
|
|
10107
|
+
echo " The canary was NOT promoted. Production is unchanged." >&2
|
|
10108
|
+
fi
|
|
10109
|
+
elif [ "$rc" -eq 0 ]; then
|
|
10110
|
+
echo " The preview deploy printed no URL, so it cannot be health-checked and is NOT promoted." >&2
|
|
10111
|
+
rc=4
|
|
10112
|
+
fi
|
|
10113
|
+
else
|
|
10114
|
+
( cd "$dir" && eval "$exec_cmd" ) || rc=$?
|
|
10115
|
+
fi
|
|
10037
10116
|
|
|
10038
10117
|
# Health probe only after a deploy that exited 0. Without a URL, or after a
|
|
10039
10118
|
# failed deploy, status is "unchecked": never a claim the service is healthy.
|
|
10040
|
-
|
|
10041
|
-
if [ "$rc" -eq 0 ] && [ -n "$health_url" ]; then
|
|
10119
|
+
if [ "$canary_kind" != vercel ] && [ "$rc" -eq 0 ] && [ -n "$health_url" ]; then
|
|
10042
10120
|
echo "Probing health: ${health_url}"
|
|
10043
10121
|
if hcode="$(_deploy_probe_health "$health_url")"; then
|
|
10044
10122
|
health_status="healthy"
|
|
@@ -10066,9 +10144,12 @@ _deploy_try_execute() {
|
|
|
10066
10144
|
fi
|
|
10067
10145
|
fi
|
|
10068
10146
|
|
|
10069
|
-
local rec
|
|
10070
|
-
|
|
10071
|
-
|
|
10147
|
+
local rec rsha
|
|
10148
|
+
finished_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
10149
|
+
rsha="$(python3 -c 'import json,sys;print((json.load(open(sys.argv[1])).get("verification") or {}).get("hash") or "")' "$pj" 2>/dev/null || printf '')"
|
|
10150
|
+
rec="$(_deploy_record "$dir" "$exec_cmd" "$rc" "${run_id:-unknown}" "${anchor:-unknown}" \
|
|
10151
|
+
"$anchor_state" "$health_status" "$health_url" "$rb_attempted" "$rb_exit" \
|
|
10152
|
+
"$rsha" "$started_at" "$finished_at" "${command%% *}" "$canary")"
|
|
10072
10153
|
echo ""
|
|
10073
10154
|
if [ "$rc" -eq 0 ]; then
|
|
10074
10155
|
echo -e "${GREEN}Deploy command exited 0.${NC}"
|
|
@@ -10208,7 +10289,7 @@ cmd_deploy() {
|
|
|
10208
10289
|
# file alone weakens this gate.
|
|
10209
10290
|
local LOKI_DEPLOY_ALLOW_UNANCHORED=false
|
|
10210
10291
|
# Per-invocation like --execute; local so an ambient env var cannot set them.
|
|
10211
|
-
local _LOKI_DEPLOY_HEALTH_URL="" _LOKI_DEPLOY_AUTO_ROLLBACK=false
|
|
10292
|
+
local _LOKI_DEPLOY_HEALTH_URL="" _LOKI_DEPLOY_AUTO_ROLLBACK=false _LOKI_DEPLOY_CANARY=false
|
|
10212
10293
|
|
|
10213
10294
|
# Arg parse (mirror cmd_preview: lenient on unknown args -> no behavior drift).
|
|
10214
10295
|
while [ $# -gt 0 ]; do
|
|
@@ -10216,7 +10297,7 @@ cmd_deploy() {
|
|
|
10216
10297
|
--help|-h|help)
|
|
10217
10298
|
echo -e "${BOLD}Loki Mode -- deploy command advisory (print-only by default)${NC}"
|
|
10218
10299
|
echo ""
|
|
10219
|
-
echo "Usage: loki deploy [--dir <path>] [--no-clip] [--execute] [--health-url URL [--auto-rollback]]"
|
|
10300
|
+
echo "Usage: loki deploy [--dir <path>] [--no-clip] [--execute [--canary]] [--health-url URL [--auto-rollback]]"
|
|
10220
10301
|
echo ""
|
|
10221
10302
|
echo "Detects your project type and your installed cloud CLI (and any"
|
|
10222
10303
|
echo "CI/CD pipeline), then PRINTS the exact deploy command for YOU to run."
|
|
@@ -10283,6 +10364,18 @@ cmd_deploy() {
|
|
|
10283
10364
|
echo " print a manual note. Needs --health-url. The rolled-back"
|
|
10284
10365
|
echo " service is not re-probed."
|
|
10285
10366
|
echo ""
|
|
10367
|
+
echo "Staged rollout (only with --execute):"
|
|
10368
|
+
echo " --canary Vercel: deploy without --prod, health-check the preview"
|
|
10369
|
+
echo " URL, then run 'vercel promote <url>'; a failed probe never"
|
|
10370
|
+
echo " promotes. Fly: 'fly deploy --strategy canary'. Other"
|
|
10371
|
+
echo " providers document no staged path and refuse --canary."
|
|
10372
|
+
echo ""
|
|
10373
|
+
echo "The executed deploy's record in .loki/deploys/ is a signed loki.deploy/1"
|
|
10374
|
+
echo "attestation bound to the receipt (receipt_sha256). Re-check it offline with"
|
|
10375
|
+
echo "'loki verify <record.json>' (add --pubkey FILE to use a given key). It fails"
|
|
10376
|
+
echo "closed: a tampered, unsigned or unkeyed record, or a cited receipt that is not"
|
|
10377
|
+
echo "VERIFIED, is never reported as VERIFIED."
|
|
10378
|
+
echo ""
|
|
10286
10379
|
echo "Options:"
|
|
10287
10380
|
echo " --dir <path> Project directory to scan (default: current dir)"
|
|
10288
10381
|
echo " --no-clip Do not copy the idiomatic command to the clipboard"
|
|
@@ -10311,6 +10404,9 @@ cmd_deploy() {
|
|
|
10311
10404
|
--auto-rollback)
|
|
10312
10405
|
_LOKI_DEPLOY_AUTO_ROLLBACK=true
|
|
10313
10406
|
;;
|
|
10407
|
+
--canary)
|
|
10408
|
+
_LOKI_DEPLOY_CANARY=true
|
|
10409
|
+
;;
|
|
10314
10410
|
--dir)
|
|
10315
10411
|
# Guard against a missing OR flag-shaped value: an unguarded
|
|
10316
10412
|
# shift when --dir is the last arg underflows under set -e, and a
|
|
@@ -10332,6 +10428,10 @@ cmd_deploy() {
|
|
|
10332
10428
|
echo "loki deploy --auto-rollback requires --health-url (no health signal, no rollback)" >&2
|
|
10333
10429
|
return 1
|
|
10334
10430
|
fi
|
|
10431
|
+
if [ "$_LOKI_DEPLOY_CANARY" = true ] && [ "$do_execute" != true ]; then
|
|
10432
|
+
echo "loki deploy --canary requires --execute" >&2
|
|
10433
|
+
return 1
|
|
10434
|
+
fi
|
|
10335
10435
|
if [ "$do_execute" != true ] && [ -n "$_LOKI_DEPLOY_HEALTH_URL" ]; then
|
|
10336
10436
|
echo "Note: --health-url and --auto-rollback only apply with --execute; ignored on this advisory run." >&2
|
|
10337
10437
|
fi
|
|
@@ -10939,6 +11039,10 @@ cmd_run() {
|
|
|
10939
11039
|
local prepare_pr=false
|
|
10940
11040
|
local auto_merge=false
|
|
10941
11041
|
local run_detached=false
|
|
11042
|
+
# Internal: set by cmd_start when a one-line brief carries --pr/--worktree/
|
|
11043
|
+
# --detach. The brief is treated as a tracker-less issue so it reuses the
|
|
11044
|
+
# worktree, detach and PR machinery below instead of a parallel copy.
|
|
11045
|
+
local brief_in=""
|
|
10942
11046
|
|
|
10943
11047
|
# Parse arguments
|
|
10944
11048
|
while [[ $# -gt 0 ]]; do
|
|
@@ -11128,6 +11232,15 @@ cmd_run() {
|
|
|
11128
11232
|
--config=*|--vars=*|--env-file=*)
|
|
11129
11233
|
shift
|
|
11130
11234
|
;;
|
|
11235
|
+
--brief-text)
|
|
11236
|
+
if [ "$#" -ge 2 ]; then
|
|
11237
|
+
brief_in="$2"
|
|
11238
|
+
shift 2
|
|
11239
|
+
else
|
|
11240
|
+
echo -e "${RED}--brief-text requires text${NC}"
|
|
11241
|
+
exit 1
|
|
11242
|
+
fi
|
|
11243
|
+
;;
|
|
11131
11244
|
-*)
|
|
11132
11245
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
11133
11246
|
echo "Run 'loki run --help' for usage."
|
|
@@ -11163,6 +11276,10 @@ cmd_run() {
|
|
|
11163
11276
|
start_args+=("--parallel")
|
|
11164
11277
|
fi
|
|
11165
11278
|
|
|
11279
|
+
if [[ -n "$brief_in" ]]; then
|
|
11280
|
+
issue_ref="brief"
|
|
11281
|
+
fi
|
|
11282
|
+
|
|
11166
11283
|
if [[ -z "$issue_ref" ]]; then
|
|
11167
11284
|
echo -e "${RED}Error: Issue reference required${NC}"
|
|
11168
11285
|
echo ""
|
|
@@ -11202,16 +11319,33 @@ cmd_run() {
|
|
|
11202
11319
|
|
|
11203
11320
|
# Detect provider
|
|
11204
11321
|
local issue_provider
|
|
11205
|
-
issue_provider=$(detect_issue_provider "$issue_ref")
|
|
11206
|
-
echo -e "${CYAN}Issue provider:${NC} ${issue_provider}"
|
|
11207
|
-
|
|
11208
|
-
# Fetch the issue
|
|
11209
|
-
echo -e "${CYAN}Fetching issue...${NC}"
|
|
11210
11322
|
local issue_json
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11323
|
+
if [[ -n "$brief_in" ]]; then
|
|
11324
|
+
# Brief: no tracker to fetch from. The text travels in env so quoting in
|
|
11325
|
+
# the brief can never reach the shell or python source.
|
|
11326
|
+
issue_provider="brief"
|
|
11327
|
+
echo -e "${CYAN}Source:${NC} one-line brief"
|
|
11328
|
+
issue_json=$(LOKI_BRIEF_IN="$brief_in" LOKI_BRIEF_TS="$(date +%s)" python3 -c "
|
|
11329
|
+
import json, os
|
|
11330
|
+
t = os.environ['LOKI_BRIEF_IN']
|
|
11331
|
+
first = t.strip().splitlines()[0] if t.strip() else 'brief'
|
|
11332
|
+
print(json.dumps({'provider': 'brief', 'number': 'b' + os.environ['LOKI_BRIEF_TS'],
|
|
11333
|
+
'title': first[:80], 'body': t, 'labels': [], 'url': '', 'repo': ''}))
|
|
11334
|
+
") || {
|
|
11335
|
+
echo -e "${RED}Failed to build brief${NC}"
|
|
11336
|
+
exit 1
|
|
11337
|
+
}
|
|
11338
|
+
else
|
|
11339
|
+
issue_provider=$(detect_issue_provider "$issue_ref")
|
|
11340
|
+
echo -e "${CYAN}Issue provider:${NC} ${issue_provider}"
|
|
11341
|
+
|
|
11342
|
+
# Fetch the issue
|
|
11343
|
+
echo -e "${CYAN}Fetching issue...${NC}"
|
|
11344
|
+
issue_json=$(fetch_issue "$issue_ref") || {
|
|
11345
|
+
echo -e "${RED}Failed to fetch issue${NC}"
|
|
11346
|
+
exit 1
|
|
11347
|
+
}
|
|
11348
|
+
fi
|
|
11215
11349
|
|
|
11216
11350
|
# Extract fields for display
|
|
11217
11351
|
local title number url
|
|
@@ -11557,7 +11691,7 @@ ${_gh_receipt}"
|
|
|
11557
11691
|
fi
|
|
11558
11692
|
|
|
11559
11693
|
case "${issue_provider:-github}" in
|
|
11560
|
-
github)
|
|
11694
|
+
github|brief)
|
|
11561
11695
|
if command -v gh &>/dev/null; then
|
|
11562
11696
|
local branch_current
|
|
11563
11697
|
branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
@@ -11609,7 +11743,7 @@ ${_gh_receipt}"
|
|
|
11609
11743
|
branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
11610
11744
|
if gh pr merge "$branch_current" --squash --delete-branch 2>/dev/null; then
|
|
11611
11745
|
echo -e "${GREEN}PR merged and branch deleted${NC}"
|
|
11612
|
-
if [[
|
|
11746
|
+
if [[ "${number:-}" =~ ^[0-9]+$ ]]; then
|
|
11613
11747
|
gh issue close "$number" --comment "Resolved by Loki Mode" 2>/dev/null || true
|
|
11614
11748
|
fi
|
|
11615
11749
|
else
|
|
@@ -18134,9 +18268,15 @@ cmd_verify() {
|
|
|
18134
18268
|
# verified by the engine10 verifier directly; bin/loki sends a bare verify back here.
|
|
18135
18269
|
for _pk_arg in "$@"; do
|
|
18136
18270
|
case "$_pk_arg" in
|
|
18137
|
-
composite.json|*/composite.json|--seal-composite|--seal-composite
|
|
18271
|
+
--sign-deploy|--receipt|composite.json|*/composite.json|--seal-composite|--seal-composite=*|--max-age|--max-age=*|.loki/groups/*/receipt.json|*/.loki/groups/*/receipt.json)
|
|
18138
18272
|
_loki_bun_delegate engine10 verify "$@"
|
|
18139
18273
|
return $? ;;
|
|
18274
|
+
# A signed deploy record (.loki/deploys/*.json, schema loki.deploy/1) is re-checked by the same verifier.
|
|
18275
|
+
*.json)
|
|
18276
|
+
if [ -f "$_pk_arg" ] && grep -q 'loki\.deploy/1' "$_pk_arg" 2>/dev/null; then
|
|
18277
|
+
_loki_bun_delegate engine10 verify "$@"
|
|
18278
|
+
return $?
|
|
18279
|
+
fi ;;
|
|
18140
18280
|
esac
|
|
18141
18281
|
done
|
|
18142
18282
|
# --fast: deterministic checks only, in milliseconds. Measured on this repo
|
|
@@ -28012,93 +28152,32 @@ cmd_trust_metrics() {
|
|
|
28012
28152
|
python3 "$tm_mod" --loki-dir "$loki_dir" ${pass_args[@]+"${pass_args[@]}"}
|
|
28013
28153
|
}
|
|
28014
28154
|
|
|
28015
|
-
#
|
|
28016
|
-
#
|
|
28017
|
-
#
|
|
28018
|
-
# for
|
|
28019
|
-
# never collected, never a fabricated $0.00.
|
|
28020
|
-
# Forward cost projection. Wraps tools/estimate-run.py.
|
|
28021
|
-
#
|
|
28022
|
-
# --iterations is REQUIRED by that tool by design: no multi-run history exists
|
|
28023
|
-
# to derive a horizon from, so projecting a total without one would be guessing
|
|
28024
|
-
# at the single number the operator asked for. We surface that requirement here
|
|
28025
|
-
# rather than inventing a default, for the same reason cost reports UNKNOWN
|
|
28026
|
-
# instead of $0.00 -- an unearned number is worse than an absent one.
|
|
28155
|
+
# loki estimate: one estimator. The estimate is loki-ts/src/runner/router/cost_preview.ts,
|
|
28156
|
+
# the same one 'loki start' prints; this arm only hands off to the Bun CLI (bin/loki is the
|
|
28157
|
+
# normal route; this covers 'loki help estimate' and a direct bash invocation). tools/estimate-run.py
|
|
28158
|
+
# stays for its other callers (preflight, model-advisor) but is no longer reachable from here.
|
|
28027
28159
|
cmd_estimate() {
|
|
28028
|
-
local
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28032
|
-
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
echo ""
|
|
28036
|
-
echo "Usage: loki estimate --iterations N [--json]"
|
|
28037
|
-
echo ""
|
|
28038
|
-
echo "Projects a run cost from this workspace's MEASURED history in"
|
|
28039
|
-
echo ".loki/metrics/efficiency/. With no measured history there is no"
|
|
28040
|
-
echo "basis, and it says so rather than returning a fabricated number."
|
|
28041
|
-
echo ""
|
|
28042
|
-
echo "Options:"
|
|
28043
|
-
echo " --iterations N REQUIRED. How many iterations to project."
|
|
28044
|
-
echo " No multi-run history exists to derive a horizon"
|
|
28045
|
-
echo " from, so there is no honest default."
|
|
28046
|
-
echo " --json Machine-readable JSON output"
|
|
28047
|
-
echo " --help, -h Show this help"
|
|
28048
|
-
echo ""
|
|
28049
|
-
echo "Examples:"
|
|
28050
|
-
echo " loki estimate --iterations 10"
|
|
28051
|
-
echo " loki estimate --iterations 25 --json"
|
|
28052
|
-
echo ""
|
|
28053
|
-
echo "See also: loki cost --detail (what a run ALREADY spent)"
|
|
28054
|
-
return 0
|
|
28055
|
-
;;
|
|
28056
|
-
--json) show_json=true; shift ;;
|
|
28057
|
-
--iterations) iterations="${2:-}"; shift 2 || shift ;;
|
|
28058
|
-
--iterations=*) iterations="${1#*=}"; shift ;;
|
|
28059
|
-
*) echo -e "${RED}Unknown option: $1${NC}" >&2; echo "Run 'loki estimate --help' for usage." >&2; return 1 ;;
|
|
28060
|
-
esac
|
|
28061
|
-
done
|
|
28062
|
-
|
|
28063
|
-
if [ -z "$iterations" ]; then
|
|
28064
|
-
echo -e "${RED}--iterations is required${NC}" >&2
|
|
28065
|
-
echo "" >&2
|
|
28066
|
-
echo "No multi-run history exists to derive a horizon from, so there is no" >&2
|
|
28067
|
-
echo "honest default for how many iterations to project. Say how many you" >&2
|
|
28068
|
-
echo "expect and the projection is scaled from measured per-iteration cost." >&2
|
|
28069
|
-
echo "" >&2
|
|
28070
|
-
echo " loki estimate --iterations 10" >&2
|
|
28071
|
-
echo "" >&2
|
|
28072
|
-
echo "Run 'loki estimate --help' for usage." >&2
|
|
28073
|
-
return 1
|
|
28074
|
-
fi
|
|
28075
|
-
|
|
28076
|
-
case "$iterations" in
|
|
28077
|
-
''|*[!0-9]*)
|
|
28078
|
-
echo -e "${RED}--iterations must be a positive whole number (got: $iterations)${NC}" >&2
|
|
28079
|
-
return 1
|
|
28080
|
-
;;
|
|
28081
|
-
esac
|
|
28082
|
-
|
|
28083
|
-
local estimate_py="$SKILL_DIR/tools/estimate-run.py"
|
|
28084
|
-
if ! command -v python3 >/dev/null 2>&1; then
|
|
28085
|
-
echo -e "${RED}python3 is required for 'loki estimate'${NC}" >&2
|
|
28086
|
-
echo "Install python3 and re-run." >&2
|
|
28087
|
-
return 1
|
|
28160
|
+
local entry="${LOKI_TS_ENTRY:-}"
|
|
28161
|
+
if [ -z "$entry" ]; then
|
|
28162
|
+
if [ -f "$SKILL_DIR/loki-ts/dist/loki.js" ]; then
|
|
28163
|
+
entry="$SKILL_DIR/loki-ts/dist/loki.js"
|
|
28164
|
+
elif [ -f "$SKILL_DIR/loki-ts/src/cli.ts" ]; then
|
|
28165
|
+
entry="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
28166
|
+
fi
|
|
28088
28167
|
fi
|
|
28089
|
-
if [ ! -f "$
|
|
28090
|
-
echo -e "${RED}
|
|
28091
|
-
echo "
|
|
28168
|
+
if [ -z "$entry" ] || [ ! -f "$entry" ] || ! command -v bun >/dev/null 2>&1; then
|
|
28169
|
+
echo -e "${RED}loki estimate requires the Bun runtime and is not available on the bash route.${NC}" >&2
|
|
28170
|
+
echo "Install Bun (https://bun.sh) or remove LOKI_LEGACY_BASH=1." >&2
|
|
28092
28171
|
return 1
|
|
28093
28172
|
fi
|
|
28094
|
-
|
|
28095
|
-
# Same workspace derivation as 'loki cost --detail': the tool takes the
|
|
28096
|
-
# workspace and appends .loki itself, while LOKI_DIR is the .loki path.
|
|
28097
|
-
local est_args=("$(dirname "${LOKI_DIR:-.loki}")" "--iterations" "$iterations")
|
|
28098
|
-
[ "$show_json" = "true" ] && est_args+=("--json")
|
|
28099
|
-
python3 "$estimate_py" "${est_args[@]}"
|
|
28173
|
+
bun "$entry" estimate "$@"
|
|
28100
28174
|
}
|
|
28101
28175
|
|
|
28176
|
+
# Transparent cost view (R3): per-run + per-project spend, model routing, and
|
|
28177
|
+
# budget status with the 80% warn line. Reuses efficiency_cost.collect_efficiency
|
|
28178
|
+
# for the current-run aggregate (single source of truth) and reads .loki/proofs/
|
|
28179
|
+
# for persistent per-run history. Honest: prints "not recorded" when cost was
|
|
28180
|
+
# never collected, never a fabricated $0.00.
|
|
28102
28181
|
cmd_cost() {
|
|
28103
28182
|
local show_json=false
|
|
28104
28183
|
local last_n=0
|