@tostudy-ai/cli 0.16.8 → 0.16.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1440,7 +1440,8 @@ async function saveSession(session, configDir) {
1440
1440
  userName: session.userName,
1441
1441
  expiresAt: session.expiresAt,
1442
1442
  apiUrl: session.apiUrl,
1443
- sessionId: session.sessionId
1443
+ sessionId: session.sessionId,
1444
+ ...session.locale ? { locale: session.locale } : {}
1444
1445
  };
1445
1446
  fs2.writeFileSync(path3.join(dir, "config.json"), JSON.stringify(stored, null, 2), {
1446
1447
  mode: 384
@@ -1460,7 +1461,8 @@ async function getSession(configDir) {
1460
1461
  userName: stored.userName,
1461
1462
  expiresAt: stored.expiresAt,
1462
1463
  apiUrl: stored.apiUrl,
1463
- sessionId: stored.sessionId
1464
+ sessionId: stored.sessionId,
1465
+ ...stored.locale ? { locale: stored.locale } : {}
1464
1466
  };
1465
1467
  }
1466
1468
  async function clearSessionArtifacts(configDir) {
@@ -2645,10 +2647,20 @@ var init_login = __esm({
2645
2647
  const body = await res.json();
2646
2648
  error(body.error ?? "Falha na autentica\xE7\xE3o");
2647
2649
  }
2648
- const { token, refreshToken, sessionId, userId, userName, expiresAt } = await res.json();
2649
- await saveSession({ token, refreshToken, sessionId, userId, userName, expiresAt, apiUrl });
2650
+ const { token, refreshToken, sessionId, userId, userName, expiresAt, locale } = await res.json();
2651
+ await saveSession({
2652
+ token,
2653
+ refreshToken,
2654
+ sessionId,
2655
+ userId,
2656
+ userName,
2657
+ expiresAt,
2658
+ apiUrl,
2659
+ ...locale === "pt-BR" || locale === "en-US" ? { locale } : {}
2660
+ });
2650
2661
  console.log(`
2651
2662
  \u2713 Logado como ${userName}`);
2663
+ const installedFiles = [];
2652
2664
  try {
2653
2665
  const { detectIDEs: detectIDEs2 } = await Promise.resolve().then(() => (init_ide_detector(), ide_detector_exports));
2654
2666
  const { installUniversalCommand: installUniversalCommand2 } = await Promise.resolve().then(() => (init_instruction_files(), instruction_files_exports));
@@ -2664,7 +2676,6 @@ var init_login = __esm({
2664
2676
  }
2665
2677
  const cwd = process.cwd();
2666
2678
  const home = homedir2();
2667
- const installedFiles = [];
2668
2679
  for (const runtime of RUNTIMES2) {
2669
2680
  if (runtime.writeUniversal && runtime.detect(home, cwd)) {
2670
2681
  installedFiles.push(...installUniversalCommand2(runtime.id, cwd));
@@ -2692,7 +2703,16 @@ var init_login = __esm({
2692
2703
  }
2693
2704
  console.log("");
2694
2705
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
2695
- console.log(" Pronto! Abra seu IDE e digite /tostudy");
2706
+ console.log(" Pronto! Para estudar no terminal:");
2707
+ console.log("");
2708
+ console.log(" mkdir ~/meus-cursos && cd ~/meus-cursos");
2709
+ console.log(" tostudy courses lista seus cursos");
2710
+ console.log(" tostudy select <n> ativa um curso nesta pasta");
2711
+ console.log(" tostudy start abre a primeira aula");
2712
+ if (installedFiles.length > 0) {
2713
+ console.log("");
2714
+ console.log(" Ou, na sua IDE, digite /tostudy.");
2715
+ }
2696
2716
  console.log("");
2697
2717
  } catch (err) {
2698
2718
  const msg = err instanceof Error ? err.message : "Login falhou";
@@ -2780,6 +2800,7 @@ var init_errors_pt_br = __esm({
2780
2800
  insufficientCredits: "\u274C Voc\xEA est\xE1 sem cr\xE9ditos. Cada resposta do tutor de IA consome cr\xE9ditos para cobrir o processamento. Recarregue em https://tostudy.ai/student/credits para continuar de onde parou.\n",
2781
2801
  workspaceRefusedAtHome: "N\xE3o d\xE1 para configurar o estudo direto na sua pasta pessoal.\nCrie uma pasta para o curso e entre nela antes de continuar:\n\n mkdir ~/meus-cursos && cd ~/meus-cursos\n",
2782
2802
  courseArchived: "Este curso foi arquivado e est\xE1 dispon\xEDvel somente para leitura.\nUse `tostudy lesson` para revisar o conte\xFAdo j\xE1 estudado.\n",
2803
+ noActiveCourse: "Nenhum curso ativo nesta pasta.\nVeja seus cursos com `tostudy courses` e ative um com `tostudy select <n>`.\n",
2783
2804
  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"
2784
2805
  };
2785
2806
  }
@@ -2801,12 +2822,25 @@ var init_errors_en_us = __esm({
2801
2822
  insufficientCredits: "\u274C You're out of credits. Every reply from the AI tutor uses credits to cover processing. Top up at https://tostudy.ai/student/credits to continue from where you stopped.\n",
2802
2823
  workspaceRefusedAtHome: "Study can't be set up directly in your home folder.\nCreate a folder for the course and switch into it first:\n\n mkdir ~/my-courses && cd ~/my-courses\n",
2803
2824
  courseArchived: "This course has been archived and is available for reading only.\nUse `tostudy lesson` to review the content you've already studied.\n",
2825
+ noActiveCourse: "No active course in this folder.\nList your courses with `tostudy courses` and activate one with `tostudy select <n>`.\n",
2804
2826
  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"
2805
2827
  };
2806
2828
  }
2807
2829
  });
2808
2830
 
2809
2831
  // src/errors/index.ts
2832
+ import fs6 from "node:fs";
2833
+ import path7 from "node:path";
2834
+ function readStoredLocale() {
2835
+ try {
2836
+ const p = path7.join(getConfigDir(), "config.json");
2837
+ if (!fs6.existsSync(p)) return null;
2838
+ const { locale } = JSON.parse(fs6.readFileSync(p, "utf-8"));
2839
+ return locale === "pt-BR" || locale === "en-US" ? locale : null;
2840
+ } catch {
2841
+ return null;
2842
+ }
2843
+ }
2810
2844
  function resolveLocale() {
2811
2845
  if (_cachedLocale !== null) return _cachedLocale;
2812
2846
  const argv = process.argv;
@@ -2818,6 +2852,11 @@ function resolveLocale() {
2818
2852
  return val;
2819
2853
  }
2820
2854
  }
2855
+ const stored = readStoredLocale();
2856
+ if (stored) {
2857
+ _cachedLocale = stored;
2858
+ return stored;
2859
+ }
2821
2860
  const lang = (process.env["LANG"] ?? "").toLowerCase();
2822
2861
  if (lang.startsWith("en")) {
2823
2862
  _cachedLocale = "en-US";
@@ -2849,6 +2888,7 @@ var BUNDLES, _cachedLocale;
2849
2888
  var init_errors = __esm({
2850
2889
  "src/errors/index.ts"() {
2851
2890
  "use strict";
2891
+ init_config_dir();
2852
2892
  init_errors_pt_br();
2853
2893
  init_errors_en_us();
2854
2894
  BUNDLES = {
@@ -2862,12 +2902,12 @@ var init_errors = __esm({
2862
2902
  // src/workspace/workspace-marker.ts
2863
2903
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "node:fs";
2864
2904
  import os4 from "node:os";
2865
- import path7 from "node:path";
2905
+ import path8 from "node:path";
2866
2906
  function resolveMarkerPath(workspacePath) {
2867
- if (path7.basename(workspacePath) === ".tostudy") {
2868
- return path7.join(workspacePath, "workspace.json");
2907
+ if (path8.basename(workspacePath) === ".tostudy") {
2908
+ return path8.join(workspacePath, "workspace.json");
2869
2909
  }
2870
- return path7.join(workspacePath, ".tostudy", "workspace.json");
2910
+ return path8.join(workspacePath, ".tostudy", "workspace.json");
2871
2911
  }
2872
2912
  async function readWorkspaceMarker(workspacePath) {
2873
2913
  const filePath = resolveMarkerPath(workspacePath);
@@ -2885,13 +2925,13 @@ async function readWorkspaceMarker(workspacePath) {
2885
2925
  }
2886
2926
  }
2887
2927
  async function writeWorkspaceMarker(workspacePath, input2, options = {}) {
2888
- const homeDir = path7.resolve(options.homeDir ?? os4.homedir());
2889
- const resolvedWorkspace = path7.resolve(workspacePath);
2890
- if (resolvedWorkspace === homeDir || homeDir.startsWith(resolvedWorkspace + path7.sep)) {
2928
+ const homeDir = path8.resolve(options.homeDir ?? os4.homedir());
2929
+ const resolvedWorkspace = path8.resolve(workspacePath);
2930
+ if (resolvedWorkspace === homeDir || homeDir.startsWith(resolvedWorkspace + path8.sep)) {
2891
2931
  throw new Error(getErrors().workspaceRefusedAtHome);
2892
2932
  }
2893
2933
  const filePath = resolveMarkerPath(workspacePath);
2894
- const dir = path7.dirname(filePath);
2934
+ const dir = path8.dirname(filePath);
2895
2935
  if (!existsSync3(dir)) {
2896
2936
  mkdirSync3(dir, { recursive: true });
2897
2937
  }
@@ -2928,7 +2968,7 @@ __export(workspace_state_exports, {
2928
2968
  });
2929
2969
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
2930
2970
  import os5 from "node:os";
2931
- import path8 from "node:path";
2971
+ import path9 from "node:path";
2932
2972
  async function readWorkspaceState(workspacePath) {
2933
2973
  const marker = await readWorkspaceMarker(workspacePath);
2934
2974
  if (!marker) return null;
@@ -2957,13 +2997,13 @@ async function readWorkspaceState(workspacePath) {
2957
2997
  }
2958
2998
  }
2959
2999
  async function findWorkspaceState(startCwd = process.cwd(), homeDir = os5.homedir()) {
2960
- const home = path8.resolve(homeDir);
2961
- let current = path8.resolve(startCwd);
3000
+ const home = path9.resolve(homeDir);
3001
+ let current = path9.resolve(startCwd);
2962
3002
  while (true) {
2963
3003
  if (current === home) return null;
2964
3004
  const state = await readWorkspaceState(current);
2965
3005
  if (state) return { state, workspacePath: current };
2966
- const parent = path8.dirname(current);
3006
+ const parent = path9.dirname(current);
2967
3007
  if (parent === current) return null;
2968
3008
  current = parent;
2969
3009
  }
@@ -3032,7 +3072,7 @@ __export(course_state_exports, {
3032
3072
  setCourseWorkspacePath: () => setCourseWorkspacePath,
3033
3073
  setLastInitCourseId: () => setLastInitCourseId
3034
3074
  });
3035
- import fs6 from "node:fs";
3075
+ import fs7 from "node:fs";
3036
3076
  async function getCourseOnboardingState(courseId, configDir) {
3037
3077
  const state = readCourseOnboardingState(configDir);
3038
3078
  return state[courseId] ?? null;
@@ -3054,7 +3094,7 @@ async function saveCourseLearnerProfile(course, learnerProfile, artifacts, confi
3054
3094
  }
3055
3095
  async function clearAllCourseOnboarding(configDir) {
3056
3096
  const onboardingPath = getCourseOnboardingPath(configDir);
3057
- if (fs6.existsSync(onboardingPath)) fs6.unlinkSync(onboardingPath);
3097
+ if (fs7.existsSync(onboardingPath)) fs7.unlinkSync(onboardingPath);
3058
3098
  }
3059
3099
  var init_course_state = __esm({
3060
3100
  "src/onboarding/course-state.ts"() {
@@ -3097,14 +3137,14 @@ __export(resolve_exports, {
3097
3137
  resolveVaultPath: () => resolveVaultPath,
3098
3138
  resolveWorkspace: () => resolveWorkspace
3099
3139
  });
3100
- import fs7 from "node:fs/promises";
3101
- import path9 from "node:path";
3140
+ import fs8 from "node:fs/promises";
3141
+ import path10 from "node:path";
3102
3142
  import os6 from "node:os";
3103
3143
  async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
3104
3144
  const slug = courseSlug(courseTitle);
3105
- const candidate = path9.join(basePath, slug);
3145
+ const candidate = path10.join(basePath, slug);
3106
3146
  try {
3107
- await fs7.access(path9.join(candidate, ".ana-config.json"));
3147
+ await fs8.access(path10.join(candidate, ".ana-config.json"));
3108
3148
  return { found: true, workspacePath: candidate, source: "default" };
3109
3149
  } catch {
3110
3150
  return { found: false, workspacePath: null };
@@ -3114,49 +3154,49 @@ async function isCwdWorkspace(cwd = process.cwd()) {
3114
3154
  return await resolveCwdWorkspacePath(cwd) !== null;
3115
3155
  }
3116
3156
  async function resolveCwdWorkspacePath(cwd = process.cwd()) {
3117
- const tostudyDir = path9.join(cwd, ".tostudy");
3157
+ const tostudyDir = path10.join(cwd, ".tostudy");
3118
3158
  try {
3119
- const stat = await fs7.stat(tostudyDir);
3159
+ const stat = await fs8.stat(tostudyDir);
3120
3160
  if (stat.isDirectory()) return tostudyDir;
3121
3161
  } catch {
3122
3162
  }
3123
3163
  try {
3124
- await fs7.access(path9.join(cwd, ".ana-config.json"));
3164
+ await fs8.access(path10.join(cwd, ".ana-config.json"));
3125
3165
  return cwd;
3126
3166
  } catch {
3127
3167
  return null;
3128
3168
  }
3129
3169
  }
3130
3170
  async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
3131
- const home = path9.resolve(homeDir);
3132
- let current = path9.resolve(startCwd);
3171
+ const home = path10.resolve(homeDir);
3172
+ let current = path10.resolve(startCwd);
3133
3173
  while (true) {
3134
3174
  if (current === home) return null;
3135
3175
  const direct = await resolveCwdWorkspacePath(current);
3136
3176
  if (direct) return direct;
3137
- const parent = path9.dirname(current);
3177
+ const parent = path10.dirname(current);
3138
3178
  if (parent === current) return null;
3139
3179
  current = parent;
3140
3180
  }
3141
3181
  }
3142
3182
  function resolveVaultPath(workspacePath, slug) {
3143
- const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
3144
- return path9.join(base, `vault-${slug}`);
3183
+ const base = path10.basename(workspacePath) === ".tostudy" ? path10.dirname(workspacePath) : workspacePath;
3184
+ return path10.join(base, `vault-${slug}`);
3145
3185
  }
3146
3186
  function cliWorkspacePaths(workspacePath, courseTitle) {
3147
- const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
3187
+ const base = path10.basename(workspacePath) === ".tostudy" ? path10.dirname(workspacePath) : workspacePath;
3148
3188
  return { workspace: base, vault: resolveVaultPath(workspacePath, courseSlug(courseTitle)) };
3149
3189
  }
3150
3190
  async function findExistingVault(workspacePath, slug) {
3151
3191
  const slugged = resolveVaultPath(workspacePath, slug);
3152
3192
  try {
3153
- await fs7.access(path9.join(slugged, ".ana-vault.json"));
3193
+ await fs8.access(path10.join(slugged, ".ana-vault.json"));
3154
3194
  return slugged;
3155
3195
  } catch {
3156
3196
  }
3157
- const legacy = path9.join(workspacePath, "vault");
3197
+ const legacy = path10.join(workspacePath, "vault");
3158
3198
  try {
3159
- await fs7.access(path9.join(legacy, ".ana-vault.json"));
3199
+ await fs8.access(path10.join(legacy, ".ana-vault.json"));
3160
3200
  return legacy;
3161
3201
  } catch {
3162
3202
  return null;
@@ -3169,7 +3209,7 @@ async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.
3169
3209
  }
3170
3210
  if (storedPath) {
3171
3211
  try {
3172
- await fs7.access(path9.join(storedPath, ".ana-config.json"));
3212
+ await fs8.access(path10.join(storedPath, ".ana-config.json"));
3173
3213
  return { found: true, workspacePath: storedPath, source: "stored" };
3174
3214
  } catch {
3175
3215
  }
@@ -3190,7 +3230,7 @@ var init_resolve = __esm({
3190
3230
  "src/workspace/resolve.ts"() {
3191
3231
  "use strict";
3192
3232
  init_slug();
3193
- DEFAULT_BASE = path9.join(os6.homedir(), "study");
3233
+ DEFAULT_BASE = path10.join(os6.homedir(), "study");
3194
3234
  courseSlug = slugify;
3195
3235
  resolveActiveCourseWithOverride = resolveActiveCourseFromWorkspace;
3196
3236
  }
@@ -3246,7 +3286,7 @@ async function requireActiveCourse() {
3246
3286
  await primeWorkspaceTokenPaths(result.state);
3247
3287
  return result.state;
3248
3288
  }
3249
- process.stderr.write("Erro: Nenhum workspace configurado. Rode: tostudy setup\n");
3289
+ process.stderr.write(`Erro: ${getErrors().noActiveCourse}`);
3250
3290
  process.exit(3);
3251
3291
  }
3252
3292
  async function primeWorkspaceTokenPaths(state) {
@@ -3272,6 +3312,7 @@ var init_guards = __esm({
3272
3312
  "src/auth/guards.ts"() {
3273
3313
  "use strict";
3274
3314
  init_session_store();
3315
+ init_errors();
3275
3316
  }
3276
3317
  });
3277
3318
 
@@ -3320,7 +3361,9 @@ var init_mcp_setup = __esm({
3320
3361
  import { Command as Command3 } from "commander";
3321
3362
  async function runSetup(_opts, deps = defaultDeps) {
3322
3363
  deps.log("\n \u26A0\uFE0F `tostudy setup` foi incorporado ao `tostudy login`.");
3323
- deps.log(" Rode `tostudy login` para configurar seu ambiente.\n");
3364
+ deps.log(
3365
+ " Rode `tostudy login`, depois `tostudy courses` e `tostudy select <n>` para estudar no terminal.\n"
3366
+ );
3324
3367
  }
3325
3368
  async function runSetupMcpSubcommand() {
3326
3369
  console.log("\n ToStudy MCP Setup\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
@@ -3383,7 +3426,7 @@ var CLI_VERSION;
3383
3426
  var init_version = __esm({
3384
3427
  "src/version.ts"() {
3385
3428
  "use strict";
3386
- CLI_VERSION = true ? "0.16.8" : "0.7.1";
3429
+ CLI_VERSION = true ? "0.16.9" : "0.7.1";
3387
3430
  }
3388
3431
  });
3389
3432
 
@@ -3397,13 +3440,13 @@ __export(update_checker_exports, {
3397
3440
  readCache: () => readCache,
3398
3441
  writeCache: () => writeCache
3399
3442
  });
3400
- import fs8 from "node:fs";
3401
- import path10 from "node:path";
3443
+ import fs9 from "node:fs";
3444
+ import path11 from "node:path";
3402
3445
  function readCache(configDir) {
3403
3446
  try {
3404
- const p = path10.join(configDir ?? getConfigDir(), CACHE_FILE);
3405
- if (!fs8.existsSync(p)) return null;
3406
- return JSON.parse(fs8.readFileSync(p, "utf-8"));
3447
+ const p = path11.join(configDir ?? getConfigDir(), CACHE_FILE);
3448
+ if (!fs9.existsSync(p)) return null;
3449
+ return JSON.parse(fs9.readFileSync(p, "utf-8"));
3407
3450
  } catch {
3408
3451
  return null;
3409
3452
  }
@@ -3411,8 +3454,8 @@ function readCache(configDir) {
3411
3454
  function writeCache(cache, configDir) {
3412
3455
  try {
3413
3456
  const dir = configDir ?? getConfigDir();
3414
- fs8.mkdirSync(dir, { recursive: true });
3415
- fs8.writeFileSync(path10.join(dir, CACHE_FILE), JSON.stringify(cache));
3457
+ fs9.mkdirSync(dir, { recursive: true });
3458
+ fs9.writeFileSync(path11.join(dir, CACHE_FILE), JSON.stringify(cache));
3416
3459
  } catch {
3417
3460
  }
3418
3461
  }
@@ -3482,17 +3525,17 @@ var init_update_checker = __esm({
3482
3525
  });
3483
3526
 
3484
3527
  // src/learner-brief/cache.ts
3485
- import fs9 from "node:fs";
3486
- import path11 from "node:path";
3528
+ import fs10 from "node:fs";
3529
+ import path12 from "node:path";
3487
3530
  function resolveCachePath(configDir) {
3488
- return path11.join(configDir, "student-brief.json");
3531
+ return path12.join(configDir, "student-brief.json");
3489
3532
  }
3490
3533
  async function readBriefCache(configDir) {
3491
3534
  const dir = configDir ?? getConfigDir();
3492
3535
  const p = resolveCachePath(dir);
3493
- if (!fs9.existsSync(p)) return null;
3536
+ if (!fs10.existsSync(p)) return null;
3494
3537
  try {
3495
- const raw = fs9.readFileSync(p, "utf-8");
3538
+ const raw = fs10.readFileSync(p, "utf-8");
3496
3539
  return JSON.parse(raw);
3497
3540
  } catch {
3498
3541
  return null;
@@ -3500,8 +3543,8 @@ async function readBriefCache(configDir) {
3500
3543
  }
3501
3544
  async function writeBriefCache(configDir, cache) {
3502
3545
  const dir = configDir ?? getConfigDir();
3503
- fs9.mkdirSync(dir, { recursive: true });
3504
- fs9.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
3546
+ fs10.mkdirSync(dir, { recursive: true });
3547
+ fs10.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
3505
3548
  }
3506
3549
  var init_cache = __esm({
3507
3550
  "src/learner-brief/cache.ts"() {
@@ -3511,23 +3554,23 @@ var init_cache = __esm({
3511
3554
  });
3512
3555
 
3513
3556
  // src/tutor-persona/cache.ts
3514
- import fs10 from "node:fs";
3515
- import path12 from "node:path";
3557
+ import fs11 from "node:fs";
3558
+ import path13 from "node:path";
3516
3559
  function resolveCachePath2(configDir) {
3517
- return path12.join(configDir, "tutor-personalities.json");
3560
+ return path13.join(configDir, "tutor-personalities.json");
3518
3561
  }
3519
3562
  function readAll(configDir) {
3520
3563
  const p = resolveCachePath2(configDir);
3521
- if (!fs10.existsSync(p)) return {};
3564
+ if (!fs11.existsSync(p)) return {};
3522
3565
  try {
3523
- return JSON.parse(fs10.readFileSync(p, "utf-8"));
3566
+ return JSON.parse(fs11.readFileSync(p, "utf-8"));
3524
3567
  } catch {
3525
3568
  return {};
3526
3569
  }
3527
3570
  }
3528
3571
  function writeAll(configDir, cache) {
3529
- fs10.mkdirSync(configDir, { recursive: true });
3530
- fs10.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
3572
+ fs11.mkdirSync(configDir, { recursive: true });
3573
+ fs11.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
3531
3574
  }
3532
3575
  async function readPersonaCacheForCourse(configDir, courseId) {
3533
3576
  const dir = configDir ?? getConfigDir();
@@ -4770,7 +4813,7 @@ var init_next = __esm({
4770
4813
 
4771
4814
  // src/commands/lesson.ts
4772
4815
  import { Command as Command11 } from "commander";
4773
- import path13 from "node:path";
4816
+ import path14 from "node:path";
4774
4817
  function adjustTimeEstimate(type, baseMinutes) {
4775
4818
  if (type === "exercise") return Math.max(baseMinutes, 30);
4776
4819
  return baseMinutes;
@@ -4783,7 +4826,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
4783
4826
  const isPractical = data.type === "exercise" || isCheckpoint(data.title);
4784
4827
  const lines = [`\u2501\u2501\u2501 Li\xE7\xE3o: ${data.title} \u2501\u2501\u2501`];
4785
4828
  if (isPractical && workspacePath) {
4786
- const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
4829
+ const base = path14.basename(workspacePath) === ".tostudy" ? path14.dirname(workspacePath) : workspacePath;
4787
4830
  const slug = courseSlug(courseTitle);
4788
4831
  const vaultPath = resolveVaultPath(workspacePath, slug);
4789
4832
  lines.push(
@@ -5327,10 +5370,10 @@ function mergeDefs(...defs) {
5327
5370
  function cloneDef(schema) {
5328
5371
  return mergeDefs(schema._zod.def);
5329
5372
  }
5330
- function getElementAtPath(obj, path24) {
5331
- if (!path24)
5373
+ function getElementAtPath(obj, path25) {
5374
+ if (!path25)
5332
5375
  return obj;
5333
- return path24.reduce((acc, key) => acc?.[key], obj);
5376
+ return path25.reduce((acc, key) => acc?.[key], obj);
5334
5377
  }
5335
5378
  function promiseAllObject(promisesObj) {
5336
5379
  const keys = Object.keys(promisesObj);
@@ -5642,11 +5685,11 @@ function aborted(x, startIndex = 0) {
5642
5685
  }
5643
5686
  return false;
5644
5687
  }
5645
- function prefixIssues(path24, issues) {
5688
+ function prefixIssues(path25, issues) {
5646
5689
  return issues.map((iss) => {
5647
5690
  var _a2;
5648
5691
  (_a2 = iss).path ?? (_a2.path = []);
5649
- iss.path.unshift(path24);
5692
+ iss.path.unshift(path25);
5650
5693
  return iss;
5651
5694
  });
5652
5695
  }
@@ -5888,7 +5931,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
5888
5931
  }
5889
5932
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
5890
5933
  const result = { errors: [] };
5891
- const processError = (error50, path24 = []) => {
5934
+ const processError = (error50, path25 = []) => {
5892
5935
  var _a2, _b;
5893
5936
  for (const issue2 of error50.issues) {
5894
5937
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -5898,7 +5941,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
5898
5941
  } else if (issue2.code === "invalid_element") {
5899
5942
  processError({ issues: issue2.issues }, issue2.path);
5900
5943
  } else {
5901
- const fullpath = [...path24, ...issue2.path];
5944
+ const fullpath = [...path25, ...issue2.path];
5902
5945
  if (fullpath.length === 0) {
5903
5946
  result.errors.push(mapper(issue2));
5904
5947
  continue;
@@ -5930,8 +5973,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
5930
5973
  }
5931
5974
  function toDotPath(_path) {
5932
5975
  const segs = [];
5933
- const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5934
- for (const seg of path24) {
5976
+ const path25 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5977
+ for (const seg of path25) {
5935
5978
  if (typeof seg === "number")
5936
5979
  segs.push(`[${seg}]`);
5937
5980
  else if (typeof seg === "symbol")
@@ -18625,13 +18668,13 @@ function resolveRef(ref, ctx) {
18625
18668
  if (!ref.startsWith("#")) {
18626
18669
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
18627
18670
  }
18628
- const path24 = ref.slice(1).split("/").filter(Boolean);
18629
- if (path24.length === 0) {
18671
+ const path25 = ref.slice(1).split("/").filter(Boolean);
18672
+ if (path25.length === 0) {
18630
18673
  return ctx.rootSchema;
18631
18674
  }
18632
18675
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
18633
- if (path24[0] === defsKey) {
18634
- const key = path24[1];
18676
+ if (path25[0] === defsKey) {
18677
+ const key = path25[1];
18635
18678
  if (!key || !ctx.defs[key]) {
18636
18679
  throw new Error(`Reference not found: ${ref}`);
18637
18680
  }
@@ -19963,8 +20006,8 @@ var init_init_template = __esm({
19963
20006
  });
19964
20007
 
19965
20008
  // src/commands/validate.ts
19966
- import fs11 from "node:fs";
19967
- import path14 from "node:path";
20009
+ import fs12 from "node:fs";
20010
+ import path15 from "node:path";
19968
20011
  import { Command as Command14 } from "commander";
19969
20012
  var logger16, validateCommand;
19970
20013
  var init_validate = __esm({
@@ -20008,21 +20051,21 @@ var init_validate = __esm({
20008
20051
  }
20009
20052
  let solution;
20010
20053
  if (opts.stdin) {
20011
- solution = fs11.readFileSync("/dev/stdin", "utf-8");
20054
+ solution = fs12.readFileSync("/dev/stdin", "utf-8");
20012
20055
  } else if (file2) {
20013
- if (!fs11.existsSync(file2)) {
20056
+ if (!fs12.existsSync(file2)) {
20014
20057
  if (opts.json)
20015
20058
  jsonError("file_not_found", { message: `Arquivo n\xE3o encontrado: ${file2}` });
20016
20059
  error(`Arquivo n\xE3o encontrado: ${file2}`);
20017
20060
  }
20018
- solution = fs11.readFileSync(file2, "utf-8");
20061
+ solution = fs12.readFileSync(file2, "utf-8");
20019
20062
  } else {
20020
20063
  if (opts.json)
20021
20064
  jsonError("no_solution_provided", { message: "Forne\xE7a um arquivo ou use --stdin" });
20022
20065
  error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
20023
20066
  }
20024
20067
  if (file2 && activeCourse.courseTags?.length) {
20025
- const ext = path14.extname(file2).toLowerCase();
20068
+ const ext = path15.extname(file2).toLowerCase();
20026
20069
  const LANG_EXTENSIONS = {
20027
20070
  ".html": ["html", "html5"],
20028
20071
  ".css": ["css"],
@@ -20490,14 +20533,14 @@ var init_init = __esm({
20490
20533
  });
20491
20534
 
20492
20535
  // ../../packages/tostudy-core/src/workspace/setup-workspace.ts
20493
- import fs12 from "node:fs/promises";
20494
- import path15 from "node:path";
20536
+ import fs13 from "node:fs/promises";
20537
+ import path16 from "node:path";
20495
20538
  async function setupWorkspace(input2) {
20496
- const workspacePath = path15.join(input2.basePath, input2.courseSlug);
20539
+ const workspacePath = path16.join(input2.basePath, input2.courseSlug);
20497
20540
  for (const dir of WORKSPACE_DIRS) {
20498
- await fs12.mkdir(path15.join(workspacePath, dir), { recursive: true });
20541
+ await fs13.mkdir(path16.join(workspacePath, dir), { recursive: true });
20499
20542
  }
20500
- const configPath = path15.join(workspacePath, ".ana-config.json");
20543
+ const configPath = path16.join(workspacePath, ".ana-config.json");
20501
20544
  const config2 = {
20502
20545
  courseId: input2.courseId,
20503
20546
  courseSlug: input2.courseSlug,
@@ -20507,7 +20550,7 @@ async function setupWorkspace(input2) {
20507
20550
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
20508
20551
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
20509
20552
  };
20510
- await fs12.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20553
+ await fs13.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20511
20554
  const readme = [
20512
20555
  `# ${input2.courseName}`,
20513
20556
  "",
@@ -20532,7 +20575,7 @@ async function setupWorkspace(input2) {
20532
20575
  "tostudy vault sync # Sincronizar progresso",
20533
20576
  "```"
20534
20577
  ].join("\n");
20535
- await fs12.writeFile(path15.join(workspacePath, "README.md"), readme, "utf-8");
20578
+ await fs13.writeFile(path16.join(workspacePath, "README.md"), readme, "utf-8");
20536
20579
  return { workspacePath, directories: WORKSPACE_DIRS, configPath };
20537
20580
  }
20538
20581
  var WORKSPACE_DIRS;
@@ -20633,8 +20676,8 @@ var init_templates = __esm({
20633
20676
  });
20634
20677
 
20635
20678
  // ../../packages/tostudy-core/src/workspace/extract-exercise.ts
20636
- import fs13 from "node:fs/promises";
20637
- import path16 from "node:path";
20679
+ import fs14 from "node:fs/promises";
20680
+ import path17 from "node:path";
20638
20681
  function padOrder(n) {
20639
20682
  return String(n).padStart(2, "0");
20640
20683
  }
@@ -20660,16 +20703,16 @@ async function extractExercise(input2) {
20660
20703
  const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
20661
20704
  const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
20662
20705
  const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
20663
- const exercisePath = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
20664
- await fs13.mkdir(exercisePath, { recursive: true });
20706
+ const exercisePath = path17.join(workspacePath, "exercises", moduleDir, lessonDir);
20707
+ await fs14.mkdir(exercisePath, { recursive: true });
20665
20708
  const extractedFiles = [];
20666
20709
  let hasStarterCode = false;
20667
20710
  if (lessonData.sandpackConfig?.files) {
20668
20711
  for (const [filePath, fileData] of Object.entries(lessonData.sandpackConfig.files)) {
20669
20712
  const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
20670
- const fullPath = path16.join(exercisePath, cleanPath);
20671
- await fs13.mkdir(path16.dirname(fullPath), { recursive: true });
20672
- await fs13.writeFile(fullPath, fileData.code, "utf-8");
20713
+ const fullPath = path17.join(exercisePath, cleanPath);
20714
+ await fs14.mkdir(path17.dirname(fullPath), { recursive: true });
20715
+ await fs14.writeFile(fullPath, fileData.code, "utf-8");
20673
20716
  extractedFiles.push(cleanPath);
20674
20717
  hasStarterCode = true;
20675
20718
  }
@@ -20677,13 +20720,13 @@ async function extractExercise(input2) {
20677
20720
  const tierData = getTierData(lessonData.structuredData, exerciseTier);
20678
20721
  const tierCode = tierData?.code;
20679
20722
  if (tierCode) {
20680
- await fs13.writeFile(path16.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20723
+ await fs14.writeFile(path17.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20681
20724
  extractedFiles.push("exercise.js");
20682
20725
  hasStarterCode = true;
20683
20726
  } else {
20684
20727
  const starter = getStarterCode(lessonData.structuredData);
20685
20728
  if (starter) {
20686
- await fs13.writeFile(path16.join(exercisePath, "exercise.js"), starter, "utf-8");
20729
+ await fs14.writeFile(path17.join(exercisePath, "exercise.js"), starter, "utf-8");
20687
20730
  extractedFiles.push("exercise.js");
20688
20731
  hasStarterCode = true;
20689
20732
  }
@@ -20701,8 +20744,8 @@ async function extractExercise(input2) {
20701
20744
  ...exerciseDeps
20702
20745
  }
20703
20746
  };
20704
- await fs13.writeFile(
20705
- path16.join(exercisePath, "package.json"),
20747
+ await fs14.writeFile(
20748
+ path17.join(exercisePath, "package.json"),
20706
20749
  JSON.stringify(pkgJson, null, 2),
20707
20750
  "utf-8"
20708
20751
  );
@@ -20714,20 +20757,20 @@ async function extractExercise(input2) {
20714
20757
  );
20715
20758
  for (const [configFile, configContent] of Object.entries(scaffold.configs)) {
20716
20759
  if (!sandpackFileNames.has(configFile)) {
20717
- await fs13.writeFile(path16.join(exercisePath, configFile), configContent, "utf-8");
20760
+ await fs14.writeFile(path17.join(exercisePath, configFile), configContent, "utf-8");
20718
20761
  extractedFiles.push(configFile);
20719
20762
  }
20720
20763
  }
20721
20764
  const setupSh = `#!/bin/sh
20722
20765
  ${scaffold.setupScript}
20723
20766
  `;
20724
- await fs13.writeFile(path16.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20767
+ await fs14.writeFile(path17.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20725
20768
  extractedFiles.push("setup.sh");
20726
20769
  }
20727
20770
  }
20728
20771
  const readme = generateReadme(lessonData, exerciseTier);
20729
- const readmePath = path16.join(exercisePath, "README.md");
20730
- await fs13.writeFile(readmePath, readme, "utf-8");
20772
+ const readmePath = path17.join(exercisePath, "README.md");
20773
+ await fs14.writeFile(readmePath, readme, "utf-8");
20731
20774
  extractedFiles.push("README.md");
20732
20775
  return {
20733
20776
  exercisePath,
@@ -20809,9 +20852,9 @@ var init_workspace = __esm({
20809
20852
 
20810
20853
  // src/commands/workspace.ts
20811
20854
  import { Command as Command17 } from "commander";
20812
- import path17 from "node:path";
20855
+ import path18 from "node:path";
20813
20856
  import os8 from "node:os";
20814
- import fs14 from "node:fs/promises";
20857
+ import fs15 from "node:fs/promises";
20815
20858
  var logger18, workspaceCommand;
20816
20859
  var init_workspace2 = __esm({
20817
20860
  "src/commands/workspace.ts"() {
@@ -20835,14 +20878,14 @@ var init_workspace2 = __esm({
20835
20878
  let directories;
20836
20879
  if (!opts.path && cwdIsWorkspace) {
20837
20880
  const resolvedCwd = await resolveCwdWorkspacePath(process.cwd());
20838
- workspacePath = resolvedCwd ?? path17.join(process.cwd(), ".tostudy");
20839
- await fs14.mkdir(workspacePath, { recursive: true });
20881
+ workspacePath = resolvedCwd ?? path18.join(process.cwd(), ".tostudy");
20882
+ await fs15.mkdir(workspacePath, { recursive: true });
20840
20883
  directories = ["exercises", "generated", "notes", "diagrams"];
20841
20884
  for (const dir of directories) {
20842
- await fs14.mkdir(path17.join(workspacePath, dir), { recursive: true });
20885
+ await fs15.mkdir(path18.join(workspacePath, dir), { recursive: true });
20843
20886
  }
20844
- const configPath = path17.join(workspacePath, ".ana-config.json");
20845
- await fs14.writeFile(
20887
+ const configPath = path18.join(workspacePath, ".ana-config.json");
20888
+ await fs15.writeFile(
20846
20889
  configPath,
20847
20890
  JSON.stringify(
20848
20891
  {
@@ -20860,7 +20903,7 @@ var init_workspace2 = __esm({
20860
20903
  "utf-8"
20861
20904
  );
20862
20905
  } else {
20863
- const basePath = opts.path ?? path17.join(os8.homedir(), "study");
20906
+ const basePath = opts.path ?? path18.join(os8.homedir(), "study");
20864
20907
  const result2 = await setupWorkspace({
20865
20908
  courseId: activeCourse.courseId,
20866
20909
  courseSlug: courseSlug(activeCourse.courseTitle),
@@ -20894,7 +20937,7 @@ Pr\xF3ximo passo: tostudy export
20894
20937
  process.exit(1);
20895
20938
  }
20896
20939
  });
20897
- 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) => {
20940
+ 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) => {
20898
20941
  try {
20899
20942
  const activeCourse = await requireActiveCourse();
20900
20943
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -20911,40 +20954,40 @@ Pr\xF3ximo passo: tostudy export
20911
20954
  const workspacePath = ws.workspacePath;
20912
20955
  let configData = null;
20913
20956
  try {
20914
- const raw = await fs14.readFile(path17.join(workspacePath, ".ana-config.json"), "utf-8");
20957
+ const raw = await fs15.readFile(path18.join(workspacePath, ".ana-config.json"), "utf-8");
20915
20958
  configData = JSON.parse(raw);
20916
20959
  } catch {
20917
20960
  configData = null;
20918
20961
  }
20919
- const exercisesDir = path17.join(workspacePath, "exercises");
20962
+ const exercisesDir = path18.join(workspacePath, "exercises");
20920
20963
  let exerciseCount = 0;
20921
20964
  try {
20922
- const moduleDirs = await fs14.readdir(exercisesDir);
20965
+ const moduleDirs = await fs15.readdir(exercisesDir);
20923
20966
  for (const modDir of moduleDirs) {
20924
- const modPath = path17.join(exercisesDir, modDir);
20925
- const stat = await fs14.stat(modPath);
20967
+ const modPath = path18.join(exercisesDir, modDir);
20968
+ const stat = await fs15.stat(modPath);
20926
20969
  if (stat.isDirectory()) {
20927
- const lessonDirs = await fs14.readdir(modPath);
20970
+ const lessonDirs = await fs15.readdir(modPath);
20928
20971
  for (const lessonDir of lessonDirs) {
20929
- const lessonPath = path17.join(modPath, lessonDir);
20930
- const lstat = await fs14.stat(lessonPath);
20972
+ const lessonPath = path18.join(modPath, lessonDir);
20973
+ const lstat = await fs15.stat(lessonPath);
20931
20974
  if (lstat.isDirectory()) exerciseCount++;
20932
20975
  }
20933
20976
  }
20934
20977
  }
20935
20978
  } catch {
20936
20979
  }
20937
- const generatedDir = path17.join(workspacePath, "generated");
20980
+ const generatedDir = path18.join(workspacePath, "generated");
20938
20981
  let artifactCount = 0;
20939
20982
  try {
20940
- const files = await fs14.readdir(generatedDir);
20983
+ const files = await fs15.readdir(generatedDir);
20941
20984
  artifactCount = files.length;
20942
20985
  } catch {
20943
20986
  }
20944
- const diagramsDir = path17.join(workspacePath, "diagrams");
20987
+ const diagramsDir = path18.join(workspacePath, "diagrams");
20945
20988
  let diagramCount = 0;
20946
20989
  try {
20947
- const files = await fs14.readdir(diagramsDir);
20990
+ const files = await fs15.readdir(diagramsDir);
20948
20991
  diagramCount = files.length;
20949
20992
  } catch {
20950
20993
  }
@@ -20993,9 +21036,9 @@ Pr\xF3ximo passo: tostudy export
20993
21036
 
20994
21037
  // src/commands/export.ts
20995
21038
  import { Command as Command18 } from "commander";
20996
- import path18 from "node:path";
21039
+ import path19 from "node:path";
20997
21040
  import os9 from "node:os";
20998
- import fs15 from "node:fs/promises";
21041
+ import fs16 from "node:fs/promises";
20999
21042
  var logger19, exportCommand;
21000
21043
  var init_export = __esm({
21001
21044
  "src/commands/export.ts"() {
@@ -21008,7 +21051,7 @@ var init_export = __esm({
21008
21051
  init_resolve();
21009
21052
  init_errors();
21010
21053
  logger19 = createLogger("cli:export");
21011
- exportCommand = new Command18("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) => {
21054
+ exportCommand = new Command18("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) => {
21012
21055
  try {
21013
21056
  const session = await requireSession();
21014
21057
  const activeCourse = await requireActiveCourse();
@@ -21026,21 +21069,21 @@ var init_export = __esm({
21026
21069
  opts.path
21027
21070
  );
21028
21071
  if (ws.found && ws.source === "cwd" && ws.workspacePath) {
21029
- const configPath = path18.join(ws.workspacePath, ".ana-config.json");
21072
+ const configPath = path19.join(ws.workspacePath, ".ana-config.json");
21030
21073
  let hasConfig = false;
21031
21074
  try {
21032
- await fs15.access(configPath);
21075
+ await fs16.access(configPath);
21033
21076
  hasConfig = true;
21034
21077
  } catch {
21035
21078
  }
21036
21079
  if (!hasConfig) {
21037
21080
  const slug = courseSlug(activeCourse.courseTitle);
21038
21081
  logger19.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
21039
- await fs15.mkdir(ws.workspacePath, { recursive: true });
21082
+ await fs16.mkdir(ws.workspacePath, { recursive: true });
21040
21083
  for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
21041
- await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
21084
+ await fs16.mkdir(path19.join(ws.workspacePath, dir), { recursive: true });
21042
21085
  }
21043
- await fs15.writeFile(
21086
+ await fs16.writeFile(
21044
21087
  configPath,
21045
21088
  JSON.stringify(
21046
21089
  {
@@ -21058,7 +21101,7 @@ var init_export = __esm({
21058
21101
  "utf-8"
21059
21102
  );
21060
21103
  await setCourseWorkspacePath(activeCourse.courseId, ws.workspacePath);
21061
- const isNamespaced = ws.workspacePath === path18.join(process.cwd(), ".tostudy");
21104
+ const isNamespaced = ws.workspacePath === path19.join(process.cwd(), ".tostudy");
21062
21105
  process.stderr.write(
21063
21106
  isNamespaced ? `\u2728 Workspace inicializado em .tostudy/ (isolado do projeto).
21064
21107
  ` : `\u2728 Workspace inicializado nesta pasta.
@@ -21112,7 +21155,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
21112
21155
  // src/commands/open.ts
21113
21156
  import { Command as Command19 } from "commander";
21114
21157
  import { execFile as execFile3 } from "node:child_process";
21115
- import path19 from "node:path";
21158
+ import path20 from "node:path";
21116
21159
  import os10 from "node:os";
21117
21160
  var logger20, openCommand;
21118
21161
  var init_open = __esm({
@@ -21124,7 +21167,7 @@ var init_open = __esm({
21124
21167
  init_resolve();
21125
21168
  init_errors();
21126
21169
  logger20 = createLogger("cli:open");
21127
- openCommand = new Command19("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path19.join(os10.homedir(), "study")).action(async (opts) => {
21170
+ openCommand = new Command19("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path20.join(os10.homedir(), "study")).action(async (opts) => {
21128
21171
  try {
21129
21172
  const activeCourse = await requireActiveCourse();
21130
21173
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -21178,24 +21221,24 @@ var init_types = __esm({
21178
21221
  });
21179
21222
 
21180
21223
  // ../../packages/tostudy-core/src/vault/write-vault.ts
21181
- import fs16 from "node:fs/promises";
21182
- import path20 from "node:path";
21224
+ import fs17 from "node:fs/promises";
21225
+ import path21 from "node:path";
21183
21226
  async function writeVaultFiles(files, outputPath, courseId, courseSlug2) {
21184
21227
  for (const file2 of files) {
21185
- const fullPath = path20.join(outputPath, file2.relativePath);
21186
- await fs16.mkdir(path20.dirname(fullPath), { recursive: true });
21187
- await fs16.writeFile(fullPath, file2.content, "utf-8");
21228
+ const fullPath = path21.join(outputPath, file2.relativePath);
21229
+ await fs17.mkdir(path21.dirname(fullPath), { recursive: true });
21230
+ await fs17.writeFile(fullPath, file2.content, "utf-8");
21188
21231
  }
21189
- const vaultPath = path20.join(outputPath, courseSlug2);
21232
+ const vaultPath = path21.join(outputPath, courseSlug2);
21190
21233
  const marker = {
21191
21234
  courseId,
21192
21235
  courseSlug: courseSlug2,
21193
21236
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
21194
21237
  version: VAULT_MARKER_VERSION
21195
21238
  };
21196
- await fs16.mkdir(vaultPath, { recursive: true });
21197
- await fs16.writeFile(
21198
- path20.join(vaultPath, VAULT_MARKER_FILENAME),
21239
+ await fs17.mkdir(vaultPath, { recursive: true });
21240
+ await fs17.writeFile(
21241
+ path21.join(vaultPath, VAULT_MARKER_FILENAME),
21199
21242
  JSON.stringify(marker, null, 2),
21200
21243
  "utf-8"
21201
21244
  );
@@ -21220,9 +21263,9 @@ var init_vault = __esm({
21220
21263
 
21221
21264
  // src/commands/vault.ts
21222
21265
  import { Command as Command20 } from "commander";
21223
- import path21 from "node:path";
21266
+ import path22 from "node:path";
21224
21267
  import os11 from "node:os";
21225
- import fs17 from "node:fs/promises";
21268
+ import fs18 from "node:fs/promises";
21226
21269
  var logger21, vaultCommand;
21227
21270
  var init_vault2 = __esm({
21228
21271
  "src/commands/vault.ts"() {
@@ -21238,7 +21281,7 @@ var init_vault2 = __esm({
21238
21281
  init_errors();
21239
21282
  logger21 = createLogger("cli:vault");
21240
21283
  vaultCommand = new Command20("vault").description("Gerenciar vault Obsidian do curso");
21241
- 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) => {
21284
+ 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) => {
21242
21285
  try {
21243
21286
  const session = await requireSession();
21244
21287
  const activeCourse = await requireActiveCourse();
@@ -21319,7 +21362,7 @@ Para visualizar:
21319
21362
  process.exit(1);
21320
21363
  }
21321
21364
  });
21322
- 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) => {
21365
+ 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) => {
21323
21366
  try {
21324
21367
  const session = await requireSession();
21325
21368
  const activeCourse = await requireActiveCourse();
@@ -21345,8 +21388,8 @@ Para visualizar:
21345
21388
  const data = createHttpProvider(session.apiUrl, session.token);
21346
21389
  const deps = { data, logger: logger21 };
21347
21390
  const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
21348
- const markerPath = path21.join(vaultPath, ".ana-vault.json");
21349
- const markerRaw = await fs17.readFile(markerPath, "utf-8");
21391
+ const markerPath = path22.join(vaultPath, ".ana-vault.json");
21392
+ const markerRaw = await fs18.readFile(markerPath, "utf-8");
21350
21393
  const marker = JSON.parse(markerRaw);
21351
21394
  marker.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
21352
21395
  marker.progress = {
@@ -21354,10 +21397,10 @@ Para visualizar:
21354
21397
  currentModule: progress.currentModule.title,
21355
21398
  currentLesson: progress.currentLesson.title
21356
21399
  };
21357
- await fs17.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
21358
- const courseIndexPath = path21.join(vaultPath, slug, "index.md");
21400
+ await fs18.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
21401
+ const courseIndexPath = path22.join(vaultPath, slug, "index.md");
21359
21402
  try {
21360
- let indexContent = await fs17.readFile(courseIndexPath, "utf-8");
21403
+ let indexContent = await fs18.readFile(courseIndexPath, "utf-8");
21361
21404
  indexContent = indexContent.replace(/\n---\n\n> 📊 Progresso:.*\n/g, "");
21362
21405
  const titleEnd = indexContent.indexOf("\n");
21363
21406
  if (titleEnd !== -1) {
@@ -21368,7 +21411,7 @@ Para visualizar:
21368
21411
  `;
21369
21412
  indexContent = indexContent.slice(0, titleEnd) + banner + indexContent.slice(titleEnd);
21370
21413
  }
21371
- await fs17.writeFile(courseIndexPath, indexContent, "utf-8");
21414
+ await fs18.writeFile(courseIndexPath, indexContent, "utf-8");
21372
21415
  } catch {
21373
21416
  }
21374
21417
  const syncedAt = marker.lastSyncedAt;
@@ -21688,16 +21731,16 @@ var init_brief_open = __esm({
21688
21731
  });
21689
21732
 
21690
21733
  // src/sessions/storage.ts
21691
- import fs18 from "node:fs";
21692
- import path22 from "node:path";
21734
+ import fs19 from "node:fs";
21735
+ import path23 from "node:path";
21693
21736
  function sessionsDir(workspacePath) {
21694
- return path22.join(workspacePath, ".tostudy", "sessions");
21737
+ return path23.join(workspacePath, ".tostudy", "sessions");
21695
21738
  }
21696
21739
  async function saveModuleSummary(workspacePath, input2) {
21697
21740
  const dir = sessionsDir(workspacePath);
21698
- fs18.mkdirSync(dir, { recursive: true });
21741
+ fs19.mkdirSync(dir, { recursive: true });
21699
21742
  const filename = `module-${input2.moduleId}-summary.md`;
21700
- const filePath = path22.join(dir, filename);
21743
+ const filePath = path23.join(dir, filename);
21701
21744
  const header = [
21702
21745
  "---",
21703
21746
  `moduleId: ${input2.moduleId}`,
@@ -21706,17 +21749,17 @@ async function saveModuleSummary(workspacePath, input2) {
21706
21749
  "---",
21707
21750
  ""
21708
21751
  ].join("\n");
21709
- fs18.writeFileSync(filePath, header + input2.summary, { mode: 384 });
21752
+ fs19.writeFileSync(filePath, header + input2.summary, { mode: 384 });
21710
21753
  logger25.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
21711
21754
  return filePath;
21712
21755
  }
21713
21756
  async function loadSessionContext(workspacePath) {
21714
21757
  const dir = sessionsDir(workspacePath);
21715
- if (!fs18.existsSync(dir)) return { moduleSummaries: [] };
21716
- const files = fs18.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
21758
+ if (!fs19.existsSync(dir)) return { moduleSummaries: [] };
21759
+ const files = fs19.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
21717
21760
  const summaries = [];
21718
21761
  for (const file2 of files) {
21719
- const content = fs18.readFileSync(path22.join(dir, file2), "utf-8");
21762
+ const content = fs19.readFileSync(path23.join(dir, file2), "utf-8");
21720
21763
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
21721
21764
  if (!frontmatterMatch) continue;
21722
21765
  const meta3 = frontmatterMatch[1];
@@ -21738,7 +21781,7 @@ var init_storage = __esm({
21738
21781
  });
21739
21782
 
21740
21783
  // src/commands/compact.ts
21741
- import fs19 from "node:fs";
21784
+ import fs20 from "node:fs";
21742
21785
  import { Command as Command26 } from "commander";
21743
21786
  var logger26, compactCommand;
21744
21787
  var init_compact = __esm({
@@ -21758,7 +21801,7 @@ var init_compact = __esm({
21758
21801
  if (opts.json) jsonError("no_workspace");
21759
21802
  error("Nenhum workspace encontrado.");
21760
21803
  }
21761
- const summary = fs19.readFileSync("/dev/stdin", "utf-8").trim();
21804
+ const summary = fs20.readFileSync("/dev/stdin", "utf-8").trim();
21762
21805
  if (!summary) {
21763
21806
  if (opts.json) jsonError("empty_summary", { message: "Summary vazio" });
21764
21807
  error("Summary vazio. Envie o conte\xFAdo via stdin.");
@@ -21797,13 +21840,14 @@ var init_context = __esm({
21797
21840
  init_workspace_state();
21798
21841
  init_storage();
21799
21842
  init_formatter();
21843
+ init_errors();
21800
21844
  logger27 = createLogger("cli:context");
21801
21845
  contextCommand = new Command27("context").description("Load session context (workspace state + module summaries) for LLM consumption").option("--json", "Output structured JSON").action(async (opts) => {
21802
21846
  try {
21803
21847
  const ws = await findWorkspaceState();
21804
21848
  if (!ws) {
21805
21849
  if (opts.json) jsonError("no_workspace");
21806
- error("Nenhum workspace encontrado. Rode: tostudy setup");
21850
+ error(getErrors().noActiveCourse.trimEnd());
21807
21851
  }
21808
21852
  const sessionCtx = await loadSessionContext(ws.workspacePath);
21809
21853
  const result = {
@@ -21858,13 +21902,14 @@ var init_memory = __esm({
21858
21902
  init_workspace_state();
21859
21903
  init_guards();
21860
21904
  init_formatter();
21905
+ init_errors();
21861
21906
  logger28 = createLogger("cli:memory");
21862
21907
  memoryCommand = new Command28("memory").description("Load accumulated student memory (learning profile + recent lessons) for the tutor").option("--json", "Output structured JSON").action(async (opts) => {
21863
21908
  try {
21864
21909
  const ws = await findWorkspaceState();
21865
21910
  if (!ws) {
21866
21911
  if (opts.json) return jsonError("no_workspace");
21867
- return error("Nenhum workspace encontrado. Rode: tostudy setup");
21912
+ return error(getErrors().noActiveCourse.trimEnd());
21868
21913
  }
21869
21914
  const { courseId, enrollmentId } = ws.state;
21870
21915
  if (!courseId || !enrollmentId) {
@@ -21901,6 +21946,7 @@ var init_insight = __esm({
21901
21946
  init_workspace_state();
21902
21947
  init_guards();
21903
21948
  init_formatter();
21949
+ init_errors();
21904
21950
  logger29 = createLogger("cli:insight");
21905
21951
  VALID_TYPES = ["difficulty", "breakthrough", "question"];
21906
21952
  insightCommand = new Command29("insight").description(
@@ -21919,7 +21965,7 @@ var init_insight = __esm({
21919
21965
  const ws = await findWorkspaceState();
21920
21966
  if (!ws) {
21921
21967
  if (opts.json) return jsonError("no_workspace");
21922
- return error("Nenhum workspace encontrado. Rode: tostudy setup");
21968
+ return error(getErrors().noActiveCourse.trimEnd());
21923
21969
  }
21924
21970
  const { courseId, enrollmentId } = ws.state;
21925
21971
  if (!courseId || !enrollmentId) {
@@ -23009,11 +23055,11 @@ __export(auto_updater_exports, {
23009
23055
  detectManager: () => detectManager,
23010
23056
  maybeAutoUpdate: () => maybeAutoUpdate
23011
23057
  });
23012
- import fs20 from "node:fs";
23013
- import path23 from "node:path";
23058
+ import fs21 from "node:fs";
23059
+ import path24 from "node:path";
23014
23060
  import { spawn as spawn2 } from "node:child_process";
23015
23061
  function detectManager(entryRealPath) {
23016
- const p = entryRealPath.split(path23.sep).join("/");
23062
+ const p = entryRealPath.split(path24.sep).join("/");
23017
23063
  if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23018
23064
  if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23019
23065
  if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
@@ -23021,10 +23067,10 @@ function detectManager(entryRealPath) {
23021
23067
  return null;
23022
23068
  }
23023
23069
  function installDir(entryRealPath) {
23024
- const normalized = entryRealPath.split(path23.sep).join("/");
23070
+ const normalized = entryRealPath.split(path24.sep).join("/");
23025
23071
  const marker = `/node_modules/${PACKAGE_NAME2}/`;
23026
23072
  const idx = normalized.indexOf(marker);
23027
- if (idx === -1) return path23.dirname(entryRealPath);
23073
+ if (idx === -1) return path24.dirname(entryRealPath);
23028
23074
  return entryRealPath.slice(0, idx + marker.length - 1);
23029
23075
  }
23030
23076
  function maybeAutoUpdate(currentVersion, opts = {}) {
@@ -23040,10 +23086,10 @@ function maybeAutoUpdate(currentVersion, opts = {}) {
23040
23086
  if (cache.attemptedVersion === cache.latest) return;
23041
23087
  const entryPath = opts.entryPath ?? argv[1];
23042
23088
  if (!entryPath) return;
23043
- const real = fs20.realpathSync(entryPath);
23089
+ const real = fs21.realpathSync(entryPath);
23044
23090
  const mgr = detectManager(real);
23045
23091
  if (!mgr) return;
23046
- fs20.accessSync(installDir(real), fs20.constants.W_OK);
23092
+ fs21.accessSync(installDir(real), fs21.constants.W_OK);
23047
23093
  writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
23048
23094
  process.stderr.write(
23049
23095
  `