@learnpack/learnpack 5.0.22 → 5.0.23

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/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
21
21
  $ learnpack COMMAND
22
22
  running command...
23
23
  $ learnpack (-v|--version|version)
24
- @learnpack/learnpack/5.0.22 win32-x64 node-v20.16.0
24
+ @learnpack/learnpack/5.0.23 win32-x64 node-v20.16.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -75,7 +75,7 @@ DESCRIPTION
75
75
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
76
76
  ```
77
77
 
78
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\audit.ts)_
78
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\audit.ts)_
79
79
 
80
80
  ## `learnpack clean`
81
81
 
@@ -90,7 +90,7 @@ DESCRIPTION
90
90
  Extra documentation goes here
91
91
  ```
92
92
 
93
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\clean.ts)_
93
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\clean.ts)_
94
94
 
95
95
  ## `learnpack download [PACKAGE]`
96
96
 
@@ -108,7 +108,7 @@ DESCRIPTION
108
108
  Extra documentation goes here
109
109
  ```
110
110
 
111
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\download.ts)_
111
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\download.ts)_
112
112
 
113
113
  ## `learnpack help [COMMAND]`
114
114
 
@@ -140,7 +140,7 @@ OPTIONS
140
140
  -y, --yes Skip all prompts and initialize an empty project
141
141
  ```
142
142
 
143
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\init.ts)_
143
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\init.ts)_
144
144
 
145
145
  ## `learnpack login [PACKAGE]`
146
146
 
@@ -158,7 +158,7 @@ DESCRIPTION
158
158
  Extra documentation goes here
159
159
  ```
160
160
 
161
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\login.ts)_
161
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\login.ts)_
162
162
 
163
163
  ## `learnpack logout [PACKAGE]`
164
164
 
@@ -176,7 +176,7 @@ DESCRIPTION
176
176
  Extra documentation goes here
177
177
  ```
178
178
 
179
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\logout.ts)_
179
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\logout.ts)_
180
180
 
181
181
  ## `learnpack plugins`
182
182
 
@@ -307,7 +307,7 @@ OPTIONS
307
307
  -h, --help show CLI help
308
308
  ```
309
309
 
310
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\publish.ts)_
310
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\publish.ts)_
311
311
 
312
312
  ## `learnpack start`
313
313
 
@@ -329,7 +329,7 @@ OPTIONS
329
329
  -y, --yes Skip all prompts and initialize an empty project
330
330
  ```
331
331
 
332
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\start.ts)_
332
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\start.ts)_
333
333
 
334
334
  ## `learnpack test [EXERCISESLUG]`
335
335
 
@@ -346,7 +346,7 @@ OPTIONS
346
346
  -y, --yes Skip all prompts and initialize an empty project
347
347
  ```
348
348
 
349
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\test.ts)_
349
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\test.ts)_
350
350
 
351
351
  ## `learnpack translate`
352
352
 
@@ -360,7 +360,7 @@ OPTIONS
360
360
  -y, --yes Skip all prompts and initialize an empty project
361
361
  ```
362
362
 
363
- _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.22/src\commands\translate.ts)_
363
+ _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.23/src\commands\translate.ts)_
364
364
  <!-- commandsstop -->
365
365
 
366
366
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -23,8 +23,23 @@ const slugify = (text) => {
23
23
  .replace(/\s+/g, "-")
24
24
  .replace(/[^\w-]+/g, "");
25
25
  };
26
- const getExNumber = (index) => {
27
- return index < 10 ? `0${index}` : `${index}`;
26
+ const getExInfo = (title) => {
27
+ // Example title: '1.0 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]'
28
+ let [exNumber, exTitle] = title.split(" - ");
29
+ // Extract kind and description
30
+ const kindMatch = exTitle.match(/\[(.*?):(.*?)]/);
31
+ const kind = kindMatch ? kindMatch[1].trim().toLowerCase() : "read";
32
+ const description = kindMatch ? kindMatch[2].trim() : "";
33
+ exNumber = exNumber.trim();
34
+ // Clean title
35
+ exTitle = exTitle.replace((kindMatch === null || kindMatch === void 0 ? void 0 : kindMatch[0]) || "", "").trim();
36
+ exTitle = slugify(exTitle);
37
+ return {
38
+ exNumber,
39
+ kind,
40
+ description,
41
+ exTitle,
42
+ };
28
43
  };
29
44
  function extractImagesFromMarkdown(markdown) {
30
45
  const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g;
@@ -66,6 +81,38 @@ async function createPreviewReadme(tutorialDir, packageInfo, rigoToken, readmeCo
66
81
  });
67
82
  fs.writeFileSync(path.join(tutorialDir, readmeFilename), readmeContent.answer);
68
83
  }
