feat(coding): add core host api composition

This commit is contained in:
2026-08-23 20:00:35 +08:00
parent 98bac20639
commit 22b4a9f9c4
23 changed files with 2258 additions and 59 deletions

View File

@@ -10,6 +10,7 @@ import type { AgentBrowserModule } from '../../electron/agent-browser';
import {
CodingProductHostError,
createCodingProductHost,
type CodingProductHostOptions,
} from '../../electron/api/coding-product-services';
import { CodingAttachmentStore } from '../../electron/coding-projects/attachment-store';
import { createCodingConversationStore } from '../../electron/coding-projects/conversation-store';
@@ -65,6 +66,35 @@ function productTools(root: string): PiProductTools {
});
}
function projectService(
root: string,
active = true,
): CodingProductHostOptions['projects'] {
const project = {
id: 'project-a',
path: root,
name: 'project-a',
createdAt: '2026-08-23T00:00:00.000Z',
updatedAt: '2026-08-23T00:00:00.000Z',
lastOpenedAt: '2026-08-23T00:00:00.000Z',
};
return {
getActiveProject: async () => active ? project : null,
findActiveConversation: async (id) => {
if (!active) throw new Error('No active project');
const conversation = await createCodingConversationStore(root).get(id);
if (!conversation) {
throw new CodingProductHostError(
404,
'CODING_CONVERSATION_NOT_FOUND',
'Coding Conversation does not exist',
);
}
return { project, conversation };
},
};
}
async function git(root: string, ...args: string[]): Promise<void> {
await exec('git', ['-C', root, ...args], { windowsHide: true });
}
@@ -78,7 +108,7 @@ describe('PI-105 product Host composition', () => {
const root = await configuredProject();
const tools = productTools(root);
const host = createCodingProductHost({
getActiveProject: async () => ({ id: 'project-a', path: root }),
projects: projectService(root),
productTools: tools,
listPiCommands: async () => ({
commands: [
@@ -113,7 +143,7 @@ describe('PI-105 product Host composition', () => {
await writeFile(path.join(root, 'notes.txt'), 'baseline\n', 'utf8');
const tools = productTools(root);
const host = createCodingProductHost({
getActiveProject: async () => ({ id: 'project-a', path: root }),
projects: projectService(root),
productTools: tools,
});
await tools.beginRun({ conversationId, runId: 'run-a', projectPath: root });
@@ -145,7 +175,7 @@ describe('PI-105 product Host composition', () => {
const tools = productTools(root);
const host = createCodingProductHost({
getActiveProject: async () => ({ id: 'project-a', path: root }),
projects: projectService(root),
productTools: tools,
});
await tools.beginRun({ conversationId, runId: 'run-conflict', projectPath: root });
@@ -165,7 +195,7 @@ describe('PI-105 product Host composition', () => {
const root = await configuredProject();
const tools = productTools(root);
const unavailable = createCodingProductHost({
getActiveProject: async () => null,
projects: projectService(root, false),
productTools: tools,
});
await expect(unavailable.fileStatus()).rejects.toMatchObject<CodingProductHostError>({
@@ -173,7 +203,7 @@ describe('PI-105 product Host composition', () => {
});
const host = createCodingProductHost({
getActiveProject: async () => ({ id: 'project-a', path: root }),
projects: projectService(root),
productTools: tools,
});
await expect(host.listSkills('missing')).rejects.toMatchObject<CodingProductHostError>({