feat(projects): add interactive AI app scaffold skill

This commit is contained in:
2026-09-04 20:36:28 +08:00
parent 336e0bb0ca
commit 959b6333b5
40 changed files with 2647 additions and 65 deletions

View File

@@ -42,7 +42,7 @@ const AdmZip = require('adm-zip') as typeof import('adm-zip');
const tempDirectories: string[] = [];
async function createProject(projectType: ProjectType = 'mini_game'): Promise<string> {
async function createProject(projectType: ProjectType = 'interactive_ai_app'): Promise<string> {
const projectPath = await mkdtemp(join(tmpdir(), 'makelore-project-packager-'));
tempDirectories.push(projectPath);
await mkdir(join(projectPath, 'src'), { recursive: true });
@@ -180,7 +180,7 @@ describe('project packager', () => {
]);
expect(archive.readAsText('niancode.yml')).toBe(
'schema_version: 1\n'
+ 'project_type: mini_game\n'
+ 'project_type: interactive_ai_app\n'
+ 'kind: web\n'
+ 'runtime: static\n'
+ 'build:\n'
@@ -188,22 +188,34 @@ describe('project packager', () => {
+ ' package_manager: npm\n'
+ ' entry: index.html\n',
);
expect(first.manifest.project_type).toBe('mini_game');
expect(first.manifest.project_type).toBe('interactive_ai_app');
expect(entries.every((entry) => entry.header.time.getFullYear() === 1980)).toBe(true);
await expect(readFile(join(projectPath, 'niancode.yml'), 'utf8')).resolves.toBe('runtime: compose\n');
});
it('writes the selected publishable project type into the generated manifest', async () => {
const projectPath = await createProject('mini_program');
const outputDirectory = await mkdtemp(join(tmpdir(), 'makelore-project-archives-'));
tempDirectories.push(outputDirectory);
const archivePath = join(outputDirectory, 'project.zip');
it.each(['mini_game', 'mini_program'] as const)(
'normalizes a legacy %s project to the canonical manifest type',
async (legacyProjectType) => {
const projectPath = await createProject();
await writeFile(
join(projectPath, '.makelore', 'project.json'),
JSON.stringify({
...createCodingProjectConfigV2('2026-08-09T00:00:00.000Z', 'interactive_ai_app'),
projectType: legacyProjectType,
}),
'utf8',
);
const outputDirectory = await mkdtemp(join(tmpdir(), 'makelore-project-archives-'));
tempDirectories.push(outputDirectory);
const archivePath = join(outputDirectory, 'project.zip');
const summary = await createStaticProjectPackage({ projectPath, archivePath });
const summary = await createStaticProjectPackage({ projectPath, archivePath });
expect(summary.manifest.project_type).toBe('mini_program');
expect(new AdmZip(archivePath).readAsText('niancode.yml')).toContain('project_type: mini_program\n');
});
expect(summary.manifest.project_type).toBe('interactive_ai_app');
expect(new AdmZip(archivePath).readAsText('niancode.yml'))
.toContain('project_type: interactive_ai_app\n');
},
);
it('rejects custom projects before validating Vite source files', async () => {
const projectPath = await mkdtemp(join(tmpdir(), 'makelore-custom-packager-'));
@@ -222,7 +234,7 @@ describe('project packager', () => {
archivePath: join(outputDirectory, 'project.zip'),
})).rejects.toMatchObject<ProjectPackageError>({
code: 'PROJECT_TYPE_UNPUBLISHABLE',
message: '自定义项目暂未配置发布方式,请新建小游戏或小程序项目',
message: '自定义项目暂未配置发布方式,请新建交互式 AI 应用项目',
});
});