mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-19 21:09:50 +00:00
feat(cli): add skill_lookup MCP for non-native agents (#1035)
* test: reproduce issue #752 * fix: expose skill lookup MCP tool (closes #752) * test: cover ACP skill lookup instructions * fix: inject ACP skill lookup instruction * test: narrow skill lookup auto-approval * fix: restrict skill lookup auto-approval * test: cover exact skill lookup tool names
This commit is contained in:
@@ -266,7 +266,9 @@ export async function opencodeLocalLauncher(
|
||||
let happyServer: { url: string; stop: () => void } | null = null;
|
||||
let opencodeConfigPath: string | null = null;
|
||||
try {
|
||||
const bridge = await buildHapiMcpBridge(session.client);
|
||||
const bridge = await buildHapiMcpBridge(session.client, {
|
||||
skillLookup: { workingDirectory: session.path, flavor: 'opencode' }
|
||||
});
|
||||
happyServer = bridge.server;
|
||||
logger.debug(`[opencode-local]: Started hapi MCP server at ${happyServer.url}`);
|
||||
|
||||
|
||||
@@ -178,6 +178,19 @@ describe('opencodeRemoteLauncher inline model switch', () => {
|
||||
harness.thoughtLevelOption = null;
|
||||
});
|
||||
|
||||
it('injects the skill lookup instruction only on the first prompt', async () => {
|
||||
const { session } = createSessionStub([
|
||||
{ message: 'first', mode: createMode() },
|
||||
{ message: 'second', mode: createMode() }
|
||||
]);
|
||||
|
||||
await opencodeRemoteLauncher(session as never);
|
||||
|
||||
expect(JSON.stringify(harness.promptContents[0])).toContain('$name');
|
||||
expect(JSON.stringify(harness.promptContents[0])).toContain('skill_lookup');
|
||||
expect(JSON.stringify(harness.promptContents[1])).not.toContain('skill_lookup');
|
||||
});
|
||||
|
||||
it('calls setModel with opencode flavor between turns when the queued model differs', async () => {
|
||||
const { session } = createSessionStub([
|
||||
{ message: 'first', mode: createMode('ollama/exaone:4.5-33b-q8') },
|
||||
|
||||
@@ -55,7 +55,9 @@ class OpencodeRemoteLauncher extends RemoteLauncherBase {
|
||||
const session = this.session;
|
||||
const messageBuffer = this.messageBuffer;
|
||||
|
||||
const { server: happyServer, mcpServers } = await buildHapiMcpBridge(session.client);
|
||||
const { server: happyServer, mcpServers } = await buildHapiMcpBridge(session.client, {
|
||||
skillLookup: { workingDirectory: session.path, flavor: 'opencode' }
|
||||
});
|
||||
this.happyServer = happyServer;
|
||||
|
||||
const backend = createOpencodeBackend({
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { ensureOpencodeConfig } from './opencodeConfig'
|
||||
import { TITLE_INSTRUCTION } from './systemPrompt'
|
||||
|
||||
describe('OpenCode local HAPI instructions', () => {
|
||||
let configDirectory: string | null = null
|
||||
|
||||
afterEach(async () => {
|
||||
if (configDirectory) {
|
||||
await rm(configDirectory, { recursive: true, force: true })
|
||||
configDirectory = null
|
||||
}
|
||||
})
|
||||
|
||||
it('writes the skill lookup instruction into the configured system prompt', async () => {
|
||||
configDirectory = await mkdtemp(join(tmpdir(), 'hapi-opencode-prompt-'))
|
||||
const { instructionsPath } = ensureOpencodeConfig(
|
||||
configDirectory,
|
||||
{ command: 'hapi', args: ['mcp'] },
|
||||
TITLE_INSTRUCTION
|
||||
)
|
||||
|
||||
const instructions = await readFile(instructionsPath, 'utf8')
|
||||
expect(instructions).toContain('$name')
|
||||
expect(instructions).toContain('skill_lookup')
|
||||
})
|
||||
})
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import { trimIdent } from '@/utils/trimIdent';
|
||||
import { SKILL_LOOKUP_INSTRUCTION } from '@/modules/common/skillLookupInstruction';
|
||||
|
||||
/**
|
||||
* Title instruction for OpenCode to call the hapi MCP tool.
|
||||
@@ -13,6 +14,7 @@ import { trimIdent } from '@/utils/trimIdent';
|
||||
export const TITLE_INSTRUCTION = trimIdent(`
|
||||
Use the title tool sparingly. For a new chat, call the tool "hapi_change_title" once after the user's initial request is clear, and set a concise task title. Do not rename the chat for routine progress, substeps, implementation details, or a slightly better wording. Rename only when the user's primary objective changes substantially and the existing title would be misleading.
|
||||
When you create or find a local image file that the user should see, call the tool "hapi_display_image" with the image path so HAPI can show it inline.
|
||||
${SKILL_LOOKUP_INSTRUCTION}
|
||||
`);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user