Summary
When @earendil-works/pi-coding-agent is embedded as a library (not used through the TUI), code paths that touch the global theme throw
Theme not initialized. Call initTheme() first.
because initTheme() is only called by the TUI at startup, and the theme global is a throwing Proxy until then. Additionally, when a host does call initTheme(), theme file loading can fail because PI_PACKAGE_DIR may resolve to a shim directory that has no dist/modes/interactive/theme/.
Environment
@earendil-works/pi-coding-agent 0.80.2
- Usage: embedded as a library with extensions enabled. The extension loader (and some shared code) reads
theme, which is uninitialized in a non-TUI host.
Root cause
dist/modes/interactive/theme/theme.js:601 — const THEME_KEY = Symbol.for("@earendil-works/pi-coding-agent:theme") is a global.
dist/modes/interactive/theme/theme.js:609 — the global is a Proxy whose get trap throws "Theme not initialized. Call initTheme() first." until initTheme() runs.
dist/modes/interactive/theme/theme.js:631 — initTheme(themeName, enableWatcher) calls loadTheme(), which reads theme JSON files from ${PI_PACKAGE_DIR}/dist/modes/interactive/theme/.
- The TUI calls
initTheme() at startup. A library embedder that never touches the TUI still imports code paths that read theme (via the extension loader), and gets the throw.
PI_PACKAGE_DIR resolution (ensurePiPackageDirShim) can land on a shim directory that only has a bare package.json and no dist/modes/interactive/theme/, so even calling initTheme() from a host fails to locate the theme files.
Reproduction
import { /* anything that pulls in the extension loader */ } from "@earendil-works/pi-coding-agent";
// create a session with extensions enabled; extension loading reads `theme`
// → throws "Theme not initialized. Call initTheme() first."
Proposal
- Make
theme lazily resolve to a safe default / no-op when accessed outside the TUI (or auto-initialize on first access), so library embedders don't have to know about initTheme().
- Ensure
initTheme() resolves the real package directory (not the shim) for theme file loading, so hosts that do call it succeed regardless of PI_PACKAGE_DIR pointing at a shim.
Workaround currently used by Archon (host side, not a real fix)
Archon's Pi provider temporarily repoints PI_PACKAGE_DIR to the real package directory (resolved via import.meta.resolve('@earendil-works/pi-coding-agent')) and calls initTheme(undefined, false) before creating sessions.
Happy to open a PR if there's interest.
Summary
When
@earendil-works/pi-coding-agentis embedded as a library (not used through the TUI), code paths that touch the globalthemethrowbecause
initTheme()is only called by the TUI at startup, and thethemeglobal is a throwing Proxy until then. Additionally, when a host does callinitTheme(), theme file loading can fail becausePI_PACKAGE_DIRmay resolve to a shim directory that has nodist/modes/interactive/theme/.Environment
@earendil-works/pi-coding-agent0.80.2theme, which is uninitialized in a non-TUI host.Root cause
dist/modes/interactive/theme/theme.js:601—const THEME_KEY = Symbol.for("@earendil-works/pi-coding-agent:theme")is a global.dist/modes/interactive/theme/theme.js:609— the global is aProxywhosegettrap throws"Theme not initialized. Call initTheme() first."untilinitTheme()runs.dist/modes/interactive/theme/theme.js:631—initTheme(themeName, enableWatcher)callsloadTheme(), which reads theme JSON files from${PI_PACKAGE_DIR}/dist/modes/interactive/theme/.initTheme()at startup. A library embedder that never touches the TUI still imports code paths that readtheme(via the extension loader), and gets the throw.PI_PACKAGE_DIRresolution (ensurePiPackageDirShim) can land on a shim directory that only has a barepackage.jsonand nodist/modes/interactive/theme/, so even callinginitTheme()from a host fails to locate the theme files.Reproduction
Proposal
themelazily resolve to a safe default / no-op when accessed outside the TUI (or auto-initialize on first access), so library embedders don't have to know aboutinitTheme().initTheme()resolves the real package directory (not the shim) for theme file loading, so hosts that do call it succeed regardless ofPI_PACKAGE_DIRpointing at a shim.Workaround currently used by Archon (host side, not a real fix)
Archon's Pi provider temporarily repoints
PI_PACKAGE_DIRto the real package directory (resolved viaimport.meta.resolve('@earendil-works/pi-coding-agent')) and callsinitTheme(undefined, false)before creating sessions.Happy to open a PR if there's interest.