84
+ const initializeInteractiveCreation = async (rigoToken, courseInfo) => {
85
+ let prevInteractions = "";
86
+ let isReady = false;
87
+ let currentSteps = [];
88
+ while (!isReady) {
89
+ // eslint-disable-next-line
90
+ const res = await (0, rigoActions_1.interactiveCreation)(rigoToken, {
91
+ courseInfo: courseInfo,
92
+ prevInteractions: prevInteractions,
93
+ });
94
+ currentSteps = res.parsed.listOfSteps;
95
+ isReady = res.parsed.ready;
96
+ if (!isReady) {
97
+ console.log(currentSteps);
98
+ console_1.default.info(`AI: ${res.parsed.aiMessage}`);
99
+ prevInteractions += `\nAI: ${res.parsed.aiMessage}`;
100
+ // eslint-disable-next-line
101
+ const userMessage = await prompts([
102
+ {
103
+ type: "text",
104
+ name: "userMessage",
105
+ message: "Your message: ",
106
+ },
107
+ ]);
108
+ prevInteractions += `\nUser: ${userMessage.userMessage}`;
109
+ }
110
+ }
111
+ return {
112
+ currentSteps,
113
+ prevInteractions,
114
+ };
115
+ };
69
116
  const handleAILogic = async (tutorialDir, packageInfo) => {
70
117
  console_1.default.info("Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/creators");
71
118
  fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"));
@@ -108,23 +155,6 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
108
155
  process.exit(1);
109
156
  }
110
157
  console_1.default.success("🎉 Let's begin this learning journey!");
111
- const aiChoices = await prompts([
112
- {
113
- type: "text",
114
- name: "tutorialAbout",
115
- message: "What kind of tutorial do you want to create? Please expand a little on the content, the outcome and the goal of the tutorial",
116
- initial: "",
117
- },
118
- {
119
- type: "text",
120
- name: "exercisesNumber",
121
- message: "How many steps or exercises do you want? Please provide a number",
122
- validate: (value) => {
123
- const n = Math.floor(Number(value));
124
- return n !== Number.POSITIVE_INFINITY && String(n) === value && n > 0;
125
- },
126
- },
127
- ]);
128
158
  const packageContext = `
129
159
  \n
130
160
  The following information comes from user inputs
@@ -136,28 +166,34 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
136
166
 
137
167
  Use it to generate more relevant exercises
138
168
  `;
139
- const inputs = {
140
- tutorial_about: aiChoices.tutorialAbout + packageContext,
141
- number_of_exercises: aiChoices.exercisesNumber,
142
- };
143
- console_1.default.info("Creating lessons...");
144
- const res = await (0, rigoActions_1.getExercisesNames)(rigoToken, inputs);
169
+ const { currentSteps } = await initializeInteractiveCreation(rigoToken, packageContext);
145
170
  const exercisesDir = path.join(tutorialDir, "exercises");
146
171
  fs.ensureDirSync(exercisesDir);
