loki-mode 11.8.1 → 11.10.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 +15 -3
- package/autonomy/lib/workspace.py +95 -4
- package/autonomy/loki +189 -108
- package/autonomy/trigger-server.py +302 -4
- 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 +15 -1
- package/docs/v10/D61-SPEED.md +2 -0
- package/docs/v10/PROGRESS.md +25 -1
- package/loki-ts/dist/loki.js +1016 -946
- 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 +457 -279
- 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-BnIg9ilA.js → AdminPage-D3PgG7SH.js} +1 -1
- package/web-app/dist/assets/{Avatar-0BTVsQ2A.js → Avatar-Ct77hvlG.js} +1 -1
- package/web-app/dist/assets/{Badge-Bbq8CXgn.js → Badge-Cg_gZaJF.js} +1 -1
- package/web-app/dist/assets/{Button-CKD4n0ld.js → Button-i9r2G84n.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-DJhLNLAz.js → CockpitPage-C3UyLXyp.js} +1 -1
- package/web-app/dist/assets/{ComparePage-Di2akuII.js → ComparePage-B1dXHga_.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-9lhsYG-z.js → ErrorBoundary-CQ_xREw0.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CQDGXvDr.js → EvidenceReceiptPanel-DwUxd_Xi.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-Y_w2k9Ej.js → GitHubIssuesPanel-C0jAnuAd.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-pMSQedCv.js → GitHubPRsPanel-B4pNXJMz.js} +1 -1
- package/web-app/dist/assets/{HomePage-CIAkiGdt.js → HomePage-BC9UTS1l.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BES4Pk6k.js → LoginPage-CnpdwK8F.js} +1 -1
- package/web-app/dist/assets/{MagicPage-B154fMhi.js → MagicPage-Bj0lZuNp.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BcNyaiUO.js → MetricsPage-yyw3VnfX.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-D28FqYxG.js → NotFoundPage-LMxkJP15.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-C_O3u8Jx.js → ProjectPage-2x0MRkNu.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-uqdahuPA.js → ProjectsPage-A8J8o1gj.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DLvVA9OZ.js → SettingsPage-fwvOVcRb.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-BAZP8vsm.js → ShowcasePage-DqI4sUwk.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-DPYZkLWh.js → SystemSettingsPage-CyytfNo_.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-BgSyb5Pd.js → TeamsPage-BTTe76W1.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-D_Jh5VU1.js → TemplatesPage-CIp3OTlL.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-4v8poDKb.js → TerminalOutput-dX5L4ky-.js} +1 -1
- package/web-app/dist/assets/{activity-CE2b6kdw.js → activity-uHXr2AaF.js} +1 -1
- package/web-app/dist/assets/{bell-CxJDlLSR.js → bell-D6RbxRMo.js} +1 -1
- package/web-app/dist/assets/{bot-C2WuK4zw.js → bot-D31w2Mu6.js} +1 -1
- package/web-app/dist/assets/{check-81epVldw.js → check-CoKJksbr.js} +1 -1
- package/web-app/dist/assets/{chevron-left-B6EXh_3C.js → chevron-left-Bjx7oX7s.js} +1 -1
- package/web-app/dist/assets/{circle-alert-0iqoo-iy.js → circle-alert-DOAh8Jmi.js} +1 -1
- package/web-app/dist/assets/{clock-CFrBpNUp.js → clock-DgEXPNWH.js} +1 -1
- package/web-app/dist/assets/{cloud-BBSFhUT_.js → cloud-0dXJFzBv.js} +1 -1
- package/web-app/dist/assets/{code-xml-BCU2V1rq.js → code-xml-DytQ_bq7.js} +1 -1
- package/web-app/dist/assets/{database-D2hvO52K.js → database-Qyjryb72.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-CXFA9b6T.js → dollar-sign-BzhTVy1e.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CqTCEEfS.js → file-code-corner-DTdSOALw.js} +1 -1
- package/web-app/dist/assets/{file-plus-DYFbkGbs.js → file-plus-VdjOG1RK.js} +1 -1
- package/web-app/dist/assets/{globe-DpYHLobk.js → globe-BhxNOfyM.js} +1 -1
- package/web-app/dist/assets/{hammer-CZ97SEYo.js → hammer-MQzrw8ee.js} +1 -1
- package/web-app/dist/assets/{index-DzswgpRo.js → index-DaxrMbHf.js} +3 -3
- package/web-app/dist/assets/{layers-2Tq0edx4.js → layers-C7nusTP-.js} +1 -1
- package/web-app/dist/assets/{loader-circle-BFP4kUX-.js → loader-circle-B1qBkgOY.js} +1 -1
- package/web-app/dist/assets/{lock-DkKdZH4d.js → lock-BdkOxK2O.js} +1 -1
- package/web-app/dist/assets/{package-ChOGD8yf.js → package-RVyK96Zn.js} +1 -1
- package/web-app/dist/assets/{plus-hggB1o6b.js → plus-ntmwFRrp.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DYwVLOeF.js → refresh-cw-BJ39b_PP.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CUdY506q.js → rotate-ccw-uPx_-3vz.js} +1 -1
- package/web-app/dist/assets/{scroll-text-Wvidz5uN.js → scroll-text-D9qvXkj-.js} +1 -1
- package/web-app/dist/assets/{server-O11QmkCN.js → server-gs4xmD8Y.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DXXx27uV.js → shield-alert-O2_YffGI.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-DF8ivs3A.js → thumbs-up-B3RVBhQx.js} +1 -1
- package/web-app/dist/assets/{trash-2-LA6PW0Nf.js → trash-2-DEUt7qhq.js} +1 -1
- package/web-app/dist/assets/{trending-down-Bw_vLKHT.js → trending-down-oyTns4UZ.js} +1 -1
- package/web-app/dist/assets/{trending-up-tX8motha.js → trending-up-CdpZK0Oc.js} +1 -1
- package/web-app/dist/assets/{upload-CxtTWJd2.js → upload-D9sbjjC8.js} +1 -1
- package/web-app/dist/assets/{usePolling-D-P5Iofq.js → usePolling-6ognrnyV.js} +1 -1
- package/web-app/dist/assets/{user-dKTcuUFq.js → user-CBKc8QoI.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
|
|
@@ -10971,6 +11075,7 @@ cmd_run() {
|
|
|
10971
11075
|
echo " --sandbox Run in Docker sandbox (default: off; requires Docker)"
|
|
10972
11076
|
echo " --no-plan Skip auto-shown PRD analysis at startup"
|
|
10973
11077
|
echo " --budget USD Per-run cost cap (default $(loki_budget_default_usd))"
|
|
11078
|
+
echo " --brief-text TEXT (internal) set by 'loki start --brief'; not for direct use"
|
|
10974
11079
|
echo ""
|
|
10975
11080
|
echo "Progressive Isolation:"
|
|
10976
11081
|
echo " --worktree, -w Git worktree isolation (separate branch)"
|
|
@@ -11128,6 +11233,15 @@ cmd_run() {
|
|
|
11128
11233
|
--config=*|--vars=*|--env-file=*)
|
|
11129
11234
|
shift
|
|
11130
11235
|
;;
|
|
11236
|
+
--brief-text)
|
|
11237
|
+
if [ "$#" -ge 2 ]; then
|
|
11238
|
+
brief_in="$2"
|
|
11239
|
+
shift 2
|
|
11240
|
+
else
|
|
11241
|
+
echo -e "${RED}--brief-text requires text${NC}"
|
|
11242
|
+
exit 1
|
|
11243
|
+
fi
|
|
11244
|
+
;;
|
|
11131
11245
|
-*)
|
|
11132
11246
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
11133
11247
|
echo "Run 'loki run --help' for usage."
|
|
@@ -11163,6 +11277,10 @@ cmd_run() {
|
|
|
11163
11277
|
start_args+=("--parallel")
|
|
11164
11278
|
fi
|
|
11165
11279
|
|
|
11280
|
+
if [[ -n "$brief_in" ]]; then
|
|
11281
|
+
issue_ref="brief"
|
|
11282
|
+
fi
|
|
11283
|
+
|
|
11166
11284
|
if [[ -z "$issue_ref" ]]; then
|
|
11167
11285
|
echo -e "${RED}Error: Issue reference required${NC}"
|
|
11168
11286
|
echo ""
|
|
@@ -11202,16 +11320,33 @@ cmd_run() {
|
|
|
11202
11320
|
|
|
11203
11321
|
# Detect provider
|
|
11204
11322
|
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
11323
|
local issue_json
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11324
|
+
if [[ -n "$brief_in" ]]; then
|
|
11325
|
+
# Brief: no tracker to fetch from. The text travels in env so quoting in
|
|
11326
|
+
# the brief can never reach the shell or python source.
|
|
11327
|
+
issue_provider="brief"
|
|
11328
|
+
echo -e "${CYAN}Source:${NC} one-line brief"
|
|
11329
|
+
issue_json=$(LOKI_BRIEF_IN="$brief_in" LOKI_BRIEF_TS="$(date +%s)" python3 -c "
|
|
11330
|
+
import json, os
|
|
11331
|
+
t = os.environ['LOKI_BRIEF_IN']
|
|
11332
|
+
first = t.strip().splitlines()[0] if t.strip() else 'brief'
|
|
11333
|
+
print(json.dumps({'provider': 'brief', 'number': 'b' + os.environ['LOKI_BRIEF_TS'],
|
|
11334
|
+
'title': first[:80], 'body': t, 'labels': [], 'url': '', 'repo': ''}))
|
|
11335
|
+
") || {
|
|
11336
|
+
echo -e "${RED}Failed to build brief${NC}"
|
|
11337
|
+
exit 1
|
|
11338
|
+
}
|
|
11339
|
+
else
|
|
11340
|
+
issue_provider=$(detect_issue_provider "$issue_ref")
|
|
11341
|
+
echo -e "${CYAN}Issue provider:${NC} ${issue_provider}"
|
|
11342
|
+
|
|
11343
|
+
# Fetch the issue
|
|
11344
|
+
echo -e "${CYAN}Fetching issue...${NC}"
|
|
11345
|
+
issue_json=$(fetch_issue "$issue_ref") || {
|
|
11346
|
+
echo -e "${RED}Failed to fetch issue${NC}"
|
|
11347
|
+
exit 1
|
|
11348
|
+
}
|
|
11349
|
+
fi
|
|
11215
11350
|
|
|
11216
11351
|
# Extract fields for display
|
|
11217
11352
|
local title number url
|
|
@@ -11357,7 +11492,8 @@ fi
|
|
|
11357
11492
|
if [[ "$LOKI_AUTO_MERGE" == "true" ]]; then
|
|
11358
11493
|
branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
11359
11494
|
if gh pr merge "$branch_current" --squash --delete-branch 2>/dev/null; then
|
|
11360
|
-
|
|
11495
|
+
# A brief run carries a synthetic ref (b<epoch>), not a real issue: close only numeric refs.
|
|
11496
|
+
if [[ "${LOKI_ISSUE_NUMBER:-}" =~ ^[0-9]+$ ]]; then
|
|
11361
11497
|
gh issue close "$LOKI_ISSUE_NUMBER" --comment "Resolved by Loki Mode" 2>/dev/null || true
|
|
11362
11498
|
fi
|
|
11363
11499
|
fi
|
|
@@ -11557,7 +11693,7 @@ ${_gh_receipt}"
|
|
|
11557
11693
|
fi
|
|
11558
11694
|
|
|
11559
11695
|
case "${issue_provider:-github}" in
|
|
11560
|
-
github)
|
|
11696
|
+
github|brief)
|
|
11561
11697
|
if command -v gh &>/dev/null; then
|
|
11562
11698
|
local branch_current
|
|
11563
11699
|
branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
@@ -11604,12 +11740,12 @@ ${_gh_receipt}"
|
|
|
11604
11740
|
if $auto_merge; then
|
|
11605
11741
|
echo -e "${GREEN}Auto-merging...${NC}"
|
|
11606
11742
|
case "${issue_provider:-github}" in
|
|
11607
|
-
github)
|
|
11743
|
+
github|brief)
|
|
11608
11744
|
local branch_current
|
|
11609
11745
|
branch_current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
11610
11746
|
if gh pr merge "$branch_current" --squash --delete-branch 2>/dev/null; then
|
|
11611
11747
|
echo -e "${GREEN}PR merged and branch deleted${NC}"
|
|
11612
|
-
if [[
|
|
11748
|
+
if [[ "${number:-}" =~ ^[0-9]+$ ]]; then
|
|
11613
11749
|
gh issue close "$number" --comment "Resolved by Loki Mode" 2>/dev/null || true
|
|
11614
11750
|
fi
|
|
11615
11751
|
else
|
|
@@ -18134,9 +18270,15 @@ cmd_verify() {
|
|
|
18134
18270
|
# verified by the engine10 verifier directly; bin/loki sends a bare verify back here.
|
|
18135
18271
|
for _pk_arg in "$@"; do
|
|
18136
18272
|
case "$_pk_arg" in
|
|
18137
|
-
composite.json|*/composite.json|--seal-composite|--seal-composite=*|--max-age|--max-age=*|.loki/groups/*/receipt.json|*/.loki/groups/*/receipt.json)
|
|
18273
|
+
--sign-deploy|--receipt|composite.json|*/composite.json|--seal-composite|--seal-composite=*|--max-age|--max-age=*|.loki/groups/*/receipt.json|*/.loki/groups/*/receipt.json)
|
|
18138
18274
|
_loki_bun_delegate engine10 verify "$@"
|
|
18139
18275
|
return $? ;;
|
|
18276
|
+
# A signed deploy record (.loki/deploys/*.json, schema loki.deploy/1) is re-checked by the same verifier.
|
|
18277
|
+
*.json)
|
|
18278
|
+
if [ -f "$_pk_arg" ] && grep -q 'loki\.deploy/1' "$_pk_arg" 2>/dev/null; then
|
|
18279
|
+
_loki_bun_delegate engine10 verify "$@"
|
|
18280
|
+
return $?
|
|
18281
|
+
fi ;;
|
|
18140
18282
|
esac
|
|
18141
18283
|
done
|
|
18142
18284
|
# --fast: deterministic checks only, in milliseconds. Measured on this repo
|
|
@@ -28012,93 +28154,32 @@ cmd_trust_metrics() {
|
|
|
28012
28154
|
python3 "$tm_mod" --loki-dir "$loki_dir" ${pass_args[@]+"${pass_args[@]}"}
|
|
28013
28155
|
}
|
|
28014
28156
|
|
|
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.
|
|
28157
|
+
# loki estimate: one estimator. The estimate is loki-ts/src/runner/router/cost_preview.ts,
|
|
28158
|
+
# the same one 'loki start' prints; this arm only hands off to the Bun CLI (bin/loki is the
|
|
28159
|
+
# normal route; this covers 'loki help estimate' and a direct bash invocation). tools/estimate-run.py
|
|
28160
|
+
# stays for its other callers (preflight, model-advisor) but is no longer reachable from here.
|
|
28027
28161
|
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
|
|
28162
|
+
local entry="${LOKI_TS_ENTRY:-}"
|
|
28163
|
+
if [ -z "$entry" ]; then
|
|
28164
|
+
if [ -f "$SKILL_DIR/loki-ts/dist/loki.js" ]; then
|
|
28165
|
+
entry="$SKILL_DIR/loki-ts/dist/loki.js"
|
|
28166
|
+
elif [ -f "$SKILL_DIR/loki-ts/src/cli.ts" ]; then
|
|
28167
|
+
entry="$SKILL_DIR/loki-ts/src/cli.ts"
|
|
28168
|
+
fi
|
|
28088
28169
|
fi
|
|
28089
|
-
if [ ! -f "$
|
|
28090
|
-
echo -e "${RED}
|
|
28091
|
-
echo "
|
|
28170
|
+
if [ -z "$entry" ] || [ ! -f "$entry" ] || ! command -v bun >/dev/null 2>&1; then
|
|
28171
|
+
echo -e "${RED}loki estimate requires the Bun runtime and is not available on the bash route.${NC}" >&2
|
|
28172
|
+
echo "Install Bun (https://bun.sh) or remove LOKI_LEGACY_BASH=1." >&2
|
|
28092
28173
|
return 1
|
|
28093
28174
|
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[@]}"
|
|
28175
|
+
bun "$entry" estimate "$@"
|
|
28100
28176
|
}
|
|
28101
28177
|
|
|
28178
|
+
# Transparent cost view (R3): per-run + per-project spend, model routing, and
|
|
28179
|
+
# budget status with the 80% warn line. Reuses efficiency_cost.collect_efficiency
|
|
28180
|
+
# for the current-run aggregate (single source of truth) and reads .loki/proofs/
|
|
28181
|
+
# for persistent per-run history. Honest: prints "not recorded" when cost was
|
|
28182
|
+
# never collected, never a fabricated $0.00.
|
|
28102
28183
|
cmd_cost() {
|
|
28103
28184
|
local show_json=false
|
|
28104
28185
|
local last_n=0
|