loki-mode 9.22.5 → 9.22.7
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/README.md +11 -4
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/issue-parser.sh +112 -0
- package/autonomy/issue-providers.sh +82 -11
- package/autonomy/lib/proof-generator.py +88 -0
- package/autonomy/lib/proof-pr.sh +52 -0
- package/autonomy/loki +147 -3
- package/autonomy/quickstart.sh +250 -5
- package/autonomy/run.sh +26 -0
- package/dashboard/__init__.py +1 -1
- package/docs/EXECUTION-COCKPIT-PLAN.md +289 -0
- package/docs/HARNESS-INTELLIGENCE-PLAN.md +194 -0
- package/docs/ISSUE-TO-PR-GOLDEN-PATH-PLAN.md +257 -0
- package/loki-ts/dist/loki.js +299 -297
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-DNGNuAge.js → AdminPage-DLhWX6si.js} +1 -1
- package/web-app/dist/assets/{Avatar-CqAveJaT.js → Avatar-CqyDcUxl.js} +1 -1
- package/web-app/dist/assets/{Badge-BtMejA0Z.js → Badge-BKGSt1n2.js} +1 -1
- package/web-app/dist/assets/{Button-CikxmqSs.js → Button-DuuKHXre.js} +1 -1
- package/web-app/dist/assets/CockpitPage-DdClYERC.js +47 -0
- package/web-app/dist/assets/{ComparePage-BB8DJBWR.js → ComparePage-CUWlqBxv.js} +1 -1
- package/web-app/dist/assets/ErrorBoundary-RwfGKyRJ.js +16 -0
- package/web-app/dist/assets/{EvidenceReceiptPanel-6oquKEjd.js → EvidenceReceiptPanel-CzIgk9sg.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-5XLYWzWH.js → GitHubIssuesPanel-CsXVKuyJ.js} +3 -3
- package/web-app/dist/assets/{GitHubPRsPanel-DUfmFYeu.js → GitHubPRsPanel-CYRp1eKS.js} +3 -3
- package/web-app/dist/assets/{HomePage-DZ4MmOdH.js → HomePage-CVJcI5pQ.js} +3 -3
- package/web-app/dist/assets/LoginPage-BBJNV9Qb.js +1 -0
- package/web-app/dist/assets/{MagicPage-CFbX5gWg.js → MagicPage-CIi9P8jK.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-DbL6xmRQ.js → MetricsPage-Bm34ipiy.js} +2 -2
- package/web-app/dist/assets/{NotFoundPage-BUrjXPAR.js → NotFoundPage-DpTbcC2P.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-iP6dJihg.js → ProjectPage-CY2SMGx9.js} +65 -90
- package/web-app/dist/assets/{ProjectsPage-BibN9bHl.js → ProjectsPage-DSrXUUKJ.js} +2 -2
- package/web-app/dist/assets/{SettingsPage-C0WJp1hW.js → SettingsPage-jvDyOqYi.js} +3 -3
- package/web-app/dist/assets/{ShowcasePage-DUv0Njp8.js → ShowcasePage-C8tFWKau.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-BIaCB0Vn.js → SystemSettingsPage-DH4FTH3K.js} +2 -2
- package/web-app/dist/assets/{TeamsPage-BE0szv9r.js → TeamsPage-C1fQYauF.js} +2 -2
- package/web-app/dist/assets/{TemplatesPage-BzkA9kat.js → TemplatesPage-CSTFt-4P.js} +1 -1
- package/web-app/dist/assets/TerminalOutput-Cu4NsyN6.js +16 -0
- package/web-app/dist/assets/{activity-AbfFz38r.js → activity-CtjsDzmh.js} +1 -1
- package/web-app/dist/assets/{bell-DF6Z6y5U.js → bell-C5es1xAs.js} +1 -1
- package/web-app/dist/assets/{bot-DQ4np_3n.js → bot-DE04U5Lc.js} +1 -1
- package/web-app/dist/assets/{check-RWuguCwE.js → check-DSKKA9EU.js} +1 -1
- package/web-app/dist/assets/{chevron-left-VpbQPOkC.js → chevron-left-DJ13W5AR.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DwT82lJx.js → circle-alert-CS0BfqAd.js} +1 -1
- package/web-app/dist/assets/{clock-BBlUa93G.js → clock-D1Y5nruk.js} +1 -1
- package/web-app/dist/assets/{cloud-Dgbz3VCA.js → cloud-nU6LBoZL.js} +1 -1
- package/web-app/dist/assets/{code-xml-r6qqION5.js → code-xml-BZfvIdFo.js} +1 -1
- package/web-app/dist/assets/{copy-DMHj52B8.js → copy-CK_2O1Xv.js} +1 -1
- package/web-app/dist/assets/{database-uJqHSHR1.js → database-DJiwznAm.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-BZagljek.js → dollar-sign-pO20A3QY.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-DMopmhu0.js → file-code-corner-Besy6vek.js} +1 -1
- package/web-app/dist/assets/{file-plus-DU_hT_3w.js → file-plus-DeaswxYY.js} +1 -1
- package/web-app/dist/assets/{folder-open-BP5TtQAW.js → folder-open-cr1UvQLI.js} +1 -1
- package/web-app/dist/assets/{git-commit-horizontal-zFk_IobS.js → git-commit-horizontal-BLxnfbSn.js} +1 -1
- package/web-app/dist/assets/{globe-mthCRtfp.js → globe-DrhG1NfX.js} +1 -1
- package/web-app/dist/assets/{hammer-BWaLBvSA.js → hammer-CeZtPBfE.js} +1 -1
- package/web-app/dist/assets/index-84JUN_aA.css +1 -0
- package/web-app/dist/assets/{index-BXA3UT6P.js → index-CLvjO22Z.js} +24 -24
- package/web-app/dist/assets/{layers-Q1LXV5fy.js → layers-fldADzyE.js} +1 -1
- package/web-app/dist/assets/{lightbulb-vRRNJ3dB.js → lightbulb-CznOfsPb.js} +1 -1
- package/web-app/dist/assets/{loader-circle-KgjeHEcV.js → loader-circle---lPB1Fs.js} +1 -1
- package/web-app/dist/assets/{lock-BerKqno7.js → lock-D5jVF-tO.js} +1 -1
- package/web-app/dist/assets/{mail-XAxTbprX.js → mail-D7HkLnlW.js} +1 -1
- package/web-app/dist/assets/minus-C-dT19Ct.js +6 -0
- package/web-app/dist/assets/{package-DLPKWAvW.js → package-Cm9B-cKE.js} +1 -1
- package/web-app/dist/assets/{plus-NMxxwHMn.js → plus-CQwOffLD.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-Dt42kYtM.js → refresh-cw-CvWJZLbN.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-BCR4Tp9u.js → rotate-ccw-C2Q1LYgM.js} +1 -1
- package/web-app/dist/assets/{save-sR4MJd42.js → save-BP0MvpoQ.js} +1 -1
- package/web-app/dist/assets/{server-SXkchokx.js → server-4i4VZXix.js} +1 -1
- package/web-app/dist/assets/{shield-alert-Bvgp6_Dz.js → shield-alert-D2_bgb7j.js} +1 -1
- package/web-app/dist/assets/thumbs-up-vACbzeXj.js +26 -0
- package/web-app/dist/assets/{trash-2-D0IGudH7.js → trash-2-EYjdESLs.js} +1 -1
- package/web-app/dist/assets/trending-down-BWBdcvE3.js +6 -0
- package/web-app/dist/assets/{trending-up-DDrJKL5i.js → trending-up-Bix6V_-Y.js} +1 -1
- package/web-app/dist/assets/{upload-C54rjN8K.js → upload-B7hh6ERF.js} +1 -1
- package/web-app/dist/assets/{usePolling-D7fw4YSM.js → usePolling-b_e-k-IN.js} +1 -1
- package/web-app/dist/assets/{user-Cg7fbzdh.js → user-pmjZKWEg.js} +1 -1
- package/web-app/dist/index.html +2 -2
- package/web-app/dist/assets/LoginPage-VMzI6ROD.js +0 -1
- package/web-app/dist/assets/TerminalOutput-DBrdT_Fp.js +0 -31
- package/web-app/dist/assets/index-DgpUuqwR.css +0 -1
- package/web-app/dist/assets/trending-down-BbFFBq-v.js +0 -11
package/autonomy/quickstart.sh
CHANGED
|
@@ -406,12 +406,21 @@ _qs_selected_provider() {
|
|
|
406
406
|
# non-zero if the estimator gave no result (caller falls back to a no-number
|
|
407
407
|
# confirm, never fabricating a figure).
|
|
408
408
|
_qs_emit_plan() {
|
|
409
|
-
local prd_path="$1" template_name="$2" json_output="${3:-false}" input_kind="${4:-idea}"
|
|
409
|
+
local prd_path="$1" template_name="$2" json_output="${3:-false}" input_kind="${4:-idea}" input_value="${5:-}"
|
|
410
|
+
local source_digest_before="" source_digest_after=""
|
|
411
|
+
if [ "$json_output" = true ] && [ "$input_kind" = "prd" ]; then
|
|
412
|
+
source_digest_before=$(shasum -a 256 "$prd_path" 2>/dev/null | awk '{print $1}') || return 1
|
|
413
|
+
[ -n "$source_digest_before" ] || return 1
|
|
414
|
+
fi
|
|
410
415
|
local plan_json=""
|
|
411
416
|
plan_json=$(show_prd_plan "$prd_path" "true" "false" 2>/dev/null) || plan_json=""
|
|
412
417
|
if [ -z "$plan_json" ]; then
|
|
413
418
|
return 1
|
|
414
419
|
fi
|
|
420
|
+
if [ "$json_output" = true ] && [ "$input_kind" = "prd" ]; then
|
|
421
|
+
source_digest_after=$(shasum -a 256 "$prd_path" 2>/dev/null | awk '{print $1}') || return 1
|
|
422
|
+
[ "$source_digest_before" = "$source_digest_after" ] || return 1
|
|
423
|
+
fi
|
|
415
424
|
local parsed
|
|
416
425
|
parsed=$(printf '%s' "$plan_json" | python3 -c "
|
|
417
426
|
import json, sys
|
|
@@ -447,7 +456,19 @@ import sys
|
|
|
447
456
|
plan = json.load(sys.stdin)
|
|
448
457
|
if not isinstance(plan, dict):
|
|
449
458
|
sys.exit(1)
|
|
450
|
-
template_name, input_kind = sys.argv[1:
|
|
459
|
+
template_name, input_kind, input_value, source_digest = sys.argv[1:5]
|
|
460
|
+
if input_kind == "idea":
|
|
461
|
+
continuation = {
|
|
462
|
+
"kind": "idea",
|
|
463
|
+
"template": template_name,
|
|
464
|
+
"value": input_value,
|
|
465
|
+
}
|
|
466
|
+
else:
|
|
467
|
+
continuation = {
|
|
468
|
+
"kind": "prd",
|
|
469
|
+
"path": input_value,
|
|
470
|
+
"sha256": source_digest,
|
|
471
|
+
}
|
|
451
472
|
payload = {
|
|
452
473
|
"schema_version": 1,
|
|
453
474
|
"command": "loki quickstart",
|
|
@@ -456,9 +477,10 @@ payload = {
|
|
|
456
477
|
"selected_template": template_name if input_kind == "idea" else None,
|
|
457
478
|
"source_name": template_name if input_kind == "prd" else None,
|
|
458
479
|
"plan": plan,
|
|
480
|
+
"continuation": continuation,
|
|
459
481
|
}
|
|
460
482
|
json.dump(payload, sys.stdout, separators=(",", ":"), sort_keys=True)
|
|
461
|
-
' "$template_name" "$input_kind" 2>/dev/null) || json_payload=""
|
|
483
|
+
' "$template_name" "$input_kind" "$input_value" "$source_digest_after" 2>/dev/null) || json_payload=""
|
|
462
484
|
[ -n "$json_payload" ] || return 1
|
|
463
485
|
printf '%s\n' "$json_payload" >&3
|
|
464
486
|
return 0
|
|
@@ -481,6 +503,111 @@ json.dump(payload, sys.stdout, separators=(",", ":"), sort_keys=True)
|
|
|
481
503
|
return 0
|
|
482
504
|
}
|
|
483
505
|
|
|
506
|
+
# _qs_load_preview <path>: validate one bounded schema-v1 preview before any
|
|
507
|
+
# provider, estimator, PRD, or build boundary. Only the continuation fields are
|
|
508
|
+
# handed to the shell. The saved plan is evidence, not execution authority;
|
|
509
|
+
# cmd_quickstart always recomputes and displays the current estimator result.
|
|
510
|
+
_qs_load_preview() {
|
|
511
|
+
local preview_path="$1"
|
|
512
|
+
if [ ! -f "$preview_path" ] || [ ! -r "$preview_path" ] || [ -L "$preview_path" ]; then
|
|
513
|
+
printf 'Preview path is not a readable regular non-symlink file: %s\n' "$preview_path" >&2
|
|
514
|
+
return 2
|
|
515
|
+
fi
|
|
516
|
+
local preview_size
|
|
517
|
+
preview_size=$(wc -c < "$preview_path" 2>/dev/null | tr -d '[:space:]') || return 2
|
|
518
|
+
case "$preview_size" in
|
|
519
|
+
""|*[!0-9]*) return 2;;
|
|
520
|
+
esac
|
|
521
|
+
if [ "$preview_size" -eq 0 ] || [ "$preview_size" -gt 1048576 ]; then
|
|
522
|
+
printf 'Preview JSON must be between 1 byte and 1 MiB.\n' >&2
|
|
523
|
+
return 2
|
|
524
|
+
fi
|
|
525
|
+
|
|
526
|
+
python3 - "$preview_path" <<'PY'
|
|
527
|
+
import base64
|
|
528
|
+
import json
|
|
529
|
+
import os
|
|
530
|
+
import re
|
|
531
|
+
import stat
|
|
532
|
+
import sys
|
|
533
|
+
|
|
534
|
+
path = sys.argv[1]
|
|
535
|
+
try:
|
|
536
|
+
flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)
|
|
537
|
+
descriptor = os.open(path, flags)
|
|
538
|
+
try:
|
|
539
|
+
metadata = os.fstat(descriptor)
|
|
540
|
+
if not stat.S_ISREG(metadata.st_mode) or metadata.st_size < 1 or metadata.st_size > 1048576:
|
|
541
|
+
raise ValueError("unsafe preview")
|
|
542
|
+
raw = os.read(descriptor, 1048577)
|
|
543
|
+
finally:
|
|
544
|
+
os.close(descriptor)
|
|
545
|
+
if len(raw) > 1048576:
|
|
546
|
+
raise ValueError("oversized")
|
|
547
|
+
def reject_duplicate_keys(pairs):
|
|
548
|
+
result = {}
|
|
549
|
+
for key, value in pairs:
|
|
550
|
+
if key in result:
|
|
551
|
+
raise ValueError("duplicate key")
|
|
552
|
+
result[key] = value
|
|
553
|
+
return result
|
|
554
|
+
|
|
555
|
+
payload = json.loads(raw, object_pairs_hook=reject_duplicate_keys)
|
|
556
|
+
except Exception:
|
|
557
|
+
sys.exit(2)
|
|
558
|
+
|
|
559
|
+
if not isinstance(payload, dict):
|
|
560
|
+
sys.exit(2)
|
|
561
|
+
if payload.get("schema_version") != 1:
|
|
562
|
+
sys.exit(2)
|
|
563
|
+
if payload.get("command") != "loki quickstart" or payload.get("mode") != "dry-run":
|
|
564
|
+
sys.exit(2)
|
|
565
|
+
if not isinstance(payload.get("plan"), dict) or not payload["plan"]:
|
|
566
|
+
sys.exit(2)
|
|
567
|
+
|
|
568
|
+
continuation = payload.get("continuation")
|
|
569
|
+
if not isinstance(continuation, dict):
|
|
570
|
+
sys.exit(2)
|
|
571
|
+
kind = continuation.get("kind")
|
|
572
|
+
|
|
573
|
+
def valid_text(value):
|
|
574
|
+
return (
|
|
575
|
+
isinstance(value, str)
|
|
576
|
+
and 0 < len(value) <= 4096
|
|
577
|
+
and not any(ord(char) < 32 or ord(char) == 127 for char in value)
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
if kind == "idea":
|
|
581
|
+
value = continuation.get("value")
|
|
582
|
+
template = continuation.get("template")
|
|
583
|
+
if not valid_text(value):
|
|
584
|
+
sys.exit(2)
|
|
585
|
+
if not isinstance(template, str) or re.fullmatch(r"[a-z0-9-]+", template) is None:
|
|
586
|
+
sys.exit(2)
|
|
587
|
+
if payload.get("input_kind") != "idea":
|
|
588
|
+
sys.exit(2)
|
|
589
|
+
if payload.get("selected_template") != template or payload.get("source_name") is not None:
|
|
590
|
+
sys.exit(2)
|
|
591
|
+
encoded = base64.b64encode(value.encode("utf-8")).decode("ascii")
|
|
592
|
+
print(f"idea|{template}|{encoded}|")
|
|
593
|
+
elif kind == "prd":
|
|
594
|
+
value = continuation.get("path")
|
|
595
|
+
digest = continuation.get("sha256")
|
|
596
|
+
if not valid_text(value):
|
|
597
|
+
sys.exit(2)
|
|
598
|
+
if not isinstance(digest, str) or re.fullmatch(r"[0-9a-f]{64}", digest) is None:
|
|
599
|
+
sys.exit(2)
|
|
600
|
+
if payload.get("input_kind") != "prd":
|
|
601
|
+
sys.exit(2)
|
|
602
|
+
if payload.get("selected_template") is not None or payload.get("source_name") != os.path.basename(value):
|
|
603
|
+
sys.exit(2)
|
|
604
|
+
encoded = base64.b64encode(value.encode("utf-8")).decode("ascii")
|
|
605
|
+
print(f"prd||{encoded}|{digest}")
|
|
606
|
+
else:
|
|
607
|
+
sys.exit(2)
|
|
608
|
+
PY
|
|
609
|
+
}
|
|
610
|
+
|
|
484
611
|
# _qs_help: concise usage for `loki quickstart --help`.
|
|
485
612
|
_qs_help() {
|
|
486
613
|
printf '%sloki quickstart%s - guided first build (setup, idea, template, plan, go)\n' "$_QS_BOLD" "$_QS_NC"
|
|
@@ -498,6 +625,7 @@ _qs_help() {
|
|
|
498
625
|
printf ' --yes, -y Auto-confirm the final build prompt (still shows the plan)\n'
|
|
499
626
|
printf ' --dry-run Preview the selected template and plan; write/start nothing\n'
|
|
500
627
|
printf ' --json With --dry-run, emit one machine-readable JSON object\n'
|
|
628
|
+
printf ' --from-preview F Continue a saved JSON preview; requires explicit --yes\n'
|
|
501
629
|
printf ' --template N Use the exact shipped template N for an IDEA\n'
|
|
502
630
|
printf ' --list-templates List every shipped template and its purpose\n'
|
|
503
631
|
printf ' --help, -h Show this help and exit\n'
|
|
@@ -515,6 +643,7 @@ _qs_help() {
|
|
|
515
643
|
printf ' An IDEA (or readable PRD path) is required. No provider is checked,\n'
|
|
516
644
|
printf ' no file is written, and no build is started. Do not combine with --yes.\n'
|
|
517
645
|
printf ' Add --json for versioned JSON only; --json requires --dry-run.\n'
|
|
646
|
+
printf ' Save that JSON, then continue it with --from-preview FILE --yes.\n'
|
|
518
647
|
printf '\n'
|
|
519
648
|
printf 'Steps:\n'
|
|
520
649
|
printf ' 1. Setup Check for an AI provider for execution (skipped in preview)\n'
|
|
@@ -550,6 +679,9 @@ cmd_quickstart() {
|
|
|
550
679
|
local template_flag_seen=false
|
|
551
680
|
local list_templates=false
|
|
552
681
|
local list_templates_flag_seen=false
|
|
682
|
+
local from_preview=""
|
|
683
|
+
local from_preview_flag_seen=false
|
|
684
|
+
local preview_prd_digest=""
|
|
553
685
|
if _qs_assume_yes; then assume_yes=true; fi
|
|
554
686
|
|
|
555
687
|
# yes_flag tracks EXPLICIT --yes/-y on THIS command's argv, and nothing else.
|
|
@@ -604,6 +736,19 @@ cmd_quickstart() {
|
|
|
604
736
|
list_templates_flag_seen=true
|
|
605
737
|
shift
|
|
606
738
|
;;
|
|
739
|
+
--from-preview)
|
|
740
|
+
if [ "$from_preview_flag_seen" = true ]; then
|
|
741
|
+
printf '%s--from-preview may be specified only once.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
742
|
+
exit 2
|
|
743
|
+
fi
|
|
744
|
+
from_preview_flag_seen=true
|
|
745
|
+
if [ $# -lt 2 ] || [ -z "${2:-}" ] || [[ "${2:-}" == --* ]]; then
|
|
746
|
+
printf '%s--from-preview requires a preview JSON path.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
747
|
+
exit 2
|
|
748
|
+
fi
|
|
749
|
+
from_preview="$2"
|
|
750
|
+
shift 2
|
|
751
|
+
;;
|
|
607
752
|
--*)
|
|
608
753
|
printf '%sUnknown option: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
|
|
609
754
|
printf "Run 'loki quickstart --help' for usage.\n" >&2
|
|
@@ -622,6 +767,52 @@ cmd_quickstart() {
|
|
|
622
767
|
esac
|
|
623
768
|
done
|
|
624
769
|
|
|
770
|
+
# Continuation is a standalone execution shape. Explicit argv consent is
|
|
771
|
+
# mandatory, and no caller-supplied input or selector may compete with the
|
|
772
|
+
# reviewed preview. Validation happens before every provider, estimator,
|
|
773
|
+
# PRD, and build boundary. Accepted values then rejoin the existing path.
|
|
774
|
+
if [ "$from_preview_flag_seen" = true ]; then
|
|
775
|
+
if [ -n "$positional" ] || [ "$yes_flag" != true ] || [ "$dry_run" = true ] || [ "$json_output" = true ] || [ "$template_flag_seen" = true ] || [ "$list_templates" = true ]; then
|
|
776
|
+
printf '%s--from-preview accepts only a preview JSON path and explicit --yes.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
777
|
+
exit 2
|
|
778
|
+
fi
|
|
779
|
+
local preview_fields="" preview_kind="" preview_template="" preview_encoded="" preview_digest="" preview_value=""
|
|
780
|
+
preview_fields=$(_qs_load_preview "$from_preview") || {
|
|
781
|
+
printf 'Preview JSON is malformed or incompatible.\n' >&2
|
|
782
|
+
exit 2
|
|
783
|
+
}
|
|
784
|
+
IFS='|' read -r preview_kind preview_template preview_encoded preview_digest <<< "$preview_fields"
|
|
785
|
+
preview_value=$(python3 -c 'import base64,sys; sys.stdout.buffer.write(base64.b64decode(sys.argv[1], validate=True))' "$preview_encoded" 2>/dev/null) || {
|
|
786
|
+
printf 'Preview JSON continuation is invalid.\n' >&2
|
|
787
|
+
exit 2
|
|
788
|
+
}
|
|
789
|
+
[ -n "$preview_value" ] || {
|
|
790
|
+
printf 'Preview JSON continuation is empty.\n' >&2
|
|
791
|
+
exit 2
|
|
792
|
+
}
|
|
793
|
+
if [ "$preview_kind" = "idea" ]; then
|
|
794
|
+
positional="$preview_value"
|
|
795
|
+
template_override="$preview_template"
|
|
796
|
+
template_flag_seen=true
|
|
797
|
+
elif [ "$preview_kind" = "prd" ]; then
|
|
798
|
+
positional="$preview_value"
|
|
799
|
+
if [ ! -f "$positional" ] || [ ! -r "$positional" ] || [ -L "$positional" ]; then
|
|
800
|
+
printf 'Preview PRD is not a readable regular non-symlink file: %s\n' "$positional" >&2
|
|
801
|
+
exit 2
|
|
802
|
+
fi
|
|
803
|
+
local current_digest=""
|
|
804
|
+
current_digest=$(shasum -a 256 "$positional" 2>/dev/null | awk '{print $1}') || current_digest=""
|
|
805
|
+
if [ -z "$current_digest" ] || [ "$current_digest" != "$preview_digest" ]; then
|
|
806
|
+
printf 'Preview PRD has changed since the saved preview; run --dry-run --json again.\n' >&2
|
|
807
|
+
exit 2
|
|
808
|
+
fi
|
|
809
|
+
preview_prd_digest="$preview_digest"
|
|
810
|
+
else
|
|
811
|
+
printf 'Preview JSON continuation kind is unsupported.\n' >&2
|
|
812
|
+
exit 2
|
|
813
|
+
fi
|
|
814
|
+
fi
|
|
815
|
+
|
|
625
816
|
# Discovery is a standalone read-only command shape. Refuse input and every
|
|
626
817
|
# execution/preview selector rather than guessing intent; --json is its only
|
|
627
818
|
# compatible modifier. This return precedes terminal, provider, estimator,
|
|
@@ -883,8 +1074,20 @@ cmd_quickstart() {
|
|
|
883
1074
|
# The non-interactive path shows it before execution rather than before a
|
|
884
1075
|
# prompt: the operator still gets the honest quote in the transcript, and it
|
|
885
1076
|
# is still the figure cmd_start runs with, so the quote equals the charge.
|
|
1077
|
+
if [ -n "$preview_prd_digest" ]; then
|
|
1078
|
+
local pre_estimate_digest=""
|
|
1079
|
+
if [ ! -f "$prd_source" ] || [ ! -r "$prd_source" ] || [ -L "$prd_source" ]; then
|
|
1080
|
+
printf 'Preview PRD changed before estimation; run --dry-run --json again.\n' >&2
|
|
1081
|
+
return 2
|
|
1082
|
+
fi
|
|
1083
|
+
pre_estimate_digest=$(shasum -a 256 "$prd_source" 2>/dev/null | awk '{print $1}') || pre_estimate_digest=""
|
|
1084
|
+
if [ "$pre_estimate_digest" != "$preview_prd_digest" ]; then
|
|
1085
|
+
printf 'Preview PRD changed before estimation; run --dry-run --json again.\n' >&2
|
|
1086
|
+
return 2
|
|
1087
|
+
fi
|
|
1088
|
+
fi
|
|
886
1089
|
local estimate_ok=true
|
|
887
|
-
if ! _qs_emit_plan "$prd_source" "$template_name" "$json_output" "$input_kind"; then
|
|
1090
|
+
if ! _qs_emit_plan "$prd_source" "$template_name" "$json_output" "$input_kind" "$positional"; then
|
|
888
1091
|
estimate_ok=false
|
|
889
1092
|
# No honest number available. Interactively this flips the confirm to
|
|
890
1093
|
# default-NO below. Non-interactively there is no one to review the
|
|
@@ -904,6 +1107,19 @@ cmd_quickstart() {
|
|
|
904
1107
|
fi
|
|
905
1108
|
fi
|
|
906
1109
|
|
|
1110
|
+
if [ -n "$preview_prd_digest" ]; then
|
|
1111
|
+
local post_estimate_digest=""
|
|
1112
|
+
if [ ! -f "$prd_source" ] || [ ! -r "$prd_source" ] || [ -L "$prd_source" ]; then
|
|
1113
|
+
printf 'Preview PRD changed during estimation; started no build.\n' >&2
|
|
1114
|
+
return 2
|
|
1115
|
+
fi
|
|
1116
|
+
post_estimate_digest=$(shasum -a 256 "$prd_source" 2>/dev/null | awk '{print $1}') || post_estimate_digest=""
|
|
1117
|
+
if [ "$post_estimate_digest" != "$preview_prd_digest" ]; then
|
|
1118
|
+
printf 'Preview PRD changed during estimation; started no build.\n' >&2
|
|
1119
|
+
return 2
|
|
1120
|
+
fi
|
|
1121
|
+
fi
|
|
1122
|
+
|
|
907
1123
|
# The preview terminal boundary is intentionally immediately after the
|
|
908
1124
|
# shared estimator. Reaching it proves the same template and plan were
|
|
909
1125
|
# rendered, while returning here makes the confirm, PRD copy, and cmd_start
|
|
@@ -968,7 +1184,36 @@ cmd_quickstart() {
|
|
|
968
1184
|
done
|
|
969
1185
|
fi
|
|
970
1186
|
fi
|
|
971
|
-
if
|
|
1187
|
+
if [ "$from_preview_flag_seen" = true ]; then
|
|
1188
|
+
# A reviewed continuation must publish exactly the bytes that were
|
|
1189
|
+
# previewed, and must never win a race by overwriting another file.
|
|
1190
|
+
# Stage in the destination directory, verify the staged bytes, then
|
|
1191
|
+
# claim the final name with an atomic hard link (which fails if another
|
|
1192
|
+
# process created it after the suffix walk). Removing the private stage
|
|
1193
|
+
# leaves the linked final file intact.
|
|
1194
|
+
local preview_stage="" preview_stage_digest=""
|
|
1195
|
+
preview_stage=$(mktemp "./.loki-quickstart-prd.XXXXXX") || {
|
|
1196
|
+
printf '%sCould not stage the reviewed PRD in the current directory.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
1197
|
+
return 2
|
|
1198
|
+
}
|
|
1199
|
+
if ! cp "$prd_source" "$preview_stage" 2>/dev/null; then
|
|
1200
|
+
rm -f "$preview_stage"
|
|
1201
|
+
printf '%sCould not stage the reviewed PRD in the current directory.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
1202
|
+
return 2
|
|
1203
|
+
fi
|
|
1204
|
+
preview_stage_digest=$(shasum -a 256 "$preview_stage" 2>/dev/null | awk '{print $1}') || preview_stage_digest=""
|
|
1205
|
+
if [ -z "$preview_stage_digest" ] || { [ -n "$preview_prd_digest" ] && [ "$preview_stage_digest" != "$preview_prd_digest" ]; }; then
|
|
1206
|
+
rm -f "$preview_stage"
|
|
1207
|
+
printf '%sReviewed PRD changed before publish; started no build.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
1208
|
+
return 2
|
|
1209
|
+
fi
|
|
1210
|
+
if ! ln "$preview_stage" "$target" 2>/dev/null; then
|
|
1211
|
+
rm -f "$preview_stage"
|
|
1212
|
+
printf '%sPRD destination changed before publish; started no build.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
1213
|
+
return 2
|
|
1214
|
+
fi
|
|
1215
|
+
rm -f "$preview_stage"
|
|
1216
|
+
elif ! cp "$prd_source" "$target" 2>/dev/null; then
|
|
972
1217
|
printf '%sCould not write the PRD to the current directory. Try a writable directory.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
973
1218
|
exit 1
|
|
974
1219
|
fi
|
package/autonomy/run.sh
CHANGED
|
@@ -25125,6 +25125,32 @@ handle_pause() {
|
|
|
25125
25125
|
PAUSED=true
|
|
25126
25126
|
local loki_dir="${TARGET_DIR:-.}/.loki"
|
|
25127
25127
|
|
|
25128
|
+
# Human-intervention counter for the Evidence Receipt.
|
|
25129
|
+
#
|
|
25130
|
+
# Counted HERE because this is the single point where a run actually blocks
|
|
25131
|
+
# waiting on a person -- every pause path funnels through handle_pause, and
|
|
25132
|
+
# the _PAUSE_IN_PROGRESS guard above means one blocking pause counts once.
|
|
25133
|
+
# Counting at the PAUSE-file write sites instead would over-count
|
|
25134
|
+
# auto-cleared perpetual-mode pauses, which no human ever saw.
|
|
25135
|
+
#
|
|
25136
|
+
# proof-generator reads this; when the file is absent the receipt renders NO
|
|
25137
|
+
# interventions row rather than claiming zero. Best-effort: never fail a run.
|
|
25138
|
+
if [ -n "${loki_dir}" ]; then
|
|
25139
|
+
mkdir -p "$loki_dir/state" 2>/dev/null || true
|
|
25140
|
+
local _iv_file="$loki_dir/state/interventions.json"
|
|
25141
|
+
local _iv_prev=0
|
|
25142
|
+
if [ -f "$_iv_file" ]; then
|
|
25143
|
+
_iv_prev=$(python3 -c "import json,sys
|
|
25144
|
+
try:
|
|
25145
|
+
v=json.load(open(sys.argv[1])).get('count')
|
|
25146
|
+
print(int(v) if isinstance(v,int) and v>=0 else 0)
|
|
25147
|
+
except Exception:
|
|
25148
|
+
print(0)" "$_iv_file" 2>/dev/null || echo 0)
|
|
25149
|
+
fi
|
|
25150
|
+
printf '{"count":%s,"basis":"blocking pauses that waited on a human"}\n' \
|
|
25151
|
+
"$(( _iv_prev + 1 ))" > "$_iv_file" 2>/dev/null || true
|
|
25152
|
+
fi
|
|
25153
|
+
|
|
25128
25154
|
# Save state before pausing so it persists across potential crashes
|
|
25129
25155
|
save_state ${RETRY_COUNT:-0} "paused" 0
|
|
25130
25156
|
|