loki-mode 9.16.0 → 9.17.2

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/autonomy/run.sh CHANGED
@@ -2049,40 +2049,7 @@ except Exception:
2049
2049
  pass
2050
2050
  " 2>/dev/null || true)"
2051
2051
  fi
2052
- if [ -n "$_cost" ]; then
2053
- _fields="${_fields} | \$${_cost}"
2054
- # PROJECTED spend at the iteration cap, shown beside the actual.
2055
- #
2056
- # WHY. MAX_ITERATIONS (default 25) is the ONLY backstop on a run's cost:
2057
- # LOKI_BUDGET_LIMIT defaults to "" and LOKI_MAX_DURATION to 0, both
2058
- # documented at run.sh:1118-1121, and the runtime says so out loud at
2059
- # :21333. That is a defensible default -- a run killed mid-flight at a
2060
- # dollar threshold the user never chose is worse than one that finishes.
2061
- # But it left the user unable to SEE where the run was heading: $4.20 at
2062
- # iteration 3 of 25 reads as cheap right up to the moment it is not.
2063
- #
2064
- # Linear extrapolation, and labelled "proj" rather than presented as a
2065
- # forecast: later iterations are usually cheaper than early ones (more
2066
- # cache hits, smaller diffs), so this is an upper bound, not a promise.
2067
- # Shown only when it would actually tell the user something -- from
2068
- # iteration 2 (one data point cannot extrapolate) and only when the
2069
- # projection is meaningfully above what has already been spent.
2070
- if [ "${_iter:-0}" -ge 2 ] && [ "${_max:-0}" -gt 0 ]; then
2071
- local _proj
2072
- _proj="$(LOKI_C="$_cost" LOKI_I="$_iter" LOKI_M="$_max" python3 -c "
2073
- import os
2074
- try:
2075
- c = float(os.environ['LOKI_C']); i = int(os.environ['LOKI_I']); m = int(os.environ['LOKI_M'])
2076
- if i > 0 and m > i:
2077
- p = c / i * m
2078
- if p >= c * 1.5:
2079
- print('%.2f' % p)
2080
- except Exception:
2081
- pass
2082
- " 2>/dev/null || true)"
2083
- [ -n "$_proj" ] && _fields="${_fields} (proj \$${_proj} at ${_max})"
2084
- fi
2085
- fi
2052
+ [ -n "$_cost" ] && _fields="${_fields} | \$${_cost}"
2086
2053
 
2087
2054
  # Files changed (+ins/-del and file count) vs the run start SHA. Reuse the
2088
2055
  # build_completion_summary diff approach incl. the .loki/.git exclude pathspec.