147
- for (const [index, exercise] of res.parsed.exercises.entries()) {
148
- const exerciseDir = path.join(exercisesDir, `${getExNumber(index)}-${slugify(exercise)}`);
172
+ console_1.default.info("Creating lessons...");
173
+ for (const [index, exercise] of currentSteps.entries()) {
174
+ const { exNumber, exTitle } = getExInfo(exercise);
175
+ const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`);
149
176
  fs.ensureDirSync(exerciseDir);
150
177
  }
151
- const exercisePromises = res.parsed.exercises.map(async (exercise, index) => {
152
- const exerciseDir = path.join(exercisesDir, `${getExNumber(index)}-${slugify(exercise)}`);
153
- const readme = await (0, rigoActions_1.createReadme)(rigoToken, {
154
- title: `\`${getExNumber(index)}\` ${exercise}`,
178
+ const exercisePromises = currentSteps.map(async (exercise, index) => {
179
+ const { exNumber, exTitle, kind, description } = getExInfo(exercise);
180
+ const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`);
181
+ const readme = await (0, rigoActions_1.readmeCreator)(rigoToken, {
182
+ title: `${exNumber} - ${exTitle}`,
155
183
  output_lang: "en",
156
- list_of_exercises: res.parsed.exercises.join(","),
157
- tutorial_description: aiChoices.tutorialAbout,
184
+ list_of_exercises: currentSteps.join(","),
185
+ tutorial_description: packageContext,
186
+ lesson_description: description,
187
+ kind: kind.toLowerCase(),
158
188
  });
159
189
  const readmeFilename = "README.md";
160
190
  fs.writeFileSync(path.join(exerciseDir, readmeFilename), readme.parsed.content);
191
+ if (kind.toLowerCase() === "code") {
192
+ const codeFile = await (0, rigoActions_1.createCodeFile)(rigoToken, {
193
+ readme: readme.parsed.content,
194
+ });
195
+ fs.writeFileSync(path.join(exerciseDir, `app.${codeFile.parsed.extension.replace(".", "")}`), codeFile.parsed.content);
196
+ }
161
197
  return readme.parsed.content;
162
198
  });
163
199
  let imagesArray = [];
@@ -1,9 +1,10 @@
1
- export declare const getExercisesNames: (token: string, inputs: object) => Promise<any>;
2
1
  type TCreateReadmeInputs = {
3
2
  title: string;
4
3
  output_lang: string;
5
4
  list_of_exercises: string;
6
5
  tutorial_description: string;
6
+ include_quiz: string;
7
+ lesson_description: string;
7
8
  };
8
9
  export declare const createReadme: (token: string, inputs: TCreateReadmeInputs) => Promise<any>;
9
10
  export declare const hasCreatorPermission: (token: string) => Promise<boolean>;
@@ -22,4 +23,30 @@ type TGenerateCourseIntroductionInputs = {
22
23
  lessons_context: string;
23
24
  };
24
25
  export declare const generateCourseIntroduction: (token: string, inputs: TGenerateCourseIntroductionInputs) => Promise<any>;
26
+ type TInteractiveCreationInputs = {
27
+ courseInfo: string;
28
+ prevInteractions: string;
29
+ };
30
+ export declare const interactiveCreation: (token: string, inputs: TInteractiveCreationInputs) => Promise<any>;
31
+ type TCreateCodeFileInputs = {
32
+ readme: string;
33
+ };
34
+ export declare const createCodeFile: (token: string, inputs: TCreateCodeFileInputs) => Promise<any>;
35
+ type TCreateCodingReadmeInputs = {
36
+ tutorial_description: string;
37
+ list_of_exercises: string;
38
+ output_lang: string;
39
+ title: string;
40
+ lesson_description: string;
41
+ };
42
+ export declare const createCodingReadme: (token: string, inputs: TCreateCodingReadmeInputs) => Promise<any>;
43
+ type TReadmeCreatorInputs = {
44
+ tutorial_description: string;
45
+ list_of_exercises: string;
46
+ output_lang: string;
47
+ title: string;
48
+ lesson_description: string;
49
+ kind: string;
50
+ };
51
+ export declare const readmeCreator: (token: string, inputs: TReadmeCreatorInputs) => Promise<any>;
25
52
  export {};
@@ -1,37 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = exports.getExercisesNames = void 0;
3
+ exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
4
4
  exports.downloadImage = downloadImage;
5
5
  const axios_1 = require("axios");
6
6
  const fs_1 = require("fs");
7
7
  const console_1 = require("../utils/console");
8
8
  const RIGOBOT_HOST = "https://rigobot.herokuapp.com";
9
- const getExercisesNames = async (token, inputs) => {
10
- const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/60/`, {
11
- inputs: inputs,
12
- include_purpose_objective: false,
13
- execute_async: false,
14
- }, {
15
- headers: {
16
- "Content-Type": "application/json",
17
- Authorization: "Token " + token,
18
- },
19
- });
20
- return response.data;
21
- };
22
- exports.getExercisesNames = getExercisesNames;
23
9
  const createReadme = async (token, inputs) => {
24
- const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/61/`, {
25
- inputs: inputs,
26
- include_purpose_objective: false,
27
- execute_async: false,
28
- }, {
29
- headers: {
30
- "Content-Type": "application/json",
31
- Authorization: "Token " + token,
32
- },
33
- });
34
- return response.data;
10
+ try {
11
+ const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/423/`, {
12
+ inputs,
13
+ include_purpose_objective: false,
14
+ execute_async: false,
15
+ }, {
16
+ headers: {
17
+ "Content-Type": "application/json",
18
+ Authorization: "Token " + token,
19
+ },
20
+ });
21
+ return response.data;
22
+ }
23
+ catch (error) {
24
+ console.error(error);
25
+ return null;
26
+ }
35
27
  };
