@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 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 Command38 } from "commander";
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.4" : "0.7.1";
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/enroll.ts
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 Command6("enroll").description(getErrors().enrollCommandDescription).argument("<courseId>", "Published course ID (UUID)").action(async (courseId) => {
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 Command7 } from "commander";
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 Command7("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(
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 Command8 } from "commander";
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 Command8("progress").description("Show your progress in the active course").option("--json", "Output structured JSON").action(async (opts) => {
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 Command9 } from "commander";
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 Command9("start").description("Start (or resume) the current module of the active course").option("--json", "Output structured JSON").action(async (opts) => {
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 Command10 } from "commander";
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 Command10("start-next").description("Transition to the next module after completing the current one").option("--json", "Output structured JSON").action(async (opts) => {
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 Command11 } from "commander";
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 Command11("next").description("Advance to the next lesson in the active course").option("--json", "Output structured JSON").action(async (opts) => {
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 Command12 } from "commander";
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 path13 from "node:path";
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 = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
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 Command12("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").option(
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 Command13 } from "commander";
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 Command13("knowledge").description("Show the active course's knowledge base (base de conhecimento)").option("--json", "Output structured JSON").action(async (opts) => {
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 Command14 } from "commander";
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 Command14("hint").description("Get a progressive hint for the current exercise").option("--json", "Output structured JSON").action(async (opts) => {
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 fs11 from "node:fs";
5431
- import path14 from "node:path";
5432
- import { Command as Command15 } from "commander";
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, path28) {
6222
- if (!path28)
6500
+ function getElementAtPath(obj, path29) {
6501
+ if (!path29)
6223
6502
  return obj;
6224
- return path28.reduce((acc, key) => acc?.[key], obj);
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(path28, issues) {
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(path28);
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, path28 = []) => {
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 = [...path28, ...issue2.path];
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 path28 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
6837
- for (const seg of path28) {
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 path28 = ref.slice(1).split("/").filter(Boolean);
18815
- if (path28.length === 0) {
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 (path28[0] === defsKey) {
18820
- const key = path28[1];
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 Command15("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) => {
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 = fs11.readFileSync("/dev/stdin", "utf-8");
19926
+ solution = fs12.readFileSync("/dev/stdin", "utf-8");
19648
19927
  } else if (file2) {
19649
- if (!fs11.existsSync(file2)) {
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 = fs11.readFileSync(file2, "utf-8");
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 = path14.extname(file2).toLowerCase();
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 Command16 } from "commander";
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 Command16("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) => {
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 Command17 } from "commander";
19824
- var menuCommand = new Command17("menu").description("Show available commands and current study context").action(async () => {
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 Command18 } from "commander";
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 Command18("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(
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 Command19 } from "commander";
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 fs12 from "node:fs/promises";
20210
- import path15 from "node:path";
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 = path15.join(input2.basePath, input2.courseSlug);
20492
+ const workspacePath = path16.join(input2.basePath, input2.courseSlug);
20214
20493
  for (const dir of WORKSPACE_DIRS) {
20215
- await fs12.mkdir(path15.join(workspacePath, dir), { recursive: true });
20494
+ await fs13.mkdir(path16.join(workspacePath, dir), { recursive: true });
20216
20495
  }
20217
- const configPath = path15.join(workspacePath, ".ana-config.json");
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 fs12.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
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 fs12.writeFile(path15.join(workspacePath, "README.md"), readme, "utf-8");
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 fs13 from "node:fs/promises";
20258
- import path16 from "node:path";
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 = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
20371
- await fs13.mkdir(exercisePath, { recursive: true });
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 = path16.join(exercisePath, cleanPath);
20378
- await fs13.mkdir(path16.dirname(fullPath), { recursive: true });
20379
- await fs13.writeFile(fullPath, fileData.code, "utf-8");
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 fs13.writeFile(path16.join(exercisePath, "exercise.js"), tierCode, "utf-8");
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 fs13.writeFile(path16.join(exercisePath, "exercise.js"), starter, "utf-8");
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 fs13.writeFile(
20412
- path16.join(exercisePath, "package.json"),
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 fs13.writeFile(path16.join(exercisePath, configFile), configContent, "utf-8");
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 fs13.writeFile(path16.join(exercisePath, "setup.sh"), setupSh, "utf-8");
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 = path16.join(exercisePath, "README.md");
20437
- await fs13.writeFile(readmePath, readme, "utf-8");
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 path17 from "node:path";
20772
+ import path18 from "node:path";
20494
20773
  import os8 from "node:os";
20495
- import fs14 from "node:fs/promises";
20774
+ import fs15 from "node:fs/promises";
20496
20775
  var logger19 = createLogger("cli:workspace");
20497
- var workspaceCommand = new Command19("workspace").description(
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 ?? path17.join(process.cwd(), ".tostudy");
20510
- await fs14.mkdir(workspacePath, { recursive: true });
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 fs14.mkdir(path17.join(workspacePath, dir), { recursive: true });
20792
+ await fs15.mkdir(path18.join(workspacePath, dir), { recursive: true });
20514
20793
  }
20515
- const configPath = path17.join(workspacePath, ".ana-config.json");
20516
- await fs14.writeFile(
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 ?? path17.join(os8.homedir(), "study");
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", path17.join(os8.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
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 fs14.readFile(path17.join(workspacePath, ".ana-config.json"), "utf-8");
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 = path17.join(workspacePath, "exercises");
20869
+ const exercisesDir = path18.join(workspacePath, "exercises");
20591
20870
  let exerciseCount = 0;
20592
20871
  try {
20593
- const moduleDirs = await fs14.readdir(exercisesDir);
20872
+ const moduleDirs = await fs15.readdir(exercisesDir);
20594
20873
  for (const modDir of moduleDirs) {
20595
- const modPath = path17.join(exercisesDir, modDir);
20596
- const stat = await fs14.stat(modPath);
20874
+ const modPath = path18.join(exercisesDir, modDir);
20875
+ const stat = await fs15.stat(modPath);
20597
20876
  if (stat.isDirectory()) {
20598
- const lessonDirs = await fs14.readdir(modPath);
20877
+ const lessonDirs = await fs15.readdir(modPath);
20599
20878
  for (const lessonDir of lessonDirs) {
20600
- const lessonPath2 = path17.join(modPath, lessonDir);
20601
- const lstat = await fs14.stat(lessonPath2);
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 = path17.join(workspacePath, "generated");
20887
+ const generatedDir = path18.join(workspacePath, "generated");
20609
20888
  let artifactCount = 0;
20610
20889
  try {
20611
- const files = await fs14.readdir(generatedDir);
20890
+ const files = await fs15.readdir(generatedDir);
20612
20891
  artifactCount = files.length;
20613
20892
  } catch {
20614
20893
  }
20615
- const diagramsDir = path17.join(workspacePath, "diagrams");
20894
+ const diagramsDir = path18.join(workspacePath, "diagrams");
20616
20895
  let diagramCount = 0;
20617
20896
  try {
20618
- const files = await fs14.readdir(diagramsDir);
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 Command20 } from "commander";
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 path18 from "node:path";
20949
+ import path19 from "node:path";
20671
20950
  import os9 from "node:os";
20672
- import fs15 from "node:fs/promises";
20951
+ import fs16 from "node:fs/promises";
20673
20952
  var logger20 = createLogger("cli:export");
20674
- var exportCommand = new Command20("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", path18.join(os9.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
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 = path18.join(ws.workspacePath, ".ana-config.json");
20971
+ const configPath = path19.join(ws.workspacePath, ".ana-config.json");
20693
20972
  let hasConfig = false;
20694
20973
  try {
20695
- await fs15.access(configPath);
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 fs15.mkdir(ws.workspacePath, { recursive: true });
20981
+ await fs16.mkdir(ws.workspacePath, { recursive: true });
20703
20982
  for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
20704
- await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
20983
+ await fs16.mkdir(path19.join(ws.workspacePath, dir), { recursive: true });
20705
20984
  }
20706
- await fs15.writeFile(
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 === path18.join(process.cwd(), ".tostudy");
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 Command21 } from "commander";
21058
+ import { Command as Command22 } from "commander";
20780
21059
  import { execFile as execFile3 } from "node:child_process";
20781
- import path19 from "node:path";
21060
+ import path20 from "node:path";
20782
21061
  import os10 from "node:os";
20783
21062
  var logger21 = createLogger("cli:open");
20784
- var openCommand = new Command21("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path19.join(os10.homedir(), "study")).action(async (opts) => {
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 Command22 } from "commander";
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 fs16 from "node:fs/promises";
20826
- import path20 from "node:path";
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 = path20.join(outputPath, file2.relativePath);
20836
- await fs16.mkdir(path20.dirname(fullPath), { recursive: true });
20837
- await fs16.writeFile(fullPath, file2.content, "utf-8");
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 = path20.join(outputPath, courseSlug2);
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 fs16.mkdir(vaultPath, { recursive: true });
20847
- await fs16.writeFile(
20848
- path20.join(vaultPath, VAULT_MARKER_FILENAME),
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 path21 from "node:path";
21140
+ import path22 from "node:path";
20862
21141
  import os11 from "node:os";
20863
- import fs17 from "node:fs/promises";
21142
+ import fs18 from "node:fs/promises";
20864
21143
  var logger22 = createLogger("cli:vault");
20865
- var vaultCommand = new Command22("vault").description("Gerenciar vault Obsidian do curso");
20866
- vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path21.join(os11.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
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", path21.join(os11.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
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 = path21.join(vaultPath, ".ana-vault.json");
20974
- const markerRaw = await fs17.readFile(markerPath, "utf-8");
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 fs17.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
20983
- const courseIndexPath = path21.join(vaultPath, slug, "index.md");
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 fs17.readFile(courseIndexPath, "utf-8");
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 fs17.writeFile(courseIndexPath, indexContent, "utf-8");
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 path25 from "node:path";
21038
- import { Command as Command24 } from "commander";
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 fs18 from "node:fs/promises";
21312
- import path22 from "node:path";
21313
- var STATE_FILE = path22.join(".tostudy", "creator.json");
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 fs18.readFile(path22.join(root, STATE_FILE), "utf-8"));
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 = path22.join(root, STATE_FILE);
21333
- await fs18.mkdir(path22.dirname(file2), { recursive: true });
21334
- await fs18.writeFile(file2, JSON.stringify(state, null, 2) + "\n", "utf-8");
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 fs19 from "node:fs/promises";
21630
+ import fs20 from "node:fs/promises";
21352
21631
  import os12 from "node:os";
21353
- import path23 from "node:path";
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 = path23.resolve(root, relPath);
21368
- const back = path23.relative(path23.resolve(root), abs);
21369
- const escapes = back === "" || back === ".." || back.startsWith(`..${path23.sep}`) || path23.isAbsolute(back);
21370
- if (path23.isAbsolute(relPath) || escapes) {
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 = path23.resolve(home);
21381
- const resolved = path23.resolve(dir);
21382
- const prefix = resolved.endsWith(path23.sep) ? resolved : resolved + path23.sep;
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 fs19.readFile(path23.join(root, MANIFEST_FILE2), "utf-8");
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(path23.resolve(dir));
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 fs19.realpath(path23.resolve(root, relPath));
21713
+ real = await fs20.realpath(path24.resolve(root, relPath));
21435
21714
  } catch {
21436
21715
  return null;
21437
21716
  }
21438
- if (!real.startsWith(realRoot + path23.sep)) {
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 fs19.readFile(real, "utf-8");
21724
+ return fs20.readFile(real, "utf-8");
21446
21725
  }
21447
21726
  async function readWorkspace(dir) {
21448
- const root = path23.resolve(dir);
21727
+ const root = path24.resolve(dir);
21449
21728
  const manifestRaw = await readManifest(root);
21450
- const realRoot = await fs19.realpath(root);
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 = path23.join(root, ".gitignore");
21463
- const current = await fs19.readFile(file2, "utf-8").catch(() => "");
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 fs19.writeFile(file2, `${current}${separator}${STATE_GITIGNORE_ENTRY}
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 = path23.resolve(dir);
21750
+ const root = path24.resolve(dir);
21472
21751
  assertNotHome(root, home);
21473
- await fs19.mkdir(root, { recursive: true });
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 fs19.writeFile(path23.join(root, MANIFEST_FILE2), JSON.stringify(manifest, null, 2) + "\n", {
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 fs19.mkdir(path23.join(root, "modules", "01"), { recursive: true });
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 fs20 from "node:fs/promises";
21551
- import path24 from "node:path";
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 fs20.access(file2);
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 = path24.resolve(root, rel);
21862
+ const abs = path25.resolve(root, rel);
21584
21863
  let target;
21585
21864
  try {
21586
- target = await fs20.lstat(abs);
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 = path24.dirname(abs);
21598
- while (!await exists(dir)) dir = path24.dirname(dir);
21599
- const realDir = await fs20.realpath(dir);
21600
- if (realDir !== realRoot && !realDir.startsWith(realRoot + path24.sep)) {
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 fs20.realpath(root);
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(path24.resolve(root, sidecarRel))) {
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 = path24.resolve(root, file2.rel);
21645
- await fs20.mkdir(path24.dirname(abs), { recursive: true });
21646
- await fs20.writeFile(abs, file2.text, "utf-8");
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 Command23 } from "commander";
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 Command23("brief-open").description("Open the learner brief editor in your web browser").action(async () => {
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 Command24("creator").description(
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(path25.resolve(process.cwd(), dir), home);
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 Command25 } from "commander";
22311
- var profileCommand = new Command25("profile").description("Show your learner profile for the active course").option("--json", "Output structured JSON").action(async (opts) => {
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 Command26 } from "commander";
22652
+ import { Command as Command27 } from "commander";
22374
22653
  init_workspace_state();
22375
22654
  var logger24 = createLogger("cli:sync");
22376
- var syncCommand = new Command26("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").option(
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 Command27 } from "commander";
22727
+ import { Command as Command28 } from "commander";
22449
22728
  init_formatter();
22450
22729
  var logger25 = createLogger("cli:brief");
22451
- var briefCommand = new Command27("brief").description("Show your base learner brief (T1) status and content").option("--json", "Output structured JSON").action(async (opts) => {
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 Command28 } from "commander";
22771
+ import { Command as Command29 } from "commander";
22493
22772
  init_formatter();
22494
22773
  var logger26 = createLogger("cli:brief-create");
22495
- var briefCreateCommand = new Command28("brief-create").description("Create your base learner brief via interactive prompts (T1 bootstrap)").action(async () => {
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 fs22 from "node:fs";
22529
- import { Command as Command29 } from "commander";
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 fs21 from "node:fs";
22534
- import path26 from "node:path";
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 path26.join(workspacePath, ".tostudy", "sessions");
22816
+ return path27.join(workspacePath, ".tostudy", "sessions");
22538
22817
  }
22539
22818
  async function saveModuleSummary(workspacePath, input2) {
22540
22819
  const dir = sessionsDir(workspacePath);
22541
- fs21.mkdirSync(dir, { recursive: true });
22820
+ fs22.mkdirSync(dir, { recursive: true });
22542
22821
  const filename = `module-${input2.moduleId}-summary.md`;
22543
- const filePath = path26.join(dir, filename);
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
- fs21.writeFileSync(filePath, header + input2.summary, { mode: 384 });
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 (!fs21.existsSync(dir)) return { moduleSummaries: [] };
22559
- const files = fs21.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
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 = fs21.readFileSync(path26.join(dir, file2), "utf-8");
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 Command29("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) => {
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 = fs22.readFileSync("/dev/stdin", "utf-8").trim();
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 Command30 } from "commander";
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 Command30("context").description("Load session context (workspace state + module summaries) for LLM consumption").option("--json", "Output structured JSON").action(async (opts) => {
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 Command31 } from "commander";
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 Command31("memory").description("Load accumulated student memory (learning profile + recent lessons) for the tutor").option("--json", "Output structured JSON").action(async (opts) => {
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 Command32 } from "commander";
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 Command32("insight").description(
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 Command33 } from "commander";
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 Command33("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) => {
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 Command34 } from "commander";
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 Command34("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) => {
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 Command35 } from "commander";
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 Command35("export-grades").description("Exporta o hist\xF3rico de valida\xE7\xF5es (notas) do curso ativo").option(
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 Command36 } from "commander";
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 Command36("grades").description("Mostra um resumo das notas (todas as matr\xEDculas ou um curso espec\xEDfico)").option(
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 Command37 } from "commander";
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 Command37("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(
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 Command38();
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 fs23 from "node:fs";
23742
- import path27 from "node:path";
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(path27.sep).join("/");
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(path27.sep).join("/");
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 path27.dirname(entryRealPath);
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 = fs23.realpathSync(entryPath);
24053
+ const real = fs24.realpathSync(entryPath);
23774
24054
  const mgr = detectManager(real);
23775
24055
  if (!mgr) return;
23776
- fs23.accessSync(installDir(real), fs23.constants.W_OK);
24056
+ fs24.accessSync(installDir(real), fs24.constants.W_OK);
23777
24057
  writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
23778
24058
  process.stderr.write(
23779
24059
  `