@learnpack/learnpack 5.0.42 → 5.0.43

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.42 win32-x64 node-v22.14.0
24
+ @learnpack/learnpack/5.0.43 win32-x64 node-v22.14.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -79,7 +79,7 @@ DESCRIPTION
79
79
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
80
80
  ```
81
81
 
82
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\audit.ts)_
82
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\audit.ts)_
83
83
 
84
84
  ## `learnpack breakToken`
85
85
 
@@ -94,7 +94,7 @@ OPTIONS
94
94
  -y, --yes Skip all prompts and initialize an empty project
95
95
  ```
96
96
 
97
- _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\breakToken.ts)_
97
+ _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\breakToken.ts)_
98
98
 
99
99
  ## `learnpack clean`
100
100
 
@@ -109,7 +109,7 @@ DESCRIPTION
109
109
  Extra documentation goes here
110
110
  ```
111
111
 
112
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\clean.ts)_
112
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\clean.ts)_
113
113
 
114
114
  ## `learnpack download [PACKAGE]`
115
115
 
@@ -127,7 +127,7 @@ DESCRIPTION
127
127
  Extra documentation goes here
128
128
  ```
129
129
 
130
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\download.ts)_
130
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\download.ts)_
131
131
 
132
132
  ## `learnpack help [COMMAND]`
133
133
 
@@ -159,7 +159,7 @@ OPTIONS
159
159
  -y, --yes Skip all prompts and initialize an empty project
160
160
  ```
161
161
 
162
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\init.ts)_
162
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\init.ts)_
163
163
 
164
164
  ## `learnpack login [PACKAGE]`
165
165
 
@@ -177,7 +177,7 @@ DESCRIPTION
177
177
  Extra documentation goes here
178
178
  ```
179
179
 
180
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\login.ts)_
180
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\login.ts)_
181
181
 
182
182
  ## `learnpack logout [PACKAGE]`
183
183
 
@@ -195,7 +195,7 @@ DESCRIPTION
195
195
  Extra documentation goes here
196
196
  ```
197
197
 
198
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\logout.ts)_
198
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\logout.ts)_
199
199
 
200
200
  ## `learnpack plugins`
201
201
 
@@ -327,7 +327,7 @@ OPTIONS
327
327
  -s, --strict strict mode
328
328
  ```
329
329
 
330
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\publish.ts)_
330
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\publish.ts)_
331
331
 
332
332
  ## `learnpack start`
333
333
 
@@ -349,7 +349,7 @@ OPTIONS
349
349
  -y, --yes Skip all prompts and initialize an empty project
350
350
  ```
351
351
 
352
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\start.ts)_
352
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\start.ts)_
353
353
 
354
354
  ## `learnpack test [EXERCISESLUG]`
355
355
 
@@ -366,7 +366,7 @@ OPTIONS
366
366
  -y, --yes Skip all prompts and initialize an empty project
367
367
  ```
368
368
 
369
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\test.ts)_
369
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\test.ts)_
370
370
 
371
371
  ## `learnpack translate`
372
372
 
@@ -380,7 +380,7 @@ OPTIONS
380
380
  -y, --yes Skip all prompts and initialize an empty project
