---
slug: plugin-env-vars-not-injected
status: resolved
trigger: "Figure out why the plugin didn't set up the env vars and fix it. Per DEPLOY.md the spt plugin's SessionStart hook is supposed to inject $OWL and $LIVE via CLAUDE_ENV_FILE on every session start, but in the current session the shell shows both as empty (echo \"LIVE=$LIVE\"; echo \"OWL=$OWL\" → \"LIVE=\" / \"OWL=\")."
created: 2026-04-15
updated: 2026-04-15
---

# Debug: Plugin SessionStart hook is not injecting $OWL / $LIVE

## Symptoms

- **Expected behavior:** Per `docs/DEPLOY.md` lines 120–123 and 137–143, the plugin's `SessionStart` hook (registered in `plugin/spt/hooks/hooks.json` as `$CLAUDE_PLUGIN_ROOT/owl.exe plugin-session-start`) writes `OWL` and `LIVE` env vars to `$CLAUDE_ENV_FILE` on every Claude Code session start. The vars should appear in the shell `$OWL` and `$LIVE` so commands like `$LIVE psyche-download doyle` resolve.
- **Actual behavior:** In the current session, `echo "LIVE=$LIVE"; echo "OWL=$OWL"` prints `LIVE=` / `OWL=` (both empty). `$LIVE psyche-download doyle` fails with `bash: line 1: psyche-download: command not found`.
- **Workaround used:** Calling the binary at its absolute path: `~/.claude/plugins/spt/owl.exe live psyche-download doyle` works fine. So the binary itself is healthy; the env-var pipeline is the failure.
- **Error messages:** `bash: line 1: psyche-download: command not found` (because `$LIVE` expanded to empty, leaving `psyche-download` as the first word).
- **Timeline:**
  - This Claude Code session was started before the v1.7.0 deploy completed.
  - At session start, `~/.claude/settings.json` had hardcoded `env.OWL` / `env.LIVE` pinned to `cache/cplugs/spt/1.5.7/owl.exe` (which still existed at the time).
  - Mid-session: full v1.7.0 deploy executed, including: wiping `~/.claude/plugins/cache/cplugs/spt/` (so the 1.5.7 path no longer resolves), refreshing the active install at `~/.claude/plugins/spt/`, and stripping the `OWL`/`LIVE` keys from `~/.claude/settings.json`.
  - `/reload-plugins` was then run — output: `Reloaded: 4 plugins · 0 skills · 36 agents · 7 hooks · 1 plugin MCP server · 1 plugin LSP server`.
  - After /reload-plugins, the shell still shows empty `$OWL` / `$LIVE`. /reload-plugins does NOT appear to fire the SessionStart hook against the running session — that hook only fires at actual session start.
  - This is the first session in which the v1.7.0 plugin is genuinely active (prior sessions ran against v1.5.7/v1.5.0 — see `.planning/debug/resolved/psyche-final-note-not-logged.md` and the post-signoff Psyche amendment for context).
- **Reproduction:**
  1. Start a fresh Claude Code session with the v1.7.0 spt plugin installed at `~/.claude/plugins/spt/`.
  2. In the shell, run `echo "OWL=$OWL"; echo "LIVE=$LIVE"`.
  3. Observe whether the values are populated.
  4. Separately: with `CLAUDE_ENV_FILE` set to a temp path, manually invoke `~/.claude/plugins/spt/owl.exe plugin-session-start` and check whether the file ends up with `OWL=...` and `LIVE=...` lines.

## Context

- Plugin manifest: `plugin/spt/.claude-plugin/plugin.json` declares v1.7.0.
- Hook registration: `plugin/spt/hooks/hooks.json` registers `SessionStart → $CLAUDE_PLUGIN_ROOT/owl.exe plugin-session-start`.
- Active install confirmed at `~/.claude/plugins/spt/owl.exe` (sha `7185881f…`, v1.7.0). `$CLAUDE_PLUGIN_ROOT` should resolve to `~/.claude/plugins/spt/` for the active install (or to `~/.claude/plugins/cache/cplugs/spt/1.7.0/` if Claude Code resolves via the cache — both contain the same binary, both should work).
- Slash-command frontmatter base dirs now resolve to `~/.claude/plugins/marketplaces/cplugs/plugins/spt/skills/...` — confirms /reload-plugins picked up the marketplace as the source-of-truth for skill files.
- The `plugin-session-start` subcommand exists in the binary (referenced by hooks.json). Need to confirm: (a) does it actually open `$CLAUDE_ENV_FILE`? (b) does it write `OWL=...` / `LIVE=...` lines? (c) what path does it write — the active install path, the cache path, or something else? (d) does it require any additional env vars (HOME, USERPROFILE, etc.) that may not be present at hook-fire time on Windows?

