loki-mode 9.22.8 → 9.22.10
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 +18 -0
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/issue-parser.sh +30 -1
- package/autonomy/lib/gate_policy.py +66 -0
- package/autonomy/lib/proof-generator.py +13 -2
- package/autonomy/lib/proof-pr.sh +6 -1
- package/autonomy/loki +107 -3
- package/autonomy/quickstart.sh +110 -1
- package/autonomy/run.sh +111 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/server.py +55 -0
- package/dashboard/static/index.html +91 -27
- package/docs/INSTALLATION.md +1 -1
- package/loki-ts/dist/loki.js +434 -423
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -101,12 +101,17 @@ object instead of terminal text:
|
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
loki quickstart "a todo app with user accounts" --dry-run --json > preview.json
|
|
104
|
+
loki quickstart --verify-preview preview.json --json
|
|
104
105
|
loki quickstart --from-preview preview.json --yes
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
The object contains the input kind, deterministic selected template (or `null`
|
|
108
109
|
for an existing PRD), the exact estimator response under `plan`, and a bounded
|
|
109
110
|
continuation containing the exact idea/template or the PRD path and SHA-256.
|
|
111
|
+
`--verify-preview` validates the same bounded duplicate-key-rejecting schema and
|
|
112
|
+
requires either a currently shipped idea template or the unchanged digest-bound
|
|
113
|
+
PRD, while emitting no idea or PRD path. It accepts a file or piped stdin and
|
|
114
|
+
returns before provider discovery, estimation, writes, or build execution.
|
|
110
115
|
`--from-preview` requires explicit argv `--yes`, rejects malformed, conflicting,
|
|
111
116
|
symlinked, or changed inputs before provider and build boundaries, then uses the
|
|
112
117
|
existing no-clobber quickstart path. The saved plan is evidence rather than
|
|
@@ -394,6 +399,19 @@ honest verdict (VERIFIED / VERIFIED WITH GAPS / NOT VERIFIED), the key facts
|
|
|
394
399
|
only when the receipt's own headline is VERIFIED. This is on by default whenever
|
|
395
400
|
Loki opens or advises a PR; opt out with `LOKI_PROVEN_PR=0`.
|
|
396
401
|
|
|
402
|
+
For a review-before-publish workflow, preparation and GitHub mutation are two
|
|
403
|
+
explicit steps:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
loki start owner/repo#42 --prepare-pr # builds and writes exact title/body locally
|
|
407
|
+
loki ship --publish # pushes that issue branch and opens the PR
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The second command consumes `.loki/state/pr-title.txt` and
|
|
411
|
+
`.loki/state/pr-body.md` byte-for-byte. Plain `loki ship`, previews, and default
|
|
412
|
+
start paths remain non-publishing. A failed PR creation preserves both files and
|
|
413
|
+
prints the exact remote-branch rollback command.
|
|
414
|
+
|
|
397
415
|
An optional advisory status check (`loki: verified-completion`) maps the verdict
|
|
398
416
|
to a GitHub check-run. It is opt-in (`LOKI_PROVEN_PR_CHECK=1`) and can never block
|
|
399
417
|
a merge on its own. To make verified-completion blocking, add it as a required
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Autonomous spec-driven build system with a built-in trust layer. It does not call work done until it is verified (RARV-C closure loop, 8 quality gates, completion council, verified-completion evidence gate). Triggers on "Loki Mode". Takes a spec (PRD, GitHub issue, OpenAPI doc, etc.) to deployed product with minimal human intervention. Provider-agnostic. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v9.22.
|
|
6
|
+
# Loki Mode v9.22.10
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -470,4 +470,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
470
470
|
|
|
471
471
|
---
|
|
472
472
|
|
|
473
|
-
**v9.22.
|
|
473
|
+
**v9.22.10 | [Autonomi](https://www.autonomi.dev/) flagship product | ~410 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
9.22.
|
|
1
|
+
9.22.10
|
package/autonomy/issue-parser.sh
CHANGED
|
@@ -265,7 +265,7 @@ _gp_criteria_lines() {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
# write_journey_context <owner> <repo> <number> <title> <url> <acceptance>
|
|
268
|
-
# <files> <type> <priority>
|
|
268
|
+
# <files> <type> <priority> [journey-start-epoch]
|
|
269
269
|
#
|
|
270
270
|
# Writes .loki/state/issue-context.json (feature 2: the exact acceptance context)
|
|
271
271
|
# and .loki/state/journey-plan.json (feature 3: the early machine-readable
|
|
@@ -277,6 +277,7 @@ _gp_criteria_lines() {
|
|
|
277
277
|
write_journey_context() {
|
|
278
278
|
local owner="$1" repo="$2" number="$3" title="$4" url="$5"
|
|
279
279
|
local acceptance="$6" files="$7" issue_type="$8" priority="$9"
|
|
280
|
+
local journey_started_epoch="${10:-}"
|
|
280
281
|
|
|
281
282
|
local state_dir="${LOKI_DIR:-.loki}/state"
|
|
282
283
|
mkdir -p "$state_dir" 2>/dev/null || return 0
|
|
@@ -348,6 +349,34 @@ write_journey_context() {
|
|
|
348
349
|
rm -f "$plan_tmp" 2>/dev/null || true
|
|
349
350
|
fi
|
|
350
351
|
|
|
352
|
+
# The parsed, acceptance-bound plan is the first useful result: it tells the
|
|
353
|
+
# user what Loki understood and what it will attempt before provider latency
|
|
354
|
+
# begins. Measure it honestly from command entry when the caller supplied a
|
|
355
|
+
# valid epoch. This is explicitly a proposed plan, never represented as a
|
|
356
|
+
# verified patch. Later first-artifact evidence supersedes it in receipts.
|
|
357
|
+
case "$journey_started_epoch" in
|
|
358
|
+
''|*[!0-9]*) ;;
|
|
359
|
+
*)
|
|
360
|
+
local now_epoch elapsed first_tmp
|
|
361
|
+
now_epoch="$(date +%s)"
|
|
362
|
+
elapsed=$((now_epoch >= journey_started_epoch ? now_epoch - journey_started_epoch : 0))
|
|
363
|
+
first_tmp="$state_dir/.first-useful-result.$$.json"
|
|
364
|
+
if jq -n --argjson seconds "$elapsed" \
|
|
365
|
+
--arg generated_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
366
|
+
'{schema_version:"1.0", kind:"proposed_solution_plan",
|
|
367
|
+
seconds_to_first_result:$seconds,
|
|
368
|
+
under_60_seconds:($seconds < 60),
|
|
369
|
+
verified_patch:false, generated_at:$generated_at,
|
|
370
|
+
artifact:".loki/state/journey-plan.json"}' \
|
|
371
|
+
> "$first_tmp" 2>/dev/null; then
|
|
372
|
+
mv -f "$first_tmp" "$state_dir/first-useful-result.json" 2>/dev/null \
|
|
373
|
+
|| rm -f "$first_tmp"
|
|
374
|
+
else
|
|
375
|
+
rm -f "$first_tmp" 2>/dev/null || true
|
|
376
|
+
fi
|
|
377
|
+
;;
|
|
378
|
+
esac
|
|
379
|
+
|
|
351
380
|
return 0
|
|
352
381
|
}
|
|
353
382
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Read-only quality-gate policy reporting for CLI and dashboard consumers."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
SCHEMA_VERSION = 1
|
|
8
|
+
|
|
9
|
+
PROMOTABLE = {
|
|
10
|
+
"magic_debate": ("LOKI_GATE_MAGIC_DEBATE_BLOCKING", "true", "spec-vs-implementation debate"),
|
|
11
|
+
"test_coverage": ("LOKI_COV_ENFORCE", "1", "project test runner pass/fail"),
|
|
12
|
+
"policy_approval": ("LOKI_POLICY_APPROVAL_ENFORCE", "1", "staged-autonomy approval policy"),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
ALWAYS_BLOCKING = {
|
|
16
|
+
"static_analysis": "static-analysis findings on the diff",
|
|
17
|
+
"code_review": "blind review Critical/High findings",
|
|
18
|
+
"mock_integrity": "tautological assertions and excessive mocking",
|
|
19
|
+
"mutation_integrity": "test-fitting assertion churn",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _counts(loki_dir):
|
|
24
|
+
path = os.path.join(loki_dir, "quality", "gate-failure-count.json")
|
|
25
|
+
try:
|
|
26
|
+
with open(path, encoding="utf-8") as handle:
|
|
27
|
+
data = json.load(handle)
|
|
28
|
+
return data if isinstance(data, dict) else None
|
|
29
|
+
except (OSError, ValueError):
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def assess(loki_dir=".loki", env=None):
|
|
34
|
+
"""Return policy without mutating files or environment.
|
|
35
|
+
|
|
36
|
+
`audit_hits` remains null when the ledger is absent or malformed. Reporting
|
|
37
|
+
zero there would falsely claim the gate ran and never fired.
|
|
38
|
+
"""
|
|
39
|
+
env = os.environ if env is None else env
|
|
40
|
+
counts = _counts(loki_dir)
|
|
41
|
+
gates = []
|
|
42
|
+
for name, why in sorted(ALWAYS_BLOCKING.items()):
|
|
43
|
+
gates.append({
|
|
44
|
+
"gate": name,
|
|
45
|
+
"mode": "blocking",
|
|
46
|
+
"promotable": False,
|
|
47
|
+
"audit_hits": counts.get(name) if counts is not None else None,
|
|
48
|
+
"why": why,
|
|
49
|
+
"promote_with": None,
|
|
50
|
+
})
|
|
51
|
+
for name, (variable, value, why) in sorted(PROMOTABLE.items()):
|
|
52
|
+
enabled = str(env.get(variable, "")).strip().lower() in ("1", "true", "yes")
|
|
53
|
+
gates.append({
|
|
54
|
+
"gate": name,
|
|
55
|
+
"mode": "blocking" if enabled else "advisory",
|
|
56
|
+
"promotable": True,
|
|
57
|
+
"audit_hits": counts.get(name) if counts is not None else None,
|
|
58
|
+
"why": why,
|
|
59
|
+
"promote_with": None if enabled else f"{variable}={value}",
|
|
60
|
+
})
|
|
61
|
+
return {
|
|
62
|
+
"schema_version": SCHEMA_VERSION,
|
|
63
|
+
"status": "measured",
|
|
64
|
+
"ledger": "present" if counts is not None else "absent",
|
|
65
|
+
"gates": gates,
|
|
66
|
+
}
|
|
@@ -1031,13 +1031,24 @@ def _collect_journey(loki_dir):
|
|
|
1031
1031
|
},
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
|
-
# Time to first useful result.
|
|
1035
|
-
#
|
|
1034
|
+
# Time to first useful result. Prefer the first verified code artifact when
|
|
1035
|
+
# one exists. Before that, issue mode can truthfully report its acceptance-
|
|
1036
|
+
# bound proposed plan, explicitly labelled as NOT a verified patch.
|
|
1036
1037
|
fa = _read_json(os.path.join(state, "first-artifact.json"), default=None)
|
|
1037
1038
|
if isinstance(fa, dict):
|
|
1038
1039
|
v = fa.get("seconds_to_first_artifact")
|
|
1039
1040
|
if isinstance(v, (int, float)) and v >= 0:
|
|
1040
1041
|
out["time_to_first_result_sec"] = int(v)
|
|
1042
|
+
out["first_result_kind"] = "code_change"
|
|
1043
|
+
out["first_result_verified_patch"] = True
|
|
1044
|
+
else:
|
|
1045
|
+
fr = _read_json(os.path.join(state, "first-useful-result.json"), default=None)
|
|
1046
|
+
if isinstance(fr, dict):
|
|
1047
|
+
v = fr.get("seconds_to_first_result")
|
|
1048
|
+
if isinstance(v, (int, float)) and v >= 0:
|
|
1049
|
+
out["time_to_first_result_sec"] = int(v)
|
|
1050
|
+
out["first_result_kind"] = str(fr.get("kind") or "")
|
|
1051
|
+
out["first_result_verified_patch"] = bool(fr.get("verified_patch") is True)
|
|
1041
1052
|
|
|
1042
1053
|
# Human interventions. Fills the socket trust_trajectory.py:145 already
|
|
1043
1054
|
# reads and documents as "no per-run counter persisted today". Absent file
|
package/autonomy/lib/proof-pr.sh
CHANGED
|
@@ -220,7 +220,12 @@ def main():
|
|
|
220
220
|
|
|
221
221
|
ttfr = journey.get("time_to_first_result_sec")
|
|
222
222
|
if isinstance(ttfr, int):
|
|
223
|
-
|
|
223
|
+
first_kind = str(journey.get("first_result_kind") or "").strip()
|
|
224
|
+
if first_kind == "proposed_solution_plan":
|
|
225
|
+
_line("| First useful result | proposed plan in " + str(ttfr)
|
|
226
|
+
+ "s (not a verified patch) |")
|
|
227
|
+
else:
|
|
228
|
+
_line("| Time to first result | " + str(ttfr) + "s |")
|
|
224
229
|
|
|
225
230
|
ivs = journey.get("interventions")
|
|
226
231
|
if isinstance(ivs, int):
|
package/autonomy/loki
CHANGED
|
@@ -4617,6 +4617,89 @@ cmd_next() {
|
|
|
4617
4617
|
}
|
|
4618
4618
|
|
|
4619
4619
|
# loki ship -- the build is done, now get it out (review + PR advice in one flow).
|
|
4620
|
+
# Publish an already-prepared PR. This is deliberately a separate, explicit
|
|
4621
|
+
# mutation boundary from the default print-only `loki ship` flow. Preparation
|
|
4622
|
+
# writes the exact reviewed title/body under .loki/state; publishing consumes
|
|
4623
|
+
# those bytes without rebuilding, re-rendering, or inventing evidence.
|
|
4624
|
+
publish_prepared_pr() {
|
|
4625
|
+
local state_dir="${LOKI_DIR:-.loki}/state"
|
|
4626
|
+
local title_file="$state_dir/pr-title.txt"
|
|
4627
|
+
local body_file="$state_dir/pr-body.md"
|
|
4628
|
+
local pr_state="$state_dir/pr.json"
|
|
4629
|
+
|
|
4630
|
+
if [ ! -s "$title_file" ] || [ ! -s "$body_file" ]; then
|
|
4631
|
+
echo "Error: no prepared PR found. Run 'loki start <issue> --prepare-pr' first." >&2
|
|
4632
|
+
return 2
|
|
4633
|
+
fi
|
|
4634
|
+
if ! command -v git >/dev/null 2>&1 || ! git rev-parse --git-dir >/dev/null 2>&1; then
|
|
4635
|
+
echo "Error: publishing a prepared PR requires a git repository." >&2
|
|
4636
|
+
return 2
|
|
4637
|
+
fi
|
|
4638
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
4639
|
+
echo "Error: publishing a prepared PR requires the GitHub CLI (gh)." >&2
|
|
4640
|
+
return 2
|
|
4641
|
+
fi
|
|
4642
|
+
|
|
4643
|
+
local branch base title existing_url pr_url tmp_state
|
|
4644
|
+
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
4645
|
+
case "$branch" in
|
|
4646
|
+
""|HEAD|main|master)
|
|
4647
|
+
echo "Error: refusing to publish prepared work from '$branch'; check out its issue branch." >&2
|
|
4648
|
+
return 2 ;;
|
|
4649
|
+
esac
|
|
4650
|
+
if ! git remote get-url origin >/dev/null 2>&1; then
|
|
4651
|
+
echo "Error: publishing a prepared PR requires an 'origin' remote." >&2
|
|
4652
|
+
return 2
|
|
4653
|
+
fi
|
|
4654
|
+
title="$(cat "$title_file")"
|
|
4655
|
+
if [ -z "$title" ]; then
|
|
4656
|
+
echo "Error: prepared PR title must be one non-empty line." >&2
|
|
4657
|
+
return 2
|
|
4658
|
+
fi
|
|
4659
|
+
case "$title" in
|
|
4660
|
+
*$'\n'*)
|
|
4661
|
+
echo "Error: prepared PR title must be one non-empty line." >&2
|
|
4662
|
+
return 2 ;;
|
|
4663
|
+
esac
|
|
4664
|
+
|
|
4665
|
+
# Idempotence: an interrupted/retried publish reuses the open PR and never
|
|
4666
|
+
# creates a duplicate. This read-only query occurs only after --publish.
|
|
4667
|
+
existing_url="$(gh pr list --head "$branch" --state open --json url --jq '.[0].url // empty' 2>/dev/null || true)"
|
|
4668
|
+
if [ -n "$existing_url" ]; then
|
|
4669
|
+
pr_url="$existing_url"
|
|
4670
|
+
else
|
|
4671
|
+
# Do not attempt PR creation after a failed push. The prepared body
|
|
4672
|
+
# remains local and byte-identical, so the operator can fix auth/network
|
|
4673
|
+
# and retry. A successful push followed by create failure is reversible
|
|
4674
|
+
# with the exact command printed below.
|
|
4675
|
+
if ! git push --set-upstream origin "$branch"; then
|
|
4676
|
+
echo "Error: branch push failed; prepared PR files were preserved." >&2
|
|
4677
|
+
return 1
|
|
4678
|
+
fi
|
|
4679
|
+
if ! pr_url="$(gh pr create --head "$branch" --title "$title" --body-file "$body_file")"; then
|
|
4680
|
+
echo "Error: PR creation failed; prepared PR files were preserved." >&2
|
|
4681
|
+
echo "Rollback remote branch: git push origin --delete $branch" >&2
|
|
4682
|
+
return 1
|
|
4683
|
+
fi
|
|
4684
|
+
pr_url="$(printf '%s\n' "$pr_url" | grep -Eo 'https://[^[:space:]]+' | head -1)"
|
|
4685
|
+
if [ -z "$pr_url" ]; then
|
|
4686
|
+
echo "Error: gh reported success without a PR URL; prepared files were preserved." >&2
|
|
4687
|
+
echo "Inspect: gh pr list --head $branch" >&2
|
|
4688
|
+
return 1
|
|
4689
|
+
fi
|
|
4690
|
+
fi
|
|
4691
|
+
|
|
4692
|
+
mkdir -p "$state_dir" || return 1
|
|
4693
|
+
tmp_state="$pr_state.tmp.$$"
|
|
4694
|
+
if command -v jq >/dev/null 2>&1; then
|
|
4695
|
+
jq -n --arg url "$pr_url" --arg branch "$branch" --arg title "$title" \
|
|
4696
|
+
'{state:"created", url:$url, branch:$branch, title:$title,
|
|
4697
|
+
body_path:".loki/state/pr-body.md"}' > "$tmp_state" \
|
|
4698
|
+
&& mv "$tmp_state" "$pr_state" || { rm -f "$tmp_state"; return 1; }
|
|
4699
|
+
fi
|
|
4700
|
+
echo "PR ready: $pr_url"
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4620
4703
|
# Composes existing surfaces only: optionally opens the running app (--preview),
|
|
4621
4704
|
# runs the SAME diff quality gates as cmd_review, and on a clean-enough result
|
|
4622
4705
|
# prints the exact branch-aware PR command via the shared print_pr_advice (the
|
|
@@ -4626,6 +4709,7 @@ cmd_next() {
|
|
|
4626
4709
|
# and points at the findings instead of advising a PR.
|
|
4627
4710
|
cmd_ship() {
|
|
4628
4711
|
local do_preview=false
|
|
4712
|
+
local publish=false
|
|
4629
4713
|
local review_args=()
|
|
4630
4714
|
# Tracks whether the caller passed an explicit scope flag (--staged, --since,
|
|
4631
4715
|
# ...). Passthrough flags like --yes/--severity do NOT set this, so they do
|
|
@@ -4637,6 +4721,7 @@ cmd_ship() {
|
|
|
4637
4721
|
echo -e "${BOLD}Loki Mode -- finish the build: review, then PR advice (print-only)${NC}"
|
|
4638
4722
|
echo ""
|
|
4639
4723
|
echo "Usage: loki ship [--preview] [--yes] [review options]"
|
|
4724
|
+
echo " loki ship --publish"
|
|
4640
4725
|
echo ""
|
|
4641
4726
|
echo "Runs the natural finish line in one command:"
|
|
4642
4727
|
echo " 1. (with --preview) opens the app Loki built and started locally"
|
|
@@ -4645,13 +4730,15 @@ cmd_ship() {
|
|
|
4645
4730
|
echo " 3. on a clean-enough result, PRINTS the exact branch-aware"
|
|
4646
4731
|
echo " git push + pull-request command for YOU to run"
|
|
4647
4732
|
echo ""
|
|
4648
|
-
echo "
|
|
4649
|
-
echo "creates a PR
|
|
4733
|
+
echo "The default flow is advisory only: it NEVER runs 'git push' or"
|
|
4734
|
+
echo "creates a PR. --publish is the explicit mutation boundary: it"
|
|
4735
|
+
echo "publishes the exact title/body previously written by --prepare-pr."
|
|
4650
4736
|
echo "If review finds HIGH or CRITICAL findings it stops and points you"
|
|
4651
4737
|
echo "at the findings instead of advising a PR (exit code 1)."
|
|
4652
4738
|
echo ""
|
|
4653
4739
|
echo "Options:"
|
|
4654
4740
|
echo " --preview Open the running local app first (loki preview)"
|
|
4741
|
+
echo " --publish Push the current issue branch and publish its prepared PR"
|
|
4655
4742
|
echo " --yes, -y Pass through to review (skip its confirmation prompt)"
|
|
4656
4743
|
echo " --help, -h Show this help and exit"
|
|
4657
4744
|
echo ""
|
|
@@ -4660,6 +4747,7 @@ cmd_ship() {
|
|
|
4660
4747
|
return 0
|
|
4661
4748
|
;;
|
|
4662
4749
|
--preview) do_preview=true ;;
|
|
4750
|
+
--publish) publish=true ;;
|
|
4663
4751
|
# Only a known set of NON-scope passthrough flags are exempt from
|
|
4664
4752
|
# "explicit scope". Everything else the caller passes -- an explicit
|
|
4665
4753
|
# scope flag (--staged/--since/...), a positional <file-or-dir>, or any
|
|
@@ -4689,6 +4777,15 @@ cmd_ship() {
|
|
|
4689
4777
|
shift
|
|
4690
4778
|
done
|
|
4691
4779
|
|
|
4780
|
+
if [ "$publish" = true ]; then
|
|
4781
|
+
if [ "$do_preview" = true ] || [ "${#review_args[@]}" -gt 0 ]; then
|
|
4782
|
+
echo "Error: --publish cannot be combined with preview/review options." >&2
|
|
4783
|
+
return 2
|
|
4784
|
+
fi
|
|
4785
|
+
publish_prepared_pr
|
|
4786
|
+
return $?
|
|
4787
|
+
fi
|
|
4788
|
+
|
|
4692
4789
|
# 1. Optional preview of the running local app. Only when an app-runner state
|
|
4693
4790
|
# file exists, so we never imply an app is running when it is not.
|
|
4694
4791
|
if [ "$do_preview" = true ]; then
|
|
@@ -9694,6 +9791,12 @@ cmd_issue_view() {
|
|
|
9694
9791
|
#===============================================================================
|
|
9695
9792
|
|
|
9696
9793
|
cmd_run() {
|
|
9794
|
+
# Wall-clock anchor for the issue journey. Captured before argument parsing,
|
|
9795
|
+
# auth, or issue fetch so the first-result measurement includes the wait the
|
|
9796
|
+
# user actually experiences. Exported only through the explicit writer call
|
|
9797
|
+
# below; it does not alter runner/provider behavior.
|
|
9798
|
+
local _issue_journey_started_epoch
|
|
9799
|
+
_issue_journey_started_epoch="$(date +%s)"
|
|
9697
9800
|
# v6.84.0: `loki run` is now a deprecated alias for `loki start <issue-ref>`.
|
|
9698
9801
|
# When invoked directly (not via the unified start dispatcher), emit a
|
|
9699
9802
|
# deprecation notice and a telemetry event so we can track adoption of the
|
|
@@ -10060,7 +10163,8 @@ cmd_run() {
|
|
|
10060
10163
|
_gp_acc=$(extract_acceptance_criteria "$_gp_body" 2>/dev/null || true)
|
|
10061
10164
|
fi
|
|
10062
10165
|
write_journey_context "$_gp_owner" "$_gp_repo" "${number:-}" "$title" \
|
|
10063
|
-
"$url" "$_gp_acc" "" "${issue_provider:-github}" ""
|
|
10166
|
+
"$url" "$_gp_acc" "" "${issue_provider:-github}" "" \
|
|
10167
|
+
"$_issue_journey_started_epoch" 2>/dev/null || true
|
|
10064
10168
|
fi
|
|
10065
10169
|
|
|
10066
10170
|
# Per-session locking (v6.4.0): export session ID so run.sh uses
|
package/autonomy/quickstart.sh
CHANGED
|
@@ -623,6 +623,86 @@ PY
|
|
|
623
623
|
python3 -c "$validator_code" "$preview_path"
|
|
624
624
|
}
|
|
625
625
|
|
|
626
|
+
# _qs_verify_preview <path> <json>: prove that one bounded schema-v1 preview is
|
|
627
|
+
# still actionable without crossing provider, estimator, PRD-write, or build
|
|
628
|
+
# boundaries. Idea previews must name a currently shipped template. PRD
|
|
629
|
+
# previews must still resolve to the exact readable non-symlink file digest.
|
|
630
|
+
# Output deliberately excludes the idea and PRD path.
|
|
631
|
+
_qs_verify_preview() {
|
|
632
|
+
local preview_path="$1" json_output="${2:-false}"
|
|
633
|
+
local fields="" kind="" template="" encoded="" digest="" value=""
|
|
634
|
+
fields=$(_qs_load_preview "$preview_path") || {
|
|
635
|
+
printf 'Preview JSON is malformed or incompatible.\n' >&2
|
|
636
|
+
return 2
|
|
637
|
+
}
|
|
638
|
+
IFS='|' read -r kind template encoded digest <<< "$fields"
|
|
639
|
+
value=$(python3 -c 'import base64,sys; sys.stdout.buffer.write(base64.b64decode(sys.argv[1], validate=True))' "$encoded" 2>/dev/null) || {
|
|
640
|
+
printf 'Preview JSON continuation is invalid.\n' >&2
|
|
641
|
+
return 2
|
|
642
|
+
}
|
|
643
|
+
[ -n "$value" ] || {
|
|
644
|
+
printf 'Preview JSON continuation is empty.\n' >&2
|
|
645
|
+
return 2
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
local verdict=""
|
|
649
|
+
if [ "$kind" = "idea" ]; then
|
|
650
|
+
if ! _qs_template_exists "$template"; then
|
|
651
|
+
printf 'Preview template is not currently shipped: %s\n' "$template" >&2
|
|
652
|
+
return 2
|
|
653
|
+
fi
|
|
654
|
+
verdict="SHIPPED_TEMPLATE_MATCH"
|
|
655
|
+
elif [ "$kind" = "prd" ]; then
|
|
656
|
+
if [ ! -f "$value" ] || [ ! -r "$value" ] || [ -L "$value" ]; then
|
|
657
|
+
printf 'Preview PRD is not a readable regular non-symlink file.\n' >&2
|
|
658
|
+
return 2
|
|
659
|
+
fi
|
|
660
|
+
local current_digest=""
|
|
661
|
+
current_digest=$(shasum -a 256 "$value" 2>/dev/null | awk '{print $1}') || current_digest=""
|
|
662
|
+
if [ -z "$current_digest" ] || [ "$current_digest" != "$digest" ]; then
|
|
663
|
+
printf 'Preview PRD has changed since the saved preview; run --dry-run --json again.\n' >&2
|
|
664
|
+
return 2
|
|
665
|
+
fi
|
|
666
|
+
verdict="EXACT_PRD_MATCH"
|
|
667
|
+
else
|
|
668
|
+
printf 'Preview JSON continuation kind is unsupported.\n' >&2
|
|
669
|
+
return 2
|
|
670
|
+
fi
|
|
671
|
+
|
|
672
|
+
if [ "$json_output" = true ]; then
|
|
673
|
+
python3 - "$kind" "$template" "$digest" "$verdict" <<'PY'
|
|
674
|
+
import json
|
|
675
|
+
import sys
|
|
676
|
+
|
|
677
|
+
kind, template, digest, verdict = sys.argv[1:5]
|
|
678
|
+
json.dump(
|
|
679
|
+
{
|
|
680
|
+
"command": "loki quickstart",
|
|
681
|
+
"input_kind": kind,
|
|
682
|
+
"mode": "verify-preview",
|
|
683
|
+
"prd_sha256": digest if kind == "prd" else None,
|
|
684
|
+
"schema_version": 1,
|
|
685
|
+
"selected_template": template if kind == "idea" else None,
|
|
686
|
+
"valid": True,
|
|
687
|
+
"verdict": verdict,
|
|
688
|
+
},
|
|
689
|
+
sys.stdout,
|
|
690
|
+
separators=(",", ":"),
|
|
691
|
+
sort_keys=True,
|
|
692
|
+
)
|
|
693
|
+
sys.stdout.write("\n")
|
|
694
|
+
PY
|
|
695
|
+
return $?
|
|
696
|
+
fi
|
|
697
|
+
|
|
698
|
+
if [ "$kind" = "idea" ]; then
|
|
699
|
+
printf 'VERIFIED / %s / template=%s\n' "$verdict" "$template"
|
|
700
|
+
else
|
|
701
|
+
printf 'VERIFIED / %s / sha256=%s\n' "$verdict" "$digest"
|
|
702
|
+
fi
|
|
703
|
+
return 0
|
|
704
|
+
}
|
|
705
|
+
|
|
626
706
|
# _qs_help: concise usage for `loki quickstart --help`.
|
|
627
707
|
_qs_help() {
|
|
628
708
|
printf '%sloki quickstart%s - guided first build (setup, idea, template, plan, go)\n' "$_QS_BOLD" "$_QS_NC"
|
|
@@ -639,8 +719,9 @@ _qs_help() {
|
|
|
639
719
|
printf 'Options:\n'
|
|
640
720
|
printf ' --yes, -y Auto-confirm the final build prompt (still shows the plan)\n'
|
|
641
721
|
printf ' --dry-run Preview the selected template and plan; write/start nothing\n'
|
|
642
|
-
printf ' --json
|
|
722
|
+
printf ' --json Emit machine-readable output for a read-only command\n'
|
|
643
723
|
printf ' --from-preview F Continue saved JSON from file F (or - for piped stdin); requires --yes\n'
|
|
724
|
+
printf ' --verify-preview F Verify saved JSON from file F (or - for piped stdin); executes nothing\n'
|
|
644
725
|
printf ' --template N Use the exact shipped template N for an IDEA\n'
|
|
645
726
|
printf ' --list-templates List every shipped template and its purpose\n'
|
|
646
727
|
printf ' --help, -h Show this help and exit\n'
|
|
@@ -660,6 +741,7 @@ _qs_help() {
|
|
|
660
741
|
printf ' Add --json for versioned JSON only; --json requires --dry-run.\n'
|
|
661
742
|
printf ' Save that JSON, then continue it with --from-preview FILE --yes.\n'
|
|
662
743
|
printf ' Or pipe it with --from-preview - --yes; terminal stdin is refused.\n'
|
|
744
|
+
printf ' Verify it without execution using --verify-preview FILE (and optional --json).\n'
|
|
663
745
|
printf '\n'
|
|
664
746
|
printf 'Steps:\n'
|
|
665
747
|
printf ' 1. Setup Check for an AI provider for execution (skipped in preview)\n'
|
|
@@ -697,6 +779,8 @@ cmd_quickstart() {
|
|
|
697
779
|
local list_templates_flag_seen=false
|
|
698
780
|
local from_preview=""
|
|
699
781
|
local from_preview_flag_seen=false
|
|
782
|
+
local verify_preview=""
|
|
783
|
+
local verify_preview_flag_seen=false
|
|
700
784
|
local preview_prd_digest=""
|
|
701
785
|
if _qs_assume_yes; then assume_yes=true; fi
|
|
702
786
|
|
|
@@ -765,6 +849,19 @@ cmd_quickstart() {
|
|
|
765
849
|
from_preview="$2"
|
|
766
850
|
shift 2
|
|
767
851
|
;;
|
|
852
|
+
--verify-preview)
|
|
853
|
+
if [ "$verify_preview_flag_seen" = true ]; then
|
|
854
|
+
printf '%s--verify-preview may be specified only once.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
855
|
+
exit 2
|
|
856
|
+
fi
|
|
857
|
+
verify_preview_flag_seen=true
|
|
858
|
+
if [ $# -lt 2 ] || [ -z "${2:-}" ] || [[ "${2:-}" == --* ]]; then
|
|
859
|
+
printf '%s--verify-preview requires a preview JSON path.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
860
|
+
exit 2
|
|
861
|
+
fi
|
|
862
|
+
verify_preview="$2"
|
|
863
|
+
shift 2
|
|
864
|
+
;;
|
|
768
865
|
--*)
|
|
769
866
|
printf '%sUnknown option: %s%s\n' "$_QS_RED" "$1" "$_QS_NC" >&2
|
|
770
867
|
printf "Run 'loki quickstart --help' for usage.\n" >&2
|
|
@@ -783,6 +880,18 @@ cmd_quickstart() {
|
|
|
783
880
|
esac
|
|
784
881
|
done
|
|
785
882
|
|
|
883
|
+
# Verification is a standalone read-only shape. It accepts only optional
|
|
884
|
+
# JSON formatting, validates the full current continuation boundary, and
|
|
885
|
+
# returns before terminal, provider, estimator, PRD-write, and build seams.
|
|
886
|
+
if [ "$verify_preview_flag_seen" = true ]; then
|
|
887
|
+
if [ -n "$positional" ] || [ "$yes_flag" = true ] || [ "$dry_run" = true ] || [ "$template_flag_seen" = true ] || [ "$list_templates" = true ] || [ "$from_preview_flag_seen" = true ]; then
|
|
888
|
+
printf '%s--verify-preview accepts only a preview JSON path and optional --json.%s\n' "$_QS_RED" "$_QS_NC" >&2
|
|
889
|
+
exit 2
|
|
890
|
+
fi
|
|
891
|
+
_qs_verify_preview "$verify_preview" "$json_output"
|
|
892
|
+
return $?
|
|
893
|
+
fi
|
|
894
|
+
|
|
786
895
|
# Continuation is a standalone execution shape. Explicit argv consent is
|
|
787
896
|
# mandatory, and no caller-supplied input or selector may compete with the
|
|
788
897
|
# reviewed preview. Validation happens before every provider, estimator,
|