381
381
  ```
382
382
 
383
- _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.42/src\commands\translate.ts)_
383
+ _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.43/src\commands\translate.ts)_
384
384
  <!-- commandsstop -->
385
385
 
386
386
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -20,6 +20,13 @@ const durationByKind = {
20
20
  quiz: 2,
21
21
  read: 1,
22
22
  };
23
+ function estimateActivities(estimatedDuration) {
24
+ const result = {};
25
+ for (const kind of Object.keys(durationByKind)) {
26
+ result[kind] = Math.floor(estimatedDuration / durationByKind[kind]);
27
+ }
28
+ return result;
29
+ }
23
30
  const PARAMS = {
24
31
  expected_grade_level: "6",
25
32
  max_fkgl: 8,
@@ -154,7 +161,7 @@ const appendContentIndex = async () => {
154
161
  },
155
162
  ]);
156
163
  if (choices.contentIndex) {
157
- await (0, creatorUtilities_1.createFileOnDesktop)();
164
+ await (0, creatorUtilities_1.createFileOnDesktop)("content_index.txt");
158
165
  console_1.default.info("Please make the necessary in the recently created file in your desktop, it should automatically open. Edit the file to match your expectations and save it. Keep the same name and structure as the example file. Continue when ready.");
159
166
  const isReady = await prompts([
160
167
  {
@@ -167,11 +174,38 @@ const appendContentIndex = async () => {
167
174
  console_1.default.error("Please make the necessary changes and try again.");
168
175
  process.exit(1);
169
176
  }
170
- const contentIndex = (0, creatorUtilities_1.getContentIndex)();
177
+ const contentIndex = (0, creatorUtilities_1.getDesktopFile)("content_index.txt");
171
178
  return contentIndex;
172
179
  }
173
180
  return null;
174
181
  };
182
+ const appendAIRules = async () => {
183
+ const choices = await prompts([
184
+ {
185
+ type: "confirm",
186
+ name: "airules",
187
+ message: "Do you want to add any specific rules for the AI? (e.g. no code exercises, no quizzes, etc, a particular writting style, etc)",
188
+ },
189
+ ]);
190
+ if (choices.airules) {
191
+ await (0, creatorUtilities_1.createFileOnDesktop)("airules.txt");
192
+ console_1.default.info("Please make the necessary in the recently created file in your desktop, it should automatically open. Edit the file to match your expectations and save it. Keep the same name and structure as the example file. Continue when ready.");
193
+ const isReady = await prompts([
194
+ {
195
+ type: "confirm",
196
+ name: "isReady",
197
+ message: "Are you ready to continue?",
198
+ },
199
+ ]);
200
+ if (!isReady.isReady) {
201
+ console_1.default.error("Please make the necessary changes and try again.");
202
+ process.exit(1);
203
+ }
204
+ const airules = (0, creatorUtilities_1.getDesktopFile)("airules.txt");
205
+ return airules;
206
+ }
207
+ return null;
208
+ };
175
209
  const handleAILogic = async (tutorialDir, packageInfo) => {
176
210
  fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"));
177
211
  let sessionPayload = await session_1.default.getPayload();
@@ -206,6 +240,7 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
206
240
  console_1.default.success("🎉 Let's begin this learning journey!");
207
241
  const { targetAudience, estimatedDuration } = await whichTargetAudienceAndEstimatedDuration();
208
242
  const contentIndex = await appendContentIndex();
243
+ const airules = await appendAIRules();
209
244
  let packageContext = `
210
245
  \n
211
246
  Title: "${packageInfo.title.us}"
