diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index ad3a064d..381e717d 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added +- `pi.setLabel(entryId, label)` in ExtensionAPI for setting per-entry labels from extensions ([#806](https://github.com/badlogic/pi-mono/issues/806)) - Export `keyHint`, `appKeyHint`, `editorKey`, `appKey`, `rawKeyHint` for extensions to format keybinding hints consistently - Added `showHardwareCursor` setting to control cursor visibility while still positioning it for IME support. ([#800](https://github.com/badlogic/pi-mono/pull/800) by [@ghoulr](https://github.com/ghoulr)) - Added `ctx.compact()` and `ctx.getContextUsage()` to extension contexts for programmatic compaction and context usage checks. diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index 75c62cfd..cf62e8ef 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -904,6 +904,23 @@ if (name) { } ``` +### pi.setLabel(entryId, label) + +Set or clear a label on an entry. Labels are user-defined markers for bookmarking and navigation (shown in `/tree` selector). + +```typescript +// Set a label +pi.setLabel(entryId, "checkpoint-before-refactor"); + +// Clear a label +pi.setLabel(entryId, undefined); + +// Read labels via sessionManager +const label = ctx.sessionManager.getLabel(entryId); +``` + +Labels persist in the session and survive restarts. Use them to mark important points (turns, checkpoints) in the conversation tree. + ### pi.registerCommand(name, options) Register a command. diff --git a/packages/coding-agent/src/core/extensions/index.ts b/packages/coding-agent/src/core/extensions/index.ts index 3d487e89..ff81d28e 100644 --- a/packages/coding-agent/src/core/extensions/index.ts +++ b/packages/coding-agent/src/core/extensions/index.ts @@ -98,6 +98,7 @@ export type { SessionSwitchEvent, SessionTreeEvent, SetActiveToolsHandler, + SetLabelHandler, SetModelHandler, SetThinkingLevelHandler, // Events - Tool diff --git a/packages/coding-agent/src/core/extensions/loader.ts b/packages/coding-agent/src/core/extensions/loader.ts index a6bcafda..d15e7e63 100644 --- a/packages/coding-agent/src/core/extensions/loader.ts +++ b/packages/coding-agent/src/core/extensions/loader.ts @@ -114,6 +114,7 @@ export function createExtensionRuntime(): ExtensionRuntime { appendEntry: notInitialized, setSessionName: notInitialized, getSessionName: notInitialized, + setLabel: notInitialized, getActiveTools: notInitialized, getAllTools: notInitialized, setActiveTools: notInitialized, @@ -205,6 +206,10 @@ function createExtensionAPI( return runtime.getSessionName(); }, + setLabel(entryId: string, label: string | undefined): void { + runtime.setLabel(entryId, label); + }, + exec(command: string, args: string[], options?: ExecOptions) { return execCommand(command, args, options?.cwd ?? cwd, options); }, diff --git a/packages/coding-agent/src/core/extensions/runner.ts b/packages/coding-agent/src/core/extensions/runner.ts index 64ffd0b0..4ce99b14 100644 --- a/packages/coding-agent/src/core/extensions/runner.ts +++ b/packages/coding-agent/src/core/extensions/runner.ts @@ -149,6 +149,7 @@ export class ExtensionRunner { this.runtime.appendEntry = actions.appendEntry; this.runtime.setSessionName = actions.setSessionName; this.runtime.getSessionName = actions.getSessionName; + this.runtime.setLabel = actions.setLabel; this.runtime.getActiveTools = actions.getActiveTools; this.runtime.getAllTools = actions.getAllTools; this.runtime.setActiveTools = actions.setActiveTools; diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index a9918344..8a2c3579 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -813,6 +813,9 @@ export interface ExtensionAPI { /** Get the current session name, if set. */ getSessionName(): string | undefined; + /** Set or clear a label on an entry. Labels are user-defined markers for bookmarking/navigation. */ + setLabel(entryId: string, label: string | undefined): void; + /** Execute a shell command. */ exec(command: string, args: string[], options?: ExecOptions): Promise; @@ -902,6 +905,8 @@ export type GetThinkingLevelHandler = () => ThinkingLevel; export type SetThinkingLevelHandler = (level: ThinkingLevel) => void; +export type SetLabelHandler = (entryId: string, label: string | undefined) => void; + /** * Shared state created by loader, used during registration and runtime. * Contains flag values (defaults set during registration, CLI values set after). @@ -920,6 +925,7 @@ export interface ExtensionActions { appendEntry: AppendEntryHandler; setSessionName: SetSessionNameHandler; getSessionName: GetSessionNameHandler; + setLabel: SetLabelHandler; getActiveTools: GetActiveToolsHandler; getAllTools: GetAllToolsHandler; setActiveTools: SetActiveToolsHandler; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 534d03aa..1a267f4a 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -669,6 +669,9 @@ export class InteractiveMode { getSessionName: () => { return this.sessionManager.getSessionName(); }, + setLabel: (entryId, label) => { + this.sessionManager.appendLabelChange(entryId, label); + }, getActiveTools: () => this.session.getActiveToolNames(), getAllTools: () => this.session.getAllTools(), setActiveTools: (toolNames) => this.session.setActiveToolsByName(toolNames), diff --git a/packages/coding-agent/src/modes/print-mode.ts b/packages/coding-agent/src/modes/print-mode.ts index 8af94fac..1a079718 100644 --- a/packages/coding-agent/src/modes/print-mode.ts +++ b/packages/coding-agent/src/modes/print-mode.ts @@ -60,6 +60,9 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti getSessionName: () => { return session.sessionManager.getSessionName(); }, + setLabel: (entryId, label) => { + session.sessionManager.appendLabelChange(entryId, label); + }, getActiveTools: () => session.getActiveToolNames(), getAllTools: () => session.getAllTools(), setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames), diff --git a/packages/coding-agent/src/modes/rpc/rpc-mode.ts b/packages/coding-agent/src/modes/rpc/rpc-mode.ts index 11687b7b..95575bec 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-mode.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-mode.ts @@ -273,6 +273,9 @@ export async function runRpcMode(session: AgentSession): Promise { getSessionName: () => { return session.sessionManager.getSessionName(); }, + setLabel: (entryId, label) => { + session.sessionManager.appendLabelChange(entryId, label); + }, getActiveTools: () => session.getActiveToolNames(), getAllTools: () => session.getAllTools(), setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames), diff --git a/packages/coding-agent/test/compaction-extensions.test.ts b/packages/coding-agent/test/compaction-extensions.test.ts index 62db5cce..4789e268 100644 --- a/packages/coding-agent/test/compaction-extensions.test.ts +++ b/packages/coding-agent/test/compaction-extensions.test.ts @@ -110,6 +110,7 @@ describe.skipIf(!API_KEY)("Compaction extensions", () => { appendEntry: async () => {}, setSessionName: () => {}, getSessionName: () => undefined, + setLabel: () => {}, getActiveTools: () => [], getAllTools: () => [], setActiveTools: () => {},