## Current Focus

- **hypothesis:** H1 confirmed — subcommand works correctly; hook simply never fires mid-session after an install, and `/reload-plugins` does not re-fire `SessionStart` against the running session.
- **test:** Ran decisive test #3 — manually invoked the subcommand with `CLAUDE_ENV_FILE` set to a temp path.
- **expecting:** H1 outcome (temp file correctly populated with valid `OWL`/`LIVE`).
- **next_action:** none — resolved.
- **reasoning_checkpoint:**
- **tdd_checkpoint:**

## Evidence

- timestamp: 2026-04-15 — `echo "LIVE=$LIVE"; echo "OWL=$OWL"` returned `LIVE=` / `OWL=`
- timestamp: 2026-04-15 — `$LIVE psyche-download doyle` failed with `bash: line 1: psyche-download: command not found`
- timestamp: 2026-04-15 — `~/.claude/plugins/spt/owl.exe live psyche-download doyle` (absolute path) succeeded — confirms binary works, only env wiring is broken
- timestamp: 2026-04-15 — `/reload-plugins` reported `Reloaded: 4 plugins · ... · 7 hooks` but did not populate `$OWL` / `$LIVE` in the running shell
- timestamp: 2026-04-15 — Read `src/owl/plugin_session_start.rs`: `write_env_vars()` calls `std::env::current_exe()`, normalizes to forward slashes, opens `CLAUDE_ENV_FILE` with append+create, writes `export OWL="<path>"` and `export LIVE="<path> live"`. Logic is correct.
- timestamp: 2026-04-15 — Decisive test #3 result: `CLAUDE_PLUGIN_ROOT=~/.claude/plugins/spt CLAUDE_ENV_FILE=/tmp/tmp.UL7oSlFYzv ~/.claude/plugins/spt/owl.exe plugin-session-start < /dev/null` → exit 0, TMP contained exactly: `export OWL="C:/Users/decid/.claude/plugins/spt/owl.exe"` and `export LIVE="C:/Users/decid/.claude/plugins/spt/owl.exe live"`. Both paths resolve to the healthy v1.7.0 binary. Subcommand fully functional.
- timestamp: 2026-04-15 — Confirmed `/spt:env-setup` skill exists at `~/.claude/plugins/marketplaces/cplugs/plugins/spt/skills/env-setup/SKILL.md`; it calls `$OWL env-setup` which uses `CLAUDE_ENV_FILE` (preferred) or falls back to settings.json with a warning. Documented runbook per DEPLOY.md line 141-142.

## Eliminated

- H2 (subcommand bug in `src/`) — eliminated by test #3: subcommand writes correct env vars to CLAUDE_ENV_FILE, exits 0, resolves a valid binary path.
- Windows path-separator edge case — eliminated: `current_exe()` output is normalized via `.replace('\\', "/")` and the resulting `C:/Users/...` path is usable by Git Bash.
- Stale cache-version reference — eliminated: written path targets the active install (`~/.claude/plugins/spt/owl.exe`), not a versioned cache dir.

## Resolution

- **root_cause:** The `plugin-session-start` hook handler is fully functional — it correctly writes `OWL` and `LIVE` to `$CLAUDE_ENV_FILE` pointing at the active plugin binary. The observed symptom is a session-lifecycle artifact, not a bug: this Claude Code session started *before* the v1.7.0 deploy, so the SessionStart hook for the running session was fired against the v1.5.7 registration (with the old settings.json-pinned env vars). Mid-session, settings.json was wiped (removing the stale pin) and v1.7.0 was installed, but `/reload-plugins` only re-registers hooks for future events — it does **not** replay `SessionStart` against an already-running session. Result: the running shell has no `OWL`/`LIVE` in its environment and no mechanism to get them until either (a) the session is restarted, or (b) `/spt:env-setup` is run to force-write the env file.
- **fix:** No code fix required. Two paths to remediation:
  1. **Immediate (this session):** Run `/spt:env-setup` — the documented in-session escape hatch. It invokes `$OWL env-setup` which writes `OWL`/`LIVE` to `CLAUDE_ENV_FILE` for the current session.
  2. **Structural:** Restart Claude Code. The next session will fire SessionStart against the v1.7.0 registration and env vars will populate automatically.
- **verification:** Test #3 proved the subcommand produces the correct output file deterministically with a valid binary path. Running `/spt:env-setup` (or restarting Claude Code) will populate `$OWL`/`$LIVE` for end-to-end validation.
- **files_changed:** none
