@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 +12 -12
- package/lib/commands/init.js +51 -4
- package/lib/commands/publish.js +14 -0
- package/lib/utils/creatorUtilities.d.ts +2 -2
- package/lib/utils/creatorUtilities.js +5 -5
- package/lib/utils/rigoActions.d.ts +4 -0
- package/lib/utils/rigoActions.js +11 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +63 -5
- package/src/commands/publish.ts +23 -1
- package/src/utils/creatorUtilities.ts +5 -5
- package/src/utils/rigoActions.ts +22 -0
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
package/lib/commands/init.js
CHANGED
@@ -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.
|
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
|
-
|
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;
|
package/lib/commands/publish.js
CHANGED
@@ -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
|
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.
|
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,
|
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
|
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,
|
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 {};
|
package/lib/utils/rigoActions.js
CHANGED
@@ -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;
|
package/oclif.manifest.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":"5.0.
|
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.
|
4
|
+
"version": "5.0.43",
|
5
5
|
"author": "Alejandro Sanchez @alesanchezr",
|
6
6
|
"contributors": [
|
7
7
|
{
|
package/src/commands/init.ts
CHANGED
@@ -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 =
|
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
|
-
|
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 } =
|
package/src/commands/publish.ts
CHANGED
@@ -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,
|
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
|
255
|
+
export function getDesktopFile(fileName: string) {
|
256
256
|
const desktopPath = join(homedir(), "Desktop")
|
257
|
-
const filePath = join(desktopPath,
|
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
|
package/src/utils/rigoActions.ts
CHANGED
@@ -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
|
+
}
|