feat: make project scaffold project-wide
This commit is contained in:
@@ -7,6 +7,8 @@ import {
|
||||
} from '../../electron/coding-plugins/effective-resolver';
|
||||
import {
|
||||
DATA_SERVICE_PLUGIN_DEFINITION,
|
||||
PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
type CodingPluginDefinition,
|
||||
} from '../../shared/coding-plugins';
|
||||
|
||||
@@ -53,6 +55,20 @@ const serverDefinition: CodingPluginDefinition = {
|
||||
surfaces: {},
|
||||
};
|
||||
|
||||
const projectScaffoldDefinition: CodingPluginDefinition = {
|
||||
...skillOnlyDefinition,
|
||||
id: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
displayName: 'Project Scaffold',
|
||||
description: 'Project-scoped scaffold Skill',
|
||||
releaseId: PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
provenance: { source: 'bundled', packageRoot: 'project-scaffold' },
|
||||
skills: [{
|
||||
id: 'makelore-project-scaffold',
|
||||
entryPath: 'skills/makelore-project-scaffold/SKILL.md',
|
||||
grants: [],
|
||||
}],
|
||||
};
|
||||
|
||||
const binding = { accountKey: 'account-a', epoch: 4 };
|
||||
|
||||
function library(runtimeStatus: 'enabled' | 'suspended' = 'enabled', catalogStatus: 'active' | 'retired' = 'active') {
|
||||
@@ -487,4 +503,65 @@ describe('effective plugin resolver', () => {
|
||||
.resolves.toMatchObject({ id: bundled.id, releaseId: bundled.releaseId });
|
||||
expect(getInstalledRelease).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('materializes the project-wide Scaffold Skill after project enablement without Agent assignment', async () => {
|
||||
const scaffoldLibrary = {
|
||||
...library(),
|
||||
items: [{
|
||||
...library().items[0],
|
||||
pluginId: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
title: projectScaffoldDefinition.displayName,
|
||||
}],
|
||||
};
|
||||
const effective = createEffectivePluginResolver({
|
||||
definitions: [projectScaffoldDefinition],
|
||||
getAccountBinding: () => binding,
|
||||
getLibrary: vi.fn(async () => scaffoldLibrary),
|
||||
getEnabledPluginIds: vi.fn(async () => [PROJECT_SCAFFOLD_PLUGIN_ID]),
|
||||
});
|
||||
|
||||
await expect(effective.resolve({
|
||||
projectId: 'project-a',
|
||||
projectPath: 'C:/project-a',
|
||||
assignedSkillIds: [],
|
||||
role: 'parent',
|
||||
})).resolves.toMatchObject({
|
||||
pluginReleaseIds: [PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID],
|
||||
effectiveSkillIds: ['makelore-project-scaffold'],
|
||||
skillEntries: [{
|
||||
id: 'makelore-project-scaffold',
|
||||
entryPath: 'skills/makelore-project-scaffold/SKILL.md',
|
||||
}],
|
||||
unavailableReasons: [],
|
||||
});
|
||||
|
||||
await expect(createEffectivePluginResolver({
|
||||
definitions: [projectScaffoldDefinition],
|
||||
getAccountBinding: () => binding,
|
||||
getLibrary: vi.fn(async () => scaffoldLibrary),
|
||||
getEnabledPluginIds: vi.fn(async () => []),
|
||||
}).resolve({
|
||||
projectId: 'project-a',
|
||||
projectPath: 'C:/project-a',
|
||||
assignedSkillIds: [],
|
||||
role: 'parent',
|
||||
})).resolves.toMatchObject({
|
||||
effectiveSkillIds: [],
|
||||
unavailableReasons: [expect.objectContaining({
|
||||
pluginId: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
code: 'project_disabled',
|
||||
})],
|
||||
});
|
||||
|
||||
await expect(effective.resolve({
|
||||
projectId: 'project-a',
|
||||
projectPath: 'C:/project-a',
|
||||
assignedSkillIds: [],
|
||||
role: 'child',
|
||||
})).resolves.toMatchObject({
|
||||
pluginReleaseIds: [],
|
||||
effectiveSkillIds: [],
|
||||
skillEntries: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -298,7 +298,9 @@ describe('buildPluginWorkspaceProjection', () => {
|
||||
library: { ...library, items: [bundledLibrary] },
|
||||
marketplaceInstallations: {},
|
||||
project: bundledProject,
|
||||
})).toEqual(['remove_from_library', 'enable_project', 'open_agent_assignment']);
|
||||
})).toEqual(pluginId === 'makelore.project-scaffold'
|
||||
? ['remove_from_library', 'enable_project']
|
||||
: ['remove_from_library', 'enable_project', 'open_agent_assignment']);
|
||||
}
|
||||
|
||||
expect(commandKinds({ marketplaceInstallations: {} })).toEqual([
|
||||
|
||||
@@ -427,7 +427,7 @@ describe('PluginsView', () => {
|
||||
|
||||
expect(screen.getByRole('heading', { name: '插件' })).toBeVisible();
|
||||
expect(screen.getByText(/MakeLore 运营发布官方插件/)).toBeVisible();
|
||||
expect(screen.getByText(/获取、项目启用与伙伴分配彼此独立/)).toBeVisible();
|
||||
expect(screen.getByText(/获取与项目启用彼此独立.*需要定向生效的插件还可分配伙伴/)).toBeVisible();
|
||||
expect(screen.getByText('开发数据服务')).toBeVisible();
|
||||
expect(screen.getByText('Pi Web Search')).toBeVisible();
|
||||
expect(screen.getByRole('heading', { name: '本机全局生效' })).toBeVisible();
|
||||
@@ -486,6 +486,10 @@ describe('PluginsView', () => {
|
||||
|
||||
expect(screen.getAllByText('随应用提供')).toHaveLength(2);
|
||||
expect(screen.queryByText('未下载官方包')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('随项目启用')).toBeVisible();
|
||||
expect(screen.getByRole('heading', { name: '生效范围' })).toBeVisible();
|
||||
expect(screen.getByText(/启用当前项目后.*无需单独分配/)).toBeVisible();
|
||||
expect(screen.queryByRole('heading', { name: '伙伴分配' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows one dialog detail surface, confirms destructive commands, and embeds Data Service settings', () => {
|
||||
|
||||
Reference in New Issue
Block a user