@tostudy-ai/cli 0.18.3 → 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
@@ -1024,6 +1024,18 @@ var init_errors_pt_br = __esm({
1024
1024
  setupCommandDescription: "Atalho antigo. A pasta de estudo \xE9 workspace setup; o MCP \xE9 setup mcp",
1025
1025
  setupRetiredNotice: "\n \u26A0\uFE0F `tostudy setup` n\xE3o cria a pasta de estudo.",
1026
1026
  setupRetiredNext: " Pasta de estudo: `tostudy workspace setup`. MCP: `tostudy setup mcp`. Para entrar: `tostudy login`, depois `tostudy courses` e `tostudy select <n>`.\n",
1027
+ enrollCommandDescription: "Matricula em um curso gratuito. Curso pago abre no navegador.",
1028
+ enrollInvalidId: "Informe o ID do curso (UUID). Exemplo: tostudy enroll 11111111-1111-4111-8111-111111111111",
1029
+ enrollDone: "Matriculado. Rode `tostudy courses` e depois `tostudy select <n>`.",
1030
+ enrollDoneWebOnly: "Matriculado. Este curso n\xE3o abre no terminal. Continue no navegador:",
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:",
1027
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",
1028
1040
  creator: {
1029
1041
  loginBrowserOnly: "`--creator` s\xF3 funciona no login pelo browser. Rode `tostudy login --creator` sem --code, --magic-link ou --manual.",
@@ -1121,6 +1133,18 @@ var init_errors_en_us = __esm({
1121
1133
  setupCommandDescription: "Old shortcut. The study folder is workspace setup; MCP is setup mcp",
1122
1134
  setupRetiredNotice: "\n \u26A0\uFE0F `tostudy setup` does not create the study folder.",
1123
1135
  setupRetiredNext: " Study folder: `tostudy workspace setup`. MCP: `tostudy setup mcp`. To sign in: `tostudy login`, then `tostudy courses` and `tostudy select <n>`.\n",
1136
+ enrollCommandDescription: "Enroll in a free course. A priced course opens in the browser.",
1137
+ enrollInvalidId: "Pass the course ID (UUID). Example: tostudy enroll 11111111-1111-4111-8111-111111111111",
1138
+ enrollDone: "Enrolled. Run `tostudy courses`, then `tostudy select <n>`.",
1139
+ enrollDoneWebOnly: "Enrolled. This course does not open in the terminal. Continue in the browser:",
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:",
1124
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",
1125
1149
  creator: {
1126
1150
  loginBrowserOnly: "`--creator` only works with the browser login. Run `tostudy login --creator` without --code, --magic-link or --manual.",
@@ -3140,7 +3164,7 @@ var init_guards = __esm({
3140
3164
  });
3141
3165
 
3142
3166
  // src/cli.ts
3143
- import { Command as Command37 } from "commander";
3167
+ import { Command as Command39 } from "commander";
3144
3168
 
3145
3169
  // src/commands/login.ts
3146
3170
  init_dist();
@@ -3786,7 +3810,7 @@ init_workspace_state();
3786
3810
  init_formatter();
3787
3811
 
3788
3812
  // src/version.ts
3789
- var CLI_VERSION = true ? "0.18.3" : "0.7.1";
3813
+ var CLI_VERSION = true ? "0.18.5" : "0.7.1";
3790
3814
 
3791
3815
  // src/update-checker.ts
3792
3816
  init_config_dir();
@@ -4282,9 +4306,338 @@ var coursesCommand = new Command5("courses").description("List your enrolled cou
4282
4306
  }
4283
4307
  });
4284
4308
 
4309
+ // src/commands/suggest.ts
4310
+ import { Command as Command6 } from "commander";
4311
+ init_guards();
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();
4598
+ init_formatter();
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;
4600
+ function formatEnrollResult(result) {
4601
+ const copy = getErrors();
4602
+ if (!result.enrolled) {
4603
+ return `${copy.enrollPaid}
4604
+ ${result.enrollUrl}`;
4605
+ }
4606
+ if (!result.cliChannel) {
4607
+ return `${copy.enrollDoneWebOnly}
4608
+ ${result.courseUrl}`;
4609
+ }
4610
+ return copy.enrollDone;
4611
+ }
4612
+ async function runEnroll(courseId, deps = {
4613
+ requireSession,
4614
+ request: ({ apiUrl, token, courseId: id }) => cliApiFetch(`${apiUrl}/api/cli/enrollments`, token, {
4615
+ method: "POST",
4616
+ body: JSON.stringify({ courseId: id })
4617
+ })
4618
+ }) {
4619
+ if (!COURSE_ID.test(courseId)) {
4620
+ throw new Error(getErrors().enrollInvalidId);
4621
+ }
4622
+ const session = await deps.requireSession();
4623
+ const result = await deps.request({
4624
+ apiUrl: session.apiUrl,
4625
+ token: session.token,
4626
+ courseId
4627
+ });
4628
+ return formatEnrollResult(result);
4629
+ }
4630
+ var enrollCommand = new Command7("enroll").description(getErrors().enrollCommandDescription).argument("<courseId>", "Published course ID (UUID)").action(async (courseId) => {
4631
+ try {
4632
+ output(await runEnroll(courseId), { json: false });
4633
+ } catch (err) {
4634
+ error(err instanceof Error ? err.message : String(err));
4635
+ }
4636
+ });
4637
+
4285
4638
  // src/commands/select.ts
4286
4639
  init_dist();
4287
- import { Command as Command6 } from "commander";
4640
+ import { Command as Command8 } from "commander";
4288
4641
  import os7 from "node:os";
4289
4642
  init_guards();
4290
4643
  init_workspace_marker();
@@ -4317,26 +4670,6 @@ init_dist();
4317
4670
  init_workspace_marker();
4318
4671
  init_workspace_state();
4319
4672
 
4320
- // src/learner-brief/api.ts
4321
- async function fetchLearnerBrief(input2) {
4322
- const response = await cliApiFetch(
4323
- `${input2.apiUrl}/api/cli/student/learner-brief`,
4324
- input2.token
4325
- );
4326
- return response.brief;
4327
- }
4328
- async function upsertLearnerBrief(input2) {
4329
- const response = await cliApiFetch(
4330
- `${input2.apiUrl}/api/cli/student/learner-brief`,
4331
- input2.token,
4332
- {
4333
- method: "POST",
4334
- body: JSON.stringify({ text: input2.text, source: input2.source })
4335
- }
4336
- );
4337
- return response.brief;
4338
- }
4339
-
4340
4673
  // src/learner-brief/bootstrap.ts
4341
4674
  import readline from "node:readline/promises";
4342
4675
  import { stdin as processStdin, stdout as processStdout } from "node:process";
@@ -4728,7 +5061,7 @@ function isInteractiveTerminal(json2) {
4728
5061
 
4729
5062
  // src/commands/select.ts
4730
5063
  var logger8 = createLogger("cli:select");
4731
- var selectCommand = new Command6("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(
4732
5065
  "--root-agents",
4733
5066
  "Add the ToStudy block to an existing AGENTS.md in this folder without asking"
4734
5067
  ).action(async (course, opts) => {
@@ -4856,13 +5189,13 @@ var selectCommand = new Command6("select").description("Activate a course by ID
4856
5189
 
4857
5190
  // src/commands/progress.ts
4858
5191
  init_dist();
4859
- import { Command as Command7 } from "commander";
5192
+ import { Command as Command9 } from "commander";
4860
5193
  init_guards();
4861
5194
  init_course_state();
4862
5195
  init_formatter();
4863
5196
  init_errors();
4864
5197
  var logger9 = createLogger("cli:progress");
4865
- var progressCommand = new Command7("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) => {
4866
5199
  try {
4867
5200
  const session = await requireSession();
4868
5201
  const activeCourse = await requireActiveCourse();
@@ -4888,7 +5221,7 @@ var progressCommand = new Command7("progress").description("Show your progress i
4888
5221
 
4889
5222
  // src/commands/start.ts
4890
5223
  init_dist();
4891
- import { Command as Command8 } from "commander";
5224
+ import { Command as Command10 } from "commander";
4892
5225
 
4893
5226
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/lessons/next-lesson.ts
4894
5227
  async function nextLesson(input2, deps) {
@@ -4997,20 +5330,20 @@ async function runStart(opts, deps = defaultDeps3) {
4997
5330
  deps.error(msg);
4998
5331
  }
4999
5332
  }
5000
- var startCommand = new Command8("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) => {
5001
5334
  await runStart(opts);
5002
5335
  });
5003
5336
 
5004
5337
  // src/commands/start-next.ts
5005
5338
  init_dist();
5006
- import { Command as Command9 } from "commander";
5339
+ import { Command as Command11 } from "commander";
5007
5340
  init_guards();
5008
5341
  init_course_state();
5009
5342
  init_workspace_state();
5010
5343
  init_formatter();
5011
5344
  init_errors();
5012
5345
  var logger11 = createLogger("cli:start-next");
5013
- var startNextCommand = new Command9("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) => {
5014
5347
  try {
5015
5348
  const session = await requireSession();
5016
5349
  const activeCourse = await requireActiveCourse();
@@ -5053,14 +5386,14 @@ var startNextCommand = new Command9("start-next").description("Transition to the
5053
5386
 
5054
5387
  // src/commands/next.ts
5055
5388
  init_dist();
5056
- import { Command as Command10 } from "commander";
5389
+ import { Command as Command12 } from "commander";
5057
5390
  init_guards();
5058
5391
  init_course_state();
5059
5392
  init_workspace_state();
5060
5393
  init_formatter();
5061
5394
  init_errors();
5062
5395
  var logger12 = createLogger("cli:next");
5063
- var nextCommand = new Command10("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) => {
5064
5397
  try {
5065
5398
  const session = await requireSession();
5066
5399
  const activeCourse = await requireActiveCourse();
@@ -5138,14 +5471,14 @@ var nextCommand = new Command10("next").description("Advance to the next lesson
5138
5471
 
5139
5472
  // src/commands/lesson.ts
5140
5473
  init_dist();
5141
- import { Command as Command11 } from "commander";
5474
+ import { Command as Command13 } from "commander";
5142
5475
  init_guards();
5143
5476
  init_course_state();
5144
5477
  init_formatter();
5145
5478
  init_resolve();
5146
5479
  init_workspace_tokens();
5147
5480
  init_errors();
5148
- import path13 from "node:path";
5481
+ import path14 from "node:path";
5149
5482
  var logger13 = createLogger("cli:lesson");
5150
5483
  function adjustTimeEstimate(type, baseMinutes) {
5151
5484
  if (type === "exercise") return Math.max(baseMinutes, 30);
@@ -5162,7 +5495,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
5162
5495
  const isPractical = isPracticalType(data.type) || isCheckpoint(data.type, data.title);
5163
5496
  const lines = [`\u2501\u2501\u2501 Li\xE7\xE3o: ${data.title} \u2501\u2501\u2501`];
5164
5497
  if (isPractical && workspacePath) {
5165
- const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
5498
+ const base = path14.basename(workspacePath) === ".tostudy" ? path14.dirname(workspacePath) : workspacePath;
5166
5499
  const slug = courseSlug(courseTitle);
5167
5500
  const vaultPath = resolveVaultPath(workspacePath, slug);
5168
5501
  lines.push(
@@ -5196,7 +5529,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
5196
5529
  }
5197
5530
  return lines.join("\n");
5198
5531
  }
5199
- var lessonCommand = new Command11("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(
5200
5533
  "--read <lessonId>",
5201
5534
  "Read a specific lesson by id (works on archived / read-only courses)"
5202
5535
  ).action(async (opts) => {
@@ -5267,7 +5600,7 @@ var lessonCommand = new Command11("lesson").description("Show the content of the
5267
5600
 
5268
5601
  // src/commands/knowledge.ts
5269
5602
  init_dist();
5270
- import { Command as Command12 } from "commander";
5603
+ import { Command as Command14 } from "commander";
5271
5604
  init_guards();
5272
5605
  init_formatter();
5273
5606
  var logger14 = createLogger("cli:knowledge");
@@ -5287,7 +5620,7 @@ function formatKnowledge(data) {
5287
5620
  });
5288
5621
  return lines.join("\n").trimEnd();
5289
5622
  }
5290
- var knowledgeCommand = new Command12("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) => {
5291
5624
  try {
5292
5625
  const activeCourse = await requireActiveCourse();
5293
5626
  if (!activeCourse.courseId) {
@@ -5318,13 +5651,13 @@ var knowledgeCommand = new Command12("knowledge").description("Show the active c
5318
5651
 
5319
5652
  // src/commands/hint.ts
5320
5653
  init_dist();
5321
- import { Command as Command13 } from "commander";
5654
+ import { Command as Command15 } from "commander";
5322
5655
  init_guards();
5323
5656
  init_course_state();
5324
5657
  init_formatter();
5325
5658
  init_errors();
5326
5659
  var logger15 = createLogger("cli:hint");
5327
- var hintCommand = new Command13("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) => {
5328
5661
  try {
5329
5662
  const session = await requireSession();
5330
5663
  const activeCourse = await requireActiveCourse();
@@ -5373,9 +5706,9 @@ var hintCommand = new Command13("hint").description("Get a progressive hint for
5373
5706
 
5374
5707
  // src/commands/validate.ts
5375
5708
  init_dist();
5376
- import fs11 from "node:fs";
5377
- import path14 from "node:path";
5378
- import { Command as Command14 } from "commander";
5709
+ import fs12 from "node:fs";
5710
+ import path15 from "node:path";
5711
+ import { Command as Command16 } from "commander";
5379
5712
 
5380
5713
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/exercises/validate-solution.ts
5381
5714
  async function validateSolution(input2, deps) {
@@ -6164,10 +6497,10 @@ function mergeDefs(...defs) {
6164
6497
  function cloneDef(schema) {
6165
6498
  return mergeDefs(schema._zod.def);
6166
6499
  }
6167
- function getElementAtPath(obj, path28) {
6168
- if (!path28)
6500
+ function getElementAtPath(obj, path29) {
6501
+ if (!path29)
6169
6502
  return obj;
6170
- return path28.reduce((acc, key) => acc?.[key], obj);
6503
+ return path29.reduce((acc, key) => acc?.[key], obj);
6171
6504
  }
6172
6505
  function promiseAllObject(promisesObj) {
6173
6506
  const keys = Object.keys(promisesObj);
@@ -6550,11 +6883,11 @@ function aborted(x, startIndex = 0) {
6550
6883
  }
6551
6884
  return false;
6552
6885
  }
6553
- function prefixIssues(path28, issues) {
6886
+ function prefixIssues(path29, issues) {
6554
6887
  return issues.map((iss) => {
6555
6888
  var _a2;
6556
6889
  (_a2 = iss).path ?? (_a2.path = []);
6557
- iss.path.unshift(path28);
6890
+ iss.path.unshift(path29);
6558
6891
  return iss;
6559
6892
  });
6560
6893
  }
@@ -6737,7 +7070,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
6737
7070
  }
6738
7071
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
6739
7072
  const result = { errors: [] };
6740
- const processError = (error50, path28 = []) => {
7073
+ const processError = (error50, path29 = []) => {
6741
7074
  var _a2, _b;
6742
7075
  for (const issue2 of error50.issues) {
6743
7076
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -6747,7 +7080,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
6747
7080
  } else if (issue2.code === "invalid_element") {
6748
7081
  processError({ issues: issue2.issues }, issue2.path);
6749
7082
  } else {
6750
- const fullpath = [...path28, ...issue2.path];
7083
+ const fullpath = [...path29, ...issue2.path];
6751
7084
  if (fullpath.length === 0) {
6752
7085
  result.errors.push(mapper(issue2));
6753
7086
  continue;
@@ -6779,8 +7112,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
6779
7112
  }
6780
7113
  function toDotPath(_path) {
6781
7114
  const segs = [];
6782
- const path28 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
6783
- for (const seg of path28) {
7115
+ const path29 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7116
+ for (const seg of path29) {
6784
7117
  if (typeof seg === "number")
6785
7118
  segs.push(`[${seg}]`);
6786
7119
  else if (typeof seg === "symbol")
@@ -18757,13 +19090,13 @@ function resolveRef(ref, ctx) {
18757
19090
  if (!ref.startsWith("#")) {
18758
19091
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
18759
19092
  }
18760
- const path28 = ref.slice(1).split("/").filter(Boolean);
18761
- if (path28.length === 0) {
19093
+ const path29 = ref.slice(1).split("/").filter(Boolean);
19094
+ if (path29.length === 0) {
18762
19095
  return ctx.rootSchema;
18763
19096
  }
18764
19097
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
18765
- if (path28[0] === defsKey) {
18766
- const key = path28[1];
19098
+ if (path29[0] === defsKey) {
19099
+ const key = path29[1];
18767
19100
  if (!key || !ctx.defs[key]) {
18768
19101
  throw new Error(`Reference not found: ${ref}`);
18769
19102
  }
@@ -19561,7 +19894,7 @@ async function clearRetryPin() {
19561
19894
  await updateWorkspaceState(ws.workspacePath, { retryLessonId: void 0 });
19562
19895
  }
19563
19896
  }
19564
- var validateCommand = new Command14("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) => {
19565
19898
  try {
19566
19899
  const session = await requireSession();
19567
19900
  const activeCourse = await requireActiveCourse();
@@ -19590,21 +19923,21 @@ var validateCommand = new Command14("validate").description("Validate your solut
19590
19923
  }
19591
19924
  let solution;
19592
19925
  if (opts.stdin) {
19593
- solution = fs11.readFileSync("/dev/stdin", "utf-8");
19926
+ solution = fs12.readFileSync("/dev/stdin", "utf-8");
19594
19927
  } else if (file2) {
19595
- if (!fs11.existsSync(file2)) {
19928
+ if (!fs12.existsSync(file2)) {
19596
19929
  if (opts.json)
19597
19930
  jsonError("file_not_found", { message: `Arquivo n\xE3o encontrado: ${file2}` });
19598
19931
  error(`Arquivo n\xE3o encontrado: ${file2}`);
19599
19932
  }
19600
- solution = fs11.readFileSync(file2, "utf-8");
19933
+ solution = fs12.readFileSync(file2, "utf-8");
19601
19934
  } else {
19602
19935
  if (opts.json)
19603
19936
  jsonError("no_solution_provided", { message: "Forne\xE7a um arquivo ou use --stdin" });
19604
19937
  error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
19605
19938
  }
19606
19939
  if (file2 && activeCourse.courseTags?.length) {
19607
- const ext = path14.extname(file2).toLowerCase();
19940
+ const ext = path15.extname(file2).toLowerCase();
19608
19941
  const LANG_EXTENSIONS = {
19609
19942
  ".html": ["html", "html5"],
19610
19943
  ".css": ["css"],
@@ -19699,13 +20032,13 @@ var validateCommand = new Command14("validate").description("Validate your solut
19699
20032
 
19700
20033
  // src/commands/retry.ts
19701
20034
  init_dist();
19702
- import { Command as Command15 } from "commander";
20035
+ import { Command as Command17 } from "commander";
19703
20036
  init_guards();
19704
20037
  init_workspace_state();
19705
20038
  init_formatter();
19706
20039
  var logger17 = createLogger("cli:retry");
19707
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;
19708
- var retryCommand = new Command15("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) => {
19709
20042
  try {
19710
20043
  if (opts.clear) {
19711
20044
  const ws2 = await findWorkspaceState();
@@ -19766,8 +20099,8 @@ var retryCommand = new Command15("retry").description("Revalidate a lesson you a
19766
20099
  init_session_store();
19767
20100
  init_workspace_state();
19768
20101
  init_formatter();
19769
- import { Command as Command16 } from "commander";
19770
- var menuCommand = new Command16("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 () => {
19771
20104
  const session = await getSession();
19772
20105
  const wsResult = session ? await findWorkspaceState() : null;
19773
20106
  const activeCourse = wsResult?.state ?? null;
@@ -19824,7 +20157,7 @@ var menuCommand = new Command16("menu").description("Show available commands and
19824
20157
 
19825
20158
  // src/commands/init.ts
19826
20159
  init_dist();
19827
- import { Command as Command17 } from "commander";
20160
+ import { Command as Command19 } from "commander";
19828
20161
  init_session_store();
19829
20162
  init_course_state();
19830
20163
  init_workspace_state();
@@ -20140,7 +20473,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
20140
20473
  deps.output(artifacts.learnerBrief, { json: false });
20141
20474
  }
20142
20475
  }
20143
- var initCommand = new Command17("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(
20144
20477
  "--root-agents",
20145
20478
  "Add the ToStudy block to an existing AGENTS.md in this folder without asking"
20146
20479
  ).option("--json", "Output structured JSON").action(async (opts) => {
@@ -20149,18 +20482,18 @@ var initCommand = new Command17("init").description("Generate tutor instructions
20149
20482
 
20150
20483
  // src/commands/workspace.ts
20151
20484
  init_dist();
20152
- import { Command as Command18 } from "commander";
20485
+ import { Command as Command20 } from "commander";
20153
20486
 
20154
20487
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/setup-workspace.ts
20155
- import fs12 from "node:fs/promises";
20156
- import path15 from "node:path";
20488
+ import fs13 from "node:fs/promises";
20489
+ import path16 from "node:path";
20157
20490
  var WORKSPACE_DIRS = ["exercises", "generated", "notes", "diagrams", "vault"];
20158
20491
  async function setupWorkspace(input2) {
20159
- const workspacePath = path15.join(input2.basePath, input2.courseSlug);
20492
+ const workspacePath = path16.join(input2.basePath, input2.courseSlug);
20160
20493
  for (const dir of WORKSPACE_DIRS) {
20161
- await fs12.mkdir(path15.join(workspacePath, dir), { recursive: true });
20494
+ await fs13.mkdir(path16.join(workspacePath, dir), { recursive: true });
20162
20495
  }
20163
- const configPath = path15.join(workspacePath, ".ana-config.json");
20496
+ const configPath = path16.join(workspacePath, ".ana-config.json");
20164
20497
  const config2 = {
20165
20498
  courseId: input2.courseId,
20166
20499
  courseSlug: input2.courseSlug,
@@ -20170,7 +20503,7 @@ async function setupWorkspace(input2) {
20170
20503
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
20171
20504
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
20172
20505
  };
20173
- await fs12.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20506
+ await fs13.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20174
20507
  const readme = [
20175
20508
  `# ${input2.courseName}`,
20176
20509
  "",
@@ -20195,13 +20528,13 @@ async function setupWorkspace(input2) {
20195
20528
  "tostudy vault sync # Sincronizar progresso",
20196
20529
  "```"
20197
20530
  ].join("\n");
20198
- await fs12.writeFile(path15.join(workspacePath, "README.md"), readme, "utf-8");
20531
+ await fs13.writeFile(path16.join(workspacePath, "README.md"), readme, "utf-8");
20199
20532
  return { workspacePath, directories: WORKSPACE_DIRS, configPath };
20200
20533
  }
20201
20534
 
20202
20535
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/extract-exercise.ts
20203
- import fs13 from "node:fs/promises";
20204
- import path16 from "node:path";
20536
+ import fs14 from "node:fs/promises";
20537
+ import path17 from "node:path";
20205
20538
 
20206
20539
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/workspace/templates.ts
20207
20540
  var VITE_VERSION = "^6.0.0";
@@ -20313,16 +20646,16 @@ async function extractExercise(input2) {
20313
20646
  const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
20314
20647
  const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
20315
20648
  const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
20316
- const exercisePath = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
20317
- await fs13.mkdir(exercisePath, { recursive: true });
20649
+ const exercisePath = path17.join(workspacePath, "exercises", moduleDir, lessonDir);
20650
+ await fs14.mkdir(exercisePath, { recursive: true });
20318
20651
  const extractedFiles = [];
20319
20652
  let hasStarterCode = false;
20320
20653
  if (lessonData.sandpackConfig?.files) {
20321
20654
  for (const [filePath, fileData] of Object.entries(lessonData.sandpackConfig.files)) {
20322
20655
  const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
20323
- const fullPath = path16.join(exercisePath, cleanPath);
20324
- await fs13.mkdir(path16.dirname(fullPath), { recursive: true });
20325
- 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");
20326
20659
  extractedFiles.push(cleanPath);
20327
20660
  hasStarterCode = true;
20328
20661
  }
@@ -20330,13 +20663,13 @@ async function extractExercise(input2) {
20330
20663
  const tierData = getTierData(lessonData.structuredData, exerciseTier2);
20331
20664
  const tierCode = tierData?.code;
20332
20665
  if (tierCode) {
20333
- await fs13.writeFile(path16.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20666
+ await fs14.writeFile(path17.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20334
20667
  extractedFiles.push("exercise.js");
20335
20668
  hasStarterCode = true;
20336
20669
  } else {
20337
20670
  const starter = getStarterCode(lessonData.structuredData);
20338
20671
  if (starter) {
20339
- await fs13.writeFile(path16.join(exercisePath, "exercise.js"), starter, "utf-8");
20672
+ await fs14.writeFile(path17.join(exercisePath, "exercise.js"), starter, "utf-8");
20340
20673
  extractedFiles.push("exercise.js");
20341
20674
  hasStarterCode = true;
20342
20675
  }
@@ -20354,8 +20687,8 @@ async function extractExercise(input2) {
20354
20687
  ...exerciseDeps
20355
20688
  }
20356
20689
  };
20357
- await fs13.writeFile(
20358
- path16.join(exercisePath, "package.json"),
20690
+ await fs14.writeFile(
20691
+ path17.join(exercisePath, "package.json"),
20359
20692
  JSON.stringify(pkgJson, null, 2),
20360
20693
  "utf-8"
20361
20694
  );
@@ -20367,20 +20700,20 @@ async function extractExercise(input2) {
20367
20700
  );
20368
20701
  for (const [configFile, configContent] of Object.entries(scaffold.configs)) {
20369
20702
  if (!sandpackFileNames.has(configFile)) {
20370
- await fs13.writeFile(path16.join(exercisePath, configFile), configContent, "utf-8");
20703
+ await fs14.writeFile(path17.join(exercisePath, configFile), configContent, "utf-8");
20371
20704
  extractedFiles.push(configFile);
20372
20705
  }
20373
20706
  }
20374
20707
  const setupSh = `#!/bin/sh
20375
20708
  ${scaffold.setupScript}
20376
20709
  `;
20377
- await fs13.writeFile(path16.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20710
+ await fs14.writeFile(path17.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20378
20711
  extractedFiles.push("setup.sh");
20379
20712
  }
20380
20713
  }
20381
20714
  const readme = generateReadme(lessonData, exerciseTier2);
20382
- const readmePath = path16.join(exercisePath, "README.md");
20383
- await fs13.writeFile(readmePath, readme, "utf-8");
20715
+ const readmePath = path17.join(exercisePath, "README.md");
20716
+ await fs14.writeFile(readmePath, readme, "utf-8");
20384
20717
  extractedFiles.push("README.md");
20385
20718
  return {
20386
20719
  exercisePath,
@@ -20436,11 +20769,11 @@ init_guards();
20436
20769
  init_course_state();
20437
20770
  init_resolve();
20438
20771
  init_errors();
20439
- import path17 from "node:path";
20772
+ import path18 from "node:path";
20440
20773
  import os8 from "node:os";
20441
- import fs14 from "node:fs/promises";
20774
+ import fs15 from "node:fs/promises";
20442
20775
  var logger19 = createLogger("cli:workspace");
20443
- var workspaceCommand = new Command18("workspace").description(
20776
+ var workspaceCommand = new Command20("workspace").description(
20444
20777
  getErrors().workspaceCommandDescription
20445
20778
  );
20446
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) => {
@@ -20452,14 +20785,14 @@ workspaceCommand.command("setup").description(getErrors().workspaceSetupDescript
20452
20785
  let directories;
20453
20786
  if (!opts.path && cwdIsWorkspace) {
20454
20787
  const resolvedCwd = await resolveCwdWorkspacePath(process.cwd());
20455
- workspacePath = resolvedCwd ?? path17.join(process.cwd(), ".tostudy");
20456
- await fs14.mkdir(workspacePath, { recursive: true });
20788
+ workspacePath = resolvedCwd ?? path18.join(process.cwd(), ".tostudy");
20789
+ await fs15.mkdir(workspacePath, { recursive: true });
20457
20790
  directories = ["exercises", "generated", "notes", "diagrams"];
20458
20791
  for (const dir of directories) {
20459
- await fs14.mkdir(path17.join(workspacePath, dir), { recursive: true });
20792
+ await fs15.mkdir(path18.join(workspacePath, dir), { recursive: true });
20460
20793
  }
20461
- const configPath = path17.join(workspacePath, ".ana-config.json");
20462
- await fs14.writeFile(
20794
+ const configPath = path18.join(workspacePath, ".ana-config.json");
20795
+ await fs15.writeFile(
20463
20796
  configPath,
20464
20797
  JSON.stringify(
20465
20798
  {
@@ -20477,7 +20810,7 @@ workspaceCommand.command("setup").description(getErrors().workspaceSetupDescript
20477
20810
  "utf-8"
20478
20811
  );
20479
20812
  } else {
20480
- const basePath = opts.path ?? path17.join(os8.homedir(), "study");
20813
+ const basePath = opts.path ?? path18.join(os8.homedir(), "study");
20481
20814
  const result2 = await setupWorkspace({
20482
20815
  courseId: activeCourse.courseId,
20483
20816
  courseSlug: courseSlug(activeCourse.courseTitle),
@@ -20511,7 +20844,7 @@ Pr\xF3ximo passo: tostudy export
20511
20844
  process.exit(1);
20512
20845
  }
20513
20846
  });
20514
- 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) => {
20515
20848
  try {
20516
20849
  const activeCourse = await requireActiveCourse();
20517
20850
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -20528,40 +20861,40 @@ workspaceCommand.command("status").description("Mostrar status do workspace do c
20528
20861
  const workspacePath = ws.workspacePath;
20529
20862
  let configData = null;
20530
20863
  try {
20531
- 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");
20532
20865
  configData = JSON.parse(raw2);
20533
20866
  } catch {
20534
20867
  configData = null;
20535
20868
  }
20536
- const exercisesDir = path17.join(workspacePath, "exercises");
20869
+ const exercisesDir = path18.join(workspacePath, "exercises");
20537
20870
  let exerciseCount = 0;
20538
20871
  try {
20539
- const moduleDirs = await fs14.readdir(exercisesDir);
20872
+ const moduleDirs = await fs15.readdir(exercisesDir);
20540
20873
  for (const modDir of moduleDirs) {
20541
- const modPath = path17.join(exercisesDir, modDir);
20542
- const stat = await fs14.stat(modPath);
20874
+ const modPath = path18.join(exercisesDir, modDir);
20875
+ const stat = await fs15.stat(modPath);
20543
20876
  if (stat.isDirectory()) {
20544
- const lessonDirs = await fs14.readdir(modPath);
20877
+ const lessonDirs = await fs15.readdir(modPath);
20545
20878
  for (const lessonDir of lessonDirs) {
20546
- const lessonPath2 = path17.join(modPath, lessonDir);
20547
- const lstat = await fs14.stat(lessonPath2);
20879
+ const lessonPath2 = path18.join(modPath, lessonDir);
20880
+ const lstat = await fs15.stat(lessonPath2);
20548
20881
  if (lstat.isDirectory()) exerciseCount++;
20549
20882
  }
20550
20883
  }
20551
20884
  }
20552
20885
  } catch {
20553
20886
  }
20554
- const generatedDir = path17.join(workspacePath, "generated");
20887
+ const generatedDir = path18.join(workspacePath, "generated");
20555
20888
  let artifactCount = 0;
20556
20889
  try {
20557
- const files = await fs14.readdir(generatedDir);
20890
+ const files = await fs15.readdir(generatedDir);
20558
20891
  artifactCount = files.length;
20559
20892
  } catch {
20560
20893
  }
20561
- const diagramsDir = path17.join(workspacePath, "diagrams");
20894
+ const diagramsDir = path18.join(workspacePath, "diagrams");
20562
20895
  let diagramCount = 0;
20563
20896
  try {
20564
- const files = await fs14.readdir(diagramsDir);
20897
+ const files = await fs15.readdir(diagramsDir);
20565
20898
  diagramCount = files.length;
20566
20899
  } catch {
20567
20900
  }
@@ -20608,16 +20941,16 @@ workspaceCommand.command("status").description("Mostrar status do workspace do c
20608
20941
 
20609
20942
  // src/commands/export.ts
20610
20943
  init_dist();
20611
- import { Command as Command19 } from "commander";
20944
+ import { Command as Command21 } from "commander";
20612
20945
  init_guards();
20613
20946
  init_course_state();
20614
20947
  init_resolve();
20615
20948
  init_errors();
20616
- import path18 from "node:path";
20949
+ import path19 from "node:path";
20617
20950
  import os9 from "node:os";
20618
- import fs15 from "node:fs/promises";
20951
+ import fs16 from "node:fs/promises";
20619
20952
  var logger20 = createLogger("cli:export");
20620
- var exportCommand = new Command19("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) => {
20621
20954
  try {
20622
20955
  const session = await requireSession();
20623
20956
  const activeCourse = await requireActiveCourse();
@@ -20635,21 +20968,21 @@ var exportCommand = new Command19("export").description("Extrair exerc\xEDcio at
20635
20968
  opts.path
20636
20969
  );
20637
20970
  if (ws.found && ws.source === "cwd" && ws.workspacePath) {
20638
- const configPath = path18.join(ws.workspacePath, ".ana-config.json");
20971
+ const configPath = path19.join(ws.workspacePath, ".ana-config.json");
20639
20972
  let hasConfig = false;
20640
20973
  try {
20641
- await fs15.access(configPath);
20974
+ await fs16.access(configPath);
20642
20975
  hasConfig = true;
20643
20976
  } catch {
20644
20977
  }
20645
20978
  if (!hasConfig) {
20646
20979
  const slug = courseSlug(activeCourse.courseTitle);
20647
20980
  logger20.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
20648
- await fs15.mkdir(ws.workspacePath, { recursive: true });
20981
+ await fs16.mkdir(ws.workspacePath, { recursive: true });
20649
20982
  for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
20650
- await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
20983
+ await fs16.mkdir(path19.join(ws.workspacePath, dir), { recursive: true });
20651
20984
  }
20652
- await fs15.writeFile(
20985
+ await fs16.writeFile(
20653
20986
  configPath,
20654
20987
  JSON.stringify(
20655
20988
  {
@@ -20667,7 +21000,7 @@ var exportCommand = new Command19("export").description("Extrair exerc\xEDcio at
20667
21000
  "utf-8"
20668
21001
  );
20669
21002
  await setCourseWorkspacePath(activeCourse.courseId, ws.workspacePath);
20670
- const isNamespaced = ws.workspacePath === path18.join(process.cwd(), ".tostudy");
21003
+ const isNamespaced = ws.workspacePath === path19.join(process.cwd(), ".tostudy");
20671
21004
  process.stderr.write(
20672
21005
  isNamespaced ? `\u2728 Workspace inicializado em .tostudy/ (isolado do projeto).
20673
21006
  ` : `\u2728 Workspace inicializado nesta pasta.
@@ -20722,12 +21055,12 @@ init_guards();
20722
21055
  init_course_state();
20723
21056
  init_resolve();
20724
21057
  init_errors();
20725
- import { Command as Command20 } from "commander";
21058
+ import { Command as Command22 } from "commander";
20726
21059
  import { execFile as execFile3 } from "node:child_process";
20727
- import path19 from "node:path";
21060
+ import path20 from "node:path";
20728
21061
  import os10 from "node:os";
20729
21062
  var logger21 = createLogger("cli:open");
20730
- var openCommand = new Command20("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) => {
20731
21064
  try {
20732
21065
  const activeCourse = await requireActiveCourse();
20733
21066
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -20762,14 +21095,14 @@ var openCommand = new Command20("open").description("Abrir workspace do curso na
20762
21095
 
20763
21096
  // src/commands/vault.ts
20764
21097
  init_dist();
20765
- import { Command as Command21 } from "commander";
21098
+ import { Command as Command23 } from "commander";
20766
21099
 
20767
21100
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/generate-vault-files.ts
20768
21101
  init_slug();
20769
21102
 
20770
21103
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/write-vault.ts
20771
- import fs16 from "node:fs/promises";
20772
- import path20 from "node:path";
21104
+ import fs17 from "node:fs/promises";
21105
+ import path21 from "node:path";
20773
21106
 
20774
21107
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/types.ts
20775
21108
  var VAULT_MARKER_FILENAME = ".ana-vault.json";
@@ -20778,20 +21111,20 @@ var VAULT_MARKER_VERSION = 1;
20778
21111
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/tostudy-core/src/vault/write-vault.ts
20779
21112
  async function writeVaultFiles(files, outputPath, courseId, courseSlug2) {
20780
21113
  for (const file2 of files) {
20781
- const fullPath = path20.join(outputPath, file2.relativePath);
20782
- await fs16.mkdir(path20.dirname(fullPath), { recursive: true });
20783
- 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");
20784
21117
  }
20785
- const vaultPath = path20.join(outputPath, courseSlug2);
21118
+ const vaultPath = path21.join(outputPath, courseSlug2);
20786
21119
  const marker = {
20787
21120
  courseId,
20788
21121
  courseSlug: courseSlug2,
20789
21122
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
20790
21123
  version: VAULT_MARKER_VERSION
20791
21124
  };
20792
- await fs16.mkdir(vaultPath, { recursive: true });
20793
- await fs16.writeFile(
20794
- path20.join(vaultPath, VAULT_MARKER_FILENAME),
21125
+ await fs17.mkdir(vaultPath, { recursive: true });
21126
+ await fs17.writeFile(
21127
+ path21.join(vaultPath, VAULT_MARKER_FILENAME),
20795
21128
  JSON.stringify(marker, null, 2),
20796
21129
  "utf-8"
20797
21130
  );
@@ -20804,12 +21137,12 @@ init_guards();
20804
21137
  init_course_state();
20805
21138
  init_resolve();
20806
21139
  init_errors();
20807
- import path21 from "node:path";
21140
+ import path22 from "node:path";
20808
21141
  import os11 from "node:os";
20809
- import fs17 from "node:fs/promises";
21142
+ import fs18 from "node:fs/promises";
20810
21143
  var logger22 = createLogger("cli:vault");
20811
- var vaultCommand = new Command21("vault").description("Gerenciar vault Obsidian do curso");
20812
- 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) => {
20813
21146
  try {
20814
21147
  const session = await requireSession();
20815
21148
  const activeCourse = await requireActiveCourse();
@@ -20890,7 +21223,7 @@ Para visualizar:
20890
21223
  process.exit(1);
20891
21224
  }
20892
21225
  });
20893
- 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) => {
20894
21227
  try {
20895
21228
  const session = await requireSession();
20896
21229
  const activeCourse = await requireActiveCourse();
@@ -20916,8 +21249,8 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
20916
21249
  const data = createHttpProvider(session.apiUrl, session.token);
20917
21250
  const deps = { data, logger: logger22 };
20918
21251
  const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
20919
- const markerPath = path21.join(vaultPath, ".ana-vault.json");
20920
- 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");
20921
21254
  const marker = JSON.parse(markerRaw);
20922
21255
  marker.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
20923
21256
  marker.progress = {
@@ -20925,10 +21258,10 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
20925
21258
  currentModule: progress.currentModule.title,
20926
21259
  currentLesson: progress.currentLesson.title
20927
21260
  };
20928
- await fs17.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
20929
- 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");
20930
21263
  try {
20931
- let indexContent = await fs17.readFile(courseIndexPath, "utf-8");
21264
+ let indexContent = await fs18.readFile(courseIndexPath, "utf-8");
20932
21265
  indexContent = indexContent.replace(/\n---\n\n> 📊 Progresso:.*\n/g, "");
20933
21266
  const titleEnd = indexContent.indexOf("\n");
20934
21267
  if (titleEnd !== -1) {
@@ -20939,7 +21272,7 @@ vaultCommand.command("sync").description("Sincronizar progresso do curso com o v
20939
21272
  `;
20940
21273
  indexContent = indexContent.slice(0, titleEnd) + banner + indexContent.slice(titleEnd);
20941
21274
  }
20942
- await fs17.writeFile(courseIndexPath, indexContent, "utf-8");
21275
+ await fs18.writeFile(courseIndexPath, indexContent, "utf-8");
20943
21276
  } catch {
20944
21277
  }
20945
21278
  const syncedAt = marker.lastSyncedAt;
@@ -20980,8 +21313,8 @@ init_dist();
20980
21313
  init_formatter();
20981
21314
  init_errors();
20982
21315
  import os13 from "node:os";
20983
- import path25 from "node:path";
20984
- import { Command as Command23 } from "commander";
21316
+ import path26 from "node:path";
21317
+ import { Command as Command25 } from "commander";
20985
21318
 
20986
21319
  // ../../../../../Users/cleitonparis/www/pg/apps/cursos/packages/validators/src/authored-course.ts
20987
21320
  import { createHash } from "node:crypto";
@@ -21254,12 +21587,12 @@ function assembleAuthoredCourse(manifest, files) {
21254
21587
  init_guards();
21255
21588
 
21256
21589
  // src/creator/state.ts
21257
- import fs18 from "node:fs/promises";
21258
- import path22 from "node:path";
21259
- 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");
21260
21593
  async function readCreatorState(root) {
21261
21594
  try {
21262
- 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"));
21263
21596
  const hashes = parsed.baseHashes;
21264
21597
  const wellFormed = Array.isArray(hashes) && hashes.every(
21265
21598
  (mod) => Array.isArray(mod) && // wire shape of authoredCoursePushSchema.baseHashes; anything else is not a base we can send
@@ -21275,9 +21608,9 @@ async function readCreatorState(root) {
21275
21608
  }
21276
21609
  }
21277
21610
  async function writeCreatorState(root, state) {
21278
- const file2 = path22.join(root, STATE_FILE);
21279
- await fs18.mkdir(path22.dirname(file2), { recursive: true });
21280
- 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");
21281
21614
  }
21282
21615
  function changedSlots(baseHashes, remoteHashes, incomingHashes) {
21283
21616
  const changed = [];
@@ -21294,9 +21627,9 @@ function changedSlots(baseHashes, remoteHashes, incomingHashes) {
21294
21627
  // src/creator/workspace.ts
21295
21628
  init_errors();
21296
21629
  import { randomUUID } from "node:crypto";
21297
- import fs19 from "node:fs/promises";
21630
+ import fs20 from "node:fs/promises";
21298
21631
  import os12 from "node:os";
21299
- import path23 from "node:path";
21632
+ import path24 from "node:path";
21300
21633
  var MANIFEST_FILE2 = "tostudy.json";
21301
21634
  var STATE_GITIGNORE_ENTRY = ".tostudy/creator.json";
21302
21635
  var CreatorWorkspaceError = class extends Error {
@@ -21310,10 +21643,10 @@ var CreatorWorkspaceError = class extends Error {
21310
21643
  file;
21311
21644
  };
21312
21645
  function assertInsideRoot(root, relPath) {
21313
- const abs = path23.resolve(root, relPath);
21314
- const back = path23.relative(path23.resolve(root), abs);
21315
- const escapes = back === "" || back === ".." || back.startsWith(`..${path23.sep}`) || path23.isAbsolute(back);
21316
- 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) {
21317
21650
  throw new CreatorWorkspaceError(
21318
21651
  "PATH_OUTSIDE_ROOT",
21319
21652
  fillTemplate(getErrors().creator.pathOutsideRoot, { path: relPath }),
@@ -21323,9 +21656,9 @@ function assertInsideRoot(root, relPath) {
21323
21656
  return abs;
21324
21657
  }
21325
21658
  function assertNotHome(dir, home = os12.homedir()) {
21326
- const resolvedHome = path23.resolve(home);
21327
- const resolved = path23.resolve(dir);
21328
- 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;
21329
21662
  if (resolved === resolvedHome || resolvedHome.startsWith(prefix)) {
21330
21663
  throw new CreatorWorkspaceError("REFUSED_AT_HOME", getErrors().creator.refusedAtHome);
21331
21664
  }
@@ -21344,7 +21677,7 @@ function manifestLessonPaths(manifestRaw) {
21344
21677
  async function readManifest(root) {
21345
21678
  let text2;
21346
21679
  try {
21347
- text2 = await fs19.readFile(path23.join(root, MANIFEST_FILE2), "utf-8");
21680
+ text2 = await fs20.readFile(path24.join(root, MANIFEST_FILE2), "utf-8");
21348
21681
  } catch {
21349
21682
  throw new CreatorWorkspaceError(
21350
21683
  "MANIFEST_MISSING",
@@ -21363,7 +21696,7 @@ async function readManifest(root) {
21363
21696
  }
21364
21697
  }
21365
21698
  async function readCourseId(dir) {
21366
- const manifest = await readManifest(path23.resolve(dir));
21699
+ const manifest = await readManifest(path24.resolve(dir));
21367
21700
  const courseId = manifest?.courseId;
21368
21701
  if (typeof courseId !== "string" || courseId.length === 0) {
21369
21702
  throw new CreatorWorkspaceError(
@@ -21377,23 +21710,23 @@ async function readCourseId(dir) {
21377
21710
  async function readInside(root, realRoot, relPath) {
21378
21711
  let real;
21379
21712
  try {
21380
- real = await fs19.realpath(path23.resolve(root, relPath));
21713
+ real = await fs20.realpath(path24.resolve(root, relPath));
21381
21714
  } catch {
21382
21715
  return null;
21383
21716
  }
21384
- if (!real.startsWith(realRoot + path23.sep)) {
21717
+ if (!real.startsWith(realRoot + path24.sep)) {
21385
21718
  throw new CreatorWorkspaceError(
21386
21719
  "PATH_OUTSIDE_ROOT",
21387
21720
  fillTemplate(getErrors().creator.pathOutsideRoot, { path: relPath }),
21388
21721
  relPath
21389
21722
  );
21390
21723
  }
21391
- return fs19.readFile(real, "utf-8");
21724
+ return fs20.readFile(real, "utf-8");
21392
21725
  }
21393
21726
  async function readWorkspace(dir) {
21394
- const root = path23.resolve(dir);
21727
+ const root = path24.resolve(dir);
21395
21728
  const manifestRaw = await readManifest(root);
21396
- const realRoot = await fs19.realpath(root);
21729
+ const realRoot = await fs20.realpath(root);
21397
21730
  const files = {};
21398
21731
  for (const relPath of manifestLessonPaths(manifestRaw).flat()) {
21399
21732
  assertInsideRoot(root, relPath);
@@ -21405,18 +21738,18 @@ async function readWorkspace(dir) {
21405
21738
  return { manifestRaw, files, root };
21406
21739
  }
21407
21740
  async function ensureGitignore(root) {
21408
- const file2 = path23.join(root, ".gitignore");
21409
- 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(() => "");
21410
21743
  if (current.split(/\r?\n/).some((line) => line.trim() === STATE_GITIGNORE_ENTRY)) return false;
21411
21744
  const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
21412
- await fs19.writeFile(file2, `${current}${separator}${STATE_GITIGNORE_ENTRY}
21745
+ await fs20.writeFile(file2, `${current}${separator}${STATE_GITIGNORE_ENTRY}
21413
21746
  `, "utf-8");
21414
21747
  return true;
21415
21748
  }
21416
21749
  async function scaffoldWorkspace(dir, home) {
21417
- const root = path23.resolve(dir);
21750
+ const root = path24.resolve(dir);
21418
21751
  assertNotHome(root, home);
21419
- await fs19.mkdir(root, { recursive: true });
21752
+ await fs20.mkdir(root, { recursive: true });
21420
21753
  const files = [];
21421
21754
  let courseId = randomUUID();
21422
21755
  let manifestCreated = true;
@@ -21427,11 +21760,11 @@ async function scaffoldWorkspace(dir, home) {
21427
21760
  modules: [{ title: "", description: "", objectives: [], lessons: [] }]
21428
21761
  };
21429
21762
  try {
21430
- 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", {
21431
21764
  encoding: "utf-8",
21432
21765
  flag: "wx"
21433
21766
  });
21434
- await fs19.mkdir(path23.join(root, "modules", "01"), { recursive: true });
21767
+ await fs20.mkdir(path24.join(root, "modules", "01"), { recursive: true });
21435
21768
  files.push(MANIFEST_FILE2, "modules/01/");
21436
21769
  } catch (err) {
21437
21770
  if (err.code !== "EEXIST") throw err;
@@ -21493,8 +21826,8 @@ async function validateWorkspace(dir, opts = {}) {
21493
21826
  }
21494
21827
 
21495
21828
  // src/creator/pull.ts
21496
- import fs20 from "node:fs/promises";
21497
- import path24 from "node:path";
21829
+ import fs21 from "node:fs/promises";
21830
+ import path25 from "node:path";
21498
21831
  init_errors();
21499
21832
  function stripToSchema(schema, value) {
21500
21833
  const current = structuredClone(value);
@@ -21519,17 +21852,17 @@ function stripToSchema(schema, value) {
21519
21852
  }
21520
21853
  async function exists(file2) {
21521
21854
  try {
21522
- await fs20.access(file2);
21855
+ await fs21.access(file2);
21523
21856
  return true;
21524
21857
  } catch {
21525
21858
  return false;
21526
21859
  }
21527
21860
  }
21528
21861
  async function assertWritableInside(realRoot, root, rel) {
21529
- const abs = path24.resolve(root, rel);
21862
+ const abs = path25.resolve(root, rel);
21530
21863
  let target;
21531
21864
  try {
21532
- target = await fs20.lstat(abs);
21865
+ target = await fs21.lstat(abs);
21533
21866
  } catch {
21534
21867
  target = void 0;
21535
21868
  }
@@ -21540,10 +21873,10 @@ async function assertWritableInside(realRoot, root, rel) {
21540
21873
  rel
21541
21874
  );
21542
21875
  }
21543
- let dir = path24.dirname(abs);
21544
- while (!await exists(dir)) dir = path24.dirname(dir);
21545
- const realDir = await fs20.realpath(dir);
21546
- 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)) {
21547
21880
  throw new CreatorWorkspaceError(
21548
21881
  "PATH_OUTSIDE_ROOT",
21549
21882
  fillTemplate(getErrors().creator.pathOutsideRoot, { path: rel }),
@@ -21552,7 +21885,7 @@ async function assertWritableInside(realRoot, root, rel) {
21552
21885
  }
21553
21886
  }
21554
21887
  async function applyPull(root, manifestRaw, remoteModules) {
21555
- const realRoot = await fs20.realpath(root);
21888
+ const realRoot = await fs21.realpath(root);
21556
21889
  const local = manifestLessonPaths(manifestRaw);
21557
21890
  const localShape = local.map((mod) => mod.length);
21558
21891
  const remoteShape = remoteModules.map((mod) => mod.lessons.length);
@@ -21571,7 +21904,7 @@ async function applyPull(root, manifestRaw, remoteModules) {
21571
21904
  assertInsideRoot(root, rel);
21572
21905
  const contract = stripped.data;
21573
21906
  const sidecarRel = sidecarPath(rel);
21574
- if (await exists(path24.resolve(root, sidecarRel))) {
21907
+ if (await exists(path25.resolve(root, sidecarRel))) {
21575
21908
  const { teachingContent, ...rest } = contract;
21576
21909
  planned.push({ rel: sidecarRel, text: String(teachingContent ?? "") });
21577
21910
  planned.push({ rel, text: `${JSON.stringify(rest, null, 2)}
@@ -21587,9 +21920,9 @@ async function applyPull(root, manifestRaw, remoteModules) {
21587
21920
  await assertWritableInside(realRoot, root, file2.rel);
21588
21921
  }
21589
21922
  for (const file2 of planned) {
21590
- const abs = path24.resolve(root, file2.rel);
21591
- await fs20.mkdir(path24.dirname(abs), { recursive: true });
21592
- 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");
21593
21926
  }
21594
21927
  return { kind: "written", files: planned.map((file2) => file2.rel) };
21595
21928
  }
@@ -21923,7 +22256,7 @@ function installCreatorSkill(input2) {
21923
22256
  // src/commands/brief-open.ts
21924
22257
  init_guards();
21925
22258
  init_formatter();
21926
- import { Command as Command22 } from "commander";
22259
+ import { Command as Command24 } from "commander";
21927
22260
  import { execFile as execFile4 } from "node:child_process";
21928
22261
  import { platform } from "node:process";
21929
22262
  var BRIEF_URL = "https://tostudy.ai/student/settings/learner-brief";
@@ -21948,7 +22281,7 @@ function openUrl(url2, options = {}) {
21948
22281
  }
21949
22282
  });
21950
22283
  }
21951
- var briefOpenCommand = new Command22("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 () => {
21952
22285
  await requireSession();
21953
22286
  output(`Abrindo ${BRIEF_URL} no navegador...`, { json: false });
21954
22287
  openUrl(BRIEF_URL);
@@ -21956,7 +22289,7 @@ var briefOpenCommand = new Command22("brief-open").description("Open the learner
21956
22289
 
21957
22290
  // src/commands/creator.ts
21958
22291
  var logger23 = createLogger("cli:creator");
21959
- var creatorCommand = new Command23("creator").description(
22292
+ var creatorCommand = new Command25("creator").description(
21960
22293
  "Author a course with your own AI agent: init, validate, push, pull, audit, status, open"
21961
22294
  );
21962
22295
  function fail(err, opts, context) {
@@ -21970,7 +22303,7 @@ function fail(err, opts, context) {
21970
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) => {
21971
22304
  try {
21972
22305
  const home = os13.homedir();
21973
- const scaffold = await scaffoldWorkspace(path25.resolve(process.cwd(), dir), home);
22306
+ const scaffold = await scaffoldWorkspace(path26.resolve(process.cwd(), dir), home);
21974
22307
  const kept = [];
21975
22308
  const installed = installCreatorSkill({
21976
22309
  cwd: scaffold.root,
@@ -22253,8 +22586,8 @@ creatorCommand.command("open").description("Open the course in the creator porta
22253
22586
  init_guards();
22254
22587
  init_course_state();
22255
22588
  init_user_profile();
22256
- import { Command as Command24 } from "commander";
22257
- var profileCommand = new Command24("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) => {
22258
22591
  const activeCourse = await requireActiveCourse();
22259
22592
  const onboarding = await getCourseOnboardingState(activeCourse.courseId);
22260
22593
  const profile = onboarding?.learnerProfile ?? await getUserProfile();
@@ -22316,10 +22649,10 @@ var profileCommand = new Command24("profile").description("Show your learner pro
22316
22649
  init_dist();
22317
22650
  init_guards();
22318
22651
  init_formatter();
22319
- import { Command as Command25 } from "commander";
22652
+ import { Command as Command27 } from "commander";
22320
22653
  init_workspace_state();
22321
22654
  var logger24 = createLogger("cli:sync");
22322
- var syncCommand = new Command25("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(
22323
22656
  "--all-runtimes",
22324
22657
  "Write instruction files for every supported runtime, even undetected ones"
22325
22658
  ).option(
@@ -22391,10 +22724,10 @@ var syncCommand = new Command25("sync").description("Regenerate instruction file
22391
22724
  // src/commands/brief.ts
22392
22725
  init_dist();
22393
22726
  init_guards();
22394
- import { Command as Command26 } from "commander";
22727
+ import { Command as Command28 } from "commander";
22395
22728
  init_formatter();
22396
22729
  var logger25 = createLogger("cli:brief");
22397
- var briefCommand = new Command26("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) => {
22398
22731
  try {
22399
22732
  const session = await requireSession();
22400
22733
  const cached2 = await readBriefCache();
@@ -22435,10 +22768,10 @@ var briefCommand = new Command26("brief").description("Show your base learner br
22435
22768
  // src/commands/brief-create.ts
22436
22769
  init_dist();
22437
22770
  init_guards();
22438
- import { Command as Command27 } from "commander";
22771
+ import { Command as Command29 } from "commander";
22439
22772
  init_formatter();
22440
22773
  var logger26 = createLogger("cli:brief-create");
22441
- var briefCreateCommand = new Command27("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 () => {
22442
22775
  try {
22443
22776
  const session = await requireSession();
22444
22777
  const answers = await collectBootstrapAnswers({ userName: session.userName });
@@ -22471,22 +22804,22 @@ var briefCreateCommand = new Command27("brief-create").description("Create your
22471
22804
  init_dist();
22472
22805
  init_guards();
22473
22806
  init_workspace_state();
22474
- import fs22 from "node:fs";
22475
- import { Command as Command28 } from "commander";
22807
+ import fs23 from "node:fs";
22808
+ import { Command as Command30 } from "commander";
22476
22809
 
22477
22810
  // src/sessions/storage.ts
22478
22811
  init_dist();
22479
- import fs21 from "node:fs";
22480
- import path26 from "node:path";
22812
+ import fs22 from "node:fs";
22813
+ import path27 from "node:path";
22481
22814
  var logger27 = createLogger("cli:sessions");
22482
22815
  function sessionsDir(workspacePath) {
22483
- return path26.join(workspacePath, ".tostudy", "sessions");
22816
+ return path27.join(workspacePath, ".tostudy", "sessions");
22484
22817
  }
22485
22818
  async function saveModuleSummary(workspacePath, input2) {
22486
22819
  const dir = sessionsDir(workspacePath);
22487
- fs21.mkdirSync(dir, { recursive: true });
22820
+ fs22.mkdirSync(dir, { recursive: true });
22488
22821
  const filename = `module-${input2.moduleId}-summary.md`;
22489
- const filePath = path26.join(dir, filename);
22822
+ const filePath = path27.join(dir, filename);
22490
22823
  const header = [
22491
22824
  "---",
22492
22825
  `moduleId: ${input2.moduleId}`,
@@ -22495,17 +22828,17 @@ async function saveModuleSummary(workspacePath, input2) {
22495
22828
  "---",
22496
22829
  ""
22497
22830
  ].join("\n");
22498
- fs21.writeFileSync(filePath, header + input2.summary, { mode: 384 });
22831
+ fs22.writeFileSync(filePath, header + input2.summary, { mode: 384 });
22499
22832
  logger27.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
22500
22833
  return filePath;
22501
22834
  }
22502
22835
  async function loadSessionContext(workspacePath) {
22503
22836
  const dir = sessionsDir(workspacePath);
22504
- if (!fs21.existsSync(dir)) return { moduleSummaries: [] };
22505
- 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();
22506
22839
  const summaries = [];
22507
22840
  for (const file2 of files) {
22508
- const content = fs21.readFileSync(path26.join(dir, file2), "utf-8");
22841
+ const content = fs22.readFileSync(path27.join(dir, file2), "utf-8");
22509
22842
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
22510
22843
  if (!frontmatterMatch) continue;
22511
22844
  const meta3 = frontmatterMatch[1];
@@ -22521,7 +22854,7 @@ async function loadSessionContext(workspacePath) {
22521
22854
  // src/commands/compact.ts
22522
22855
  init_formatter();
22523
22856
  var logger28 = createLogger("cli:compact");
22524
- var compactCommand = new Command28("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) => {
22525
22858
  try {
22526
22859
  const activeCourse = await requireActiveCourse();
22527
22860
  const ws = await findWorkspaceState();
@@ -22529,7 +22862,7 @@ var compactCommand = new Command28("compact").description("Save a module study s
22529
22862
  if (opts.json) jsonError("no_workspace");
22530
22863
  error("Nenhum workspace encontrado.");
22531
22864
  }
22532
- const summary = fs22.readFileSync("/dev/stdin", "utf-8").trim();
22865
+ const summary = fs23.readFileSync("/dev/stdin", "utf-8").trim();
22533
22866
  if (!summary) {
22534
22867
  if (opts.json) jsonError("empty_summary", { message: "Summary vazio" });
22535
22868
  error("Summary vazio. Envie o conte\xFAdo via stdin.");
@@ -22559,12 +22892,12 @@ var compactCommand = new Command28("compact").description("Save a module study s
22559
22892
  // src/commands/context.ts
22560
22893
  init_dist();
22561
22894
  init_workspace_state();
22562
- import { Command as Command29 } from "commander";
22895
+ import { Command as Command31 } from "commander";
22563
22896
  init_course_state();
22564
22897
  init_formatter();
22565
22898
  init_errors();
22566
22899
  var logger29 = createLogger("cli:context");
22567
- var contextCommand = new Command29("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) => {
22568
22901
  try {
22569
22902
  const ws = await findWorkspaceState();
22570
22903
  if (!ws) {
@@ -22616,13 +22949,13 @@ var contextCommand = new Command29("context").description("Load session context
22616
22949
 
22617
22950
  // src/commands/memory.ts
22618
22951
  init_dist();
22619
- import { Command as Command30 } from "commander";
22952
+ import { Command as Command32 } from "commander";
22620
22953
  init_workspace_state();
22621
22954
  init_guards();
22622
22955
  init_formatter();
22623
22956
  init_errors();
22624
22957
  var logger30 = createLogger("cli:memory");
22625
- var memoryCommand = new Command30("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) => {
22626
22959
  try {
22627
22960
  const ws = await findWorkspaceState();
22628
22961
  if (!ws) {
@@ -22653,14 +22986,14 @@ var memoryCommand = new Command30("memory").description("Load accumulated studen
22653
22986
 
22654
22987
  // src/commands/insight.ts
22655
22988
  init_dist();
22656
- import { Command as Command31 } from "commander";
22989
+ import { Command as Command33 } from "commander";
22657
22990
  init_workspace_state();
22658
22991
  init_guards();
22659
22992
  init_formatter();
22660
22993
  init_errors();
22661
22994
  var logger31 = createLogger("cli:insight");
22662
22995
  var VALID_TYPES = ["difficulty", "breakthrough", "question"];
22663
- var insightCommand = new Command31("insight").description(
22996
+ var insightCommand = new Command33("insight").description(
22664
22997
  "Persist a student cognitive insight (difficulty | breakthrough | question) into course memory"
22665
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) => {
22666
22999
  try {
@@ -22708,7 +23041,7 @@ var insightCommand = new Command31("insight").description(
22708
23041
 
22709
23042
  // src/commands/level.ts
22710
23043
  init_dist();
22711
- import { Command as Command32 } from "commander";
23044
+ import { Command as Command34 } from "commander";
22712
23045
  init_guards();
22713
23046
  init_formatter();
22714
23047
  var logger32 = createLogger("cli:level");
@@ -22723,7 +23056,7 @@ var LEVEL_LABELS2 = {
22723
23056
  function isExerciseLevel2(value) {
22724
23057
  return ALL_LEVELS.includes(value);
22725
23058
  }
22726
- var levelCommand = new Command32("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) => {
22727
23060
  try {
22728
23061
  const session = await requireSession();
22729
23062
  const activeCourse = await requireActiveCourse();
@@ -22788,10 +23121,10 @@ init_dist();
22788
23121
  init_guards();
22789
23122
  init_formatter();
22790
23123
  init_errors();
22791
- import { Command as Command33 } from "commander";
23124
+ import { Command as Command35 } from "commander";
22792
23125
  var logger33 = createLogger("cli:theory");
22793
23126
  var EXERCISE_LEVELS2 = ["L0", "L1", "L2", "L3", "L4"];
22794
- var theoryCommand = new Command33("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) => {
22795
23128
  try {
22796
23129
  const session = await requireSession();
22797
23130
  const active = await requireActiveCourse();
@@ -22886,7 +23219,7 @@ var theoryCommand = new Command33("theory").description("Request more theory on
22886
23219
  // src/commands/export-grades.ts
22887
23220
  init_dist();
22888
23221
  init_guards();
22889
- import { Command as Command34 } from "commander";
23222
+ import { Command as Command36 } from "commander";
22890
23223
 
22891
23224
  // src/formatters/grades.ts
22892
23225
  function formatGradesJson(attempts) {
@@ -23254,7 +23587,7 @@ async function runExportGrades(opts, deps = defaultDeps5) {
23254
23587
  if (!rendered.endsWith("\n")) rendered += "\n";
23255
23588
  deps.stdoutWrite(rendered);
23256
23589
  }
23257
- var exportGradesCommand = new Command34("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(
23258
23591
  "--course <slug>",
23259
23592
  "Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, exporta o curso ativo."
23260
23593
  ).option("--format <json|csv|md>", "Formato de sa\xEDda (default: json)", "json").addHelpText(
@@ -23278,7 +23611,7 @@ C\xF3digos de sa\xEDda:
23278
23611
 
23279
23612
  // src/commands/grades.ts
23280
23613
  init_dist();
23281
- import { Command as Command35 } from "commander";
23614
+ import { Command as Command37 } from "commander";
23282
23615
  init_guards();
23283
23616
  var logger35 = createLogger("cli:grades");
23284
23617
  var defaultDeps6 = {
@@ -23434,7 +23767,7 @@ async function runGrades(opts, deps = defaultDeps6) {
23434
23767
  );
23435
23768
  deps.stdoutWrite(formatGradesAllEnrollmentsTable(summaries) + "\n");
23436
23769
  }
23437
- var gradesCommand = new Command35("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(
23438
23771
  "--course <slug>",
23439
23772
  "Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, lista todos os cursos."
23440
23773
  ).addHelpText(
@@ -23458,7 +23791,7 @@ C\xF3digos de sa\xEDda:
23458
23791
  // src/commands/review.ts
23459
23792
  init_dist();
23460
23793
  init_guards();
23461
- import { Command as Command36 } from "commander";
23794
+ import { Command as Command38 } from "commander";
23462
23795
  import { createInterface } from "node:readline/promises";
23463
23796
  import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
23464
23797
  var logger36 = createLogger("cli:review");
@@ -23608,7 +23941,7 @@ async function runReview(slug, opts, deps = defaultDeps7) {
23608
23941
  }) + "\n"
23609
23942
  );
23610
23943
  }
23611
- var reviewCommand = new Command36("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(
23612
23945
  "after",
23613
23946
  `
23614
23947
  Exemplos:
@@ -23631,12 +23964,12 @@ C\xF3digos de sa\xEDda:
23631
23964
 
23632
23965
  // src/cli.ts
23633
23966
  function createProgram() {
23634
- const program = new Command37();
23967
+ const program = new Command39();
23635
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(
23636
23969
  "before",
23637
23970
  [
23638
23971
  "",
23639
- " Come\xE7ando: login \u2192 courses \u2192 select <n> \u2192 start",
23972
+ " Come\xE7ando: login \u2192 suggest \u2192 enroll <id> \u2192 courses \u2192 select <n> \u2192 start",
23640
23973
  " Estudando: next \xB7 lesson \xB7 hint \xB7 validate \xB7 theory \xB7 progress",
23641
23974
  " Com d\xFAvida: tostudy menu (mostra onde voc\xEA est\xE1 e o que fazer)",
23642
23975
  ""
@@ -23648,6 +23981,8 @@ function createProgram() {
23648
23981
  program.addCommand(loginCommand);
23649
23982
  program.addCommand(logoutCommand);
23650
23983
  program.addCommand(coursesCommand);
23984
+ program.addCommand(suggestCommand);
23985
+ program.addCommand(enrollCommand);
23651
23986
  program.addCommand(selectCommand);
23652
23987
  program.addCommand(progressCommand);
23653
23988
  program.addCommand(startCommand);
@@ -23683,12 +24018,12 @@ function createProgram() {
23683
24018
 
23684
24019
  // src/auto-updater.ts
23685
24020
  init_config_dir();
23686
- import fs23 from "node:fs";
23687
- import path27 from "node:path";
24021
+ import fs24 from "node:fs";
24022
+ import path28 from "node:path";
23688
24023
  import { spawn as spawn2 } from "node:child_process";
23689
24024
  var PACKAGE_NAME2 = "@tostudy-ai/cli";
23690
24025
  function detectManager(entryRealPath) {
23691
- const p = entryRealPath.split(path27.sep).join("/");
24026
+ const p = entryRealPath.split(path28.sep).join("/");
23692
24027
  if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23693
24028
  if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23694
24029
  if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
@@ -23696,10 +24031,10 @@ function detectManager(entryRealPath) {
23696
24031
  return null;
23697
24032
  }
23698
24033
  function installDir(entryRealPath) {
23699
- const normalized = entryRealPath.split(path27.sep).join("/");
24034
+ const normalized = entryRealPath.split(path28.sep).join("/");
23700
24035
  const marker = `/node_modules/${PACKAGE_NAME2}/`;
23701
24036
  const idx = normalized.indexOf(marker);
23702
- if (idx === -1) return path27.dirname(entryRealPath);
24037
+ if (idx === -1) return path28.dirname(entryRealPath);
23703
24038
  return entryRealPath.slice(0, idx + marker.length - 1);
23704
24039
  }
23705
24040
  function maybeAutoUpdate(currentVersion, opts = {}) {
@@ -23715,10 +24050,10 @@ function maybeAutoUpdate(currentVersion, opts = {}) {
23715
24050
  if (cache.attemptedVersion === cache.latest) return;
23716
24051
  const entryPath = opts.entryPath ?? argv[1];
23717
24052
  if (!entryPath) return;
23718
- const real = fs23.realpathSync(entryPath);
24053
+ const real = fs24.realpathSync(entryPath);
23719
24054
  const mgr = detectManager(real);
23720
24055
  if (!mgr) return;
23721
- fs23.accessSync(installDir(real), fs23.constants.W_OK);
24056
+ fs24.accessSync(installDir(real), fs24.constants.W_OK);
23722
24057
  writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
23723
24058
  process.stderr.write(
23724
24059
  `