@khalilgharbaoui/opencode-claude-code-plugin 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -281,6 +281,20 @@ By default, the plugin proxies `Bash`, `Edit`, `Write`, `WebFetch`, and `Task`.
281
281
  - **Nested tasks:** current opencode defaults `subagent_depth` to `1`, so a first-level child cannot launch another child. Increase top-level `subagent_depth` to permit deeper nesting, and explicitly grant `permission.task` on every subagent that should delegate; opencode otherwise adds a task deny to spawned subagent sessions.
282
282
  - **Background:** `background: true` returns after starting the child and lets opencode notify the parent when it finishes. Current opencode requires `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` in the environment of the opencode process. Foreground is the default.
283
283
 
284
+ **Steering models to it.** Headless Claude Code CLIs expose no `Agent`/`Task`
285
+ dispatch tool of their own (verified on 2.1.211), while they *do* expose
286
+ `TaskCreate` — a todo tool. So "use a subagent" requests get mis-resolved:
287
+ a todo appears, nothing runs, and the model may still narrate a successful
288
+ dispatch. Two spawn-time countermeasures prevent that. The plugin injects
289
+ opencode's live agent-type list into the `task` proxy description (so the model
290
+ picks a real `subagent_type` instead of guessing a Claude Code name like
291
+ `general-purpose`, and doesn't grep configs to check a subagent exists), and
292
+ appends a system-prompt note naming
293
+ `mcp__opencode_proxy__task` as the only dispatch path — with the ToolSearch
294
+ recovery step for harnesses that defer MCP tool schemas. Both apply per Claude
295
+ process at spawn, and provider options are read once at opencode startup, so
296
+ `proxyTools` changes need a full opencode restart.
297
+
284
298
  Only those five values are actually proxied; anything else you put in `proxyTools` is ignored. Proxying `Edit` also disables `MultiEdit` — opencode has no batched-edit equivalent, so Claude is forced to fan out into single `Edit` calls that each flow through the permission UI.
285
299
 
286
300
  Without `"Task"` in `proxyTools`, Claude's built-in `Agent` tool stays enabled and Claude orchestrates subagents internally with no opencode child-session visibility. To opt out of all proxying, including Task, use an explicit empty list:
