feat: make project scaffold project-wide

This commit is contained in:
2026-09-05 12:34:36 +08:00
parent d642d7607c
commit 6710527e8f
9 changed files with 252 additions and 12 deletions

View File

@@ -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: [],
});
});
});