@kernel.chat/kbot 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gamedev.d.ts","sourceRoot":"","sources":["../../src/tools/gamedev.ts"],"names":[],"mappings":"AA2EA,wBAAgB,oBAAoB,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"gamedev.d.ts","sourceRoot":"","sources":["../../src/tools/gamedev.ts"],"names":[],"mappings":"AA2EA,wBAAgB,oBAAoB,IAAI,IAAI,CA0qV3C"}
|
package/dist/tools/gamedev.js
CHANGED
|
@@ -77,6 +77,7 @@ function seededRng(seed) {
|
|
|
77
77
|
}
|
|
78
78
|
// ── Registration ─────────────────────────────────────────────────────
|
|
79
79
|
export function registerGamedevTools() {
|
|
80
|
+
const htmlSafe = (s) => s.replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c] ?? c));
|
|
80
81
|
// ── Tool 1: scaffold_game ──────────────────────────────────────────
|
|
81
82
|
/** Per-engine scaffold file generators. Each returns an array of [relativePath, content] */
|
|
82
83
|
const scaffoldFiles = {
|
|
@@ -125,7 +126,7 @@ export function registerGamedevTools() {
|
|
|
125
126
|
const is3d = tpl === '3d';
|
|
126
127
|
return [
|
|
127
128
|
['package.json', JSON.stringify({ name: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'), version: '0.1.0', private: true, scripts: { dev: 'vite', build: 'vite build' }, dependencies: { phaser: '^3.80.0' }, devDependencies: { vite: '^5.0.0', typescript: '^5.4.0' } }, null, 2)],
|
|
128
|
-
['index.html', `<!DOCTYPE html>\n<html><head><title>${name}</title></head>\n<body><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
129
|
+
['index.html', `<!DOCTYPE html>\n<html><head><title>${htmlSafe(name)}</title></head>\n<body><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
129
130
|
['src/main.ts', `import Phaser from 'phaser'\nimport { MainScene } from './scenes/MainScene'\n\nnew Phaser.Game({\n type: Phaser.AUTO,\n width: 800,\n height: 600,\n physics: { default: 'arcade', arcade: { gravity: { x: 0, y: 300 }, debug: false } },\n scene: [MainScene],\n})\n`],
|
|
130
131
|
['src/scenes/MainScene.ts', `import Phaser from 'phaser'\n\nexport class MainScene extends Phaser.Scene {\n constructor() { super('MainScene') }\n preload() { }\n create() {\n this.add.text(400, 300, '${name}', { fontSize: '32px', color: '#fff' }).setOrigin(0.5)\n }\n}\n`],
|
|
131
132
|
['tsconfig.json', JSON.stringify({ compilerOptions: { target: 'ES2020', module: 'ESNext', moduleResolution: 'bundler', strict: true, esModuleInterop: true }, include: ['src'] }, null, 2)],
|
|
@@ -136,7 +137,7 @@ export function registerGamedevTools() {
|
|
|
136
137
|
const is3d = true; // Three.js is always 3D
|
|
137
138
|
return [
|
|
138
139
|
['package.json', JSON.stringify({ name: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'), version: '0.1.0', private: true, scripts: { dev: 'vite', build: 'vite build' }, dependencies: { three: '^0.170.0' }, devDependencies: { '@types/three': '^0.170.0', vite: '^5.0.0', typescript: '^5.4.0' } }, null, 2)],
|
|
139
|
-
['index.html', `<!DOCTYPE html>\n<html><head><title>${name}</title><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head>\n<body><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
140
|
+
['index.html', `<!DOCTYPE html>\n<html><head><title>${htmlSafe(name)}</title><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head>\n<body><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
140
141
|
['src/main.ts', `import * as THREE from 'three'\n\nconst scene = new THREE.Scene()\nconst camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)\nconst renderer = new THREE.WebGLRenderer({ antialias: true })\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.domElement)\n\nconst geometry = new THREE.BoxGeometry()\nconst material = new THREE.MeshStandardMaterial({ color: 0x4488ff })\nconst cube = new THREE.Mesh(geometry, material)\nscene.add(cube)\n\nscene.add(new THREE.AmbientLight(0x404040))\nconst light = new THREE.DirectionalLight(0xffffff, 1)\nlight.position.set(5, 5, 5)\nscene.add(light)\n\ncamera.position.z = 5\n\nfunction animate() {\n requestAnimationFrame(animate)\n cube.rotation.x += 0.01\n cube.rotation.y += 0.01\n renderer.render(scene, camera)\n}\nanimate()\n\nwindow.addEventListener('resize', () => {\n camera.aspect = window.innerWidth / window.innerHeight\n camera.updateProjectionMatrix()\n renderer.setSize(window.innerWidth, window.innerHeight)\n})\n`],
|
|
141
142
|
['tsconfig.json', JSON.stringify({ compilerOptions: { target: 'ES2020', module: 'ESNext', moduleResolution: 'bundler', strict: true, esModuleInterop: true }, include: ['src'] }, null, 2)],
|
|
142
143
|
['.gitignore', 'node_modules/\ndist/\n'],
|
|
@@ -145,7 +146,7 @@ export function registerGamedevTools() {
|
|
|
145
146
|
playcanvas(name, tpl) {
|
|
146
147
|
return [
|
|
147
148
|
['package.json', JSON.stringify({ name: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'), version: '0.1.0', private: true, scripts: { dev: 'vite', build: 'vite build' }, dependencies: { playcanvas: '^2.1.0' }, devDependencies: { vite: '^5.0.0', typescript: '^5.4.0' } }, null, 2)],
|
|
148
|
-
['index.html', `<!DOCTYPE html>\n<html><head><title>${name}</title><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head>\n<body><canvas id="app"></canvas><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
149
|
+
['index.html', `<!DOCTYPE html>\n<html><head><title>${htmlSafe(name)}</title><style>body{margin:0;overflow:hidden}canvas{display:block}</style></head>\n<body><canvas id="app"></canvas><script type="module" src="/src/main.ts"></script></body></html>\n`],
|
|
149
150
|
['src/main.ts', `import * as pc from 'playcanvas'\n\nconst canvas = document.getElementById('app') as HTMLCanvasElement\nconst app = new pc.Application(canvas, {})\napp.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW)\napp.setCanvasResolution(pc.RESOLUTION_AUTO)\n\nconst camera = new pc.Entity('camera')\ncamera.addComponent('camera', { clearColor: new pc.Color(0.1, 0.1, 0.15) })\ncamera.setPosition(0, 2, 5)\ncamera.lookAt(pc.Vec3.ZERO)\napp.root.addChild(camera)\n\nconst light = new pc.Entity('light')\nlight.addComponent('light')\nlight.setEulerAngles(45, 30, 0)\napp.root.addChild(light)\n\nconst box = new pc.Entity('box')\nbox.addComponent('render', { type: 'box' })\napp.root.addChild(box)\n\napp.on('update', (dt: number) => { box.rotate(10 * dt, 20 * dt, 0) })\napp.start()\n`],
|
|
150
151
|
['tsconfig.json', JSON.stringify({ compilerOptions: { target: 'ES2020', module: 'ESNext', moduleResolution: 'bundler', strict: true, esModuleInterop: true }, include: ['src'] }, null, 2)],
|
|
151
152
|
['.gitignore', 'node_modules/\ndist/\n'],
|
|
@@ -974,8 +975,8 @@ void fragment() {
|
|
|
974
975
|
},
|
|
975
976
|
sphere(p) {
|
|
976
977
|
const radius = p.radius ?? 0.5;
|
|
977
|
-
const rings = p.rings ?? 16;
|
|
978
|
-
const segments = p.segments ?? 32;
|
|
978
|
+
const rings = Math.min(p.rings ?? 16, 256);
|
|
979
|
+
const segments = Math.min(p.segments ?? 32, 512);
|
|
979
980
|
const verts = [];
|
|
980
981
|
const norms = [];
|
|
981
982
|
const uvs = [];
|
|
@@ -1466,7 +1467,7 @@ void fragment() {
|
|
|
1466
1467
|
const atlasW = nextPow2(atlasSize.width);
|
|
1467
1468
|
const atlasH = nextPow2(atlasSize.height);
|
|
1468
1469
|
// Composite atlas using ImageMagick
|
|
1469
|
-
ensureDir(outputImage);
|
|
1470
|
+
ensureDir(dirname(outputImage));
|
|
1470
1471
|
const compositeArgs = [
|
|
1471
1472
|
'-size', `${atlasW}x${atlasH}`, 'xc:transparent',
|
|
1472
1473
|
];
|
|
@@ -1571,7 +1572,7 @@ void fragment() {
|
|
|
1571
1572
|
};
|
|
1572
1573
|
metadata = JSON.stringify(jsonData, null, 2);
|
|
1573
1574
|
}
|
|
1574
|
-
ensureDir(outputData);
|
|
1575
|
+
ensureDir(dirname(outputData));
|
|
1575
1576
|
writeFileSync(outputData, metadata);
|
|
1576
1577
|
return `Sprite atlas packed successfully:
|
|
1577
1578
|
Atlas: ${outputImage} (${atlasW}x${atlasH})
|
|
@@ -1596,7 +1597,13 @@ void fragment() {
|
|
|
1596
1597
|
async execute(args) {
|
|
1597
1598
|
const type = String(args.type).toLowerCase();
|
|
1598
1599
|
const engine = String(args.engine || 'rapier').toLowerCase();
|
|
1599
|
-
|
|
1600
|
+
let params = {};
|
|
1601
|
+
try {
|
|
1602
|
+
params = args.params ? JSON.parse(String(args.params)) : {};
|
|
1603
|
+
}
|
|
1604
|
+
catch {
|
|
1605
|
+
return 'Error: params must be valid JSON';
|
|
1606
|
+
}
|
|
1600
1607
|
const outputPath = String(args.output_path);
|
|
1601
1608
|
const validTypes = ['rigidbody', 'softbody', 'ragdoll', 'vehicle', 'cloth', 'joints'];
|
|
1602
1609
|
const validEngines = ['godot', 'unity', 'unreal', 'bevy', 'cannon', 'rapier', 'matter'];
|
|
@@ -2859,7 +2866,7 @@ const JOINT_CONFIG = ${JSON.stringify({
|
|
|
2859
2866
|
fileExt = 'cpp';
|
|
2860
2867
|
else if (fileExt === 'ts' && engine === 'bevy')
|
|
2861
2868
|
fileExt = 'rs';
|
|
2862
|
-
ensureDir(outputPath);
|
|
2869
|
+
ensureDir(dirname(outputPath));
|
|
2863
2870
|
writeFileSync(outputPath, code);
|
|
2864
2871
|
return `Physics setup generated:
|
|
2865
2872
|
Type: ${type}
|
|
@@ -2883,7 +2890,13 @@ const JOINT_CONFIG = ${JSON.stringify({
|
|
|
2883
2890
|
const effect = String(args.effect).toLowerCase();
|
|
2884
2891
|
const engine = String(args.engine || 'three').toLowerCase();
|
|
2885
2892
|
const outputPath = String(args.output_path);
|
|
2886
|
-
|
|
2893
|
+
let overrides = {};
|
|
2894
|
+
try {
|
|
2895
|
+
overrides = args.params ? JSON.parse(String(args.params)) : {};
|
|
2896
|
+
}
|
|
2897
|
+
catch {
|
|
2898
|
+
return 'Error: params must be valid JSON';
|
|
2899
|
+
}
|
|
2887
2900
|
const validEffects = ['fire', 'smoke', 'rain', 'snow', 'sparks', 'magic', 'explosion', 'dust', 'bubbles', 'leaves', 'confetti'];
|
|
2888
2901
|
const validEngines = ['godot', 'unity', 'unreal', 'three', 'phaser', 'pixi'];
|
|
2889
2902
|
if (!validEffects.includes(effect))
|
|
@@ -3561,7 +3574,7 @@ ${effect === 'fire' || effect === 'smoke' ? ` // Grow over lifetime
|
|
|
3561
3574
|
}
|
|
3562
3575
|
if (!code)
|
|
3563
3576
|
return `Error: No implementation for effect="${effect}" with engine="${engine}"`;
|
|
3564
|
-
ensureDir(outputPath);
|
|
3577
|
+
ensureDir(dirname(outputPath));
|
|
3565
3578
|
writeFileSync(outputPath, code);
|
|
3566
3579
|
return `Particle system generated:
|
|
3567
3580
|
Effect: ${effect}
|
|
@@ -3588,12 +3601,18 @@ ${effect === 'fire' || effect === 'smoke' ? ` // Grow over lifetime
|
|
|
3588
3601
|
tier: 'free',
|
|
3589
3602
|
async execute(args) {
|
|
3590
3603
|
const type = String(args.type).toLowerCase();
|
|
3591
|
-
const width = typeof args.width === 'number' ? args.width : 40;
|
|
3592
|
-
const height = typeof args.height === 'number' ? args.height : 30;
|
|
3604
|
+
const width = Math.min(typeof args.width === 'number' ? args.width : 40, 1000);
|
|
3605
|
+
const height = Math.min(typeof args.height === 'number' ? args.height : 30, 1000);
|
|
3593
3606
|
const seedVal = typeof args.seed === 'number' ? args.seed : Date.now();
|
|
3594
3607
|
const outputPath = String(args.output_path);
|
|
3595
3608
|
const format = String(args.format || 'json');
|
|
3596
|
-
|
|
3609
|
+
let params = {};
|
|
3610
|
+
try {
|
|
3611
|
+
params = args.params ? JSON.parse(String(args.params)) : {};
|
|
3612
|
+
}
|
|
3613
|
+
catch {
|
|
3614
|
+
return 'Error: params must be valid JSON';
|
|
3615
|
+
}
|
|
3597
3616
|
const validTypes = ['dungeon', 'platformer', 'overworld', 'maze', 'arena'];
|
|
3598
3617
|
if (!validTypes.includes(type))
|
|
3599
3618
|
return `Error: Invalid type "${type}". Use: ${validTypes.join(', ')}`;
|
|
@@ -4114,7 +4133,7 @@ ${effect === 'fire' || effect === 'smoke' ? ` // Grow over lifetime
|
|
|
4114
4133
|
};
|
|
4115
4134
|
output = JSON.stringify(jsonData, null, 2);
|
|
4116
4135
|
}
|
|
4117
|
-
ensureDir(outputPath);
|
|
4136
|
+
ensureDir(dirname(outputPath));
|
|
4118
4137
|
writeFileSync(outputPath, output);
|
|
4119
4138
|
const floorCount = map.flat().filter(t => t === FLOOR || t === SPAWN || t === EXIT || t === DOOR).length;
|
|
4120
4139
|
return `Level generated:
|
|
@@ -4155,7 +4174,12 @@ ${effect === 'fire' || effect === 'smoke' ? ` // Grow over lifetime
|
|
|
4155
4174
|
// Parse or generate map data
|
|
4156
4175
|
let mapData;
|
|
4157
4176
|
if (args.map_data) {
|
|
4158
|
-
|
|
4177
|
+
try {
|
|
4178
|
+
mapData = JSON.parse(String(args.map_data));
|
|
4179
|
+
}
|
|
4180
|
+
catch {
|
|
4181
|
+
return 'Error: map_data must be valid JSON';
|
|
4182
|
+
}
|
|
4159
4183
|
}
|
|
4160
4184
|
else {
|
|
4161
4185
|
// Auto-generate a sample terrain map
|
|
@@ -4490,7 +4514,7 @@ tile_${i}/terrain_peering/left = ${cardW ? 0 : -1}`;
|
|
|
4490
4514
|
};
|
|
4491
4515
|
output = JSON.stringify(jsonOutput, null, 2);
|
|
4492
4516
|
}
|
|
4493
|
-
ensureDir(outputPath);
|
|
4517
|
+
ensureDir(dirname(outputPath));
|
|
4494
4518
|
writeFileSync(outputPath, output);
|
|
4495
4519
|
const terrainTileCount = tiledMap.flat().filter(t => t >= 0).length;
|
|
4496
4520
|
return `Tilemap generated:
|
|
@@ -4517,7 +4541,13 @@ tile_${i}/terrain_peering/left = ${cardW ? 0 : -1}`;
|
|
|
4517
4541
|
const engine = String(args.engine || 'recast').toLowerCase();
|
|
4518
4542
|
const agentType = String(args.agent_type || 'humanoid').toLowerCase();
|
|
4519
4543
|
const outputPath = String(args.output_path);
|
|
4520
|
-
|
|
4544
|
+
let overrides = {};
|
|
4545
|
+
try {
|
|
4546
|
+
overrides = args.params ? JSON.parse(String(args.params)) : {};
|
|
4547
|
+
}
|
|
4548
|
+
catch {
|
|
4549
|
+
return 'Error: params must be valid JSON';
|
|
4550
|
+
}
|
|
4521
4551
|
const validEngines = ['godot', 'unity', 'unreal', 'recast', 'three'];
|
|
4522
4552
|
const validAgents = ['humanoid', 'vehicle', 'flying', 'small_creature'];
|
|
4523
4553
|
if (!validEngines.includes(engine))
|
|
@@ -5449,7 +5479,7 @@ export class NavigationSystem {
|
|
|
5449
5479
|
}
|
|
5450
5480
|
if (!code)
|
|
5451
5481
|
return `Error: No implementation for engine="${engine}"`;
|
|
5452
|
-
ensureDir(outputPath);
|
|
5482
|
+
ensureDir(dirname(outputPath));
|
|
5453
5483
|
writeFileSync(outputPath, code);
|
|
5454
5484
|
return `Navigation mesh config generated:
|
|
5455
5485
|
Engine: ${engine}
|
|
@@ -5620,7 +5650,13 @@ export class NavigationSystem {
|
|
|
5620
5650
|
const system = String(args.system).toLowerCase();
|
|
5621
5651
|
const engine = String(args.engine || 'web').toLowerCase();
|
|
5622
5652
|
const outputPath = String(args.output_path);
|
|
5623
|
-
|
|
5653
|
+
let params = {};
|
|
5654
|
+
try {
|
|
5655
|
+
params = args.params ? JSON.parse(String(args.params)) : {};
|
|
5656
|
+
}
|
|
5657
|
+
catch {
|
|
5658
|
+
return 'Error: params must be valid JSON';
|
|
5659
|
+
}
|
|
5624
5660
|
const validSystems = ['spatial', 'music_layers', 'sound_bank', 'howler', 'web_audio'];
|
|
5625
5661
|
if (!validSystems.includes(system)) {
|
|
5626
5662
|
return `Error: Unknown audio system "${system}". Valid: ${validSystems.join(', ')}`;
|
|
@@ -7073,7 +7109,13 @@ export class AudioEngine {
|
|
|
7073
7109
|
const transport = String(args.transport || 'websocket').toLowerCase();
|
|
7074
7110
|
const framework = String(args.framework || 'raw').toLowerCase();
|
|
7075
7111
|
const outputDir = String(args.output_dir);
|
|
7076
|
-
|
|
7112
|
+
let features = ['lobby', 'state_sync'];
|
|
7113
|
+
try {
|
|
7114
|
+
features = args.features ? JSON.parse(String(args.features)) : ['lobby', 'state_sync'];
|
|
7115
|
+
}
|
|
7116
|
+
catch {
|
|
7117
|
+
return 'Error: features must be valid JSON';
|
|
7118
|
+
}
|
|
7077
7119
|
const validArch = ['client_server', 'peer_to_peer', 'relay'];
|
|
7078
7120
|
if (!validArch.includes(architecture)) {
|
|
7079
7121
|
return `Error: Unknown architecture "${architecture}". Valid: ${validArch.join(', ')}`;
|
|
@@ -8981,7 +9023,13 @@ export default defineConfig({
|
|
|
8981
9023
|
const testType = String(args.test_type).toLowerCase();
|
|
8982
9024
|
const engine = String(args.engine || 'web').toLowerCase();
|
|
8983
9025
|
const outputPath = String(args.output_path);
|
|
8984
|
-
|
|
9026
|
+
let params = {};
|
|
9027
|
+
try {
|
|
9028
|
+
params = args.params ? JSON.parse(String(args.params)) : {};
|
|
9029
|
+
}
|
|
9030
|
+
catch {
|
|
9031
|
+
return 'Error: params must be valid JSON';
|
|
9032
|
+
}
|
|
8985
9033
|
const validTypes = ['fps_profiler', 'memory_tracker', 'input_recorder', 'screenshot_test', 'performance_budget'];
|
|
8986
9034
|
if (!validTypes.includes(testType)) {
|
|
8987
9035
|
return `Error: Unknown test type "${testType}". Valid: ${validTypes.join(', ')}`;
|
|
@@ -10281,9 +10329,21 @@ export class PerformanceBudget {
|
|
|
10281
10329
|
tier: 'free',
|
|
10282
10330
|
async execute(args) {
|
|
10283
10331
|
const framework = String(args.framework).toLowerCase();
|
|
10284
|
-
|
|
10332
|
+
let entities = [];
|
|
10333
|
+
try {
|
|
10334
|
+
entities = JSON.parse(String(args.entities));
|
|
10335
|
+
}
|
|
10336
|
+
catch {
|
|
10337
|
+
return 'Error: entities must be valid JSON';
|
|
10338
|
+
}
|
|
10285
10339
|
const outputDir = String(args.output_dir);
|
|
10286
|
-
|
|
10340
|
+
let systems = [];
|
|
10341
|
+
try {
|
|
10342
|
+
systems = args.systems ? JSON.parse(String(args.systems)) : [];
|
|
10343
|
+
}
|
|
10344
|
+
catch {
|
|
10345
|
+
return 'Error: systems must be valid JSON';
|
|
10346
|
+
}
|
|
10287
10347
|
const validFrameworks = ['bevy', 'unity_dots', 'bitecs', 'miniplex', 'ecsy'];
|
|
10288
10348
|
if (!validFrameworks.includes(framework)) {
|
|
10289
10349
|
return `Error: Unknown ECS framework "${framework}". Valid: ${validFrameworks.join(', ')}`;
|