feat(agents): wire project-brain into Pi via before_agent_start extension - #193
Merged
Conversation
…sion Adds pi/extensions/project-brain-autoload.ts: a before_agent_start extension (session_start can't inject a message) that injects the active project-brain initiative's core.md + STATUS.md once per session, gated by a module-scope flag. Reuses ai-agents/skills/project-brain/scripts/session-start.ps1 rather than reimplementing the resolve-and-read procedure - the same script Claude Code's and Codex's own SessionStart hooks run - by feeding it a SessionStart-shaped JSON payload on stdin and forwarding hookSpecificOutput.additionalContext from its stdout. Found and fixed a real bug while verifying against the actual installed Pi: PowerShell 7 falls back to the legacy OEM codepage for stdout when spawned without an attached console (as Node's execFile does), silently mangling multi-byte characters (e.g. an arrow read from core.md) into invalid JSON. Fixed by running via -Command with an explicit [Console]::OutputEncoding preamble, scoped to this extension's own invocation so Claude Code's and Codex's hooks are unaffected. Closes #187 Refs: AB#187
…ss [#c1-f1][#c1-f2][#c1-f3] pi/extensions/project-brain-autoload.ts: - Reset the injected gate on Pi's session_start event (new/resume/fork/ reload) instead of relying solely on the extension factory's closure lifetime, which review couldn't confirm resets per session boundary from static source alone. - Attach a no-op error listener on child.stdin before writing: an unhandled 'error' event on that stream (spawn failure, or EPIPE from a child that exits before draining stdin) crashed the host Pi process instead of the fail-safe undefined every other path returns. Reproduced reliably via a fast-exiting fixture script with a large payload. - Read SESSION_START_SCRIPT from PI_PROJECT_BRAIN_SESSION_START_SCRIPT when set, so tests can drive the real default-exported handler end-to-end against a fixture instead of this machine's actual Pi projection. tests/pi-project-brain-autoload.Tests.ps1: - Replace source-text regex assertions over the once-per-session gate, handler registration, and message shape with real behavioral tests: import the module (node --experimental-strip-types), register a fake pi object, and drive the actual before_agent_start/session_start handlers across a simulated session boundary. - Add a regression test for the stdin EPIPE crash. Refs: AB#187
…f1][#c2-f2] pi/extensions/project-brain-autoload.ts: session-start.ps1 reads stdin via [Console]::In.ReadToEnd(), which uses Console.InputEncoding - the same no-attached-console OEM-codepage fallback that motivated the OutputEncoding preamble applies symmetrically to input. A non-ASCII cwd was silently mangled before session-start.ps1 ever saw it. Reproduced directly with node's execFile outside Pester; fixed by adding the InputEncoding preamble alongside OutputEncoding. pi/README.md: updated the stale "gated to fire once via a module-scope flag" description (predates the session_start reset added for the prior fix commit) and documented the InputEncoding preamble. Refs: AB#187
tests/pi-project-brain-autoload.Tests.ps1: the "resolveBrainContext contract" and "session-start.ps1 stdin contract" tests regex-extracted resolveBrainContext's source text and re-evaluated it, anchored on a literal blank line (\n\}\n\nexport default) between the function's closing brace and the next declaration. On CI (Windows runner, no .gitattributes forcing LF) the repo checks out with CRLF line endings, so the two bare \n's in that pattern never span the actual \r\n\r\n blank line - the regex silently matched nothing and every test in the file failed with "resolveBrainContext function not found", despite passing locally. Replaced the extraction with a real import (node --experimental-strip-types, same mechanism the "extension handler behavior" tests already use), driving before_agent_start end to end instead of re-evaluating pulled-apart source text. No regex, no CRLF fragility. Verified against both LF and CRLF checkouts of the extension file locally before restoring LF. Refs: AB#187
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wires project-brain into Pi via a
before_agent_startextension, mirroring Claude Code's and Codex's own SessionStart hooks (#186).What changed
pi/extensions/project-brain-autoload.ts(new): injects the active project-brain initiative'score.md+STATUS.mdonce per session.session_startalone can't inject a message into the conversation, so injection happens onbefore_agent_start, gated by a flag reset onsession_start(new/resume/fork/reload).ai-agents/skills/project-brain/scripts/session-start.ps1, the same script Claude Code's and Codex's hooks run, rather than reimplementing the resolve-and-read procedure in TypeScript. Feeds it a SessionStart-shaped{ cwd }JSON payload on stdin, forwardshookSpecificOutput.additionalContextfrom its stdout.pi/README.md: documents the extension.tests/pi-project-brain-autoload.Tests.ps1(new): behavioral tests driving the real handler (vianode --experimental-strip-types), not source-text regex matching.Bugs found and fixed while implementing/reviewing
pwshis spawned without an attached console (as Node'sexecFiledoes). This silently mangles multi-byte characters both ways: a non-ASCIIcwdgoing in, and content read fromcore.md/STATUS.md(e.g. "→") coming back out as invalid JSON. Fixed with explicit[Console]::InputEncoding/OutputEncodingUTF-8 preambles, scoped to this extension's own invocation only (not asession-start.ps1change, so Claude Code's and Codex's hooks are unaffected).session_start.child.stdinhad noerrorlistener; an EPIPE/ENOENT on that stream could crash the host Pi process instead of failing safe (reproduced reliably). Fixed with a no-op error listener.Review
Ran through
review-fix-loop(2 cycles, fable + Codex sol): all above-floor findings fixed and verified, full test suite green, lint clean.Closes #187