@saltcorn/copilot 0.7.4 → 0.8.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/actions/generate-js-action.js +43 -5
- package/actions/generate-tables.js +281 -96
- package/actions/generate-workflow.js +83 -36
- package/actions/install-plugin-action.js +103 -0
- package/agent-skills/app-constructor-context.js +25 -0
- package/agent-skills/database-design.js +139 -87
- package/agent-skills/install-plugin.js +111 -0
- package/agent-skills/js-action.js +183 -0
- package/agent-skills/registry-editor.js +911 -0
- package/agent-skills/viewgen.js +302 -53
- package/agent-skills/workflow.js +52 -2
- package/app-constructor/common.js +12 -0
- package/app-constructor/errors.js +102 -0
- package/app-constructor/feedback-action.js +175 -0
- package/app-constructor/feedback.js +112 -0
- package/app-constructor/progress.js +116 -0
- package/app-constructor/prompts.js +58 -0
- package/app-constructor/requirements.js +156 -0
- package/app-constructor/run_task.js +128 -0
- package/app-constructor/schema.js +186 -0
- package/app-constructor/taskchart.js +70 -0
- package/app-constructor/tasks.js +401 -0
- package/app-constructor/tools.js +81 -0
- package/app-constructor/view.js +209 -0
- package/builder-gen.js +1505 -24
- package/builder-schema.js +706 -0
- package/common.js +20 -0
- package/copilot-as-agent.js +6 -1
- package/index.js +24 -1
- package/js-code-gen.js +65 -0
- package/package.json +1 -1
- package/standard-prompt.js +83 -0
- package/tests/builder-gen.test.js +56 -0
|
@@ -14,7 +14,7 @@ const optionNames = (options = []) =>
|
|
|
14
14
|
.map((opt) =>
|
|
15
15
|
typeof opt === "string"
|
|
16
16
|
? opt
|
|
17
|
-
: opt?.name || opt?.label || opt?.value || ""
|
|
17
|
+
: opt?.name || opt?.label || opt?.value || ""
|
|
18
18
|
)
|
|
19
19
|
.filter(Boolean);
|
|
20
20
|
|
|
@@ -39,8 +39,12 @@ const summarizeTriggerActionMatrix = () => {
|
|
|
39
39
|
const pool = table_triggers.includes(when) ? allActions : noRowActions;
|
|
40
40
|
return `* ${when}: ${joinOptionNames(pool)}`;
|
|
41
41
|
});
|
|
42
|
-
const notes = `Triggers requiring a table context: ${table_triggers.join(
|
|
43
|
-
|
|
42
|
+
const notes = `Triggers requiring a table context: ${table_triggers.join(
|
|
43
|
+
", "
|
|
44
|
+
)}.
|
|
45
|
+
Additional triggers that commonly use contextual only_if checks: ${additional_triggers_with_onlyif.join(
|
|
46
|
+
", "
|
|
47
|
+
)}.`;
|
|
44
48
|
return `${notes}
|
|
45
49
|
${lines.join("\n")}`;
|
|
46
50
|
} catch (e) {
|
|
@@ -58,8 +62,10 @@ const summarizeTables = async () => {
|
|
|
58
62
|
const rows = tables.map((table) => {
|
|
59
63
|
const description = table.description ? ` – ${table.description}` : "";
|
|
60
64
|
const fields = (table.fields || [])
|
|
61
|
-
.
|
|
62
|
-
|
|
65
|
+
.map((f) => {
|
|
66
|
+
const tag = f.calculated && !f.stored ? " (virtual, read-only)" : "";
|
|
67
|
+
return `${f.name}:${fieldType(f)}${tag}`;
|
|
68
|
+
})
|
|
63
69
|
.join(", ");
|
|
64
70
|
const fieldText = fields ? ` fields: ${fields}` : "";
|
|
65
71
|
return `* ${table.name}${description}${fieldText}`;
|
|
@@ -77,7 +83,7 @@ const steps = async () => {
|
|
|
77
83
|
|
|
78
84
|
let stateActions = getState().actions;
|
|
79
85
|
const stateActionList = Object.entries(stateActions).filter(
|
|
80
|
-
([k, v]) => !v.disableInWorkflow
|
|
86
|
+
([k, v]) => !v.disableInWorkflow
|
|
81
87
|
);
|
|
82
88
|
|
|
83
89
|
const stepTypeAndCfg = Object.keys(actionExplainers).map((actionName) => {
|
|
@@ -85,7 +91,7 @@ const steps = async () => {
|
|
|
85
91
|
step_type: { type: "string", enum: [actionName] },
|
|
86
92
|
};
|
|
87
93
|
const myFields = actionFields.filter(
|
|
88
|
-
(f) => f.showIf?.wf_action_name === actionName
|
|
94
|
+
(f) => f.showIf?.wf_action_name === actionName
|
|
89
95
|
);
|
|
90
96
|
const required = ["step_type"];
|
|
91
97
|
myFields.forEach((f) => {
|
|
@@ -147,14 +153,15 @@ const steps = async () => {
|
|
|
147
153
|
const table = Table.findOne({ id: trigger.table_id });
|
|
148
154
|
const fieldSpecs = [];
|
|
149
155
|
table.fields.forEach((f) => {
|
|
156
|
+
if (f.calculated && !f.stored) return; // virtual fields have no DB column
|
|
150
157
|
// TODO fkeys dereferenced.
|
|
151
158
|
fieldSpecs.push(`${f.name} with ${f.pretty_type} type`);
|
|
152
159
|
});
|
|
153
160
|
properties.row_expr = {
|
|
154
161
|
type: "string",
|
|
155
162
|
description: `JavaScript expression for the input to the action. This should be an expression for an object, with the following field name and types: ${fieldSpecs.join(
|
|
156
|
-
"; "
|
|
157
|
-
)}.`,
|
|
163
|
+
"; "
|
|
164
|
+
)}. IMPORTANT: omit any non-stored calculated fields — they have no database column and cannot be written. Only include regular fields and stored calculated fields. Keep this expression simple: it should reference values already in the context, not perform inline queries or complex logic. If the values are not yet in context, add a dedicated preceding step (TableQuery, run_js_code, or whichever fits) to fetch or compute them first.`,
|
|
158
165
|
};
|
|
159
166
|
}
|
|
160
167
|
const required = ["step_type"];
|
|
@@ -218,7 +225,9 @@ class GenerateWorkflow {
|
|
|
218
225
|
enum: Trigger.when_options,
|
|
219
226
|
},
|
|
220
227
|
trigger_table: {
|
|
221
|
-
description: `If the workflow trigger is ${table_triggers.join(
|
|
228
|
+
description: `If the workflow trigger is ${table_triggers.join(
|
|
229
|
+
", "
|
|
230
|
+
)}, the name of the table that triggers the workflow`,
|
|
222
231
|
type: "string",
|
|
223
232
|
},
|
|
224
233
|
},
|
|
@@ -229,7 +238,7 @@ class GenerateWorkflow {
|
|
|
229
238
|
const actionExplainers = WorkflowStep.builtInActionExplainers();
|
|
230
239
|
let stateActions = getState().actions;
|
|
231
240
|
const stateActionList = Object.entries(stateActions).filter(
|
|
232
|
-
([k, v]) => !v.disableInWorkflow
|
|
241
|
+
([k, v]) => !v.disableInWorkflow
|
|
233
242
|
);
|
|
234
243
|
const [tableSummary, triggerMatrix] = await Promise.all([
|
|
235
244
|
summarizeTables(),
|
|
@@ -242,30 +251,42 @@ class GenerateWorkflow {
|
|
|
242
251
|
.filter(Boolean)
|
|
243
252
|
.join("\n\n");
|
|
244
253
|
|
|
245
|
-
return `Use the generate_workflow tool to construct computational workflows according to specifications. You must create
|
|
254
|
+
return `Use the generate_workflow tool to construct computational workflows according to specifications. You must create
|
|
246
255
|
the workflow by calling the generate_workflow tool, with the step required to implement the specification.
|
|
247
|
-
|
|
256
|
+
|
|
248
257
|
${contextBlocks}
|
|
249
|
-
|
|
250
|
-
The steps are specified as JSON objects. Each step has a name, specified in the step_name key in the JSON object.
|
|
258
|
+
|
|
259
|
+
The steps are specified as JSON objects. Each step has a name, specified in the step_name key in the JSON object.
|
|
251
260
|
The step name should be a valid JavaScript identifier.
|
|
252
|
-
|
|
261
|
+
|
|
253
262
|
When the user explicitly names an action/step type (for example "ForLoop" or "Toast"), ensure the generated workflow contains at least one step whose step_configuration.step_type exactly matches every requested action. Only skip this if the action genuinely does not exist; in that case, clearly explain the omission in the workflow description.
|
|
254
263
|
|
|
264
|
+
CRITICAL — non-stored calculated fields cannot be written:
|
|
265
|
+
In the table summary above, fields marked (virtual, read-only) are non-stored calculated fields — they have no database column and are computed on-the-fly by Saltcorn. Never include such fields in modify_row row expressions, run_js_code return objects, or SQL UPDATE statements. If you see a field in the table summary marked (virtual, read-only), treat it as read-only everywhere: omit it from any write operation. Only regular fields and stored calculated fields (not marked virtual) have database columns and can be written. Virtual fields refresh automatically when the fields they depend on change — no explicit update is needed.
|
|
266
|
+
|
|
267
|
+
IMPORTANT — keep row expressions simple; use dedicated steps for data fetching:
|
|
268
|
+
Row expressions (e.g. in modify_row) should be simple references to values already in the context — not inline queries or complex logic. If the values you need are not already in context, add a dedicated step before the row expression step to fetch or compute them and write the results into the context. Choose the step type that fits the job: TableQuery to query a table, run_js_code for custom computation, or any other appropriate step. Each step should do one clear thing; the row expression then just picks the relevant context values.
|
|
269
|
+
|
|
270
|
+
CRITICAL — every workflow must form a single connected chain from the first step to the last:
|
|
271
|
+
- Every step except the very last one MUST have a next_step that names another step in the workflow.
|
|
272
|
+
- The very last step must have next_step omitted or set to an empty string to terminate the workflow.
|
|
273
|
+
- No step may be an island: every step must be reachable by following next_step links from the first step.
|
|
274
|
+
- Before submitting, mentally trace the path: first_step → next_step → … → last_step. If any step is unreachable or any link is missing, fix it before calling the tool.
|
|
275
|
+
|
|
255
276
|
Each run of the workflow is executed in the presence of a context, which is a JavaScript object that individual
|
|
256
|
-
steps can read values from and write values to. This context is a state that is persisted on disk for each workflow
|
|
257
|
-
run.
|
|
258
|
-
|
|
259
|
-
Each step can have a next_step key which is the name of the next step, or a JavaScript expression which evaluates
|
|
260
|
-
to the name of the next step based on the context. In the evaluation of the next step, each value in the context is
|
|
261
|
-
in scope and can be addressed directly. Identifiers for the step names are also in scope, the name of the next step
|
|
262
|
-
can be used directly without enclosing it in quotes to form a string.
|
|
263
|
-
|
|
277
|
+
steps can read values from and write values to. This context is a state that is persisted on disk for each workflow
|
|
278
|
+
run.
|
|
279
|
+
|
|
280
|
+
Each step can have a next_step key which is the name of the next step, or a JavaScript expression which evaluates
|
|
281
|
+
to the name of the next step based on the context. In the evaluation of the next step, each value in the context is
|
|
282
|
+
in scope and can be addressed directly. Identifiers for the step names are also in scope, the name of the next step
|
|
283
|
+
can be used directly without enclosing it in quotes to form a string.
|
|
284
|
+
|
|
264
285
|
For example, if the context contains a value x which is an integer and you have steps named "too_low" and "too_high",
|
|
265
286
|
and you would like the next step to be too_low if x is less than 10 and too_high otherwise,
|
|
266
287
|
use this as the next_step expression: x<10 ? too_low : too_high
|
|
267
|
-
|
|
268
|
-
If the next_step is omitted then the workflow terminates.
|
|
288
|
+
|
|
289
|
+
If the next_step is omitted then the workflow terminates. Only the final step should have next_step omitted.
|
|
269
290
|
|
|
270
291
|
Each step has a step_configuration object which contains the step type and the specific parameters of
|
|
271
292
|
that step type. You should specify the step type in the step_type subfield of the step_configuration
|
|
@@ -282,9 +303,11 @@ class GenerateWorkflow {
|
|
|
282
303
|
step types:
|
|
283
304
|
|
|
284
305
|
run_js_code: if the step_type is "run_js_code" then the step object should include the JavaScript code to be executed in the "code"
|
|
285
|
-
key. You can use await in the code if you need to run asynchronous code. The values in the context are directly in scope and can be accessed using their name. In addition, the variable
|
|
306
|
+
key. You can use await in the code if you need to run asynchronous code. The values in the context are directly in scope and can be accessed using their name. In addition, the variable
|
|
286
307
|
"context" is also in scope and can be used to address the context as a whole. To write values to the context, return an
|
|
287
|
-
object. The
|
|
308
|
+
object. The following Saltcorn models are already available in scope without any require: Table, Row, Field, User, View, Trigger, Page, File — use them directly.
|
|
309
|
+
When you need to require other modules, always use a plain require call, e.g. const moment = require('moment');
|
|
310
|
+
NEVER use the pattern const X = X || require(...) — this causes a ReferenceError because const variables cannot be referenced before initialization. The values in this object will be written into the current context. If a value already exists in the context
|
|
288
311
|
it will be overwritten. For example, If the context contains values x and y which are numbers and you would like to push
|
|
289
312
|
the value "sum" which is the sum of x and y, then use this as the code: return {sum: x+y}. You cannot set the next step in the
|
|
290
313
|
return object or by returning a string from a run_js_code step, this will not work. To set the next step from a code action, always use the next_step property of the step object.
|
|
@@ -318,8 +341,25 @@ class GenerateWorkflow {
|
|
|
318
341
|
static async execute(
|
|
319
342
|
{ workflow_steps, workflow_name, when_trigger, trigger_table },
|
|
320
343
|
req,
|
|
344
|
+
context_vars
|
|
321
345
|
) {
|
|
322
|
-
|
|
346
|
+
let allSteps = workflow_steps;
|
|
347
|
+
let initContextStep = null;
|
|
348
|
+
if (context_vars && Object.keys(context_vars).length) {
|
|
349
|
+
const firstUserStep = allSteps[0]?.step_name || "";
|
|
350
|
+
initContextStep = {
|
|
351
|
+
step_name: "init_context",
|
|
352
|
+
only_if: "",
|
|
353
|
+
next_step: firstUserStep,
|
|
354
|
+
step_configuration: {
|
|
355
|
+
step_type: "run_js_code",
|
|
356
|
+
run_where: "Server",
|
|
357
|
+
code: `return ${JSON.stringify(context_vars, null, 2)};`,
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
allSteps = [initContextStep, ...allSteps];
|
|
361
|
+
}
|
|
362
|
+
const steps = this.process_all_steps(allSteps);
|
|
323
363
|
let table_id;
|
|
324
364
|
if (trigger_table) {
|
|
325
365
|
const table = Table.findOne({ name: trigger_table });
|
|
@@ -333,10 +373,17 @@ class GenerateWorkflow {
|
|
|
333
373
|
action: "Workflow",
|
|
334
374
|
configuration: {},
|
|
335
375
|
});
|
|
336
|
-
|
|
376
|
+
// Insert user steps first so the init_context next_step target already
|
|
377
|
+
// exists in the DB when init_context is created.
|
|
378
|
+
const [initStep, ...userSteps] = initContextStep ? steps : [null, ...steps];
|
|
379
|
+
for (const step of initContextStep ? userSteps : steps) {
|
|
337
380
|
step.trigger_id = trigger.id;
|
|
338
381
|
await WorkflowStep.create(step);
|
|
339
382
|
}
|
|
383
|
+
if (initStep) {
|
|
384
|
+
initStep.trigger_id = trigger.id;
|
|
385
|
+
await WorkflowStep.create(initStep);
|
|
386
|
+
}
|
|
340
387
|
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req?.user, {
|
|
341
388
|
entity_type: "Trigger",
|
|
342
389
|
entity_name: trigger.name,
|
|
@@ -346,7 +393,7 @@ class GenerateWorkflow {
|
|
|
346
393
|
"Workflow created. " +
|
|
347
394
|
a(
|
|
348
395
|
{ target: "_blank", href: `/actions/configure/${trigger.id}` },
|
|
349
|
-
"Configure workflow."
|
|
396
|
+
"Configure workflow."
|
|
350
397
|
),
|
|
351
398
|
};
|
|
352
399
|
}
|
|
@@ -364,14 +411,14 @@ class GenerateWorkflow {
|
|
|
364
411
|
step.id = ix + 1;
|
|
365
412
|
});
|
|
366
413
|
const mmdia = WorkflowStep.generate_diagram(
|
|
367
|
-
steps.map((s) => new WorkflowStep(s))
|
|
414
|
+
steps.map((s) => new WorkflowStep(s))
|
|
368
415
|
);
|
|
369
416
|
console.log({ mmdia });
|
|
370
417
|
return (
|
|
371
418
|
div(
|
|
372
419
|
`${workflow_name}${when_trigger ? `: ${when_trigger}` : ""}${
|
|
373
420
|
trigger_table ? ` on ${trigger_table}` : ""
|
|
374
|
-
}
|
|
421
|
+
}`
|
|
375
422
|
) +
|
|
376
423
|
pre({ class: "mermaid" }, mmdia) +
|
|
377
424
|
script(
|
|
@@ -380,13 +427,13 @@ class GenerateWorkflow {
|
|
|
380
427
|
mermaid.initialize({ startOnLoad: false });
|
|
381
428
|
mermaid.run({ querySelector: ".mermaid" });
|
|
382
429
|
});
|
|
383
|
-
`)
|
|
384
|
-
)
|
|
430
|
+
`)
|
|
431
|
+
)
|
|
385
432
|
);
|
|
386
433
|
}
|
|
387
434
|
|
|
388
435
|
return `A workflow! Step names: ${workflow_steps.map(
|
|
389
|
-
(s) => s.step_name
|
|
436
|
+
(s) => s.step_name
|
|
390
437
|
)}. Upgrade Saltcorn to see diagrams in copilot`;
|
|
391
438
|
}
|
|
392
439
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Core install logic. Deprecated chat-copilot format;
|
|
2
|
+
// the Agent Chat structure uses agent-skills/install-plugin.js instead.
|
|
3
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
4
|
+
const db = require("@saltcorn/data/db");
|
|
5
|
+
const Plugin = require("@saltcorn/data/models/plugin");
|
|
6
|
+
const { div, span, a } = require("@saltcorn/markup/tags");
|
|
7
|
+
|
|
8
|
+
class InstallPluginAction {
|
|
9
|
+
static title = "Install Plugin";
|
|
10
|
+
static function_name = "install_plugin";
|
|
11
|
+
static description = "Install a Saltcorn plugin from the store or from npm";
|
|
12
|
+
|
|
13
|
+
static json_schema() {
|
|
14
|
+
return {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
plugin_name: {
|
|
18
|
+
description:
|
|
19
|
+
"Name of the plugin as it appears in the Saltcorn plugin store (e.g. 'maps', 'chart'). Use this when the user refers to a plugin by its friendly name.",
|
|
20
|
+
type: "string",
|
|
21
|
+
},
|
|
22
|
+
npm_package: {
|
|
23
|
+
description:
|
|
24
|
+
"NPM package name to install directly (e.g. '@saltcorn/fullcalendar'). Use this when the user specifies an npm package name.",
|
|
25
|
+
type: "string",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static async system_prompt() {
|
|
32
|
+
return (
|
|
33
|
+
`Use the install_plugin function to install a Saltcorn plugin when the user asks to install, add, or enable a plugin. ` +
|
|
34
|
+
`Prefer plugin_name (store lookup) when the user gives a human-readable name. ` +
|
|
35
|
+
`Use npm_package when the user supplies an npm package name. ` +
|
|
36
|
+
`Do not install the same plugin twice; check if it is already installed first.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static render_html({ plugin_name, npm_package }) {
|
|
41
|
+
const label = plugin_name || npm_package;
|
|
42
|
+
return div(
|
|
43
|
+
{ class: "mb-2" },
|
|
44
|
+
span({ class: "badge bg-secondary me-2" }, "Plugin"),
|
|
45
|
+
span({ class: "fw-bold" }, label)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static async execute({ plugin_name, npm_package }, req) {
|
|
50
|
+
const schema = db.getTenantSchema();
|
|
51
|
+
|
|
52
|
+
// Resolve the plugin object
|
|
53
|
+
let plugin;
|
|
54
|
+
if (plugin_name) {
|
|
55
|
+
plugin = await Plugin.store_by_name(plugin_name);
|
|
56
|
+
if (!plugin) {
|
|
57
|
+
return { postExec: `Plugin "${plugin_name}" not found in store.` };
|
|
58
|
+
}
|
|
59
|
+
// strip any existing DB id so we insert fresh
|
|
60
|
+
delete plugin.id;
|
|
61
|
+
} else if (npm_package) {
|
|
62
|
+
plugin = new Plugin({
|
|
63
|
+
name: npm_package,
|
|
64
|
+
source: "npm",
|
|
65
|
+
location: npm_package,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
return { postExec: "Please provide a plugin name or npm package." };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check already installed
|
|
72
|
+
const existing = await Plugin.findOne({ name: plugin.name });
|
|
73
|
+
if (existing) {
|
|
74
|
+
return {
|
|
75
|
+
postExec:
|
|
76
|
+
`Plugin "${plugin.name}" is already installed. ` +
|
|
77
|
+
a({ target: "_blank", href: `/plugins` }, "Manage plugins."),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const force = schema === db.connectObj.default_schema;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const msgs = await Plugin.loadAndSaveNewPlugin(
|
|
85
|
+
plugin,
|
|
86
|
+
force,
|
|
87
|
+
undefined,
|
|
88
|
+
(s) => s,
|
|
89
|
+
true // allowUnsafeOnTenantsWithoutConfigSetting
|
|
90
|
+
);
|
|
91
|
+
const warnings = (msgs || []).map((m) => `<br>⚠ ${m}`).join("");
|
|
92
|
+
return {
|
|
93
|
+
postExec:
|
|
94
|
+
`Plugin "${plugin.name}" installed successfully.${warnings} ` +
|
|
95
|
+
a({ target: "_blank", href: `/plugins` }, "Manage plugins."),
|
|
96
|
+
};
|
|
97
|
+
} catch (e) {
|
|
98
|
+
return { postExec: `Error installing plugin: ${e.message}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = InstallPluginAction;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const user_registration_hints = `User registration notes:
|
|
2
|
+
* The "users" table is built-in. Passwords are platform-managed — never add a password field to a view.
|
|
3
|
+
* Signup uses a built-in form, not an Edit view.
|
|
4
|
+
* For verification/confirmation emails after registration, create a Trigger with event "Insert" on "users".
|
|
5
|
+
The trigger must use a Workflow action with a single step of type "run_js_code". Do NOT use the send_email action — it will not work for verification. The run_js_code step must contain exactly:
|
|
6
|
+
const { send_verification_email } = require("@saltcorn/data/models/email");
|
|
7
|
+
await send_verification_email(row, req);`;
|
|
8
|
+
|
|
9
|
+
class AppConstructorContextSkill {
|
|
10
|
+
static skill_name = "AppConstructor Context";
|
|
11
|
+
|
|
12
|
+
get skill_label() {
|
|
13
|
+
return "AppConstructor Context";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor(cfg) {
|
|
17
|
+
Object.assign(this, cfg);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async systemPrompt() {
|
|
21
|
+
return user_registration_hints;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = AppConstructorContextSkill;
|