@@ -3349,34 +3316,10 @@ detect_complexity() {
3349
3316
  file_count="${file_count:-0}"
3350
3317
  file_count="${file_count//[^0-9]/}"
3351
3318
 
3352
- # Check for external integrations.
3353
- #
3354
- # THE EXCLUDES ARE LOAD-BEARING. This grep used to prune nothing while the
3355
- # find eleven lines above it prunes node_modules/.git/vendor/dist/build --
3356
- # same function, same intent, inconsistent implementation. With --include
3357
- # "*.json" that meant ANY transitive dependency whose package.json mentions
3358
- # azure, stripe or aws-sdk set has_external=true.
3359
- #
3360
- # And has_external does not merely block "simple": in the classifier below it
3361
- # jumps straight to "complex", skipping "standard". So a one-liner in any
3362
- # repo that has ever run npm install landed on the MOST expensive tier, which
3363
- # then runs the architecture doc suite (up to 300s of silence per attempt)
3364
- # and holds the council's forced minimum-iteration floor at 3 instead of 1.
3365
- #
3366
- # Reproduced from scratch before fixing: a project with ONE dependency naming
3367
- # @azure/core classified complex; adding --exclude-dir=node_modules made the
3368
- # identical project classify simple. It also fired TRUE on this repo.
3369
- #
3370
- # A prior incident matches exactly -- a coffee landing page took 1h34m over
3371
- # 11 iterations because the simple fast-path never engaged. The fast path was
3372
- # correctly built and correctly wired the whole time; this one missing prune
3373
- # was what made it unreachable.
3319
+ # Check for external integrations
3374
3320
  local has_external=false
3375
3321
  if grep -rq "oauth\|SAML\|OIDC\|stripe\|twilio\|aws-sdk\|@google-cloud\|azure" \
3376
- "$target_dir" --include="*.json" --include="*.ts" --include="*.js" \
3377
- --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=vendor \
3378
- --exclude-dir=dist --exclude-dir=build --exclude-dir=__pycache__ \
3379
- --exclude-dir=.venv --exclude-dir=venv 2>/dev/null; then
3322
+ "$target_dir" --include="*.json" --include="*.ts" --include="*.js" 2>/dev/null; then
3380
3323
  has_external=true
3381
3324
  fi
3382
3325
 
@@ -11399,7 +11342,58 @@ enforce_test_coverage() {
11399
11342
  if [ -f "${TARGET_DIR:-.}/package.json" ]; then
11400
11343
  # BUG-EC-014: Wrap test runners with timeout to prevent hanging indefinitely
11401
11344
  local gate_timeout="${LOKI_GATE_TIMEOUT:-300}" # 5 minutes default
11402
- if grep -q '"vitest"' "${TARGET_DIR:-.}/package.json" 2>/dev/null; then
11345
+ # A DECLARED scripts.test wins over an installed package.
11346
+ #
11347
+ # The v7.41.x fix below already established that grep false-positives on
11348
+ # devDependencies -- read its comment -- but it only guarded the `else`
11349
+ # branch, so the three grep branches AHEAD of it still shadowed it. This
11350
+ # repo is the proof: jest is a devDependency with no jest config while
11351
+ # scripts.test runs `bash -n` + `node --test`, and the grep branch
11352
+ # hijacked the runner, ran jest over 895 non-jest files and failed every
11353
+ # one of them.
11354
+ #
11355
+ # _has_declared_test_script is true only when the project DECLARES a real
11356
+ # test script, which is the project's own statement of its runner. When
11357
+ # it declares nothing, the grep branches still apply exactly as before --
11358
+ # that is the legitimate case they were written for (a package that ships
11359
+ # a runner but no npm script).
11360
+ local _declared_test_script=""
11361
+ _declared_test_script=$(_LOKI_PKG="${TARGET_DIR:-.}/package.json" python3 -c "
11362
+ import json, os, sys
11363
+ try:
11364
+ with open(os.environ['_LOKI_PKG']) as f:
11365
+ d = json.load(f)
11366
+ except Exception:
11367
+ sys.exit(0)
11368
+ if not isinstance(d, dict):
11369
+ sys.exit(0)
11370
+ t = (d.get('scripts') or {}).get('test') or ''
11371
+ if 'no test specified' in t.lower():
11372
+ sys.exit(0)
11373
+ sys.stdout.write(t.strip())
11374
+ " 2>/dev/null || echo "")
11375
+ if [ -n "$_declared_test_script" ]; then
11376
+ # Run the DECLARED script. This body used to live in the trailing
11377
+ # `else`; a no-op `:` here would have terminated the if/elif chain
11378
+ # and left test_runner=none, turning a false BLOCK into a silently
11379
+ # unmeasured gate -- strictly worse than the bug being fixed.
11380
+ #
11381
+ # LOKI_TEST_COMMAND lets an operator override the invocation; the
11382
+ # default is the project's own `npm test`.
11383
+ local _test_cmd="${LOKI_TEST_COMMAND:-npm test}"
11384
+ # Label the runner by what the script invokes so evidence is honest
11385
+ # (node --test, vitest, jest, etc. all surface here).
11386
+ case "$_declared_test_script" in
11387
+ *"node --test"*|*"node:test"*) test_runner="node-test" ;;
11388
+ *vitest*) test_runner="vitest" ;;
11389
+ *jest*) test_runner="jest" ;;
11390
+ *mocha*) test_runner="mocha" ;;
11391
+ *) test_runner="npm-test" ;;
11392
+ esac
11393
+ local output
11394
+ output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" sh -c "$_test_cmd" 2>&1) || test_passed=false
11395
+ details="$test_runner ($_test_cmd): $(echo "$output" | tail -5 | tr '\n' ' ')"
11396
+ elif grep -q '"vitest"' "${TARGET_DIR:-.}/package.json" 2>/dev/null; then
11403
11397
  test_runner="vitest"
