feat: structured logging for provider diagnostics + Cursor SDK rules/skills logs - #85
Draft
rfhold wants to merge 1 commit into
Draft
feat: structured logging for provider diagnostics + Cursor SDK rules/skills logs#85rfhold wants to merge 1 commit into
rfhold wants to merge 1 commit into
Conversation
…skills logs
Converts every console.warn/console.error in the provider (agent-backend,
agent-events, language-model) to route through opencode's client.app.log()
API instead, with a console.* fallback when no client bridge is published.
Also captures @cursor/sdk's own internal rules/skills load-completion
diagnostics (e.g. "LocalCursorRulesService load completed
meta={durationMs, ruleCount}"), which the SDK writes straight to
console.log with no public logger hook, and re-emits them as structured
opencode logs instead of raw terminal noise:
- In-process transport: a narrowly-scoped console.log interceptor matches
only the three known Cursor rules/skills messages; everything else
passes through unchanged (src/provider/cursor-log-intercept.ts).
- Sidecar transport: the child process installs the same interception and
forwards matches over the existing JSONL protocol as a new "log" event
(src/sidecar/agent-host.mjs), which SidecarClient forwards via a new
onLog option.
New src/provider/log-bridge.ts mirrors the existing subagent-bridge.ts
globalThis pattern to give the provider layer access to the plugin's
opencode client without a circular import.
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
The plugin's own diagnostics, and diagnostics from the bundled
@cursor/sdk, were being written straight toconsole.warn/console.error/console.loginstead of using opencode's plugin logging API. This surfaced as raw, unstructured lines in the terminal, e.g.:These two (plus a third,
CursorPluginsAgentSkillsService load completed) come from@cursor/sdk's bundled local-exec runtime, which writes them straight toconsole.log— there's no public logger hook in the SDK to redirect them.What changed
console.warn/console.errorcall inagent-backend.ts,agent-events.ts, andlanguage-model.tsnow routes throughclient.app.log({ body: { service: "opencode-cursor", level, message, extra } }), with aconsole.*fallback when no client bridge is available (e.g. the provider used standalone/in tests).src/provider/log-bridge.ts: mirrors the existingsubagent-bridge.tsglobalThispattern so the provider layer (which has no direct import path to the plugin's opencode client) can log through it. Published byplugin/index.tson init, cleared ondispose().src/provider/cursor-log-intercept.ts: a narrowly-scopedconsole.loginterceptor used for the in-process transport. It matches only the three known Cursor rules/skills "load completed" messages (ANSI-safe) and re-emits them viapluginLog; every otherconsole.logcall passes through untouched.src/sidecar/agent-host.mjs(the Node child that hosts@cursor/sdkwhen Bun'shttp2client can't be used) installs the same interception in the child process and forwards matches to the parent over the existing JSONL protocol as a new{ ev: "log" }message.SidecarClientgained anonLogoption to receive these.Testing
npm run typecheck— cleannpm test— 371 passed (29 pre-existing files + 2 new:log-bridge.test.ts,cursor-log-intercept.test.ts;sidecar.test.tsgained a log-forwarding case backed by a newemitRulesLogflag in the fake SDK fixture)npm run build— verified the interceptor + log bridge are present in bothdist/chunk-*.js(shared by provider/plugin) anddist/sidecar/agent-host.jsNotes
A broader interception of every Cursor SDK
console.logcall would risk swallowing unrelated output; this only recognizes the three confirmed rules/skills messages. If Cursor exposes a proper logger callback upstream, we can drop the interceptor and pass it through directly.