Skip to content

Commit 8d47980

Browse files
committed
- working basic mvp draft
1 parent ffe2db1 commit 8d47980

7 files changed

+992
-6
lines changed

actions/create-react-website.hbs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
```description
2+
Creates a reactJS webapp project.
3+
```
4+
15
```pre:js
26
// javascript code to run on project before executing the template prompt
37
// contains a 'context' object with the keys:
@@ -7,9 +11,13 @@
711
// you also have some methods for asking info to the user like (await prompt)
812
// anything queried to the user is translated to the user's language using the LLM
913
const project_name = await prompt('What is the name of your project?')
10-
const app_name = await queryLLM('Create a reactJS app name for the following project: ' + project_name);
14+
const app_name = await queryLLM('Create a short folder name (max 10 chars without spaces) for the following text: ' + project_name,
15+
z.object({
16+
name: z.string().describe('the reactjs folder name for the text'),
17+
})
18+
);
1119
return {
12-
app_name
20+
app_name: app_name.data.name
1321
}
1422
```
1523

actions/index.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// read all .hbs files in the current directory
2+
// parse the md code-blocks looking for 'description' lang field blocks
3+
// create a prompt for the CLI to choose the best action template based on the user input
4+
//
5+
const codeblocks = require('code-blocks');
6+
const path = require('path');
7+
const fs = require('fs').promises;
8+
const { glob } = require("glob");
9+
10+
class indexFolder {
11+
constructor(inputText) {
12+
this.code_blocks = [];
13+
this.input = inputText;
14+
this.currentFolder = process.cwd();
15+
}
16+
17+
async initialize() {
18+
// traverse current folder and read all .hbs files
19+
const files = await glob('**/*.hbs', { cwd: this.currentFolder, nodir:true, absolute:true });
20+
for (const file of files) {
21+
// read 'file' contents
22+
const content = await fs.readFile(file, 'utf-8');
23+
// parse the md code-blocks looking for 'description' lang field blocks
24+
const code_blocks = await codeblocks.fromString(content);
25+
for (const block of code_blocks) {
26+
if (block.lang === 'description') {
27+
this.code_blocks.push({file, content: block.value});
28+
}
29+
}
30+
}
31+
}
32+
33+
async getPrompt() {
34+
await this.initialize();
35+
let prompt = `Given the following user input text:\n\n${this.input}\n\nPlease select the most suitable action template file from the availables and their description relateness to the user text:\n\n`;
36+
for (const block of this.code_blocks) {
37+
prompt += `file:${block.file}\ndescription:\n${block.content}\n\n`;
38+
}
39+
return prompt;
40+
}
41+
}
42+
43+
module.exports = indexFolder;

actions/write-readme.hbs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
```description
2+
Generates a README file for a project.
3+
```
14
Project Path: {{ absolute_code_path }}
25

36
I'd like you to generate a high-quality README file for this project, suitable for hosting on GitHub. Analyze the codebase to understand the purpose, functionality, and structure of the project.
@@ -41,10 +44,11 @@ Feel free to infer reasonable details if needed, but try to stick to what can be
4144

4245
```js
4346
// nodejs code to run after getting results (runs within an isolated async function block)
44-
// context vars: results (schema), absolute_code_path, files, source_tree, etc (all the template vars)
47+
// context vars: schema (results), absolute_code_path, files, source_tree, etc (all the template vars)
4548
const fs = require('fs').promises;
46-
// save 'readme' results.readme contents to disk (abs)
47-
await fs.writeFile(`${absolute_code_path}/README.md`, results.readme, 'utf8');
49+
// save 'readme' schema.readme contents to disk (abs)
50+
await fs.writeFile(`${absolute_code_path}/README.md`, schema.readme, 'utf8');
51+
// if you return an object here, it will be available for the next code block
4852
```
4953