11404
11398
  local output
11405
11399
  output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" npx vitest run --reporter=json 2>&1) || test_passed=false
@@ -11425,37 +11419,13 @@ enforce_test_coverage() {
11425
11419
  # would false-positive on devDeps / unrelated keys), then run the
11426
11420
  # configured command. This MUST sit before the monorepo/python/go/rust
11427
11421
  # checks, all of which gate on test_runner=="none".
11428
- local _pkg_test_script
11429
- _pkg_test_script=$(_LOKI_PKG="${TARGET_DIR:-.}/package.json" python3 -c "
11430
- import json, os, sys
11431
- try:
11432
- with open(os.environ['_LOKI_PKG']) as f:
11433
- d = json.load(f)
11434
- except Exception:
11435
- sys.exit(0)
11436
- t = (d.get('scripts') or {}).get('test') or ''
11437
- # npm's default placeholder; treat as 'no test'.
11438
- if 'no test specified' in t.lower():
11439
- sys.exit(0)
11440
- sys.stdout.write(t.strip())
11441
- " 2>/dev/null || echo "")
11442
- if [ -n "$_pkg_test_script" ]; then
11443
- # LOKI_TEST_COMMAND lets an operator override the invocation; the
11444
- # default is the project's own `npm test`.
11445
- local _test_cmd="${LOKI_TEST_COMMAND:-npm test}"
11446
- # Label the runner by what the script invokes so evidence is
11447
- # honest (node --test, vitest, jest, etc. all surface here).
11448
- case "$_pkg_test_script" in
11449
- *"node --test"*|*"node:test"*) test_runner="node-test" ;;
11450
- *vitest*) test_runner="vitest" ;;
11451
- *jest*) test_runner="jest" ;;
11452
- *mocha*) test_runner="mocha" ;;
11453
- *) test_runner="npm-test" ;;
11454
- esac
11455
- local output
11456
- output=$(cd "${TARGET_DIR:-.}" && timeout "$gate_timeout" sh -c "$_test_cmd" 2>&1) || test_passed=false
11457
- details="$test_runner ($_test_cmd): $(echo "$output" | tail -5 | tr '\n' ' ')"
11458
- fi
11422
+ #
11423
+ # That handler now runs in the FIRST branch above, because a declared
11424
+ # script must win over an installed devDependency. Nothing is left to
11425
+ # do here: reaching this point means the project declares no test
11426
+ # script AND ships no recognised runner, so test_runner stays "none"
11427
+ # and the monorepo/python/go/rust detection below takes over.
11428
+ :
11459
11429
  fi
11460
11430
  fi
11461
11431
 
