fix: Windows spawn, prefab path, preview API, asset deps, error messages; add execute-context resource, tool descriptions, scene/script/asset tools
Bug fixes (verified against Cocos Creator 3.8.8): - diagnostics: shell:true for .cmd/.bat on Windows (T125/T126) - prefabs: duplicatePrefab target resolves under assets/ (T420) - cocos-project: add preview.open candidate for 3.8.8 (T444) - assets-advanced: detect directory assets in inspectAssetDependencies (T110) - scene: improve component-not-found errors with compilation hint (T429) Documentation improvements: - tool-registry: add enum and injected vars to execute_javascript, path format examples, asset ref limitation note - resources: add cocos://mcp/execute-context resource with variables, patterns, pitfalls New tools (core 37->38, full 101->110): - scene-management: create_scene, query_scene_state (core), copy_paste_node, rename_node, reparent_node - scripts: create_script with component/plain templates - prefabs: create_prefab - assets-advanced: batch_asset_ops, find_unused_assets
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { resolveProjectPath } = require('../path-safety');
|
||||
const { refreshAssets } = require('./files');
|
||||
|
||||
const PRIMITIVE_TYPES = new Set(['Number', 'String', 'Boolean']);
|
||||
|
||||
function generateComponentTemplate(className, properties = []) {
|
||||
const ccTypes = new Set(['Component']);
|
||||
const propLines = [];
|
||||
|
||||
for (const prop of properties) {
|
||||
const propName = String(prop.name || 'property').trim();
|
||||
const propType = String(prop.type || 'Number').trim();
|
||||
const defaultValue = prop.default !== undefined ? prop.default : getDefaultForType(propType);
|
||||
|
||||
if (!PRIMITIVE_TYPES.has(propType)) {
|
||||
ccTypes.add(propType);
|
||||
}
|
||||
|
||||
if (PRIMITIVE_TYPES.has(propType)) {
|
||||
propLines.push(` @property`);
|
||||
propLines.push(` ${propName}: ${typeToTs(propType)} = ${defaultValue};`);
|
||||
} else {
|
||||
propLines.push(` @property(${propType})`);
|
||||
propLines.push(` ${propName}: ${propType} | null = null;`);
|
||||
}
|
||||
}
|
||||
|
||||
const importTypes = Array.from(ccTypes).join(', ');
|
||||
const propsBlock = propLines.length > 0
|
||||
? propLines.join('\n')
|
||||
: ' // Add properties here';
|
||||
|
||||
return `import { _decorator, ${importTypes} } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('${className}')
|
||||
export class ${className} extends Component {
|
||||
${propsBlock}
|
||||
|
||||
start() {
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function generatePlainTemplate(className) {
|
||||
return `export class ${className} {
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function getDefaultForType(type) {
|
||||
switch (type) {
|
||||
case 'Number': return '0';
|
||||
case 'String': return "''";
|
||||
case 'Boolean': return 'false';
|
||||
default: return 'null';
|
||||
}
|
||||
}
|
||||
|
||||
function typeToTs(type) {
|
||||
switch (type) {
|
||||
case 'Number': return 'number';
|
||||
case 'String': return 'string';
|
||||
case 'Boolean': return 'boolean';
|
||||
default: return type;
|
||||
}
|
||||
}
|
||||
|
||||
function createScriptTools({ createSchema, getRuntimeContext }) {
|
||||
return [
|
||||
{
|
||||
name: 'create_script',
|
||||
profile: 'full',
|
||||
description: '[core] Create a new TypeScript component script with a standard Cocos template.',
|
||||
inputSchema: createSchema(
|
||||
{
|
||||
scriptName: { type: 'string', description: 'Class name in PascalCase, e.g. PlayerController.' },
|
||||
savePath: { type: 'string', description: 'Asset-db URL or project path, e.g. db://assets/scripts/PlayerController.ts.' },
|
||||
template: {
|
||||
type: 'string',
|
||||
enum: ['component', 'plain'],
|
||||
description: 'Script template. Default: component.',
|
||||
},
|
||||
properties: {
|
||||
type: 'array',
|
||||
items: { type: 'object' },
|
||||
description: 'Optional @property declarations. Each item: { name, type, default }. Type can be Number, String, Boolean, or a CC class name like Label, Sprite, Node.',
|
||||
},
|
||||
},
|
||||
['scriptName', 'savePath']
|
||||
),
|
||||
handler: async (args) => {
|
||||
const scriptName = String(args.scriptName || '').trim();
|
||||
let savePath = String(args.savePath || '').trim();
|
||||
if (!scriptName) {
|
||||
throw new Error('scriptName is required.');
|
||||
}
|
||||
if (!savePath) {
|
||||
throw new Error('savePath is required.');
|
||||
}
|
||||
if (!savePath.endsWith('.ts')) {
|
||||
throw new Error('savePath must end with .ts');
|
||||
}
|
||||
if (savePath.startsWith('db://')) {
|
||||
savePath = savePath.slice('db://'.length);
|
||||
}
|
||||
|
||||
const templateType = args.template === 'plain' ? 'plain' : 'component';
|
||||
const properties = Array.isArray(args.properties) ? args.properties : [];
|
||||
|
||||
const content = templateType === 'plain'
|
||||
? generatePlainTemplate(scriptName)
|
||||
: generateComponentTemplate(scriptName, properties);
|
||||
|
||||
const { projectPath } = getRuntimeContext();
|
||||
const fullPath = resolveProjectPath(projectPath, savePath);
|
||||
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
||||
fs.writeFileSync(fullPath, content, 'utf8');
|
||||
|
||||
const refreshMessage = await refreshAssets(projectPath, fullPath);
|
||||
|
||||
return {
|
||||
created: true,
|
||||
scriptName,
|
||||
savePath,
|
||||
template: templateType,
|
||||
className: scriptName,
|
||||
contentLength: content.length,
|
||||
refreshMessage,
|
||||
};
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createScriptTools,
|
||||
generateComponentTemplate,
|
||||
generatePlainTemplate,
|
||||
};
|
||||
Reference in New Issue
Block a user