5054
```bash

helpers/codeBlocks.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// helper for executing HBS action code blocks and giving them context
2+
const safeEval = require('safe-eval');
3+
const { z } = require('zod');
4+
5+
class codeBlocks {
6+
constructor() {
7+
this.code_blocks = [];
8+
this.currentFolder = process.cwd();
9+
this.x_console = new (require('@concepto/console'))();
10+
this.lastEval = '';
11+
}
12+
13+
async executeNode(context=null,code=null) {
14+
// context=object with variables returned by previous code blocks
15+
const prompts = require('prompts');
16+
let wAsync = `(async function() {
17+
${code}
18+
})();\n`;
19+
const self = this;
20+
// returns methods,vars available within the code blocks contexts
21+
let context_ = {
22+
process,
23+
z,
24+
console: {
25+
log: function(message,data) {
26+
self.x_console.setColorTokens({
27+
'*':'yellow',
28+
'#':'cyan',
29+
'@':'green'
30+
});
31+
self.x_console.out({ color:'cyan', message:self.x_console.colorize(message), data });
32+
},
33+
},
34+
prompt: async(question='',validation=null)=>{
35+
const resp = (
36+
await prompts({
37+
type: 'text',
38+
name: 'value',
39+
message: this.x_console.colorize(question),
40+
validate: (value) => {
41+
if (validation) return validation(value);
42+
return true
43+
}
44+
})
45+
).value;
46+
return resp;
47+
}
48+
};
49+
if (context) {
50+
context_ = {...context_,...context};
51+
}
52+
// execute code block on an isolated async context
53+
this.lastEval = wAsync;
54+
let tmp = await safeEval(wAsync, context_);
55+
return tmp;
56+
//
57+
}
58+
59+
async executeBash(context={},code=null) {
60+
console.log('TODO executeBash');
61+
}
62+
}
63+
64+
module.exports = codeBlocks;

index.js

+45-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const yargs = require('yargs/yargs');
44
const { hideBin } = require('yargs/helpers');
5+
const actionsIndex = require('./actions');
56

67
const argv = yargs(hideBin(process.argv))
78
.usage('Usage: $0 <command> [options]')
@@ -46,11 +47,54 @@ const { z } = require('zod');
4647
z.object({
4748
is_action: z.boolean().describe('True if the input is an action, False if the input is a question'),
4849
is_question: z.boolean().describe('True if the input is a question, True if the input is a question'),
49-
language: z.string().describe('The language of the input in 2 letters (e.g. en, fr, es, etc.)'),
50+
language: z.string().describe('2 letters language of the input (ex. en, fr, sp, etc.)'),
5051
})
5152
);
5253
console.log('is action or question?',action_or_question.data);
5354
// 1) if the input is an action, select the best action template from the availables and run it
55+
if (action_or_question.data.is_action) {
56+
const user_action = new actionsIndex(argv.input);
57+
const prompt = await user_action.getPrompt();
58+
let additional_context = {
59+
queryLLM:async(question,schema)=>{
60+
return await general.queryLLM(question,schema);
61+
}
62+
};
63+
const action = await general.queryLLM(prompt,
64+
z.object({
65+
file: z.string().describe('the choosen template file'),
66+
reason: z.string().describe('the reason why the template is the best for the user input'),
67+
})
68+
);
69+
console.log('action',action.data);
70+
// render the template using code2prompt
71+
const actioncode = new code2prompt({
72+
path: currentWorkingDirectory,
73+
template: action.data.file,
74+
extensions: [],
75+
ignore: ["**/node_modules/**","**/*.png","**/*.jpg","**/*.gif"],
76+
OPENAI_KEY: process.env.OPENAI_KEY
77+
});
78+
// get the code blocks
79+
const code_helper = new (require('./helpers/codeBlocks'));
80+
const context_prompt = await actioncode.generateContextPrompt(null,true);
81+
const code_blocks = await actioncode.getCodeBlocks();
82+
// check if we have 'pre:' code blocks (must run before the template)
83+
//console.log('code_blocks for choosen template',code_blocks);
84+
for (const block of code_blocks) {
85+
// if block.lang contains 'pre:'
86+
if (block.lang.startsWith('pre:js')) {
87+
const code_executed = await code_helper.executeNode(additional_context,block.code);
88+
// if code_executed is an object
89+
if (typeof code_executed === 'object') {
90+
console.log('adding context from pre:js code block',code_executed);
91+
additional_context = {...additional_context,...code_executed};
92+
}
93+
}
94+
}
95+
// query the template
96+
// check if we have none 'pre:' code blocks (must run after the template)
97+
}
5498
// 2) if the input is a question, run the question to the model with the 'default-template' and return the response
5599

56100
console.log(`Processing input: ${argv.input}`,currentWorkingDirectory,userOS);

0 commit comments

Comments
 (0)