@@ -12355,14 +12325,6 @@ auto_generate_docs_if_needed() {
12355
12325
  elif command -v timeout >/dev/null 2>&1; then
12356
12326
  _doc_cmd=(timeout "${_doc_to}s")
12357
12327
  fi
12358
- # SAY WHAT IS HAPPENING BEFORE GOING QUIET. This call discards child output
12359
- # and can run for the full timeout (default 300s), so without this line the
12360
- # user sees a single "Auto-documentation" header and then minutes of nothing.
12361
- # A silent gap reads as a hang: the observed incident was a build stuck ~55
12362
- # min here with the work committed but never pushed, and nothing on screen
12363
- # said which step owned the time. Naming the step and its cap turns an
12364
- # apparent freeze into a bounded wait the user can reason about.
12365
- log_info "Auto-documentation: generating architecture suite (no output until it finishes; up to ${_doc_to}s)"
12366
12328
  if "${_doc_cmd[@]}" "$loki_bin" docs generate "$project_dir" >/dev/null 2>&1; then
12367
12329
  :
12368
12330
  else
@@ -12421,10 +12383,6 @@ run_magic_debate_gate() {
12421
12383
  # verdict on genuinely thin input, not a spurious process block.
12422
12384
  log_info "Magic Modules: running debate on '$latest_name'"
12423
12385
  local debate_out debate_rc
12424
- # Captured to a variable, so nothing reaches the screen for up to 300s. Same
12425
- # reasoning as the doc suite above: name the step and its cap so a bounded
12426
- # wait does not read as a hang.
12427
- log_info "Magic debate: reviewing $latest_name (2 rounds, output shown when it finishes; up to 300s)"
12428
12386
  debate_out=$(cd "$TARGET_DIR" && PYTHONPATH="$PROJECT_DIR" LOKI_PROVIDER="${PROVIDER_NAME:-claude}" \
12429
12387
  timeout 300 "$PROJECT_DIR/autonomy/loki" magic debate "$latest_name" --rounds 2 2>&1) \
12430
12388
  && debate_rc=0 || debate_rc=$?
@@ -20089,11 +20047,8 @@ except Exception:
20089
20047
  if [ -n "$gate_escalation_context" ]; then
20090
20048
  _legacy_priority="${_legacy_priority}${_legacy_priority:+ }${gate_escalation_context}"
20091
20049
  fi
20092
- # Same cap, same reason, same default as the degraded path above --
20093
- # a pasted spec has to be bounded, but the bound must not silently
20094
- # eat requirements. 4000 bytes dropped anything past ~600 words.
20095
20050
  if [ -n "$prd" ] && [ -f "$prd" ]; then
20096
- _legacy_prd_content=$(head -c "${LOKI_DEGRADED_PRD_CAP:-24000}" "$prd")
20051
+ _legacy_prd_content=$(head -c 4000 "$prd")
20097
20052
  fi
20098
20053
  if [ $retry -eq 0 ]; then
20099
20054
  if [ -n "$prd" ]; then
@@ -20144,35 +20099,9 @@ except Exception:
20144
20099
 
20145
20100
  if [ "${PROVIDER_DEGRADED:-false}" = "true" ]; then
20146
20101
  # Degraded providers: simpler wording, but still static-first.
20147
- #
20148
- # THE CAP IS NOW ANNOUNCED, NOT SILENT. This path PASTES the spec text
20149
- # (a degraded provider cannot be told "read the file at this path" the
20150
- # way claude/cline/opencode are at :20196), so it has to be bounded. It
20151
- # was bounded at 4000 bytes with no notice: a requirement past ~600 words
20152
- # was dropped mid-sentence and the model never knew a spec existed beyond
20153
- # what it saw. Demonstrated on a 4229-byte spec -- the requirement on the
20154
- # last line was simply absent from what the model received.
20155
- #
20156
- # That is the same class of defect spec-expand.sh:5-7 already names for
20157
- # OpenAPI ("a 40-operation file loses 21 of 40 ops") and fixed for
20158
- # contracts only. Markdown specs still had it, and only for the two
20159
- # degraded providers -- so codex and aider users silently got a worse
20160
- # build than claude users from the identical spec.
20161
- #
20162
- # Raised to 24000 (a large PRD fits whole) and, when the spec still
20163
- # exceeds it, the model is TOLD so and given the path to read the rest.
20164
- # An unannounced truncation makes the model confidently build the wrong
20165
- # thing; an announced one makes it go look.
20166
- local _prd_cap="${LOKI_DEGRADED_PRD_CAP:-24000}"
20167
20102
  local prd_content=""
20168
- local _prd_truncated=0
20169
20103
  if [ -n "$prd" ] && [ -f "$prd" ]; then
20170
- prd_content=$(head -c "$_prd_cap" "$prd")
20171
- local _prd_bytes
20172
- _prd_bytes=$(wc -c < "$prd" 2>/dev/null | tr -d ' ')
20173
- if [ -n "$_prd_bytes" ] && [ "$_prd_bytes" -gt "$_prd_cap" ] 2>/dev/null; then
20174
- _prd_truncated=1
20175
- fi
20104
+ prd_content=$(head -c 4000 "$prd")
20176
20105
  fi
20177
20106
 
20178
20107
  local degraded_prd_anchor="Loki Mode"
@@ -20201,38 +20130,6 @@ except Exception:
20201
20130
  [ -n "$queue_tasks" ] && printf 'Tasks: %s\n' "$queue_tasks"
20202
20131
  if [ -n "$prd" ]; then
20203
20132
  printf 'PRD contents: %s\n' "$prd_content"
20204
- # Announce the cut. Silence here is what made the old 4000-byte cap
20205
- # dangerous: the model treated a partial spec as the whole spec and
20206
- # built confidently against requirements it had never seen. Naming
20207
- # the file lets it read the remainder itself.
20208
- if [ "${_prd_truncated:-0}" = "1" ]; then
20209
- printf 'NOTE: the spec above is TRUNCATED at %s bytes. The full spec is at %s -- read it before deciding the work is complete.\n' \
20210
- "$_prd_cap" "$prd"
20211
- fi
20212
- fi
20213
-
20214
- # FIRST-PASS EXCELLENCE FOR DEGRADED PROVIDERS.
20215
- #
20216
- # This directive existed only for Claude. providers/claude.sh:322 injects
20217
- # it via --append-system-prompt, a flag codex/aider do not have, so the
20218
- # one mechanism built specifically to make a WEAKER model land complete
20219
- # on iteration 1 reached only the strongest one. Measured before writing
20220
- # this: grep for FIRST_PASS_EXCELLENCE returns 0 in codex.sh, aider.sh,
20221
- # cline.sh and opencode.sh.
20222
- #
20223
- # It matters most exactly where it was missing. The premise (recorded
20224
- # when the Claude version was built) is that iteration count is a proxy
20225
- # for how much the first pass missed, and that for a weak model context
20226
- # quality beats iteration count. Codex is also the free on-ramp, so the
20227
- # users least able to absorb a bad build were the ones getting no help.
20228
- #
20229
- # Condensed rather than byte-mirrored: the Claude text is ~4.3KB of
20230
- # system prompt, and these providers take it inline in the user turn
20231
- # where budget is tighter. The four load-bearing instructions are kept --
20232
- # build fully, wire the backend, verify by RUNNING, commit to one design.
20233
- # Same iteration-1 gate and same env var, so one switch controls both.
20234
- if [ "${LOKI_FIRST_PASS_EXCELLENCE:-1}" != "0" ] && [ "${iteration:-1}" -le 1 ] 2>/dev/null; then
20235
- printf '%s\n' '[FIRST-PASS EXCELLENCE] Treat THIS pass as your one shot to ship a complete, working solution. The loop is a safety net, not a plan. 1) BUILD IT FULLY: no stubs, no TODOs, no placeholder or mock data where real logic belongs. If the spec implies a backend (auth, persistence, a form that submits), WIRE IT so it actually persists -- a UI whose buttons do nothing is the most common failure. 2) VERIFY BY RUNNING each acceptance path, not by reading the code. 3) DECIDE the architecture now rather than refactoring later. 4) Commit to ONE specific design; avoid the generic purple-gradient default look.'
20236
20133
  fi
20237
20134
  printf '</dynamic_context>\n'
20238
20135
  return 0
@@ -421,6 +421,30 @@ _verify_zero_tests_executed() {
421
421
  }
422
422
 
423
423
  # ---------------------------------------------------------------------------
424
+ # Reads scripts.test out of a package.json, or prints nothing.
425
+ #
426
+ # Parsed as JSON, never grepped: a substring search over the whole file is the
427
+ # exact bug this helper exists to remove, and re-introducing it one level down
428
+ # would be invisible. A malformed package.json prints nothing, which routes to
429
+ # runner=none -> INCONCLUSIVE, never to a guessed runner.
430
+ _verify_pkg_test_script() {
431
+ local tree="$1"
432
+ [ -f "$tree/package.json" ] || return 0
433
+ python3 -c '
434
+ import json,sys
435
+ try:
436
+ with open(sys.argv[1]) as fh:
437
+ d = json.load(fh)
438
+ except Exception:
439
+ sys.exit(0)
440
+ if not isinstance(d, dict):
441
+ sys.exit(0)
442
+ s = d.get("scripts")
443
+ if isinstance(s, dict) and isinstance(s.get("test"), str):
444
+ sys.stdout.write(s["test"])
445
+ ' "$tree/package.json" 2>/dev/null || true
446
+ }
447
+
424
448
  # Gate: tests (faithful port of enforce_test_coverage detection, run.sh:6624).
425
449
  #
426
450
  # Detection order mirrors the source: vitest -> jest -> mocha (package.json),
@@ -451,16 +475,52 @@ verify_gate_tests() {
451
475
  local out=""
452
476
 
453
477
  if [ -f "$tree/package.json" ]; then
454
- if grep -q '"vitest"' "$tree/package.json" 2>/dev/null; then
455
- runner="vitest"
456
- out="$(cd "$tree" && $_vt npx vitest run 2>&1)" || rc=$?
457
- elif grep -q '"jest"' "$tree/package.json" 2>/dev/null; then
458
- runner="jest"
459
- out="$(cd "$tree" && $_vt npx jest --passWithNoTests --forceExit 2>&1)" || rc=$?
460
- elif grep -q '"mocha"' "$tree/package.json" 2>/dev/null; then
461
- runner="mocha"
462
- out="$(cd "$tree" && $_vt npx mocha 2>&1)" || rc=$?
463
- fi
478
+ # WHICH runner, decided by what `npm test` ACTUALLY INVOKES -- not by
479
+ # what happens to be installed.
480
+ #
481
+ # The old check was `grep '"jest"' package.json`, which matches a
482
+ # DEVDEPENDENCY entry. On this very repo that is exactly what happened:
483
+ # jest is a devDependency with NO jest config, while scripts.test runs
484
+ # bash -n plus `node --test`. So verify hijacked the runner, jest globbed
485
+ # 895 files that are not jest tests, every one reported "Your test suite
486
+ # must contain at least one test", and `loki verify` returned BLOCKED on
487
+ # a clean tree -- permanently, for a defect that does not exist.
488
+ #
489
+ # A false BLOCK on the flagship verification command is worse than a
490
+ # missed one: it trains users to ignore the verdict.
491
+ #
492
+ # scripts.test is the project's own declaration of its runner, so it is
493
+ # the signal with authority here. A declared script that names none of
494
+ # the three is still RUN, via `npm test` -- mirroring run.sh's npm-test
495
+ # fallback. Falling through instead would be its own defect: this repo
496
+ # declares `bash -n ... && node --test ...`, and without the fallback
497
+ # verify skipped it and ran PYTEST over a bash/node project.
498
+ local _test_script=""
499
+ _test_script="$(_verify_pkg_test_script "$tree")"
500
+ case "$_test_script" in
501
+ *vitest*)
502
+ runner="vitest"
503
+ out="$(cd "$tree" && $_vt npx vitest run 2>&1)" || rc=$? ;;
504
+ *jest*)
505
+ runner="jest"
506
+ out="$(cd "$tree" && $_vt npx jest --passWithNoTests --forceExit 2>&1)" || rc=$? ;;
507
+ *mocha*)
508
+ runner="mocha"
509
+ out="$(cd "$tree" && $_vt npx mocha 2>&1)" || rc=$? ;;
510
+ "")
511
+ : ;; # nothing declared: leave runner=none for the paths below
512
+ *"no test specified"*)
513
+ : ;; # npm's placeholder is not a test script
514
+ *)
515
+ # Labelled by what it invokes, so the evidence names the real
516
+ # runner rather than a generic "npm".
517
+ case "$_test_script" in
518
+ *"node --test"*|*"node:test"*) runner="node-test" ;;
519
+ *pytest*) runner="pytest" ;;
520
+ *) runner="npm-test" ;;
521
+ esac
522
+ out="$(cd "$tree" && $_vt npm test 2>&1)" || rc=$? ;;
523
+ esac
464
524
  fi
