loki-mode 10.6.13 → 10.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/SKILL.md +3 -3
- package/VERSION +1 -1
- package/autonomy/acp-agent.py +128 -0
- package/autonomy/completion-council.sh +3 -3
- package/autonomy/docker-run.sh +8 -0
- package/autonomy/issue-parser.sh +8 -7
- package/autonomy/issue-providers.sh +159 -1
- package/autonomy/lib/engine10-push.sh +4 -1
- package/autonomy/lib/review_risk.py +91 -0
- package/autonomy/loki +225 -31
- package/autonomy/mobile-verify.sh +66 -0
- package/autonomy/quickstart.sh +44 -4
- package/autonomy/run.sh +44 -2
- package/completions/_loki +2 -0
- package/completions/loki.bash +1 -1
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_runs_v1.py +80 -0
- package/dashboard/server.py +2 -0
- package/docs/COMPETITOR-DEPLOYMENT-MODELS.md +3 -3
- package/docs/INSTALLATION.md +5 -1
- package/docs/acp-editors.md +29 -0
- package/docs/api-runs-v1.md +12 -0
- package/docs/architecture/ADR-001-runtime-migration.md +13 -13
- package/docs/dev/architecture-reference.md +6 -2
- package/docs/merge-queue.md +13 -0
- package/docs/mobile-verify.md +11 -0
- package/docs/sentry-intake.md +13 -0
- package/docs/v10/BOARD.md +66 -12
- package/docs/v10/CP-ENTERPRISE-UI.md +276 -0
- package/docs/v10/D61-SPEED.md +4 -0
- package/docs/v10/DECISIONS.md +85 -0
- package/docs/v10/FOUNDER-QUEUE.md +1 -0
- package/docs/v10/GAP-RESEARCH-1003.md +32 -0
- package/docs/v10/MORNING-BRIEF-1003.md +39 -0
- package/docs/v10/PROGRESS.md +107 -0
- package/docs/v10/SWARM.md +32 -0
- package/docs/visual-evidence.md +5 -0
- package/loki-ts/dist/loki.js +766 -624
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/packages/control-plane/dist/server.js +263 -9
- package/packages/control-plane/src/server/actions.ts +60 -0
- package/packages/control-plane/src/server/app.ts +39 -3
- package/packages/control-plane/src/shipper/watch.ts +64 -0
- package/packages/control-plane/ui/dist/assets/index-CUQ65vJY.css +1 -0
- package/packages/control-plane/ui/dist/assets/index-CwwGfp6E.js +129 -0
- package/packages/control-plane/ui/dist/index.html +3 -3
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/skills/healing.md +2 -2
- package/tools/proof-passport.py +1 -1
- package/web-app/dist/assets/{AdminPage-CVEdde3t.js → AdminPage-BQPb5rc5.js} +1 -1
- package/web-app/dist/assets/{Avatar-D1r8n_Da.js → Avatar-Cttco6FC.js} +1 -1
- package/web-app/dist/assets/{Badge-BAPyJ97M.js → Badge-CPb6kpbH.js} +1 -1
- package/web-app/dist/assets/{Button-C-GCctTq.js → Button-caTL28a4.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-CV9MhyuV.js → CockpitPage-bEbLx6ph.js} +1 -1
- package/web-app/dist/assets/{ComparePage-D1R5Lxmy.js → ComparePage-DNtJSij-.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-CFvJx4vV.js → ErrorBoundary-BrgC3K44.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-9I7dfPz0.js → EvidenceReceiptPanel-Q-mSrUUQ.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-8YH6WkTh.js → GitHubIssuesPanel-DbmYl_Er.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BZvEHBTF.js → GitHubPRsPanel-C6PCHM4B.js} +1 -1
- package/web-app/dist/assets/{HomePage-DhjWTo8R.js → HomePage-7zsL_k_e.js} +2 -2
- package/web-app/dist/assets/{LoginPage-BwNGyWxP.js → LoginPage-Cl4ON8RQ.js} +1 -1
- package/web-app/dist/assets/{MagicPage-Dg9z7deq.js → MagicPage-BNEAhTqH.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-DLF1F6XC.js → MetricsPage-DyBH-88q.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-CZClMI3s.js → NotFoundPage-CIVr21yL.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-CnAvyHDJ.js → ProjectPage-BMeVYocF.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-D33VGyVL.js → ProjectsPage-D-H2WRVU.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-DQtlpXPe.js → SettingsPage-o0kR9RAt.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CAitzJlY.js → ShowcasePage-DtfMDMjp.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-CV49UjwU.js → SystemSettingsPage-CkXdpZ_T.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-5EKrbkN3.js → TeamsPage-Cu5H9cQ0.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-BRsrm00y.js → TemplatesPage-3wdbVwil.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-DgEnMwnQ.js → TerminalOutput-CWn301xh.js} +1 -1
- package/web-app/dist/assets/{activity-hXTYu4Zb.js → activity-YGaAt4zJ.js} +1 -1
- package/web-app/dist/assets/{bell-D5QkmMjg.js → bell-C28q0GO7.js} +1 -1
- package/web-app/dist/assets/{bot-CGOzPami.js → bot-DLcQGI92.js} +1 -1
- package/web-app/dist/assets/{check-HxPvzPXm.js → check-w0sBNlcm.js} +1 -1
- package/web-app/dist/assets/{chevron-left-D7HYUp4T.js → chevron-left-W7O0vC_m.js} +1 -1
- package/web-app/dist/assets/{circle-alert-BdufMh8P.js → circle-alert-Cxjq9fxB.js} +1 -1
- package/web-app/dist/assets/{clock-DMcS0xjR.js → clock-C8EqzmNl.js} +1 -1
- package/web-app/dist/assets/{cloud-Bt-0bh9m.js → cloud-xNgp4g3Y.js} +1 -1
- package/web-app/dist/assets/{code-xml-DRtDKt4_.js → code-xml-B-lyNDLJ.js} +1 -1
- package/web-app/dist/assets/{database-B0ChKZQ4.js → database-DoW5lmWc.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-st_8sF_M.js → dollar-sign-BcARA5sC.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-UdiLz5N6.js → file-code-corner-CehcII0J.js} +1 -1
- package/web-app/dist/assets/{file-plus-De1dimur.js → file-plus-DI9Zo5hP.js} +1 -1
- package/web-app/dist/assets/{globe-rrbXmytb.js → globe-CiZWBk3H.js} +1 -1
- package/web-app/dist/assets/{hammer-CBtNZi-S.js → hammer-3qv_T2N-.js} +1 -1
- package/web-app/dist/assets/{index-D3YvZaMV.js → index-gF9grAz-.js} +3 -3
- package/web-app/dist/assets/{layers-BmthCdkO.js → layers-CN7d0By9.js} +1 -1
- package/web-app/dist/assets/{loader-circle-C_sXFJUa.js → loader-circle-DywcM-im.js} +1 -1
- package/web-app/dist/assets/{lock-DT6V3D97.js → lock-_hdSmkU4.js} +1 -1
- package/web-app/dist/assets/{package-D86K1oU6.js → package-BV5zgqxJ.js} +1 -1
- package/web-app/dist/assets/{plus-Dwkssum8.js → plus-CU_Hh4-x.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-DhRlqf1F.js → refresh-cw-DMK-fNl-.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-EvA5ZJVL.js → rotate-ccw-CEB3_v5_.js} +1 -1
- package/web-app/dist/assets/{scroll-text-D4ABo43E.js → scroll-text-BSdXxcRN.js} +1 -1
- package/web-app/dist/assets/{server-BAAdr_fD.js → server-CAMlP1yJ.js} +1 -1
- package/web-app/dist/assets/{shield-alert-DJPLy9t8.js → shield-alert-B-r1Dt3h.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-D07JqmoS.js → thumbs-up-CYfxw9o4.js} +1 -1
- package/web-app/dist/assets/{trash-2-BJYwlHzl.js → trash-2-DX3nGPS8.js} +1 -1
- package/web-app/dist/assets/{trending-down-Bo8txoOw.js → trending-down-Bt-hSntd.js} +1 -1
- package/web-app/dist/assets/{trending-up-Bb13tkln.js → trending-up-DVSlvQp8.js} +1 -1
- package/web-app/dist/assets/{upload-D60DiVNZ.js → upload-D45RRHAP.js} +1 -1
- package/web-app/dist/assets/{usePolling-DJmgDiio.js → usePolling-fq_Xj8iw.js} +1 -1
- package/web-app/dist/assets/{user-BmXCbSJm.js → user-BWcJkLlr.js} +1 -1
- package/web-app/dist/index.html +1 -1
- package/packages/control-plane/ui/dist/assets/index-CFfrqCx2.css +0 -1
- package/packages/control-plane/ui/dist/assets/index-Jt31nrK-.js +0 -114
package/autonomy/loki
CHANGED
|
@@ -1693,6 +1693,7 @@ show_help() {
|
|
|
1693
1693
|
echo "Observe:"
|
|
1694
1694
|
echo " dashboard [cmd] Operations UI server (start|stop|status|url|open)"
|
|
1695
1695
|
echo " mcp Launch the MCP server (Model Context Protocol, stdio)"
|
|
1696
|
+
echo " acp Run Loki as an ACP agent over stdio (VS Code, JetBrains, Zed)"
|
|
1696
1697
|
echo ""
|
|
1697
1698
|
echo "Report:"
|
|
1698
1699
|
echo " report [cmd] Reporting: session|metrics|cost|export|share|dogfood"
|
|
@@ -1812,19 +1813,19 @@ show_help() {
|
|
|
1812
1813
|
# missing from this list, so a new command cannot ship invisible.
|
|
1813
1814
|
echo ""
|
|
1814
1815
|
echo "All commands:"
|
|
1815
|
-
echo " agent analyze api assets audit backlog bench checkpoint (cp) ci cleanup"
|
|
1816
|
+
echo " acp agent analyze api assets audit backlog bench checkpoint (cp) ci cleanup"
|
|
1816
1817
|
echo " cluster cockpit code compliance completions compound config context (ctx)"
|
|
1817
1818
|
echo " cost council crash dashboard demo deploy docker docs doctor dogfood"
|
|
1818
1819
|
echo " enterprise estimate explain export failover github grill heal help"
|
|
1819
1820
|
echo " import init intent"
|
|
1820
|
-
echo " issue kpis logs magic mcp memory metrics migrate modernize monitor"
|
|
1821
|
+
echo " issue keys kpis logs magic mcp memory metrics migrate modernize monitor"
|
|
1821
1822
|
echo " next notify onboard open optimize otel outcomes own (handoff) pause plan preview"
|
|
1822
1823
|
echo " projects proof (receipt) provider quick quickstart rc remote report reset"
|
|
1823
|
-
echo " resume review rollback run sandbox secrets secure self-update sentrux"
|
|
1824
|
+
echo " merge resume review rollback run sandbox secrets secure self-update sentrux"
|
|
1824
1825
|
echo " serve setup-skill share ship spec start state stats status steer stop"
|
|
1825
1826
|
echo " syslog telemetry template test tour trigger trust trust-metrics"
|
|
1826
1827
|
echo " ultracode update verify version voice watch watchdog web welcome why"
|
|
1827
|
-
echo " wiki worktree (wt)"
|
|
1828
|
+
echo " wiki workspace worktree (wt)"
|
|
1828
1829
|
echo " control"
|
|
1829
1830
|
echo ""
|
|
1830
1831
|
echo "Any command: loki <command> --help"
|
|
@@ -2007,6 +2008,9 @@ _loki_control_ui() {
|
|
|
2007
2008
|
|
|
2008
2009
|
cmd_ui() {
|
|
2009
2010
|
_loki_control_ui "$@" && return 0
|
|
2011
|
+
# CP-LEGACY: callers that must never start the classic server (run.sh
|
|
2012
|
+
# auto-open) set LOKI_UI_NO_CLASSIC=1.
|
|
2013
|
+
[ "${LOKI_UI_NO_CLASSIC:-0}" = "1" ] && return 1
|
|
2010
2014
|
local port="" pid=""
|
|
2011
2015
|
if [ -f "$DASHBOARD_PID_FILE" ]; then
|
|
2012
2016
|
pid=$(cat "$DASHBOARD_PID_FILE" 2>/dev/null)
|
|
@@ -2084,6 +2088,18 @@ detect_arg_type() {
|
|
|
2084
2088
|
return 0
|
|
2085
2089
|
fi
|
|
2086
2090
|
|
|
2091
|
+
# Sentry intake: an https Sentry issue URL, or an exported Sentry JSON file
|
|
2092
|
+
# on disk, is an issue (checked before the .json -> prd rule below).
|
|
2093
|
+
if [[ "$arg" =~ ^https://[^/]*sentry[^/]*/.*issues/[0-9]+ ]]; then
|
|
2094
|
+
echo "issue"
|
|
2095
|
+
return 0
|
|
2096
|
+
fi
|
|
2097
|
+
if [[ "$arg" == *.json ]] && [ -f "$arg" ] \
|
|
2098
|
+
&& head -c 2000000 "$arg" 2>/dev/null | grep -Eq '"(shortId|culprit)"[[:space:]]*:|"exception"[[:space:]]*:|"type"[[:space:]]*:[[:space:]]*"exception"'; then
|
|
2099
|
+
echo "issue"
|
|
2100
|
+
return 0
|
|
2101
|
+
fi
|
|
2102
|
+
|
|
2087
2103
|
# Known PRD extensions - treat as PRD first (highest priority).
|
|
2088
2104
|
# This handles edge case where a file is literally named "123.md" or
|
|
2089
2105
|
# sits in a dir that matches another pattern.
|
|
@@ -7441,14 +7457,28 @@ _loki_dashboard_bind_is_safe() {
|
|
|
7441
7457
|
return 1
|
|
7442
7458
|
}
|
|
7443
7459
|
|
|
7460
|
+
_loki_dashboard_args_need_classic() {
|
|
7461
|
+
local a
|
|
7462
|
+
for a in "$@"; do
|
|
7463
|
+
case "$a" in
|
|
7464
|
+
--host|--host=*|--tls-cert|--tls-cert=*|--tls-key|--tls-key=*) return 0 ;;
|
|
7465
|
+
esac
|
|
7466
|
+
done
|
|
7467
|
+
return 1
|
|
7468
|
+
}
|
|
7469
|
+
|
|
7444
7470
|
cmd_dashboard() {
|
|
7445
7471
|
local subcommand="${1:-}"
|
|
7446
7472
|
|
|
7447
|
-
#
|
|
7448
|
-
#
|
|
7473
|
+
# CP-LEGACY (D57): the Control Plane is the default UI for `dashboard`,
|
|
7474
|
+
# `dashboard start` and `dashboard open`, with or without flags. The legacy
|
|
7475
|
+
# Python dashboard stays reachable only by explicit opt-in:
|
|
7476
|
+
# LOKI_LEGACY_DASHBOARD=1 or LOKI_CONTROL_DEFAULT=0. Enterprise bind/TLS
|
|
7477
|
+
# flags (--host, --tls-cert, --tls-key) are classic-server concepts and
|
|
7478
|
+
# keep the classic server.
|
|
7449
7479
|
case "$subcommand" in
|
|
7450
7480
|
""|start|open)
|
|
7451
|
-
if [
|
|
7481
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ] && ! _loki_dashboard_args_need_classic "${@:2}"; then
|
|
7452
7482
|
_loki_control_ui "${@:2}" && return 0
|
|
7453
7483
|
fi
|
|
7454
7484
|
;;
|
|
@@ -8307,6 +8337,11 @@ cmd_web_redirect_to_dashboard() {
|
|
|
8307
8337
|
echo " it watches runs that are already going. To build from that spec:" >&2
|
|
8308
8338
|
echo " loki start '${_web_dropped_prd}'" >&2
|
|
8309
8339
|
fi
|
|
8340
|
+
# Flags such as --port must reach the classic validator, so only the
|
|
8341
|
+
# bare form (optionally --no-open) is routed to the Control Plane.
|
|
8342
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ] && [ ${#dash_args[@]} -eq 0 ] && _loki_control_ui; then
|
|
8343
|
+
return 0
|
|
8344
|
+
fi
|
|
8310
8345
|
cmd_dashboard_start ${dash_args[@]+"${dash_args[@]}"}
|
|
8311
8346
|
}
|
|
8312
8347
|
|
|
@@ -19270,7 +19305,7 @@ LANG_BY_EXT = {
|
|
|
19270
19305
|
}
|
|
19271
19306
|
|
|
19272
19307
|
TEST_HINT = re.compile(r'(^|[/_.-])(test|tests|spec|specs|__tests__)([/_.-]|s?$|\.)', re.I)
|
|
19273
|
-
TODO_RE = re.compile(r'\b(TODO|FIXME|HACK|XXX)
|
|
19308
|
+
TODO_RE = re.compile(r'\b(TODO|FIXME|HACK|XXX)s?\b')
|
|
19274
19309
|
|
|
19275
19310
|
# import/require line -> the referenced module basename(s). Deliberately cheap:
|
|
19276
19311
|
# we only need a proxy for how many OTHER files reference a given file, to rank
|
|
@@ -19291,6 +19326,7 @@ total_files = 0
|
|
|
19291
19326
|
todo_count = 0
|
|
19292
19327
|
has_tests = False
|
|
19293
19328
|
big_files = [] # (loc, relpath)
|
|
19329
|
+
max_loc = 0 # largest file by LOC, across every file
|
|
19294
19330
|
# module reference tallies: how many files import a given basename (no-ext)
|
|
19295
19331
|
ref_counts = {}
|
|
19296
19332
|
file_records = {} # relpath -> record
|
|
@@ -19340,6 +19376,8 @@ for dirpath, dirnames, filenames in os.walk(root):
|
|
|
19340
19376
|
}
|
|
19341
19377
|
files.append(rec)
|
|
19342
19378
|
file_records[rel] = rec
|
|
19379
|
+
if loc > max_loc:
|
|
19380
|
+
max_loc = loc
|
|
19343
19381
|
if loc >= 500:
|
|
19344
19382
|
big_files.append((loc, rel))
|
|
19345
19383
|
# tally inbound references toward the imported basenames
|
|
@@ -19352,7 +19390,7 @@ for rec in files:
|
|
|
19352
19390
|
|
|
19353
19391
|
# --- Technical-debt / maintenance-risk signals (all deterministic) ---
|
|
19354
19392
|
big_files.sort(reverse=True)
|
|
19355
|
-
largest_loc =
|
|
19393
|
+
largest_loc = max_loc
|
|
19356
19394
|
# dependency-manifest presence (a real, cheap signal; NOT a staleness figure).
|
|
19357
19395
|
manifest_names = {
|
|
19358
19396
|
'package.json', 'requirements.txt', 'Pipfile', 'pyproject.toml', 'go.mod',
|
|
@@ -21761,8 +21799,10 @@ ultracode council demo dogfood heal modernize import issue github init template
|
|
|
21761
21799
|
status stop pause resume monitor watch watchdog dashboard web open preview share \
|
|
21762
21800
|
memory context ctx checkpoint state report kpis cost metrics stats logs otel \
|
|
21763
21801
|
syslog telemetry crash config provider doctor setup-skill onboard quickstart \
|
|
21764
|
-
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
21802
|
+
welcome update self-update rollback merge failover cluster enterprise remote deploy docker \
|
|
21765
21803
|
sandbox api mcp magic compound assets export wiki control docs explain why audit compliance \
|
|
21804
|
+
welcome update self-update rollback failover cluster enterprise remote deploy docker \
|
|
21805
|
+
sandbox api mcp acp magic compound assets export wiki control docs explain why audit compliance \
|
|
21766
21806
|
secrets analyze optimize bench ci reset cleanup notify trigger voice sentrux \
|
|
21767
21807
|
worktree wt projects cp rc trust-metrics serve agent code self_update test help version"
|
|
21768
21808
|
local best
|
|
@@ -22094,6 +22134,9 @@ main() {
|
|
|
22094
22134
|
rollback)
|
|
22095
22135
|
cmd_rollback "$@"
|
|
22096
22136
|
;;
|
|
22137
|
+
merge)
|
|
22138
|
+
cmd_merge "$@"
|
|
22139
|
+
;;
|
|
22097
22140
|
council)
|
|
22098
22141
|
cmd_council "$@"
|
|
22099
22142
|
;;
|
|
@@ -22174,6 +22217,10 @@ main() {
|
|
|
22174
22217
|
mcp)
|
|
22175
22218
|
cmd_mcp "$@"
|
|
22176
22219
|
;;
|
|
22220
|
+
acp)
|
|
22221
|
+
# ACP agent over stdio; stdout is protocol-only. See docs/acp-editors.md.
|
|
22222
|
+
LOKI_ACP_LOKI="${LOKI_ACP_LOKI:-$_LOKI_SCRIPT_DIR/loki}" exec python3 "$_LOKI_SCRIPT_DIR/acp-agent.py"
|
|
22223
|
+
;;
|
|
22177
22224
|
migrate)
|
|
22178
22225
|
# CLI consolidation (Phase B): 'migrate' -> 'modernize migrate'.
|
|
22179
22226
|
_deprecated_alias migrate "modernize migrate" "$@"
|
|
@@ -22706,6 +22753,7 @@ cmd_review() {
|
|
|
22706
22753
|
local review_ultra=false
|
|
22707
22754
|
local review_assume_yes=false
|
|
22708
22755
|
local review_timeout=""
|
|
22756
|
+
local review_risk=false
|
|
22709
22757
|
|
|
22710
22758
|
while [[ $# -gt 0 ]]; do
|
|
22711
22759
|
case "$1" in
|
|
@@ -22733,6 +22781,10 @@ cmd_review() {
|
|
|
22733
22781
|
echo " skip the prompt. Optional [file-or-dir] is"
|
|
22734
22782
|
echo " passed as the ultrareview target (branch / PR"
|
|
22735
22783
|
echo " number / base branch)."
|
|
22784
|
+
echo " --risk Deterministic 0-100 diff risk score with per-factor"
|
|
22785
|
+
echo " breakdown (files, sensitive paths, size, test delta,"
|
|
22786
|
+
echo " deleted tests). Works with --pr/--staged/--since;"
|
|
22787
|
+
echo " 'loki review <pr-number> --risk' also works. Use --json"
|
|
22736
22788
|
echo " --yes, -y Skip the --ultra confirmation prompt"
|
|
22737
22789
|
echo " --timeout <minutes> --ultra: max minutes to wait (default 30)"
|
|
22738
22790
|
echo " --help, -h Show this help"
|
|
@@ -22756,6 +22808,8 @@ cmd_review() {
|
|
|
22756
22808
|
return 0
|
|
22757
22809
|
;;
|
|
22758
22810
|
--ultra) review_ultra=true; shift ;;
|
|
22811
|
+
--risk) review_risk=true; shift ;;
|
|
22812
|
+
--json) review_format="json"; shift ;;
|
|
22759
22813
|
--yes|-y) review_assume_yes=true; shift ;;
|
|
22760
22814
|
--timeout)
|
|
22761
22815
|
shift
|
|
@@ -22811,6 +22865,29 @@ cmd_review() {
|
|
|
22811
22865
|
return $?
|
|
22812
22866
|
fi
|
|
22813
22867
|
|
|
22868
|
+
if [ "$review_risk" = true ]; then
|
|
22869
|
+
if [ -z "$review_pr" ] && [ -z "$review_since" ] && [[ "$review_target" =~ ^[0-9]+$ ]] && [ ! -e "$review_target" ]; then
|
|
22870
|
+
review_pr="$review_target"
|
|
22871
|
+
fi
|
|
22872
|
+
local risk_diff risk_src="uncommitted changes"
|
|
22873
|
+
if [ -n "$review_pr" ]; then
|
|
22874
|
+
command -v gh &>/dev/null || { echo "Error: gh CLI required for PR risk" >&2; return 1; }
|
|
22875
|
+
risk_diff=$(gh pr diff "$review_pr" 2>&1) || { echo "Error: Could not fetch PR #${review_pr}: $risk_diff" >&2; return 1; }
|
|
22876
|
+
risk_src="PR #${review_pr}"
|
|
22877
|
+
elif [ -n "$review_since" ]; then
|
|
22878
|
+
risk_diff=$(git diff "$review_since" 2>&1) || { echo "Error: Invalid commit reference: $review_since" >&2; return 1; }
|
|
22879
|
+
risk_src="changes since $review_since"
|
|
22880
|
+
elif [ "$review_staged" = true ]; then
|
|
22881
|
+
risk_diff=$(git diff --cached 2>/dev/null); risk_src="staged changes"
|
|
22882
|
+
else
|
|
22883
|
+
risk_diff=$(git diff 2>/dev/null)
|
|
22884
|
+
fi
|
|
22885
|
+
local risk_args=()
|
|
22886
|
+
[ "$review_format" = "json" ] && risk_args+=(--json)
|
|
22887
|
+
printf '%s\n' "$risk_diff" | python3 "$_LOKI_SCRIPT_DIR/lib/review_risk.py" ${risk_args[@]+"${risk_args[@]}"} "$risk_src"
|
|
22888
|
+
return $?
|
|
22889
|
+
fi
|
|
22890
|
+
|
|
22814
22891
|
# Severity level ordering (higher number = more severe)
|
|
22815
22892
|
_review_sev_level() {
|
|
22816
22893
|
case "$1" in
|
|
@@ -24647,16 +24724,17 @@ if total_removed > 0:
|
|
|
24647
24724
|
# Show or rebuild the memory index layer
|
|
24648
24725
|
if [ "${2:-}" = "rebuild" ]; then
|
|
24649
24726
|
PYTHONPATH="${SKILL_DIR}${PYTHONPATH:+:$PYTHONPATH}" python3 -c "
|
|
24727
|
+
import sys
|
|
24650
24728
|
try:
|
|
24651
|
-
from memory.
|
|
24652
|
-
|
|
24653
|
-
|
|
24654
|
-
|
|
24655
|
-
except ImportError:
|
|
24656
|
-
print('Error: memory.layers module not found')
|
|
24729
|
+
from memory.engine import MemoryEngine
|
|
24730
|
+
engine = MemoryEngine(base_path='.loki/memory')
|
|
24731
|
+
engine.rebuild_index()
|
|
24732
|
+
total = (engine.get_index() or {}).get('total_memories', 0)
|
|
24657
24733
|
except Exception as e:
|
|
24658
|
-
print(f'Error: {e}')
|
|
24659
|
-
|
|
24734
|
+
print(f'Error: index rebuild failed: {type(e).__name__}: {e}', file=sys.stderr)
|
|
24735
|
+
sys.exit(1)
|
|
24736
|
+
print(f'Index rebuilt: {total} memories indexed')
|
|
24737
|
+
" || return 1
|
|
24660
24738
|
else
|
|
24661
24739
|
cat .loki/memory/index.json 2>/dev/null | python3 -m json.tool 2>/dev/null || echo "No index found"
|
|
24662
24740
|
fi
|
|
@@ -26290,6 +26368,93 @@ SHOW_EOF
|
|
|
26290
26368
|
esac
|
|
26291
26369
|
}
|
|
26292
26370
|
|
|
26371
|
+
# loki merge: serial PR merge queue. Queue lives in .loki/merge-queue (one PR
|
|
26372
|
+
# number per line). A PR merges only when `gh pr checks` exits 0 (all green);
|
|
26373
|
+
# after each merge the next PR is rebased onto the new base (gh pr update-branch
|
|
26374
|
+
# --rebase) and must go green again before it merges. Never forces anything.
|
|
26375
|
+
cmd_merge() {
|
|
26376
|
+
local sub="${1:-help}"
|
|
26377
|
+
shift 2>/dev/null || true
|
|
26378
|
+
local qfile=".loki/merge-queue"
|
|
26379
|
+
case "$sub" in
|
|
26380
|
+
add)
|
|
26381
|
+
[ $# -gt 0 ] || { echo "Usage: loki merge add <pr-number>..." >&2; return 2; }
|
|
26382
|
+
mkdir -p .loki || return 1
|
|
26383
|
+
local n
|
|
26384
|
+
for n in "$@"; do
|
|
26385
|
+
case "$n" in ''|*[!0-9]*) echo "Invalid PR number: $n" >&2; return 2 ;; esac
|
|
26386
|
+
if [ -f "$qfile" ] && grep -qx "$n" "$qfile"; then
|
|
26387
|
+
echo "PR #$n already queued"
|
|
26388
|
+
else
|
|
26389
|
+
echo "$n" >> "$qfile"
|
|
26390
|
+
echo "Queued PR #$n"
|
|
26391
|
+
fi
|
|
26392
|
+
done
|
|
26393
|
+
;;
|
|
26394
|
+
list|ls)
|
|
26395
|
+
if [ -s "$qfile" ]; then cat "$qfile" | sed 's/^/#/'; else echo "Merge queue is empty"; fi
|
|
26396
|
+
;;
|
|
26397
|
+
clear)
|
|
26398
|
+
rm -f "$qfile"; echo "Merge queue cleared"
|
|
26399
|
+
;;
|
|
26400
|
+
run)
|
|
26401
|
+
local dry=false
|
|
26402
|
+
[ "${1:-}" = "--dry-run" ] && dry=true
|
|
26403
|
+
if [ ! -s "$qfile" ]; then echo "Merge queue is empty"; return 0; fi
|
|
26404
|
+
command -v gh >/dev/null 2>&1 || { echo "gh CLI is required" >&2; return 1; }
|
|
26405
|
+
local poll="${LOKI_MERGE_POLL_S:-20}" tries="${LOKI_MERGE_MAX_POLLS:-90}"
|
|
26406
|
+
local first=true n rc i remaining="" merged=0 blocked=0
|
|
26407
|
+
while IFS= read -r n; do
|
|
26408
|
+
[ -n "$n" ] || continue
|
|
26409
|
+
if $dry; then
|
|
26410
|
+
rc=0; gh pr checks "$n" >/dev/null 2>&1 || rc=$?
|
|
26411
|
+
case "$rc" in
|
|
26412
|
+
0) echo "#$n: checks green, would merge (squash)" ;;
|
|
26413
|
+
8) echo "#$n: checks pending, would wait" ;;
|
|
26414
|
+
*) echo "#$n: checks not green, would skip" ;;
|
|
26415
|
+
esac
|
|
26416
|
+
$first || echo "#$n: would rebase onto new base first"
|
|
26417
|
+
first=false
|
|
26418
|
+
continue
|
|
26419
|
+
fi
|
|
26420
|
+
if ! $first; then
|
|
26421
|
+
gh pr update-branch "$n" --rebase >/dev/null 2>&1 \
|
|
26422
|
+
|| { echo "#$n: rebase onto new base failed, left in queue"; remaining="$remaining$n"$'\n'; blocked=$((blocked+1)); continue; }
|
|
26423
|
+
echo "#$n: rebased onto new base"
|
|
26424
|
+
fi
|
|
26425
|
+
rc=8; i=0
|
|
26426
|
+
while [ "$rc" = "8" ] && [ "$i" -lt "$tries" ]; do
|
|
26427
|
+
rc=0; gh pr checks "$n" >/dev/null 2>&1 || rc=$?
|
|
26428
|
+
[ "$rc" = "8" ] && { sleep "$poll"; i=$((i+1)); }
|
|
26429
|
+
done
|
|
26430
|
+
if [ "$rc" != "0" ]; then
|
|
26431
|
+
echo "#$n: checks not green (rc=$rc), left in queue, not merged"
|
|
26432
|
+
remaining="$remaining$n"$'\n'; blocked=$((blocked+1)); continue
|
|
26433
|
+
fi
|
|
26434
|
+
if gh pr merge "$n" --squash >/dev/null 2>&1; then
|
|
26435
|
+
echo "#$n: merged"; merged=$((merged+1)); first=false
|
|
26436
|
+
else
|
|
26437
|
+
echo "#$n: merge failed, left in queue"
|
|
26438
|
+
remaining="$remaining$n"$'\n'; blocked=$((blocked+1))
|
|
26439
|
+
fi
|
|
26440
|
+
done < "$qfile"
|
|
26441
|
+
if $dry; then echo "Dry run: nothing changed"; return 0; fi
|
|
26442
|
+
printf '%s' "$remaining" > "$qfile"
|
|
26443
|
+
echo "Merged: $merged, left in queue: $blocked"
|
|
26444
|
+
[ "$blocked" -eq 0 ]
|
|
26445
|
+
;;
|
|
26446
|
+
help|--help|-h|*)
|
|
26447
|
+
echo "Usage: loki merge <add|list|run|clear>"
|
|
26448
|
+
echo " add <pr>... Queue pull requests"
|
|
26449
|
+
echo " list Show the queue"
|
|
26450
|
+
echo " run [--dry-run] Merge one at a time, only when checks are green;"
|
|
26451
|
+
echo " each next PR is rebased onto the new base first."
|
|
26452
|
+
echo " --dry-run lists what would happen, changes nothing."
|
|
26453
|
+
[ "$sub" = "help" ] || [ "$sub" = "--help" ] || [ "$sub" = "-h" ] || return 2
|
|
26454
|
+
;;
|
|
26455
|
+
esac
|
|
26456
|
+
}
|
|
26457
|
+
|
|
26293
26458
|
# R6: one-command rollback. Top-level, obvious entry point that mirrors the Bun
|
|
26294
26459
|
# `loki rollback` subcommands (list/show/to/latest) so both routes are at parity.
|
|
26295
26460
|
# Restore is destructive on .loki/ state, so it ALWAYS captures a forced
|
|
@@ -38105,18 +38270,28 @@ cmd_docker() {
|
|
|
38105
38270
|
# the port-in-use check, so the picked port is ignored on reuse). Run in
|
|
38106
38271
|
# a subshell so its internal `exit` paths (already-running -> exit 0,
|
|
38107
38272
|
# errors -> exit 1) never abort the blocking docker run.
|
|
38108
|
-
|
|
38109
|
-
|
|
38110
|
-
|
|
38111
|
-
|
|
38112
|
-
|
|
38273
|
+
# CP-LEGACY (D57): open the Control Plane, never the classic server,
|
|
38274
|
+
# unless LOKI_LEGACY_DASHBOARD=1 opts back in. Same open gate as before.
|
|
38275
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" != "1" ]; then
|
|
38276
|
+
local _cp_args=()
|
|
38277
|
+
if [ ! -t 1 ] || [ "$_docker_bg" = "1" ] || [ "${LOKI_NO_AUTO_OPEN:-0}" = "1" ]; then
|
|
38278
|
+
_cp_args=(--no-open)
|
|
38279
|
+
fi
|
|
38280
|
+
( _loki_control_ui ${_cp_args[@]+"${_cp_args[@]}"} ) >&2 || true
|
|
38281
|
+
else
|
|
38282
|
+
local _dash_port="57374"
|
|
38283
|
+
if declare -F loki_docker_pick_host_port >/dev/null 2>&1; then
|
|
38284
|
+
_dash_port="$(loki_docker_pick_host_port 2>/dev/null || echo 57374)"
|
|
38285
|
+
fi
|
|
38286
|
+
( cmd_dashboard_start --port "$_dash_port" ) || true
|
|
38113
38287
|
|
|
38114
|
-
|
|
38115
|
-
|
|
38116
|
-
|
|
38117
|
-
|
|
38118
|
-
|
|
38119
|
-
|
|
38288
|
+
# Auto-open the dashboard in the browser, gated EXACTLY like
|
|
38289
|
+
# run.sh:~10088: a TTY on stdout, not background, and not opted out via
|
|
38290
|
+
# LOKI_NO_AUTO_OPEN=1. cmd_dashboard_open reads the persisted port file,
|
|
38291
|
+
# so the URL is correct even when an existing dashboard was reused.
|
|
38292
|
+
if [ -t 1 ] && [ "$_docker_bg" != "1" ] && [ "${LOKI_NO_AUTO_OPEN:-0}" != "1" ]; then
|
|
38293
|
+
( cmd_dashboard_open ) || true
|
|
38294
|
+
fi
|
|
38120
38295
|
fi
|
|
38121
38296
|
|
|
38122
38297
|
# F4 FIX-DOCKER-STOP (write side): record this run's container/state so
|
|
@@ -38135,8 +38310,27 @@ cmd_docker() {
|
|
|
38135
38310
|
# its live status from the bind-mounted .loki/session.json. Best-effort and
|
|
38136
38311
|
# failure-swallowed: registry bookkeeping must never block a build.
|
|
38137
38312
|
_loki_docker_register_host running
|
|
38138
|
-
"
|
|
38139
|
-
|
|
38313
|
+
local rc _errlog=""
|
|
38314
|
+
# Capture output for a failure summary only when no TTY is attached (a TTY
|
|
38315
|
+
# run already shows the live output and must not be piped).
|
|
38316
|
+
if [ ! -t 1 ] || [ ! -t 0 ]; then
|
|
38317
|
+
_errlog="$(mktemp "${TMPDIR:-/tmp}/loki-docker-out.XXXXXX" 2>/dev/null)" || _errlog=""
|
|
38318
|
+
fi
|
|
38319
|
+
if [ -n "$_errlog" ]; then
|
|
38320
|
+
"${docker_argv[@]}" 2>&1 | tee "$_errlog"
|
|
38321
|
+
rc=${PIPESTATUS[0]}
|
|
38322
|
+
else
|
|
38323
|
+
"${docker_argv[@]}"
|
|
38324
|
+
rc=$?
|
|
38325
|
+
fi
|
|
38326
|
+
if [ "$rc" -ne 0 ]; then
|
|
38327
|
+
local _last=""
|
|
38328
|
+
[ -n "$_errlog" ] && _last="$(grep -v '^[[:space:]]*$' "$_errlog" 2>/dev/null | tail -n 1)"
|
|
38329
|
+
echo -e "${RED}loki docker: container exited with code ${rc}.${NC}" >&2
|
|
38330
|
+
[ -n "$_last" ] && echo "Last error: ${_last}" >&2
|
|
38331
|
+
echo "Fix hint: for PRs set GH_TOKEN=\$(gh auth token), and ensure ANTHROPIC_API_KEY or a Claude login is available, then rerun." >&2
|
|
38332
|
+
fi
|
|
38333
|
+
[ -n "$_errlog" ] && rm -f "$_errlog" 2>/dev/null
|
|
38140
38334
|
_loki_docker_register_host stopped
|
|
38141
38335
|
# F4 FIX-DOCKER-STOP (clear side): runs unconditionally after the blocking
|
|
38142
38336
|
# call (not gated on rc), same as register_host stopped.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Mobile emulator tests: run a mobile project's tests on an available emulator
|
|
3
|
+
# or simulator. Never reports PASS without a run.
|
|
4
|
+
# Usage: mobile-verify.sh [project_dir]
|
|
5
|
+
# Env: LOKI_MOBILE_TEST_CMD override the test command
|
|
6
|
+
# LOKI_MOBILE_TIMEOUT seconds (default 600)
|
|
7
|
+
# Exit: 0 = ran and passed, or not a mobile project (stated)
|
|
8
|
+
# 1 = ran and failed
|
|
9
|
+
# 3 = NOT VERIFIED (no emulator/simulator, or no runnable test command)
|
|
10
|
+
set -u
|
|
11
|
+
dir="${1:-.}"
|
|
12
|
+
cd "$dir" || { echo "mobile tests: NOT VERIFIED (cannot enter $dir)"; exit 3; }
|
|
13
|
+
|
|
14
|
+
kind=""
|
|
15
|
+
if [ -f pubspec.yaml ] && grep -q "flutter" pubspec.yaml 2>/dev/null; then
|
|
16
|
+
kind="flutter"
|
|
17
|
+
elif [ -f package.json ] && grep -qE '"(expo|react-native)"[[:space:]]*:' package.json 2>/dev/null; then
|
|
18
|
+
kind="react-native"
|
|
19
|
+
fi
|
|
20
|
+
if [ -z "$kind" ]; then
|
|
21
|
+
echo "mobile tests: not applicable (no React Native, Expo or Flutter project)"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
device=""
|
|
26
|
+
if command -v adb >/dev/null 2>&1; then
|
|
27
|
+
d="$(adb devices 2>/dev/null | awk 'NR>1 && $2=="device" {print $1; exit}')"
|
|
28
|
+
[ -n "$d" ] && device="android:$d"
|
|
29
|
+
fi
|
|
30
|
+
if [ -z "$device" ] && command -v xcrun >/dev/null 2>&1; then
|
|
31
|
+
d="$(xcrun simctl list devices booted 2>/dev/null | awk '/\(Booted\)/ {print "ios-simulator"; exit}')"
|
|
32
|
+
[ -n "$d" ] && device="$d"
|
|
33
|
+
fi
|
|
34
|
+
if [ -z "$device" ]; then
|
|
35
|
+
echo "mobile tests: NOT VERIFIED ($kind project, no booted Android emulator or iOS simulator found; tests were not run)"
|
|
36
|
+
exit 3
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
cmd="${LOKI_MOBILE_TEST_CMD:-}"
|
|
40
|
+
if [ -z "$cmd" ]; then
|
|
41
|
+
if [ "$kind" = "flutter" ]; then
|
|
42
|
+
if [ -d integration_test ]; then cmd="flutter test integration_test"; else cmd="flutter test"; fi
|
|
43
|
+
elif grep -q '"test:e2e"' package.json 2>/dev/null; then
|
|
44
|
+
cmd="npm run test:e2e"
|
|
45
|
+
elif grep -q '"e2e"' package.json 2>/dev/null; then
|
|
46
|
+
cmd="npm run e2e"
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
if [ -z "$cmd" ]; then
|
|
50
|
+
echo "mobile tests: NOT VERIFIED ($kind project on $device, but no e2e test script (test:e2e or e2e) to run)"
|
|
51
|
+
exit 3
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
timeout_s="${LOKI_MOBILE_TIMEOUT:-600}"
|
|
55
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
56
|
+
timeout -k 10 "$timeout_s" bash -c "$cmd"
|
|
57
|
+
else
|
|
58
|
+
bash -c "$cmd"
|
|
59
|
+
fi
|
|
60
|
+
rc=$?
|
|
61
|
+
if [ "$rc" -eq 0 ]; then
|
|
62
|
+
echo "mobile tests: PASS ($kind on $device, ran: $cmd)"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
echo "mobile tests: FAIL ($kind on $device, ran: $cmd, exit $rc)"
|
|
66
|
+
exit 1
|
package/autonomy/quickstart.sh
CHANGED
|
@@ -152,6 +152,12 @@ landing:static-landing-page:3
|
|
|
152
152
|
static:static-landing-page:3
|
|
153
153
|
marketing:static-landing-page:3
|
|
154
154
|
fullstack:full-stack-demo:3
|
|
155
|
+
shortener:rest-api:3
|
|
156
|
+
url:rest-api:2
|
|
157
|
+
urls:rest-api:2
|
|
158
|
+
stats:dashboard:3
|
|
159
|
+
statistics:dashboard:3
|
|
160
|
+
metrics:dashboard:3
|
|
155
161
|
MAP
|
|
156
162
|
}
|
|
157
163
|
|
|
@@ -171,6 +177,10 @@ _qs_is_stopword() {
|
|
|
171
177
|
# plus the curated keyword weights. No network, no provider, no LLM.
|
|
172
178
|
_qs_score_templates() {
|
|
173
179
|
local brief="$1"
|
|
180
|
+
# --best: print only the strongest real score (baseline excluded) so callers
|
|
181
|
+
# can tell a genuine match from simple-todo-app winning by default.
|
|
182
|
+
local best_mode=false
|
|
183
|
+
[ "${2:-}" = "--best" ] && best_mode=true
|
|
174
184
|
local tdir; tdir="$(_qs_templates_dir)"
|
|
175
185
|
local brief_lc; brief_lc=$(printf '%s' "$brief" | tr '[:upper:]' '[:lower:]')
|
|
176
186
|
|
|
@@ -198,7 +208,7 @@ _qs_score_templates() {
|
|
|
198
208
|
|
|
199
209
|
# No templates resolvable: fall back to the guaranteed default only.
|
|
200
210
|
if [ -z "$scores" ]; then
|
|
201
|
-
printf 'simple-todo-app\n'
|
|
211
|
+
if [ "$best_mode" = true ]; then printf '0\n'; else printf 'simple-todo-app\n'; fi
|
|
202
212
|
return 0
|
|
203
213
|
fi
|
|
204
214
|
|
|
@@ -230,7 +240,7 @@ _qs_score_templates() {
|
|
|
230
240
|
done
|
|
231
241
|
|
|
232
242
|
# Guaranteed default baseline: simple-todo-app gets +1 and wins exact ties.
|
|
233
|
-
deltas="${deltas}simple-todo-app\t1\n"
|
|
243
|
+
[ "$best_mode" = true ] || deltas="${deltas}simple-todo-app\t1\n"
|
|
234
244
|
|
|
235
245
|
# Fold: keep only names that are REAL templates (a keyword map entry naming
|
|
236
246
|
# a template that does not exist must not invent one), sum the deltas, then
|
|
@@ -250,7 +260,20 @@ _qs_score_templates() {
|
|
|
250
260
|
printf "%d\t%d\t%s\n", sum[n], prio, n
|
|
251
261
|
}
|
|
252
262
|
}
|
|
253
|
-
' | sort -t"$(printf '\t')" -k1,1nr -k2,2n -k3,3 |
|
|
263
|
+
' | sort -t"$(printf '\t')" -k1,1nr -k2,2n -k3,3 | {
|
|
264
|
+
if [ "$best_mode" = true ]; then
|
|
265
|
+
head -1 | cut -f1 | awk '{ print ($1 == "" ? 0 : $1) }'
|
|
266
|
+
else
|
|
267
|
+
head -3 | cut -f3
|
|
268
|
+
fi
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# _qs_match_strength <brief>: the best real (non-baseline) template score. 0
|
|
273
|
+
# means no template matched the brief at all; >= 2 is a real match (one
|
|
274
|
+
# filename token or one curated keyword).
|
|
275
|
+
_qs_match_strength() {
|
|
276
|
+
_qs_score_templates "$1" --best
|
|
254
277
|
}
|
|
255
278
|
|
|
256
279
|
# _qs_template_summary <name>: a short one-line description for the picker.
|
|
@@ -1363,6 +1386,10 @@ cmd_quickstart() {
|
|
|
1363
1386
|
[ -n "$line" ] && top3+=("$line")
|
|
1364
1387
|
done < <(_qs_score_templates "$brief")
|
|
1365
1388
|
|
|
1389
|
+
local match_strength=0
|
|
1390
|
+
match_strength="$(_qs_match_strength "$brief")"
|
|
1391
|
+
case "$match_strength" in ''|*[!0-9]*) match_strength=0;; esac
|
|
1392
|
+
|
|
1366
1393
|
# Defensive: guarantee a default if scoring produced nothing.
|
|
1367
1394
|
if [ "${#top3[@]}" -eq 0 ]; then
|
|
1368
1395
|
top3=("simple-todo-app")
|
|
@@ -1388,6 +1415,9 @@ cmd_quickstart() {
|
|
|
1388
1415
|
printf ' %d) %-18s %s%s\n' "$i" "$t" "$(_qs_template_summary "$t")" "$suffix"
|
|
1389
1416
|
i=$((i + 1))
|
|
1390
1417
|
done
|
|
1418
|
+
if [ "$match_strength" -lt 2 ] && [ -n "$brief" ]; then
|
|
1419
|
+
printf ' No strong match (score %s); these are weak guesses. Type "none" to build from your description.\n' "$match_strength"
|
|
1420
|
+
fi
|
|
1391
1421
|
printf ' Choose 1-%d, or press Enter for 1.\n' "${#top3[@]}"
|
|
1392
1422
|
printf '> '
|
|
1393
1423
|
read -r pick 2>/dev/null || pick=""
|
|
@@ -1400,7 +1430,17 @@ cmd_quickstart() {
|
|
|
1400
1430
|
# non-interactive choice is exactly the one an operator pressing
|
|
1401
1431
|
# Enter would get. No picker, no prompt, no second code path.
|
|
1402
1432
|
printf '%sStep 3 of 4: Template%s\n' "$_QS_BOLD" "$_QS_NC"
|
|
1403
|
-
|
|
1433
|
+
if [ "$match_strength" -ge 2 ]; then
|
|
1434
|
+
printf ' Selected %s (top match, score %s) for "%s".\n\n' "${top3[0]}" "$match_strength" "$brief"
|
|
1435
|
+
elif [ -n "$brief" ]; then
|
|
1436
|
+
# No template matched: building a todo app for an unrelated
|
|
1437
|
+
# brief is wrong (issue #217). Use the brief as the spec.
|
|
1438
|
+
pick="none"
|
|
1439
|
+
printf ' No strong template match for "%s" (score %s).\n' "$brief" "$match_strength"
|
|
1440
|
+
printf ' Falling back to a plain-spec build from your description.\n\n'
|
|
1441
|
+
else
|
|
1442
|
+
printf ' Selected %s (default) for the sample Todo app.\n\n' "${top3[0]}"
|
|
1443
|
+
fi
|
|
1404
1444
|
fi
|
|
1405
1445
|
|
|
1406
1446
|
# An explicit rejection is honored, never converted into a selection.
|
package/autonomy/run.sh
CHANGED
|
@@ -11804,6 +11804,26 @@ result = {
|
|
|
11804
11804
|
"successes": filter_relevant(successes, context)
|
|
11805
11805
|
}
|
|
11806
11806
|
|
|
11807
|
+
# Project memory: learnings and decisions persisted by earlier runs in THIS
|
|
11808
|
+
# project, newest first, bounded by entry count and total characters.
|
|
11809
|
+
if os.environ.get("LOKI_PROJECT_MEMORY", "1") != "0":
|
|
11810
|
+
try:
|
|
11811
|
+
max_chars = int(os.environ.get("LOKI_PROJECT_MEMORY_MAX_CHARS", "2000"))
|
|
11812
|
+
except ValueError:
|
|
11813
|
+
max_chars = 2000
|
|
11814
|
+
mem, used = [], 0
|
|
11815
|
+
for kind in ("mistakes", "patterns", "successes"):
|
|
11816
|
+
for e in reversed(load_jsonl(f".loki/memory/learnings/project-{kind}.jsonl")):
|
|
11817
|
+
d = e.get("description", "").strip()[:300]
|
|
11818
|
+
if not d or used + len(d) > max_chars:
|
|
11819
|
+
continue
|
|
11820
|
+
used += len(d)
|
|
11821
|
+
mem.append({"kind": kind[:-1] if kind != "successes" else "success", "description": d})
|
|
11822
|
+
if len(mem) >= 15:
|
|
11823
|
+
break
|
|
11824
|
+
if mem:
|
|
11825
|
+
result["project_memory"] = mem
|
|
11826
|
+
|
|
11807
11827
|
with open(".loki/state/relevant-learnings.json", 'w') as f:
|
|
11808
11828
|
json.dump(result, f, indent=2)
|
|
11809
11829
|
LEARNINGS_SCRIPT
|
|
@@ -11863,7 +11883,23 @@ def get_existing_hashes(filepath):
|
|
|
11863
11883
|
return hashes
|
|
11864
11884
|
|
|
11865
11885
|
def save_entries(filepath, entries, category):
|
|
11866
|
-
"""Save entries avoiding duplicates (case-insensitive)
|
|
11886
|
+
"""Save entries avoiding duplicates (case-insensitive).
|
|
11887
|
+
|
|
11888
|
+
Also mirrors into the project-scoped memory (.loki/memory/learnings/
|
|
11889
|
+
project-<kind>.jsonl) so the next run in this project reads them back.
|
|
11890
|
+
"""
|
|
11891
|
+
if os.environ.get("LOKI_PROJECT_MEMORY", "1") != "0":
|
|
11892
|
+
try:
|
|
11893
|
+
pdir = ".loki/memory/learnings"
|
|
11894
|
+
os.makedirs(pdir, exist_ok=True)
|
|
11895
|
+
pfile = os.path.join(pdir, "project-" + os.path.basename(filepath))
|
|
11896
|
+
if pfile != filepath:
|
|
11897
|
+
save_entries_to(pfile, entries, category)
|
|
11898
|
+
except Exception:
|
|
11899
|
+
pass
|
|
11900
|
+
return save_entries_to(filepath, entries, category)
|
|
11901
|
+
|
|
11902
|
+
def save_entries_to(filepath, entries, category):
|
|
11867
11903
|
existing = get_existing_hashes(filepath)
|
|
11868
11904
|
saved = 0
|
|
11869
11905
|
with open(filepath, 'a') as f:
|
|
@@ -19522,8 +19558,14 @@ start_dashboard() {
|
|
|
19522
19558
|
# Auto-open the dashboard in the browser, but ONLY for an interactive
|
|
19523
19559
|
# foreground session. loki_open_url (lib/browser-open.sh) refuses on
|
|
19524
19560
|
# no TTY, CI, test runners, LOKI_NO_BROWSER=1 or LOKI_NO_AUTO_OPEN=1.
|
|
19561
|
+
# CP-LEGACY (D57): open the Control Plane, not this legacy server,
|
|
19562
|
+
# unless LOKI_LEGACY_DASHBOARD=1 opts back in.
|
|
19525
19563
|
if [ "${BACKGROUND_MODE:-false}" != "true" ]; then
|
|
19526
|
-
|
|
19564
|
+
if [ "${LOKI_LEGACY_DASHBOARD:-0}" = "1" ]; then
|
|
19565
|
+
loki_open_url "${url_scheme}://127.0.0.1:$DASHBOARD_PORT/" 2>/dev/null || true
|
|
19566
|
+
elif [ -t 1 ] && [ "${LOKI_NO_AUTO_OPEN:-0}" != "1" ] && [ -x "${skill_dir}/autonomy/loki" ]; then
|
|
19567
|
+
LOKI_UI_NO_CLASSIC=1 "${skill_dir}/autonomy/loki" ui >/dev/null 2>&1 || true
|
|
19568
|
+
fi
|
|
19527
19569
|
fi
|
|
19528
19570
|
return 0
|
|
19529
19571
|
else
|
package/completions/_loki
CHANGED
|
@@ -188,6 +188,8 @@ function _loki_commands {
|
|
|
188
188
|
'deploy:Advisory deploy command (print-only)'
|
|
189
189
|
'docker:Run Loki in a Docker container'
|
|
190
190
|
'mcp:MCP server commands'
|
|
191
|
+
'merge:Green-only serial merge queue'
|
|
192
|
+
'acp:Agent Client Protocol agent over stdio'
|
|
191
193
|
'magic:Spec-driven component generation'
|
|
192
194
|
'assets:Export/import shareable team assets'
|
|
193
195
|
'spec:Living spec drift detection'
|