36
28
  exports.createReadme = createReadme;
37
29
  const hasCreatorPermission = async (token) => {
@@ -110,3 +102,65 @@ const generateCourseIntroduction = async (token, inputs) => {
110
102
  return response.data;
111
103
  };
112
104
  exports.generateCourseIntroduction = generateCourseIntroduction;
105
+ const interactiveCreation = async (token, inputs) => {
106
+ const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/390/`, {
107
+ inputs: inputs,
108
+ include_purpose_objective: false,
109
+ execute_async: false,
110
+ }, {
111
+ headers: {
112
+ "Content-Type": "application/json",
113
+ Authorization: "Token " + token,
114
+ },
115
+ });
116
+ return response.data;
117
+ };
118
+ exports.interactiveCreation = interactiveCreation;
119
+ const createCodeFile = async (token, inputs) => {
120
+ const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/456/`, {
121
+ inputs: inputs,
122
+ include_purpose_objective: false,
123
+ execute_async: false,
124
+ }, {
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ Authorization: "Token " + token,
128
+ },
129
+ });
130
+ return response.data;
131
+ };
132
+ exports.createCodeFile = createCodeFile;
133
+ const createCodingReadme = async (token, inputs) => {
134
+ const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/489/`, { inputs, include_purpose_objective: false, execute_async: false }, {
135
+ headers: {
136
+ "Content-Type": "application/json",
137
+ Authorization: "Token " + token,
138
+ },
139
+ });
140
+ return response.data;
141
+ };
142
+ exports.createCodingReadme = createCodingReadme;
143
+ const readmeCreator = async (token, inputs) => {
144
+ if (inputs.kind === "quiz" || inputs.kind === "read") {
145
+ const createReadmeInputs = {
146
+ title: inputs.title,
147
+ output_lang: inputs.output_lang,
148
+ list_of_exercises: inputs.list_of_exercises,
149
+ tutorial_description: inputs.tutorial_description,
150
+ include_quiz: inputs.kind === "quiz" ? "true" : "false",
151
+ lesson_description: inputs.lesson_description,
152
+ };
153
+ return (0, exports.createReadme)(token, createReadmeInputs);
154
+ }
155
+ if (inputs.kind === "code") {
156
+ return (0, exports.createCodingReadme)(token, {
157
+ title: inputs.title,
158
+ output_lang: inputs.output_lang,
159
+ list_of_exercises: inputs.list_of_exercises,
160
+ tutorial_description: inputs.tutorial_description,
161
+ lesson_description: inputs.lesson_description,
162
+ });
163
+ }
164
+ throw new Error("Invalid kind of lesson");
165
+ };
166
+ exports.readmeCreator = readmeCreator;
@@ -1 +1 @@
1
- {"version":"5.0.22","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
1
+ {"version":"5.0.23","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.22",
4
+ "version": "5.0.23",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -13,13 +13,15 @@ import { ValidationError } from "../utils/errors"
13
13
  import * as path from "path"
14
14
  import {
15
15
  hasCreatorPermission,
16
- createReadme,
17
- getExercisesNames,
18
16
  generateImage,
19
17
  downloadImage,
20
18
  generateCourseIntroduction,
19
+ interactiveCreation,
20
+ createCodeFile,
21
+ readmeCreator,
21
22
  } from "../utils/rigoActions"
22
23
  import { getConsumables } from "../utils/api"
24
+
23
25
  const slugify = (text: string) => {
24
26
  return text
25
27
  .toString()
@@ -31,8 +33,26 @@ const slugify = (text: string) => {
31
33
  .replace(/[^\w-]+/g, "")
32
34
  }
33
35
 
34
- const getExNumber = (index: number) => {
35
- return index < 10 ? `0${index}` : `${index}`
36
+ const getExInfo = (title: string) => {
37
+ // Example title: '1.0 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]'
38
+ let [exNumber, exTitle] = title.split(" - ")
39
+
40
+ // Extract kind and description
41
+ const kindMatch = exTitle.match(/\[(.*?):(.*?)]/)
42
+ const kind = kindMatch ? kindMatch[1].trim().toLowerCase() : "read"
43
+ const description = kindMatch ? kindMatch[2].trim() : ""
44
+
45
+ exNumber = exNumber.trim()
46
+ // Clean title
47
+ exTitle = exTitle.replace(kindMatch?.[0] || "", "").trim()
48
+ exTitle = slugify(exTitle)
49
+
50
+ return {
51
+ exNumber,
52
+ kind,
53
+ description,
54
+ exTitle,
55
+ }
36
56
  }
37
57
 
38
58
  function extractImagesFromMarkdown(markdown: string) {
@@ -99,6 +119,47 @@ type PackageInfo = {
99
119
  }
100
120
  }
101
121
 
122
+ const initializeInteractiveCreation = async (
123
+ rigoToken: string,
124
+ courseInfo: string
125
+ ): Promise<{
126
+ currentSteps: string[]
127
+ prevInteractions: string
128
+ }> => {
129
+ let prevInteractions = ""
130
+ let isReady = false
131
+ let currentSteps = []
132
+ while (!isReady) {
133
+ // eslint-disable-next-line
134
+ const res = await interactiveCreation(rigoToken, {
135
+ courseInfo: courseInfo,
136
+ prevInteractions: prevInteractions,
137
+ })
138
+ currentSteps = res.parsed.listOfSteps
139
+ isReady = res.parsed.ready
140
+
141
+ if (!isReady) {
142
+ console.log(currentSteps)
143
+ Console.info(`AI: ${res.parsed.aiMessage}`)
144
+ prevInteractions += `\nAI: ${res.parsed.aiMessage}`
145
+ // eslint-disable-next-line
146
+ const userMessage = await prompts([
147
+ {
148
+ type: "text",
149
+ name: "userMessage",
150
+ message: "Your message: ",
151
+ },
152
+ ])
153
+ prevInteractions += `\nUser: ${userMessage.userMessage}`
154
+ }
155
+ }
156
+
157
+ return {
158
+ currentSteps,
159
+ prevInteractions,
160
+ }
161
+ }
162
+
102
163
  const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
103
164
  Console.info(
104
165
  "Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/creators"
@@ -158,26 +219,6 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
158
219
 
159
220
  Console.success("🎉 Let's begin this learning journey!")
160
221
 
161
- const aiChoices = await prompts([
162
- {
163
- type: "text",
164
- name: "tutorialAbout",
165
- message:
166
- "What kind of tutorial do you want to create? Please expand a little on the content, the outcome and the goal of the tutorial",
167
- initial: "",
168
- },
169
- {
170
- type: "text",
171
- name: "exercisesNumber",
172
- message:
173
- "How many steps or exercises do you want? Please provide a number",
174
- validate: (value: string) => {
175
- const n = Math.floor(Number(value))
176
- return n !== Number.POSITIVE_INFINITY && String(n) === value && n > 0
177
- },
178
- },
179
- ])
180
-
181
222
  const packageContext = `
182
223
  \n
183
224
  The following information comes from user inputs
@@ -190,36 +231,33 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
190
231
  Use it to generate more relevant exercises
191
232
  `
192
233
 
193
- const inputs = {
194
- tutorial_about: aiChoices.tutorialAbout + packageContext,
195
- number_of_exercises: aiChoices.exercisesNumber,
196
- }
197
- Console.info("Creating lessons...")
198
- const res = await getExercisesNames(rigoToken, inputs)
234
+ const { currentSteps } = await initializeInteractiveCreation(
235
+ rigoToken,
236
+ packageContext
237
+ )
199
238
 
200
239
  const exercisesDir = path.join(tutorialDir, "exercises")
201
240
  fs.ensureDirSync(exercisesDir)
202
241
 
203
- for (const [index, exercise] of res.parsed.exercises.entries()) {
204
- const exerciseDir = path.join(
205
- exercisesDir,
206
- `${getExNumber(index)}-${slugify(exercise)}`
207
- )
242
+ Console.info("Creating lessons...")
243
+ for (const [index, exercise] of currentSteps.entries()) {
244
+ const { exNumber, exTitle } = getExInfo(exercise)
245
+ const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`)
208
246
  fs.ensureDirSync(exerciseDir)
