@tostudy-ai/cli 0.18.4 → 0.18.5
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/main.js +524 -244
- package/dist/main.js.map +4 -4
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1029,6 +1029,13 @@ var init_errors_pt_br = __esm({
|
|
|
1029
1029
|
enrollDone: "Matriculado. Rode `tostudy courses` e depois `tostudy select <n>`.",
|
|
1030
1030
|
enrollDoneWebOnly: "Matriculado. Este curso n\xE3o abre no terminal. Continue no navegador:",
|
|
1031
1031
|
enrollPaid: "Este curso \xE9 pago. A matr\xEDcula fica no navegador e nada \xE9 debitado por aqui:",
|
|
1032
|
+
suggestCommandDescription: "Sugere at\xE9 3 cursos gratuitos para matricular no terminal.",
|
|
1033
|
+
suggestEmpty: "Nenhum curso gratuito dispon\xEDvel para o terminal.",
|
|
1034
|
+
suggestNoWorkspace: "Sem contexto nesta pasta. Usei s\xF3 o perfil.",
|
|
1035
|
+
suggestNoOverlap: "Sem coincid\xEAncia com o perfil.",
|
|
1036
|
+
suggestEmptyProfile: "Perfil vazio. Mostrando cursos gratuitos em aberto.",
|
|
1037
|
+
suggestReason: "Motivo:",
|
|
1038
|
+
suggestPaidHeader: "Pagos. A matr\xEDcula fica no navegador e nada \xE9 debitado por aqui:",
|
|
1032
1039
|
enrollmentNotEntitled: "\u{1F6AB} Sua matr\xEDcula neste curso n\xE3o est\xE1 mais ativa (reembolso, cancelamento ou preview expirado).\nPara voltar a estudar, adquira o curso novamente em https://tostudy.ai.\n",
|
|
1033
1040
|
creator: {
|
|
1034
1041
|
loginBrowserOnly: "`--creator` s\xF3 funciona no login pelo browser. Rode `tostudy login --creator` sem --code, --magic-link ou --manual.",
|
|
@@ -1131,6 +1138,13 @@ var init_errors_en_us = __esm({
|
|
|
1131
1138
|
enrollDone: "Enrolled. Run `tostudy courses`, then `tostudy select <n>`.",
|
|
1132
1139
|
enrollDoneWebOnly: "Enrolled. This course does not open in the terminal. Continue in the browser:",
|
|
1133
1140
|
enrollPaid: "This course is paid. Enrollment stays in the browser and nothing is charged here:",
|
|
1141
|
+
suggestCommandDescription: "Suggest up to 3 free courses to enroll in from the terminal.",
|
|
1142
|
+
suggestEmpty: "No free course is available for the terminal.",
|
|
1143
|
+
suggestNoWorkspace: "No context in this folder. Only the profile was used.",
|
|
1144
|
+
suggestNoOverlap: "No word in the profile matched.",
|
|
1145
|
+
suggestEmptyProfile: "Profile is empty. Showing open free courses.",
|
|
1146
|
+
suggestReason: "Why:",
|
|
1147
|
+
suggestPaidHeader: "Paid. Enrollment stays in the browser and nothing is charged here:",
|
|
1134
1148
|
enrollmentNotEntitled: "\u{1F6AB} Your enrollment in this course is no longer active (refund, cancellation, or expired preview).\nTo study again, purchase the course at https://tostudy.ai.\n",
|
|
1135
1149
|
creator: {
|
|
1136
1150
|
loginBrowserOnly: "`--creator` only works with the browser login. Run `tostudy login --creator` without --code, --magic-link or --manual.",
|
|
@@ -3150,7 +3164,7 @@ var init_guards = __esm({
|
|
|
3150
3164
|
});
|
|
3151
3165
|
|
|
3152
3166
|
// src/cli.ts
|
|
3153
|
-
import { Command as
|
|
3167
|
+
import { Command as Command39 } from "commander";
|
|
3154
3168
|
|
|
3155
3169
|
// src/commands/login.ts
|
|
3156
3170
|
init_dist();
|
|
@@ -3796,7 +3810,7 @@ init_workspace_state();
|
|
|
3796
3810
|
init_formatter();
|
|
3797
3811
|
|
|
3798
3812
|
// src/version.ts
|
|
3799
|
-
var CLI_VERSION = true ? "0.18.
|
|
3813
|
+
var CLI_VERSION = true ? "0.18.5" : "0.7.1";
|
|
3800
3814
|
|
|
3801
3815
|
// src/update-checker.ts
|
|
3802
3816
|
init_config_dir();
|
|
@@ -4292,10 +4306,295 @@ var coursesCommand = new Command5("courses").description("List your enrolled cou
|
|
|
4292
4306
|
}
|
|
4293
4307
|
});
|
|
4294
4308
|
|
|
4295
|
-
// src/commands/
|
|
4309
|
+
// src/commands/suggest.ts
|
|
4296
4310
|
import { Command as Command6 } from "commander";
|
|
4297
4311
|
init_guards();
|
|
4298
4312
|
init_errors();
|
|
4313
|
+
|
|
4314
|
+
// src/learner-brief/api.ts
|
|
4315
|
+
async function fetchLearnerBrief(input2) {
|
|
4316
|
+
const response = await cliApiFetch(
|
|
4317
|
+
`${input2.apiUrl}/api/cli/student/learner-brief`,
|
|
4318
|
+
input2.token
|
|
4319
|
+
);
|
|
4320
|
+
return response.brief;
|
|
4321
|
+
}
|
|
4322
|
+
async function upsertLearnerBrief(input2) {
|
|
4323
|
+
const response = await cliApiFetch(
|
|
4324
|
+
`${input2.apiUrl}/api/cli/student/learner-brief`,
|
|
4325
|
+
input2.token,
|
|
4326
|
+
{
|
|
4327
|
+
method: "POST",
|
|
4328
|
+
body: JSON.stringify({ text: input2.text, source: input2.source })
|
|
4329
|
+
}
|
|
4330
|
+
);
|
|
4331
|
+
return response.brief;
|
|
4332
|
+
}
|
|
4333
|
+
|
|
4334
|
+
// src/commands/suggest.ts
|
|
4335
|
+
init_user_profile();
|
|
4336
|
+
init_formatter();
|
|
4337
|
+
|
|
4338
|
+
// src/commands/suggest-score.ts
|
|
4339
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
4340
|
+
"de",
|
|
4341
|
+
"da",
|
|
4342
|
+
"do",
|
|
4343
|
+
"das",
|
|
4344
|
+
"dos",
|
|
4345
|
+
"para",
|
|
4346
|
+
"com",
|
|
4347
|
+
"uma",
|
|
4348
|
+
"um",
|
|
4349
|
+
"the",
|
|
4350
|
+
"and",
|
|
4351
|
+
"for",
|
|
4352
|
+
"you",
|
|
4353
|
+
"seu",
|
|
4354
|
+
"sua",
|
|
4355
|
+
"curso",
|
|
4356
|
+
"course"
|
|
4357
|
+
]);
|
|
4358
|
+
var TOKEN_SPLIT = /[^\p{L}\p{N}]+/u;
|
|
4359
|
+
var MAX_RESULTS = 3;
|
|
4360
|
+
var MAX_REASON_TOKENS = 3;
|
|
4361
|
+
var LEVEL_BONUS = 2;
|
|
4362
|
+
function tokensOf(value, dropStops) {
|
|
4363
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4364
|
+
const tokens = [];
|
|
4365
|
+
for (const raw2 of value.toLowerCase().split(TOKEN_SPLIT)) {
|
|
4366
|
+
if (raw2.length < 3 || seen.has(raw2)) continue;
|
|
4367
|
+
if (dropStops && STOP_WORDS.has(raw2)) continue;
|
|
4368
|
+
seen.add(raw2);
|
|
4369
|
+
tokens.push(raw2);
|
|
4370
|
+
}
|
|
4371
|
+
return tokens;
|
|
4372
|
+
}
|
|
4373
|
+
function tokenSet(parts) {
|
|
4374
|
+
return new Set(tokensOf(parts.join(" "), false));
|
|
4375
|
+
}
|
|
4376
|
+
function haystack(course) {
|
|
4377
|
+
return tokenSet([
|
|
4378
|
+
course.title,
|
|
4379
|
+
course.tags.join(" "),
|
|
4380
|
+
course.description,
|
|
4381
|
+
course.level,
|
|
4382
|
+
course.category
|
|
4383
|
+
]);
|
|
4384
|
+
}
|
|
4385
|
+
function haystackWithoutLevel(course) {
|
|
4386
|
+
return tokenSet([course.title, course.tags.join(" "), course.description, course.category]);
|
|
4387
|
+
}
|
|
4388
|
+
function rankSuggestions(courses, query) {
|
|
4389
|
+
const text2 = query.text.trim();
|
|
4390
|
+
const level = query.level;
|
|
4391
|
+
if (text2 === "" && level === null) {
|
|
4392
|
+
return courses.slice(0, MAX_RESULTS).map((course) => ({
|
|
4393
|
+
course,
|
|
4394
|
+
reason: "empty-profile"
|
|
4395
|
+
}));
|
|
4396
|
+
}
|
|
4397
|
+
const textTokens = tokensOf(text2, true);
|
|
4398
|
+
const levelTokens = level === null ? [] : tokensOf(level, false);
|
|
4399
|
+
const scored = courses.map((course) => {
|
|
4400
|
+
const stack = haystack(course);
|
|
4401
|
+
const outsideLevel = haystackWithoutLevel(course);
|
|
4402
|
+
const hits = [];
|
|
4403
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4404
|
+
for (const token of textTokens) {
|
|
4405
|
+
if (seen.has(token) || !stack.has(token)) continue;
|
|
4406
|
+
seen.add(token);
|
|
4407
|
+
hits.push(token);
|
|
4408
|
+
}
|
|
4409
|
+
for (const token of levelTokens) {
|
|
4410
|
+
if (seen.has(token) || !outsideLevel.has(token)) continue;
|
|
4411
|
+
seen.add(token);
|
|
4412
|
+
hits.push(token);
|
|
4413
|
+
}
|
|
4414
|
+
const levelBonus = level !== null && course.level.toLowerCase() === level.trim().toLowerCase() ? LEVEL_BONUS : 0;
|
|
4415
|
+
return {
|
|
4416
|
+
course,
|
|
4417
|
+
score: hits.length + levelBonus,
|
|
4418
|
+
reason: hits.slice(0, MAX_REASON_TOKENS).join(",") || "no-overlap"
|
|
4419
|
+
};
|
|
4420
|
+
});
|
|
4421
|
+
scored.sort((left, right) => {
|
|
4422
|
+
if (right.score !== left.score) return right.score - left.score;
|
|
4423
|
+
return left.course.title.localeCompare(right.course.title);
|
|
4424
|
+
});
|
|
4425
|
+
return scored.slice(0, MAX_RESULTS).map(({ course, reason }) => ({ course, reason }));
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
// src/commands/suggest-workspace.ts
|
|
4429
|
+
import fs11 from "node:fs";
|
|
4430
|
+
import path13 from "node:path";
|
|
4431
|
+
var DOC_LINE_LIMIT = 40;
|
|
4432
|
+
var MIN_TOKEN_LENGTH = 3;
|
|
4433
|
+
var FENCE_LINE = /^[ \t]{0,3}```/;
|
|
4434
|
+
function readWorkspaceSignals(cwd) {
|
|
4435
|
+
const packagePath = path13.join(cwd, "package.json");
|
|
4436
|
+
const readmePath = path13.join(cwd, "README.md");
|
|
4437
|
+
const agentsPath = path13.join(cwd, "AGENTS.md");
|
|
4438
|
+
const hasPackage = fs11.existsSync(packagePath);
|
|
4439
|
+
const hasReadme = fs11.existsSync(readmePath);
|
|
4440
|
+
const hasAgents = fs11.existsSync(agentsPath);
|
|
4441
|
+
if (!hasPackage && !hasReadme && !hasAgents) {
|
|
4442
|
+
return { tokens: [], found: false };
|
|
4443
|
+
}
|
|
4444
|
+
const tokens = [];
|
|
4445
|
+
if (hasPackage) tokens.push(...readPackageTokens(packagePath));
|
|
4446
|
+
const docPath = hasReadme ? readmePath : hasAgents ? agentsPath : null;
|
|
4447
|
+
if (docPath) tokens.push(...readDocTokens(docPath));
|
|
4448
|
+
return { tokens: dedupe(tokens), found: true };
|
|
4449
|
+
}
|
|
4450
|
+
function readPackageTokens(filePath) {
|
|
4451
|
+
const raw2 = readUtf8(filePath);
|
|
4452
|
+
if (raw2 === null) return [];
|
|
4453
|
+
let parsed;
|
|
4454
|
+
try {
|
|
4455
|
+
parsed = JSON.parse(raw2);
|
|
4456
|
+
} catch {
|
|
4457
|
+
return [];
|
|
4458
|
+
}
|
|
4459
|
+
if (!isRecord(parsed)) return [];
|
|
4460
|
+
const names = [
|
|
4461
|
+
...dependencyNames(parsed.dependencies),
|
|
4462
|
+
...dependencyNames(parsed.devDependencies)
|
|
4463
|
+
];
|
|
4464
|
+
return tokenize(names.join("\n"));
|
|
4465
|
+
}
|
|
4466
|
+
function readDocTokens(filePath) {
|
|
4467
|
+
const raw2 = readUtf8(filePath);
|
|
4468
|
+
if (raw2 === null) return [];
|
|
4469
|
+
const lines = raw2.split(/\r?\n/).slice(0, DOC_LINE_LIMIT);
|
|
4470
|
+
return tokenize(stripFencedBlocks(lines));
|
|
4471
|
+
}
|
|
4472
|
+
function stripFencedBlocks(lines) {
|
|
4473
|
+
const kept = [];
|
|
4474
|
+
let inFence = false;
|
|
4475
|
+
for (const line of lines) {
|
|
4476
|
+
if (FENCE_LINE.test(line)) {
|
|
4477
|
+
inFence = !inFence;
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
if (!inFence) kept.push(line);
|
|
4481
|
+
}
|
|
4482
|
+
return kept.join("\n");
|
|
4483
|
+
}
|
|
4484
|
+
function dependencyNames(value) {
|
|
4485
|
+
if (!isRecord(value)) return [];
|
|
4486
|
+
return Object.keys(value);
|
|
4487
|
+
}
|
|
4488
|
+
function isRecord(value) {
|
|
4489
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4490
|
+
}
|
|
4491
|
+
function tokenize(text2) {
|
|
4492
|
+
return text2.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= MIN_TOKEN_LENGTH);
|
|
4493
|
+
}
|
|
4494
|
+
function dedupe(tokens) {
|
|
4495
|
+
return [...new Set(tokens)];
|
|
4496
|
+
}
|
|
4497
|
+
function readUtf8(filePath) {
|
|
4498
|
+
try {
|
|
4499
|
+
return fs11.readFileSync(filePath, "utf8");
|
|
4500
|
+
} catch {
|
|
4501
|
+
return null;
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
// src/commands/suggest.ts
|
|
4506
|
+
var defaultSuggestDeps = {
|
|
4507
|
+
requireSession,
|
|
4508
|
+
fetchCandidates: async () => {
|
|
4509
|
+
const session = await requireSession();
|
|
4510
|
+
return cliApiFetch(`${session.apiUrl}/api/cli/courses/suggest`, session.token, {
|
|
4511
|
+
method: "GET"
|
|
4512
|
+
});
|
|
4513
|
+
},
|
|
4514
|
+
readBrief: async () => {
|
|
4515
|
+
const session = await requireSession();
|
|
4516
|
+
return fetchLearnerBrief({ apiUrl: session.apiUrl, token: session.token });
|
|
4517
|
+
},
|
|
4518
|
+
readProfile: () => getUserProfile(),
|
|
4519
|
+
readWorkspace: () => readWorkspaceSignals(process.cwd())
|
|
4520
|
+
};
|
|
4521
|
+
function suggestionQuery(brief, profile, workspaceTokens) {
|
|
4522
|
+
const parts = [];
|
|
4523
|
+
const briefText = brief?.text.trim() ?? "";
|
|
4524
|
+
if (briefText !== "") parts.push(briefText);
|
|
4525
|
+
if (profile) {
|
|
4526
|
+
parts.push(
|
|
4527
|
+
profile.goal,
|
|
4528
|
+
profile.segment,
|
|
4529
|
+
profile.productsOrServices,
|
|
4530
|
+
profile.team,
|
|
4531
|
+
profile.learnerLevel
|
|
4532
|
+
);
|
|
4533
|
+
}
|
|
4534
|
+
const workspaceText = workspaceTokens.join(" ");
|
|
4535
|
+
if (workspaceText !== "") parts.push(workspaceText);
|
|
4536
|
+
return {
|
|
4537
|
+
text: parts.map((part) => part.trim()).filter((part) => part !== "").join(" "),
|
|
4538
|
+
level: profile?.learnerLevel ?? null
|
|
4539
|
+
};
|
|
4540
|
+
}
|
|
4541
|
+
function reasonText(reason, copy) {
|
|
4542
|
+
if (reason === "empty-profile") return copy.suggestEmptyProfile;
|
|
4543
|
+
if (reason === "no-overlap") return copy.suggestNoOverlap;
|
|
4544
|
+
return reason;
|
|
4545
|
+
}
|
|
4546
|
+
function formatBlock(ranked, lineFor) {
|
|
4547
|
+
const copy = getErrors();
|
|
4548
|
+
const lines = [];
|
|
4549
|
+
for (const [index, row] of ranked.entries()) {
|
|
4550
|
+
lines.push(`${index + 1}. ${row.course.title}`);
|
|
4551
|
+
lines.push(`${copy.suggestReason} ${reasonText(row.reason, copy)}`);
|
|
4552
|
+
lines.push(lineFor(row));
|
|
4553
|
+
}
|
|
4554
|
+
return lines;
|
|
4555
|
+
}
|
|
4556
|
+
function formatSuggestResult(free, paid, workspaceFound, enrollUrlFor) {
|
|
4557
|
+
const copy = getErrors();
|
|
4558
|
+
if (free.length === 0 && paid.length === 0) return copy.suggestEmpty;
|
|
4559
|
+
const lines = [];
|
|
4560
|
+
lines.push(...formatBlock(free, (row) => `tostudy enroll ${row.course.id}`));
|
|
4561
|
+
if (paid.length > 0) {
|
|
4562
|
+
if (lines.length > 0) lines.push("");
|
|
4563
|
+
lines.push(copy.suggestPaidHeader);
|
|
4564
|
+
lines.push(...formatBlock(paid, (row) => enrollUrlFor(row.course.id)));
|
|
4565
|
+
}
|
|
4566
|
+
if (!workspaceFound) lines.push(copy.suggestNoWorkspace);
|
|
4567
|
+
return lines.join("\n");
|
|
4568
|
+
}
|
|
4569
|
+
async function runSuggest(deps = defaultSuggestDeps) {
|
|
4570
|
+
const session = await deps.requireSession();
|
|
4571
|
+
const [candidates, brief, profile] = await Promise.all([
|
|
4572
|
+
deps.fetchCandidates(),
|
|
4573
|
+
deps.readBrief(),
|
|
4574
|
+
deps.readProfile()
|
|
4575
|
+
]);
|
|
4576
|
+
const workspace = deps.readWorkspace();
|
|
4577
|
+
const query = suggestionQuery(brief, profile, workspace.tokens);
|
|
4578
|
+
const origin = session.apiUrl.replace(/\/$/, "");
|
|
4579
|
+
return formatSuggestResult(
|
|
4580
|
+
rankSuggestions(candidates.free, query),
|
|
4581
|
+
rankSuggestions(candidates.paid, query),
|
|
4582
|
+
workspace.found,
|
|
4583
|
+
(courseId) => `${origin}/enroll/${courseId}`
|
|
4584
|
+
);
|
|
4585
|
+
}
|
|
4586
|
+
var suggestCommand = new Command6("suggest").description(getErrors().suggestCommandDescription).action(async () => {
|
|
4587
|
+
try {
|
|
4588
|
+
output(await runSuggest(), { json: false });
|
|
4589
|
+
} catch (err) {
|
|
4590
|
+
error(err instanceof Error ? err.message : String(err));
|
|
4591
|
+
}
|
|
4592
|
+
});
|
|
4593
|
+
|
|
4594
|
+
// src/commands/enroll.ts
|
|
4595
|
+
import { Command as Command7 } from "commander";
|
|
4596
|
+
init_guards();
|
|
4597
|
+
init_errors();
|
|
4299
4598
|
init_formatter();
|
|
4300
4599
|
var COURSE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4301
4600
|
function formatEnrollResult(result) {
|
|
@@ -4328,7 +4627,7 @@ async function runEnroll(courseId, deps = {
|
|
|
4328
4627
|
});
|
|
4329
4628
|
return formatEnrollResult(result);
|
|
4330
4629
|
}
|
|
4331
|
-
var enrollCommand = new
|
|
4630
|
+
var enrollCommand = new Command7("enroll").description(getErrors().enrollCommandDescription).argument("<courseId>", "Published course ID (UUID)").action(async (courseId) => {
|
|
4332
4631
|
try {
|
|
4333
4632
|
output(await runEnroll(courseId), { json: false });
|
|
4334
4633
|
} catch (err) {
|
|
@@ -4338,7 +4637,7 @@ var enrollCommand = new Command6("enroll").description(getErrors().enrollCommand
|
|
|
4338
4637
|
|
|
4339
4638
|
// src/commands/select.ts
|
|
4340
4639
|
init_dist();
|
|
4341
|
-
import { Command as
|
|
4640
|
+
import { Command as Command8 } from "commander";
|
|
4342
4641
|
import os7 from "node:os";
|
|
4343
4642
|
init_guards();
|
|
4344
4643
|
init_workspace_marker();
|
|
@@ -4371,26 +4670,6 @@ init_dist();
|
|
|
4371
4670
|
init_workspace_marker();
|
|
4372
4671
|
init_workspace_state();
|
|
4373
4672
|
|
|
4374
|
-
// src/learner-brief/api.ts
|
|
4375
|
-
async function fetchLearnerBrief(input2) {
|
|
4376
|
-
const response = await cliApiFetch(
|
|
4377
|
-
`${input2.apiUrl}/api/cli/student/learner-brief`,
|
|
4378
|
-
input2.token
|
|
4379
|
-
);
|
|
4380
|
-
return response.brief;
|
|
4381
|
-
}
|
|
4382
|
-
async function upsertLearnerBrief(input2) {
|
|
4383
|
-
const response = await cliApiFetch(
|
|
4384
|
-
`${input2.apiUrl}/api/cli/student/learner-brief`,
|
|
4385
|
-
input2.token,
|
|
4386
|
-
{
|
|
4387
|
-
method: "POST",
|
|
4388
|
-
body: JSON.stringify({ text: input2.text, source: input2.source })
|
|
4389
|
-
}
|
|
4390
|
-
);
|
|
4391
|
-
return response.brief;
|
|
4392
|
-
}
|
|
4393
|
-
|
|
4394
4673
|
// src/learner-brief/bootstrap.ts
|
|
4395
4674
|
import readline from "node:readline/promises";
|
|
4396
4675
|
import { stdin as processStdin, stdout as processStdout } from "node:process";
|
|
@@ -4782,7 +5061,7 @@ function isInteractiveTerminal(json2) {
|
|
|
4782
5061
|
|
|
4783
5062
|
// src/commands/select.ts
|
|
4784
5063
|
var logger8 = createLogger("cli:select");
|
|
4785
|
-
var selectCommand = new
|
|
5064
|
+
var selectCommand = new Command8("select").description("Activate a course by ID or list index number").argument("<course>", "Course ID (UUID) or index number from `tostudy courses`").option("--json", "Output structured JSON").option(
|
|
4786
5065
|
"--root-agents",
|
|
4787
5066
|
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
4788
5067
|
).action(async (course, opts) => {
|
|
@@ -4910,13 +5189,13 @@ var selectCommand = new Command7("select").description("Activate a course by ID
|
|
|
4910
5189
|
|
|
4911
5190
|
// src/commands/progress.ts
|
|
4912
5191
|
init_dist();
|
|
4913
|
-
import { Command as
|
|
5192
|
+
import { Command as Command9 } from "commander";
|
|
4914
5193
|
init_guards();
|
|
4915
5194
|
init_course_state();
|
|
4916
5195
|
init_formatter();
|
|
4917
5196
|
init_errors();
|
|
4918
5197
|
var logger9 = createLogger("cli:progress");
|
|
4919
|
-
var progressCommand = new
|
|
5198
|
+
var progressCommand = new Command9("progress").description("Show your progress in the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
4920
5199
|
try {
|
|
4921
5200
|
const session = await requireSession();
|
|
4922
5201
|
const activeCourse = await requireActiveCourse();
|
|
@@ -4942,7 +5221,7 @@ var progressCommand = new Command8("progress").description("Show your progress i
|
|
|
4942
5221
|
|
|
4943
5222
|
// src/commands/start.ts
|
|
4944
5223
|
init_dist();
|
|
4945
|
-
import { Command as
|
|
5224
|
+
import { Command as Command10 } from "commander";
|
|
4946
5225
|
|
|
4947
5226
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/lessons/next-lesson.ts
|
|
4948
5227
|
async function nextLesson(input2, deps) {
|
|
@@ -5051,20 +5330,20 @@ async function runStart(opts, deps = defaultDeps3) {
|
|
|
5051
5330
|
deps.error(msg);
|
|
5052
5331
|
}
|
|
5053
5332
|
}
|
|
5054
|
-
var startCommand = new
|
|
5333
|
+
var startCommand = new Command10("start").description("Start (or resume) the current module of the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
5055
5334
|
await runStart(opts);
|
|
5056
5335
|
});
|
|
5057
5336
|
|
|
5058
5337
|
// src/commands/start-next.ts
|
|
5059
5338
|
init_dist();
|
|
5060
|
-
import { Command as
|
|
5339
|
+
import { Command as Command11 } from "commander";
|
|
5061
5340
|
init_guards();
|
|
5062
5341
|
init_course_state();
|
|
5063
5342
|
init_workspace_state();
|
|
5064
5343
|
init_formatter();
|
|
5065
5344
|
init_errors();
|
|
5066
5345
|
var logger11 = createLogger("cli:start-next");
|
|
5067
|
-
var startNextCommand = new
|
|
5346
|
+
var startNextCommand = new Command11("start-next").description("Transition to the next module after completing the current one").option("--json", "Output structured JSON").action(async (opts) => {
|
|
5068
5347
|
try {
|
|
5069
5348
|
const session = await requireSession();
|
|
5070
5349
|
const activeCourse = await requireActiveCourse();
|
|
@@ -5107,14 +5386,14 @@ var startNextCommand = new Command10("start-next").description("Transition to th
|
|
|
5107
5386
|
|
|
5108
5387
|
// src/commands/next.ts
|
|
5109
5388
|
init_dist();
|
|
5110
|
-
import { Command as
|
|
5389
|
+
import { Command as Command12 } from "commander";
|
|
5111
5390
|
init_guards();
|
|
5112
5391
|
init_course_state();
|
|
5113
5392
|
init_workspace_state();
|
|
5114
5393
|
init_formatter();
|
|
5115
5394
|
init_errors();
|
|
5116
5395
|
var logger12 = createLogger("cli:next");
|
|
5117
|
-
var nextCommand = new
|
|
5396
|
+
var nextCommand = new Command12("next").description("Advance to the next lesson in the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
5118
5397
|
try {
|
|
5119
5398
|
const session = await requireSession();
|
|
5120
5399
|
const activeCourse = await requireActiveCourse();
|
|
@@ -5192,14 +5471,14 @@ var nextCommand = new Command11("next").description("Advance to the next lesson
|
|
|
5192
5471
|
|
|
5193
5472
|
// src/commands/lesson.ts
|
|
5194
5473
|
init_dist();
|
|
5195
|
-
import { Command as
|
|
5474
|
+
import { Command as Command13 } from "commander";
|
|
5196
5475
|
init_guards();
|
|
5197
5476
|
init_course_state();
|
|
5198
5477
|
init_formatter();
|
|
5199
5478
|
init_resolve();
|
|
5200
5479
|
init_workspace_tokens();
|
|
5201
5480
|
init_errors();
|
|
5202
|
-
import
|
|
5481
|
+
import path14 from "node:path";
|
|
5203
5482
|
var logger13 = createLogger("cli:lesson");
|
|
5204
5483
|
function adjustTimeEstimate(type, baseMinutes) {
|
|
5205
5484
|
if (type === "exercise") return Math.max(baseMinutes, 30);
|
|
@@ -5216,7 +5495,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
|
|
|
5216
5495
|
const isPractical = isPracticalType(data.type) || isCheckpoint(data.type, data.title);
|
|
5217
5496
|
const lines = [`\u2501\u2501\u2501 Li\xE7\xE3o: ${data.title} \u2501\u2501\u2501`];
|
|
5218
5497
|
if (isPractical && workspacePath) {
|
|
5219
|
-
const base =
|
|
5498
|
+
const base = path14.basename(workspacePath) === ".tostudy" ? path14.dirname(workspacePath) : workspacePath;
|
|
5220
5499
|
const slug = courseSlug(courseTitle);
|
|
5221
5500
|
const vaultPath = resolveVaultPath(workspacePath, slug);
|
|
5222
5501
|
lines.push(
|
|
@@ -5250,7 +5529,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
|
|
|
5250
5529
|
}
|
|
5251
5530
|
return lines.join("\n");
|
|
5252
5531
|
}
|
|
5253
|
-
var lessonCommand = new
|
|
5532
|
+
var lessonCommand = new Command13("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").option(
|
|
5254
5533
|
"--read <lessonId>",
|
|
5255
5534
|
"Read a specific lesson by id (works on archived / read-only courses)"
|
|
5256
5535
|
).action(async (opts) => {
|
|
@@ -5321,7 +5600,7 @@ var lessonCommand = new Command12("lesson").description("Show the content of the
|
|
|
5321
5600
|
|
|
5322
5601
|
// src/commands/knowledge.ts
|
|
5323
5602
|
init_dist();
|
|
5324
|
-
import { Command as
|
|
5603
|
+
import { Command as Command14 } from "commander";
|
|
5325
5604
|
init_guards();
|
|
5326
5605
|
init_formatter();
|
|
5327
5606
|
var logger14 = createLogger("cli:knowledge");
|
|
@@ -5341,7 +5620,7 @@ function formatKnowledge(data) {
|
|
|
5341
5620
|
});
|
|
5342
5621
|
return lines.join("\n").trimEnd();
|
|
5343
5622
|
}
|
|
5344
|
-
var knowledgeCommand = new
|
|
5623
|
+
var knowledgeCommand = new Command14("knowledge").description("Show the active course's knowledge base (base de conhecimento)").option("--json", "Output structured JSON").action(async (opts) => {
|
|
5345
5624
|
try {
|
|
5346
5625
|
const activeCourse = await requireActiveCourse();
|
|
5347
5626
|
if (!activeCourse.courseId) {
|
|
@@ -5372,13 +5651,13 @@ var knowledgeCommand = new Command13("knowledge").description("Show the active c
|
|
|
5372
5651
|
|
|
5373
5652
|
// src/commands/hint.ts
|
|
5374
5653
|
init_dist();
|
|
5375
|
-
import { Command as
|
|
5654
|
+
import { Command as Command15 } from "commander";
|
|
5376
5655
|
init_guards();
|
|
5377
5656
|
init_course_state();
|
|
5378
5657
|
init_formatter();
|
|
5379
5658
|
init_errors();
|
|
5380
5659
|
var logger15 = createLogger("cli:hint");
|
|
5381
|
-
var hintCommand = new
|
|
5660
|
+
var hintCommand = new Command15("hint").description("Get a progressive hint for the current exercise").option("--json", "Output structured JSON").action(async (opts) => {
|
|
5382
5661
|
try {
|
|
5383
5662
|
const session = await requireSession();
|
|
5384
5663
|
const activeCourse = await requireActiveCourse();
|
|
@@ -5427,9 +5706,9 @@ var hintCommand = new Command14("hint").description("Get a progressive hint for
|
|
|
5427
5706
|
|
|
5428
5707
|
// src/commands/validate.ts
|
|
5429
5708
|
init_dist();
|
|
5430
|
-
import
|
|
5431
|
-
import
|
|
5432
|
-
import { Command as
|
|
5709
|
+
import fs12 from "node:fs";
|
|
5710
|
+
import path15 from "node:path";
|
|
5711
|
+
import { Command as Command16 } from "commander";
|
|
5433
5712
|
|
|
5434
5713
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/exercises/validate-solution.ts
|
|
5435
5714
|
async function validateSolution(input2, deps) {
|
|
@@ -6218,10 +6497,10 @@ function mergeDefs(...defs) {
|
|
|
6218
6497
|
function cloneDef(schema) {
|
|
6219
6498
|
return mergeDefs(schema._zod.def);
|
|
6220
6499
|
}
|
|
6221
|
-
function getElementAtPath(obj,
|
|
6222
|
-
if (!
|
|
6500
|
+
function getElementAtPath(obj, path29) {
|
|
6501
|
+
if (!path29)
|
|
6223
6502
|
return obj;
|
|
6224
|
-
return
|
|
6503
|
+
return path29.reduce((acc, key) => acc?.[key], obj);
|
|
6225
6504
|
}
|
|
6226
6505
|
function promiseAllObject(promisesObj) {
|
|
6227
6506
|
const keys = Object.keys(promisesObj);
|
|
@@ -6604,11 +6883,11 @@ function aborted(x, startIndex = 0) {
|
|
|
6604
6883
|
}
|
|
6605
6884
|
return false;
|
|
6606
6885
|
}
|
|
6607
|
-
function prefixIssues(
|
|
6886
|
+
function prefixIssues(path29, issues) {
|
|
6608
6887
|
return issues.map((iss) => {
|
|
6609
6888
|
var _a2;
|
|
6610
6889
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
6611
|
-
iss.path.unshift(
|
|
6890
|
+
iss.path.unshift(path29);
|
|
6612
6891
|
return iss;
|
|
6613
6892
|
});
|
|
6614
6893
|
}
|
|
@@ -6791,7 +7070,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
|
|
|
6791
7070
|
}
|
|
6792
7071
|
function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
6793
7072
|
const result = { errors: [] };
|
|
6794
|
-
const processError = (error50,
|
|
7073
|
+
const processError = (error50, path29 = []) => {
|
|
6795
7074
|
var _a2, _b;
|
|
6796
7075
|
for (const issue2 of error50.issues) {
|
|
6797
7076
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -6801,7 +7080,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
6801
7080
|
} else if (issue2.code === "invalid_element") {
|
|
6802
7081
|
processError({ issues: issue2.issues }, issue2.path);
|
|
6803
7082
|
} else {
|
|
6804
|
-
const fullpath = [...
|
|
7083
|
+
const fullpath = [...path29, ...issue2.path];
|
|
6805
7084
|
if (fullpath.length === 0) {
|
|
6806
7085
|
result.errors.push(mapper(issue2));
|
|
6807
7086
|
continue;
|
|
@@ -6833,8 +7112,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
|
|
|
6833
7112
|
}
|
|
6834
7113
|
function toDotPath(_path) {
|
|
6835
7114
|
const segs = [];
|
|
6836
|
-
const
|
|
6837
|
-
for (const seg of
|
|
7115
|
+
const path29 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
7116
|
+
for (const seg of path29) {
|
|
6838
7117
|
if (typeof seg === "number")
|
|
6839
7118
|
segs.push(`[${seg}]`);
|
|
6840
7119
|
else if (typeof seg === "symbol")
|
|
@@ -18811,13 +19090,13 @@ function resolveRef(ref, ctx) {
|
|
|
18811
19090
|
if (!ref.startsWith("#")) {
|
|
18812
19091
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
18813
19092
|
}
|
|
18814
|
-
const
|
|
18815
|
-
if (
|
|
19093
|
+
const path29 = ref.slice(1).split("/").filter(Boolean);
|
|
19094
|
+
if (path29.length === 0) {
|
|
18816
19095
|
return ctx.rootSchema;
|
|
18817
19096
|
}
|
|
18818
19097
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
18819
|
-
if (
|
|
18820
|
-
const key =
|
|
19098
|
+
if (path29[0] === defsKey) {
|
|
19099
|
+
const key = path29[1];
|
|
18821
19100
|
if (!key || !ctx.defs[key]) {
|
|
18822
19101
|
throw new Error(`Reference not found: ${ref}`);
|
|
18823
19102
|
}
|
|
@@ -19615,7 +19894,7 @@ async function clearRetryPin() {
|
|
|
19615
19894
|
await updateWorkspaceState(ws.workspacePath, { retryLessonId: void 0 });
|
|
19616
19895
|
}
|
|
19617
19896
|
}
|
|
19618
|
-
var validateCommand = new
|
|
19897
|
+
var validateCommand = new Command16("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) => {
|
|
19619
19898
|
try {
|
|
19620
19899
|
const session = await requireSession();
|
|
19621
19900
|
const activeCourse = await requireActiveCourse();
|
|
@@ -19644,21 +19923,21 @@ var validateCommand = new Command15("validate").description("Validate your solut
|
|
|
19644
19923
|
}
|
|
19645
19924
|
let solution;
|
|
19646
19925
|
if (opts.stdin) {
|
|
19647
|
-
solution =
|
|
19926
|
+
solution = fs12.readFileSync("/dev/stdin", "utf-8");
|
|
19648
19927
|
} else if (file2) {
|
|
19649
|
-
if (!
|
|
19928
|
+
if (!fs12.existsSync(file2)) {
|
|
19650
19929
|
if (opts.json)
|
|
19651
19930
|
jsonError("file_not_found", { message: `Arquivo n\xE3o encontrado: ${file2}` });
|
|
19652
19931
|
error(`Arquivo n\xE3o encontrado: ${file2}`);
|
|
19653
19932
|
}
|
|
19654
|
-
solution =
|
|
19933
|
+
solution = fs12.readFileSync(file2, "utf-8");
|
|
19655
19934
|
} else {
|
|
19656
19935
|
if (opts.json)
|
|
19657
19936
|
jsonError("no_solution_provided", { message: "Forne\xE7a um arquivo ou use --stdin" });
|
|
19658
19937
|
error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
|
|
19659
19938
|
}
|
|
19660
19939
|
if (file2 && activeCourse.courseTags?.length) {
|
|
19661
|
-
const ext =
|
|
19940
|
+
const ext = path15.extname(file2).toLowerCase();
|
|
19662
19941
|
const LANG_EXTENSIONS = {
|
|
19663
19942
|
".html": ["html", "html5"],
|
|
19664
19943
|
".css": ["css"],
|
|
@@ -19753,13 +20032,13 @@ var validateCommand = new Command15("validate").description("Validate your solut
|
|
|
19753
20032
|
|
|
19754
20033
|
// src/commands/retry.ts
|
|
19755
20034
|
init_dist();
|
|
19756
|
-
import { Command as
|
|
20035
|
+
import { Command as Command17 } from "commander";
|
|
19757
20036
|
init_guards();
|
|
19758
20037
|
init_workspace_state();
|
|
19759
20038
|
init_formatter();
|
|
19760
20039
|
var logger17 = createLogger("cli:retry");
|
|
19761
20040
|
var LESSON_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19762
|
-
var retryCommand = new
|
|
20041
|
+
var retryCommand = new Command17("retry").description("Revalidate a lesson you already completed (your grade keeps the best attempt)").argument("[lessonId]", "Id of the completed lesson (see: tostudy grades --course <slug> --json)").option("--clear", "Cancel the pending retry; validate goes back to the current lesson").option("--json", "Output structured JSON").action(async (lessonId, opts) => {
|
|
19763
20042
|
try {
|
|
19764
20043
|
if (opts.clear) {
|
|
19765
20044
|
const ws2 = await findWorkspaceState();
|
|
@@ -19820,8 +20099,8 @@ var retryCommand = new Command16("retry").description("Revalidate a lesson you a
|
|
|
19820
20099
|
init_session_store();
|
|
19821
20100
|
init_workspace_state();
|
|
19822
20101
|
init_formatter();
|
|
19823
|
-
import { Command as
|
|
19824
|
-
var menuCommand = new
|
|
20102
|
+
import { Command as Command18 } from "commander";
|
|
20103
|
+
var menuCommand = new Command18("menu").description("Show available commands and current study context").action(async () => {
|
|
19825
20104
|
const session = await getSession();
|
|
19826
20105
|
const wsResult = session ? await findWorkspaceState() : null;
|
|
19827
20106
|
const activeCourse = wsResult?.state ?? null;
|
|
@@ -19878,7 +20157,7 @@ var menuCommand = new Command17("menu").description("Show available commands and
|
|
|
19878
20157
|
|
|
19879
20158
|
// src/commands/init.ts
|
|
19880
20159
|
init_dist();
|
|
19881
|
-
import { Command as
|
|
20160
|
+
import { Command as Command19 } from "commander";
|
|
19882
20161
|
init_session_store();
|
|
19883
20162
|
init_course_state();
|
|
19884
20163
|
init_workspace_state();
|
|
@@ -20194,7 +20473,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
|
|
|
20194
20473
|
deps.output(artifacts.learnerBrief, { json: false });
|
|
20195
20474
|
}
|
|
20196
20475
|
}
|
|
20197
|
-
var initCommand = new
|
|
20476
|
+
var initCommand = new Command19("init").description("Generate tutor instructions and learner brief for the active course").option("--segment <segment>", "Learner segment/niche").option("--company <company>", "Company or business type").option("--products <products>", "Main products or services").option("--region <region>", "Operating region").option("--team <team>", "Team involved").option("--goal <goal>", "Primary learning goal").option("--level <level>", "Learner level: beginner, intermediate, advanced").option("--adapt-context", "Adapt examples to learner's real context").option(
|
|
20198
20477
|
"--root-agents",
|
|
20199
20478
|
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
20200
20479
|
).option("--json", "Output structured JSON").action(async (opts) => {
|
|
@@ -20203,18 +20482,18 @@ var initCommand = new Command18("init").description("Generate tutor instructions
|
|
|
20203
20482
|
|
|
20204
20483
|
// src/commands/workspace.ts
|
|
20205
20484
|
init_dist();
|
|
20206
|
-
import { Command as
|
|
20485
|
+
import { Command as Command20 } from "commander";
|
|
20207
20486
|
|
|
20208
20487
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/setup-workspace.ts
|
|
20209
|
-
import
|
|
20210
|
-
import
|
|
20488
|
+
import fs13 from "node:fs/promises";
|
|
20489
|
+
import path16 from "node:path";
|
|
20211
20490
|
var WORKSPACE_DIRS = ["exercises", "generated", "notes", "diagrams", "vault"];
|
|
20212
20491
|
async function setupWorkspace(input2) {
|
|
20213
|
-
const workspacePath =
|
|
20492
|
+
const workspacePath = path16.join(input2.basePath, input2.courseSlug);
|
|
20214
20493
|
for (const dir of WORKSPACE_DIRS) {
|
|
20215
|
-
await
|
|
20494
|
+
await fs13.mkdir(path16.join(workspacePath, dir), { recursive: true });
|
|
20216
20495
|
}
|
|
20217
|
-
const configPath =
|
|
20496
|
+
const configPath = path16.join(workspacePath, ".ana-config.json");
|
|
20218
20497
|
const config2 = {
|
|
20219
20498
|
courseId: input2.courseId,
|
|
20220
20499
|
courseSlug: input2.courseSlug,
|
|
@@ -20224,7 +20503,7 @@ async function setupWorkspace(input2) {
|
|
|
20224
20503
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20225
20504
|
lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20226
20505
|
};
|
|
20227
|
-
await
|
|
20506
|
+
await fs13.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
20228
20507
|
const readme = [
|
|
20229
20508
|
`# ${input2.courseName}`,
|
|
20230
20509
|
"",
|
|
@@ -20249,13 +20528,13 @@ async function setupWorkspace(input2) {
|
|
|
20249
20528
|
"tostudy vault sync # Sincronizar progresso",
|
|
20250
20529
|
"```"
|
|
20251
20530
|
].join("\n");
|
|
20252
|
-
await
|
|
20531
|
+
await fs13.writeFile(path16.join(workspacePath, "README.md"), readme, "utf-8");
|
|
20253
20532
|
return { workspacePath, directories: WORKSPACE_DIRS, configPath };
|
|
20254
20533
|
}
|
|
20255
20534
|
|
|
20256
20535
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/extract-exercise.ts
|
|
20257
|
-
import
|
|
20258
|
-
import
|
|
20536
|
+
import fs14 from "node:fs/promises";
|
|
20537
|
+
import path17 from "node:path";
|
|
20259
20538
|
|
|
20260
20539
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/templates.ts
|
|
20261
20540
|
var VITE_VERSION = "^6.0.0";
|
|
@@ -20367,16 +20646,16 @@ async function extractExercise(input2) {
|
|
|
20367
20646
|
const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
|
|
20368
20647
|
const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
|
|
20369
20648
|
const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
|
|
20370
|
-
const exercisePath =
|
|
20371
|
-
await
|
|
20649
|
+
const exercisePath = path17.join(workspacePath, "exercises", moduleDir, lessonDir);
|
|
20650
|
+
await fs14.mkdir(exercisePath, { recursive: true });
|
|
20372
20651
|
const extractedFiles = [];
|
|
20373
20652
|
let hasStarterCode = false;
|
|
20374
20653
|
if (lessonData.sandpackConfig?.files) {
|
|
20375
20654
|
for (const [filePath, fileData] of Object.entries(lessonData.sandpackConfig.files)) {
|
|
20376
20655
|
const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
20377
|
-
const fullPath =
|
|
20378
|
-
await
|
|
20379
|
-
await
|
|
20656
|
+
const fullPath = path17.join(exercisePath, cleanPath);
|
|
20657
|
+
await fs14.mkdir(path17.dirname(fullPath), { recursive: true });
|
|
20658
|
+
await fs14.writeFile(fullPath, fileData.code, "utf-8");
|
|
20380
20659
|
extractedFiles.push(cleanPath);
|
|
20381
20660
|
hasStarterCode = true;
|
|
20382
20661
|
}
|
|
@@ -20384,13 +20663,13 @@ async function extractExercise(input2) {
|
|
|
20384
20663
|
const tierData = getTierData(lessonData.structuredData, exerciseTier2);
|
|
20385
20664
|
const tierCode = tierData?.code;
|
|
20386
20665
|
if (tierCode) {
|
|
20387
|
-
await
|
|
20666
|
+
await fs14.writeFile(path17.join(exercisePath, "exercise.js"), tierCode, "utf-8");
|
|
20388
20667
|
extractedFiles.push("exercise.js");
|
|
20389
20668
|
hasStarterCode = true;
|
|
20390
20669
|
} else {
|
|
20391
20670
|
const starter = getStarterCode(lessonData.structuredData);
|
|
20392
20671
|
if (starter) {
|
|
20393
|
-
await
|
|
20672
|
+
await fs14.writeFile(path17.join(exercisePath, "exercise.js"), starter, "utf-8");
|
|
20394
20673
|
extractedFiles.push("exercise.js");
|
|
20395
20674
|
hasStarterCode = true;
|
|
20396
20675
|
}
|
|
@@ -20408,8 +20687,8 @@ async function extractExercise(input2) {
|
|
|
20408
20687
|
...exerciseDeps
|
|
20409
20688
|
}
|
|
20410
20689
|
};
|
|
20411
|
-
await
|
|
20412
|
-
|
|
20690
|
+
await fs14.writeFile(
|
|
20691
|
+
path17.join(exercisePath, "package.json"),
|
|
20413
20692
|
JSON.stringify(pkgJson, null, 2),
|
|
20414
20693
|
"utf-8"
|
|
20415
20694
|
);
|
|
@@ -20421,20 +20700,20 @@ async function extractExercise(input2) {
|
|
|
20421
20700
|
);
|
|
20422
20701
|
for (const [configFile, configContent] of Object.entries(scaffold.configs)) {
|
|
20423
20702
|
if (!sandpackFileNames.has(configFile)) {
|
|
20424
|
-
await
|
|
20703
|
+
await fs14.writeFile(path17.join(exercisePath, configFile), configContent, "utf-8");
|
|
20425
20704
|
extractedFiles.push(configFile);
|
|
20426
20705
|
}
|
|
20427
20706
|
}
|
|
20428
20707
|
const setupSh = `#!/bin/sh
|
|
20429
20708
|
${scaffold.setupScript}
|
|
20430
20709
|
`;
|
|
20431
|
-
await
|
|
20710
|
+
await fs14.writeFile(path17.join(exercisePath, "setup.sh"), setupSh, "utf-8");
|
|
20432
20711
|
extractedFiles.push("setup.sh");
|
|
20433
20712
|
}
|
|
20434
20713
|
}
|
|
20435
20714
|
const readme = generateReadme(lessonData, exerciseTier2);
|
|
20436
|
-
const readmePath =
|
|
20437
|
-
await
|
|
20715
|
+
const readmePath = path17.join(exercisePath, "README.md");
|
|
20716
|
+
await fs14.writeFile(readmePath, readme, "utf-8");
|
|
20438
20717
|
extractedFiles.push("README.md");
|
|
20439
20718
|
return {
|
|
20440
20719
|
exercisePath,
|
|
@@ -20490,11 +20769,11 @@ init_guards();
|
|
|
20490
20769
|
init_course_state();
|
|
20491
20770
|
init_resolve();
|
|
20492
20771
|
init_errors();
|
|
20493
|
-
import
|
|
20772
|
+
import path18 from "node:path";
|
|
20494
20773
|
import os8 from "node:os";
|
|
20495
|
-
import
|
|
20774
|
+
import fs15 from "node:fs/promises";
|
|
20496
20775
|
var logger19 = createLogger("cli:workspace");
|
|
20497
|
-
var workspaceCommand = new
|
|
20776
|
+
var workspaceCommand = new Command20("workspace").description(
|
|
20498
20777
|
getErrors().workspaceCommandDescription
|
|
20499
20778
|
);
|
|
20500
20779
|
workspaceCommand.command("setup").description(getErrors().workspaceSetupDescription).option("--path <dir>", "Diret\xF3rio base do workspace (omita para usar a pasta atual)").option("--json", "Output structured JSON").action(async (opts) => {
|
|
@@ -20506,14 +20785,14 @@ workspaceCommand.command("setup").description(getErrors().workspaceSetupDescript
|
|
|
20506
20785
|
let directories;
|
|
20507
20786
|
if (!opts.path && cwdIsWorkspace) {
|
|
20508
20787
|
const resolvedCwd = await resolveCwdWorkspacePath(process.cwd());
|
|
20509
|
-
workspacePath = resolvedCwd ??
|
|
20510
|
-
await
|
|
20788
|
+
workspacePath = resolvedCwd ?? path18.join(process.cwd(), ".tostudy");
|
|
20789
|
+
await fs15.mkdir(workspacePath, { recursive: true });
|
|
20511
20790
|
directories = ["exercises", "generated", "notes", "diagrams"];
|
|
20512
20791
|
for (const dir of directories) {
|
|
20513
|
-
await
|
|
20792
|
+
await fs15.mkdir(path18.join(workspacePath, dir), { recursive: true });
|
|
20514
20793
|
}
|
|
20515
|
-
const configPath =
|
|
20516
|
-
await
|
|
20794
|
+
const configPath = path18.join(workspacePath, ".ana-config.json");
|
|
20795
|
+
await fs15.writeFile(
|
|
20517
20796
|
configPath,
|
|
20518
20797
|
JSON.stringify(
|
|
20519
20798
|
{
|
|
@@ -20531,7 +20810,7 @@ workspaceCommand.command("setup").description(getErrors().workspaceSetupDescript
|
|
|
20531
20810
|
"utf-8"
|
|
20532
20811
|
);
|
|
20533
20812
|
} else {
|
|
20534
|
-
const basePath = opts.path ??
|
|
20813
|
+
const basePath = opts.path ?? path18.join(os8.homedir(), "study");
|
|
20535
20814
|
const result2 = await setupWorkspace({
|
|
20536
20815
|
courseId: activeCourse.courseId,
|
|
20537
20816
|
courseSlug: courseSlug(activeCourse.courseTitle),
|
|
@@ -20565,7 +20844,7 @@ Pr\xF3ximo passo: tostudy export
|
|
|
20565
20844
|
process.exit(1);
|
|
20566
20845
|
}
|
|
20567
20846
|
});
|
|
20568
|
-
workspaceCommand.command("status").description("Mostrar status do workspace do curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
20847
|
+
workspaceCommand.command("status").description("Mostrar status do workspace do curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path18.join(os8.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
20569
20848
|
try {
|
|
20570
20849
|
const activeCourse = await requireActiveCourse();
|
|
20571
20850
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
|
|
@@ -20582,40 +20861,40 @@ workspaceCommand.command("status").description("Mostrar status do workspace do c
|
|
|
20582
20861
|
const workspacePath = ws.workspacePath;
|
|
20583
20862
|
let configData = null;
|
|
20584
20863
|
try {
|
|
20585
|
-
const raw2 = await
|
|
20864
|
+
const raw2 = await fs15.readFile(path18.join(workspacePath, ".ana-config.json"), "utf-8");
|
|
20586
20865
|
configData = JSON.parse(raw2);
|
|
20587
20866
|
} catch {
|
|
20588
20867
|
configData = null;
|
|
20589
20868
|
}
|
|
20590
|
-
const exercisesDir =
|
|
20869
|
+
const exercisesDir = path18.join(workspacePath, "exercises");
|
|
20591
20870
|
let exerciseCount = 0;
|
|
20592
20871
|
try {
|
|
20593
|
-
const moduleDirs = await
|
|
20872
|
+
const moduleDirs = await fs15.readdir(exercisesDir);
|
|
20594
20873
|
for (const modDir of moduleDirs) {
|
|
20595
|
-
const modPath =
|
|
20596
|
-
const stat = await
|
|
20874
|
+
const modPath = path18.join(exercisesDir, modDir);
|
|
20875
|
+
const stat = await fs15.stat(modPath);
|
|
20597
20876
|
if (stat.isDirectory()) {
|
|
20598
|
-
const lessonDirs = await
|
|
20877
|
+
const lessonDirs = await fs15.readdir(modPath);
|
|
20599
20878
|
for (const lessonDir of lessonDirs) {
|
|
20600
|
-
const lessonPath2 =
|
|
20601
|
-
const lstat = await
|
|
20879
|
+
const lessonPath2 = path18.join(modPath, lessonDir);
|
|
20880
|
+
const lstat = await fs15.stat(lessonPath2);
|
|
20602
20881
|
if (lstat.isDirectory()) exerciseCount++;
|
|
20603
20882
|
}
|
|
20604
20883
|
}
|
|
20605
20884
|
}
|
|
20606
20885
|
} catch {
|
|
20607
20886
|
}
|
|
20608
|
-
const generatedDir =
|
|
20887
|
+
const generatedDir = path18.join(workspacePath, "generated");
|
|
20609
20888
|
let artifactCount = 0;
|
|
20610
20889
|
try {
|
|
20611
|
-
const files = await
|
|
20890
|
+
const files = await fs15.readdir(generatedDir);
|
|
20612
20891
|
artifactCount = files.length;
|
|
20613
20892
|
} catch {
|
|
20614
20893
|
}
|
|
20615
|
-
const diagramsDir =
|
|
20894
|
+
const diagramsDir = path18.join(workspacePath, "diagrams");
|
|
20616
20895
|
let diagramCount = 0;
|
|
20617
20896
|
try {
|
|
20618
|
-
const files = await
|
|
20897
|
+
const files = await fs15.readdir(diagramsDir);
|
|
20619
20898
|
diagramCount = files.length;
|
|
20620
20899
|
} catch {
|
|
20621
20900
|
}
|
|
@@ -20662,16 +20941,16 @@ workspaceCommand.command("status").description("Mostrar status do workspace do c
|
|
|
20662
20941
|
|
|
20663
20942
|
// src/commands/export.ts
|
|
20664
20943
|
init_dist();
|
|
20665
|
-
import { Command as
|
|
20944
|
+
import { Command as Command21 } from "commander";
|
|
20666
20945
|
init_guards();
|
|
20667
20946
|
init_course_state();
|
|
20668
20947
|
init_resolve();
|
|
20669
20948
|
init_errors();
|
|
20670
|
-
import
|
|
20949
|
+
import path19 from "node:path";
|
|
20671
20950
|
import os9 from "node:os";
|
|
20672
|
-
import
|
|
20951
|
+
import fs16 from "node:fs/promises";
|
|
20673
20952
|
var logger20 = createLogger("cli:export");
|
|
20674
|
-
var exportCommand = new
|
|
20953
|
+
var exportCommand = new Command21("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", path19.join(os9.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
20675
20954
|
try {
|
|
20676
20955
|
const session = await requireSession();
|
|
20677
20956
|
const activeCourse = await requireActiveCourse();
|
|
@@ -20689,21 +20968,21 @@ var exportCommand = new Command20("export").description("Extrair exerc\xEDcio at
|
|
|
20689
20968
|
opts.path
|
|
20690
20969
|
);
|
|
20691
20970
|
if (ws.found && ws.source === "cwd" && ws.workspacePath) {
|
|
20692
|
-
const configPath =
|
|
20971
|
+
const configPath = path19.join(ws.workspacePath, ".ana-config.json");
|
|
20693
20972
|
let hasConfig = false;
|
|
20694
20973
|
try {
|
|
20695
|
-
await
|
|
20974
|
+
await fs16.access(configPath);
|
|
20696
20975
|
hasConfig = true;
|
|
20697
20976
|
} catch {
|
|
20698
20977
|
}
|
|
20699
20978
|
if (!hasConfig) {
|
|
20700
20979
|
const slug = courseSlug(activeCourse.courseTitle);
|
|
20701
20980
|
logger20.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
|
|
20702
|
-
await
|
|
20981
|
+
await fs16.mkdir(ws.workspacePath, { recursive: true });
|
|
20703
20982
|
for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
|
|
20704
|
-
await
|
|
20983
|
+
await fs16.mkdir(path19.join(ws.workspacePath, dir), { recursive: true });
|
|
20705
20984
|
}
|
|
20706
|
-
await
|
|
20985
|
+
await fs16.writeFile(
|
|
20707
20986
|
configPath,
|
|
20708
20987
|
JSON.stringify(
|
|
20709
20988
|
{
|
|
@@ -20721,7 +21000,7 @@ var exportCommand = new Command20("export").description("Extrair exerc\xEDcio at
|
|
|
20721
21000
|
"utf-8"
|
|
20722
21001
|
);
|
|
20723
21002
|
await setCourseWorkspacePath(activeCourse.courseId, ws.workspacePath);
|
|
20724
|
-
const isNamespaced = ws.workspacePath ===
|
|
21003
|
+
const isNamespaced = ws.workspacePath === path19.join(process.cwd(), ".tostudy");
|
|
20725
21004
|
process.stderr.write(
|
|
20726
21005
|
isNamespaced ? `\u2728 Workspace inicializado em .tostudy/ (isolado do projeto).
|
|
20727
21006
|
` : `\u2728 Workspace inicializado nesta pasta.
|
|
@@ -20776,12 +21055,12 @@ init_guards();
|
|
|
20776
21055
|
init_course_state();
|
|
20777
21056
|
init_resolve();
|
|
20778
21057
|
init_errors();
|
|
20779
|
-
import { Command as
|
|
21058
|
+
import { Command as Command22 } from "commander";
|
|
20780
21059
|
import { execFile as execFile3 } from "node:child_process";
|
|
20781
|
-
import
|
|
21060
|
+
import path20 from "node:path";
|
|
20782
21061
|
import os10 from "node:os";
|
|
20783
21062
|
var logger21 = createLogger("cli:open");
|
|
20784
|
-
var openCommand = new
|
|
21063
|
+
var openCommand = new Command22("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path20.join(os10.homedir(), "study")).action(async (opts) => {
|
|
20785
21064
|
try {
|
|
20786
21065
|
const activeCourse = await requireActiveCourse();
|
|
20787
21066
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
|
|
@@ -20816,14 +21095,14 @@ var openCommand = new Command21("open").description("Abrir workspace do curso na
|
|
|
20816
21095
|
|
|
20817
21096
|
// src/commands/vault.ts
|
|
20818
21097
|
init_dist();
|
|
20819
|
-
import { Command as
|
|
21098
|
+
import { Command as Command23 } from "commander";
|
|
20820
21099
|
|
|
20821
21100
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/generate-vault-files.ts
|
|
20822
21101
|
init_slug();
|
|
20823
21102
|
|
|
20824
21103
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/write-vault.ts
|
|
20825
|
-
import
|
|
20826
|
-
import
|
|
21104
|
+
import fs17 from "node:fs/promises";
|
|
21105
|
+
import path21 from "node:path";
|
|
20827
21106
|
|
|
20828
21107
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/types.ts
|
|
20829
21108
|
var VAULT_MARKER_FILENAME = ".ana-vault.json";
|
|
@@ -20832,20 +21111,20 @@ var VAULT_MARKER_VERSION = 1;
|
|
|
20832
21111
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/write-vault.ts
|
|
20833
21112
|
async function writeVaultFiles(files, outputPath, courseId, courseSlug2) {
|
|
20834
21113
|
for (const file2 of files) {
|
|
20835
|
-
const fullPath =
|
|
20836
|
-
await
|
|
20837
|
-
await
|
|
21114
|
+
const fullPath = path21.join(outputPath, file2.relativePath);
|
|
21115
|
+
await fs17.mkdir(path21.dirname(fullPath), { recursive: true });
|
|
21116
|
+
await fs17.writeFile(fullPath, file2.content, "utf-8");
|
|
20838
21117
|
}
|
|
20839
|
-
const vaultPath =
|
|
21118
|
+
const vaultPath = path21.join(outputPath, courseSlug2);
|
|
20840
21119
|
const marker = {
|
|
20841
21120
|
courseId,
|
|
20842
21121
|
courseSlug: courseSlug2,
|
|
20843
21122
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20844
21123
|
version: VAULT_MARKER_VERSION
|
|
20845
21124
|
};
|
|
20846
|
-
await
|
|
20847
|
-
await
|
|
20848
|
-
|
|
21125
|
+
await fs17.mkdir(vaultPath, { recursive: true });
|
|
21126
|
+
await fs17.writeFile(
|
|
21127
|
+
path21.join(vaultPath, VAULT_MARKER_FILENAME),
|
|
20849
21128
|
JSON.stringify(marker, null, 2),
|
|
20850
21129
|
"utf-8"
|
|
20851
21130
|
);
|
|
@@ -20858,12 +21137,12 @@ init_guards();
|
|
|
20858
21137
|
init_course_state();
|
|
20859
21138
|
init_resolve();
|
|
20860
21139
|
init_errors();
|
|
20861
|
-
import
|
|
21140
|
+
import path22 from "node:path";
|
|
20862
21141
|
import os11 from "node:os";
|
|
20863
|
-
import
|
|
21142
|
+
import fs18 from "node:fs/promises";
|
|
20864
21143
|
var logger22 = createLogger("cli:vault");
|
|
20865
|
-
var vaultCommand = new
|
|
20866
|
-
vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
21144
|
+
var vaultCommand = new Command23("vault").description("Gerenciar vault Obsidian do curso");
|
|
21145
|
+
vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path22.join(os11.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
20867
21146
|
try {
|
|
20868
21147
|
const session = await requireSession();
|
|
20869
21148
|
const activeCourse = await requireActiveCourse();
|
|
@@ -20944,7 +21223,7 @@ Para visualizar:
|
|
|
20944
21223
|
process.exit(1);
|
|
20945
21224
|
}
|
|
20946
21225
|
});
|
|
20947
|
-
vaultCommand.command("sync").description("Sincronizar progresso do curso com o vault local").option("--path <dir>", "Diret\xF3rio base do workspace",
|
|
21226
|
+
vaultCommand.command("sync").description("Sincronizar progresso do curso com o vault local").option("--path <dir>", "Diret\xF3rio base do workspace", path22.join(os11.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
20948
21227
|
try {
|
|
20949
21228
|
const session = await requireSession();
|
|
20950
21229
|
const activeCourse = await requireActiveCourse();
|
|
@@ -20970,8 +21249,8 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
|
|
|
20970
21249
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
20971
21250
|
const deps = { data, logger: logger22 };
|
|
20972
21251
|
const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
20973
|
-
const markerPath =
|
|
20974
|
-
const markerRaw = await
|
|
21252
|
+
const markerPath = path22.join(vaultPath, ".ana-vault.json");
|
|
21253
|
+
const markerRaw = await fs18.readFile(markerPath, "utf-8");
|
|
20975
21254
|
const marker = JSON.parse(markerRaw);
|
|
20976
21255
|
marker.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20977
21256
|
marker.progress = {
|
|
@@ -20979,10 +21258,10 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
|
|
|
20979
21258
|
currentModule: progress.currentModule.title,
|
|
20980
21259
|
currentLesson: progress.currentLesson.title
|
|
20981
21260
|
};
|
|
20982
|
-
await
|
|
20983
|
-
const courseIndexPath =
|
|
21261
|
+
await fs18.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
|
|
21262
|
+
const courseIndexPath = path22.join(vaultPath, slug, "index.md");
|
|
20984
21263
|
try {
|
|
20985
|
-
let indexContent = await
|
|
21264
|
+
let indexContent = await fs18.readFile(courseIndexPath, "utf-8");
|
|
20986
21265
|
indexContent = indexContent.replace(/\n---\n\n> 📊 Progresso:.*\n/g, "");
|
|
20987
21266
|
const titleEnd = indexContent.indexOf("\n");
|
|
20988
21267
|
if (titleEnd !== -1) {
|
|
@@ -20993,7 +21272,7 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
|
|
|
20993
21272
|
`;
|
|
20994
21273
|
indexContent = indexContent.slice(0, titleEnd) + banner + indexContent.slice(titleEnd);
|
|
20995
21274
|
}
|
|
20996
|
-
await
|
|
21275
|
+
await fs18.writeFile(courseIndexPath, indexContent, "utf-8");
|
|
20997
21276
|
} catch {
|
|
20998
21277
|
}
|
|
20999
21278
|
const syncedAt = marker.lastSyncedAt;
|
|
@@ -21034,8 +21313,8 @@ init_dist();
|
|
|
21034
21313
|
init_formatter();
|
|
21035
21314
|
init_errors();
|
|
21036
21315
|
import os13 from "node:os";
|
|
21037
|
-
import
|
|
21038
|
-
import { Command as
|
|
21316
|
+
import path26 from "node:path";
|
|
21317
|
+
import { Command as Command25 } from "commander";
|
|
21039
21318
|
|
|
21040
21319
|
// ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/validators/src/authored-course.ts
|
|
21041
21320
|
import { createHash } from "node:crypto";
|
|
@@ -21308,12 +21587,12 @@ function assembleAuthoredCourse(manifest, files) {
|
|
|
21308
21587
|
init_guards();
|
|
21309
21588
|
|
|
21310
21589
|
// src/creator/state.ts
|
|
21311
|
-
import
|
|
21312
|
-
import
|
|
21313
|
-
var STATE_FILE =
|
|
21590
|
+
import fs19 from "node:fs/promises";
|
|
21591
|
+
import path23 from "node:path";
|
|
21592
|
+
var STATE_FILE = path23.join(".tostudy", "creator.json");
|
|
21314
21593
|
async function readCreatorState(root) {
|
|
21315
21594
|
try {
|
|
21316
|
-
const parsed = JSON.parse(await
|
|
21595
|
+
const parsed = JSON.parse(await fs19.readFile(path23.join(root, STATE_FILE), "utf-8"));
|
|
21317
21596
|
const hashes = parsed.baseHashes;
|
|
21318
21597
|
const wellFormed = Array.isArray(hashes) && hashes.every(
|
|
21319
21598
|
(mod) => Array.isArray(mod) && // wire shape of authoredCoursePushSchema.baseHashes; anything else is not a base we can send
|
|
@@ -21329,9 +21608,9 @@ async function readCreatorState(root) {
|
|
|
21329
21608
|
}
|
|
21330
21609
|
}
|
|
21331
21610
|
async function writeCreatorState(root, state) {
|
|
21332
|
-
const file2 =
|
|
21333
|
-
await
|
|
21334
|
-
await
|
|
21611
|
+
const file2 = path23.join(root, STATE_FILE);
|
|
21612
|
+
await fs19.mkdir(path23.dirname(file2), { recursive: true });
|
|
21613
|
+
await fs19.writeFile(file2, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
21335
21614
|
}
|
|
21336
21615
|
function changedSlots(baseHashes, remoteHashes, incomingHashes) {
|
|
21337
21616
|
const changed = [];
|
|
@@ -21348,9 +21627,9 @@ function changedSlots(baseHashes, remoteHashes, incomingHashes) {
|
|
|
21348
21627
|
// src/creator/workspace.ts
|
|
21349
21628
|
init_errors();
|
|
21350
21629
|
import { randomUUID } from "node:crypto";
|
|
21351
|
-
import
|
|
21630
|
+
import fs20 from "node:fs/promises";
|
|
21352
21631
|
import os12 from "node:os";
|
|
21353
|
-
import
|
|
21632
|
+
import path24 from "node:path";
|
|
21354
21633
|
var MANIFEST_FILE2 = "tostudy.json";
|
|
21355
21634
|
var STATE_GITIGNORE_ENTRY = ".tostudy/creator.json";
|
|
21356
21635
|
var CreatorWorkspaceError = class extends Error {
|
|
@@ -21364,10 +21643,10 @@ var CreatorWorkspaceError = class extends Error {
|
|
|
21364
21643
|
file;
|
|
21365
21644
|
};
|
|
21366
21645
|
function assertInsideRoot(root, relPath) {
|
|
21367
|
-
const abs =
|
|
21368
|
-
const back =
|
|
21369
|
-
const escapes = back === "" || back === ".." || back.startsWith(`..${
|
|
21370
|
-
if (
|
|
21646
|
+
const abs = path24.resolve(root, relPath);
|
|
21647
|
+
const back = path24.relative(path24.resolve(root), abs);
|
|
21648
|
+
const escapes = back === "" || back === ".." || back.startsWith(`..${path24.sep}`) || path24.isAbsolute(back);
|
|
21649
|
+
if (path24.isAbsolute(relPath) || escapes) {
|
|
21371
21650
|
throw new CreatorWorkspaceError(
|
|
21372
21651
|
"PATH_OUTSIDE_ROOT",
|
|
21373
21652
|
fillTemplate(getErrors().creator.pathOutsideRoot, { path: relPath }),
|
|
@@ -21377,9 +21656,9 @@ function assertInsideRoot(root, relPath) {
|
|
|
21377
21656
|
return abs;
|
|
21378
21657
|
}
|
|
21379
21658
|
function assertNotHome(dir, home = os12.homedir()) {
|
|
21380
|
-
const resolvedHome =
|
|
21381
|
-
const resolved =
|
|
21382
|
-
const prefix = resolved.endsWith(
|
|
21659
|
+
const resolvedHome = path24.resolve(home);
|
|
21660
|
+
const resolved = path24.resolve(dir);
|
|
21661
|
+
const prefix = resolved.endsWith(path24.sep) ? resolved : resolved + path24.sep;
|
|
21383
21662
|
if (resolved === resolvedHome || resolvedHome.startsWith(prefix)) {
|
|
21384
21663
|
throw new CreatorWorkspaceError("REFUSED_AT_HOME", getErrors().creator.refusedAtHome);
|
|
21385
21664
|
}
|
|
@@ -21398,7 +21677,7 @@ function manifestLessonPaths(manifestRaw) {
|
|
|
21398
21677
|
async function readManifest(root) {
|
|
21399
21678
|
let text2;
|
|
21400
21679
|
try {
|
|
21401
|
-
text2 = await
|
|
21680
|
+
text2 = await fs20.readFile(path24.join(root, MANIFEST_FILE2), "utf-8");
|
|
21402
21681
|
} catch {
|
|
21403
21682
|
throw new CreatorWorkspaceError(
|
|
21404
21683
|
"MANIFEST_MISSING",
|
|
@@ -21417,7 +21696,7 @@ async function readManifest(root) {
|
|
|
21417
21696
|
}
|
|
21418
21697
|
}
|
|
21419
21698
|
async function readCourseId(dir) {
|
|
21420
|
-
const manifest = await readManifest(
|
|
21699
|
+
const manifest = await readManifest(path24.resolve(dir));
|
|
21421
21700
|
const courseId = manifest?.courseId;
|
|
21422
21701
|
if (typeof courseId !== "string" || courseId.length === 0) {
|
|
21423
21702
|
throw new CreatorWorkspaceError(
|
|
@@ -21431,23 +21710,23 @@ async function readCourseId(dir) {
|
|
|
21431
21710
|
async function readInside(root, realRoot, relPath) {
|
|
21432
21711
|
let real;
|
|
21433
21712
|
try {
|
|
21434
|
-
real = await
|
|
21713
|
+
real = await fs20.realpath(path24.resolve(root, relPath));
|
|
21435
21714
|
} catch {
|
|
21436
21715
|
return null;
|
|
21437
21716
|
}
|
|
21438
|
-
if (!real.startsWith(realRoot +
|
|
21717
|
+
if (!real.startsWith(realRoot + path24.sep)) {
|
|
21439
21718
|
throw new CreatorWorkspaceError(
|
|
21440
21719
|
"PATH_OUTSIDE_ROOT",
|
|
21441
21720
|
fillTemplate(getErrors().creator.pathOutsideRoot, { path: relPath }),
|
|
21442
21721
|
relPath
|
|
21443
21722
|
);
|
|
21444
21723
|
}
|
|
21445
|
-
return
|
|
21724
|
+
return fs20.readFile(real, "utf-8");
|
|
21446
21725
|
}
|
|
21447
21726
|
async function readWorkspace(dir) {
|
|
21448
|
-
const root =
|
|
21727
|
+
const root = path24.resolve(dir);
|
|
21449
21728
|
const manifestRaw = await readManifest(root);
|
|
21450
|
-
const realRoot = await
|
|
21729
|
+
const realRoot = await fs20.realpath(root);
|
|
21451
21730
|
const files = {};
|
|
21452
21731
|
for (const relPath of manifestLessonPaths(manifestRaw).flat()) {
|
|
21453
21732
|
assertInsideRoot(root, relPath);
|
|
@@ -21459,18 +21738,18 @@ async function readWorkspace(dir) {
|
|
|
21459
21738
|
return { manifestRaw, files, root };
|
|
21460
21739
|
}
|
|
21461
21740
|
async function ensureGitignore(root) {
|
|
21462
|
-
const file2 =
|
|
21463
|
-
const current = await
|
|
21741
|
+
const file2 = path24.join(root, ".gitignore");
|
|
21742
|
+
const current = await fs20.readFile(file2, "utf-8").catch(() => "");
|
|
21464
21743
|
if (current.split(/\r?\n/).some((line) => line.trim() === STATE_GITIGNORE_ENTRY)) return false;
|
|
21465
21744
|
const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
|
|
21466
|
-
await
|
|
21745
|
+
await fs20.writeFile(file2, `${current}${separator}${STATE_GITIGNORE_ENTRY}
|
|
21467
21746
|
`, "utf-8");
|
|
21468
21747
|
return true;
|
|
21469
21748
|
}
|
|
21470
21749
|
async function scaffoldWorkspace(dir, home) {
|
|
21471
|
-
const root =
|
|
21750
|
+
const root = path24.resolve(dir);
|
|
21472
21751
|
assertNotHome(root, home);
|
|
21473
|
-
await
|
|
21752
|
+
await fs20.mkdir(root, { recursive: true });
|
|
21474
21753
|
const files = [];
|
|
21475
21754
|
let courseId = randomUUID();
|
|
21476
21755
|
let manifestCreated = true;
|
|
@@ -21481,11 +21760,11 @@ async function scaffoldWorkspace(dir, home) {
|
|
|
21481
21760
|
modules: [{ title: "", description: "", objectives: [], lessons: [] }]
|
|
21482
21761
|
};
|
|
21483
21762
|
try {
|
|
21484
|
-
await
|
|
21763
|
+
await fs20.writeFile(path24.join(root, MANIFEST_FILE2), JSON.stringify(manifest, null, 2) + "\n", {
|
|
21485
21764
|
encoding: "utf-8",
|
|
21486
21765
|
flag: "wx"
|
|
21487
21766
|
});
|
|
21488
|
-
await
|
|
21767
|
+
await fs20.mkdir(path24.join(root, "modules", "01"), { recursive: true });
|
|
21489
21768
|
files.push(MANIFEST_FILE2, "modules/01/");
|
|
21490
21769
|
} catch (err) {
|
|
21491
21770
|
if (err.code !== "EEXIST") throw err;
|
|
@@ -21547,8 +21826,8 @@ async function validateWorkspace(dir, opts = {}) {
|
|
|
21547
21826
|
}
|
|
21548
21827
|
|
|
21549
21828
|
// src/creator/pull.ts
|
|
21550
|
-
import
|
|
21551
|
-
import
|
|
21829
|
+
import fs21 from "node:fs/promises";
|
|
21830
|
+
import path25 from "node:path";
|
|
21552
21831
|
init_errors();
|
|
21553
21832
|
function stripToSchema(schema, value) {
|
|
21554
21833
|
const current = structuredClone(value);
|
|
@@ -21573,17 +21852,17 @@ function stripToSchema(schema, value) {
|
|
|
21573
21852
|
}
|
|
21574
21853
|
async function exists(file2) {
|
|
21575
21854
|
try {
|
|
21576
|
-
await
|
|
21855
|
+
await fs21.access(file2);
|
|
21577
21856
|
return true;
|
|
21578
21857
|
} catch {
|
|
21579
21858
|
return false;
|
|
21580
21859
|
}
|
|
21581
21860
|
}
|
|
21582
21861
|
async function assertWritableInside(realRoot, root, rel) {
|
|
21583
|
-
const abs =
|
|
21862
|
+
const abs = path25.resolve(root, rel);
|
|
21584
21863
|
let target;
|
|
21585
21864
|
try {
|
|
21586
|
-
target = await
|
|
21865
|
+
target = await fs21.lstat(abs);
|
|
21587
21866
|
} catch {
|
|
21588
21867
|
target = void 0;
|
|
21589
21868
|
}
|
|
@@ -21594,10 +21873,10 @@ async function assertWritableInside(realRoot, root, rel) {
|
|
|
21594
21873
|
rel
|
|
21595
21874
|
);
|
|
21596
21875
|
}
|
|
21597
|
-
let dir =
|
|
21598
|
-
while (!await exists(dir)) dir =
|
|
21599
|
-
const realDir = await
|
|
21600
|
-
if (realDir !== realRoot && !realDir.startsWith(realRoot +
|
|
21876
|
+
let dir = path25.dirname(abs);
|
|
21877
|
+
while (!await exists(dir)) dir = path25.dirname(dir);
|
|
21878
|
+
const realDir = await fs21.realpath(dir);
|
|
21879
|
+
if (realDir !== realRoot && !realDir.startsWith(realRoot + path25.sep)) {
|
|
21601
21880
|
throw new CreatorWorkspaceError(
|
|
21602
21881
|
"PATH_OUTSIDE_ROOT",
|
|
21603
21882
|
fillTemplate(getErrors().creator.pathOutsideRoot, { path: rel }),
|
|
@@ -21606,7 +21885,7 @@ async function assertWritableInside(realRoot, root, rel) {
|
|
|
21606
21885
|
}
|
|
21607
21886
|
}
|
|
21608
21887
|
async function applyPull(root, manifestRaw, remoteModules) {
|
|
21609
|
-
const realRoot = await
|
|
21888
|
+
const realRoot = await fs21.realpath(root);
|
|
21610
21889
|
const local = manifestLessonPaths(manifestRaw);
|
|
21611
21890
|
const localShape = local.map((mod) => mod.length);
|
|
21612
21891
|
const remoteShape = remoteModules.map((mod) => mod.lessons.length);
|
|
@@ -21625,7 +21904,7 @@ async function applyPull(root, manifestRaw, remoteModules) {
|
|
|
21625
21904
|
assertInsideRoot(root, rel);
|
|
21626
21905
|
const contract = stripped.data;
|
|
21627
21906
|
const sidecarRel = sidecarPath(rel);
|
|
21628
|
-
if (await exists(
|
|
21907
|
+
if (await exists(path25.resolve(root, sidecarRel))) {
|
|
21629
21908
|
const { teachingContent, ...rest } = contract;
|
|
21630
21909
|
planned.push({ rel: sidecarRel, text: String(teachingContent ?? "") });
|
|
21631
21910
|
planned.push({ rel, text: `${JSON.stringify(rest, null, 2)}
|
|
@@ -21641,9 +21920,9 @@ async function applyPull(root, manifestRaw, remoteModules) {
|
|
|
21641
21920
|
await assertWritableInside(realRoot, root, file2.rel);
|
|
21642
21921
|
}
|
|
21643
21922
|
for (const file2 of planned) {
|
|
21644
|
-
const abs =
|
|
21645
|
-
await
|
|
21646
|
-
await
|
|
21923
|
+
const abs = path25.resolve(root, file2.rel);
|
|
21924
|
+
await fs21.mkdir(path25.dirname(abs), { recursive: true });
|
|
21925
|
+
await fs21.writeFile(abs, file2.text, "utf-8");
|
|
21647
21926
|
}
|
|
21648
21927
|
return { kind: "written", files: planned.map((file2) => file2.rel) };
|
|
21649
21928
|
}
|
|
@@ -21977,7 +22256,7 @@ function installCreatorSkill(input2) {
|
|
|
21977
22256
|
// src/commands/brief-open.ts
|
|
21978
22257
|
init_guards();
|
|
21979
22258
|
init_formatter();
|
|
21980
|
-
import { Command as
|
|
22259
|
+
import { Command as Command24 } from "commander";
|
|
21981
22260
|
import { execFile as execFile4 } from "node:child_process";
|
|
21982
22261
|
import { platform } from "node:process";
|
|
21983
22262
|
var BRIEF_URL = "https://tostudy.ai/student/settings/learner-brief";
|
|
@@ -22002,7 +22281,7 @@ function openUrl(url2, options = {}) {
|
|
|
22002
22281
|
}
|
|
22003
22282
|
});
|
|
22004
22283
|
}
|
|
22005
|
-
var briefOpenCommand = new
|
|
22284
|
+
var briefOpenCommand = new Command24("brief-open").description("Open the learner brief editor in your web browser").action(async () => {
|
|
22006
22285
|
await requireSession();
|
|
22007
22286
|
output(`Abrindo ${BRIEF_URL} no navegador...`, { json: false });
|
|
22008
22287
|
openUrl(BRIEF_URL);
|
|
@@ -22010,7 +22289,7 @@ var briefOpenCommand = new Command23("brief-open").description("Open the learner
|
|
|
22010
22289
|
|
|
22011
22290
|
// src/commands/creator.ts
|
|
22012
22291
|
var logger23 = createLogger("cli:creator");
|
|
22013
|
-
var creatorCommand = new
|
|
22292
|
+
var creatorCommand = new Command25("creator").description(
|
|
22014
22293
|
"Author a course with your own AI agent: init, validate, push, pull, audit, status, open"
|
|
22015
22294
|
);
|
|
22016
22295
|
function fail(err, opts, context) {
|
|
@@ -22024,7 +22303,7 @@ function fail(err, opts, context) {
|
|
|
22024
22303
|
creatorCommand.command("init").description("Create an authoring workspace (tostudy.json, modules/01) and install the skill").argument("[dir]", "Workspace folder", ".").option("--all-runtimes", "Install the skill for every supported runtime, detected or not").option("--json", "Output structured JSON").action(async (dir, opts) => {
|
|
22025
22304
|
try {
|
|
22026
22305
|
const home = os13.homedir();
|
|
22027
|
-
const scaffold = await scaffoldWorkspace(
|
|
22306
|
+
const scaffold = await scaffoldWorkspace(path26.resolve(process.cwd(), dir), home);
|
|
22028
22307
|
const kept = [];
|
|
22029
22308
|
const installed = installCreatorSkill({
|
|
22030
22309
|
cwd: scaffold.root,
|
|
@@ -22307,8 +22586,8 @@ creatorCommand.command("open").description("Open the course in the creator porta
|
|
|
22307
22586
|
init_guards();
|
|
22308
22587
|
init_course_state();
|
|
22309
22588
|
init_user_profile();
|
|
22310
|
-
import { Command as
|
|
22311
|
-
var profileCommand = new
|
|
22589
|
+
import { Command as Command26 } from "commander";
|
|
22590
|
+
var profileCommand = new Command26("profile").description("Show your learner profile for the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22312
22591
|
const activeCourse = await requireActiveCourse();
|
|
22313
22592
|
const onboarding = await getCourseOnboardingState(activeCourse.courseId);
|
|
22314
22593
|
const profile = onboarding?.learnerProfile ?? await getUserProfile();
|
|
@@ -22370,10 +22649,10 @@ var profileCommand = new Command25("profile").description("Show your learner pro
|
|
|
22370
22649
|
init_dist();
|
|
22371
22650
|
init_guards();
|
|
22372
22651
|
init_formatter();
|
|
22373
|
-
import { Command as
|
|
22652
|
+
import { Command as Command27 } from "commander";
|
|
22374
22653
|
init_workspace_state();
|
|
22375
22654
|
var logger24 = createLogger("cli:sync");
|
|
22376
|
-
var syncCommand = new
|
|
22655
|
+
var syncCommand = new Command27("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").option(
|
|
22377
22656
|
"--all-runtimes",
|
|
22378
22657
|
"Write instruction files for every supported runtime, even undetected ones"
|
|
22379
22658
|
).option(
|
|
@@ -22445,10 +22724,10 @@ var syncCommand = new Command26("sync").description("Regenerate instruction file
|
|
|
22445
22724
|
// src/commands/brief.ts
|
|
22446
22725
|
init_dist();
|
|
22447
22726
|
init_guards();
|
|
22448
|
-
import { Command as
|
|
22727
|
+
import { Command as Command28 } from "commander";
|
|
22449
22728
|
init_formatter();
|
|
22450
22729
|
var logger25 = createLogger("cli:brief");
|
|
22451
|
-
var briefCommand = new
|
|
22730
|
+
var briefCommand = new Command28("brief").description("Show your base learner brief (T1) status and content").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22452
22731
|
try {
|
|
22453
22732
|
const session = await requireSession();
|
|
22454
22733
|
const cached2 = await readBriefCache();
|
|
@@ -22489,10 +22768,10 @@ var briefCommand = new Command27("brief").description("Show your base learner br
|
|
|
22489
22768
|
// src/commands/brief-create.ts
|
|
22490
22769
|
init_dist();
|
|
22491
22770
|
init_guards();
|
|
22492
|
-
import { Command as
|
|
22771
|
+
import { Command as Command29 } from "commander";
|
|
22493
22772
|
init_formatter();
|
|
22494
22773
|
var logger26 = createLogger("cli:brief-create");
|
|
22495
|
-
var briefCreateCommand = new
|
|
22774
|
+
var briefCreateCommand = new Command29("brief-create").description("Create your base learner brief via interactive prompts (T1 bootstrap)").action(async () => {
|
|
22496
22775
|
try {
|
|
22497
22776
|
const session = await requireSession();
|
|
22498
22777
|
const answers = await collectBootstrapAnswers({ userName: session.userName });
|
|
@@ -22525,22 +22804,22 @@ var briefCreateCommand = new Command28("brief-create").description("Create your
|
|
|
22525
22804
|
init_dist();
|
|
22526
22805
|
init_guards();
|
|
22527
22806
|
init_workspace_state();
|
|
22528
|
-
import
|
|
22529
|
-
import { Command as
|
|
22807
|
+
import fs23 from "node:fs";
|
|
22808
|
+
import { Command as Command30 } from "commander";
|
|
22530
22809
|
|
|
22531
22810
|
// src/sessions/storage.ts
|
|
22532
22811
|
init_dist();
|
|
22533
|
-
import
|
|
22534
|
-
import
|
|
22812
|
+
import fs22 from "node:fs";
|
|
22813
|
+
import path27 from "node:path";
|
|
22535
22814
|
var logger27 = createLogger("cli:sessions");
|
|
22536
22815
|
function sessionsDir(workspacePath) {
|
|
22537
|
-
return
|
|
22816
|
+
return path27.join(workspacePath, ".tostudy", "sessions");
|
|
22538
22817
|
}
|
|
22539
22818
|
async function saveModuleSummary(workspacePath, input2) {
|
|
22540
22819
|
const dir = sessionsDir(workspacePath);
|
|
22541
|
-
|
|
22820
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
22542
22821
|
const filename = `module-${input2.moduleId}-summary.md`;
|
|
22543
|
-
const filePath =
|
|
22822
|
+
const filePath = path27.join(dir, filename);
|
|
22544
22823
|
const header = [
|
|
22545
22824
|
"---",
|
|
22546
22825
|
`moduleId: ${input2.moduleId}`,
|
|
@@ -22549,17 +22828,17 @@ async function saveModuleSummary(workspacePath, input2) {
|
|
|
22549
22828
|
"---",
|
|
22550
22829
|
""
|
|
22551
22830
|
].join("\n");
|
|
22552
|
-
|
|
22831
|
+
fs22.writeFileSync(filePath, header + input2.summary, { mode: 384 });
|
|
22553
22832
|
logger27.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
|
|
22554
22833
|
return filePath;
|
|
22555
22834
|
}
|
|
22556
22835
|
async function loadSessionContext(workspacePath) {
|
|
22557
22836
|
const dir = sessionsDir(workspacePath);
|
|
22558
|
-
if (!
|
|
22559
|
-
const files =
|
|
22837
|
+
if (!fs22.existsSync(dir)) return { moduleSummaries: [] };
|
|
22838
|
+
const files = fs22.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
|
|
22560
22839
|
const summaries = [];
|
|
22561
22840
|
for (const file2 of files) {
|
|
22562
|
-
const content =
|
|
22841
|
+
const content = fs22.readFileSync(path27.join(dir, file2), "utf-8");
|
|
22563
22842
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
22564
22843
|
if (!frontmatterMatch) continue;
|
|
22565
22844
|
const meta3 = frontmatterMatch[1];
|
|
@@ -22575,7 +22854,7 @@ async function loadSessionContext(workspacePath) {
|
|
|
22575
22854
|
// src/commands/compact.ts
|
|
22576
22855
|
init_formatter();
|
|
22577
22856
|
var logger28 = createLogger("cli:compact");
|
|
22578
|
-
var compactCommand = new
|
|
22857
|
+
var compactCommand = new Command30("compact").description("Save a module study summary (LLM-generated, from stdin)").option("--module-id <id>", "Module ID").option("--module-title <title>", "Module title").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22579
22858
|
try {
|
|
22580
22859
|
const activeCourse = await requireActiveCourse();
|
|
22581
22860
|
const ws = await findWorkspaceState();
|
|
@@ -22583,7 +22862,7 @@ var compactCommand = new Command29("compact").description("Save a module study s
|
|
|
22583
22862
|
if (opts.json) jsonError("no_workspace");
|
|
22584
22863
|
error("Nenhum workspace encontrado.");
|
|
22585
22864
|
}
|
|
22586
|
-
const summary =
|
|
22865
|
+
const summary = fs23.readFileSync("/dev/stdin", "utf-8").trim();
|
|
22587
22866
|
if (!summary) {
|
|
22588
22867
|
if (opts.json) jsonError("empty_summary", { message: "Summary vazio" });
|
|
22589
22868
|
error("Summary vazio. Envie o conte\xFAdo via stdin.");
|
|
@@ -22613,12 +22892,12 @@ var compactCommand = new Command29("compact").description("Save a module study s
|
|
|
22613
22892
|
// src/commands/context.ts
|
|
22614
22893
|
init_dist();
|
|
22615
22894
|
init_workspace_state();
|
|
22616
|
-
import { Command as
|
|
22895
|
+
import { Command as Command31 } from "commander";
|
|
22617
22896
|
init_course_state();
|
|
22618
22897
|
init_formatter();
|
|
22619
22898
|
init_errors();
|
|
22620
22899
|
var logger29 = createLogger("cli:context");
|
|
22621
|
-
var contextCommand = new
|
|
22900
|
+
var contextCommand = new Command31("context").description("Load session context (workspace state + module summaries) for LLM consumption").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22622
22901
|
try {
|
|
22623
22902
|
const ws = await findWorkspaceState();
|
|
22624
22903
|
if (!ws) {
|
|
@@ -22670,13 +22949,13 @@ var contextCommand = new Command30("context").description("Load session context
|
|
|
22670
22949
|
|
|
22671
22950
|
// src/commands/memory.ts
|
|
22672
22951
|
init_dist();
|
|
22673
|
-
import { Command as
|
|
22952
|
+
import { Command as Command32 } from "commander";
|
|
22674
22953
|
init_workspace_state();
|
|
22675
22954
|
init_guards();
|
|
22676
22955
|
init_formatter();
|
|
22677
22956
|
init_errors();
|
|
22678
22957
|
var logger30 = createLogger("cli:memory");
|
|
22679
|
-
var memoryCommand = new
|
|
22958
|
+
var memoryCommand = new Command32("memory").description("Load accumulated student memory (learning profile + recent lessons) for the tutor").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22680
22959
|
try {
|
|
22681
22960
|
const ws = await findWorkspaceState();
|
|
22682
22961
|
if (!ws) {
|
|
@@ -22707,14 +22986,14 @@ var memoryCommand = new Command31("memory").description("Load accumulated studen
|
|
|
22707
22986
|
|
|
22708
22987
|
// src/commands/insight.ts
|
|
22709
22988
|
init_dist();
|
|
22710
|
-
import { Command as
|
|
22989
|
+
import { Command as Command33 } from "commander";
|
|
22711
22990
|
init_workspace_state();
|
|
22712
22991
|
init_guards();
|
|
22713
22992
|
init_formatter();
|
|
22714
22993
|
init_errors();
|
|
22715
22994
|
var logger31 = createLogger("cli:insight");
|
|
22716
22995
|
var VALID_TYPES = ["difficulty", "breakthrough", "question"];
|
|
22717
|
-
var insightCommand = new
|
|
22996
|
+
var insightCommand = new Command33("insight").description(
|
|
22718
22997
|
"Persist a student cognitive insight (difficulty | breakthrough | question) into course memory"
|
|
22719
22998
|
).argument("<type>", "difficulty | breakthrough | question").argument("<content>", 'Short description, e.g. "confunde async/await com promises"').option("--module-id <id>", "Module the insight relates to").option("--json", "Output structured JSON").action(async (type, content, opts) => {
|
|
22720
22999
|
try {
|
|
@@ -22762,7 +23041,7 @@ var insightCommand = new Command32("insight").description(
|
|
|
22762
23041
|
|
|
22763
23042
|
// src/commands/level.ts
|
|
22764
23043
|
init_dist();
|
|
22765
|
-
import { Command as
|
|
23044
|
+
import { Command as Command34 } from "commander";
|
|
22766
23045
|
init_guards();
|
|
22767
23046
|
init_formatter();
|
|
22768
23047
|
var logger32 = createLogger("cli:level");
|
|
@@ -22777,7 +23056,7 @@ var LEVEL_LABELS2 = {
|
|
|
22777
23056
|
function isExerciseLevel2(value) {
|
|
22778
23057
|
return ALL_LEVELS.includes(value);
|
|
22779
23058
|
}
|
|
22780
|
-
var levelCommand = new
|
|
23059
|
+
var levelCommand = new Command34("level").description("Show or set the exercise scaffolding level for the active course").argument("[level]", "Target level: L0, L1, L2, L3, or L4").option("--json", "Output structured JSON").action(async (rawLevel, opts) => {
|
|
22781
23060
|
try {
|
|
22782
23061
|
const session = await requireSession();
|
|
22783
23062
|
const activeCourse = await requireActiveCourse();
|
|
@@ -22842,10 +23121,10 @@ init_dist();
|
|
|
22842
23121
|
init_guards();
|
|
22843
23122
|
init_formatter();
|
|
22844
23123
|
init_errors();
|
|
22845
|
-
import { Command as
|
|
23124
|
+
import { Command as Command35 } from "commander";
|
|
22846
23125
|
var logger33 = createLogger("cli:theory");
|
|
22847
23126
|
var EXERCISE_LEVELS2 = ["L0", "L1", "L2", "L3", "L4"];
|
|
22848
|
-
var theoryCommand = new
|
|
23127
|
+
var theoryCommand = new Command35("theory").description("Request more theory on the current lesson (escape hatch)").option("--focus <text>", "Optional focus area in PT-BR (max 500 chars)").option("--level <L0|L1|L2|L3|L4>", "Override current exercise level (otherwise auto-detect)").option("--lesson <uuid>", "Override lesson ID (defaults to current workspace lesson)").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22849
23128
|
try {
|
|
22850
23129
|
const session = await requireSession();
|
|
22851
23130
|
const active = await requireActiveCourse();
|
|
@@ -22940,7 +23219,7 @@ var theoryCommand = new Command34("theory").description("Request more theory on
|
|
|
22940
23219
|
// src/commands/export-grades.ts
|
|
22941
23220
|
init_dist();
|
|
22942
23221
|
init_guards();
|
|
22943
|
-
import { Command as
|
|
23222
|
+
import { Command as Command36 } from "commander";
|
|
22944
23223
|
|
|
22945
23224
|
// src/formatters/grades.ts
|
|
22946
23225
|
function formatGradesJson(attempts) {
|
|
@@ -23308,7 +23587,7 @@ async function runExportGrades(opts, deps = defaultDeps5) {
|
|
|
23308
23587
|
if (!rendered.endsWith("\n")) rendered += "\n";
|
|
23309
23588
|
deps.stdoutWrite(rendered);
|
|
23310
23589
|
}
|
|
23311
|
-
var exportGradesCommand = new
|
|
23590
|
+
var exportGradesCommand = new Command36("export-grades").description("Exporta o hist\xF3rico de valida\xE7\xF5es (notas) do curso ativo").option(
|
|
23312
23591
|
"--course <slug>",
|
|
23313
23592
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, exporta o curso ativo."
|
|
23314
23593
|
).option("--format <json|csv|md>", "Formato de sa\xEDda (default: json)", "json").addHelpText(
|
|
@@ -23332,7 +23611,7 @@ C\xF3digos de sa\xEDda:
|
|
|
23332
23611
|
|
|
23333
23612
|
// src/commands/grades.ts
|
|
23334
23613
|
init_dist();
|
|
23335
|
-
import { Command as
|
|
23614
|
+
import { Command as Command37 } from "commander";
|
|
23336
23615
|
init_guards();
|
|
23337
23616
|
var logger35 = createLogger("cli:grades");
|
|
23338
23617
|
var defaultDeps6 = {
|
|
@@ -23488,7 +23767,7 @@ async function runGrades(opts, deps = defaultDeps6) {
|
|
|
23488
23767
|
);
|
|
23489
23768
|
deps.stdoutWrite(formatGradesAllEnrollmentsTable(summaries) + "\n");
|
|
23490
23769
|
}
|
|
23491
|
-
var gradesCommand = new
|
|
23770
|
+
var gradesCommand = new Command37("grades").description("Mostra um resumo das notas (todas as matr\xEDculas ou um curso espec\xEDfico)").option(
|
|
23492
23771
|
"--course <slug>",
|
|
23493
23772
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, lista todos os cursos."
|
|
23494
23773
|
).addHelpText(
|
|
@@ -23512,7 +23791,7 @@ C\xF3digos de sa\xEDda:
|
|
|
23512
23791
|
// src/commands/review.ts
|
|
23513
23792
|
init_dist();
|
|
23514
23793
|
init_guards();
|
|
23515
|
-
import { Command as
|
|
23794
|
+
import { Command as Command38 } from "commander";
|
|
23516
23795
|
import { createInterface } from "node:readline/promises";
|
|
23517
23796
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
23518
23797
|
var logger36 = createLogger("cli:review");
|
|
@@ -23662,7 +23941,7 @@ async function runReview(slug, opts, deps = defaultDeps7) {
|
|
|
23662
23941
|
}) + "\n"
|
|
23663
23942
|
);
|
|
23664
23943
|
}
|
|
23665
|
-
var reviewCommand = new
|
|
23944
|
+
var reviewCommand = new Command38("review").description("Revisa a \xFAltima tentativa de valida\xE7\xE3o de uma li\xE7\xE3o").argument("<slug>", "Slug do curso (gerado a partir do t\xEDtulo)").option("--json", "Sa\xEDda em JSON (sem prompt interativo)").addHelpText(
|
|
23666
23945
|
"after",
|
|
23667
23946
|
`
|
|
23668
23947
|
Exemplos:
|
|
@@ -23685,12 +23964,12 @@ C\xF3digos de sa\xEDda:
|
|
|
23685
23964
|
|
|
23686
23965
|
// src/cli.ts
|
|
23687
23966
|
function createProgram() {
|
|
23688
|
-
const program = new
|
|
23967
|
+
const program = new Command39();
|
|
23689
23968
|
program.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").option("--locale <code>", "Output locale (pt-BR | en-US); defaults to LANG env then pt-BR").addHelpText(
|
|
23690
23969
|
"before",
|
|
23691
23970
|
[
|
|
23692
23971
|
"",
|
|
23693
|
-
" Come\xE7ando: login \u2192 enroll <id> \u2192 courses \u2192 select <n> \u2192 start",
|
|
23972
|
+
" Come\xE7ando: login \u2192 suggest \u2192 enroll <id> \u2192 courses \u2192 select <n> \u2192 start",
|
|
23694
23973
|
" Estudando: next \xB7 lesson \xB7 hint \xB7 validate \xB7 theory \xB7 progress",
|
|
23695
23974
|
" Com d\xFAvida: tostudy menu (mostra onde voc\xEA est\xE1 e o que fazer)",
|
|
23696
23975
|
""
|
|
@@ -23702,6 +23981,7 @@ function createProgram() {
|
|
|
23702
23981
|
program.addCommand(loginCommand);
|
|
23703
23982
|
program.addCommand(logoutCommand);
|
|
23704
23983
|
program.addCommand(coursesCommand);
|
|
23984
|
+
program.addCommand(suggestCommand);
|
|
23705
23985
|
program.addCommand(enrollCommand);
|
|
23706
23986
|
program.addCommand(selectCommand);
|
|
23707
23987
|
program.addCommand(progressCommand);
|
|
@@ -23738,12 +24018,12 @@ function createProgram() {
|
|
|
23738
24018
|
|
|
23739
24019
|
// src/auto-updater.ts
|
|
23740
24020
|
init_config_dir();
|
|
23741
|
-
import
|
|
23742
|
-
import
|
|
24021
|
+
import fs24 from "node:fs";
|
|
24022
|
+
import path28 from "node:path";
|
|
23743
24023
|
import { spawn as spawn2 } from "node:child_process";
|
|
23744
24024
|
var PACKAGE_NAME2 = "@tostudy-ai/cli";
|
|
23745
24025
|
function detectManager(entryRealPath) {
|
|
23746
|
-
const p = entryRealPath.split(
|
|
24026
|
+
const p = entryRealPath.split(path28.sep).join("/");
|
|
23747
24027
|
if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
|
|
23748
24028
|
if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
|
|
23749
24029
|
if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
|
|
@@ -23751,10 +24031,10 @@ function detectManager(entryRealPath) {
|
|
|
23751
24031
|
return null;
|
|
23752
24032
|
}
|
|
23753
24033
|
function installDir(entryRealPath) {
|
|
23754
|
-
const normalized = entryRealPath.split(
|
|
24034
|
+
const normalized = entryRealPath.split(path28.sep).join("/");
|
|
23755
24035
|
const marker = `/node_modules/${PACKAGE_NAME2}/`;
|
|
23756
24036
|
const idx = normalized.indexOf(marker);
|
|
23757
|
-
if (idx === -1) return
|
|
24037
|
+
if (idx === -1) return path28.dirname(entryRealPath);
|
|
23758
24038
|
return entryRealPath.slice(0, idx + marker.length - 1);
|
|
23759
24039
|
}
|
|
23760
24040
|
function maybeAutoUpdate(currentVersion, opts = {}) {
|
|
@@ -23770,10 +24050,10 @@ function maybeAutoUpdate(currentVersion, opts = {}) {
|
|
|
23770
24050
|
if (cache.attemptedVersion === cache.latest) return;
|
|
23771
24051
|
const entryPath = opts.entryPath ?? argv[1];
|
|
23772
24052
|
if (!entryPath) return;
|
|
23773
|
-
const real =
|
|
24053
|
+
const real = fs24.realpathSync(entryPath);
|
|
23774
24054
|
const mgr = detectManager(real);
|
|
23775
24055
|
if (!mgr) return;
|
|
23776
|
-
|
|
24056
|
+
fs24.accessSync(installDir(real), fs24.constants.W_OK);
|
|
23777
24057
|
writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
|
|
23778
24058
|
process.stderr.write(
|
|
23779
24059
|
`
|