Simplify extensions to thin wrappers: server-owned prompts, vendor trim, dumb-pipe CLI#801
Merged
backnotprop merged 20 commits intoMay 28, 2026
Merged
Conversation
Replace ~130 lines of local file resolution, URL fetching, HTML conversion, and folder detection with a single call to startAnnotationSessionFromArgs() that passes raw args to the binary. The daemon handles all content resolution — matching how OpenCode and the CLI already work. Remove unused imports: htmlToMarkdown, urlToMarkdown, isConvertedSource, hasMarkdownFiles, resolveUserPath, FILE_BROWSER_EXCLUDED, resolveAtReference, resolveUseJina, basename.
Reduce generated/ from ~42 files to 14 (11 vendored shared modules + 3 tiny stubs for transitive deps). Replace full vendored copies of resolve-file, at-reference, and vcs-core with minimal stubs containing only the functions/types Pi actually uses. Inline DiffType and VcsSelection types in plannotator-browser.ts and plannotator-events.ts. Remove turndown, @joplin/turndown-plugin-gfm, and @pierre/diffs dependencies — HTML/URL conversion is now handled by the binary.
Pass the original command string (with @-prefixes intact) to the binary instead of the parsed/stripped filePath. The daemon's resolveAnnotateInput handles @-reference resolution from raw args. Use the binary's result.mode and result.filePath to build the feedback prompt — "Folder: /abs/path" for folders, "File: path" for files — instead of hardcoding "File".
The annotate and review servers now compose the final agent-ready prompt at decision time and include it as `result.prompt`. Extensions pipe it through to the agent session instead of building prompts locally. Server changes: - annotate.ts: compose prompt using mode/filePath/origin at feedback time - review.ts: compose prompt with approved/denied/PR logic at feedback time - plugin-protocol.ts: add prompt? field to PluginAnnotateResult and PluginReviewResult Extension changes (Pi + OpenCode): - Replace all getAnnotateFileFeedbackPrompt, getAnnotateMessageFeedbackPrompt, getReviewApprovedPrompt, getReviewDeniedSuffix calls with result.prompt - Remove prompt function imports from both extensions - Pi: remove dead anchorMessageFeedback/excerptText helpers - OpenCode: update test to verify server prompt passthrough
CLI review output now uses result.prompt instead of rebuilding the prompt locally. Remove getReviewApprovedPrompt and getReviewDeniedSuffix imports from CLI. OpenCode annotate-last now pipes result.prompt directly instead of double-wrapping with getAnnotateMessageFeedbackPrompt. Remove the import.
Plan server now composes denial prompts at /api/deny time using getPlanDeniedPrompt/getPlanToolName/buildPlanFileRule and returns result.prompt. All 4 CLI hook paths (Copilot, Codex, Gemini, Claude Code) now use result.prompt instead of building prompts locally. Remove resolveUseJina from CLI — daemon resolves Jina settings from its own config. CLI just passes the noJina flag through. CLI now has zero prompt function imports and zero config-based prompt generation. Only remaining loadConfig call is for the PFM improve-context hook (documented exception — reads hook files from local filesystem).
Add /daemon/improve-context endpoint that reads improvement hooks and composes PFM context server-side. CLI now calls the daemon instead of reading hook files and loading config locally. Remove dead imports: hostnameOrFallback, parseReviewArgs, loadConfig, resolveUseJina, readImprovementHook, composeImproveContext. Remove dead parseReviewArgs() call in review handler. CLI now has zero @plannotator/shared imports for processing — only types, agent config for origin detection, and goal-setup normalization.
Replace parseAnnotateArgs with 3 includes() checks — Pi passes raw args to the binary anyway. Add startCodeReviewSessionFromArgs that passes raw args (matching annotate pattern). Remove parseReviewArgs. Replace vendored agents.ts (35 lines, display names, badge classes) with 2-line type stub — only Origin type needed by plugin-protocol. Removed from generated/: - annotate-args.ts, review-args.ts (replaced by simple string checks) - at-reference.ts, resolve-file.ts, vcs-core.ts (transitive stubs, no longer needed) - agents.ts full file (replaced with type-only stub)
… plan file rule, OpenCode guard - Fix runtime crash in improve-context: ensureDaemonClient returns DaemonClient directly, not an object with .client — use client.getJson() instead - Add try/catch around improve-context daemon call so hook failures silently pass through instead of crashing (matches old local-read behavior) - Make DaemonClient.getJson public for daemon endpoint access - Fix emitAnnotateOutcome to use result.prompt ?? result.feedback (CLI annotate paths were ignoring server-composed prompts) - Revert planFileRule to "" in plan server — slug is not a real file path, and non-Gemini origins never had a plan file rule - Fix OpenCode annotate guard: check result.prompt || result.feedback instead of just result.feedback
… excerpt The server holds the original assistant message for the session lifetime. When composing annotate-last prompts, include a blockquoted excerpt so the agent knows which message the annotations apply to — even if the conversation has moved on by the time feedback is submitted.
ensureDaemonClient calls process.exit(1) on failure instead of throwing, so the try/catch never caught anything. Use discoverDaemon directly — if a daemon is already running, call the endpoint; if not, exit 0 silently. No daemon startup, no retries. Improve-context is best-effort.
Add bestEffort option to ensureDaemonClient — throws instead of calling process.exit, so callers can try/catch gracefully. The improve-context handler now auto-starts the daemon like every other command, but silently passes through if the daemon can't start. Document fact: daemon starts on install and is always running.
cleanupDaemonStateForSessionCommand had its own error handler that called process.exit, bypassing the try/catch in the improve-context handler. Now it re-throws under bestEffort mode so the caller can handle it.
The contract changed: the binary now returns result.prompt and clients stopped building prompts locally. Old binaries that don't return prompt must be rejected so ensurePlannotatorBinary triggers a re-install.
- Define DiffType and VcsSelection once in plannotator-browser.ts, import in plannotator-events.ts instead of duplicating - Remove isCurrentPiSessionDifferentFrom (no longer imported) - Remove hasSessionMovedPastEntry (no longer imported)
Custom workflows using --json or --hook expect raw user feedback in the output fields, not the server-composed prompt. Keep result.feedback for structured output modes, use result.prompt only for plaintext (agent-facing) mode.
The CLI reads planFilename from Gemini's event input and now passes it through to the daemon session. The plan server uses it in buildPlanFileRule so denied Gemini plans include "Your plan is saved at: <file>" instructions. Non-Gemini origins pass undefined, which produces an empty rule (unchanged).
Pi can't import from packages/shared at runtime (npm package), so the type is inlined. Was missing merge-base, all, worktree, and p4 variants.
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.
Summary
result.prompt. Extensions and the CLI pipe it through viaresult.prompt ?? result.feedback— they never rebuild or reformat prompts. The only exception is plan mode prompts, which are host-specific (each extension has its own planning workflow)./daemon/improve-contextendpoint.includes()checks and raw-args binary calls. Removedturndown,@joplin/turndown-plugin-gfm,@pierre/diffsdependencies (zero runtime deps). Stubbedagents.tsto just theOrigintype.result.promptfrom the server.Architecture
All three extension surfaces are now symmetric thin wrappers:
No local prompt generation. No file processing. No HTML conversion. No URL fetching. The daemon handles everything.
Plan mode is the exception — Pi/OpenCode keep their planning workflows (phase management, tool gating, checklist tracking, prompt injection) because those require host-specific APIs.
Files changed
packages/server/index.tsgetPlanDeniedPromptpackages/server/review.tspackages/server/annotate.tspackages/server/daemon/server.ts/daemon/improve-contextendpointpackages/server/daemon/client.tsgetJsonmade public for endpoint accesspackages/shared/plugin-protocol.tsprompt?: stringto all result typesapps/hook/server/index.tsresult.promptapps/pi-extension/index.tsapps/pi-extension/plannotator-browser.tsstartCodeReviewSessionFromArgs,startAnnotationSessionFromArgsapps/pi-extension/vendor.shapps/opencode-plugin/commands.tsresult.promptapps/opencode-plugin/index.tsgetAnnotateMessageFeedbackPromptwrappingTest plan
bun run typecheckpassesbun testpasses (1,331 tests, 0 failures)bash apps/pi-extension/vendor.shruns cleanlyls apps/pi-extension/generated/shows exactly 9 filesgrep -c 'parseAnnotateArgs\|parseReviewArgs' apps/pi-extension/index.tsreturns 0result.prompt ?? result.feedbackpaths verified across CLI, Pi, OpenCode