@tostudy-ai/cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +412 -262
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -372,6 +372,23 @@ function progressBar(percent, width = 20) {
|
|
|
372
372
|
const empty = width - filled;
|
|
373
373
|
return "\u2588".repeat(filled) + "\u2591".repeat(empty) + ` ${clamped}%`;
|
|
374
374
|
}
|
|
375
|
+
function filterExerciseContent(content, tierIndex) {
|
|
376
|
+
const positions = [];
|
|
377
|
+
for (const pattern of TIER_HEADERS) {
|
|
378
|
+
const match = content.match(pattern);
|
|
379
|
+
if (match?.index != null) {
|
|
380
|
+
positions.push({ index: match.index, match: match[0] });
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
positions.sort((a, b2) => a.index - b2.index);
|
|
384
|
+
if (positions.length === 0) return content;
|
|
385
|
+
const intro = content.slice(0, positions[0].index).trimEnd();
|
|
386
|
+
const tierPos = positions[tierIndex];
|
|
387
|
+
if (!tierPos) return content;
|
|
388
|
+
const nextPos = positions[tierIndex + 1];
|
|
389
|
+
const tierContent = nextPos ? content.slice(tierPos.index, nextPos.index).trimEnd() : content.slice(tierPos.index).trimEnd();
|
|
390
|
+
return intro + "\n\n" + tierContent;
|
|
391
|
+
}
|
|
375
392
|
function formatCourseList(courses3) {
|
|
376
393
|
if (courses3.length === 0) {
|
|
377
394
|
return [
|
|
@@ -413,16 +430,17 @@ function formatProgress(data) {
|
|
|
413
430
|
return lines.join("\n");
|
|
414
431
|
}
|
|
415
432
|
function formatLesson(data) {
|
|
416
|
-
const { lesson, wayfinding, progress: progress3 } = data;
|
|
433
|
+
const { lesson, wayfinding, progress: progress3, exercise } = data;
|
|
417
434
|
const courseBar = progressBar(progress3.coursePercent);
|
|
418
435
|
const moduleBar = progressBar(progress3.modulePercent);
|
|
436
|
+
const tierSuffix = exercise ? ` \xB7 ${exercise.tierLabel} (${exercise.index + 1}/${exercise.total})` : "";
|
|
419
437
|
const lines = [
|
|
420
|
-
`\u2501\u2501\u2501 M\xF3dulo ${wayfinding.moduleOrder}/${wayfinding.totalModules} \xB7 Li\xE7\xE3o ${wayfinding.lessonOrder}/${wayfinding.totalLessons} \u2501\u2501\u2501`,
|
|
438
|
+
`\u2501\u2501\u2501 M\xF3dulo ${wayfinding.moduleOrder}/${wayfinding.totalModules} \xB7 Li\xE7\xE3o ${wayfinding.lessonOrder}/${wayfinding.totalLessons}${tierSuffix} \u2501\u2501\u2501`,
|
|
421
439
|
"",
|
|
422
440
|
`\u{1F4D6} ${lesson.title}`,
|
|
423
441
|
` M\xF3dulo: ${lesson.moduleTitle} | Tipo: ${lesson.type}`,
|
|
424
442
|
"",
|
|
425
|
-
lesson.content,
|
|
443
|
+
exercise ? filterExerciseContent(lesson.content, exercise.index) : lesson.content,
|
|
426
444
|
"",
|
|
427
445
|
` Curso: ${courseBar}`,
|
|
428
446
|
` M\xF3dulo: ${moduleBar}`
|
|
@@ -433,12 +451,21 @@ function formatLesson(data) {
|
|
|
433
451
|
if (lesson.exerciseContent) {
|
|
434
452
|
lines.push("", "\u2500\u2500\u2500 Exerc\xEDcio \u2500\u2500\u2500", lesson.exerciseContent);
|
|
435
453
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
454
|
+
if (/^checkpoint/i.test(lesson.title.trim())) {
|
|
455
|
+
lines.push(
|
|
456
|
+
"",
|
|
457
|
+
"\u{1F4A1} Escreva suas respostas num arquivo (ex: checkpoint.md) e valide:",
|
|
458
|
+
" tostudy validate checkpoint.md",
|
|
459
|
+
"\u2192 tostudy next para avan\xE7ar"
|
|
460
|
+
);
|
|
461
|
+
} else {
|
|
462
|
+
lines.push(
|
|
463
|
+
"",
|
|
464
|
+
"\u2192 tostudy validate <resposta> para validar sua solu\xE7\xE3o",
|
|
465
|
+
"\u2192 tostudy hint para obter uma dica",
|
|
466
|
+
"\u2192 tostudy next para avan\xE7ar (ap\xF3s completar)"
|
|
467
|
+
);
|
|
468
|
+
}
|
|
442
469
|
return lines.join("\n");
|
|
443
470
|
}
|
|
444
471
|
function formatValidation(data) {
|
|
@@ -510,9 +537,11 @@ function formatModuleStart(data) {
|
|
|
510
537
|
);
|
|
511
538
|
return lines.join("\n");
|
|
512
539
|
}
|
|
540
|
+
var TIER_HEADERS;
|
|
513
541
|
var init_formatter = __esm({
|
|
514
542
|
"src/output/formatter.ts"() {
|
|
515
543
|
"use strict";
|
|
544
|
+
TIER_HEADERS = [/^## Prática Guiada/im, /^## Prática Semi-?[Gg]uiada/im, /^## Desafio/im];
|
|
516
545
|
}
|
|
517
546
|
});
|
|
518
547
|
|
|
@@ -2176,11 +2205,14 @@ var init_select = __esm({
|
|
|
2176
2205
|
enrollmentId = found.enrollmentId;
|
|
2177
2206
|
}
|
|
2178
2207
|
}
|
|
2208
|
+
const matched = courses3.find((c) => c.courseId === courseId);
|
|
2179
2209
|
const detail = await selectCourse({ userId: session.userId, courseId }, deps);
|
|
2180
2210
|
await setActiveCourse({
|
|
2181
2211
|
courseId: detail.courseId,
|
|
2182
2212
|
courseTitle: detail.courseTitle,
|
|
2183
|
-
enrollmentId
|
|
2213
|
+
enrollmentId,
|
|
2214
|
+
courseTags: matched?.tags,
|
|
2215
|
+
courseLevel: matched?.level
|
|
2184
2216
|
});
|
|
2185
2217
|
if (opts.json) {
|
|
2186
2218
|
output({ ...detail, enrollmentId }, { json: true });
|
|
@@ -3785,7 +3817,7 @@ var init_query_promise = __esm({
|
|
|
3785
3817
|
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
3786
3818
|
const nullifyMap = {};
|
|
3787
3819
|
const result = columns.reduce(
|
|
3788
|
-
(result2, { path:
|
|
3820
|
+
(result2, { path: path14, field }, columnIndex) => {
|
|
3789
3821
|
let decoder;
|
|
3790
3822
|
if (is(field, Column)) {
|
|
3791
3823
|
decoder = field;
|
|
@@ -3797,8 +3829,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
3797
3829
|
decoder = field.sql.decoder;
|
|
3798
3830
|
}
|
|
3799
3831
|
let node = result2;
|
|
3800
|
-
for (const [pathChunkIndex, pathChunk] of
|
|
3801
|
-
if (pathChunkIndex <
|
|
3832
|
+
for (const [pathChunkIndex, pathChunk] of path14.entries()) {
|
|
3833
|
+
if (pathChunkIndex < path14.length - 1) {
|
|
3802
3834
|
if (!(pathChunk in node)) {
|
|
3803
3835
|
node[pathChunk] = {};
|
|
3804
3836
|
}
|
|
@@ -3806,8 +3838,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
|
3806
3838
|
} else {
|
|
3807
3839
|
const rawValue = row[columnIndex];
|
|
3808
3840
|
const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
|
|
3809
|
-
if (joinsNotNullableMap && is(field, Column) &&
|
|
3810
|
-
const objectName =
|
|
3841
|
+
if (joinsNotNullableMap && is(field, Column) && path14.length === 2) {
|
|
3842
|
+
const objectName = path14[0];
|
|
3811
3843
|
if (!(objectName in nullifyMap)) {
|
|
3812
3844
|
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
3813
3845
|
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
@@ -7754,13 +7786,13 @@ function Subscribe(postgres2, options) {
|
|
|
7754
7786
|
}
|
|
7755
7787
|
}
|
|
7756
7788
|
function handle(a, b2) {
|
|
7757
|
-
const
|
|
7789
|
+
const path14 = b2.relation.schema + "." + b2.relation.table;
|
|
7758
7790
|
call("*", a, b2);
|
|
7759
|
-
call("*:" +
|
|
7760
|
-
b2.relation.keys.length && call("*:" +
|
|
7791
|
+
call("*:" + path14, a, b2);
|
|
7792
|
+
b2.relation.keys.length && call("*:" + path14 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
|
|
7761
7793
|
call(b2.command, a, b2);
|
|
7762
|
-
call(b2.command + ":" +
|
|
7763
|
-
b2.relation.keys.length && call(b2.command + ":" +
|
|
7794
|
+
call(b2.command + ":" + path14, a, b2);
|
|
7795
|
+
b2.relation.keys.length && call(b2.command + ":" + path14 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
|
|
7764
7796
|
}
|
|
7765
7797
|
function pong() {
|
|
7766
7798
|
const x2 = Buffer.alloc(34);
|
|
@@ -7873,8 +7905,8 @@ function parseEvent(x) {
|
|
|
7873
7905
|
const xs = x.match(/^(\*|insert|update|delete)?:?([^.]+?\.?[^=]+)?=?(.+)?/i) || [];
|
|
7874
7906
|
if (!xs)
|
|
7875
7907
|
throw new Error("Malformed subscribe pattern: " + x);
|
|
7876
|
-
const [, command,
|
|
7877
|
-
return (command || "*") + (
|
|
7908
|
+
const [, command, path14, key] = xs;
|
|
7909
|
+
return (command || "*") + (path14 ? ":" + (path14.indexOf(".") === -1 ? "public." + path14 : path14) : "") + (key ? "=" + key : "");
|
|
7878
7910
|
}
|
|
7879
7911
|
var noop2;
|
|
7880
7912
|
var init_subscribe = __esm({
|
|
@@ -8012,10 +8044,10 @@ function Postgres(a, b2) {
|
|
|
8012
8044
|
});
|
|
8013
8045
|
return query;
|
|
8014
8046
|
}
|
|
8015
|
-
function file2(
|
|
8047
|
+
function file2(path14, args = [], options2 = {}) {
|
|
8016
8048
|
arguments.length === 2 && !Array.isArray(args) && (options2 = args, args = []);
|
|
8017
8049
|
const query = new Query([], args, (query2) => {
|
|
8018
|
-
fs5.readFile(
|
|
8050
|
+
fs5.readFile(path14, "utf8", (err, string4) => {
|
|
8019
8051
|
if (err)
|
|
8020
8052
|
return query2.reject(err);
|
|
8021
8053
|
query2.strings = [string4];
|
|
@@ -13073,10 +13105,10 @@ function mergeDefs(...defs) {
|
|
|
13073
13105
|
function cloneDef(schema) {
|
|
13074
13106
|
return mergeDefs(schema._zod.def);
|
|
13075
13107
|
}
|
|
13076
|
-
function getElementAtPath(obj,
|
|
13077
|
-
if (!
|
|
13108
|
+
function getElementAtPath(obj, path14) {
|
|
13109
|
+
if (!path14)
|
|
13078
13110
|
return obj;
|
|
13079
|
-
return
|
|
13111
|
+
return path14.reduce((acc, key) => acc?.[key], obj);
|
|
13080
13112
|
}
|
|
13081
13113
|
function promiseAllObject(promisesObj) {
|
|
13082
13114
|
const keys = Object.keys(promisesObj);
|
|
@@ -13388,11 +13420,11 @@ function aborted(x, startIndex = 0) {
|
|
|
13388
13420
|
}
|
|
13389
13421
|
return false;
|
|
13390
13422
|
}
|
|
13391
|
-
function prefixIssues(
|
|
13423
|
+
function prefixIssues(path14, issues) {
|
|
13392
13424
|
return issues.map((iss) => {
|
|
13393
13425
|
var _a2;
|
|
13394
13426
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
13395
|
-
iss.path.unshift(
|
|
13427
|
+
iss.path.unshift(path14);
|
|
13396
13428
|
return iss;
|
|
13397
13429
|
});
|
|
13398
13430
|
}
|
|
@@ -13634,7 +13666,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
|
|
|
13634
13666
|
}
|
|
13635
13667
|
function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
13636
13668
|
const result = { errors: [] };
|
|
13637
|
-
const processError = (error50,
|
|
13669
|
+
const processError = (error50, path14 = []) => {
|
|
13638
13670
|
var _a2, _b;
|
|
13639
13671
|
for (const issue2 of error50.issues) {
|
|
13640
13672
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -13644,7 +13676,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
13644
13676
|
} else if (issue2.code === "invalid_element") {
|
|
13645
13677
|
processError({ issues: issue2.issues }, issue2.path);
|
|
13646
13678
|
} else {
|
|
13647
|
-
const fullpath = [...
|
|
13679
|
+
const fullpath = [...path14, ...issue2.path];
|
|
13648
13680
|
if (fullpath.length === 0) {
|
|
13649
13681
|
result.errors.push(mapper(issue2));
|
|
13650
13682
|
continue;
|
|
@@ -13676,8 +13708,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
13676
13708
|
}
|
|
13677
13709
|
function toDotPath(_path) {
|
|
13678
13710
|
const segs = [];
|
|
13679
|
-
const
|
|
13680
|
-
for (const seg of
|
|
13711
|
+
const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
13712
|
+
for (const seg of path14) {
|
|
13681
13713
|
if (typeof seg === "number")
|
|
13682
13714
|
segs.push(`[${seg}]`);
|
|
13683
13715
|
else if (typeof seg === "symbol")
|
|
@@ -26371,13 +26403,13 @@ function resolveRef(ref, ctx) {
|
|
|
26371
26403
|
if (!ref.startsWith("#")) {
|
|
26372
26404
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
26373
26405
|
}
|
|
26374
|
-
const
|
|
26375
|
-
if (
|
|
26406
|
+
const path14 = ref.slice(1).split("/").filter(Boolean);
|
|
26407
|
+
if (path14.length === 0) {
|
|
26376
26408
|
return ctx.rootSchema;
|
|
26377
26409
|
}
|
|
26378
26410
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
26379
|
-
if (
|
|
26380
|
-
const key =
|
|
26411
|
+
if (path14[0] === defsKey) {
|
|
26412
|
+
const key = path14[1];
|
|
26381
26413
|
if (!key || !ctx.defs[key]) {
|
|
26382
26414
|
throw new Error(`Reference not found: ${ref}`);
|
|
26383
26415
|
}
|
|
@@ -39940,6 +39972,7 @@ var init_start = __esm({
|
|
|
39940
39972
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
39941
39973
|
const deps = { data, logger: logger5 };
|
|
39942
39974
|
const moduleData = await startModule({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
39975
|
+
await setActiveCourse({ ...activeCourse, currentLessonId: moduleData.firstLesson.id });
|
|
39943
39976
|
if (opts.json) {
|
|
39944
39977
|
output(moduleData, { json: true });
|
|
39945
39978
|
} else {
|
|
@@ -39979,12 +40012,18 @@ var init_start_next = __esm({
|
|
|
39979
40012
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
39980
40013
|
const deps = { data, logger: logger6 };
|
|
39981
40014
|
const moduleData = await startNextModule({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
40015
|
+
await setActiveCourse({ ...activeCourse, currentLessonId: moduleData.firstLesson.id });
|
|
39982
40016
|
if (opts.json) {
|
|
39983
40017
|
output(moduleData, { json: true });
|
|
39984
40018
|
} else {
|
|
39985
40019
|
output(formatModuleStart(moduleData), { json: false });
|
|
39986
40020
|
}
|
|
39987
40021
|
} catch (err) {
|
|
40022
|
+
if (err instanceof CliApiError && err.message === "COURSE_COMPLETE") {
|
|
40023
|
+
process.stderr.write("\u{1F389} Parab\xE9ns! Curso conclu\xEDdo!\n");
|
|
40024
|
+
process.stderr.write("\u2192 tostudy courses para ver outros cursos\n");
|
|
40025
|
+
process.exit(0);
|
|
40026
|
+
}
|
|
39988
40027
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39989
40028
|
error(msg);
|
|
39990
40029
|
}
|
|
@@ -40016,16 +40055,34 @@ var init_next = __esm({
|
|
|
40016
40055
|
{ enrollmentId: activeCourse.enrollmentId, userConfirmation: "cli-next" },
|
|
40017
40056
|
deps
|
|
40018
40057
|
);
|
|
40058
|
+
await setActiveCourse({ ...activeCourse, currentLessonId: lessonData.lesson.id });
|
|
40019
40059
|
if (opts.json) {
|
|
40020
40060
|
output(lessonData, { json: true });
|
|
40021
40061
|
} else {
|
|
40022
40062
|
output(formatLesson(lessonData), { json: false });
|
|
40023
40063
|
}
|
|
40024
40064
|
} catch (err) {
|
|
40025
|
-
if (err instanceof CliApiError
|
|
40026
|
-
|
|
40027
|
-
|
|
40028
|
-
|
|
40065
|
+
if (err instanceof CliApiError) {
|
|
40066
|
+
if (err.status === 403 && err.message === "CHANNEL_MISMATCH") {
|
|
40067
|
+
process.stderr.write("Este curso n\xE3o est\xE1 dispon\xEDvel via CLI.\n");
|
|
40068
|
+
process.stderr.write("Acesse tostudy.ai para estudar este curso pelo ChatStudy.\n");
|
|
40069
|
+
process.exit(1);
|
|
40070
|
+
}
|
|
40071
|
+
if (err.message === "MODULE_COMPLETE") {
|
|
40072
|
+
process.stderr.write("\u2713 M\xF3dulo conclu\xEDdo!\n");
|
|
40073
|
+
process.stderr.write("\u2192 tostudy start-next para iniciar o pr\xF3ximo m\xF3dulo\n");
|
|
40074
|
+
process.exit(0);
|
|
40075
|
+
}
|
|
40076
|
+
if (err.message === "COURSE_COMPLETE") {
|
|
40077
|
+
process.stderr.write("\u{1F389} Parab\xE9ns! Curso conclu\xEDdo!\n");
|
|
40078
|
+
process.stderr.write("\u2192 tostudy courses para ver outros cursos\n");
|
|
40079
|
+
process.exit(0);
|
|
40080
|
+
}
|
|
40081
|
+
if (err.message === "NO_ACTIVE_LESSON") {
|
|
40082
|
+
process.stderr.write("Nenhuma li\xE7\xE3o em progresso.\n");
|
|
40083
|
+
process.stderr.write("\u2192 tostudy start-next para iniciar o pr\xF3ximo m\xF3dulo\n");
|
|
40084
|
+
process.exit(0);
|
|
40085
|
+
}
|
|
40029
40086
|
}
|
|
40030
40087
|
const msg = err instanceof Error ? err.message : String(err);
|
|
40031
40088
|
error(msg);
|
|
@@ -40034,13 +40091,46 @@ var init_next = __esm({
|
|
|
40034
40091
|
}
|
|
40035
40092
|
});
|
|
40036
40093
|
|
|
40094
|
+
// src/workspace/resolve.ts
|
|
40095
|
+
import fs6 from "node:fs/promises";
|
|
40096
|
+
import path5 from "node:path";
|
|
40097
|
+
import os6 from "node:os";
|
|
40098
|
+
function courseSlug(title) {
|
|
40099
|
+
return title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
40100
|
+
}
|
|
40101
|
+
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
40102
|
+
const slug = courseSlug(courseTitle);
|
|
40103
|
+
const candidate = path5.join(basePath, slug);
|
|
40104
|
+
try {
|
|
40105
|
+
await fs6.access(path5.join(candidate, ".ana-config.json"));
|
|
40106
|
+
return { found: true, workspacePath: candidate };
|
|
40107
|
+
} catch {
|
|
40108
|
+
return { found: false, workspacePath: null };
|
|
40109
|
+
}
|
|
40110
|
+
}
|
|
40111
|
+
var DEFAULT_BASE;
|
|
40112
|
+
var init_resolve = __esm({
|
|
40113
|
+
"src/workspace/resolve.ts"() {
|
|
40114
|
+
"use strict";
|
|
40115
|
+
DEFAULT_BASE = path5.join(os6.homedir(), "study");
|
|
40116
|
+
}
|
|
40117
|
+
});
|
|
40118
|
+
|
|
40037
40119
|
// src/commands/lesson.ts
|
|
40038
40120
|
import { Command as Command11 } from "commander";
|
|
40121
|
+
function adjustTimeEstimate(type, baseMinutes) {
|
|
40122
|
+
if (type === "exercise") return Math.max(baseMinutes, 30);
|
|
40123
|
+
return baseMinutes;
|
|
40124
|
+
}
|
|
40125
|
+
function isCheckpoint(title) {
|
|
40126
|
+
return /^checkpoint/i.test(title.trim());
|
|
40127
|
+
}
|
|
40039
40128
|
function formatLessonContent(data) {
|
|
40129
|
+
const time4 = adjustTimeEstimate(data.type, data.estimatedTimeMinutes);
|
|
40040
40130
|
const lines = [
|
|
40041
40131
|
`\u2501\u2501\u2501 Li\xE7\xE3o: ${data.title} \u2501\u2501\u2501`,
|
|
40042
40132
|
"",
|
|
40043
|
-
`Tipo: ${data.type} | Dura\xE7\xE3o estimada: ~${
|
|
40133
|
+
`Tipo: ${data.type} | Dura\xE7\xE3o estimada: ~${time4} min`,
|
|
40044
40134
|
"",
|
|
40045
40135
|
data.content
|
|
40046
40136
|
];
|
|
@@ -40050,12 +40140,20 @@ function formatLessonContent(data) {
|
|
|
40050
40140
|
if (data.hints && data.hints.length > 0) {
|
|
40051
40141
|
lines.push("", `Dicas dispon\xEDveis: ${data.hints.length}`);
|
|
40052
40142
|
}
|
|
40053
|
-
|
|
40054
|
-
|
|
40055
|
-
|
|
40056
|
-
|
|
40057
|
-
|
|
40058
|
-
|
|
40143
|
+
if (isCheckpoint(data.title)) {
|
|
40144
|
+
lines.push(
|
|
40145
|
+
"",
|
|
40146
|
+
"\u{1F4A1} Escreva suas respostas num arquivo (ex: checkpoint.md) e valide:",
|
|
40147
|
+
" tostudy validate checkpoint.md"
|
|
40148
|
+
);
|
|
40149
|
+
} else {
|
|
40150
|
+
lines.push(
|
|
40151
|
+
"",
|
|
40152
|
+
"\u2192 tostudy validate <arquivo> para validar sua solu\xE7\xE3o",
|
|
40153
|
+
"\u2192 tostudy hint para obter uma dica",
|
|
40154
|
+
"\u2192 tostudy next para avan\xE7ar para a pr\xF3xima li\xE7\xE3o"
|
|
40155
|
+
);
|
|
40156
|
+
}
|
|
40059
40157
|
return lines.join("\n");
|
|
40060
40158
|
}
|
|
40061
40159
|
var logger8, lessonCommand;
|
|
@@ -40067,6 +40165,7 @@ var init_lesson = __esm({
|
|
|
40067
40165
|
init_http2();
|
|
40068
40166
|
init_session();
|
|
40069
40167
|
init_formatter();
|
|
40168
|
+
init_resolve();
|
|
40070
40169
|
logger8 = createLogger("cli:lesson");
|
|
40071
40170
|
lessonCommand = new Command11("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").action(async (opts) => {
|
|
40072
40171
|
try {
|
|
@@ -40086,6 +40185,14 @@ var init_lesson = __esm({
|
|
|
40086
40185
|
} else {
|
|
40087
40186
|
output(formatLessonContent(content), { json: false });
|
|
40088
40187
|
}
|
|
40188
|
+
if (content.type === "exercise") {
|
|
40189
|
+
const ws = await resolveWorkspace(activeCourse.courseTitle);
|
|
40190
|
+
if (!ws.found) {
|
|
40191
|
+
process.stderr.write(
|
|
40192
|
+
"\n\u{1F4A1} Dica: rode `tostudy workspace setup` para criar um workspace local para exerc\xEDcios.\n"
|
|
40193
|
+
);
|
|
40194
|
+
}
|
|
40195
|
+
}
|
|
40089
40196
|
} catch (err) {
|
|
40090
40197
|
const msg = err instanceof Error ? err.message : String(err);
|
|
40091
40198
|
error(msg);
|
|
@@ -40158,134 +40265,26 @@ var init_exercises = __esm({
|
|
|
40158
40265
|
}
|
|
40159
40266
|
});
|
|
40160
40267
|
|
|
40161
|
-
// src/commands/validate.ts
|
|
40162
|
-
import fs6 from "node:fs";
|
|
40163
|
-
import { Command as Command13 } from "commander";
|
|
40164
|
-
var logger10, validateCommand;
|
|
40165
|
-
var init_validate = __esm({
|
|
40166
|
-
"src/commands/validate.ts"() {
|
|
40167
|
-
"use strict";
|
|
40168
|
-
init_src();
|
|
40169
|
-
init_exercises();
|
|
40170
|
-
init_http2();
|
|
40171
|
-
init_session();
|
|
40172
|
-
init_formatter();
|
|
40173
|
-
logger10 = createLogger("cli:validate");
|
|
40174
|
-
validateCommand = new Command13("validate").description("Validate your solution for the current exercise").argument("[file]", "Path to the solution file to read").option("--stdin", "Read solution from stdin instead of a file").option("--json", "Output structured JSON").action(async (file2, opts) => {
|
|
40175
|
-
try {
|
|
40176
|
-
const session = await requireSession();
|
|
40177
|
-
const activeCourse = await requireActiveCourse();
|
|
40178
|
-
const driftWarning = await checkCourseDrift();
|
|
40179
|
-
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
40180
|
-
const lessonId = activeCourse.currentLessonId;
|
|
40181
|
-
if (!lessonId) {
|
|
40182
|
-
error("Nenhuma li\xE7\xE3o ativa encontrada. Rode: tostudy start ou tostudy next");
|
|
40183
|
-
}
|
|
40184
|
-
let solution;
|
|
40185
|
-
if (opts.stdin) {
|
|
40186
|
-
solution = fs6.readFileSync("/dev/stdin", "utf-8");
|
|
40187
|
-
} else if (file2) {
|
|
40188
|
-
if (!fs6.existsSync(file2)) {
|
|
40189
|
-
error(`Arquivo n\xE3o encontrado: ${file2}`);
|
|
40190
|
-
}
|
|
40191
|
-
solution = fs6.readFileSync(file2, "utf-8");
|
|
40192
|
-
} else {
|
|
40193
|
-
error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
|
|
40194
|
-
}
|
|
40195
|
-
const data = createHttpProvider(session.apiUrl, session.token);
|
|
40196
|
-
const deps = { data, logger: logger10 };
|
|
40197
|
-
const result = await validateSolution(
|
|
40198
|
-
{
|
|
40199
|
-
lessonId,
|
|
40200
|
-
solution,
|
|
40201
|
-
userId: session.userId,
|
|
40202
|
-
enrollmentId: activeCourse.enrollmentId
|
|
40203
|
-
},
|
|
40204
|
-
deps
|
|
40205
|
-
);
|
|
40206
|
-
if (opts.json) {
|
|
40207
|
-
output(result, { json: true });
|
|
40208
|
-
} else {
|
|
40209
|
-
output(formatValidation(result), { json: false });
|
|
40210
|
-
}
|
|
40211
|
-
process.exit(result.passed ? 0 : 1);
|
|
40212
|
-
} catch (err) {
|
|
40213
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
40214
|
-
error(msg);
|
|
40215
|
-
}
|
|
40216
|
-
});
|
|
40217
|
-
}
|
|
40218
|
-
});
|
|
40219
|
-
|
|
40220
|
-
// src/commands/menu.ts
|
|
40221
|
-
import { Command as Command14 } from "commander";
|
|
40222
|
-
var menuCommand;
|
|
40223
|
-
var init_menu = __esm({
|
|
40224
|
-
"src/commands/menu.ts"() {
|
|
40225
|
-
"use strict";
|
|
40226
|
-
init_session();
|
|
40227
|
-
init_formatter();
|
|
40228
|
-
menuCommand = new Command14("menu").description("Show available commands and current study context").action(async () => {
|
|
40229
|
-
const session = await getSession();
|
|
40230
|
-
const activeCourse = session ? await getActiveCourse() : null;
|
|
40231
|
-
const lines = [
|
|
40232
|
-
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
40233
|
-
"\u2502 ToStudy CLI \u2014 Menu \u2502",
|
|
40234
|
-
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
|
40235
|
-
""
|
|
40236
|
-
];
|
|
40237
|
-
if (!session) {
|
|
40238
|
-
lines.push(
|
|
40239
|
-
" Status: N\xE3o autenticado",
|
|
40240
|
-
"",
|
|
40241
|
-
" Para come\xE7ar:",
|
|
40242
|
-
" tostudy login Autenticar com sua conta ToStudy",
|
|
40243
|
-
""
|
|
40244
|
-
);
|
|
40245
|
-
} else {
|
|
40246
|
-
lines.push(` Usu\xE1rio: ${session.userName}`);
|
|
40247
|
-
if (activeCourse) {
|
|
40248
|
-
lines.push(` Curso ativo: ${activeCourse.courseTitle}`, "");
|
|
40249
|
-
} else {
|
|
40250
|
-
lines.push(
|
|
40251
|
-
" Curso ativo: (nenhum)",
|
|
40252
|
-
" \u2192 tostudy courses para ver seus cursos",
|
|
40253
|
-
" \u2192 tostudy select <n\xFAmero> para ativar um curso",
|
|
40254
|
-
""
|
|
40255
|
-
);
|
|
40256
|
-
}
|
|
40257
|
-
lines.push(
|
|
40258
|
-
" \u2500\u2500\u2500 Navega\xE7\xE3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40259
|
-
" tostudy courses Listar seus cursos",
|
|
40260
|
-
" tostudy select <id> Ativar um curso",
|
|
40261
|
-
" tostudy progress Ver progresso do curso ativo",
|
|
40262
|
-
"",
|
|
40263
|
-
" \u2500\u2500\u2500 Estudo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40264
|
-
" tostudy start Iniciar/retomar o m\xF3dulo atual",
|
|
40265
|
-
" tostudy start-next Avan\xE7ar para o pr\xF3ximo m\xF3dulo",
|
|
40266
|
-
" tostudy next Avan\xE7ar para a pr\xF3xima li\xE7\xE3o",
|
|
40267
|
-
" tostudy lesson Ver conte\xFAdo da li\xE7\xE3o atual",
|
|
40268
|
-
"",
|
|
40269
|
-
" \u2500\u2500\u2500 Exerc\xEDcios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40270
|
-
" tostudy validate <arquivo> Validar sua solu\xE7\xE3o",
|
|
40271
|
-
" tostudy hint Obter uma dica progressiva",
|
|
40272
|
-
"",
|
|
40273
|
-
" \u2500\u2500\u2500 Dicas r\xE1pidas \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40274
|
-
" Adicione --json a qualquer comando para sa\xEDda em JSON",
|
|
40275
|
-
" Adicione --help a qualquer comando para ver op\xE7\xF5es",
|
|
40276
|
-
""
|
|
40277
|
-
);
|
|
40278
|
-
}
|
|
40279
|
-
output(lines.join("\n"), { json: false });
|
|
40280
|
-
});
|
|
40281
|
-
}
|
|
40282
|
-
});
|
|
40283
|
-
|
|
40284
40268
|
// src/output/init-template.ts
|
|
40269
|
+
function detectTechFromTags(tags) {
|
|
40270
|
+
const found = /* @__PURE__ */ new Set();
|
|
40271
|
+
const keywords = Object.keys(LANGUAGE_TAGS);
|
|
40272
|
+
const joined = tags.map((t) => t.toLowerCase()).join(" ");
|
|
40273
|
+
for (const kw of keywords) {
|
|
40274
|
+
const pattern = new RegExp(`\\b${kw.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
40275
|
+
if (pattern.test(joined)) {
|
|
40276
|
+
found.add(kw);
|
|
40277
|
+
}
|
|
40278
|
+
}
|
|
40279
|
+
return [...found];
|
|
40280
|
+
}
|
|
40285
40281
|
function formatCourseLanguage(tags) {
|
|
40286
40282
|
if (!tags || tags.length === 0) return "Programa\xE7\xE3o";
|
|
40287
|
-
const
|
|
40288
|
-
|
|
40283
|
+
const direct = tags.map((t) => LANGUAGE_TAGS[t.toLowerCase()]).filter(Boolean);
|
|
40284
|
+
if (direct.length > 0) return direct.slice(0, 3).join(" / ");
|
|
40285
|
+
const detected = detectTechFromTags(tags);
|
|
40286
|
+
const mapped = detected.map((t) => LANGUAGE_TAGS[t]).filter(Boolean);
|
|
40287
|
+
return mapped.length > 0 ? mapped.slice(0, 3).join(" / ") : "Programa\xE7\xE3o";
|
|
40289
40288
|
}
|
|
40290
40289
|
function formatCourseLevel(level) {
|
|
40291
40290
|
const map2 = {
|
|
@@ -40405,6 +40404,167 @@ var init_init_template = __esm({
|
|
|
40405
40404
|
}
|
|
40406
40405
|
});
|
|
40407
40406
|
|
|
40407
|
+
// src/commands/validate.ts
|
|
40408
|
+
import fs7 from "node:fs";
|
|
40409
|
+
import path6 from "node:path";
|
|
40410
|
+
import { Command as Command13 } from "commander";
|
|
40411
|
+
var logger10, validateCommand;
|
|
40412
|
+
var init_validate = __esm({
|
|
40413
|
+
"src/commands/validate.ts"() {
|
|
40414
|
+
"use strict";
|
|
40415
|
+
init_src();
|
|
40416
|
+
init_exercises();
|
|
40417
|
+
init_http2();
|
|
40418
|
+
init_session();
|
|
40419
|
+
init_formatter();
|
|
40420
|
+
init_init_template();
|
|
40421
|
+
logger10 = createLogger("cli:validate");
|
|
40422
|
+
validateCommand = new Command13("validate").description("Validate your solution for the current exercise").argument("[file]", "Path to the solution file to read").option("--stdin", "Read solution from stdin instead of a file").option("--json", "Output structured JSON").action(async (file2, opts) => {
|
|
40423
|
+
try {
|
|
40424
|
+
const session = await requireSession();
|
|
40425
|
+
const activeCourse = await requireActiveCourse();
|
|
40426
|
+
const driftWarning = await checkCourseDrift();
|
|
40427
|
+
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
40428
|
+
const lessonId = activeCourse.currentLessonId;
|
|
40429
|
+
if (!lessonId) {
|
|
40430
|
+
error("Nenhuma li\xE7\xE3o ativa encontrada. Rode: tostudy start ou tostudy next");
|
|
40431
|
+
}
|
|
40432
|
+
let solution;
|
|
40433
|
+
if (opts.stdin) {
|
|
40434
|
+
solution = fs7.readFileSync("/dev/stdin", "utf-8");
|
|
40435
|
+
} else if (file2) {
|
|
40436
|
+
if (!fs7.existsSync(file2)) {
|
|
40437
|
+
error(`Arquivo n\xE3o encontrado: ${file2}`);
|
|
40438
|
+
}
|
|
40439
|
+
solution = fs7.readFileSync(file2, "utf-8");
|
|
40440
|
+
} else {
|
|
40441
|
+
error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
|
|
40442
|
+
}
|
|
40443
|
+
if (file2 && activeCourse.courseTags?.length) {
|
|
40444
|
+
const ext = path6.extname(file2).toLowerCase();
|
|
40445
|
+
const LANG_EXTENSIONS = {
|
|
40446
|
+
".html": ["html", "html5"],
|
|
40447
|
+
".css": ["css"],
|
|
40448
|
+
".js": ["javascript", "node", "nodejs", "react", "vue", "angular", "svelte", "nextjs"],
|
|
40449
|
+
".ts": ["typescript", "react", "angular", "nextjs"],
|
|
40450
|
+
".tsx": ["typescript", "react", "nextjs"],
|
|
40451
|
+
".jsx": ["javascript", "react"],
|
|
40452
|
+
".py": ["python"],
|
|
40453
|
+
".go": ["go"],
|
|
40454
|
+
".rs": ["rust"],
|
|
40455
|
+
".java": ["java"],
|
|
40456
|
+
".kt": ["kotlin"],
|
|
40457
|
+
".swift": ["swift"],
|
|
40458
|
+
".rb": ["ruby"],
|
|
40459
|
+
".php": ["php"],
|
|
40460
|
+
".cs": ["csharp", "c#"],
|
|
40461
|
+
".cpp": ["cpp"],
|
|
40462
|
+
".sql": ["sql"],
|
|
40463
|
+
".vue": ["vue"]
|
|
40464
|
+
};
|
|
40465
|
+
const expectedTags = LANG_EXTENSIONS[ext];
|
|
40466
|
+
if (expectedTags) {
|
|
40467
|
+
const courseTech = detectTechFromTags(activeCourse.courseTags);
|
|
40468
|
+
const matches = expectedTags.some((t) => courseTech.includes(t));
|
|
40469
|
+
if (!matches) {
|
|
40470
|
+
const lang = formatCourseLanguage(activeCourse.courseTags);
|
|
40471
|
+
process.stderr.write(
|
|
40472
|
+
`\u26A0\uFE0F Arquivo ${ext} \u2014 este curso \xE9 de ${lang}. Verifique se est\xE1 enviando o arquivo correto.
|
|
40473
|
+
|
|
40474
|
+
`
|
|
40475
|
+
);
|
|
40476
|
+
}
|
|
40477
|
+
}
|
|
40478
|
+
}
|
|
40479
|
+
const data = createHttpProvider(session.apiUrl, session.token);
|
|
40480
|
+
const deps = { data, logger: logger10 };
|
|
40481
|
+
const result = await validateSolution(
|
|
40482
|
+
{
|
|
40483
|
+
lessonId,
|
|
40484
|
+
solution,
|
|
40485
|
+
userId: session.userId,
|
|
40486
|
+
enrollmentId: activeCourse.enrollmentId
|
|
40487
|
+
},
|
|
40488
|
+
deps
|
|
40489
|
+
);
|
|
40490
|
+
if (opts.json) {
|
|
40491
|
+
output(result, { json: true });
|
|
40492
|
+
} else {
|
|
40493
|
+
output(formatValidation(result), { json: false });
|
|
40494
|
+
}
|
|
40495
|
+
process.exit(result.passed ? 0 : 1);
|
|
40496
|
+
} catch (err) {
|
|
40497
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40498
|
+
error(msg);
|
|
40499
|
+
}
|
|
40500
|
+
});
|
|
40501
|
+
}
|
|
40502
|
+
});
|
|
40503
|
+
|
|
40504
|
+
// src/commands/menu.ts
|
|
40505
|
+
import { Command as Command14 } from "commander";
|
|
40506
|
+
var menuCommand;
|
|
40507
|
+
var init_menu = __esm({
|
|
40508
|
+
"src/commands/menu.ts"() {
|
|
40509
|
+
"use strict";
|
|
40510
|
+
init_session();
|
|
40511
|
+
init_formatter();
|
|
40512
|
+
menuCommand = new Command14("menu").description("Show available commands and current study context").action(async () => {
|
|
40513
|
+
const session = await getSession();
|
|
40514
|
+
const activeCourse = session ? await getActiveCourse() : null;
|
|
40515
|
+
const lines = [
|
|
40516
|
+
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
40517
|
+
"\u2502 ToStudy CLI \u2014 Menu \u2502",
|
|
40518
|
+
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518",
|
|
40519
|
+
""
|
|
40520
|
+
];
|
|
40521
|
+
if (!session) {
|
|
40522
|
+
lines.push(
|
|
40523
|
+
" Status: N\xE3o autenticado",
|
|
40524
|
+
"",
|
|
40525
|
+
" Para come\xE7ar:",
|
|
40526
|
+
" tostudy login Autenticar com sua conta ToStudy",
|
|
40527
|
+
""
|
|
40528
|
+
);
|
|
40529
|
+
} else {
|
|
40530
|
+
lines.push(` Usu\xE1rio: ${session.userName}`);
|
|
40531
|
+
if (activeCourse) {
|
|
40532
|
+
lines.push(` Curso ativo: ${activeCourse.courseTitle}`, "");
|
|
40533
|
+
} else {
|
|
40534
|
+
lines.push(
|
|
40535
|
+
" Curso ativo: (nenhum)",
|
|
40536
|
+
" \u2192 tostudy courses para ver seus cursos",
|
|
40537
|
+
" \u2192 tostudy select <n\xFAmero> para ativar um curso",
|
|
40538
|
+
""
|
|
40539
|
+
);
|
|
40540
|
+
}
|
|
40541
|
+
lines.push(
|
|
40542
|
+
" \u2500\u2500\u2500 Navega\xE7\xE3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40543
|
+
" tostudy courses Listar seus cursos",
|
|
40544
|
+
" tostudy select <id> Ativar um curso",
|
|
40545
|
+
" tostudy progress Ver progresso do curso ativo",
|
|
40546
|
+
"",
|
|
40547
|
+
" \u2500\u2500\u2500 Estudo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40548
|
+
" tostudy start Iniciar/retomar o m\xF3dulo atual",
|
|
40549
|
+
" tostudy start-next Avan\xE7ar para o pr\xF3ximo m\xF3dulo",
|
|
40550
|
+
" tostudy next Avan\xE7ar para a pr\xF3xima li\xE7\xE3o",
|
|
40551
|
+
" tostudy lesson Ver conte\xFAdo da li\xE7\xE3o atual",
|
|
40552
|
+
"",
|
|
40553
|
+
" \u2500\u2500\u2500 Exerc\xEDcios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40554
|
+
" tostudy validate <arquivo> Validar sua solu\xE7\xE3o",
|
|
40555
|
+
" tostudy hint Obter uma dica progressiva",
|
|
40556
|
+
"",
|
|
40557
|
+
" \u2500\u2500\u2500 Dicas r\xE1pidas \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
40558
|
+
" Adicione --json a qualquer comando para sa\xEDda em JSON",
|
|
40559
|
+
" Adicione --help a qualquer comando para ver op\xE7\xF5es",
|
|
40560
|
+
""
|
|
40561
|
+
);
|
|
40562
|
+
}
|
|
40563
|
+
output(lines.join("\n"), { json: false });
|
|
40564
|
+
});
|
|
40565
|
+
}
|
|
40566
|
+
});
|
|
40567
|
+
|
|
40408
40568
|
// src/commands/init.ts
|
|
40409
40569
|
import { Command as Command15 } from "commander";
|
|
40410
40570
|
var logger11, initCommand;
|
|
@@ -40483,14 +40643,14 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
|
|
|
40483
40643
|
});
|
|
40484
40644
|
|
|
40485
40645
|
// ../../packages/tostudy-core/src/workspace/setup-workspace.ts
|
|
40486
|
-
import
|
|
40487
|
-
import
|
|
40646
|
+
import fs8 from "node:fs/promises";
|
|
40647
|
+
import path7 from "node:path";
|
|
40488
40648
|
async function setupWorkspace(input) {
|
|
40489
|
-
const workspacePath =
|
|
40649
|
+
const workspacePath = path7.join(input.basePath, input.courseSlug);
|
|
40490
40650
|
for (const dir of WORKSPACE_DIRS) {
|
|
40491
|
-
await
|
|
40651
|
+
await fs8.mkdir(path7.join(workspacePath, dir), { recursive: true });
|
|
40492
40652
|
}
|
|
40493
|
-
const configPath =
|
|
40653
|
+
const configPath = path7.join(workspacePath, ".ana-config.json");
|
|
40494
40654
|
const config2 = {
|
|
40495
40655
|
courseId: input.courseId,
|
|
40496
40656
|
courseSlug: input.courseSlug,
|
|
@@ -40500,7 +40660,7 @@ async function setupWorkspace(input) {
|
|
|
40500
40660
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40501
40661
|
lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
40502
40662
|
};
|
|
40503
|
-
await
|
|
40663
|
+
await fs8.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
40504
40664
|
const readme = [
|
|
40505
40665
|
`# ${input.courseName}`,
|
|
40506
40666
|
"",
|
|
@@ -40525,7 +40685,7 @@ async function setupWorkspace(input) {
|
|
|
40525
40685
|
"tostudy vault sync # Sincronizar progresso",
|
|
40526
40686
|
"```"
|
|
40527
40687
|
].join("\n");
|
|
40528
|
-
await
|
|
40688
|
+
await fs8.writeFile(path7.join(workspacePath, "README.md"), readme, "utf-8");
|
|
40529
40689
|
return { workspacePath, directories: WORKSPACE_DIRS, configPath };
|
|
40530
40690
|
}
|
|
40531
40691
|
var WORKSPACE_DIRS;
|
|
@@ -40626,8 +40786,8 @@ var init_templates = __esm({
|
|
|
40626
40786
|
});
|
|
40627
40787
|
|
|
40628
40788
|
// ../../packages/tostudy-core/src/workspace/extract-exercise.ts
|
|
40629
|
-
import
|
|
40630
|
-
import
|
|
40789
|
+
import fs9 from "node:fs/promises";
|
|
40790
|
+
import path8 from "node:path";
|
|
40631
40791
|
function padOrder(n) {
|
|
40632
40792
|
return String(n).padStart(2, "0");
|
|
40633
40793
|
}
|
|
@@ -40651,16 +40811,16 @@ async function extractExercise(input) {
|
|
|
40651
40811
|
const { lessonData, exerciseTier, workspacePath } = input;
|
|
40652
40812
|
const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
|
|
40653
40813
|
const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
|
|
40654
|
-
const exercisePath =
|
|
40655
|
-
await
|
|
40814
|
+
const exercisePath = path8.join(workspacePath, "exercises", moduleDir, lessonDir);
|
|
40815
|
+
await fs9.mkdir(exercisePath, { recursive: true });
|
|
40656
40816
|
const extractedFiles = [];
|
|
40657
40817
|
let hasStarterCode = false;
|
|
40658
40818
|
if (lessonData.sandpackConfig?.files) {
|
|
40659
40819
|
for (const [filePath, fileData] of Object.entries(lessonData.sandpackConfig.files)) {
|
|
40660
40820
|
const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
40661
|
-
const fullPath =
|
|
40662
|
-
await
|
|
40663
|
-
await
|
|
40821
|
+
const fullPath = path8.join(exercisePath, cleanPath);
|
|
40822
|
+
await fs9.mkdir(path8.dirname(fullPath), { recursive: true });
|
|
40823
|
+
await fs9.writeFile(fullPath, fileData.code, "utf-8");
|
|
40664
40824
|
extractedFiles.push(cleanPath);
|
|
40665
40825
|
hasStarterCode = true;
|
|
40666
40826
|
}
|
|
@@ -40668,13 +40828,13 @@ async function extractExercise(input) {
|
|
|
40668
40828
|
const tierData = getTierData(lessonData.structuredData, exerciseTier);
|
|
40669
40829
|
const tierCode = tierData?.code;
|
|
40670
40830
|
if (tierCode) {
|
|
40671
|
-
await
|
|
40831
|
+
await fs9.writeFile(path8.join(exercisePath, "exercise.js"), tierCode, "utf-8");
|
|
40672
40832
|
extractedFiles.push("exercise.js");
|
|
40673
40833
|
hasStarterCode = true;
|
|
40674
40834
|
} else {
|
|
40675
40835
|
const starter = getStarterCode(lessonData.structuredData);
|
|
40676
40836
|
if (starter) {
|
|
40677
|
-
await
|
|
40837
|
+
await fs9.writeFile(path8.join(exercisePath, "exercise.js"), starter, "utf-8");
|
|
40678
40838
|
extractedFiles.push("exercise.js");
|
|
40679
40839
|
hasStarterCode = true;
|
|
40680
40840
|
}
|
|
@@ -40692,8 +40852,8 @@ async function extractExercise(input) {
|
|
|
40692
40852
|
...exerciseDeps
|
|
40693
40853
|
}
|
|
40694
40854
|
};
|
|
40695
|
-
await
|
|
40696
|
-
|
|
40855
|
+
await fs9.writeFile(
|
|
40856
|
+
path8.join(exercisePath, "package.json"),
|
|
40697
40857
|
JSON.stringify(pkgJson, null, 2),
|
|
40698
40858
|
"utf-8"
|
|
40699
40859
|
);
|
|
@@ -40705,20 +40865,20 @@ async function extractExercise(input) {
|
|
|
40705
40865
|
);
|
|
40706
40866
|
for (const [configFile, configContent] of Object.entries(scaffold.configs)) {
|
|
40707
40867
|
if (!sandpackFileNames.has(configFile)) {
|
|
40708
|
-
await
|
|
40868
|
+
await fs9.writeFile(path8.join(exercisePath, configFile), configContent, "utf-8");
|
|
40709
40869
|
extractedFiles.push(configFile);
|
|
40710
40870
|
}
|
|
40711
40871
|
}
|
|
40712
40872
|
const setupSh = `#!/bin/sh
|
|
40713
40873
|
${scaffold.setupScript}
|
|
40714
40874
|
`;
|
|
40715
|
-
await
|
|
40875
|
+
await fs9.writeFile(path8.join(exercisePath, "setup.sh"), setupSh, "utf-8");
|
|
40716
40876
|
extractedFiles.push("setup.sh");
|
|
40717
40877
|
}
|
|
40718
40878
|
}
|
|
40719
40879
|
const readme = generateReadme(lessonData, exerciseTier);
|
|
40720
|
-
const readmePath =
|
|
40721
|
-
await
|
|
40880
|
+
const readmePath = path8.join(exercisePath, "README.md");
|
|
40881
|
+
await fs9.writeFile(readmePath, readme, "utf-8");
|
|
40722
40882
|
extractedFiles.push("README.md");
|
|
40723
40883
|
return {
|
|
40724
40884
|
exercisePath,
|
|
@@ -40789,9 +40949,9 @@ var init_workspace = __esm({
|
|
|
40789
40949
|
|
|
40790
40950
|
// src/commands/workspace.ts
|
|
40791
40951
|
import { Command as Command16 } from "commander";
|
|
40792
|
-
import
|
|
40793
|
-
import
|
|
40794
|
-
import
|
|
40952
|
+
import path9 from "node:path";
|
|
40953
|
+
import os7 from "node:os";
|
|
40954
|
+
import fs10 from "node:fs/promises";
|
|
40795
40955
|
var logger12, workspaceCommand;
|
|
40796
40956
|
var init_workspace2 = __esm({
|
|
40797
40957
|
"src/commands/workspace.ts"() {
|
|
@@ -40803,7 +40963,7 @@ var init_workspace2 = __esm({
|
|
|
40803
40963
|
workspaceCommand = new Command16("workspace").description(
|
|
40804
40964
|
"Gerenciar workspace de estudo local"
|
|
40805
40965
|
);
|
|
40806
|
-
workspaceCommand.command("setup").description("Criar estrutura do workspace para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
40966
|
+
workspaceCommand.command("setup").description("Criar estrutura do workspace para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path9.join(os7.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
40807
40967
|
try {
|
|
40808
40968
|
await requireSession();
|
|
40809
40969
|
const activeCourse = await requireActiveCourse();
|
|
@@ -40835,14 +40995,14 @@ Pr\xF3ximo passo: tostudy export
|
|
|
40835
40995
|
process.exit(1);
|
|
40836
40996
|
}
|
|
40837
40997
|
});
|
|
40838
|
-
workspaceCommand.command("status").description("Mostrar status do workspace do curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
40998
|
+
workspaceCommand.command("status").description("Mostrar status do workspace do curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path9.join(os7.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
40839
40999
|
try {
|
|
40840
41000
|
const activeCourse = await requireActiveCourse();
|
|
40841
|
-
const
|
|
40842
|
-
const workspacePath =
|
|
41001
|
+
const courseSlug2 = activeCourse.courseTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
41002
|
+
const workspacePath = path9.join(opts.path, courseSlug2);
|
|
40843
41003
|
let configData = null;
|
|
40844
41004
|
try {
|
|
40845
|
-
const raw = await
|
|
41005
|
+
const raw = await fs10.readFile(path9.join(workspacePath, ".ana-config.json"), "utf-8");
|
|
40846
41006
|
configData = JSON.parse(raw);
|
|
40847
41007
|
} catch {
|
|
40848
41008
|
process.stderr.write(
|
|
@@ -40850,42 +41010,42 @@ Pr\xF3ximo passo: tostudy export
|
|
|
40850
41010
|
);
|
|
40851
41011
|
process.exit(1);
|
|
40852
41012
|
}
|
|
40853
|
-
const exercisesDir =
|
|
41013
|
+
const exercisesDir = path9.join(workspacePath, "exercises");
|
|
40854
41014
|
let exerciseCount = 0;
|
|
40855
41015
|
try {
|
|
40856
|
-
const moduleDirs = await
|
|
41016
|
+
const moduleDirs = await fs10.readdir(exercisesDir);
|
|
40857
41017
|
for (const modDir of moduleDirs) {
|
|
40858
|
-
const modPath =
|
|
40859
|
-
const stat = await
|
|
41018
|
+
const modPath = path9.join(exercisesDir, modDir);
|
|
41019
|
+
const stat = await fs10.stat(modPath);
|
|
40860
41020
|
if (stat.isDirectory()) {
|
|
40861
|
-
const lessonDirs = await
|
|
41021
|
+
const lessonDirs = await fs10.readdir(modPath);
|
|
40862
41022
|
for (const lessonDir of lessonDirs) {
|
|
40863
|
-
const lessonPath =
|
|
40864
|
-
const lstat = await
|
|
41023
|
+
const lessonPath = path9.join(modPath, lessonDir);
|
|
41024
|
+
const lstat = await fs10.stat(lessonPath);
|
|
40865
41025
|
if (lstat.isDirectory()) exerciseCount++;
|
|
40866
41026
|
}
|
|
40867
41027
|
}
|
|
40868
41028
|
}
|
|
40869
41029
|
} catch {
|
|
40870
41030
|
}
|
|
40871
|
-
const generatedDir =
|
|
41031
|
+
const generatedDir = path9.join(workspacePath, "generated");
|
|
40872
41032
|
let artifactCount = 0;
|
|
40873
41033
|
try {
|
|
40874
|
-
const files = await
|
|
41034
|
+
const files = await fs10.readdir(generatedDir);
|
|
40875
41035
|
artifactCount = files.length;
|
|
40876
41036
|
} catch {
|
|
40877
41037
|
}
|
|
40878
|
-
const diagramsDir =
|
|
41038
|
+
const diagramsDir = path9.join(workspacePath, "diagrams");
|
|
40879
41039
|
let diagramCount = 0;
|
|
40880
41040
|
try {
|
|
40881
|
-
const files = await
|
|
41041
|
+
const files = await fs10.readdir(diagramsDir);
|
|
40882
41042
|
diagramCount = files.length;
|
|
40883
41043
|
} catch {
|
|
40884
41044
|
}
|
|
40885
|
-
const vaultDir =
|
|
41045
|
+
const vaultDir = path9.join(workspacePath, "vault");
|
|
40886
41046
|
let hasVault = false;
|
|
40887
41047
|
try {
|
|
40888
|
-
await
|
|
41048
|
+
await fs10.access(path9.join(vaultDir, ".ana-vault.json"));
|
|
40889
41049
|
hasVault = true;
|
|
40890
41050
|
} catch {
|
|
40891
41051
|
}
|
|
@@ -40929,19 +41089,8 @@ Pr\xF3ximo passo: tostudy export
|
|
|
40929
41089
|
|
|
40930
41090
|
// src/commands/export.ts
|
|
40931
41091
|
import { Command as Command17 } from "commander";
|
|
40932
|
-
import
|
|
40933
|
-
import
|
|
40934
|
-
import os7 from "node:os";
|
|
40935
|
-
async function findWorkspacePath(courseTitle, basePath) {
|
|
40936
|
-
const slug = courseTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
40937
|
-
const candidate = path8.join(basePath, slug);
|
|
40938
|
-
try {
|
|
40939
|
-
const config2 = await fs10.readFile(path8.join(candidate, ".ana-config.json"), "utf-8");
|
|
40940
|
-
if (config2) return candidate;
|
|
40941
|
-
} catch {
|
|
40942
|
-
}
|
|
40943
|
-
return null;
|
|
40944
|
-
}
|
|
41092
|
+
import path10 from "node:path";
|
|
41093
|
+
import os8 from "node:os";
|
|
40945
41094
|
var logger13, exportCommand;
|
|
40946
41095
|
var init_export = __esm({
|
|
40947
41096
|
"src/commands/export.ts"() {
|
|
@@ -40950,8 +41099,9 @@ var init_export = __esm({
|
|
|
40950
41099
|
init_workspace();
|
|
40951
41100
|
init_http2();
|
|
40952
41101
|
init_session();
|
|
41102
|
+
init_resolve();
|
|
40953
41103
|
logger13 = createLogger("cli:export");
|
|
40954
|
-
exportCommand = new Command17("export").description("Extrair exerc\xEDcio atual para o workspace local").option("--tier <tier>", "Tier do exerc\xEDcio: guided, semiGuided, challenging", "guided").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
41104
|
+
exportCommand = new Command17("export").description("Extrair exerc\xEDcio atual para o workspace local").option("--tier <tier>", "Tier do exerc\xEDcio: guided, semiGuided, challenging", "guided").option("--path <dir>", "Diret\xF3rio base do workspace", path10.join(os8.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
40955
41105
|
try {
|
|
40956
41106
|
const session = await requireSession();
|
|
40957
41107
|
const activeCourse = await requireActiveCourse();
|
|
@@ -40961,8 +41111,8 @@ var init_export = __esm({
|
|
|
40961
41111
|
process.stderr.write("\u274C Nenhuma li\xE7\xE3o ativa. Execute 'tostudy start' primeiro.\n");
|
|
40962
41112
|
process.exit(1);
|
|
40963
41113
|
}
|
|
40964
|
-
const
|
|
40965
|
-
if (!
|
|
41114
|
+
const ws = await resolveWorkspace(activeCourse.courseTitle, opts.path);
|
|
41115
|
+
if (!ws.found) {
|
|
40966
41116
|
process.stderr.write(
|
|
40967
41117
|
"\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' primeiro.\n"
|
|
40968
41118
|
);
|
|
@@ -40974,7 +41124,7 @@ var init_export = __esm({
|
|
|
40974
41124
|
const result = await extractExercise({
|
|
40975
41125
|
lessonData,
|
|
40976
41126
|
exerciseTier: tier,
|
|
40977
|
-
workspacePath
|
|
41127
|
+
workspacePath: ws.workspacePath
|
|
40978
41128
|
});
|
|
40979
41129
|
if (opts.json) {
|
|
40980
41130
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
@@ -41008,13 +41158,13 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
|
|
|
41008
41158
|
import { Command as Command18 } from "commander";
|
|
41009
41159
|
import { execFile as execFile3 } from "node:child_process";
|
|
41010
41160
|
import fs11 from "node:fs/promises";
|
|
41011
|
-
import
|
|
41012
|
-
import
|
|
41013
|
-
async function
|
|
41161
|
+
import path11 from "node:path";
|
|
41162
|
+
import os9 from "node:os";
|
|
41163
|
+
async function findWorkspacePath(courseTitle, basePath) {
|
|
41014
41164
|
const slug = courseTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
41015
|
-
const candidate =
|
|
41165
|
+
const candidate = path11.join(basePath, slug);
|
|
41016
41166
|
try {
|
|
41017
|
-
await fs11.access(
|
|
41167
|
+
await fs11.access(path11.join(candidate, ".ana-config.json"));
|
|
41018
41168
|
return candidate;
|
|
41019
41169
|
} catch {
|
|
41020
41170
|
return null;
|
|
@@ -41027,10 +41177,10 @@ var init_open = __esm({
|
|
|
41027
41177
|
init_src();
|
|
41028
41178
|
init_session();
|
|
41029
41179
|
logger14 = createLogger("cli:open");
|
|
41030
|
-
openCommand = new Command18("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
41180
|
+
openCommand = new Command18("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path11.join(os9.homedir(), "study")).action(async (opts) => {
|
|
41031
41181
|
try {
|
|
41032
41182
|
const activeCourse = await requireActiveCourse();
|
|
41033
|
-
const workspacePath = await
|
|
41183
|
+
const workspacePath = await findWorkspacePath(activeCourse.courseTitle, opts.path);
|
|
41034
41184
|
if (!workspacePath) {
|
|
41035
41185
|
process.stderr.write(
|
|
41036
41186
|
"\u274C Workspace n\xE3o encontrado. Execute 'tostudy workspace setup' primeiro.\n"
|
|
@@ -41077,23 +41227,23 @@ var init_types3 = __esm({
|
|
|
41077
41227
|
|
|
41078
41228
|
// ../../packages/tostudy-core/src/vault/write-vault.ts
|
|
41079
41229
|
import fs12 from "node:fs/promises";
|
|
41080
|
-
import
|
|
41081
|
-
async function writeVaultFiles(files, outputPath, courseId,
|
|
41230
|
+
import path12 from "node:path";
|
|
41231
|
+
async function writeVaultFiles(files, outputPath, courseId, courseSlug2) {
|
|
41082
41232
|
for (const file2 of files) {
|
|
41083
|
-
const fullPath =
|
|
41084
|
-
await fs12.mkdir(
|
|
41233
|
+
const fullPath = path12.join(outputPath, file2.relativePath);
|
|
41234
|
+
await fs12.mkdir(path12.dirname(fullPath), { recursive: true });
|
|
41085
41235
|
await fs12.writeFile(fullPath, file2.content, "utf-8");
|
|
41086
41236
|
}
|
|
41087
|
-
const vaultPath =
|
|
41237
|
+
const vaultPath = path12.join(outputPath, courseSlug2);
|
|
41088
41238
|
const marker = {
|
|
41089
41239
|
courseId,
|
|
41090
|
-
courseSlug,
|
|
41240
|
+
courseSlug: courseSlug2,
|
|
41091
41241
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41092
41242
|
version: VAULT_MARKER_VERSION
|
|
41093
41243
|
};
|
|
41094
41244
|
await fs12.mkdir(vaultPath, { recursive: true });
|
|
41095
41245
|
await fs12.writeFile(
|
|
41096
|
-
|
|
41246
|
+
path12.join(vaultPath, VAULT_MARKER_FILENAME),
|
|
41097
41247
|
JSON.stringify(marker, null, 2),
|
|
41098
41248
|
"utf-8"
|
|
41099
41249
|
);
|
|
@@ -41118,8 +41268,8 @@ var init_vault = __esm({
|
|
|
41118
41268
|
|
|
41119
41269
|
// src/commands/vault.ts
|
|
41120
41270
|
import { Command as Command19 } from "commander";
|
|
41121
|
-
import
|
|
41122
|
-
import
|
|
41271
|
+
import path13 from "node:path";
|
|
41272
|
+
import os10 from "node:os";
|
|
41123
41273
|
import fs13 from "node:fs/promises";
|
|
41124
41274
|
var logger15, vaultCommand;
|
|
41125
41275
|
var init_vault2 = __esm({
|
|
@@ -41132,15 +41282,15 @@ var init_vault2 = __esm({
|
|
|
41132
41282
|
init_session();
|
|
41133
41283
|
logger15 = createLogger("cli:vault");
|
|
41134
41284
|
vaultCommand = new Command19("vault").description("Gerenciar vault Obsidian do curso");
|
|
41135
|
-
vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
41285
|
+
vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path13.join(os10.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
41136
41286
|
try {
|
|
41137
41287
|
const session = await requireSession();
|
|
41138
41288
|
const activeCourse = await requireActiveCourse();
|
|
41139
41289
|
const driftWarning = await checkCourseDrift();
|
|
41140
41290
|
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
41141
|
-
const
|
|
41142
|
-
const workspacePath =
|
|
41143
|
-
const vaultOutputPath =
|
|
41291
|
+
const courseSlug2 = activeCourse.courseTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
41292
|
+
const workspacePath = path13.join(opts.path, courseSlug2);
|
|
41293
|
+
const vaultOutputPath = path13.join(workspacePath, "vault");
|
|
41144
41294
|
const res = await fetch(`${session.apiUrl}/api/cli/vault/init`, {
|
|
41145
41295
|
method: "POST",
|
|
41146
41296
|
headers: {
|
|
@@ -41162,7 +41312,7 @@ var init_vault2 = __esm({
|
|
|
41162
41312
|
data.files,
|
|
41163
41313
|
vaultOutputPath,
|
|
41164
41314
|
activeCourse.courseId,
|
|
41165
|
-
|
|
41315
|
+
courseSlug2
|
|
41166
41316
|
);
|
|
41167
41317
|
logger15.info("Vault generated", {
|
|
41168
41318
|
courseId: activeCourse.courseId,
|
|
@@ -41203,16 +41353,16 @@ Para visualizar:
|
|
|
41203
41353
|
process.exit(1);
|
|
41204
41354
|
}
|
|
41205
41355
|
});
|
|
41206
|
-
vaultCommand.command("sync").description("Sincronizar progresso do curso com o vault local").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
41356
|
+
vaultCommand.command("sync").description("Sincronizar progresso do curso com o vault local").option("--path <dir>", "Diret\xF3rio base do workspace", path13.join(os10.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
41207
41357
|
try {
|
|
41208
41358
|
const session = await requireSession();
|
|
41209
41359
|
const activeCourse = await requireActiveCourse();
|
|
41210
41360
|
const driftWarning = await checkCourseDrift();
|
|
41211
41361
|
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
41212
|
-
const
|
|
41213
|
-
const vaultPath =
|
|
41362
|
+
const courseSlug2 = activeCourse.courseTitle.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
41363
|
+
const vaultPath = path13.join(opts.path, courseSlug2, "vault");
|
|
41214
41364
|
try {
|
|
41215
|
-
await fs13.access(
|
|
41365
|
+
await fs13.access(path13.join(vaultPath, ".ana-vault.json"));
|
|
41216
41366
|
} catch {
|
|
41217
41367
|
process.stderr.write("\u274C Vault n\xE3o encontrado. Execute 'tostudy vault init' primeiro.\n");
|
|
41218
41368
|
process.exit(1);
|
|
@@ -41220,7 +41370,7 @@ Para visualizar:
|
|
|
41220
41370
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
41221
41371
|
const deps = { data, logger: logger15 };
|
|
41222
41372
|
const progress3 = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
41223
|
-
const markerPath =
|
|
41373
|
+
const markerPath = path13.join(vaultPath, ".ana-vault.json");
|
|
41224
41374
|
const markerRaw = await fs13.readFile(markerPath, "utf-8");
|
|
41225
41375
|
const marker = JSON.parse(markerRaw);
|
|
41226
41376
|
marker.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -41230,7 +41380,7 @@ Para visualizar:
|
|
|
41230
41380
|
currentLesson: progress3.currentLesson.title
|
|
41231
41381
|
};
|
|
41232
41382
|
await fs13.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
|
|
41233
|
-
const courseIndexPath =
|
|
41383
|
+
const courseIndexPath = path13.join(vaultPath, courseSlug2, "index.md");
|
|
41234
41384
|
try {
|
|
41235
41385
|
let indexContent = await fs13.readFile(courseIndexPath, "utf-8");
|
|
41236
41386
|
indexContent = indexContent.replace(/\n---\n\n> 📊 Progresso:.*\n/g, "");
|
|
@@ -41290,7 +41440,7 @@ __export(cli_exports, {
|
|
|
41290
41440
|
import { Command as Command20 } from "commander";
|
|
41291
41441
|
function createProgram() {
|
|
41292
41442
|
const program2 = new Command20();
|
|
41293
|
-
program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--
|
|
41443
|
+
program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID");
|
|
41294
41444
|
program2.addCommand(setupCommand);
|
|
41295
41445
|
program2.addCommand(doctorCommand);
|
|
41296
41446
|
program2.addCommand(initCommand);
|
|
@@ -41335,7 +41485,7 @@ var init_cli = __esm({
|
|
|
41335
41485
|
init_export();
|
|
41336
41486
|
init_open();
|
|
41337
41487
|
init_vault2();
|
|
41338
|
-
CLI_VERSION = "0.
|
|
41488
|
+
CLI_VERSION = "0.6.0";
|
|
41339
41489
|
}
|
|
41340
41490
|
});
|
|
41341
41491
|
|