465
525
 
466
526
  if [ "$runner" = "none" ]; then
@@ -1060,10 +1120,38 @@ print("%d %d %d %d" % (crit, high, mod, low))
1060
1120
  ' 2>/dev/null || echo "")"
1061
1121
  if [ -n "$sev" ]; then
1062
1122
  read -r _c _h _m _l <<<"$sev"
1123
+ # SHIPPED vs dev. The audit above covers ALL dependencies,
1124
+ # which is right for a gate -- a compromised build tool is a
1125
+ # real risk -- but the FINDING must say which of the two it
1126
+ # is. This repo reports 4 high CVEs while `npm audit
1127
+ # --omit=dev` reports ZERO: nothing a user installs is
1128
+ # vulnerable. A receipt that says "4 high severity
1129
+ # vulnerabilities" with no such qualifier reads as "the
1130
+ # shipped product is vulnerable", which is a materially
1131
+ # different and false claim.
1132
+ #
1133
+ # Measured separately rather than subtracted: the two runs
1134
+ # count different dependency graphs, so arithmetic on the
1135
+ # totals would not be sound.
1136
+ local _prod_hc=""
1137
+ _prod_hc="$(cd "$tree" && npm audit --omit=dev --json 2>/dev/null | python3 -c '
1138
+ import sys, json
1139
+ try:
1140
+ v = json.load(sys.stdin).get("metadata", {}).get("vulnerabilities", {})
1141
+ except Exception:
1142
+ sys.exit(0)
1143
+ print(v.get("critical", 0) + v.get("high", 0))
1144
+ ' 2>/dev/null || echo "")"
1145
+ local _scope_note=""
1146
+ if [ "$_prod_hc" = "0" ]; then
1147
+ _scope_note=" All are in devDependencies; \`npm audit --omit=dev\` reports 0 high/critical, so nothing in the shipped dependency tree is affected."
1148
+ elif [ -n "$_prod_hc" ]; then
1149
+ _scope_note=" $_prod_hc of these are in the SHIPPED (non-dev) dependency tree."
1150
+ fi
1063
1151
  if [ "$_c" -gt 0 ] || [ "$_h" -gt 0 ]; then
