loki-mode 7.104.0 → 7.104.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/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 v7.104.0
6
+ # Loki Mode v7.104.2
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -408,4 +408,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
408
408
 
409
409
  ---
410
410
 
411
- **v7.104.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
411
+ **v7.104.2 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 7.104.0
1
+ 7.104.2
package/autonomy/loki CHANGED
@@ -9957,12 +9957,27 @@ cmd_doctor() {
9957
9957
  echo -e " ${GREEN}PASS${NC} ANTHROPIC_API_KEY is set"
9958
9958
  pass_count=$((pass_count + 1))
9959
9959
  elif command -v claude &>/dev/null; then
9960
- # Zero-network check: warn if the Claude OAuth login has expired. An
9961
- # expired token would otherwise pass and then 401 mid-build (the build
9962
- # stalls at BOOTSTRAP). Mirror run.sh's fail-fast preflight here so a
9963
- # user catches it with `loki doctor` BEFORE starting a build.
9960
+ # Zero-network login check. v7.104.2: prefer the CLI's own local
9961
+ # auth-status (JSON with "loggedIn"), which is authoritative for BOTH the
9962
+ # file-based and the native/Keychain login. This fixes doctor missing a
9963
+ # genuine Keychain login (native install writes NO ~/.claude/.credentials
9964
+ # .json). Fall back to the expiry stamp in the file for older CLIs.
9965
+ _claude_login="$(claude auth status 2>/dev/null | python3 -c "
9966
+ import json, sys
9967
+ try:
9968
+ v = json.load(sys.stdin).get('loggedIn')
9969
+ # Only the explicit booleans are trusted; a missing/renamed field is
9970
+ # inconclusive and falls through to the file-expiry check (never a spurious
9971
+ # 'not logged in' warning). Matches the run.sh helper + bun doctor.
9972
+ if v is True:
9973
+ print('yes')
9974
+ elif v is False:
9975
+ print('no')
9976
+ except Exception:
9977
+ pass
9978
+ " 2>/dev/null)"
9964
9979
  _claude_creds="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.credentials.json"
9965
- if [ -s "$_claude_creds" ] && [ "$(python3 -c "
9980
+ _claude_expired="$( [ -s "$_claude_creds" ] && python3 -c "
9966
9981
  import json, sys, time
9967
9982
  try:
9968
9983
  d = json.load(open(sys.argv[1]))
@@ -9970,7 +9985,14 @@ try:
9970
9985
  print('expired' if isinstance(exp,(int,float)) and exp>0 and (exp/1000.0)<=(time.time()+60) else 'ok')
9971
9986
  except Exception:
9972
9987
  print('ok')
9973
- " "$_claude_creds" 2>/dev/null || echo ok)" = "expired" ]; then
9988
+ " "$_claude_creds" 2>/dev/null || echo ok )"
9989
+ if [ "$_claude_login" = "yes" ]; then
9990
+ echo -e " ${GREEN}PASS${NC} Claude CLI is logged in (subscription/OAuth login)"
9991
+ pass_count=$((pass_count + 1))
9992
+ elif [ "$_claude_login" = "no" ]; then
9993
+ echo -e " ${YELLOW}WARN${NC} Claude CLI is NOT logged in -- run 'claude login' before a build (it would otherwise stall)"
9994
+ warn_count=$((warn_count + 1))
9995
+ elif [ "$_claude_expired" = "expired" ]; then
9974
9996
  echo -e " ${YELLOW}WARN${NC} Claude login has EXPIRED -- run 'claude login' before a build (it would otherwise stall)"
9975
9997
  warn_count=$((warn_count + 1))
9976
9998
  else
package/autonomy/run.sh CHANGED
@@ -2023,6 +2023,118 @@ _loki_check_git_present() {
2023
2023
  return 0
2024
2024
  }
2025
2025
 
2026
+ # _loki_claude_login_state: report the Claude Code login state WITHOUT a network
2027
+ # call, printing exactly one of: loggedin | expired | loggedout | unknown.
2028
+ #
2029
+ # v7.104.2: the previous preflight assumed OAuth credentials always live in
2030
+ # ~/.claude/.credentials.json. That is false for the native install (Claude Code
2031
+ # 2.x on macOS), which stores the login in the macOS Keychain under the service
2032
+ # "Claude Code-credentials" and creates NO .credentials.json. A genuinely logged
2033
+ # in subscription user was therefore falsely told "installed but not logged in"
2034
+ # and blocked from every build -- the exact opposite of the zero-friction intent.
2035
+ #
2036
+ # Design: prefer the durable, CLI-owned signal (`claude auth status`, local +
2037
+ # fast + non-interactive, JSON with "loggedIn"), because the credential STORE
2038
+ # location has now moved twice (file -> keychain) and will move again. Fall back
2039
+ # to the file, then the keychain, for older CLIs that lack `auth status`. Crucial
2040
+ # rule (inconclusive-never-false-fails applied to auth): only ever return a hard
2041
+ # "loggedout" on POSITIVE evidence of no login; when we cannot tell, return
2042
+ # "unknown" and let the caller fail OPEN (warn, proceed, let the real call
2043
+ # decide) -- never hard-block a paying user on uncertainty.
2044
+ _loki_claude_login_state() {
2045
+ # 1) Primary: the CLI's own local auth-status (zero-network, ~0.2s). Newer
2046
+ # claude CLIs print JSON with a boolean "loggedIn"; parse defensively.
2047
+ if command -v claude >/dev/null 2>&1; then
2048
+ local _status_json
2049
+ _status_json="$(claude auth status 2>/dev/null)"
2050
+ if [[ -n "$_status_json" ]]; then
2051
+ local _parsed
2052
+ _parsed="$(printf '%s' "$_status_json" | python3 -c "
2053
+ import json, sys
2054
+ try:
2055
+ d = json.load(sys.stdin)
2056
+ v = d.get('loggedIn')
2057
+ # ONLY the two explicit booleans are trusted. Anything else -- a missing
2058
+ # field, a renamed schema, a JSON error object -- is inconclusive and MUST
2059
+ # fall through to the file/keychain checks, never a hard 'loggedout' (that
2060
+ # would block a genuinely logged-in user with a valid creds file, violating
2061
+ # fail-open). Mirrors the bun doctor's === true / === false / else shape.
2062
+ if v is True:
2063
+ print('loggedin')
2064
+ elif v is False:
2065
+ print('loggedout')
2066
+ # else: print nothing -> fall through
2067
+ except Exception:
2068
+ pass # not JSON (older CLI / different output) -> stay silent, fall through
2069
+ " 2>/dev/null)"
2070
+ if [[ "$_parsed" == "loggedin" ]]; then
2071
+ echo "loggedin"; return 0
2072
+ elif [[ "$_parsed" == "loggedout" ]]; then
2073
+ echo "loggedout"; return 0
2074
+ fi
2075
+ # auth status ran but was inconclusive (unparseable, or valid JSON
2076
+ # without an explicit loggedIn boolean) -> do NOT trust it as a
2077
+ # negative; fall through to the credential-store checks below.
2078
+ fi
2079
+ fi
2080
+
2081
+ # 2) Fallback: a VALID (non-expired) OAuth credentials file (older CLIs write
2082
+ # it here). Compute the expiry once; a valid file is a positive login.
2083
+ # IMPORTANT: we do NOT conclude "expired" here yet -- a stale expired file
2084
+ # can sit next to a live macOS-Keychain login (the native install writes
2085
+ # the file once, then rotates only the Keychain). Concluding "expired" off
2086
+ # the file before consulting the Keychain would wrongly block a genuinely
2087
+ # logged-in user -- the exact class of bug this whole change fixes. So the
2088
+ # Keychain (step 3, a POSITIVE signal) is checked BEFORE we ever return
2089
+ # "expired" (step 4). Unknown schema -> treat as valid (fail open).
2090
+ local _creds="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.credentials.json"
2091
+ local _file_state="" # ""=absent, "valid"=present+fresh, "expired"=present+stale
2092
+ if [[ -s "$_creds" ]]; then
2093
+ _file_state="$(python3 -c "
2094
+ import json, sys, time
2095
+ try:
2096
+ d = json.load(open(sys.argv[1]))
2097
+ exp = d.get('claudeAiOauth', {}).get('expiresAt')
2098
+ if isinstance(exp, (int, float)) and exp > 0 and (exp / 1000.0) <= (time.time() + 60):
2099
+ print('expired')
2100
+ else:
2101
+ print('valid')
2102
+ except Exception:
2103
+ print('valid') # unreadable/unknown -> fail open (treat as valid)
2104
+ " "$_creds" 2>/dev/null || echo valid)"
2105
+ fi
2106
+ if [[ "$_file_state" == "valid" ]]; then
2107
+ echo "loggedin"; return 0
2108
+ fi
2109
+
2110
+ # 3) macOS Keychain (native install stores the login here, often with NO file
2111
+ # at all, or alongside a now-stale file). A present Keychain entry is a
2112
+ # POSITIVE login signal and OVERRIDES an expired file. Existence check
2113
+ # ONLY -- never `-w` (reading the secret can pop a GUI keychain prompt that
2114
+ # would hang a headless/CI build). Guarded to Darwin.
2115
+ if [[ "$(uname 2>/dev/null)" == "Darwin" ]] && command -v security >/dev/null 2>&1; then
2116
+ if security find-generic-password -s "Claude Code-credentials" >/dev/null 2>&1; then
2117
+ echo "loggedin"; return 0
2118
+ fi
2119
+ fi
2120
+
2121
+ # 4) Only now, with no valid file and no Keychain login, is an expired file a
2122
+ # real "expired" state (genuine expiry that would 401 mid-build).
2123
+ if [[ "$_file_state" == "expired" ]]; then
2124
+ echo "expired"; return 0
2125
+ fi
2126
+
2127
+ # 5) No positive login evidence at all. On macOS (where the file AND the
2128
+ # Keychain are the two real stores, both absent) with the CLI present, that
2129
+ # is confident "logged out". Anywhere else we cannot prove absence of a
2130
+ # login (no Keychain to consult), so stay "unknown" and let the caller fail
2131
+ # open.
2132
+ if [[ "$(uname 2>/dev/null)" == "Darwin" ]] && command -v claude >/dev/null 2>&1; then
2133
+ echo "loggedout"; return 0
2134
+ fi
2135
+ echo "unknown"; return 0
2136
+ }
2137
+
2026
2138
  # _loki_check_workspace_writable: the build writes state, proofs, and source into
2027
2139
  # the working directory and its .loki/ subtree on every iteration. A read-only
2028
2140
  # volume, a permission-denied directory, or a full disk would otherwise fail
@@ -2108,21 +2220,33 @@ validate_api_keys() {
2108
2220
 
2109
2221
  # Zero-friction auth preflight for LOCAL runs (must run BEFORE the early
2110
2222
  # return below, which exits for non-Docker/K8s envs). When claude is the
2111
- # provider, the claude CLI is on PATH, there is no ANTHROPIC_API_KEY, and no
2112
- # OAuth credentials file exists (the user installed claude but never ran
2113
- # `claude login`), the build would otherwise enter, make a failing call, and
2114
- # 401 -- the worst first impression -- forcing the user to run `loki why`.
2115
- # Fail fast with the one-step fix instead. Opt out with LOKI_SKIP_AUTH_PREFLIGHT=1.
2116
- if [[ "$provider" == "claude" && "${LOKI_SKIP_AUTH_PREFLIGHT:-}" != "1" && -z "${ANTHROPIC_API_KEY:-}" ]] \
2117
- && command -v claude >/dev/null 2>&1; then
2118
- local _local_creds="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/.credentials.json"
2119
- if [[ ! -s "$_local_creds" ]]; then
2223
+ # provider and there is no ANTHROPIC_API_KEY, ask the login-state helper
2224
+ # (auth-status -> file -> keychain, all zero-network) whether the user is
2225
+ # actually logged in. A never-logged-in user would otherwise enter the build,
2226
+ # make a failing call, and 401 -- the worst first impression -- so we fail
2227
+ # fast with the one-step fix. But we ONLY hard-block on a confident
2228
+ # "loggedout"; an "expired" login gets its own message; "unknown" (we cannot
2229
+ # prove the login state, e.g. an older CLI on a non-macOS box with no file)
2230
+ # fails OPEN so a genuinely logged-in user is never blocked. This is the
2231
+ # v7.104.2 fix for the native/Keychain login being misread as logged out.
2232
+ # Opt out entirely with LOKI_SKIP_AUTH_PREFLIGHT=1.
2233
+ if [[ "$provider" == "claude" && "${LOKI_SKIP_AUTH_PREFLIGHT:-}" != "1" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
2234
+ local _login_state
2235
+ _login_state="$(_loki_claude_login_state)"
2236
+ if [[ "$_login_state" == "loggedout" ]]; then
2120
2237
  log_error "Claude Code is installed but not logged in -- the build would stall instead of running."
2121
2238
  log_error "Log in once, then retry:"
2122
2239
  log_error " claude login"
2123
2240
  log_error "(or set ANTHROPIC_API_KEY, or LOKI_SKIP_AUTH_PREFLIGHT=1 to bypass this check)"
2124
2241
  return 1
2242
+ elif [[ "$_login_state" == "expired" ]]; then
2243
+ log_error "Your Claude Code login has expired -- the build would stall instead of running."
2244
+ log_error "Fix it in one step, then retry:"
2245
+ log_error " claude login"
2246
+ log_error "(or set ANTHROPIC_API_KEY, or LOKI_SKIP_AUTH_PREFLIGHT=1 to bypass this check)"
2247
+ return 1
2125
2248
  fi
2249
+ # "loggedin" or "unknown" -> proceed (fail open on uncertainty).
2126
2250
  fi
2127
2251
 
2128
2252
  # CLI tools (claude, codex, cline, aider) use their own login sessions.
@@ -19023,7 +19147,7 @@ main() {
19023
19147
  _handoff_dir="${TARGET_DIR:-.}"
19024
19148
  _handoff_md="$_handoff_dir/HANDOFF.md"
19025
19149
  _handoff_tmp="$_handoff_dir/.HANDOFF.md.tmp"
19026
- if python3 "$_own_render" --loki-dir "$LOKI_DIR" --md > "$_handoff_tmp" 2>/dev/null; then
19150
+ if python3 "$_own_render" --loki-dir "${LOKI_DIR:-${TARGET_DIR:-.}/.loki}" --md > "$_handoff_tmp" 2>/dev/null; then
19027
19151
  mv -f "$_handoff_tmp" "$_handoff_md" 2>/dev/null || rm -f "$_handoff_tmp" 2>/dev/null || true
19028
19152
  else
19029
19153
  rm -f "$_handoff_tmp" 2>/dev/null || true
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.104.0"
10
+ __version__ = "7.104.2"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.104.0
5
+ **Version:** v7.104.2
6
6
 
7
7
  ---
8
8