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

@@ -85,6 +85,42 @@ describe('DevicePackageManager', () => {
expect(changed).toHaveBeenCalledTimes(1);
});
it('discloses executable Skill scripts and preserves their bundled resources', async () => {
const root = await temporaryRoot('script-skill');
const source = await looseSkill(root, 'script-skill');
await mkdir(path.join(source, 'scripts'), { recursive: true });
await mkdir(path.join(source, 'assets'), { recursive: true });
await mkdir(path.join(source, 'references'), { recursive: true });
await writeFile(path.join(source, 'scripts/scaffold.mjs'), 'console.log("scaffold");\n');
await writeFile(path.join(source, 'assets/template.txt'), 'template\n');
await writeFile(path.join(source, 'references/submission.md'), 'requirements\n');
const manager = new DevicePackageManager({
rootDir: path.join(root, 'store'),
now: () => NOW,
createId: () => 'plan-script-skill',
});
const preview = await manager.prepare(source, 'turn-prepare');
expect(preview).toMatchObject({
kind: 'skill-only',
includesExecutableCode: true,
extensionEntries: [],
});
expect(preview.warnings.join(' ')).toContain('Skill 脚本');
expect(preview.warnings.join(' ')).toContain('文件、网络和进程权限');
const installed = await manager.commit(preview.planId, true, 'turn-confirm');
expect(installed.packages[0]).toMatchObject({ confirmedExecutableCode: true });
const [resource] = (await manager.resolveEnabledResources()).skillEntries;
expect(await readFile(path.join(resource!.packageRoot, 'scripts/scaffold.mjs'), 'utf8'))
.toContain('scaffold');
expect(await readFile(path.join(resource!.packageRoot, 'assets/template.txt'), 'utf8'))
.toBe('template\n');
expect(await readFile(path.join(resource!.packageRoot, 'references/submission.md'), 'utf8'))
.toBe('requirements\n');
});
it('recognizes Pi extension and mixed package manifests and shows the desktop-permission warning', async () => {
const root = await temporaryRoot('pi-manifests');
const source = path.join(root, 'mixed-package');