Skip to content
This repository was archived by the owner on Feb 14, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Pr

on:
pull_request:
pull_request_target:
workflow_dispatch:

concurrency:
Expand Down
37 changes: 37 additions & 0 deletions .memory/issue-28-analysis-comment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Analysis

Thanks for the detailed report — I validated this in code and agree with the root cause.

### Summary

- `skill_use` / skill prompt injection can reset to the default agent.
- The injection path currently sends a silent prompt without `agent`, so OpenCode may fall back to default agent selection.

### CodeMapper findings

- `createInstructionInjector` is defined in `src/lib/OpenCodeChat.ts`.
- `sendPrompt` currently calls `ctx.client.session.prompt(...)` with:
- `noReply: true`
- `parts: [{ type: 'text', text }]`
- **no `agent` field**
- Callers of this injection path are in `src/index.ts`:
- `skill_use` execution path
- `skill_resource` execution path

### LSP / type-level evidence

- `ToolContext` includes `agent: string` (`@opencode-ai/plugin/dist/tool.d.ts`).
- Session prompt request body supports `agent?: string` (`@opencode-ai/sdk/dist/gen/types.gen.d.ts`).

So the agent value is available at tool execution time and can be forwarded safely.

### Proposed fix

1. Update injector props in `src/lib/OpenCodeChat.ts`:
- from `{ sessionId: string }`
- to `{ sessionId: string; agent: string }`
2. Include `agent: props.agent` in `ctx.client.session.prompt({ body: ... })`.
3. Update call sites in `src/index.ts` (`skill_use`, `skill_resource`) to pass `toolCtx.agent`.
4. Add regression test for injector to ensure silent injected messages preserve agent.

This should preserve the original/current agent during skill loading and prevent fallback to default agent.
12 changes: 0 additions & 12 deletions .mise/tasks/dev

This file was deleted.

3 changes: 0 additions & 3 deletions .mise/tasks/format

This file was deleted.

3 changes: 0 additions & 3 deletions .mise/tasks/prepare

This file was deleted.

12 changes: 0 additions & 12 deletions .mise/tasks/watch

This file was deleted.

2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const SkillsPlugin: Plugin = async (ctx) => {
for await (const skill of results.loaded) {
await sendPrompt(renderer({ data: skill, type: 'Skill' }), {
sessionId: toolCtx.sessionID,
agent: toolCtx.agent,
});
}

Expand Down Expand Up @@ -161,6 +162,7 @@ export const SkillsPlugin: Plugin = async (ctx) => {

await sendPrompt(renderer({ data: result.injection, type: 'SkillResource' }), {
sessionId: toolCtx.sessionID,
agent: toolCtx.agent,
});

return JSON.stringify({
Expand Down
43 changes: 43 additions & 0 deletions src/lib/OpenCodeChat.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { PluginInput } from '@opencode-ai/plugin';
import { describe, expect, it, vi } from 'vitest';
import { createInstructionInjector } from './OpenCodeChat';

type PromptPayload = {
path: { id: string };
body: {
agent?: string;
noReply?: boolean;
parts: Array<{ type: string; text: string }>;
};
};

describe('createInstructionInjector', () => {
it('sends silent prompt using the original agent', async () => {
const promptMock = vi.fn<(...args: [PromptPayload]) => Promise<void>>(async () => undefined);

const ctx = {
client: {
session: {
prompt: promptMock,
},
},
} as unknown as PluginInput;

const sendPrompt = createInstructionInjector(ctx);

await sendPrompt('skill payload', {
sessionId: 'session-123',
agent: 'frontend-developer',
});

expect(promptMock).toHaveBeenCalledTimes(1);
expect(promptMock).toHaveBeenCalledWith({
path: { id: 'session-123' },
body: {
agent: 'frontend-developer',
noReply: true,
parts: [{ type: 'text', text: 'skill payload' }],
},
});
});
});
3 changes: 2 additions & 1 deletion src/lib/OpenCodeChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import type { PluginInput } from '@opencode-ai/plugin';

export function createInstructionInjector(ctx: PluginInput) {
// Message 1: Skill loading header (silent insertion - no AI response)
const sendPrompt = async (text: string, props: { sessionId: string }) => {
const sendPrompt = async (text: string, props: { sessionId: string; agent: string }) => {
await ctx.client.session.prompt({
path: { id: props.sessionId },
body: {
agent: props.agent,
noReply: true,
parts: [{ type: 'text', text }],
},
Expand Down
Loading