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

@@ -1,6 +1,6 @@
// @vitest-environment node
import { copyFile, mkdir, mkdtemp, realpath, rename, rm, stat } from 'node:fs/promises';
import { copyFile, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
@@ -45,6 +45,45 @@ function makeStore(localId = 'local-project-id') {
}
describe('coding project durable identity', () => {
it.each(['interactive_ai_app', 'custom'] as const)(
'creates only project-owned metadata for %s projects',
async (projectType) => {
const projectPath = await makeRoot();
const projects = new CodingProjectService(makeStore(), {
createProjectId: () => PROJECT_ID,
});
const created = await projects.createProject({
projectPath,
projectType,
identity: { kind: 'create' },
});
expect(created.config.projectType).toBe(projectType);
expect((await readdir(projectPath)).sort()).toEqual(['.makelore', 'knowledge']);
},
);
it.each(['mini_game', 'mini_program'] as const)(
'reads legacy %s metadata as the canonical interactive type without rewriting it',
async (legacyProjectType) => {
const projectPath = await makeRoot();
await mkdir(path.join(projectPath, '.makelore'), { recursive: true });
const configPath = path.join(projectPath, '.makelore', 'project.json');
const rawConfig = `${JSON.stringify({
...createCodingProjectConfigV2(CREATED, 'interactive_ai_app'),
projectType: legacyProjectType,
}, null, 2)}\n`;
await writeFile(configPath, rawConfig, 'utf8');
await expect(readCodingProjectConfigV2(projectPath)).resolves.toMatchObject({
status: 'valid',
config: { projectType: 'interactive_ai_app' },
});
await expect(readFile(configPath, 'utf8')).resolves.toBe(rawConfig);
},
);
it('preserves canonical identity and rejects noncanonical values without backfill', () => {
const initialized = createCodingProjectConfigV2(CREATED, 'custom', PROJECT_ID);
expect(initialized.projectId).toBe(PROJECT_ID);