From 99281e5913da49c9a2ddd2faf9070dc4d7dcc476 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 30 Jan 2026 17:44:25 +0100 Subject: [PATCH] feat(coding-agent): add ctx.getSystemPrompt() to extension context Adds a method to access the effective system prompt (after any per-turn extension modifications) from the extension context. Implementation: - Add systemPrompt getter to AgentSession reading from agent.state.systemPrompt - Wire getSystemPrompt through ExtensionContextActions to ExtensionRunner - Add getSystemPrompt to interactive-mode's shortcut context - Update docs with ctx.getSystemPrompt() section - Add system-prompt-header.ts example - Add example to docs reference table Closes #1098 --- packages/coding-agent/CHANGELOG.md | 4 ++++ packages/coding-agent/docs/extensions.md | 12 ++++++++++++ .../examples/extensions/system-prompt-header.ts | 17 +++++++++++++++++ packages/coding-agent/src/core/agent-session.ts | 6 ++++++ .../coding-agent/src/core/extensions/runner.ts | 3 +++ .../coding-agent/src/core/extensions/types.ts | 3 +++ .../src/modes/interactive/interactive-mode.ts | 1 + 7 files changed, 46 insertions(+) create mode 100644 packages/coding-agent/examples/extensions/system-prompt-header.ts diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index a1a2555d..21a27d48 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added `ctx.getSystemPrompt()` to extension context for accessing the current effective system prompt ([#1098](https://github.com/badlogic/pi-mono/pull/1098) by [@kaofelix](https://github.com/kaofelix)) + ## [0.50.5] - 2026-01-30 ## [0.50.4] - 2026-01-30 diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index 0f43101c..5c450a50 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -654,6 +654,17 @@ ctx.compact({ }); ``` +### ctx.getSystemPrompt() + +Returns the current effective system prompt. This includes any modifications made by `before_agent_start` handlers for the current turn. + +```typescript +pi.on("before_agent_start", (event, ctx) => { + const prompt = ctx.getSystemPrompt(); + console.log(`System prompt length: ${prompt.length}`); +}); +``` + ## ExtensionCommandContext Command handlers receive `ExtensionCommandContext`, which extends `ExtensionContext` with session control methods. These are only available in commands because they can deadlock if called from event handlers. @@ -1688,6 +1699,7 @@ All examples in [examples/extensions/](../examples/extensions/). | `dirty-repo-guard.ts` | Warn on dirty git repo | `on("session_before_*")`, `exec` | | `input-transform.ts` | Transform user input | `on("input")` | | `model-status.ts` | React to model changes | `on("model_select")`, `setStatus` | +| `system-prompt-header.ts` | Display system prompt info | `on("agent_start")`, `getSystemPrompt` | | `claude-rules.ts` | Load rules from files | `on("session_start")`, `on("before_agent_start")` | | `file-trigger.ts` | File watcher triggers messages | `sendMessage` | | **Compaction & Sessions** ||| diff --git a/packages/coding-agent/examples/extensions/system-prompt-header.ts b/packages/coding-agent/examples/extensions/system-prompt-header.ts new file mode 100644 index 00000000..7ef77976 --- /dev/null +++ b/packages/coding-agent/examples/extensions/system-prompt-header.ts @@ -0,0 +1,17 @@ +/** + * Displays a status widget showing the system prompt length. + * + * Demonstrates ctx.getSystemPrompt() for accessing the effective system prompt. + */ +import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; + +export default function (pi: ExtensionAPI) { + pi.on("agent_start", (_event, ctx) => { + const prompt = ctx.getSystemPrompt(); + ctx.ui.setStatus("system-prompt", `System: ${prompt.length} chars`); + }); + + pi.on("session_shutdown", (_event, ctx) => { + ctx.ui.setStatus("system-prompt", undefined); + }); +} diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 3d8468af..21caa860 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -514,6 +514,11 @@ export class AgentSession { return this.agent.state.isStreaming; } + /** Current effective system prompt (includes any per-turn extension modifications) */ + get systemPrompt(): string { + return this.agent.state.systemPrompt; + } + /** Current retry attempt (0 if not retrying) */ get retryAttempt(): number { return this._retryAttempt; @@ -1756,6 +1761,7 @@ export class AgentSession { } })(); }, + getSystemPrompt: () => this.systemPrompt, }, ); } diff --git a/packages/coding-agent/src/core/extensions/runner.ts b/packages/coding-agent/src/core/extensions/runner.ts index dff981a6..7553058b 100644 --- a/packages/coding-agent/src/core/extensions/runner.ts +++ b/packages/coding-agent/src/core/extensions/runner.ts @@ -159,6 +159,7 @@ export class ExtensionRunner { private hasPendingMessagesFn: () => boolean = () => false; private getContextUsageFn: () => ContextUsage | undefined = () => undefined; private compactFn: (options?: CompactOptions) => void = () => {}; + private getSystemPromptFn: () => string = () => ""; private newSessionHandler: NewSessionHandler = async () => ({ cancelled: false }); private forkHandler: ForkHandler = async () => ({ cancelled: false }); private navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false }); @@ -203,6 +204,7 @@ export class ExtensionRunner { this.shutdownHandler = contextActions.shutdown; this.getContextUsageFn = contextActions.getContextUsage; this.compactFn = contextActions.compact; + this.getSystemPromptFn = contextActions.getSystemPrompt; // Process provider registrations queued during extension loading for (const { name, config } of this.runtime.pendingProviderRegistrations) { @@ -421,6 +423,7 @@ export class ExtensionRunner { shutdown: () => this.shutdownHandler(), getContextUsage: () => this.getContextUsageFn(), compact: (options) => this.compactFn(options), + getSystemPrompt: () => this.getSystemPromptFn(), }; } diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index 47e6051a..ad6c73dc 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -260,6 +260,8 @@ export interface ExtensionContext { getContextUsage(): ContextUsage | undefined; /** Trigger compaction without awaiting completion. */ compact(options?: CompactOptions): void; + /** Get the current effective system prompt. */ + getSystemPrompt(): string; } /** @@ -1084,6 +1086,7 @@ export interface ExtensionContextActions { shutdown: () => void; getContextUsage: () => ContextUsage | undefined; compact: (options?: CompactOptions) => void; + getSystemPrompt: () => string; } /** diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index df4c60a1..f40fe6ab 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -1120,6 +1120,7 @@ export class InteractiveMode { } })(); }, + getSystemPrompt: () => this.session.systemPrompt, }); // Set up the extension shortcut handler on the default editor