1064
- _verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs" "true"
1152
+ _verify_add_gate "dependency_audit" "fail" "npm-audit" "$_c critical, $_h high CVEs (shipped high/critical: ${_prod_hc:-unmeasured})" "true"
1065
1153
  _verify_add_finding "High" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
1066
- "npm audit found $_c critical and $_h high severity vulnerabilities."
1154
+ "npm audit found $_c critical and $_h high severity vulnerabilities.${_scope_note}"
1067
1155
  elif [ "$_m" -gt 0 ]; then
1068
1156
  _verify_add_gate "dependency_audit" "fail" "npm-audit" "$_m moderate CVEs" "true"
1069
1157
  _verify_add_finding "Medium" "dependencies" "deterministic:npm-audit" "package-lock.json" "null" \
package/completions/_loki CHANGED
@@ -172,8 +172,6 @@ function _loki_commands {
172
172
  'share:Share session report as GitHub Gist'
173
173
  'proof:Inspect/share proof-of-run artifacts'
174
174
  'outcomes:What happened to the work AFTER the receipt (reverted/reworked/survived)'
175
- 'verdict:The five measured trust signals in one readable block'
176
- 'readiness:Can an agent verify its own work in this repo? (measured, not LLM-scored)'
177
175
  'preview:Preview the locally-running app'
178
176
  'deploy:Deploy the built product (CI/CD-aware)'
179
177
  'context:Context window management'
@@ -5,7 +5,7 @@ _loki_completion() {
5
5
  _init_completion || return
6
6
 
7
7
  # Main subcommands (must match autonomy/loki main case statement)
8
- local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes verdict readiness explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
8
+ local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt outcomes explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
9
9
  local main_commands="start quick monitor demo tour welcome init stop pause resume steer status next ship dashboard web serve api sandbox notify import github issue intent config provider reset memory compound checkpoint council dogfood projects enterprise secrets cockpit secure own handoff doctor watchdog audit metrics syslog onboard share proof receipt explain plan report cost estimate kpis stats test ci watch telemetry agent context ctx code run export review optimize heal modernize migrate cluster worktree wt trigger failover remote deploy docker mcp magic assets analyze compliance crash open otel preview quickstart rc rollback self-update sentrux setup-skill spec state template trust trust-metrics ultracode update verify voice why wiki bench cleanup logs grill docs cp version completions help"
10
10
 
11
11
  # 1. If we are on the first argument (subcommand)
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "9.16.0"
10
+ __version__ = "9.17.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try: