@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
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const { eval_expression } = require("@saltcorn/data/models/expression");
|
|
2
|
+
const MetaData = require("@saltcorn/data/models/metadata");
|
|
3
|
+
const { interpolate } = require("@saltcorn/data/utils");
|
|
4
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
5
|
+
const { requirements_tool, task_tool } = require("./tools");
|
|
6
|
+
const { tool_choice } = require("./common");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
description: "Provide user feedback to the AppConstructor",
|
|
10
|
+
configFields: ({ table, mode }) => {
|
|
11
|
+
if (mode === "workflow") {
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
name: "title",
|
|
15
|
+
label: "Title",
|
|
16
|
+
sublabel:
|
|
17
|
+
"Feedback title. Use interpolations {{ }} to access variables in the context",
|
|
18
|
+
type: "String",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "description",
|
|
22
|
+
label: "Description",
|
|
23
|
+
sublabel:
|
|
24
|
+
"Feedback description. Use interpolations {{ }} to access variables in the context",
|
|
25
|
+
type: "String",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "url",
|
|
29
|
+
label: "URL",
|
|
30
|
+
sublabel:
|
|
31
|
+
"Feedback URL. Use interpolations {{ }} to access variables in the context",
|
|
32
|
+
type: "String",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
} else if (table) {
|
|
36
|
+
const textFields = table.fields
|
|
37
|
+
.filter((f) => f.type?.sql_name === "text")
|
|
38
|
+
.map((f) => f.name);
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
name: "title_field",
|
|
42
|
+
label: "Title field",
|
|
43
|
+
type: "String",
|
|
44
|
+
attributes: { options: textFields },
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "description_field",
|
|
48
|
+
label: "Description field",
|
|
49
|
+
type: "String",
|
|
50
|
+
attributes: { options: textFields },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "url_field",
|
|
54
|
+
label: "URL field",
|
|
55
|
+
type: "String",
|
|
56
|
+
attributes: { options: textFields },
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
run: async ({
|
|
62
|
+
row,
|
|
63
|
+
table,
|
|
64
|
+
user,
|
|
65
|
+
mode,
|
|
66
|
+
req,
|
|
67
|
+
configuration: {
|
|
68
|
+
title,
|
|
69
|
+
description,
|
|
70
|
+
url,
|
|
71
|
+
title_field,
|
|
72
|
+
description_field,
|
|
73
|
+
url_field,
|
|
74
|
+
},
|
|
75
|
+
}) => {
|
|
76
|
+
const use_title =
|
|
77
|
+
mode === "workflow" ? interpolate(title, row, user) : row[title_field];
|
|
78
|
+
const use_description =
|
|
79
|
+
mode === "workflow"
|
|
80
|
+
? interpolate(description, row, user)
|
|
81
|
+
: row[description_field];
|
|
82
|
+
const use_url =
|
|
83
|
+
mode === "workflow" ? interpolate(url, row, user) : row[url_field];
|
|
84
|
+
await MetaData.create({
|
|
85
|
+
type: "CopilotConstructMgr",
|
|
86
|
+
name: "feedback",
|
|
87
|
+
body: { title: use_title, description: use_description, url: use_url },
|
|
88
|
+
user_id: user?.id,
|
|
89
|
+
});
|
|
90
|
+
const spec = await MetaData.findOne({
|
|
91
|
+
type: "CopilotConstructMgr",
|
|
92
|
+
name: "spec",
|
|
93
|
+
});
|
|
94
|
+
if (!spec) return;
|
|
95
|
+
const reqAnswer = await getState().functions.llm_generate.run(
|
|
96
|
+
`The following application is being built:
|
|
97
|
+
|
|
98
|
+
Description: ${spec.body.description}
|
|
99
|
+
Audience: ${spec.body.audience}
|
|
100
|
+
Core features: ${spec.body.core_features}
|
|
101
|
+
Out of scope: ${spec.body.out_of_scope}
|
|
102
|
+
Visual style: ${spec.body.visual_style}
|
|
103
|
+
|
|
104
|
+
A new piece of feedback has come in from a user:
|
|
105
|
+
|
|
106
|
+
Title: ${use_title}
|
|
107
|
+
Description: ${use_description}
|
|
108
|
+
|
|
109
|
+
Now use the make_requirements tool to create a single or several (a single is prefered) new requirements that captures this new piece of feedback.
|
|
110
|
+
`,
|
|
111
|
+
{
|
|
112
|
+
tools: [requirements_tool],
|
|
113
|
+
...tool_choice("make_requirements"),
|
|
114
|
+
systemPrompt:
|
|
115
|
+
"You are a project manager. The user wants to build an application, and you must analyse their application description and any feedback available",
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
const tc = reqAnswer.getToolCalls()[0];
|
|
119
|
+
console.log("gotr new requiremenrts", tc.input.requirements);
|
|
120
|
+
|
|
121
|
+
for (const reqm of tc.input.requirements)
|
|
122
|
+
await MetaData.create({
|
|
123
|
+
type: "CopilotConstructMgr",
|
|
124
|
+
name: "requirement",
|
|
125
|
+
body: reqm,
|
|
126
|
+
user_id: req.user?.id,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const taskAnswer = await getState().functions.llm_generate.run(
|
|
130
|
+
`The following application is being built:
|
|
131
|
+
|
|
132
|
+
Description: ${spec.body.description}
|
|
133
|
+
Audience: ${spec.body.audience}
|
|
134
|
+
Core features: ${spec.body.core_features}
|
|
135
|
+
Out of scope: ${spec.body.out_of_scope}
|
|
136
|
+
Visual style: ${spec.body.visual_style}
|
|
137
|
+
|
|
138
|
+
This application will be implemented in Saltcorn, a database application development
|
|
139
|
+
environment.
|
|
140
|
+
|
|
141
|
+
A new piece of feedback has come in from a user:
|
|
142
|
+
|
|
143
|
+
Title: ${use_title}
|
|
144
|
+
Description: ${use_description}
|
|
145
|
+
|
|
146
|
+
A product manager has determined that the following requirements should be added to the list of application requirements:
|
|
147
|
+
|
|
148
|
+
${tc.input.requirements.map((r) => " * " + r.requirement).join("\n")}
|
|
149
|
+
|
|
150
|
+
Your plan for implementing this new fedback and requirements should not include any clarification or questions to the product owner. The
|
|
151
|
+
information you have been given so far is all that is available. Every step in the plan
|
|
152
|
+
should be immediately implementable in Saltcorn. You are writing the steps in the plan
|
|
153
|
+
for a person who is competent in using saltcorn but has no other business knowledge.
|
|
154
|
+
|
|
155
|
+
Now use the plan_tasks tool to create the tasks to implement this new feedback
|
|
156
|
+
`,
|
|
157
|
+
{
|
|
158
|
+
tools: [task_tool],
|
|
159
|
+
...tool_choice("plan_tasks"),
|
|
160
|
+
systemPrompt:
|
|
161
|
+
"You are a project manager. The user wants to build an application, and you must analyse their application description and any feedback available",
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
const tcTasks = taskAnswer.getToolCalls()[0];
|
|
165
|
+
console.log("got new tasks", tcTasks.input.tasks);
|
|
166
|
+
|
|
167
|
+
for (const task of tcTasks.input.tasks)
|
|
168
|
+
await MetaData.create({
|
|
169
|
+
type: "CopilotConstructMgr",
|
|
170
|
+
name: "task",
|
|
171
|
+
body: task,
|
|
172
|
+
user_id: req.user?.id,
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
const Field = require("@saltcorn/data/models/field");
|
|
2
|
+
const Table = require("@saltcorn/data/models/table");
|
|
3
|
+
const Form = require("@saltcorn/data/models/form");
|
|
4
|
+
const MetaData = require("@saltcorn/data/models/metadata");
|
|
5
|
+
const View = require("@saltcorn/data/models/view");
|
|
6
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
7
|
+
const { findType } = require("@saltcorn/data/models/discovery");
|
|
8
|
+
const { save_menu_items } = require("@saltcorn/data/models/config");
|
|
9
|
+
const db = require("@saltcorn/data/db");
|
|
10
|
+
const WorkflowRun = require("@saltcorn/data/models/workflow_run");
|
|
11
|
+
const {
|
|
12
|
+
localeDateTime,
|
|
13
|
+
renderForm,
|
|
14
|
+
mkTable,
|
|
15
|
+
post_delete_btn,
|
|
16
|
+
} = require("@saltcorn/markup");
|
|
17
|
+
const {
|
|
18
|
+
div,
|
|
19
|
+
script,
|
|
20
|
+
domReady,
|
|
21
|
+
pre,
|
|
22
|
+
code,
|
|
23
|
+
input,
|
|
24
|
+
h4,
|
|
25
|
+
style,
|
|
26
|
+
h5,
|
|
27
|
+
button,
|
|
28
|
+
text_attr,
|
|
29
|
+
i,
|
|
30
|
+
p,
|
|
31
|
+
span,
|
|
32
|
+
small,
|
|
33
|
+
form,
|
|
34
|
+
textarea,
|
|
35
|
+
} = require("@saltcorn/markup/tags");
|
|
36
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
37
|
+
const renderLayout = require("@saltcorn/markup/layout");
|
|
38
|
+
const { viewname } = require("./common");
|
|
39
|
+
|
|
40
|
+
const feedbackList = async (req) => {
|
|
41
|
+
const errs = await MetaData.find({
|
|
42
|
+
type: "CopilotConstructMgr",
|
|
43
|
+
name: "feedback",
|
|
44
|
+
});
|
|
45
|
+
if (errs.length) {
|
|
46
|
+
return div(
|
|
47
|
+
{ class: "mt-2" },
|
|
48
|
+
mkTable(
|
|
49
|
+
[
|
|
50
|
+
{
|
|
51
|
+
label: "Title",
|
|
52
|
+
key: (m) => m.body.title,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "Description",
|
|
56
|
+
key: (m) => m.body.description,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
{
|
|
60
|
+
label: "Delete",
|
|
61
|
+
key: (r) =>
|
|
62
|
+
button(
|
|
63
|
+
{
|
|
64
|
+
class: "btn btn-outline-danger btn-sm",
|
|
65
|
+
onclick: `view_post("${viewname}", "del_feedback", {id:${r.id}})`,
|
|
66
|
+
},
|
|
67
|
+
i({ class: "fas fa-trash-alt" }),
|
|
68
|
+
),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
errs,
|
|
72
|
+
),
|
|
73
|
+
button(
|
|
74
|
+
{
|
|
75
|
+
class: "btn btn-outline-danger",
|
|
76
|
+
onclick: `view_post("${viewname}", "del_all_feedback")`,
|
|
77
|
+
},
|
|
78
|
+
"Delete all",
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
return div({ class: "mt-2" }, p("No feedback"));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const del_feedback = async (table_id, viewname, config, body, { req, res }) => {
|
|
87
|
+
const r = await MetaData.findOne({
|
|
88
|
+
id: body.id,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (!r) throw new Error("Feedback not found");
|
|
92
|
+
await r.delete();
|
|
93
|
+
return { json: { reload_page: true } };
|
|
94
|
+
};
|
|
95
|
+
const del_all_feedback = async (
|
|
96
|
+
table_id,
|
|
97
|
+
viewname,
|
|
98
|
+
config,
|
|
99
|
+
body,
|
|
100
|
+
{ req, res },
|
|
101
|
+
) => {
|
|
102
|
+
const rs = await MetaData.find({
|
|
103
|
+
type: "CopilotConstructMgr",
|
|
104
|
+
name: "feedback",
|
|
105
|
+
});
|
|
106
|
+
for (const r of rs) await r.delete();
|
|
107
|
+
return { json: { reload_page: true } };
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const feedback_routes = { del_feedback, del_all_feedback };
|
|
111
|
+
|
|
112
|
+
module.exports = { feedbackList, feedback_routes };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const Field = require("@saltcorn/data/models/field");
|
|
2
|
+
const Table = require("@saltcorn/data/models/table");
|
|
3
|
+
const Form = require("@saltcorn/data/models/form");
|
|
4
|
+
const MetaData = require("@saltcorn/data/models/metadata");
|
|
5
|
+
const View = require("@saltcorn/data/models/view");
|
|
6
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
7
|
+
const { findType } = require("@saltcorn/data/models/discovery");
|
|
8
|
+
const { save_menu_items } = require("@saltcorn/data/models/config");
|
|
9
|
+
const db = require("@saltcorn/data/db");
|
|
10
|
+
const WorkflowRun = require("@saltcorn/data/models/workflow_run");
|
|
11
|
+
const {
|
|
12
|
+
localeDateTime,
|
|
13
|
+
renderForm,
|
|
14
|
+
mkTable,
|
|
15
|
+
post_delete_btn,
|
|
16
|
+
} = require("@saltcorn/markup");
|
|
17
|
+
const {
|
|
18
|
+
div,
|
|
19
|
+
script,
|
|
20
|
+
domReady,
|
|
21
|
+
pre,
|
|
22
|
+
code,
|
|
23
|
+
input,
|
|
24
|
+
h4,
|
|
25
|
+
style,
|
|
26
|
+
h5,
|
|
27
|
+
button,
|
|
28
|
+
text_attr,
|
|
29
|
+
i,
|
|
30
|
+
p,
|
|
31
|
+
span,
|
|
32
|
+
small,
|
|
33
|
+
form,
|
|
34
|
+
textarea,
|
|
35
|
+
} = require("@saltcorn/markup/tags");
|
|
36
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
37
|
+
const renderLayout = require("@saltcorn/markup/layout");
|
|
38
|
+
const { viewname } = require("./common");
|
|
39
|
+
|
|
40
|
+
const progressList = async (req) => {
|
|
41
|
+
const errs = await MetaData.find(
|
|
42
|
+
{
|
|
43
|
+
type: "CopilotConstructMgr",
|
|
44
|
+
name: "progress",
|
|
45
|
+
},
|
|
46
|
+
{ orderBy: "written_at" },
|
|
47
|
+
);
|
|
48
|
+
const relDateFieldview = getState().types.Date.fieldviews.relative;
|
|
49
|
+
if (errs.length) {
|
|
50
|
+
return div(
|
|
51
|
+
{ class: "mt-2" },
|
|
52
|
+
mkTable(
|
|
53
|
+
[
|
|
54
|
+
{
|
|
55
|
+
label: "Title",
|
|
56
|
+
key: (m) => relDateFieldview.run(m.written_at, req),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Progress",
|
|
60
|
+
key: (m) => m.body.text,
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
{
|
|
64
|
+
label: "Delete",
|
|
65
|
+
key: (r) =>
|
|
66
|
+
button(
|
|
67
|
+
{
|
|
68
|
+
class: "btn btn-outline-danger btn-sm",
|
|
69
|
+
onclick: `view_post("${viewname}", "del_progress", {id:${r.id}})`,
|
|
70
|
+
},
|
|
71
|
+
i({ class: "fas fa-trash-alt" }),
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
errs,
|
|
76
|
+
),
|
|
77
|
+
button(
|
|
78
|
+
{
|
|
79
|
+
class: "btn btn-outline-danger",
|
|
80
|
+
onclick: `view_post("${viewname}", "del_all_progress")`,
|
|
81
|
+
},
|
|
82
|
+
"Delete all",
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
} else {
|
|
86
|
+
return div({ class: "mt-2" }, p("No progress"));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const del_progress = async (table_id, viewname, config, body, { req, res }) => {
|
|
91
|
+
const r = await MetaData.findOne({
|
|
92
|
+
id: body.id,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!r) throw new Error("Progress not found");
|
|
96
|
+
await r.delete();
|
|
97
|
+
return { json: { reload_page: true } };
|
|
98
|
+
};
|
|
99
|
+
const del_all_progress = async (
|
|
100
|
+
table_id,
|
|
101
|
+
viewname,
|
|
102
|
+
config,
|
|
103
|
+
body,
|
|
104
|
+
{ req, res },
|
|
105
|
+
) => {
|
|
106
|
+
const rs = await MetaData.find({
|
|
107
|
+
type: "CopilotConstructMgr",
|
|
108
|
+
name: "progress",
|
|
109
|
+
});
|
|
110
|
+
for (const r of rs) await r.delete();
|
|
111
|
+
return { json: { reload_page: true } };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const progress_routes = { del_progress, del_all_progress };
|
|
115
|
+
|
|
116
|
+
module.exports = { progressList, progress_routes };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const saltcorn_description = `This application will be implemented in Saltcorn, a database application development
|
|
2
|
+
environment.
|
|
3
|
+
|
|
4
|
+
Saltcorn applications contain the following entity types:
|
|
5
|
+
|
|
6
|
+
* Tables: These are relational database tables and consists of fields of specified types and rows
|
|
7
|
+
with a value for each field. Fields optionally can be required and/or unique. Every field has a name,
|
|
8
|
+
which is a an identifier that is balid in both JavaScript and SQL, and a label, which is any short
|
|
9
|
+
user-friendly string. Every table has a primary key
|
|
10
|
+
(composite primary keys are not supported) which by default is an auto-incrementing integer with
|
|
11
|
+
name \`id\` and label ID. Fields can also be of Key type (foreign key) referencing a primary key
|
|
12
|
+
in another table, or its own table for a self-join. Tables can have
|
|
13
|
+
calculated fields, which can be stored or non-stored. Both stored and non-stored fields are
|
|
14
|
+
defined by a JavaScript expression, but only stored fields can reference other tables with join
|
|
15
|
+
fields and aggregations.
|
|
16
|
+
|
|
17
|
+
* Views: Views are elementary user interfaces into a database table. A view is defined by applying a
|
|
18
|
+
view template (also sometime called a view pattern, the two are synonymous) to a table with a certain
|
|
19
|
+
configuration. The view template defines the fundamental relationship between the UI and the table. For
|
|
20
|
+
instance, the Show view template displays a single database row, the Edit view template is a form that
|
|
21
|
+
can create a new row or edit an existing row, the List view template displays multiple rows in a grid.
|
|
22
|
+
Views can embed views, for instance Show can embed another row through a Key field relationship, or
|
|
23
|
+
some views are defined by an underlying view. For instance, the Feed view repeats an underlying view
|
|
24
|
+
for multiple tables. New viewtemplates are provided by plugin modules.
|
|
25
|
+
|
|
26
|
+
* Triggers: Triggers connect elementary actions (provided by plugin modules) to either a button in the
|
|
27
|
+
user interface, or a periodic (hourly, daily etc) or table (for instance insert on specifc table) event.
|
|
28
|
+
The elementary action each has a number of configuration fields that must be filled in after connecting
|
|
29
|
+
the action to an event, table or button.
|
|
30
|
+
|
|
31
|
+
* Page: A page has static content but can also embed views for synamic content. Pages can be either
|
|
32
|
+
defined by a Saltcorn layout, for pages that can be edited with drag and drop, or by HTML for more
|
|
33
|
+
flexible graphic designs. HTML pages should be used for landing pages.
|
|
34
|
+
|
|
35
|
+
* Plugin modules: plugin modules can supply new field types, view templates or actions. Before they can be used,
|
|
36
|
+
they need to be installed before they can be used. A plugin may also have a configuration that sets options
|
|
37
|
+
for that plugin. Layout themes is Saltcorn are plugin modules.
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const existing_tables_list = (tables) => {
|
|
41
|
+
const tableLines = [];
|
|
42
|
+
tables.forEach((table) => {
|
|
43
|
+
const fieldLines = table.fields.map(
|
|
44
|
+
(f) =>
|
|
45
|
+
` * ${f.name} with type: ${f.pretty_type}.${f.description ? ` ${f.description}` : ""}`,
|
|
46
|
+
);
|
|
47
|
+
tableLines.push(
|
|
48
|
+
`${table.name}${
|
|
49
|
+
table.description ? `: ${table.description}.` : "."
|
|
50
|
+
} Contains the following fields:\n${fieldLines.join("\n")}`,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
return `The database already contains the following tables:
|
|
54
|
+
|
|
55
|
+
${tableLines.join("\n\n")}`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = { saltcorn_description, existing_tables_list };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const Field = require("@saltcorn/data/models/field");
|
|
2
|
+
const Table = require("@saltcorn/data/models/table");
|
|
3
|
+
const Form = require("@saltcorn/data/models/form");
|
|
4
|
+
const MetaData = require("@saltcorn/data/models/metadata");
|
|
5
|
+
const View = require("@saltcorn/data/models/view");
|
|
6
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
7
|
+
const { findType } = require("@saltcorn/data/models/discovery");
|
|
8
|
+
const { save_menu_items } = require("@saltcorn/data/models/config");
|
|
9
|
+
const db = require("@saltcorn/data/db");
|
|
10
|
+
const WorkflowRun = require("@saltcorn/data/models/workflow_run");
|
|
11
|
+
const {
|
|
12
|
+
localeDateTime,
|
|
13
|
+
renderForm,
|
|
14
|
+
mkTable,
|
|
15
|
+
post_delete_btn,
|
|
16
|
+
} = require("@saltcorn/markup");
|
|
17
|
+
const {
|
|
18
|
+
div,
|
|
19
|
+
script,
|
|
20
|
+
domReady,
|
|
21
|
+
pre,
|
|
22
|
+
code,
|
|
23
|
+
input,
|
|
24
|
+
h4,
|
|
25
|
+
style,
|
|
26
|
+
h5,
|
|
27
|
+
button,
|
|
28
|
+
text_attr,
|
|
29
|
+
i,
|
|
30
|
+
p,
|
|
31
|
+
span,
|
|
32
|
+
small,
|
|
33
|
+
form,
|
|
34
|
+
textarea,
|
|
35
|
+
} = require("@saltcorn/markup/tags");
|
|
36
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
37
|
+
const renderLayout = require("@saltcorn/markup/layout");
|
|
38
|
+
const { viewname, tool_choice } = require("./common");
|
|
39
|
+
const { requirements_tool } = require("./tools");
|
|
40
|
+
|
|
41
|
+
const requirementsList = async (req) => {
|
|
42
|
+
const rs = await MetaData.find(
|
|
43
|
+
{
|
|
44
|
+
type: "CopilotConstructMgr",
|
|
45
|
+
name: "requirement",
|
|
46
|
+
},
|
|
47
|
+
{ orderBy: "written_at" },
|
|
48
|
+
);
|
|
49
|
+
const starFieldview = getState().types.Integer.fieldviews.show_star_rating;
|
|
50
|
+
|
|
51
|
+
if (rs.length) {
|
|
52
|
+
return div(
|
|
53
|
+
{ class: "mt-2" },
|
|
54
|
+
mkTable(
|
|
55
|
+
[
|
|
56
|
+
{ label: "Requirement", key: (m) => m.body.requirement },
|
|
57
|
+
{
|
|
58
|
+
label: "Priority",
|
|
59
|
+
key: (m) =>
|
|
60
|
+
starFieldview.run(m.body.priority, req, { min: 1, max: 5 }),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "Delete",
|
|
64
|
+
key: (r) =>
|
|
65
|
+
button(
|
|
66
|
+
{
|
|
67
|
+
class: "btn btn-outline-danger btn-sm",
|
|
68
|
+
onclick: `view_post("${viewname}", "del_req", {id:${r.id}})`,
|
|
69
|
+
},
|
|
70
|
+
i({ class: "fas fa-trash-alt" }),
|
|
71
|
+
),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
rs,
|
|
75
|
+
),
|
|
76
|
+
button(
|
|
77
|
+
{
|
|
78
|
+
class: "btn btn-outline-danger mb-4",
|
|
79
|
+
onclick: `view_post("${viewname}", "del_all_reqs")`,
|
|
80
|
+
},
|
|
81
|
+
"Delete all",
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
return div(
|
|
86
|
+
{ class: "mt-2" },
|
|
87
|
+
p("No requirements found"),
|
|
88
|
+
button(
|
|
89
|
+
{
|
|
90
|
+
class: "btn btn-primary",
|
|
91
|
+
onclick: `press_store_button(this);view_post("${viewname}", "gen_reqs")`,
|
|
92
|
+
},
|
|
93
|
+
"Generate requirements",
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const gen_reqs = async (table_id, viewname, config, body, { req, res }) => {
|
|
100
|
+
const spec = await MetaData.findOne({
|
|
101
|
+
type: "CopilotConstructMgr",
|
|
102
|
+
name: "spec",
|
|
103
|
+
});
|
|
104
|
+
if (!spec) throw new Error("Specification not found");
|
|
105
|
+
const answer = await getState().functions.llm_generate.run(
|
|
106
|
+
`Generate the requirements for this application:
|
|
107
|
+
|
|
108
|
+
Description: ${spec.body.description}
|
|
109
|
+
Audience: ${spec.body.audience}
|
|
110
|
+
Core features: ${spec.body.core_features}
|
|
111
|
+
Out of scope: ${spec.body.out_of_scope}
|
|
112
|
+
Visual style: ${spec.body.visual_style}
|
|
113
|
+
|
|
114
|
+
Now use the make_requirements tool to list the requirements for this software application
|
|
115
|
+
`,
|
|
116
|
+
{
|
|
117
|
+
tools: [requirements_tool],
|
|
118
|
+
...tool_choice("make_requirements"),
|
|
119
|
+
systemPrompt:
|
|
120
|
+
"You are a project manager. The user wants to build an application, and you must analyse their application description",
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const tc = answer.getToolCalls()[0];
|
|
125
|
+
|
|
126
|
+
for (const reqm of tc.input.requirements)
|
|
127
|
+
await MetaData.create({
|
|
128
|
+
type: "CopilotConstructMgr",
|
|
129
|
+
name: "requirement",
|
|
130
|
+
body: reqm,
|
|
131
|
+
user_id: req.user?.id,
|
|
132
|
+
});
|
|
133
|
+
return { json: { reload_page: true } };
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const del_req = async (table_id, viewname, config, body, { req, res }) => {
|
|
137
|
+
const r = await MetaData.findOne({
|
|
138
|
+
id: body.id,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!r) throw new Error("Requirement not found");
|
|
142
|
+
await r.delete();
|
|
143
|
+
return { json: { reload_page: true } };
|
|
144
|
+
};
|
|
145
|
+
const del_all_reqs = async (table_id, viewname, config, body, { req, res }) => {
|
|
146
|
+
const rs = await MetaData.find({
|
|
147
|
+
type: "CopilotConstructMgr",
|
|
148
|
+
name: "requirement",
|
|
149
|
+
});
|
|
150
|
+
for (const r of rs) await r.delete();
|
|
151
|
+
return { json: { reload_page: true } };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const req_routes = { gen_reqs, del_req, del_all_reqs };
|
|
155
|
+
|
|
156
|
+
module.exports = { requirementsList, req_routes };
|