@waica/cli 0.4.2 → 0.5.0
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.
- package/dist/editor/assets/{cssMode-BO8FrG9g.js → cssMode-2_8FrcPt.js} +1 -1
- package/dist/editor/assets/{freemarker2-C6rCz24J.js → freemarker2-BgSLfjNS.js} +1 -1
- package/dist/editor/assets/{handlebars-MZdu9Qw_.js → handlebars-CZIkUhwZ.js} +1 -1
- package/dist/editor/assets/{html-BrjX1TJ1.js → html-B66ooLhD.js} +1 -1
- package/dist/editor/assets/{htmlMode-cEvpMwiW.js → htmlMode-BiIT--Mv.js} +1 -1
- package/dist/editor/assets/{index-eTEBznqW.js → index-Br-M9Tdo.js} +1846 -259
- package/dist/editor/assets/{index-Btzc6zxr.css → index-q7IkLe-P.css} +1 -1
- package/dist/editor/assets/{javascript-DHUgp9_L.js → javascript-BZrbbzWQ.js} +1 -1
- package/dist/editor/assets/{jsonMode-D0PmIuaM.js → jsonMode-CABkbZgt.js} +1 -1
- package/dist/editor/assets/{liquid-UM67NCct.js → liquid-CT7pyNYe.js} +1 -1
- package/dist/editor/assets/{mdx-D3xXLqrP.js → mdx-DW7MPqdm.js} +1 -1
- package/dist/editor/assets/{python-BhLrcAva.js → python-CgBwEaNf.js} +1 -1
- package/dist/editor/assets/{razor-DcG373mQ.js → razor-Dvggr_Q1.js} +1 -1
- package/dist/editor/assets/{tsMode-ByTYaf45.js → tsMode-OfEpAJE7.js} +1 -1
- package/dist/editor/assets/{typescript-DfJlvcTN.js → typescript-BQbhlbBK.js} +1 -1
- package/dist/editor/assets/{xml-22dL0oCq.js → xml-5qToUR1k.js} +1 -1
- package/dist/editor/assets/{yaml-mgCu6yV6.js → yaml-CFM1jPNu.js} +1 -1
- package/dist/editor/index.html +2 -2
- package/dist/mcp/component-metadata.js +23 -0
- package/dist/mcp/introspection.js +2 -14
- package/dist/mcp/native-import.cjs +3 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/prefabs.js +7 -1
- package/dist/mcp/node_modules/@waica/archetype-platformer/dist/registry-data.js +3 -1
- package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +3 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/chaser.js +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/collectible.js +1 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.d.ts +42 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/hazard.js +49 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.d.ts +81 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.js +150 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +2 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/lifetime.js +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/out-of-bounds.d.ts +29 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/out-of-bounds.js +37 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.js +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/platformer-motor.js +10 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/player-states.js +26 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.d.ts +5 -13
- package/dist/mcp/node_modules/@waica/behaviors/dist/respawnable.js +5 -11
- package/dist/mcp/node_modules/@waica/behaviors/package.json +2 -2
- package/dist/mcp/node_modules/@waica/engine/dist/authoring-defaults.d.ts +11 -0
- package/dist/mcp/node_modules/@waica/engine/dist/authoring-defaults.js +51 -0
- package/dist/mcp/node_modules/@waica/engine/dist/component.d.ts +10 -1
- package/dist/mcp/node_modules/@waica/engine/dist/component.js +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +10 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.js +1 -0
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.d.ts +2 -1
- package/dist/mcp/node_modules/@waica/engine/dist/state/state-machine.js +7 -1
- package/dist/mcp/node_modules/@waica/engine/package.json +1 -1
- package/dist/mcp/package-resolver.js +10 -2
- package/dist/mcp/project-component-loader.js +287 -0
- package/dist/mcp/stdio.js +25 -0
- package/dist/mcp/template/src/characters/player.character.json +29 -2
- package/dist/mcp/template/src/characters/slime.character.json +6 -0
- package/dist/mcp/validation.js +168 -23
- package/package.json +2 -2
package/dist/mcp/validation.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { discoverArchetypes, pickArchetype } from './archetypes.js';
|
|
4
|
+
import { classDefaults, objectRecord } from './component-metadata.js';
|
|
4
5
|
import { PackageResolver, mixedSourceWarnings, provenanceRows, } from './package-resolver.js';
|
|
6
|
+
import { loadProjectComponents } from './project-component-loader.js';
|
|
5
7
|
import { directFiles, requireWaicaProject } from './project-path.js';
|
|
6
8
|
function add(context, severity, code, message, file, ref) {
|
|
7
9
|
context.findings.push({ severity, code, message, file, ...(ref ? { ref } : {}) });
|
|
@@ -45,10 +47,63 @@ function componentList(value) {
|
|
|
45
47
|
return [];
|
|
46
48
|
return value.filter((entry) => !!entry && typeof entry === 'object' && typeof entry.type === 'string');
|
|
47
49
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
function classMetadata(Class) {
|
|
51
|
+
// A throwing constructor has no observable defaults, matching list_components.
|
|
52
|
+
return { params: Class.params ?? {}, defaults: classDefaults(Class) };
|
|
53
|
+
}
|
|
54
|
+
/** Clip names declared by the sibling AnimatedSprite, or undefined when there is none (no animation contract to check). */
|
|
55
|
+
function siblingClips(siblings) {
|
|
56
|
+
const animated = siblings.find((component) => component.type === 'AnimatedSprite');
|
|
57
|
+
return animated ? new Set(Object.keys(objectRecord(animated.props?.clips))) : undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Whether `action` has at least one real binding — an own key, since bindings is keyed by an untrusted string. */
|
|
60
|
+
function isBoundAction(bindings, action) {
|
|
61
|
+
return Object.hasOwn(bindings, action) && (bindings[action]?.length ?? 0) > 0;
|
|
62
|
+
}
|
|
63
|
+
function validateParamReferences(entries, siblings, file, context) {
|
|
64
|
+
const clips = siblingClips(siblings);
|
|
65
|
+
for (const { component, only } of entries) {
|
|
66
|
+
const metadata = context.componentMetadata.get(component.type);
|
|
67
|
+
if (!metadata)
|
|
68
|
+
continue;
|
|
69
|
+
const props = objectRecord(component.props);
|
|
70
|
+
for (const [param, spec] of Object.entries(metadata.params)) {
|
|
71
|
+
if (only && !only.has(param))
|
|
72
|
+
continue;
|
|
73
|
+
if (!spec.ref || spec.options !== undefined)
|
|
74
|
+
continue;
|
|
75
|
+
const value = Object.hasOwn(props, param) ? props[param] : metadata.defaults[param];
|
|
76
|
+
if (typeof value !== 'string' || value === '')
|
|
77
|
+
continue;
|
|
78
|
+
const field = `${component.type}.${param}`;
|
|
79
|
+
switch (spec.ref) {
|
|
80
|
+
case 'prefab':
|
|
81
|
+
if (!context.prefabRefs.has(value)) {
|
|
82
|
+
add(context, 'error', 'broken-prefab-ref', `Component "${component.type}" param "${param}" references missing prefab "${value}".`, file, field);
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'clip':
|
|
86
|
+
if (clips && !clips.has(value)) {
|
|
87
|
+
add(context, 'error', 'missing-clip', `Component "${component.type}" param "${param}" references missing animation clip "${value}".`, file, field);
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case 'action':
|
|
91
|
+
if (!isBoundAction(context.bindings, value)) {
|
|
92
|
+
add(context,
|
|
93
|
+
// Consistent with the pre-existing state-transition check for the
|
|
94
|
+
// same condition (below): an unbound action is a real gap the
|
|
95
|
+
// agent should look at, but not one that flips ok:false.
|
|
96
|
+
'warning', 'input-action-unbound', `Component "${component.type}" param "${param}" references unbound input action "${value}".`, file, field);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case 'stat':
|
|
100
|
+
if (!context.declaredStats.has(value)) {
|
|
101
|
+
add(context, 'warning', 'undeclared-stat', `Component "${component.type}" param "${param}" references undeclared stat "${value}"; runtime writes may still create it.`, file, field);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
52
107
|
}
|
|
53
108
|
function machineStates(component) {
|
|
54
109
|
return objectRecord(component.props?.states);
|
|
@@ -85,10 +140,7 @@ function stateCodeExists(role, state, context) {
|
|
|
85
140
|
(context.roleStateSources.get(role) ?? []).some((source) => roleSourceHasState(source, state)));
|
|
86
141
|
}
|
|
87
142
|
function validateStateMachines(components, file, ref, context) {
|
|
88
|
-
const
|
|
89
|
-
const clips = animated
|
|
90
|
-
? new Set(Object.keys(objectRecord(animated.props?.clips)))
|
|
91
|
-
: undefined;
|
|
143
|
+
const clips = siblingClips(components);
|
|
92
144
|
for (const machine of components.filter((component) => component.type === 'StateMachine')) {
|
|
93
145
|
const states = machineStates(machine);
|
|
94
146
|
const realNames = Object.keys(states).filter((name) => name !== '*');
|
|
@@ -98,9 +150,15 @@ function validateStateMachines(components, file, ref, context) {
|
|
|
98
150
|
: (realNames[0] ?? '');
|
|
99
151
|
for (const name of realNames) {
|
|
100
152
|
const definition = objectRecord(states[name]);
|
|
101
|
-
const
|
|
153
|
+
const explicitClip = typeof definition.clip === 'string' ? definition.clip : undefined;
|
|
154
|
+
const clip = explicitClip ?? name;
|
|
155
|
+
// No "empty means none" special case here: the runtime looks up
|
|
156
|
+
// `this.states[state]?.clip ?? state`, and '' survives that nullish
|
|
157
|
+
// coalesce, so an explicit empty clip is looked up literally and must
|
|
158
|
+
// be validated like any other explicit value (unlike Collectible.stat,
|
|
159
|
+
// which the runtime genuinely treats as unset).
|
|
102
160
|
if (clips && !clips.has(clip)) {
|
|
103
|
-
add(context, 'warning', 'missing-clip', `State "${name}" uses missing animation clip "${clip}".`, file, ref ?? name);
|
|
161
|
+
add(context, explicitClip === undefined ? 'warning' : 'error', 'missing-clip', `State "${name}" uses missing animation clip "${clip}".`, file, explicitClip === undefined ? (ref ?? name) : `StateMachine.states.${name}.clip`);
|
|
104
162
|
}
|
|
105
163
|
const transitions = Array.isArray(definition.transitions) ? definition.transitions : [];
|
|
106
164
|
for (const transition of transitions) {
|
|
@@ -145,6 +203,7 @@ function validatePrefab(prefab, file, ref, context) {
|
|
|
145
203
|
const components = componentList(prefab.components);
|
|
146
204
|
for (const component of components)
|
|
147
205
|
checkComponent(component, file, ref, context);
|
|
206
|
+
validateParamReferences(components.map((component) => ({ component })), components, file, context);
|
|
148
207
|
validateStateMachines(components, file, ref, context);
|
|
149
208
|
}
|
|
150
209
|
function validateScene(scene, file, prefabs, uiNames, context) {
|
|
@@ -186,6 +245,57 @@ function validateScene(scene, file, prefabs, uiNames, context) {
|
|
|
186
245
|
}
|
|
187
246
|
}
|
|
188
247
|
}
|
|
248
|
+
// Lazily resolved: only computed when a consumer below actually needs
|
|
249
|
+
// the merged prefab+override component list (an override that changes a
|
|
250
|
+
// ref param, a clip-context change, an inline ref:clip lookup that needs
|
|
251
|
+
// the effective sibling AnimatedSprite, or state-machine revalidation).
|
|
252
|
+
// A plain "prefab: ref" entity with no overrides and no inline
|
|
253
|
+
// components never pays for building it (restores the pre-typed-refs
|
|
254
|
+
// laziness that only ran this for state-behavior changes).
|
|
255
|
+
let cachedEffectiveComponents;
|
|
256
|
+
const effectiveComponents = () => {
|
|
257
|
+
cachedEffectiveComponents ??= resolvedEntityComponents(entity, prefab);
|
|
258
|
+
return cachedEffectiveComponents;
|
|
259
|
+
};
|
|
260
|
+
const hasRefKind = (type, kind) => Object.values(context.componentMetadata.get(type)?.params ?? {}).some((spec) => spec.ref === kind && spec.options === undefined);
|
|
261
|
+
// Undefined scope means "check every declared ref param" — correct for
|
|
262
|
+
// inline components (never validated elsewhere) and for a component a
|
|
263
|
+
// clip-context change forces a full recheck of. A Set scopes the check
|
|
264
|
+
// to only the override's changed params, so a prefab-level finding for
|
|
265
|
+
// a param the override never touched is not reported a second time
|
|
266
|
+
// under the scene file.
|
|
267
|
+
const referenceScopes = new Map();
|
|
268
|
+
for (const component of inline)
|
|
269
|
+
referenceScopes.set(component, undefined);
|
|
270
|
+
const inlineTypes = new Set(inline.map((component) => component.type));
|
|
271
|
+
for (const [type, rawPatch] of Object.entries(overrides)) {
|
|
272
|
+
if (inlineTypes.has(type))
|
|
273
|
+
continue;
|
|
274
|
+
const metadata = context.componentMetadata.get(type);
|
|
275
|
+
const patch = objectRecord(rawPatch);
|
|
276
|
+
const changedRefParams = new Set(Object.entries(metadata?.params ?? {})
|
|
277
|
+
.filter(([param, spec]) => spec.ref !== undefined && spec.options === undefined && Object.hasOwn(patch, param))
|
|
278
|
+
.map(([param]) => param));
|
|
279
|
+
if (changedRefParams.size === 0)
|
|
280
|
+
continue;
|
|
281
|
+
const effective = effectiveComponents().find((component) => component.type === type);
|
|
282
|
+
if (effective && !referenceScopes.has(effective))
|
|
283
|
+
referenceScopes.set(effective, changedRefParams);
|
|
284
|
+
}
|
|
285
|
+
const changesClipContext = inline.some((component) => component.type === 'AnimatedSprite') ||
|
|
286
|
+
Object.hasOwn(overrides, 'AnimatedSprite');
|
|
287
|
+
if (changesClipContext) {
|
|
288
|
+
for (const component of effectiveComponents()) {
|
|
289
|
+
// A clip-context change can affect params this component didn't
|
|
290
|
+
// itself change, so this always widens to a full check rather than
|
|
291
|
+
// narrowing an already-scoped entry from the override loop above.
|
|
292
|
+
if (hasRefKind(component.type, 'clip'))
|
|
293
|
+
referenceScopes.set(component, undefined);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const needsSiblings = cachedEffectiveComponents !== undefined ||
|
|
297
|
+
[...referenceScopes.keys()].some((component) => hasRefKind(component.type, 'clip'));
|
|
298
|
+
validateParamReferences([...referenceScopes.entries()].map(([component, only]) => ({ component, only })), needsSiblings ? effectiveComponents() : [...referenceScopes.keys()], file, context);
|
|
189
299
|
// Re-evaluate inherited state behavior only when this entity actually
|
|
190
300
|
// changes a StateMachine or AnimatedSprite. Unrelated overrides keep the
|
|
191
301
|
// prefab-level finding as the single source of truth.
|
|
@@ -193,7 +303,7 @@ function validateScene(scene, file, prefabs, uiNames, context) {
|
|
|
193
303
|
const changesStateBehavior = inline.some((component) => stateTypes.has(component.type)) ||
|
|
194
304
|
Object.keys(overrides).some((type) => stateTypes.has(type));
|
|
195
305
|
if (changesStateBehavior) {
|
|
196
|
-
validateStateMachines(
|
|
306
|
+
validateStateMachines(effectiveComponents(), file, entityRef, context);
|
|
197
307
|
}
|
|
198
308
|
}
|
|
199
309
|
}
|
|
@@ -234,15 +344,30 @@ export async function validateProject(projectPath) {
|
|
|
234
344
|
: null;
|
|
235
345
|
const resolver = new PackageResolver(projectPath);
|
|
236
346
|
const discoveryWarnings = [];
|
|
237
|
-
const [engine, behaviors, archetypes, projectComponents, roleStateSources] = await Promise.all([
|
|
347
|
+
const [engine, behaviors, archetypes, projectComponents, roleStateSources, loadedProjectComponents,] = await Promise.all([
|
|
238
348
|
resolver.load('@waica/engine'),
|
|
239
349
|
resolver.load('@waica/behaviors'),
|
|
240
350
|
discoverArchetypes(projectPath, resolver, discoveryWarnings, activeId ? [activeId] : []),
|
|
241
351
|
projectComponentCandidates(projectPath),
|
|
242
352
|
projectRoleStateSources(projectPath),
|
|
353
|
+
loadProjectComponents(projectPath, resolver),
|
|
243
354
|
]);
|
|
355
|
+
for (const failure of loadedProjectComponents.failures) {
|
|
356
|
+
add({ findings },
|
|
357
|
+
// component-load-unsupported means Node's strip-only loader cannot run
|
|
358
|
+
// code that can still be perfectly valid in the project's Vite/browser
|
|
359
|
+
// toolchain (asset imports, TS enums, an old Node host) — that is not a
|
|
360
|
+
// project defect, so it must not flip a healthy project to ok:false.
|
|
361
|
+
failure.code === 'component-load-unsupported' ? 'info' : 'error', failure.code, `Cannot execute project module: ${failure.message}`, failure.file);
|
|
362
|
+
}
|
|
244
363
|
const manifest = pickArchetype(archetypes, activeId, projectPath).manifest;
|
|
245
364
|
const controls = objectRecord(objectRecord(fixed.get('src/controls.json')).bindings);
|
|
365
|
+
// Bound/unbound is decided from controls.json alone: the shipped runtime
|
|
366
|
+
// installs exactly controls.json's bindings (template main.ts passes
|
|
367
|
+
// controls.bindings raw; engine DEFAULT_BINDINGS is {}), so an action an
|
|
368
|
+
// archetype defines by default but controls.json drops is genuinely
|
|
369
|
+
// unbound at runtime even though discoverArchetypes never guarantees
|
|
370
|
+
// manifest.bindings exists (only manifest.id is validated).
|
|
246
371
|
const bindings = {};
|
|
247
372
|
for (const [name, value] of Object.entries(controls)) {
|
|
248
373
|
if (Array.isArray(value) && value.every((entry) => typeof entry === 'string')) {
|
|
@@ -250,16 +375,22 @@ export async function validateProject(projectPath) {
|
|
|
250
375
|
}
|
|
251
376
|
}
|
|
252
377
|
const stateFiles = new Set((await directFiles(path.join(projectPath, 'src/states'), '.ts')).map((file) => file.slice(0, -'.ts'.length)));
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
378
|
+
const declaredStats = new Set(Object.keys(objectRecord(objectRecord(fixed.get('src/stats.json')).stats)));
|
|
379
|
+
const componentMetadata = new Map(Object.entries(manifest.registry.components).map(([name, Class]) => [
|
|
380
|
+
name,
|
|
381
|
+
classMetadata(Class),
|
|
382
|
+
]));
|
|
383
|
+
for (const [name, description] of Object.entries(loadedProjectComponents.components)) {
|
|
384
|
+
componentMetadata.set(name, {
|
|
385
|
+
params: description.params,
|
|
386
|
+
defaults: description.defaults,
|
|
387
|
+
});
|
|
388
|
+
projectComponents.add(name);
|
|
389
|
+
}
|
|
390
|
+
// Reconstruct the complete ref set before validating any prefab, so a
|
|
391
|
+
// lexically earlier file can refer to one discovered later in the tree.
|
|
262
392
|
const prefabs = new Map();
|
|
393
|
+
const prefabFiles = [];
|
|
263
394
|
for (const [directory, type] of [
|
|
264
395
|
['characters', 'character'],
|
|
265
396
|
['objects', 'object'],
|
|
@@ -274,12 +405,26 @@ export async function validateProject(projectPath) {
|
|
|
274
405
|
const ref = `${directory}/${file.slice(0, -suffix.length)}`;
|
|
275
406
|
const prefab = parsed;
|
|
276
407
|
prefabs.set(ref, prefab);
|
|
277
|
-
|
|
408
|
+
prefabFiles.push({ prefab, relative, ref });
|
|
278
409
|
}
|
|
279
410
|
}
|
|
411
|
+
const context = {
|
|
412
|
+
findings,
|
|
413
|
+
manifest,
|
|
414
|
+
knownComponents: new Set(Object.keys(manifest.registry.components)),
|
|
415
|
+
projectComponents,
|
|
416
|
+
componentMetadata,
|
|
417
|
+
prefabRefs: new Set(prefabs.keys()),
|
|
418
|
+
declaredStats,
|
|
419
|
+
stateFiles,
|
|
420
|
+
roleStateSources,
|
|
421
|
+
bindings,
|
|
422
|
+
};
|
|
423
|
+
for (const { prefab, relative, ref } of prefabFiles) {
|
|
424
|
+
validatePrefab(prefab, relative, ref, context);
|
|
425
|
+
}
|
|
280
426
|
const uiFiles = await directFiles(path.join(projectPath, 'src/ui'), '.html');
|
|
281
427
|
const uiNames = new Set(uiFiles.map((file) => file.slice(0, -'.html'.length)));
|
|
282
|
-
const declaredStats = new Set(Object.keys(objectRecord(objectRecord(fixed.get('src/stats.json')).stats)));
|
|
283
428
|
for (const file of uiFiles) {
|
|
284
429
|
const relative = `src/ui/${file}`;
|
|
285
430
|
const html = await readFile(path.join(projectPath, relative), 'utf8');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waica/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Waica in one command — `npx @waica/cli` opens the editor, `waica mcp` serves the MCP server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
22
|
+
"node": ">=22.18"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc -p tsconfig.build.json && node bundle-editor.mjs && node bundle-mcp.mjs",
|