package/dist/index.d.ts CHANGED
@@ -505,6 +505,17 @@ declare class ClaudeCodeLanguageModel implements LanguageModelV3 {
505
505
  * client is unavailable, or no MCP servers are configured.
506
506
  */
507
507
  private resolvedProxyMcpTools;
508
+ /**
509
+ * Live description of opencode's `task` tool for the current
510
+ * provider/model, exactly as opencode's registry renders it for native
511
+ * models — including the "Available agent types" list (built from the
512
+ * default agent's permissions). Overlaid onto the static `task` proxy
513
+ * def so Claude sees the same subagent catalog native opencode models
514
+ * see, instead of hunting through config files. Returns undefined when
515
+ * the SDK client is unavailable (direct AI-SDK use, tests) so the
516
+ * static def stands.
517
+ */
518
+ private fetchLiveTaskDescription;
508
519
  /**
509
520
  * Create a proxy MCP server for a single active Claude process/session.
510
521
  * The process lifecycle owns the server lifecycle via session-manager.
package/dist/index.js CHANGED
@@ -2117,6 +2117,37 @@ function buildProxyTimeoutError(toolName, ms) {
2117
2117
  }
2118
2118
  return new Error(base);
2119
2119
  }
2120
+ var TASK_PROXY_NOTE = "This is the ONLY tool that dispatches opencode subagents (including user @-mentions). Claude Code's built-in TaskCreate/TaskUpdate manage a local todo list and cannot dispatch subagents. Do not search config files to verify a subagent type exists \u2014 invalid types fail fast with a clear error. Foreground calls block until the subagent finishes; set `background` to request opencode's background execution mode. Task calls get a 60-minute proxy deadline by default (configurable via proxyToolTimeoutMs).";
2121
+ var AGENT_TYPES_HEADING = "Available agent types";
2122
+ var AGENT_BLURB_LIMIT = 140;
2123
+ function extractAgentTypeList(liveDescription) {
2124
+ const live = liveDescription?.trim();
2125
+ if (!live) return void 0;
2126
+ const start = live.indexOf(AGENT_TYPES_HEADING);
2127
+ if (start === -1) return void 0;
2128
+ const entries = [];
2129
+ for (const raw of live.slice(start).split("\n")) {
2130
+ const match = /^-\s*([^:]+):\s*(.+)$/.exec(raw.trim());
2131
+ if (!match) continue;
2132
+ const name = match[1].trim();
2133
+ const blurb = match[2].trim();
2134
+ entries.push(
2135
+ `- ${name}: ${blurb.length > AGENT_BLURB_LIMIT ? `${blurb.slice(0, AGENT_BLURB_LIMIT).trimEnd()}\u2026` : blurb}`
2136
+ );
2137
+ }
2138
+ if (entries.length === 0) return void 0;
2139
+ return `Valid subagent_type values, from opencode's live registry \u2014 anything else fails:
2140
+ ${entries.join("\n")}`;
2141
+ }
2142
+ function overlayTaskProxyDescription(tools, liveDescription) {
2143
+ const agentTypes = extractAgentTypeList(liveDescription);
2144
+ if (!agentTypes) return tools;
2145
+ return tools.map(
2146
+ (t) => t.name === "task" ? { ...t, description: `${agentTypes}
2147
+
2148
+ ${t.description}` } : t
2149
+ );
2150
+ }
2120
2151
  var DEFAULT_PROXY_TOOLS = [
2121
2152
  {
2122
2153
  name: "bash",
@@ -2209,7 +2240,7 @@ var DEFAULT_PROXY_TOOLS = [
2209
2240
  },
2210
2241
  {
2211
2242
  name: "task",
2212
- description: "Launch an opencode subagent to handle a complex multi-step task autonomously. Routed through opencode's task tool so subagent orchestration, permission, and lifecycle are handled by opencode. Use `subagent_type` to pick which configured subagent runs (e.g. `build`, `general`, `explore`, or any custom subagent declared in opencode.json). Foreground calls block until the subagent finishes; set `background` to request opencode's background execution mode. Task calls get a 60-minute proxy deadline by default (configurable via proxyToolTimeoutMs).",
2243
+ description: "Launch an opencode subagent to handle a complex multi-step task autonomously. Routed through opencode's task tool so subagent orchestration, permission, and lifecycle are handled by opencode. Use `subagent_type` to pick which configured subagent runs (e.g. `build`, `general`, `explore`, or any custom subagent declared in opencode.json). " + TASK_PROXY_NOTE,
2213
2244
  inputSchema: {
2214
2245
  type: "object",
2215
2246
  properties: {
@@ -2884,6 +2915,14 @@ than pausing for user confirmation between subtasks. End the turn only
2884
2915
  when the task is done, you need clarification on intent, or you hit a real
2885
2916
  blocker. The user can interrupt or abort at any time; turn endings should
2886
2917
  mark meaningful checkpoints, not every completed substep.`;
2918
+ var SUBAGENT_DISPATCH_HINT = `## opencode subagents
2919
+
2920
+ Subagent dispatch in this environment goes through exactly one tool: \`mcp__opencode_proxy__task\`.
2921
+
2922
+ - When the user mentions \`@<agent>\` or an instruction says "call the task tool with subagent: <name>", call \`mcp__opencode_proxy__task\` with \`subagent_type: "<name>"\`.
2923
+ - If that tool is not in your visible tool list it is deferred \u2014 load it with ToolSearch (\`select:mcp__opencode_proxy__task\`), then call it.
2924
+ - Claude Code's built-in TaskCreate/TaskUpdate/TaskList manage a local todo list. They cannot dispatch subagents; creating a task there runs nothing. Never report a subagent as dispatched unless \`mcp__opencode_proxy__task\` returned its result.
2925
+ - Do not verify a subagent's existence by searching config files \u2014 the tool's description lists the available agent types, and invalid types fail fast with a clear error.`;
2887
2926
  var CLAUDE_CLI_CONTEXT_NOTE = `## Runtime environment: Claude Code CLI
2888
2927
 
2889
2928
  You are running via the Claude Code CLI (not a direct API call). This affects context management:
@@ -3058,6 +3097,24 @@ var ClaudeCodeLanguageModel = class {
3058
3097
  }
3059
3098
  return out.length > 0 ? out : null;
3060
3099
  }
3100
+ /**
3101
+ * Live description of opencode's `task` tool for the current
3102
+ * provider/model, exactly as opencode's registry renders it for native
3103
+ * models — including the "Available agent types" list (built from the
3104
+ * default agent's permissions). Overlaid onto the static `task` proxy
3105
+ * def so Claude sees the same subagent catalog native opencode models
3106
+ * see, instead of hunting through config files. Returns undefined when
3107
+ * the SDK client is unavailable (direct AI-SDK use, tests) so the
3108
+ * static def stands.
3109
+ */
3110
+ async fetchLiveTaskDescription() {
3111
+ const items = await fetchOpencodeToolList(
3112
+ this.config.provider,
3113
+ this.modelId,
3114
+ this.config.cwd
3115
+ );
3116
+ return items?.find((item) => item.id === "task")?.description || void 0;
3117
+ }
3061
3118
  /**
3062
3119
  * Create a proxy MCP server for a single active Claude process/session.
3063
3120
  * The process lifecycle owns the server lifecycle via session-manager.
@@ -3929,7 +3986,23 @@ ${plan}
3929
3986
  discovery.allEnabledServerNames
3930
3987
  );
3931
3988
  const excludeServers = proxyMcpTools ? new Set(discovery.allEnabledServerNames) : void 0;
3932
- const combinedProxyTools = resolvedProxy || proxyMcpTools ? [...resolvedProxy ?? [], ...proxyMcpTools ?? []] : null;
3989
+ const taskProxyEnabled = resolvedProxy?.some((t) => t.name === "task") ?? false;
3990
+ let enrichedProxy = resolvedProxy;
3991
+ if (resolvedProxy && taskProxyEnabled) {
3992
+ const liveTaskDescription = await self.fetchLiveTaskDescription();
3993
+ enrichedProxy = overlayTaskProxyDescription(
3994
+ resolvedProxy,
3995
+ liveTaskDescription
3996
+ );
3997
+ log.info("task proxy description overlay", {
3998
+ applied: Boolean(liveTaskDescription),
3999
+ liveDescriptionLength: liveTaskDescription?.length ?? 0,
4000
+ listsAgentTypes: Boolean(
4001
+ liveTaskDescription?.includes("Available agent types")
4002
+ )
4003
+ });
4004
+ }
4005
+ const combinedProxyTools = enrichedProxy || proxyMcpTools ? [...enrichedProxy ?? [], ...proxyMcpTools ?? []] : null;
3933
4006
  if (!proxyServer && combinedProxyTools) {
3934
4007
  proxyServer = await self.ensureProxyServer(combinedProxyTools, sk);
3935
4008
  }
@@ -3946,7 +4019,10 @@ ${plan}
3946
4019
  const systemPromptFile = activeProcess ? void 0 : buildAppendedSystemPrompt(
3947
4020
  cwd,
3948
4021
  self.config.multiStepContinuation !== false,
3949
- extractSystemMessages(options.prompt)
4022
+ [
4023
+ ...extractSystemMessages(options.prompt),
4024
+ ...taskProxyEnabled ? [SUBAGENT_DISPATCH_HINT] : []
4025
+ ]
3950
4026
  );
3951
4027
  cliArgs = buildCliArgs({
3952
4028
  sessionKey: sk,