209
247
  }
210
248
 
211
- const exercisePromises = res.parsed.exercises.map(
249
+ const exercisePromises = currentSteps.map(
212
250
  async (exercise: any, index: number) => {
213
- const exerciseDir = path.join(
214
- exercisesDir,
215
- `${getExNumber(index)}-${slugify(exercise)}`
216
- )
251
+ const { exNumber, exTitle, kind, description } = getExInfo(exercise)
252
+ const exerciseDir = path.join(exercisesDir, `${exNumber}-${exTitle}`)
217
253
 
218
- const readme = await createReadme(rigoToken, {
219
- title: `\`${getExNumber(index)}\` ${exercise}`,
254
+ const readme = await readmeCreator(rigoToken, {
255
+ title: `${exNumber} - ${exTitle}`,
220
256
  output_lang: "en",
221
- list_of_exercises: res.parsed.exercises.join(","),
222
- tutorial_description: aiChoices.tutorialAbout,
257
+ list_of_exercises: currentSteps.join(","),
258
+ tutorial_description: packageContext,
259
+ lesson_description: description,
260
+ kind: kind.toLowerCase(),
223
261
  })
224
262
 
225
263
  const readmeFilename = "README.md"
@@ -228,6 +266,21 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
228
266
  path.join(exerciseDir, readmeFilename),
229
267
  readme.parsed.content
230
268
  )
269
+
270
+ if (kind.toLowerCase() === "code") {
271
+ const codeFile = await createCodeFile(rigoToken, {
272
+ readme: readme.parsed.content,
273
+ })
274
+
275
+ fs.writeFileSync(
276
+ path.join(
277
+ exerciseDir,
278
+ `app.${codeFile.parsed.extension.replace(".", "")}`
279
+ ),
280
+ codeFile.parsed.content
281
+ )
282
+ }
283
+
231
284
  return readme.parsed.content
232
285
  }
233
286
  )
@@ -1,4 +1,3 @@
1
- import { getExercisesNames } from "../utils/rigoActions"
2
1
  import SessionCommand from "../utils/SessionCommand"
3
2
  import * as fs from "fs"
4
3
  import * as path from "path"
@@ -4,52 +4,39 @@ import Console from "../utils/console"
4
4
 
5
5
  const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
6
6
 
7
- export const getExercisesNames = async (token: string, inputs: object) => {
8
- const response = await axios.post(
9
- `${RIGOBOT_HOST}/v1/prompting/completion/60/`,
10
- {
11
- inputs: inputs,
12
- include_purpose_objective: false,
13
- execute_async: false,
14
- },
15
- {
16
- headers: {
17
- "Content-Type": "application/json",
18
- Authorization: "Token " + token,
19
- },
20
- }
21
- )
22
-
23
- return response.data
24
- }
25
-
26
7
  type TCreateReadmeInputs = {
27
8
  title: string
28
9
  output_lang: string
29
10
  list_of_exercises: string
30
11
  tutorial_description: string
12
+ include_quiz: string
13
+ lesson_description: string
31
14
  }
32
15
 
33
16
  export const createReadme = async (
34
17
  token: string,
35
18
  inputs: TCreateReadmeInputs
36
19
  ) => {
37
- const response = await axios.post(
38
- `${RIGOBOT_HOST}/v1/prompting/completion/61/`,
39
- {
40
- inputs: inputs,
41
- include_purpose_objective: false,
42
- execute_async: false,
43
- },
44
- {
45
- headers: {
46
- "Content-Type": "application/json",
47
- Authorization: "Token " + token,
20
+ try {
21
+ const response = await axios.post(
22
+ `${RIGOBOT_HOST}/v1/prompting/completion/423/`,
23
+ {
24
+ inputs,
25
+ include_purpose_objective: false,
26
+ execute_async: false,
48
27
  },
49
- }
50
- )
51
-
52
- return response.data
28
+ {
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ Authorization: "Token " + token,
32
+ },
33
+ }
34
+ )
35
+ return response.data
36
+ } catch (error) {
37
+ console.error(error)
38
+ return null
39
+ }
53
40
  }
54
41
 
55
42
  export const hasCreatorPermission = async (token: string) => {
@@ -172,3 +159,119 @@ export const generateCourseIntroduction = async (
172
159
 
173
160
  return response.data
174
161
  }
162
+
163
+ type TInteractiveCreationInputs = {
164
+ courseInfo: string
165
+ prevInteractions: string
166
+ }
167
+ export const interactiveCreation = async (
168
+ token: string,
169
+ inputs: TInteractiveCreationInputs
170
+ ) => {
171
+ const response = await axios.post(
172
+ `${RIGOBOT_HOST}/v1/prompting/completion/390/`,
173
+ {
174
+ inputs: inputs,
175
+ include_purpose_objective: false,
176
+ execute_async: false,
177
+ },
178
+ {
179
+ headers: {
180
+ "Content-Type": "application/json",
181
+ Authorization: "Token " + token,
182
+ },
183
+ }
184
+ )
185
+
186
+ return response.data
187
+ }
188
+
189
+ type TCreateCodeFileInputs = {
190
+ readme: string
191
+ }
192
+
193
+ export const createCodeFile = async (
194
+ token: string,
195
+ inputs: TCreateCodeFileInputs
196
+ ) => {
197
+ const response = await axios.post(
198
+ `${RIGOBOT_HOST}/v1/prompting/completion/456/`,
199
+ {
200
+ inputs: inputs,
201
+ include_purpose_objective: false,
202
+ execute_async: false,
203
+ },
204
+ {
205
+ headers: {
206
+ "Content-Type": "application/json",
207
+ Authorization: "Token " + token,
208
+ },
209
+ }
210
+ )
211
+
212
+ return response.data
213
+ }
214
+
215
+ type TCreateCodingReadmeInputs = {
216
+ tutorial_description: string
217
+ list_of_exercises: string
218
+ output_lang: string
219
+ title: string
220
+ lesson_description: string
221
+ }
222
+ export const createCodingReadme = async (
223
+ token: string,
224
+ inputs: TCreateCodingReadmeInputs
225
+ ) => {
226
+ const response = await axios.post(
227
+ `${RIGOBOT_HOST}/v1/prompting/completion/489/`,
228
+ { inputs, include_purpose_objective: false, execute_async: false },
229
+ {
230
+ headers: {
231
+ "Content-Type": "application/json",
232
+ Authorization: "Token " + token,
233
+ },
234
+ }
235
+ )
236
+
237
+ return response.data
238
+ }
239
+
240
+ type TReadmeCreatorInputs = {
241
+ tutorial_description: string
242
+ list_of_exercises: string
243
+ output_lang: string
244
+ title: string
245
+ lesson_description: string
246
+ kind: string
247
+ }
248
+
249
+ export const readmeCreator = async (
250
+ token: string,
251
+ inputs: TReadmeCreatorInputs
252
+ ) => {
253
+ if (inputs.kind === "quiz" || inputs.kind === "read") {
254
+ const createReadmeInputs: TCreateReadmeInputs = {
255
+ title: inputs.title,
256
+ output_lang: inputs.output_lang,
257
+ list_of_exercises: inputs.list_of_exercises,
258
+ tutorial_description: inputs.tutorial_description,
259
+ include_quiz: inputs.kind === "quiz" ? "true" : "false",
260
+ lesson_description: inputs.lesson_description,
261
+ }
262
+ return createReadme(token, createReadmeInputs)
263
+ }
264
+
265
+ if (inputs.kind === "code") {
266
+ return createCodingReadme(token, {
267
+ title: inputs.title,
268
+ output_lang: inputs.output_lang,
269
+ list_of_exercises: inputs.list_of_exercises,
270
+ tutorial_description: inputs.tutorial_description,
271
+ lesson_description: inputs.lesson_description,
272
+ })
273
+ }
274
+
275
+ throw new Error("Invalid kind of lesson")
276
+
277
+ }