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:
mingyuansi
2026-06-30 21:53:06 +08:00
parent a9b539ca9c
commit b65a4f22c3
21 changed files with 5273 additions and 53 deletions
+148
View File
@@ -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,
};