@@ -221,12 +256,24 @@ const handleAILogic = async (tutorialDir, packageInfo) => {
221
256
  ` :
222
257
  ""}
223
258
 
224
- Keep in mind that depending on the type of exercise, the duration will vary.
225
- This is the duration for each type of exercise:
259
+ This is the duration for each type of step, use it to estimate the number of steps to create:
226
260
  ${Object.entries(durationByKind)
227
261
  .map(([key, value]) => `${key}: ${value} minutes`)
228
262
  .join("\n")}
229
263
 
264
+
265
+ Within the estimated duration, is possible to have the following activities:
266
+ ${JSON.stringify(estimateActivities(estimatedDuration))}
267
+
268
+ You should create a tutorial that is engaging and fun to follow.
269
+
270
+
271
+ ${airules ?
272
+ `
273
+ This is a list of rules you need to follow when creating the tutorial:
274
+ ${airules}
275
+ ` :
276
+ ""}
230
277
  `;
231
278
  const { steps, title, description, duration, difficulty } = await initializeInteractiveCreation(rigoToken, packageContext);
232
279
  packageInfo.title.us = title;
@@ -159,6 +159,8 @@ class BuildCommand extends SessionCommand_1.default {
159
159
  // After copying the _app directory
160
160
  const indexHtmlPath = path.join(appDir, "index.html");
161
161
  const buildIndexHtmlPath = path.join(buildDir, "index.html");
162
+ const manifestPWA = path.join(appDir, "manifest.webmanifest");
163
+ const buildManifestPWA = path.join(buildDir, "manifest.webmanifest");
162
164
  if (fs.existsSync(indexHtmlPath)) {
163
165
  let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
164
166
  const description = learnJson.description.us || "LearnPack is awesome!";
@@ -177,6 +179,18 @@ class BuildCommand extends SessionCommand_1.default {
177
179
  else {
178
180
  this.error("index.html not found in _app directory");
179
181
  }
182
+ if (fs.existsSync(manifestPWA)) {
183
+ let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8");
184
+ manifestPWAContent = manifestPWAContent.replace("{{course_title}}", learnJson.title.us);
185
+ const courseShortName = await (0, rigoActions_1.generateCourseShortName)(rigoToken, {
186
+ learnJSON: JSON.stringify(learnJson),
187
+ });
188
+ manifestPWAContent = manifestPWAContent.replace("{{course_app_name}}", courseShortName.answer);
189
+ fs.writeFileSync(buildManifestPWA, manifestPWAContent);
190
+ }
191
+ else {
192
+ this.error("manifest.webmanifest not found in _app directory");
193
+ }
180
194
  // Copy exercises directory
181
195
  const exercisesDir = path.join(process.cwd(), "exercises");
182
196
  const learnExercisesDir = path.join(process.cwd(), ".learn", "exercises");
@@ -49,8 +49,8 @@ export declare const makePackageInfo: (choices: any) => {
49
49
  slug: any;
50
50
  };
51
51
  export declare function estimateDuration(listOfSteps: string[]): number;
52
- export declare function createFileOnDesktop(): Promise<void>;
53
- export declare function getContentIndex(): string;
52
+ export declare function createFileOnDesktop(fileName: string): Promise<void>;
53
+ export declare function getDesktopFile(fileName: string): string;
54
54
  export declare function extractTextFromMarkdown(mdContent: string): string;
55
55
  export declare const saveTranslatedReadme: (exercise: string, languageCode: string, readme: string) => Promise<void>;
56
56
  /**
@@ -6,7 +6,7 @@ exports.extractImagesFromMarkdown = extractImagesFromMarkdown;
6
6
  exports.getFilenameFromUrl = getFilenameFromUrl;
7
7
  exports.estimateDuration = estimateDuration;
8
8
  exports.createFileOnDesktop = createFileOnDesktop;
9
- exports.getContentIndex = getContentIndex;
9
+ exports.getDesktopFile = getDesktopFile;
10
10
  exports.extractTextFromMarkdown = extractTextFromMarkdown;
11
11
  exports.extractParagraphs = extractParagraphs;
12
12
  exports.splitIntoSyllables = splitIntoSyllables;
@@ -169,10 +169,10 @@ What is an AI Model: Explain what is an AI model and its applications
169
169
  How to use an AI Model: Different APIs, local models, etc.
170
170
  How to build an AI Model: Fine-tuning, data collection, cleaning and more.
171
171
  `;
172
- async function createFileOnDesktop() {
172
+ async function createFileOnDesktop(fileName) {
173
173
  try {
174
174
  const desktopPath = (0, path_1.join)((0, os_1.homedir)(), "Desktop");
175
- const filePath = (0, path_1.join)(desktopPath, "content_index.txt");
175
+ const filePath = (0, path_1.join)(desktopPath, fileName);
176
176
  const content = example_content.trim();
177
177
  await writeFilePromise(filePath, content);
178
178
  console.log(`File created successfully at: ${filePath}`);
@@ -202,9 +202,9 @@ async function openFile(filePath) {
202
202
  console.error("Error opening the file:", error);
203
203
  }
204
204
  }
205
- function getContentIndex() {
205
+ function getDesktopFile(fileName) {
206
206
  const desktopPath = (0, path_1.join)((0, os_1.homedir)(), "Desktop");
207
- const filePath = (0, path_1.join)(desktopPath, "content_index.txt");
207
+ const filePath = (0, path_1.join)(desktopPath, fileName);
208
208
  const content = fs.readFileSync(filePath, "utf8");
209
209
  return content;
210
210
  }
@@ -61,4 +61,8 @@ type TReduceReadmeInputs = {
61
61
  };
62
62
  export declare function makeReadmeReadable(rigoToken: string, inputs: TReduceReadmeInputs): Promise<any>;
63
63
  export declare const isValidRigoToken: (rigobotToken: string) => Promise<boolean>;
64
+ type TGenerateCourseShortNameInputs = {
65
+ learnJSON: string;
66
+ };
67
+ export declare const generateCourseShortName: (token: string, inputs: TGenerateCourseShortNameInputs) => Promise<any>;
64
68
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isValidRigoToken = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
3
+ exports.generateCourseShortName = exports.isValidRigoToken = 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
  exports.createPreviewReadme = createPreviewReadme;
6
6
  exports.makeReadmeReadable = makeReadmeReadable;
@@ -202,3 +202,13 @@ const isValidRigoToken = async (rigobotToken) => {
202
202
  return true;
203
203
  };
204
204
  exports.isValidRigoToken = isValidRigoToken;
205
+ const generateCourseShortName = async (token, inputs) => {
206
+ const response = await axios_1.default.post(`${RIGOBOT_HOST}/v1/prompting/completion/852/`, { inputs, include_purpose_objective: false, execute_async: false }, {
207
+ headers: {
208
+ "Content-Type": "application/json",
209
+ Authorization: "Token " + token,
210
+ },
211
+ });
212
+ return response.data;
213
+ };
214
+ exports.generateCourseShortName = generateCourseShortName;
@@ -1 +1 @@
1
- {"version":"5.0.42","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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","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":[]},"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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"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.43","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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false}},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","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":[]},"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":{"strict":{"name":"strict","type":"boolean","char":"s","description":"strict mode","allowNo":false},"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.42",
4
+ "version": "5.0.43",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -31,8 +31,8 @@ import {
31
31
  getFilenameFromUrl,
32
32
  makePackageInfo,
33
33
  estimateDuration,
34
- getContentIndex,
35
34
  createFileOnDesktop,
35
+ getDesktopFile,
36
36
  } from "../utils/creatorUtilities"
37
37
  import SessionManager from "../managers/session"
38
38
 
@@ -42,6 +42,16 @@ const durationByKind: Record<string, number> = {
42
42
  read: 1,
43
43
  }
44
44
 
45
+ function estimateActivities(estimatedDuration: number) {
46
+ const result: Record<string, number> = {}
47
+
48
+ for (const kind of Object.keys(durationByKind)) {
49
+ result[kind] = Math.floor(estimatedDuration / durationByKind[kind])
50
+ }
51
+
52
+ return result
53
+ }
54
+
45
55
  const PARAMS = {
46
56
  expected_grade_level: "6",
47
57
  max_fkgl: 8,
@@ -243,7 +253,7 @@ const appendContentIndex = async () => {
243
253
  },
244
254
  ])
245
255
  if (choices.contentIndex) {
246
- await createFileOnDesktop()
256
+ await createFileOnDesktop("content_index.txt")
247
257
  Console.info(
248
258
  "Please make the necessary in the recently created file in your desktop, it should automatically open. Edit the file to match your expectations and save it. Keep the same name and structure as the example file. Continue when ready."
249
259
  )
@@ -259,13 +269,46 @@ const appendContentIndex = async () => {
259
269
  process.exit(1)
260
270
  }
261
271
 
262
- const contentIndex = getContentIndex()
272
+ const contentIndex = getDesktopFile("content_index.txt")
263
273
  return contentIndex
264
274
  }
265
275
 
266
276
  return null
267
277
  }
268
278
 
279
+ const appendAIRules = async () => {
280
+ const choices = await prompts([
281
+ {
282
+ type: "confirm",
283
+ name: "airules",
284
+ message:
285
+ "Do you want to add any specific rules for the AI? (e.g. no code exercises, no quizzes, etc, a particular writting style, etc)",
286
+ },
287
+ ])
288
+ if (choices.airules) {
289
+ await createFileOnDesktop("airules.txt")
290
+ Console.info(
291
+ "Please make the necessary in the recently created file in your desktop, it should automatically open. Edit the file to match your expectations and save it. Keep the same name and structure as the example file. Continue when ready."
292
+ )
293
+ const isReady = await prompts([
294
+ {
295
+ type: "confirm",
296
+ name: "isReady",
297
+ message: "Are you ready to continue?",
298
+ },
299
+ ])
300
+ if (!isReady.isReady) {
301
+ Console.error("Please make the necessary changes and try again.")
302
+ process.exit(1)
303
+ }
304
+
305
+ const airules = getDesktopFile("airules.txt")
306
+ return airules
307
+ }
308
+
309
+ return null
310
+ }
311
+
269
312
  const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
270
313
  fs.removeSync(path.join(tutorialDir, "exercises", "01-hello-world"))
271
314
 
@@ -319,6 +362,7 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
319
362
  const { targetAudience, estimatedDuration } =
320
363
  await whichTargetAudienceAndEstimatedDuration()
321
364
  const contentIndex = await appendContentIndex()
365
+ const airules = await appendAIRules()
322
366
 
323
367
  let packageContext = `
324
368
  \n
@@ -337,12 +381,26 @@ const handleAILogic = async (tutorialDir: string, packageInfo: PackageInfo) => {
337
381
  ""
338
382
  }
339
383
 
340
- Keep in mind that depending on the type of exercise, the duration will vary.
341
- This is the duration for each type of exercise:
384
+ This is the duration for each type of step, use it to estimate the number of steps to create:
342
385
  ${Object.entries(durationByKind)
343
386
  .map(([key, value]) => `${key}: ${value} minutes`)
344
387
  .join("\n")}
345
388
 
389
+
390
+ Within the estimated duration, is possible to have the following activities:
391
+ ${JSON.stringify(estimateActivities(estimatedDuration))}
392
+
393
+ You should create a tutorial that is engaging and fun to follow.
394
+
395
+
396
+ ${
397
+ airules ?
398
+ `
399
+ This is a list of rules you need to follow when creating the tutorial:
400
+ ${airules}
401
+ ` :
402
+ ""
403
+ }
346
404
  `
347
405
 
348
406
  const { steps, title, description, duration, difficulty } =
@@ -17,7 +17,7 @@ import {
17
17
  } from "../managers/file"
18
18
  import api, { getConsumable, TAcademy } from "../utils/api"
19
19
  import * as prompts from "prompts"
20
- import { isValidRigoToken } from "../utils/rigoActions"
20
+ import { generateCourseShortName, isValidRigoToken } from "../utils/rigoActions"
21
21
 
22
22
  const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
23
23
  // const RIGOBOT_HOST =
@@ -217,6 +217,8 @@ class BuildCommand extends SessionCommand {
217
217
  // After copying the _app directory
218
218
  const indexHtmlPath = path.join(appDir, "index.html")
219
219
  const buildIndexHtmlPath = path.join(buildDir, "index.html")
220
+ const manifestPWA = path.join(appDir, "manifest.webmanifest")
221
+ const buildManifestPWA = path.join(buildDir, "manifest.webmanifest")
220
222
 
221
223
  if (fs.existsSync(indexHtmlPath)) {
222
224
  let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8")
@@ -241,6 +243,26 @@ class BuildCommand extends SessionCommand {
241
243
  this.error("index.html not found in _app directory")
242
244
  }
243
245
 
246
+ if (fs.existsSync(manifestPWA)) {
247
+ let manifestPWAContent = fs.readFileSync(manifestPWA, "utf-8")
248
+ manifestPWAContent = manifestPWAContent.replace(
249
+ "{{course_title}}",
250
+ learnJson.title.us
251
+ )
252
+
253
+ const courseShortName = await generateCourseShortName(rigoToken, {
254
+ learnJSON: JSON.stringify(learnJson),
255
+ })
256
+
257
+ manifestPWAContent = manifestPWAContent.replace(
258
+ "{{course_app_name}}",
259
+ courseShortName.answer
260
+ )
261
+ fs.writeFileSync(buildManifestPWA, manifestPWAContent)
262
+ } else {
263
+ this.error("manifest.webmanifest not found in _app directory")
264
+ }
265
+
244
266
  // Copy exercises directory
245
267
  const exercisesDir = path.join(process.cwd(), "exercises")
246
268
  const learnExercisesDir = path.join(process.cwd(), ".learn", "exercises")
@@ -216,10 +216,10 @@ How to use an AI Model: Different APIs, local models, etc.
216
216
  How to build an AI Model: Fine-tuning, data collection, cleaning and more.
217
217
  `
218
218
 
219
- export async function createFileOnDesktop() {
219
+ export async function createFileOnDesktop(fileName: string) {
220
220
  try {
221
221
  const desktopPath = join(homedir(), "Desktop")
222
- const filePath = join(desktopPath, "content_index.txt")
222
+ const filePath = join(desktopPath, fileName)
223
223
 
224
224
  const content = example_content.trim()
225
225
 
@@ -252,9 +252,9 @@ async function openFile(filePath: string) {
252
252
  }
253
253
  }
254
254
 
255
- export function getContentIndex() {
255
+ export function getDesktopFile(fileName: string) {
256
256
  const desktopPath = join(homedir(), "Desktop")
257
- const filePath = join(desktopPath, "content_index.txt")
257
+ const filePath = join(desktopPath, fileName)
258
258
 
259
259
  const content = fs.readFileSync(filePath, "utf8")
260
260
  return content
@@ -390,7 +390,7 @@ export function fleschKincaidGrade(text: string): TFKGLResult {
390
390
  fkgl: 0,
391
391
  }
392
392
  }
393
-
393
+
394
394
  const fkgl =
395
395
  // eslint-disable-next-line
396
396
  0.39 * (numWords / numSentences) + 11.8 * (numSyllables / numWords) - 15.59
@@ -337,3 +337,25 @@ export const isValidRigoToken = async (rigobotToken: string) => {
337
337
 
338
338
  return true
339
339
  }
340
+
341
+ type TGenerateCourseShortNameInputs = {
342
+ learnJSON: string
343
+ }
344
+
345
+ export const generateCourseShortName = async (
346
+ token: string,
347
+ inputs: TGenerateCourseShortNameInputs
348
+ ) => {
349
+ const response = await axios.post(
350
+ `${RIGOBOT_HOST}/v1/prompting/completion/852/`,
351
+ { inputs, include_purpose_objective: false, execute_async: false },
352
+ {
353
+ headers: {
354
+ "Content-Type": "application/json",
355
+ Authorization: "Token " + token,
356
+ },
357
+ }
358
+ )
359
+
360
+ return response.data
361
+ }