feat(plugins): bundle project scaffold marketplace plugin
This commit is contained in:
1 parent
2207806038
commit
ddb678b46f
23 files changed
+218
-86
No files matched your search
@@ -0,0 +1,52 @@
|
||||
# Task: Publish scaffold Skill through official Marketplace
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20260904-marketplace-scaffold-client-6c4e8a21
|
||||
- Mode: Feature
|
||||
- Branch: codex/20260904-integrate-project-scaffold-b6d3e8a1-integrate-project-scaffold
|
||||
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260904-integrate-project-scaffold-b6d3e8a1
|
||||
- Base commit: 22078060386e38dd5f12af4717dc6c6e3378c2df
|
||||
- Owner: codex
|
||||
- Status: Ready for Integration
|
||||
|
||||
## Scope
|
||||
|
||||
- Publish `makelore.project-scaffold` as a code-owned bundled Marketplace plugin.
|
||||
- Move the existing project-scaffold Skill runtime into `resources/coding-plugins/project-scaffold` so it ships with the signed MakeLore application.
|
||||
- Register the bundled release in the client plugin registry and preserve the acquire, project-enable, and Agent-assignment lifecycle.
|
||||
- Cover the official-script boundary with focused manifest, resolver, scaffold, and artifact-package tests.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- The Skill owns creation of the fixed interactive AI application starter and static publish-readiness checks; it does not build, upload, submit, or approve a project.
|
||||
- Official executable resources may be loaded only from the code-owned bundled plugin root. Downloadable Marketplace artifacts keep the existing text/image-only P0 asset policy, including rejection of `.mjs`.
|
||||
- Keep one runtime copy of the Skill rather than a Device Package copy plus a bundled copy.
|
||||
- Use plugin ID `makelore.project-scaffold`, Skill ID `makelore-project-scaffold`, release version `1.0.0`, and minimum client version `2.0.0`.
|
||||
- Preserve non-overwrite scaffolding and explicit user invocation of the Skill.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Moved the existing scaffold Skill, templates, release reference, and `.mjs` runner into the signed client resource tree at `resources/coding-plugins/project-scaffold`; no second runtime copy remains.
|
||||
- Added schema-v2 MakeLore capability metadata and retained the portable `.codex-plugin/plugin.json`, both identifying `makelore.project-scaffold` version `1.0.0` and Skill `makelore-project-scaffold`.
|
||||
- Registered the plugin as a fixed, code-owned, `skill_only` bundled release with ID `00000000-0000-4000-8000-000000000303` and the existing acquire, project-enable, and Agent-assignment lifecycle.
|
||||
- Narrowed executable-script authority to the fixed bundled root. Downloaded Marketplace ZIPs still reject `.mjs`; they do not install this plugin's runtime.
|
||||
- Preserved the existing non-overwrite scaffold behavior, legacy `mini_game` / `mini_program` read compatibility, canonical `interactive_ai_app` output, and static publication-readiness guidance.
|
||||
|
||||
## Verification
|
||||
|
||||
- `pnpm exec vitest run tests/unit/coding-plugin-manifest.test.ts tests/unit/coding-plugin-marketplace-client.test.ts tests/unit/project-scaffold-plugin.test.ts --maxWorkers=2`: 54 passed.
|
||||
- `node --test tests/project-scaffold/scaffold.test.mjs`: 12 passed.
|
||||
- `pnpm exec vitest run tests/unit/plugin-workspace-model.test.ts tests/unit/coding-plugin-effective-resolver.test.ts tests/unit/coding-composition-paths.test.ts --maxWorkers=2`: 33 passed.
|
||||
- ESLint on the changed TypeScript files: passed.
|
||||
- `pnpm run typecheck`: passed.
|
||||
- Plugin manifest validation: passed. Skill validation: passed with UTF-8 mode and the repository Python environment that provides PyYAML.
|
||||
- `pnpm test`: 224 test files and 1,879 tests passed, 2 skipped; one unrelated real-process timing assertion exceeded two seconds under full parallel load. Re-running `tests/unit/pi-agent-server-process-real.test.ts` alone with one worker passed all 6 tests, so no unrelated timeout change was made.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Run a packaged-app smoke test before the first public client release to prove the signed resource layout and `ELECTRON_RUN_AS_NODE` execution path outside the repository environment.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- Update ADR-008 after integration: project scaffolding is distributed as a code-owned bundled Marketplace plugin, while downloadable artifacts remain non-executable in P0.
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
CODE_OWNED_PLUGIN_PERMISSION_IDS,
|
||||
GAME_RESOURCE_BUNDLED_RELEASE_ID,
|
||||
GAME_RESOURCE_PLUGIN_ID,
|
||||
PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
DATA_SERVICE_CAPABILITY_IDS,
|
||||
DATA_SERVICE_OPERATION_DEFINITIONS,
|
||||
DATA_SERVICE_PLUGIN_ID,
|
||||
@@ -35,6 +37,7 @@ import {
|
||||
export const BUNDLED_CODING_PLUGIN_ROOTS = Object.freeze([
|
||||
'data-service',
|
||||
'game-resource',
|
||||
'project-scaffold',
|
||||
] as const);
|
||||
|
||||
const BUNDLED_CODING_PLUGIN_METADATA = Object.freeze({
|
||||
@@ -52,6 +55,13 @@ const BUNDLED_CODING_PLUGIN_METADATA = Object.freeze({
|
||||
releaseId: GAME_RESOURCE_BUNDLED_RELEASE_ID,
|
||||
bundledV2: true,
|
||||
}),
|
||||
'project-scaffold': Object.freeze({
|
||||
pluginId: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
runtimeKind: 'skill_only' as const,
|
||||
acquisitionMode: 'user_acquired' as const,
|
||||
releaseId: PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
bundledV2: true,
|
||||
}),
|
||||
});
|
||||
|
||||
const CAPABILITY_MANIFEST_RELATIVE_PATH = 'com.makelore/capability.json';
|
||||
@@ -722,8 +732,11 @@ function trustedMetadata(
|
||||
if (options.bundledV2 && !bundledV2) {
|
||||
fail(filePath, 'trusted metadata.bundledV2', 'is valid only for schema 2 packages');
|
||||
}
|
||||
if (bundledV2 && (runtimeKind !== 'platform_hosted' || releaseId === null)) {
|
||||
fail(filePath, 'trusted metadata.bundledV2', 'requires a hosted runtime and fixed Release ID');
|
||||
if (bundledV2 && (runtimeKind !== 'platform_hosted' && runtimeKind !== 'skill_only')) {
|
||||
fail(filePath, 'trusted metadata.bundledV2', 'requires a distributed runtime');
|
||||
}
|
||||
if (bundledV2 && releaseId === null) {
|
||||
fail(filePath, 'trusted metadata.bundledV2', 'requires a fixed Release ID');
|
||||
}
|
||||
const defaultProvenance: CodingPluginPackageProvenance = {
|
||||
source: schemaVersion === 1 || bundledV2 ? 'bundled' : 'marketplace',
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "makelore-project-scaffold",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "node --test tests/scaffold.test.mjs"
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "makelore-project-scaffold",
|
||||
"version": "0.1.0",
|
||||
"name": "makelore.project-scaffold",
|
||||
"version": "1.0.0",
|
||||
"description": "为 MakeLore 交互式 AI 应用生成工程骨架并检查一键提交要求。",
|
||||
"author": {
|
||||
"name": "MakeLore Team"
|
||||
@@ -10,7 +10,7 @@
|
||||
"displayName": "MakeLore 项目初始化与发布检查",
|
||||
"shortDescription": "初始化工程骨架并检查发布要求。",
|
||||
"longDescription": "读取当前 MakeLore 项目类型,生成固定、可构建的交互式 AI 应用工程骨架,并按平台的一键提交合同区分必需项、直接阻断项、打包排除项和运行期检查。",
|
||||
"developerName": "MakeLore Team",
|
||||
"developerName": "MakeLore",
|
||||
"category": "Developer Tools",
|
||||
"capabilities": ["Write"],
|
||||
"defaultPrompt": "使用 $makelore-project-scaffold 初始化当前 MakeLore 交互式 AI 应用项目,并检查是否满足一键提交要求。"
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"pluginId": "makelore.project-scaffold",
|
||||
"contractVersion": 1,
|
||||
"scope": "project",
|
||||
"runtime": {
|
||||
"kind": "skill_only"
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"id": "makelore-project-scaffold",
|
||||
"entry": "../skills/makelore-project-scaffold/SKILL.md",
|
||||
"grants": []
|
||||
}
|
||||
],
|
||||
"tools": []
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "makelore.project-scaffold",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
||||
"name": "makelore.project-scaffold",
|
||||
"version": "1.0.0",
|
||||
"description": "为 MakeLore 交互式 AI 应用生成固定工程骨架并检查发布与审批提交要求。",
|
||||
"author": {
|
||||
"name": "MakeLore"
|
||||
},
|
||||
"extensions": {
|
||||
"com.makelore": {
|
||||
"capabilityManifest": "./com.makelore/capability.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -103,6 +103,8 @@ export const DATA_SERVICE_PREVIEW_RUNTIME_SURFACE = 'data-service-v1' as const;
|
||||
|
||||
export const GAME_RESOURCE_PLUGIN_ID = 'makelore.game-resource' as const;
|
||||
export const GAME_RESOURCE_BUNDLED_RELEASE_ID = '00000000-0000-4000-8000-000000000105' as const;
|
||||
export const PROJECT_SCAFFOLD_PLUGIN_ID = 'makelore.project-scaffold' as const;
|
||||
export const PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID = '00000000-0000-4000-8000-000000000303' as const;
|
||||
|
||||
export const CODE_OWNED_OPTIONAL_BUNDLED_RELEASES = Object.freeze({
|
||||
[GAME_RESOURCE_PLUGIN_ID]: Object.freeze({
|
||||
@@ -110,6 +112,11 @@ export const CODE_OWNED_OPTIONAL_BUNDLED_RELEASES = Object.freeze({
|
||||
version: '1.0.0',
|
||||
channel: 'stable' as const,
|
||||
}),
|
||||
[PROJECT_SCAFFOLD_PLUGIN_ID]: Object.freeze({
|
||||
releaseId: PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
version: '1.0.0',
|
||||
channel: 'stable' as const,
|
||||
}),
|
||||
});
|
||||
|
||||
export function isCodeOwnedOptionalBundledPluginId(pluginId: string): boolean {
|
||||
|
||||
+4
-1
@@ -17,7 +17,10 @@ import { spawnSync } from 'node:child_process';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import test from 'node:test';
|
||||
|
||||
const pluginRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
||||
const pluginRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
'../../resources/coding-plugins/project-scaffold',
|
||||
);
|
||||
const scaffoldScript = path.join(
|
||||
pluginRoot,
|
||||
'skills',
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
|
||||
const PACKAGE_ROOT = path.resolve('resources/coding-plugins/data-service');
|
||||
const GAME_RESOURCE_ROOT = path.resolve('resources/coding-plugins/game-resource');
|
||||
const PROJECT_SCAFFOLD_ROOT = path.resolve('resources/coding-plugins/project-scaffold');
|
||||
|
||||
async function packageManifests(): Promise<{ root: Record<string, unknown>; capability: Record<string, unknown> }> {
|
||||
return {
|
||||
@@ -33,12 +34,17 @@ describe('bundled coding plugin manifests', () => {
|
||||
it('loads the fixed Data Service package and immutable declarations', async () => {
|
||||
const definitions = await loadBundledCodingPluginDefinitions(path.resolve('resources/coding-plugins'));
|
||||
const startupDefinitions = loadBundledCodingPluginDefinitionsSync(path.resolve('resources/coding-plugins'));
|
||||
expect(BUNDLED_CODING_PLUGIN_ROOTS).toEqual(['data-service', 'game-resource']);
|
||||
expect(BUNDLED_CODING_PLUGIN_ROOTS).toEqual([
|
||||
'data-service',
|
||||
'game-resource',
|
||||
'project-scaffold',
|
||||
]);
|
||||
expect(resolveBundledCodingPluginRootPaths(path.resolve('resources/coding-plugins'))).toEqual([
|
||||
PACKAGE_ROOT,
|
||||
GAME_RESOURCE_ROOT,
|
||||
PROJECT_SCAFFOLD_ROOT,
|
||||
]);
|
||||
expect(definitions).toHaveLength(2);
|
||||
expect(definitions).toHaveLength(3);
|
||||
expect(definitions[0]).toMatchObject({
|
||||
id: 'makelore.data-service',
|
||||
adapterId: 'data-service',
|
||||
@@ -54,6 +60,17 @@ describe('bundled coding plugin manifests', () => {
|
||||
provenance: { source: 'bundled', packageRoot: 'game-resource' },
|
||||
skills: [{ id: 'game-resource', entryPath: 'skills/game-resource/SKILL.md' }],
|
||||
},
|
||||
{
|
||||
id: 'makelore.project-scaffold', version: '1.0.0', runtimeKind: 'skill_only',
|
||||
acquisitionMode: 'user_acquired', releaseId: '00000000-0000-4000-8000-000000000303',
|
||||
provenance: { source: 'bundled', packageRoot: 'project-scaffold' },
|
||||
skills: [{
|
||||
id: 'makelore-project-scaffold',
|
||||
entryPath: 'skills/makelore-project-scaffold/SKILL.md',
|
||||
grants: [],
|
||||
}],
|
||||
tools: [],
|
||||
},
|
||||
]);
|
||||
expect(startupDefinitions).toEqual(definitions);
|
||||
expect(Object.isFrozen(startupDefinitions)).toBe(true);
|
||||
@@ -128,6 +145,7 @@ describe('bundled coding plugin manifests', () => {
|
||||
expect(resolveBundledCodingPluginRootPaths(path.resolve('tmp'))).toEqual([
|
||||
path.resolve('tmp/data-service'),
|
||||
path.resolve('tmp/game-resource'),
|
||||
path.resolve('tmp/project-scaffold'),
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -1419,8 +1419,8 @@ describe('PluginPackageStore', () => {
|
||||
});
|
||||
const cases = [
|
||||
{
|
||||
name: 'unsupported asset extension',
|
||||
archive: buildSkillOnlyArchive({ 'skills/example-skill/assets/run.exe': Buffer.from('not executable') }),
|
||||
name: 'executable Skill script in a downloadable artifact',
|
||||
archive: buildSkillOnlyArchive({ 'skills/example-skill/scripts/run.mjs': Buffer.from('export {};') }),
|
||||
},
|
||||
{
|
||||
name: 'non-UTF-8 Skill text',
|
||||
|
||||
@@ -261,40 +261,45 @@ describe('buildPluginWorkspaceProjection', () => {
|
||||
});
|
||||
|
||||
it('keeps bundled and downloadable official command sets inside their delivery boundaries', () => {
|
||||
const bundledCatalog = {
|
||||
...catalog.items[0]!,
|
||||
pluginId: 'makelore.game-resource',
|
||||
title: 'Game Resource',
|
||||
runtimeKind: 'bundled_typed' as const,
|
||||
};
|
||||
const bundledLibrary = {
|
||||
...library.items[0]!,
|
||||
pluginId: bundledCatalog.pluginId,
|
||||
title: bundledCatalog.title,
|
||||
};
|
||||
const bundledProject = {
|
||||
...project,
|
||||
items: [{
|
||||
...project.items[0]!,
|
||||
id: bundledCatalog.pluginId,
|
||||
displayName: bundledCatalog.title,
|
||||
enabled: false,
|
||||
state: 'disabled' as const,
|
||||
}],
|
||||
};
|
||||
for (const [pluginId, title, runtimeKind] of [
|
||||
['makelore.game-resource', 'Game Resource', 'platform_hosted'],
|
||||
['makelore.project-scaffold', 'Project Scaffold', 'skill_only'],
|
||||
] as const) {
|
||||
const bundledCatalog = {
|
||||
...catalog.items[0]!,
|
||||
pluginId,
|
||||
title,
|
||||
runtimeKind,
|
||||
};
|
||||
const bundledLibrary = {
|
||||
...library.items[0]!,
|
||||
pluginId: bundledCatalog.pluginId,
|
||||
title: bundledCatalog.title,
|
||||
};
|
||||
const bundledProject = {
|
||||
...project,
|
||||
items: [{
|
||||
...project.items[0]!,
|
||||
id: bundledCatalog.pluginId,
|
||||
displayName: bundledCatalog.title,
|
||||
enabled: false,
|
||||
state: 'disabled' as const,
|
||||
}],
|
||||
};
|
||||
|
||||
expect(commandKinds({
|
||||
catalog: { ...catalog, items: [bundledCatalog] },
|
||||
library: { ...library, items: [] },
|
||||
marketplaceInstallations: {},
|
||||
project: null,
|
||||
})).toEqual(['acquire']);
|
||||
expect(commandKinds({
|
||||
catalog: { ...catalog, items: [bundledCatalog] },
|
||||
library: { ...library, items: [bundledLibrary] },
|
||||
marketplaceInstallations: {},
|
||||
project: bundledProject,
|
||||
})).toEqual(['remove_from_library', 'enable_project', 'open_agent_assignment']);
|
||||
expect(commandKinds({
|
||||
catalog: { ...catalog, items: [bundledCatalog] },
|
||||
library: { ...library, items: [] },
|
||||
marketplaceInstallations: {},
|
||||
project: null,
|
||||
})).toEqual(['acquire']);
|
||||
expect(commandKinds({
|
||||
catalog: { ...catalog, items: [bundledCatalog] },
|
||||
library: { ...library, items: [bundledLibrary] },
|
||||
marketplaceInstallations: {},
|
||||
project: bundledProject,
|
||||
})).toEqual(['remove_from_library', 'enable_project', 'open_agent_assignment']);
|
||||
}
|
||||
|
||||
expect(commandKinds({ marketplaceInstallations: {} })).toEqual([
|
||||
'remove_from_library',
|
||||
|
||||
@@ -3,18 +3,22 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import {
|
||||
devicePackageKind,
|
||||
inspectDevicePackage,
|
||||
} from '@electron/coding-packages/device-package-format';
|
||||
import { DevicePackageManager } from '@electron/coding-packages/device-package-manager';
|
||||
import { loadBundledCodingPluginDefinitions } from '@electron/coding-plugins/manifest';
|
||||
import { createCodingProjectMetadata } from '@electron/coding-projects/project-config';
|
||||
import { createStaticProjectPackage } from '@electron/services/project-packager';
|
||||
import type { ProjectType } from '../../shared/project-config';
|
||||
import {
|
||||
PROJECT_SCAFFOLD_BUNDLED_RELEASE_ID,
|
||||
PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
isCodeOwnedOptionalBundledPluginId,
|
||||
} from '../../shared/coding-plugins';
|
||||
|
||||
const pluginRoot = resolve('plugins/makelore-project-scaffold');
|
||||
const pluginRoot = resolve('resources/coding-plugins/project-scaffold');
|
||||
const scaffoldScript = join(
|
||||
pluginRoot,
|
||||
'skills',
|
||||
@@ -42,23 +46,30 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe('MakeLore project scaffold plugin', () => {
|
||||
it('is discovered as a skill-only package with executable Skill scripts', async () => {
|
||||
it('loads as a fixed code-owned bundled Skill while retaining portable plugin metadata', async () => {
|
||||
const packageJson = JSON.parse(await readFile(join(pluginRoot, 'package.json'), 'utf8'));
|
||||
const pluginManifest = JSON.parse(await readFile(
|
||||
const portableManifest = JSON.parse(await readFile(
|
||||
join(pluginRoot, '.codex-plugin', 'plugin.json'),
|
||||
'utf8',
|
||||
));
|
||||
const marketplaceManifest = JSON.parse(await readFile(join(pluginRoot, 'plugin.json'), 'utf8'));
|
||||
const inspected = await inspectDevicePackage(pluginRoot);
|
||||
const definitions = await loadBundledCodingPluginDefinitions(resolve('resources/coding-plugins'));
|
||||
const definition = definitions.find(({ id }) => id === PROJECT_SCAFFOLD_PLUGIN_ID);
|
||||
|
||||
expect({ name: packageJson.name, version: packageJson.version }).toEqual({
|
||||
name: pluginManifest.name,
|
||||
version: pluginManifest.version,
|
||||
name: marketplaceManifest.name,
|
||||
version: marketplaceManifest.version,
|
||||
});
|
||||
expect(pluginManifest.skills).toBe('./skills/');
|
||||
expect({ name: portableManifest.name, version: portableManifest.version }).toEqual({
|
||||
name: marketplaceManifest.name,
|
||||
version: marketplaceManifest.version,
|
||||
});
|
||||
expect(portableManifest.skills).toBe('./skills/');
|
||||
expect(devicePackageKind(inspected)).toBe('skill-only');
|
||||
expect(inspected).toMatchObject({
|
||||
packageId: 'makelore-project-scaffold',
|
||||
resolvedVersion: '0.1.0',
|
||||
packageId: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
resolvedVersion: '1.0.0',
|
||||
extensionEntries: [],
|
||||
hasSkillScripts: true,
|
||||
ignoredLifecycleScripts: [],
|
||||
@@ -67,30 +78,25 @@ describe('MakeLore project scaffold plugin', () => {
|
||||
entryPath: 'skills/makelore-project-scaffold/SKILL.md',
|
||||
}],
|
||||
});
|
||||
expect(definition).toMatchObject({
|
||||
id: PROJECT_SCAFFOLD_PLUGIN_ID,
|
||||
version: '1.0.0',
|
||||
runtimeKind: 'skill_only',
|
||||
acquisitionMode: 'user_acquired',
|
||||
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: [],
|
||||
}],
|
||||
tools: [],
|
||||
});
|
||||
expect(isCodeOwnedOptionalBundledPluginId(PROJECT_SCAFFOLD_PLUGIN_ID)).toBe(true);
|
||||
});
|
||||
|
||||
it('prepares, confirms, and installs the real package with every Skill resource', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'makelore-scaffold-device-package-'));
|
||||
tempDirectories.push(root);
|
||||
const manager = new DevicePackageManager({
|
||||
rootDir: join(root, 'store'),
|
||||
now: () => Date.parse('2026-09-04T00:00:00.000Z'),
|
||||
createId: () => 'project-scaffold-plan',
|
||||
});
|
||||
|
||||
const preview = await manager.prepare(pluginRoot, 'prepare-turn');
|
||||
expect(preview).toMatchObject({
|
||||
kind: 'skill-only',
|
||||
includesExecutableCode: true,
|
||||
skillEntries: [{ id: 'makelore-project-scaffold' }],
|
||||
extensionEntries: [],
|
||||
});
|
||||
expect(preview.warnings.join(' ')).toContain('可执行 Skill 脚本');
|
||||
await manager.commit(preview.planId, true, 'confirm-turn');
|
||||
|
||||
const [resource] = (await manager.resolveEnabledResources()).skillEntries;
|
||||
expect(resource?.id).toBe('makelore-project-scaffold');
|
||||
const skillRoot = dirname(join(resource!.packageRoot, resource!.entryPath));
|
||||
it('ships the executable scaffold and every Skill resource in the bundled root', async () => {
|
||||
const skillRoot = join(pluginRoot, 'skills', 'makelore-project-scaffold');
|
||||
expect(await readFile(join(skillRoot, 'scripts', 'scaffold.mjs'), 'utf8'))
|
||||
.toContain('schemaVersion: 1');
|
||||
expect(await readFile(join(skillRoot, 'assets', 'templates', 'common', 'package-lock.json'), 'utf8'))
|
||||
@@ -164,7 +170,7 @@ describe('MakeLore project scaffold plugin', () => {
|
||||
});
|
||||
|
||||
it('creates an interactive AI application accepted by the existing source packager', async () => {
|
||||
const projectType: ProjectType = 'interactive_ai_app';
|
||||
const projectType = 'interactive_ai_app' as const;
|
||||
const projectPath = await mkdtemp(join(tmpdir(), `makelore-${projectType}-`));
|
||||
const outputPath = await mkdtemp(join(tmpdir(), 'makelore-scaffold-package-'));
|
||||
tempDirectories.push(projectPath, outputPath);
|
||||
|
||||
Reference in new issue
Block a user