@compilr-dev/factory 0.1.1 → 0.1.3
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/model/tools.js +137 -69
- package/package.json +1 -1
package/dist/model/tools.js
CHANGED
|
@@ -112,7 +112,18 @@ function createAppModelGetTool(config) {
|
|
|
112
112
|
function createAppModelUpdateTool(config) {
|
|
113
113
|
return defineTool({
|
|
114
114
|
name: 'app_model_update',
|
|
115
|
-
description: `Apply a semantic operation to the Application Model.
|
|
115
|
+
description: `Apply a semantic operation to the Application Model. If no model exists, one is created automatically.
|
|
116
|
+
|
|
117
|
+
EXAMPLES:
|
|
118
|
+
- updateIdentity: { "op": "updateIdentity", "updates": { "name": "My App", "description": "A task manager" } }
|
|
119
|
+
- updateTechStack: { "op": "updateTechStack", "updates": { "toolkit": "react-node" } }
|
|
120
|
+
- addEntity: { "op": "addEntity", "entity": { "name": "Task", "pluralName": "Tasks", "icon": "📋", "fields": [{ "name": "title", "label": "Title", "type": "string", "required": true }], "views": ["list", "detail", "card"], "relationships": [] } }
|
|
121
|
+
- addField: { "op": "addField", "entity": "Task", "field": { "name": "dueDate", "label": "Due Date", "type": "date", "required": false } }
|
|
122
|
+
- addRelationship: { "op": "addRelationship", "entity": "Task", "relationship": { "type": "belongsTo", "target": "Project" } }
|
|
123
|
+
- updateFeatures: { "op": "updateFeatures", "updates": { "dashboard": true, "darkMode": true } }
|
|
124
|
+
- updateTheme: { "op": "updateTheme", "updates": { "primaryColor": "#1976D2" } }
|
|
125
|
+
- updateLayout: { "op": "updateLayout", "updates": { "shell": "sidebar-header" } }
|
|
126
|
+
- removeEntity: { "op": "removeEntity", "entity": "Task", "force": true }`,
|
|
116
127
|
inputSchema: {
|
|
117
128
|
type: 'object',
|
|
118
129
|
properties: {
|
|
@@ -143,23 +154,31 @@ function createAppModelUpdateTool(config) {
|
|
|
143
154
|
description: 'Expected current revision for optimistic locking. Optional.',
|
|
144
155
|
},
|
|
145
156
|
entity: {
|
|
146
|
-
|
|
157
|
+
oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: true }],
|
|
158
|
+
description: 'Entity name (string) for most ops, or full entity object (for addEntity). Entity object requires: name (PascalCase), pluralName, icon, fields array, views array, relationships array.',
|
|
147
159
|
},
|
|
148
160
|
field: {
|
|
149
|
-
|
|
161
|
+
oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: true }],
|
|
162
|
+
description: 'Field name (string) for updateField/removeField/renameField, or full field object (for addField). Field object requires: name (camelCase), label, type (string|number|boolean|date|enum), required.',
|
|
150
163
|
},
|
|
151
164
|
relationship: {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
oneOf: [
|
|
166
|
+
{
|
|
167
|
+
type: 'object',
|
|
168
|
+
additionalProperties: true,
|
|
169
|
+
properties: {
|
|
170
|
+
type: { type: 'string', enum: ['belongsTo', 'hasMany'] },
|
|
171
|
+
target: { type: 'string' },
|
|
172
|
+
fieldName: { type: 'string' },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{ type: 'string' },
|
|
176
|
+
],
|
|
177
|
+
description: 'Relationship object for addRelationship. Requires: type ("belongsTo" or "hasMany"), target (entity name). Optional: fieldName.',
|
|
159
178
|
},
|
|
160
179
|
updates: {
|
|
161
|
-
type: 'object',
|
|
162
|
-
description: 'Partial updates for
|
|
180
|
+
oneOf: [{ type: 'object', additionalProperties: true }, { type: 'string' }],
|
|
181
|
+
description: 'Partial updates object for updateIdentity, updateLayout, updateFeatures, updateTheme, updateTechStack, updateEntity.',
|
|
163
182
|
},
|
|
164
183
|
newName: {
|
|
165
184
|
type: 'string',
|
|
@@ -222,28 +241,53 @@ function createAppModelUpdateTool(config) {
|
|
|
222
241
|
},
|
|
223
242
|
});
|
|
224
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Coerce a value to an object. LLMs sometimes send stringified JSON for nested objects.
|
|
246
|
+
* Returns the parsed object, or null if coercion fails.
|
|
247
|
+
*/
|
|
248
|
+
function coerceToObject(value) {
|
|
249
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
if (typeof value === 'string') {
|
|
253
|
+
try {
|
|
254
|
+
const parsed = JSON.parse(value);
|
|
255
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
256
|
+
return parsed;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Not valid JSON
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
225
265
|
function buildOperation(input) {
|
|
226
266
|
switch (input.op) {
|
|
227
267
|
case 'addEntity': {
|
|
228
|
-
|
|
229
|
-
|
|
268
|
+
const entity = coerceToObject(input.entity);
|
|
269
|
+
if (!entity) {
|
|
270
|
+
throw new Error(`addEntity requires "entity" as an object with name, pluralName, icon, fields, views, relationships. ` +
|
|
271
|
+
`Got: ${input.entity === undefined ? 'missing' : typeof input.entity}. ` +
|
|
272
|
+
`Example: { "op": "addEntity", "entity": { "name": "Employee", "pluralName": "Employees", "icon": "👤", "fields": [{ "name": "name", "label": "Name", "type": "string", "required": true }], "views": ["list", "detail", "card"], "relationships": [] } }`);
|
|
230
273
|
}
|
|
231
|
-
|
|
232
|
-
return { op: 'addEntity', entity };
|
|
274
|
+
return { op: 'addEntity', entity: entity };
|
|
233
275
|
}
|
|
234
|
-
case 'updateEntity':
|
|
276
|
+
case 'updateEntity': {
|
|
235
277
|
if (typeof input.entity !== 'string')
|
|
236
|
-
throw new Error(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
throw new Error(`updateEntity requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
279
|
+
const updates = coerceToObject(input.updates);
|
|
280
|
+
if (!updates)
|
|
281
|
+
throw new Error(`updateEntity requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}`);
|
|
282
|
+
return { op: 'updateEntity', entity: input.entity, updates };
|
|
283
|
+
}
|
|
240
284
|
case 'removeEntity':
|
|
241
285
|
if (typeof input.entity !== 'string')
|
|
242
|
-
throw new Error(
|
|
286
|
+
throw new Error(`removeEntity requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
243
287
|
return { op: 'removeEntity', entity: input.entity, force: input.force };
|
|
244
288
|
case 'renameEntity':
|
|
245
289
|
if (typeof input.entity !== 'string')
|
|
246
|
-
throw new Error(
|
|
290
|
+
throw new Error(`renameEntity requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
247
291
|
if (!input.newName)
|
|
248
292
|
throw new Error('renameEntity requires newName');
|
|
249
293
|
return { op: 'renameEntity', entity: input.entity, newName: input.newName };
|
|
@@ -253,36 +297,39 @@ function buildOperation(input) {
|
|
|
253
297
|
return { op: 'reorderEntities', order: input.order };
|
|
254
298
|
case 'addField': {
|
|
255
299
|
if (typeof input.entity !== 'string')
|
|
256
|
-
throw new Error(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
case 'updateField':
|
|
263
|
-
if (typeof input.entity !== 'string')
|
|
264
|
-
throw new Error('updateField requires entity name (string)');
|
|
265
|
-
if (typeof input.field !== 'string')
|
|
266
|
-
throw new Error('updateField requires field name (string)');
|
|
267
|
-
if (!input.updates)
|
|
268
|
-
throw new Error('updateField requires updates object');
|
|
300
|
+
throw new Error(`addField requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
301
|
+
const field = coerceToObject(input.field);
|
|
302
|
+
if (!field)
|
|
303
|
+
throw new Error(`addField requires "field" as an object with name, label, type, required. ` +
|
|
304
|
+
`Got: ${input.field === undefined ? 'missing' : typeof input.field}. ` +
|
|
305
|
+
`Example: { "op": "addField", "entity": "Employee", "field": { "name": "email", "label": "Email", "type": "string", "required": true } }`);
|
|
269
306
|
return {
|
|
270
|
-
op: '
|
|
307
|
+
op: 'addField',
|
|
271
308
|
entity: input.entity,
|
|
272
|
-
field:
|
|
273
|
-
updates: input.updates,
|
|
309
|
+
field: field,
|
|
274
310
|
};
|
|
311
|
+
}
|
|
312
|
+
case 'updateField': {
|
|
313
|
+
if (typeof input.entity !== 'string')
|
|
314
|
+
throw new Error(`updateField requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
315
|
+
if (typeof input.field !== 'string')
|
|
316
|
+
throw new Error(`updateField requires "field" as a string (field name). Got: ${input.field === undefined ? 'missing' : typeof input.field}`);
|
|
317
|
+
const updates = coerceToObject(input.updates);
|
|
318
|
+
if (!updates)
|
|
319
|
+
throw new Error(`updateField requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}`);
|
|
320
|
+
return { op: 'updateField', entity: input.entity, field: input.field, updates };
|
|
321
|
+
}
|
|
275
322
|
case 'removeField':
|
|
276
323
|
if (typeof input.entity !== 'string')
|
|
277
|
-
throw new Error(
|
|
324
|
+
throw new Error(`removeField requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
278
325
|
if (typeof input.field !== 'string')
|
|
279
|
-
throw new Error(
|
|
326
|
+
throw new Error(`removeField requires "field" as a string (field name). Got: ${input.field === undefined ? 'missing' : typeof input.field}`);
|
|
280
327
|
return { op: 'removeField', entity: input.entity, field: input.field };
|
|
281
328
|
case 'renameField':
|
|
282
329
|
if (typeof input.entity !== 'string')
|
|
283
|
-
throw new Error(
|
|
330
|
+
throw new Error(`renameField requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
284
331
|
if (typeof input.field !== 'string')
|
|
285
|
-
throw new Error(
|
|
332
|
+
throw new Error(`renameField requires "field" as a string (field name). Got: ${input.field === undefined ? 'missing' : typeof input.field}`);
|
|
286
333
|
if (!input.newName)
|
|
287
334
|
throw new Error('renameField requires newName');
|
|
288
335
|
return {
|
|
@@ -293,38 +340,59 @@ function buildOperation(input) {
|
|
|
293
340
|
};
|
|
294
341
|
case 'addRelationship': {
|
|
295
342
|
if (typeof input.entity !== 'string')
|
|
296
|
-
throw new Error(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
343
|
+
throw new Error(`addRelationship requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
344
|
+
const relationship = coerceToObject(input.relationship);
|
|
345
|
+
if (!relationship)
|
|
346
|
+
throw new Error(`addRelationship requires "relationship" object with type and target. ` +
|
|
347
|
+
`Got: ${input.relationship === undefined ? 'missing' : typeof input.relationship}. ` +
|
|
348
|
+
`Example: { "op": "addRelationship", "entity": "Task", "relationship": { "type": "belongsTo", "target": "Project" } }`);
|
|
349
|
+
return {
|
|
350
|
+
op: 'addRelationship',
|
|
351
|
+
entity: input.entity,
|
|
352
|
+
relationship: relationship,
|
|
353
|
+
};
|
|
301
354
|
}
|
|
302
355
|
case 'removeRelationship':
|
|
303
356
|
if (typeof input.entity !== 'string')
|
|
304
|
-
throw new Error(
|
|
357
|
+
throw new Error(`removeRelationship requires "entity" as a string (entity name). Got: ${input.entity === undefined ? 'missing' : typeof input.entity}`);
|
|
305
358
|
if (!input.target)
|
|
306
359
|
throw new Error('removeRelationship requires target');
|
|
307
360
|
return { op: 'removeRelationship', entity: input.entity, target: input.target };
|
|
308
|
-
case 'updateIdentity':
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (!
|
|
318
|
-
throw new Error(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return { op: '
|
|
361
|
+
case 'updateIdentity': {
|
|
362
|
+
const updates = coerceToObject(input.updates);
|
|
363
|
+
if (!updates)
|
|
364
|
+
throw new Error(`updateIdentity requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}. ` +
|
|
365
|
+
`Example: { "op": "updateIdentity", "updates": { "name": "My App", "description": "An app" } }`);
|
|
366
|
+
return { op: 'updateIdentity', updates };
|
|
367
|
+
}
|
|
368
|
+
case 'updateLayout': {
|
|
369
|
+
const updates = coerceToObject(input.updates);
|
|
370
|
+
if (!updates)
|
|
371
|
+
throw new Error(`updateLayout requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}. ` +
|
|
372
|
+
`Example: { "op": "updateLayout", "updates": { "shell": "sidebar-header" } }`);
|
|
373
|
+
return { op: 'updateLayout', updates };
|
|
374
|
+
}
|
|
375
|
+
case 'updateFeatures': {
|
|
376
|
+
const updates = coerceToObject(input.updates);
|
|
377
|
+
if (!updates)
|
|
378
|
+
throw new Error(`updateFeatures requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}. ` +
|
|
379
|
+
`Example: { "op": "updateFeatures", "updates": { "dashboard": true } }`);
|
|
380
|
+
return { op: 'updateFeatures', updates };
|
|
381
|
+
}
|
|
382
|
+
case 'updateTheme': {
|
|
383
|
+
const updates = coerceToObject(input.updates);
|
|
384
|
+
if (!updates)
|
|
385
|
+
throw new Error(`updateTheme requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}. ` +
|
|
386
|
+
`Example: { "op": "updateTheme", "updates": { "primaryColor": "#1976D2" } }`);
|
|
387
|
+
return { op: 'updateTheme', updates };
|
|
388
|
+
}
|
|
389
|
+
case 'updateTechStack': {
|
|
390
|
+
const updates = coerceToObject(input.updates);
|
|
391
|
+
if (!updates)
|
|
392
|
+
throw new Error(`updateTechStack requires "updates" object. Got: ${input.updates === undefined ? 'missing' : typeof input.updates}. ` +
|
|
393
|
+
`Example: { "op": "updateTechStack", "updates": { "toolkit": "react-node" } }`);
|
|
394
|
+
return { op: 'updateTechStack', updates };
|
|
395
|
+
}
|
|
328
396
|
default:
|
|
329
397
|
throw new Error(`Unknown operation: ${input.op}`);
|
|
330
398
|
}
|