Commits
Register a handoff command that opens Pi's model picker and stores the
selected extraction model in the user's settings. Rename the extraction
model flag, environment variable, and settings key so they consistently
describe the handoff extraction model.
Bump @amolith/pi-handoff to 0.1.0-beta.2 for publishing with the beta
tag.
Add package-level READMEs and refresh the root docs for the handoff and
personas packages.
Expose handoff extraction model configuration through Pi's extension
flag API, the existing PI_HANDOFF_MODEL env var, and the handoffModel
user setting.
Store the active persona in settings.json's persona key instead of the
old marker file.
Bump handoff and personas to 0.1.0-beta.1, keeping package-lock.json in
sync.
Replace Bun-based install, check, and version-bump workflows with npm
equivalents.
Keep release packing and publishing on npm too, and use npm's workspace
lockfile instead of maintaining bun.lock.
Add repository metadata to the publishable package manifests so npm can
link each package back to its source. Include package directories
because the packages live in a monorepo.
Set the remaining packages to the beta version and align their package
metadata with Pi package docs. Keep Pi core imports as peer
dependencies, leave typebox available for local typechecks from the
workspace root, and update handoff's npm description for the beta
package page.
Delete the local answer extension because it will be replaced by another
implementation. Drop its README entry, release selector, workspace
lockfile entries, and now-unused Apache license text so the repo only
tracks the remaining packages.
Delete the local questionnaire extension because it will be replaced by
another implementation. Drop its README entry, release selector, and
workspace lockfile entries so package automation only targets remaining
packages.
Add mise tasks for package-specific version bumps, package dry-runs, and
npm publishing. Keep package selector validation shared so release
commands reject unknown or duplicate aliases before touching package
versions or publishing.
Update the handoff tool description so agents invoke it only when the
user explicitly asks for a handoff. Keep the prompt focused on the
goal-driven automatic context transfer.
Use pi-coding-agent's getAgentDir helper for extension paths instead of
reconstructing PI_CODING_AGENT_DIR and ~/.pi/agent locally. This keeps
personas and handoff aligned with Pi's own agent directory resolution.
Use the Pi 0.78 extension docs' direct typebox dependency and import
style for package tool schemas. This removes the legacy
@sinclair/typebox dependency from packages that build TypeBox schemas at
runtime.
Use optional chaining in tool call guards to satisfy the updated Biome
rule while preserving the existing failure path for missing tool calls.
Update the root development toolchain to the latest patch releases and
refresh local Pi development providers. Keep Pi as the package peer API
and raise the advertised floor to the first published Pi package
version.
Make TypeBox a runtime dependency of the extensions that build schemas
with it instead of treating it as a host-provided peer.
Pi packages moved from the @mariozechner scope to @earendil-works.
Update the extension imports and peer dependencies so the workspace
resolves against the current packages.
Cancel the countdown on any terminal input, not just "editable" keys.
Arrow keys and other navigation are escape sequences that
isEditableInput() explicitly rejected, so navigating the handoff draft
didn't stop the auto-submit timer.
Extract parseAndSetModel() into model-utils.ts to deduplicate the
model-parsing logic shared between the agent_end handler in index.ts and
the /handoff command. The shared helper also wraps pi.setModel() in
try/catch, surfacing failures as user-visible warnings instead of
unhandled promise rejections.
The tool-path handoff (agent calling the handoff tool) bypassed the
pause-to-edit countdown and sent the prompt immediately via
sendUserMessage. Replace that with setEditorText + startCountdown so
both paths give the user a 10-second window to review and edit the
handoff draft before it's submitted.
Set editor text before awaiting setModel to avoid a race where slow
model setup could clobber user edits.
Rename the package directory, npm package name, slash command, on-disk
paths, XML tags, and all internal references from "personality" /
"personalities" to "persona" / "personas".
All four packages used "*" for their pi SDK and typebox peer
dependencies, which accepts any version including ones with breaking API
changes.
Pin to >=0.65.0 for the pi packages (pi-ai, pi-coding-agent, pi-tui)
since the current code relies on APIs introduced in that release
(session_start event, AgentToolResult.details, SessionManager.open). Pin
typebox to >=0.34.0 as the Type.Object/Array/String/Optional API is
stable across the 0.34.x line.
Break the 776-line single file into four modules along natural
boundaries:
- prompt.ts: extraction system prompt, few-shot examples, tool schema,
and shared types (ExtractedQuestion, ExtractionResult)
- extract.ts: model resolution, tool call parsing, XML escaping
- QnAComponent.ts: full interactive TUI component
- index.ts: command/shortcut registration and extraction orchestration
No behavioral changes.
Break the 905-line single file into six modules along natural
boundaries:
- session-paths.ts: path utilities (getSessionsRoot,
getFallbackSessionsRoot, normalizeSessionPath, sessionPathAllowed)
- session-analysis.ts: extractCandidateFiles, extractLoadedSkills,
resolveExtractionModel
- handoff-extraction.ts: extraction prompts, tool schema,
extractHandoffContext, assembleHandoffDraft
- session-query-tool.ts: session_query tool registration
- handoff-tool.ts: handoff tool registration
- handoff-command.ts: /handoff command registration
- index.ts: wiring, event handlers, shared state, countdown logic
No behavioral changes. Each module has a narrow interface with the rest
of the system — shared state is threaded through callbacks rather than
module-level globals.
Escape <, >, and & in question text, user answers, and notes before
embedding them in pseudo-XML tags. Without this, literal angle brackets
in content could confuse model parsing of the <qna> block.
Wrap the done() callback in a finished-flag guard so only the first
completion takes effect. The loader.onAbort handler and the in-flight
extraction promise could both call done() on user cancellation,
producing a double-completion race.
The signal guard `if (!signal || signal.aborted)` treated a missing
signal as cancelled, preventing session_query from running when the SDK
passes no AbortSignal. Change to `if (signal?.aborted)` so only an
actually-aborted signal triggers cancellation.
sessionPathAllowed() returned true when sessionsRoot was undefined,
allowing any absolute path to pass validation. Flip the default to false
so queries fail closed when the sessions root cannot be determined.
When a question has no options and allowOther is false, the opts array
is empty but optionIndex is 0. Accessing opts[0].isOther crashes with a
TypeError on undefined. Add an early return when the selected option
does not exist.
Deduplicate question IDs at normalization time by appending _2, _3, etc.
to collisions. Duplicate IDs caused answer map overwrites where only the
last answer for a given ID survived.
Wrap all filesystem calls (readdirSync, readFileSync, writeFileSync,
unlinkSync) in try/catch with graceful fallbacks — return empty/null
instead of crashing the extension.
Validate the persisted personality name against listPersonalities()
before loading it. A tampered persistence file containing a path like
../../etc/something would previously be passed straight to readFileSync;
now it is rejected if not in the available list.
The old code injected a system-reminder custom message every turn after
the first, creating a persisted entry in the session file each time.
Long conversations accumulated dozens of personality blobs, wasting
tokens and cluttering session history.
Lock the personality into the system prompt at session start via the
ephemeral systemPrompt return from before_agent_start (never persisted,
same content each turn so provider cache stays valid). Mid-session
switches queue a single one-shot message instead of touching the system
prompt — once a conversation exists, its system prompt is immutable.
The $schema URL was pinned to 2.0.0 while the installed biome is 2.4.10,
producing an info diagnostic on every lint run.
Fix ~15 type errors and 3 noExplicitAny lint warnings caused by SDK API
drift in the handoff extension.
- Replace session_switch/session_fork events with session_start (the
old events no longer exist in the SDK); use event.reason to distinguish
switch, resume, and fork
- Cast sessionManager to SessionManager instead of any for newSession()
- Type the context event handler properly instead of casting to any
- Change label from dynamic function to static string
- Add details property to all AgentToolResult returns
- Guard signal-possibly-undefined with optional chaining
- Accept string | undefined for apiKey in extractHandoffContext
- Handle getSessionFile() returning undefined in command handler
- Fix race condition: call newSession() before setting
handoffTimestamp so session_start clears stale values first
- Remove useless regex escape on closing paren
- Replace while/exec loop with for/matchAll to avoid assignment in
expression and implicit-any let
- Change trailing boundary group from consuming to lookahead so
adjacent files separated by a single space are no longer skipped
- Align EditorTheme.selectList with current SelectListTheme API
(selectedPrefix, selectedText, description, scrollInfo, noMatch)
- Replace non-null assertions on ctx.model and lastAssistantText with
captured locals after the early-return guards so closures see narrowed
types
- Add explicit fallback parameter to resolveExtractionModel instead
of asserting ctx.model inside the function
- Convert remaining string concatenations to template literals
- Remove unused private allQuestionsAnswered method
Replace string concatenation with a template literal to satisfy the
biome useTemplate lint rule.
The session-query.ts link already credits the source; no need to list it
again as a change.
Code inside markdown headings can't be linked, so replace the linked
header style with plain code headings and shields.io NPM version badges
underneath.
Non-discoverable repo conventions: commands, REUSE licensing
requirements, and VCS/commit style expectations.
Switchable agent personalities stored as markdown files, injected into
the system prompt. Includes a /personality command for interactive
switching and persistence across sessions.
Licensed under the Unlicense.
Multi-question interactive prompts with tab-based navigation, from Mario
Zechner's pi-mono examples. Changes from upstream:
- Added a free-text notes editor (press 'n') available on any tab
- Custom answers and notes are sent as a steering user message so
the model sees them as user content, not just tool results
- Added word wrapping for long question prompts via wrapTextWithAnsi
- Updated tool description to mention the notes feature
Licensed MIT per the upstream.
Interactive Q&A extraction from assistant responses with a custom TUI
for navigating and answering questions. Based on Armin Ronacher's answer
extension from agent-stuff, with several changes:
- Replaced responseFormat-based extraction with a structured
tool call (extract_questions), which models handle more reliably
- Rewrote the system prompt with detailed few-shot examples
including deduplication and standalone-question rules
- Changed model selection to prefer nemotron-3-super-120b-a12b
instead of codex-mini/haiku
- Added a notes editor (Alt+N) for attaching free-text context
alongside answers
- Added fallback extraction with current model when the preferred
model fails
Licensed Apache-2.0 per the upstream.
Context-preserving session handoff with LLM-based extraction of relevant
files, skills, and open items. Derived from Petr Baudis' pi-amplike
handoff and session-query extensions, substantially reworked with
structured tool-call extraction, auto-submit countdown, and an
integrated session query tool.
Licensed MIT per the upstream.
Bun workspaces monorepo for Pi extensions published under the @amolith
npm scope. Includes:
- Biome for linting and formatting (tabs, double quotes, 120-col)
- TypeScript for type checking (noEmit only, Pi loads .ts via jiti)
- mise tasks as thin wrappers around bun run scripts
- REUSE 3.3 compliance with per-file SPDX headers
- CC0-1.0 for ancillary files, code licenses per-package
Register a handoff command that opens Pi's model picker and stores the
selected extraction model in the user's settings. Rename the extraction
model flag, environment variable, and settings key so they consistently
describe the handoff extraction model.
Bump @amolith/pi-handoff to 0.1.0-beta.2 for publishing with the beta
tag.
Add package-level READMEs and refresh the root docs for the handoff and
personas packages.
Expose handoff extraction model configuration through Pi's extension
flag API, the existing PI_HANDOFF_MODEL env var, and the handoffModel
user setting.
Store the active persona in settings.json's persona key instead of the
old marker file.
Bump handoff and personas to 0.1.0-beta.1, keeping package-lock.json in
sync.
Update the root development toolchain to the latest patch releases and
refresh local Pi development providers. Keep Pi as the package peer API
and raise the advertised floor to the first published Pi package
version.
Make TypeBox a runtime dependency of the extensions that build schemas
with it instead of treating it as a host-provided peer.
Cancel the countdown on any terminal input, not just "editable" keys.
Arrow keys and other navigation are escape sequences that
isEditableInput() explicitly rejected, so navigating the handoff draft
didn't stop the auto-submit timer.
Extract parseAndSetModel() into model-utils.ts to deduplicate the
model-parsing logic shared between the agent_end handler in index.ts and
the /handoff command. The shared helper also wraps pi.setModel() in
try/catch, surfacing failures as user-visible warnings instead of
unhandled promise rejections.
The tool-path handoff (agent calling the handoff tool) bypassed the
pause-to-edit countdown and sent the prompt immediately via
sendUserMessage. Replace that with setEditorText + startCountdown so
both paths give the user a 10-second window to review and edit the
handoff draft before it's submitted.
Set editor text before awaiting setModel to avoid a race where slow
model setup could clobber user edits.
All four packages used "*" for their pi SDK and typebox peer
dependencies, which accepts any version including ones with breaking API
changes.
Pin to >=0.65.0 for the pi packages (pi-ai, pi-coding-agent, pi-tui)
since the current code relies on APIs introduced in that release
(session_start event, AgentToolResult.details, SessionManager.open). Pin
typebox to >=0.34.0 as the Type.Object/Array/String/Optional API is
stable across the 0.34.x line.
Break the 776-line single file into four modules along natural
boundaries:
- prompt.ts: extraction system prompt, few-shot examples, tool schema,
and shared types (ExtractedQuestion, ExtractionResult)
- extract.ts: model resolution, tool call parsing, XML escaping
- QnAComponent.ts: full interactive TUI component
- index.ts: command/shortcut registration and extraction orchestration
No behavioral changes.
Break the 905-line single file into six modules along natural
boundaries:
- session-paths.ts: path utilities (getSessionsRoot,
getFallbackSessionsRoot, normalizeSessionPath, sessionPathAllowed)
- session-analysis.ts: extractCandidateFiles, extractLoadedSkills,
resolveExtractionModel
- handoff-extraction.ts: extraction prompts, tool schema,
extractHandoffContext, assembleHandoffDraft
- session-query-tool.ts: session_query tool registration
- handoff-tool.ts: handoff tool registration
- handoff-command.ts: /handoff command registration
- index.ts: wiring, event handlers, shared state, countdown logic
No behavioral changes. Each module has a narrow interface with the rest
of the system — shared state is threaded through callbacks rather than
module-level globals.
Escape <, >, and & in question text, user answers, and notes before
embedding them in pseudo-XML tags. Without this, literal angle brackets
in content could confuse model parsing of the <qna> block.
Wrap the done() callback in a finished-flag guard so only the first
completion takes effect. The loader.onAbort handler and the in-flight
extraction promise could both call done() on user cancellation,
producing a double-completion race.
The signal guard `if (!signal || signal.aborted)` treated a missing
signal as cancelled, preventing session_query from running when the SDK
passes no AbortSignal. Change to `if (signal?.aborted)` so only an
actually-aborted signal triggers cancellation.
sessionPathAllowed() returned true when sessionsRoot was undefined,
allowing any absolute path to pass validation. Flip the default to false
so queries fail closed when the sessions root cannot be determined.
When a question has no options and allowOther is false, the opts array
is empty but optionIndex is 0. Accessing opts[0].isOther crashes with a
TypeError on undefined. Add an early return when the selected option
does not exist.
Deduplicate question IDs at normalization time by appending _2, _3, etc.
to collisions. Duplicate IDs caused answer map overwrites where only the
last answer for a given ID survived.
Wrap all filesystem calls (readdirSync, readFileSync, writeFileSync,
unlinkSync) in try/catch with graceful fallbacks — return empty/null
instead of crashing the extension.
Validate the persisted personality name against listPersonalities()
before loading it. A tampered persistence file containing a path like
../../etc/something would previously be passed straight to readFileSync;
now it is rejected if not in the available list.
The old code injected a system-reminder custom message every turn after
the first, creating a persisted entry in the session file each time.
Long conversations accumulated dozens of personality blobs, wasting
tokens and cluttering session history.
Lock the personality into the system prompt at session start via the
ephemeral systemPrompt return from before_agent_start (never persisted,
same content each turn so provider cache stays valid). Mid-session
switches queue a single one-shot message instead of touching the system
prompt — once a conversation exists, its system prompt is immutable.
Fix ~15 type errors and 3 noExplicitAny lint warnings caused by SDK API
drift in the handoff extension.
- Replace session_switch/session_fork events with session_start (the
old events no longer exist in the SDK); use event.reason to distinguish
switch, resume, and fork
- Cast sessionManager to SessionManager instead of any for newSession()
- Type the context event handler properly instead of casting to any
- Change label from dynamic function to static string
- Add details property to all AgentToolResult returns
- Guard signal-possibly-undefined with optional chaining
- Accept string | undefined for apiKey in extractHandoffContext
- Handle getSessionFile() returning undefined in command handler
- Fix race condition: call newSession() before setting
handoffTimestamp so session_start clears stale values first
- Align EditorTheme.selectList with current SelectListTheme API
(selectedPrefix, selectedText, description, scrollInfo, noMatch)
- Replace non-null assertions on ctx.model and lastAssistantText with
captured locals after the early-return guards so closures see narrowed
types
- Add explicit fallback parameter to resolveExtractionModel instead
of asserting ctx.model inside the function
- Convert remaining string concatenations to template literals
- Remove unused private allQuestionsAnswered method
Multi-question interactive prompts with tab-based navigation, from Mario
Zechner's pi-mono examples. Changes from upstream:
- Added a free-text notes editor (press 'n') available on any tab
- Custom answers and notes are sent as a steering user message so
the model sees them as user content, not just tool results
- Added word wrapping for long question prompts via wrapTextWithAnsi
- Updated tool description to mention the notes feature
Licensed MIT per the upstream.
Interactive Q&A extraction from assistant responses with a custom TUI
for navigating and answering questions. Based on Armin Ronacher's answer
extension from agent-stuff, with several changes:
- Replaced responseFormat-based extraction with a structured
tool call (extract_questions), which models handle more reliably
- Rewrote the system prompt with detailed few-shot examples
including deduplication and standalone-question rules
- Changed model selection to prefer nemotron-3-super-120b-a12b
instead of codex-mini/haiku
- Added a notes editor (Alt+N) for attaching free-text context
alongside answers
- Added fallback extraction with current model when the preferred
model fails
Licensed Apache-2.0 per the upstream.
Context-preserving session handoff with LLM-based extraction of relevant
files, skills, and open items. Derived from Petr Baudis' pi-amplike
handoff and session-query extensions, substantially reworked with
structured tool-call extraction, auto-submit countdown, and an
integrated session query tool.
Licensed MIT per the upstream.
Bun workspaces monorepo for Pi extensions published under the @amolith
npm scope. Includes:
- Biome for linting and formatting (tabs, double quotes, 120-col)
- TypeScript for type checking (noEmit only, Pi loads .ts via jiti)
- mise tasks as thin wrappers around bun run scripts
- REUSE 3.3 compliance with per-file SPDX headers
- CC0-1.0 for ancillary files, code licenses per-package