@tostudy-ai/cli 0.16.10 → 0.16.12

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
@@ -1058,8 +1058,9 @@ async function cliApiFetch(url2, token, init) {
1058
1058
  if (!response.ok) {
1059
1059
  const code = typeof body.error === "string" && body.error.length > 0 ? body.error : void 0;
1060
1060
  const friendly = typeof body.message === "string" && body.message.length > 0 ? body.message : void 0;
1061
+ const unauthenticated = response.status === 401 ? "Sess\xE3o inv\xE1lida ou expirada. Rode: tostudy login" : void 0;
1061
1062
  throw new CliApiError(
1062
- friendly ?? code ?? `API error ${response.status}`,
1063
+ friendly ?? unauthenticated ?? code ?? `API error ${response.status}`,
1063
1064
  response.status,
1064
1065
  code
1065
1066
  );
@@ -2617,11 +2618,18 @@ var init_login = __esm({
2617
2618
  }
2618
2619
  if (opts.manual) {
2619
2620
  console.log(`
2620
- Acesse este endere\xE7o no seu browser:`);
2621
- console.log(` ${apiUrl}/api/cli/auth/authorize?port=${PORT}
2621
+ Login sem browser nesta m\xE1quina:`);
2622
+ console.log(` 1. Em um computador com browser, acesse:`);
2623
+ console.log(` ${apiUrl}/api/cli/auth/authorize`);
2624
+ console.log(
2625
+ ` 2. Ap\xF3s autorizar, o browser tenta abrir http://localhost:9876/callback?code=...`
2626
+ );
2627
+ console.log(
2628
+ ` e falha (n\xE3o h\xE1 CLI nesse computador). Copie o valor de "code" da barra de endere\xE7o.`
2629
+ );
2630
+ console.log(` 3. Em at\xE9 5 minutos, volte aqui e rode:`);
2631
+ console.log(` tostudy login --code <code>
2622
2632
  `);
2623
- console.log(" Ap\xF3s autorizar, o browser ser\xE1 redirecionado e o login ser\xE1 conclu\xEDdo.");
2624
- console.log(" Certifique-se de rodar este comando sem --manual para o fluxo autom\xE1tico.\n");
2625
2633
  return;
2626
2634
  }
2627
2635
  console.log("\n Abrindo browser para autentica\xE7\xE3o...\n");
@@ -2829,18 +2837,6 @@ var init_errors_en_us = __esm({
2829
2837
  });
2830
2838
 
2831
2839
  // 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
- }
2844
2840
  function resolveLocale() {
2845
2841
  if (_cachedLocale !== null) return _cachedLocale;
2846
2842
  const argv = process.argv;
@@ -2852,8 +2848,8 @@ function resolveLocale() {
2852
2848
  return val;
2853
2849
  }
2854
2850
  }
2855
- const stored = readStoredLocale();
2856
- if (stored) {
2851
+ const stored = process.env["TOSTUDY_LOCALE"];
2852
+ if (stored === "pt-BR" || stored === "en-US") {
2857
2853
  _cachedLocale = stored;
2858
2854
  return stored;
2859
2855
  }
@@ -2888,7 +2884,6 @@ var BUNDLES, _cachedLocale;
2888
2884
  var init_errors = __esm({
2889
2885
  "src/errors/index.ts"() {
2890
2886
  "use strict";
2891
- init_config_dir();
2892
2887
  init_errors_pt_br();
2893
2888
  init_errors_en_us();
2894
2889
  BUNDLES = {
@@ -2902,12 +2897,12 @@ var init_errors = __esm({
2902
2897
  // src/workspace/workspace-marker.ts
2903
2898
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "node:fs";
2904
2899
  import os4 from "node:os";
2905
- import path8 from "node:path";
2900
+ import path7 from "node:path";
2906
2901
  function resolveMarkerPath(workspacePath) {
2907
- if (path8.basename(workspacePath) === ".tostudy") {
2908
- return path8.join(workspacePath, "workspace.json");
2902
+ if (path7.basename(workspacePath) === ".tostudy") {
2903
+ return path7.join(workspacePath, "workspace.json");
2909
2904
  }
2910
- return path8.join(workspacePath, ".tostudy", "workspace.json");
2905
+ return path7.join(workspacePath, ".tostudy", "workspace.json");
2911
2906
  }
2912
2907
  async function readWorkspaceMarker(workspacePath) {
2913
2908
  const filePath = resolveMarkerPath(workspacePath);
@@ -2925,13 +2920,13 @@ async function readWorkspaceMarker(workspacePath) {
2925
2920
  }
2926
2921
  }
2927
2922
  async function writeWorkspaceMarker(workspacePath, input2, options = {}) {
2928
- const homeDir = path8.resolve(options.homeDir ?? os4.homedir());
2929
- const resolvedWorkspace = path8.resolve(workspacePath);
2930
- if (resolvedWorkspace === homeDir || homeDir.startsWith(resolvedWorkspace + path8.sep)) {
2923
+ const homeDir = path7.resolve(options.homeDir ?? os4.homedir());
2924
+ const resolvedWorkspace = path7.resolve(workspacePath);
2925
+ if (resolvedWorkspace === homeDir || homeDir.startsWith(resolvedWorkspace + path7.sep)) {
2931
2926
  throw new Error(getErrors().workspaceRefusedAtHome);
2932
2927
  }
2933
2928
  const filePath = resolveMarkerPath(workspacePath);
2934
- const dir = path8.dirname(filePath);
2929
+ const dir = path7.dirname(filePath);
2935
2930
  if (!existsSync3(dir)) {
2936
2931
  mkdirSync3(dir, { recursive: true });
2937
2932
  }
@@ -2968,7 +2963,7 @@ __export(workspace_state_exports, {
2968
2963
  });
2969
2964
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
2970
2965
  import os5 from "node:os";
2971
- import path9 from "node:path";
2966
+ import path8 from "node:path";
2972
2967
  async function readWorkspaceState(workspacePath) {
2973
2968
  const marker = await readWorkspaceMarker(workspacePath);
2974
2969
  if (!marker) return null;
@@ -2997,13 +2992,13 @@ async function readWorkspaceState(workspacePath) {
2997
2992
  }
2998
2993
  }
2999
2994
  async function findWorkspaceState(startCwd = process.cwd(), homeDir = os5.homedir()) {
3000
- const home = path9.resolve(homeDir);
3001
- let current = path9.resolve(startCwd);
2995
+ const home = path8.resolve(homeDir);
2996
+ let current = path8.resolve(startCwd);
3002
2997
  while (true) {
3003
2998
  if (current === home) return null;
3004
2999
  const state = await readWorkspaceState(current);
3005
3000
  if (state) return { state, workspacePath: current };
3006
- const parent = path9.dirname(current);
3001
+ const parent = path8.dirname(current);
3007
3002
  if (parent === current) return null;
3008
3003
  current = parent;
3009
3004
  }
@@ -3072,7 +3067,7 @@ __export(course_state_exports, {
3072
3067
  setCourseWorkspacePath: () => setCourseWorkspacePath,
3073
3068
  setLastInitCourseId: () => setLastInitCourseId
3074
3069
  });
3075
- import fs7 from "node:fs";
3070
+ import fs6 from "node:fs";
3076
3071
  async function getCourseOnboardingState(courseId, configDir) {
3077
3072
  const state = readCourseOnboardingState(configDir);
3078
3073
  return state[courseId] ?? null;
@@ -3094,7 +3089,7 @@ async function saveCourseLearnerProfile(course, learnerProfile, artifacts, confi
3094
3089
  }
3095
3090
  async function clearAllCourseOnboarding(configDir) {
3096
3091
  const onboardingPath = getCourseOnboardingPath(configDir);
3097
- if (fs7.existsSync(onboardingPath)) fs7.unlinkSync(onboardingPath);
3092
+ if (fs6.existsSync(onboardingPath)) fs6.unlinkSync(onboardingPath);
3098
3093
  }
3099
3094
  var init_course_state = __esm({
3100
3095
  "src/onboarding/course-state.ts"() {
@@ -3137,14 +3132,14 @@ __export(resolve_exports, {
3137
3132
  resolveVaultPath: () => resolveVaultPath,
3138
3133
  resolveWorkspace: () => resolveWorkspace
3139
3134
  });
3140
- import fs8 from "node:fs/promises";
3141
- import path10 from "node:path";
3135
+ import fs7 from "node:fs/promises";
3136
+ import path9 from "node:path";
3142
3137
  import os6 from "node:os";
3143
3138
  async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
3144
3139
  const slug = courseSlug(courseTitle);
3145
- const candidate = path10.join(basePath, slug);
3140
+ const candidate = path9.join(basePath, slug);
3146
3141
  try {
3147
- await fs8.access(path10.join(candidate, ".ana-config.json"));
3142
+ await fs7.access(path9.join(candidate, ".ana-config.json"));
3148
3143
  return { found: true, workspacePath: candidate, source: "default" };
3149
3144
  } catch {
3150
3145
  return { found: false, workspacePath: null };
@@ -3154,49 +3149,49 @@ async function isCwdWorkspace(cwd = process.cwd()) {
3154
3149
  return await resolveCwdWorkspacePath(cwd) !== null;
3155
3150
  }
3156
3151
  async function resolveCwdWorkspacePath(cwd = process.cwd()) {
3157
- const tostudyDir = path10.join(cwd, ".tostudy");
3152
+ const tostudyDir = path9.join(cwd, ".tostudy");
3158
3153
  try {
3159
- const stat = await fs8.stat(tostudyDir);
3154
+ const stat = await fs7.stat(tostudyDir);
3160
3155
  if (stat.isDirectory()) return tostudyDir;
3161
3156
  } catch {
3162
3157
  }
3163
3158
  try {
3164
- await fs8.access(path10.join(cwd, ".ana-config.json"));
3159
+ await fs7.access(path9.join(cwd, ".ana-config.json"));
3165
3160
  return cwd;
3166
3161
  } catch {
3167
3162
  return null;
3168
3163
  }
3169
3164
  }
3170
3165
  async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
3171
- const home = path10.resolve(homeDir);
3172
- let current = path10.resolve(startCwd);
3166
+ const home = path9.resolve(homeDir);
3167
+ let current = path9.resolve(startCwd);
3173
3168
  while (true) {
3174
3169
  if (current === home) return null;
3175
3170
  const direct = await resolveCwdWorkspacePath(current);
3176
3171
  if (direct) return direct;
3177
- const parent = path10.dirname(current);
3172
+ const parent = path9.dirname(current);
3178
3173
  if (parent === current) return null;
3179
3174
  current = parent;
3180
3175
  }
3181
3176
  }
3182
3177
  function resolveVaultPath(workspacePath, slug) {
3183
- const base = path10.basename(workspacePath) === ".tostudy" ? path10.dirname(workspacePath) : workspacePath;
3184
- return path10.join(base, `vault-${slug}`);
3178
+ const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
3179
+ return path9.join(base, `vault-${slug}`);
3185
3180
  }
3186
3181
  function cliWorkspacePaths(workspacePath, courseTitle) {
3187
- const base = path10.basename(workspacePath) === ".tostudy" ? path10.dirname(workspacePath) : workspacePath;
3182
+ const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
3188
3183
  return { workspace: base, vault: resolveVaultPath(workspacePath, courseSlug(courseTitle)) };
3189
3184
  }
3190
3185
  async function findExistingVault(workspacePath, slug) {
3191
3186
  const slugged = resolveVaultPath(workspacePath, slug);
3192
3187
  try {
3193
- await fs8.access(path10.join(slugged, ".ana-vault.json"));
3188
+ await fs7.access(path9.join(slugged, ".ana-vault.json"));
3194
3189
  return slugged;
3195
3190
  } catch {
3196
3191
  }
3197
- const legacy = path10.join(workspacePath, "vault");
3192
+ const legacy = path9.join(workspacePath, "vault");
3198
3193
  try {
3199
- await fs8.access(path10.join(legacy, ".ana-vault.json"));
3194
+ await fs7.access(path9.join(legacy, ".ana-vault.json"));
3200
3195
  return legacy;
3201
3196
  } catch {
3202
3197
  return null;
@@ -3209,7 +3204,7 @@ async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.
3209
3204
  }
3210
3205
  if (storedPath) {
3211
3206
  try {
3212
- await fs8.access(path10.join(storedPath, ".ana-config.json"));
3207
+ await fs7.access(path9.join(storedPath, ".ana-config.json"));
3213
3208
  return { found: true, workspacePath: storedPath, source: "stored" };
3214
3209
  } catch {
3215
3210
  }
@@ -3230,37 +3225,50 @@ var init_resolve = __esm({
3230
3225
  "src/workspace/resolve.ts"() {
3231
3226
  "use strict";
3232
3227
  init_slug();
3233
- DEFAULT_BASE = path10.join(os6.homedir(), "study");
3228
+ DEFAULT_BASE = path9.join(os6.homedir(), "study");
3234
3229
  courseSlug = slugify;
3235
3230
  resolveActiveCourseWithOverride = resolveActiveCourseFromWorkspace;
3236
3231
  }
3237
3232
  });
3238
3233
 
3239
3234
  // src/auth/guards.ts
3235
+ var guards_exports = {};
3236
+ __export(guards_exports, {
3237
+ requireActiveCourse: () => requireActiveCourse,
3238
+ requireSession: () => requireSession
3239
+ });
3240
3240
  async function refreshCliSession(session, configDir) {
3241
+ let response;
3241
3242
  try {
3242
- const response = await fetch(`${session.apiUrl}/api/cli/auth/refresh`, {
3243
+ response = await fetch(`${session.apiUrl}/api/cli/auth/refresh`, {
3243
3244
  method: "POST",
3244
3245
  headers: { "Content-Type": "application/json" },
3245
3246
  body: JSON.stringify({ refreshToken: session.refreshToken })
3246
3247
  });
3247
- if (!response.ok) {
3248
- await clearSessionArtifacts(configDir);
3249
- return null;
3250
- }
3251
- const data = await response.json();
3252
- const refreshed = {
3253
- ...session,
3254
- token: data.token,
3255
- refreshToken: data.refreshToken,
3256
- expiresAt: data.expiresAt
3257
- };
3258
- await saveSession(refreshed, configDir);
3259
- return refreshed;
3260
- } catch {
3248
+ } catch (err) {
3249
+ return { status: "unavailable", reason: err instanceof Error ? err.message : String(err) };
3250
+ }
3251
+ if (response.status === 401 || response.status === 403) {
3261
3252
  await clearSessionArtifacts(configDir);
3262
- return null;
3253
+ return { status: "rejected" };
3254
+ }
3255
+ if (!response.ok) {
3256
+ return { status: "unavailable", reason: `HTTP ${response.status}` };
3257
+ }
3258
+ let data;
3259
+ try {
3260
+ data = await response.json();
3261
+ } catch (err) {
3262
+ return { status: "unavailable", reason: err instanceof Error ? err.message : String(err) };
3263
3263
  }
3264
+ const refreshed = {
3265
+ ...session,
3266
+ token: data.token,
3267
+ refreshToken: data.refreshToken,
3268
+ expiresAt: data.expiresAt
3269
+ };
3270
+ await saveSession(refreshed, configDir);
3271
+ return { status: "ok", session: refreshed };
3264
3272
  }
3265
3273
  async function requireSession(configDir) {
3266
3274
  const session = await getSession(configDir);
@@ -3270,12 +3278,17 @@ async function requireSession(configDir) {
3270
3278
  }
3271
3279
  const expiresAt = new Date(session.expiresAt);
3272
3280
  if (expiresAt < /* @__PURE__ */ new Date()) {
3273
- const refreshed = await refreshCliSession(session, configDir);
3274
- if (!refreshed) {
3281
+ const outcome = await refreshCliSession(session, configDir);
3282
+ if (outcome.status === "ok") return outcome.session;
3283
+ if (outcome.status === "rejected") {
3275
3284
  process.stderr.write("Erro: Sess\xE3o expirada. Rode: tostudy login\n");
3276
3285
  process.exit(2);
3277
3286
  }
3278
- return refreshed;
3287
+ process.stderr.write(
3288
+ `Erro: N\xE3o foi poss\xEDvel renovar a sess\xE3o (${outcome.reason}). Sua sess\xE3o foi mantida \u2014 verifique a conex\xE3o e tente de novo.
3289
+ `
3290
+ );
3291
+ process.exit(4);
3279
3292
  }
3280
3293
  return session;
3281
3294
  }
@@ -3426,7 +3439,7 @@ var CLI_VERSION;
3426
3439
  var init_version = __esm({
3427
3440
  "src/version.ts"() {
3428
3441
  "use strict";
3429
- CLI_VERSION = true ? "0.16.9" : "0.7.1";
3442
+ CLI_VERSION = true ? "0.16.12" : "0.7.1";
3430
3443
  }
3431
3444
  });
3432
3445
 
@@ -3440,13 +3453,13 @@ __export(update_checker_exports, {
3440
3453
  readCache: () => readCache,
3441
3454
  writeCache: () => writeCache
3442
3455
  });
3443
- import fs9 from "node:fs";
3444
- import path11 from "node:path";
3456
+ import fs8 from "node:fs";
3457
+ import path10 from "node:path";
3445
3458
  function readCache(configDir) {
3446
3459
  try {
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"));
3460
+ const p = path10.join(configDir ?? getConfigDir(), CACHE_FILE);
3461
+ if (!fs8.existsSync(p)) return null;
3462
+ return JSON.parse(fs8.readFileSync(p, "utf-8"));
3450
3463
  } catch {
3451
3464
  return null;
3452
3465
  }
@@ -3454,8 +3467,8 @@ function readCache(configDir) {
3454
3467
  function writeCache(cache, configDir) {
3455
3468
  try {
3456
3469
  const dir = configDir ?? getConfigDir();
3457
- fs9.mkdirSync(dir, { recursive: true });
3458
- fs9.writeFileSync(path11.join(dir, CACHE_FILE), JSON.stringify(cache));
3470
+ fs8.mkdirSync(dir, { recursive: true });
3471
+ fs8.writeFileSync(path10.join(dir, CACHE_FILE), JSON.stringify(cache));
3459
3472
  } catch {
3460
3473
  }
3461
3474
  }
@@ -3525,17 +3538,17 @@ var init_update_checker = __esm({
3525
3538
  });
3526
3539
 
3527
3540
  // src/learner-brief/cache.ts
3528
- import fs10 from "node:fs";
3529
- import path12 from "node:path";
3541
+ import fs9 from "node:fs";
3542
+ import path11 from "node:path";
3530
3543
  function resolveCachePath(configDir) {
3531
- return path12.join(configDir, "student-brief.json");
3544
+ return path11.join(configDir, "student-brief.json");
3532
3545
  }
3533
3546
  async function readBriefCache(configDir) {
3534
3547
  const dir = configDir ?? getConfigDir();
3535
3548
  const p = resolveCachePath(dir);
3536
- if (!fs10.existsSync(p)) return null;
3549
+ if (!fs9.existsSync(p)) return null;
3537
3550
  try {
3538
- const raw = fs10.readFileSync(p, "utf-8");
3551
+ const raw = fs9.readFileSync(p, "utf-8");
3539
3552
  return JSON.parse(raw);
3540
3553
  } catch {
3541
3554
  return null;
@@ -3543,8 +3556,8 @@ async function readBriefCache(configDir) {
3543
3556
  }
3544
3557
  async function writeBriefCache(configDir, cache) {
3545
3558
  const dir = configDir ?? getConfigDir();
3546
- fs10.mkdirSync(dir, { recursive: true });
3547
- fs10.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
3559
+ fs9.mkdirSync(dir, { recursive: true });
3560
+ fs9.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
3548
3561
  }
3549
3562
  var init_cache = __esm({
3550
3563
  "src/learner-brief/cache.ts"() {
@@ -3554,23 +3567,23 @@ var init_cache = __esm({
3554
3567
  });
3555
3568
 
3556
3569
  // src/tutor-persona/cache.ts
3557
- import fs11 from "node:fs";
3558
- import path13 from "node:path";
3570
+ import fs10 from "node:fs";
3571
+ import path12 from "node:path";
3559
3572
  function resolveCachePath2(configDir) {
3560
- return path13.join(configDir, "tutor-personalities.json");
3573
+ return path12.join(configDir, "tutor-personalities.json");
3561
3574
  }
3562
3575
  function readAll(configDir) {
3563
3576
  const p = resolveCachePath2(configDir);
3564
- if (!fs11.existsSync(p)) return {};
3577
+ if (!fs10.existsSync(p)) return {};
3565
3578
  try {
3566
- return JSON.parse(fs11.readFileSync(p, "utf-8"));
3579
+ return JSON.parse(fs10.readFileSync(p, "utf-8"));
3567
3580
  } catch {
3568
3581
  return {};
3569
3582
  }
3570
3583
  }
3571
3584
  function writeAll(configDir, cache) {
3572
- fs11.mkdirSync(configDir, { recursive: true });
3573
- fs11.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
3585
+ fs10.mkdirSync(configDir, { recursive: true });
3586
+ fs10.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
3574
3587
  }
3575
3588
  async function readPersonaCacheForCourse(configDir, courseId) {
3576
3589
  const dir = configDir ?? getConfigDir();
@@ -4385,6 +4398,7 @@ var init_select = __esm({
4385
4398
  interactive: isInteractiveTerminal(opts.json)
4386
4399
  });
4387
4400
  let pipelineResult = null;
4401
+ let pipelineError = null;
4388
4402
  try {
4389
4403
  pipelineResult = await resolveAndGenerate(
4390
4404
  { courseId: detail.courseId, enrollmentId },
@@ -4395,9 +4409,8 @@ var init_select = __esm({
4395
4409
  }
4396
4410
  );
4397
4411
  } catch (err) {
4398
- logger8.warn("Failed to run instruction pipeline", {
4399
- error: err instanceof Error ? err.message : String(err)
4400
- });
4412
+ pipelineError = err instanceof Error ? err.message : String(err);
4413
+ logger8.warn("Failed to run instruction pipeline", { error: pipelineError });
4401
4414
  }
4402
4415
  const slugMatch = pipelineResult?.wroteFiles.find((f) => f.startsWith(".claude/commands/"))?.match(/tostudy-(.+)\.md$/);
4403
4416
  const courseSlug2 = slugMatch?.[1] ?? "";
@@ -4415,7 +4428,8 @@ var init_select = __esm({
4415
4428
  usedT0: pipelineResult?.usedT0 ?? false,
4416
4429
  usedT1: pipelineResult?.usedT1 ?? false,
4417
4430
  usedT2: pipelineResult?.usedT2 ?? false,
4418
- instruction: pipelineResult?.renderedInstruction ?? null
4431
+ instruction: pipelineResult?.renderedInstruction ?? null,
4432
+ pipelineError
4419
4433
  },
4420
4434
  { json: true }
4421
4435
  );
@@ -4439,7 +4453,8 @@ var init_select = __esm({
4439
4453
  ` Progresso: ${detail.progress}% | ${detail.moduleCount} m\xF3dulos | ${detail.lessonCount} li\xE7\xF5es`,
4440
4454
  wsLine,
4441
4455
  "",
4442
- " Arquivos de contexto criados para seu assistente AI."
4456
+ pipelineError ? `\u26A0 Arquivos de contexto N\xC3O foram criados (${pipelineError}).
4457
+ Rode \`tostudy select\` de novo com a conex\xE3o ativa.` : " Arquivos de contexto criados para seu assistente AI."
4443
4458
  ];
4444
4459
  if (pipelineResult && !pipelineResult.usedT0) {
4445
4460
  lines.push(" (sem persona do creator \u2014 usando tutor neutro)");
@@ -4813,7 +4828,7 @@ var init_next = __esm({
4813
4828
 
4814
4829
  // src/commands/lesson.ts
4815
4830
  import { Command as Command11 } from "commander";
4816
- import path14 from "node:path";
4831
+ import path13 from "node:path";
4817
4832
  function adjustTimeEstimate(type, baseMinutes) {
4818
4833
  if (type === "exercise") return Math.max(baseMinutes, 30);
4819
4834
  return baseMinutes;
@@ -4826,7 +4841,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
4826
4841
  const isPractical = data.type === "exercise" || isCheckpoint(data.title);
4827
4842
  const lines = [`\u2501\u2501\u2501 Li\xE7\xE3o: ${data.title} \u2501\u2501\u2501`];
4828
4843
  if (isPractical && workspacePath) {
4829
- const base = path14.basename(workspacePath) === ".tostudy" ? path14.dirname(workspacePath) : workspacePath;
4844
+ const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
4830
4845
  const slug = courseSlug(courseTitle);
4831
4846
  const vaultPath = resolveVaultPath(workspacePath, slug);
4832
4847
  lines.push(
@@ -5370,10 +5385,10 @@ function mergeDefs(...defs) {
5370
5385
  function cloneDef(schema) {
5371
5386
  return mergeDefs(schema._zod.def);
5372
5387
  }
5373
- function getElementAtPath(obj, path25) {
5374
- if (!path25)
5388
+ function getElementAtPath(obj, path24) {
5389
+ if (!path24)
5375
5390
  return obj;
5376
- return path25.reduce((acc, key) => acc?.[key], obj);
5391
+ return path24.reduce((acc, key) => acc?.[key], obj);
5377
5392
  }
5378
5393
  function promiseAllObject(promisesObj) {
5379
5394
  const keys = Object.keys(promisesObj);
@@ -5685,11 +5700,11 @@ function aborted(x, startIndex = 0) {
5685
5700
  }
5686
5701
  return false;
5687
5702
  }
5688
- function prefixIssues(path25, issues) {
5703
+ function prefixIssues(path24, issues) {
5689
5704
  return issues.map((iss) => {
5690
5705
  var _a2;
5691
5706
  (_a2 = iss).path ?? (_a2.path = []);
5692
- iss.path.unshift(path25);
5707
+ iss.path.unshift(path24);
5693
5708
  return iss;
5694
5709
  });
5695
5710
  }
@@ -5931,7 +5946,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
5931
5946
  }
5932
5947
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
5933
5948
  const result = { errors: [] };
5934
- const processError = (error50, path25 = []) => {
5949
+ const processError = (error50, path24 = []) => {
5935
5950
  var _a2, _b;
5936
5951
  for (const issue2 of error50.issues) {
5937
5952
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -5941,7 +5956,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
5941
5956
  } else if (issue2.code === "invalid_element") {
5942
5957
  processError({ issues: issue2.issues }, issue2.path);
5943
5958
  } else {
5944
- const fullpath = [...path25, ...issue2.path];
5959
+ const fullpath = [...path24, ...issue2.path];
5945
5960
  if (fullpath.length === 0) {
5946
5961
  result.errors.push(mapper(issue2));
5947
5962
  continue;
@@ -5973,8 +5988,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
5973
5988
  }
5974
5989
  function toDotPath(_path) {
5975
5990
  const segs = [];
5976
- const path25 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5977
- for (const seg of path25) {
5991
+ const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
5992
+ for (const seg of path24) {
5978
5993
  if (typeof seg === "number")
5979
5994
  segs.push(`[${seg}]`);
5980
5995
  else if (typeof seg === "symbol")
@@ -18668,13 +18683,13 @@ function resolveRef(ref, ctx) {
18668
18683
  if (!ref.startsWith("#")) {
18669
18684
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
18670
18685
  }
18671
- const path25 = ref.slice(1).split("/").filter(Boolean);
18672
- if (path25.length === 0) {
18686
+ const path24 = ref.slice(1).split("/").filter(Boolean);
18687
+ if (path24.length === 0) {
18673
18688
  return ctx.rootSchema;
18674
18689
  }
18675
18690
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
18676
- if (path25[0] === defsKey) {
18677
- const key = path25[1];
18691
+ if (path24[0] === defsKey) {
18692
+ const key = path24[1];
18678
18693
  if (!key || !ctx.defs[key]) {
18679
18694
  throw new Error(`Reference not found: ${ref}`);
18680
18695
  }
@@ -20006,8 +20021,8 @@ var init_init_template = __esm({
20006
20021
  });
20007
20022
 
20008
20023
  // src/commands/validate.ts
20009
- import fs12 from "node:fs";
20010
- import path15 from "node:path";
20024
+ import fs11 from "node:fs";
20025
+ import path14 from "node:path";
20011
20026
  import { Command as Command14 } from "commander";
20012
20027
  var logger16, validateCommand;
20013
20028
  var init_validate = __esm({
@@ -20051,21 +20066,21 @@ var init_validate = __esm({
20051
20066
  }
20052
20067
  let solution;
20053
20068
  if (opts.stdin) {
20054
- solution = fs12.readFileSync("/dev/stdin", "utf-8");
20069
+ solution = fs11.readFileSync("/dev/stdin", "utf-8");
20055
20070
  } else if (file2) {
20056
- if (!fs12.existsSync(file2)) {
20071
+ if (!fs11.existsSync(file2)) {
20057
20072
  if (opts.json)
20058
20073
  jsonError("file_not_found", { message: `Arquivo n\xE3o encontrado: ${file2}` });
20059
20074
  error(`Arquivo n\xE3o encontrado: ${file2}`);
20060
20075
  }
20061
- solution = fs12.readFileSync(file2, "utf-8");
20076
+ solution = fs11.readFileSync(file2, "utf-8");
20062
20077
  } else {
20063
20078
  if (opts.json)
20064
20079
  jsonError("no_solution_provided", { message: "Forne\xE7a um arquivo ou use --stdin" });
20065
20080
  error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
20066
20081
  }
20067
20082
  if (file2 && activeCourse.courseTags?.length) {
20068
- const ext = path15.extname(file2).toLowerCase();
20083
+ const ext = path14.extname(file2).toLowerCase();
20069
20084
  const LANG_EXTENSIONS = {
20070
20085
  ".html": ["html", "html5"],
20071
20086
  ".css": ["css"],
@@ -20440,15 +20455,27 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
20440
20455
  progressData,
20441
20456
  learnerProfile
20442
20457
  );
20443
- await deps.saveRemoteEnrollmentOnboarding({
20444
- apiUrl: session.apiUrl,
20445
- token: session.token,
20446
- enrollmentId: activeCourse.enrollmentId,
20447
- learnerBrief: artifacts.learnerBrief,
20448
- learnerProfile,
20449
- source: "cli"
20450
- });
20451
20458
  await deps.saveCourseLearnerProfile(activeCourse, learnerProfile, artifacts);
20459
+ try {
20460
+ await deps.saveRemoteEnrollmentOnboarding({
20461
+ apiUrl: session.apiUrl,
20462
+ token: session.token,
20463
+ enrollmentId: activeCourse.enrollmentId,
20464
+ learnerBrief: artifacts.learnerBrief,
20465
+ learnerProfile,
20466
+ source: "cli"
20467
+ });
20468
+ } catch (err) {
20469
+ const reason = err instanceof Error ? err.message : String(err);
20470
+ deps.logger.warn("Failed to save enrollment onboarding remotely", { error: reason });
20471
+ if (!flags.json) {
20472
+ process.stderr.write(
20473
+ `\u26A0 Suas respostas foram salvas neste computador, mas n\xE3o chegaram ao servidor (${reason}).
20474
+ Rode \`tostudy init\` de novo com a conex\xE3o ativa para sincronizar.
20475
+ `
20476
+ );
20477
+ }
20478
+ }
20452
20479
  try {
20453
20480
  const rootAgents = await resolveRootAgentsConsent({
20454
20481
  cwd: process.cwd(),
@@ -20506,7 +20533,13 @@ var init_init = __esm({
20506
20533
  init_pipeline_deps();
20507
20534
  logger17 = createLogger("cli:init");
20508
20535
  defaultDeps4 = {
20509
- getSession,
20536
+ // GH #1419 item 3 — `init` used the raw session, so an expired access
20537
+ // token met the POST as a 401. Refresh through the guard when one exists.
20538
+ getSession: async () => {
20539
+ if (!await getSession()) return null;
20540
+ const { requireSession: requireSession2 } = await Promise.resolve().then(() => (init_guards(), guards_exports));
20541
+ return requireSession2();
20542
+ },
20510
20543
  findWorkspaceState,
20511
20544
  listCourses,
20512
20545
  getProgress,
@@ -20533,14 +20566,14 @@ var init_init = __esm({
20533
20566
  });
20534
20567
 
20535
20568
  // ../../packages/tostudy-core/src/workspace/setup-workspace.ts
20536
- import fs13 from "node:fs/promises";
20537
- import path16 from "node:path";
20569
+ import fs12 from "node:fs/promises";
20570
+ import path15 from "node:path";
20538
20571
  async function setupWorkspace(input2) {
20539
- const workspacePath = path16.join(input2.basePath, input2.courseSlug);
20572
+ const workspacePath = path15.join(input2.basePath, input2.courseSlug);
20540
20573
  for (const dir of WORKSPACE_DIRS) {
20541
- await fs13.mkdir(path16.join(workspacePath, dir), { recursive: true });
20574
+ await fs12.mkdir(path15.join(workspacePath, dir), { recursive: true });
20542
20575
  }
20543
- const configPath = path16.join(workspacePath, ".ana-config.json");
20576
+ const configPath = path15.join(workspacePath, ".ana-config.json");
20544
20577
  const config2 = {
20545
20578
  courseId: input2.courseId,
20546
20579
  courseSlug: input2.courseSlug,
@@ -20550,7 +20583,7 @@ async function setupWorkspace(input2) {
20550
20583
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
20551
20584
  lastAccessedAt: (/* @__PURE__ */ new Date()).toISOString()
20552
20585
  };
20553
- await fs13.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20586
+ await fs12.writeFile(configPath, JSON.stringify(config2, null, 2), "utf-8");
20554
20587
  const readme = [
20555
20588
  `# ${input2.courseName}`,
20556
20589
  "",
@@ -20575,7 +20608,7 @@ async function setupWorkspace(input2) {
20575
20608
  "tostudy vault sync # Sincronizar progresso",
20576
20609
  "```"
20577
20610
  ].join("\n");
20578
- await fs13.writeFile(path16.join(workspacePath, "README.md"), readme, "utf-8");
20611
+ await fs12.writeFile(path15.join(workspacePath, "README.md"), readme, "utf-8");
20579
20612
  return { workspacePath, directories: WORKSPACE_DIRS, configPath };
20580
20613
  }
20581
20614
  var WORKSPACE_DIRS;
@@ -20676,8 +20709,8 @@ var init_templates = __esm({
20676
20709
  });
20677
20710
 
20678
20711
  // ../../packages/tostudy-core/src/workspace/extract-exercise.ts
20679
- import fs14 from "node:fs/promises";
20680
- import path17 from "node:path";
20712
+ import fs13 from "node:fs/promises";
20713
+ import path16 from "node:path";
20681
20714
  function padOrder(n) {
20682
20715
  return String(n).padStart(2, "0");
20683
20716
  }
@@ -20703,16 +20736,16 @@ async function extractExercise(input2) {
20703
20736
  const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
20704
20737
  const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
20705
20738
  const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
20706
- const exercisePath = path17.join(workspacePath, "exercises", moduleDir, lessonDir);
20707
- await fs14.mkdir(exercisePath, { recursive: true });
20739
+ const exercisePath = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
20740
+ await fs13.mkdir(exercisePath, { recursive: true });
20708
20741
  const extractedFiles = [];
20709
20742
  let hasStarterCode = false;
20710
20743
  if (lessonData.sandpackConfig?.files) {
20711
20744
  for (const [filePath, fileData] of Object.entries(lessonData.sandpackConfig.files)) {
20712
20745
  const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
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");
20746
+ const fullPath = path16.join(exercisePath, cleanPath);
20747
+ await fs13.mkdir(path16.dirname(fullPath), { recursive: true });
20748
+ await fs13.writeFile(fullPath, fileData.code, "utf-8");
20716
20749
  extractedFiles.push(cleanPath);
20717
20750
  hasStarterCode = true;
20718
20751
  }
@@ -20720,13 +20753,13 @@ async function extractExercise(input2) {
20720
20753
  const tierData = getTierData(lessonData.structuredData, exerciseTier);
20721
20754
  const tierCode = tierData?.code;
20722
20755
  if (tierCode) {
20723
- await fs14.writeFile(path17.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20756
+ await fs13.writeFile(path16.join(exercisePath, "exercise.js"), tierCode, "utf-8");
20724
20757
  extractedFiles.push("exercise.js");
20725
20758
  hasStarterCode = true;
20726
20759
  } else {
20727
20760
  const starter = getStarterCode(lessonData.structuredData);
20728
20761
  if (starter) {
20729
- await fs14.writeFile(path17.join(exercisePath, "exercise.js"), starter, "utf-8");
20762
+ await fs13.writeFile(path16.join(exercisePath, "exercise.js"), starter, "utf-8");
20730
20763
  extractedFiles.push("exercise.js");
20731
20764
  hasStarterCode = true;
20732
20765
  }
@@ -20744,8 +20777,8 @@ async function extractExercise(input2) {
20744
20777
  ...exerciseDeps
20745
20778
  }
20746
20779
  };
20747
- await fs14.writeFile(
20748
- path17.join(exercisePath, "package.json"),
20780
+ await fs13.writeFile(
20781
+ path16.join(exercisePath, "package.json"),
20749
20782
  JSON.stringify(pkgJson, null, 2),
20750
20783
  "utf-8"
20751
20784
  );
@@ -20757,20 +20790,20 @@ async function extractExercise(input2) {
20757
20790
  );
20758
20791
  for (const [configFile, configContent] of Object.entries(scaffold.configs)) {
20759
20792
  if (!sandpackFileNames.has(configFile)) {
20760
- await fs14.writeFile(path17.join(exercisePath, configFile), configContent, "utf-8");
20793
+ await fs13.writeFile(path16.join(exercisePath, configFile), configContent, "utf-8");
20761
20794
  extractedFiles.push(configFile);
20762
20795
  }
20763
20796
  }
20764
20797
  const setupSh = `#!/bin/sh
20765
20798
  ${scaffold.setupScript}
20766
20799
  `;
20767
- await fs14.writeFile(path17.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20800
+ await fs13.writeFile(path16.join(exercisePath, "setup.sh"), setupSh, "utf-8");
20768
20801
  extractedFiles.push("setup.sh");
20769
20802
  }
20770
20803
  }
20771
20804
  const readme = generateReadme(lessonData, exerciseTier);
20772
- const readmePath = path17.join(exercisePath, "README.md");
20773
- await fs14.writeFile(readmePath, readme, "utf-8");
20805
+ const readmePath = path16.join(exercisePath, "README.md");
20806
+ await fs13.writeFile(readmePath, readme, "utf-8");
20774
20807
  extractedFiles.push("README.md");
20775
20808
  return {
20776
20809
  exercisePath,
@@ -20852,9 +20885,9 @@ var init_workspace = __esm({
20852
20885
 
20853
20886
  // src/commands/workspace.ts
20854
20887
  import { Command as Command17 } from "commander";
20855
- import path18 from "node:path";
20888
+ import path17 from "node:path";
20856
20889
  import os8 from "node:os";
20857
- import fs15 from "node:fs/promises";
20890
+ import fs14 from "node:fs/promises";
20858
20891
  var logger18, workspaceCommand;
20859
20892
  var init_workspace2 = __esm({
20860
20893
  "src/commands/workspace.ts"() {
@@ -20878,14 +20911,14 @@ var init_workspace2 = __esm({
20878
20911
  let directories;
20879
20912
  if (!opts.path && cwdIsWorkspace) {
20880
20913
  const resolvedCwd = await resolveCwdWorkspacePath(process.cwd());
20881
- workspacePath = resolvedCwd ?? path18.join(process.cwd(), ".tostudy");
20882
- await fs15.mkdir(workspacePath, { recursive: true });
20914
+ workspacePath = resolvedCwd ?? path17.join(process.cwd(), ".tostudy");
20915
+ await fs14.mkdir(workspacePath, { recursive: true });
20883
20916
  directories = ["exercises", "generated", "notes", "diagrams"];
20884
20917
  for (const dir of directories) {
20885
- await fs15.mkdir(path18.join(workspacePath, dir), { recursive: true });
20918
+ await fs14.mkdir(path17.join(workspacePath, dir), { recursive: true });
20886
20919
  }
20887
- const configPath = path18.join(workspacePath, ".ana-config.json");
20888
- await fs15.writeFile(
20920
+ const configPath = path17.join(workspacePath, ".ana-config.json");
20921
+ await fs14.writeFile(
20889
20922
  configPath,
20890
20923
  JSON.stringify(
20891
20924
  {
@@ -20903,7 +20936,7 @@ var init_workspace2 = __esm({
20903
20936
  "utf-8"
20904
20937
  );
20905
20938
  } else {
20906
- const basePath = opts.path ?? path18.join(os8.homedir(), "study");
20939
+ const basePath = opts.path ?? path17.join(os8.homedir(), "study");
20907
20940
  const result2 = await setupWorkspace({
20908
20941
  courseId: activeCourse.courseId,
20909
20942
  courseSlug: courseSlug(activeCourse.courseTitle),
@@ -20937,7 +20970,7 @@ Pr\xF3ximo passo: tostudy export
20937
20970
  process.exit(1);
20938
20971
  }
20939
20972
  });
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) => {
20973
+ 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) => {
20941
20974
  try {
20942
20975
  const activeCourse = await requireActiveCourse();
20943
20976
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -20954,40 +20987,40 @@ Pr\xF3ximo passo: tostudy export
20954
20987
  const workspacePath = ws.workspacePath;
20955
20988
  let configData = null;
20956
20989
  try {
20957
- const raw = await fs15.readFile(path18.join(workspacePath, ".ana-config.json"), "utf-8");
20990
+ const raw = await fs14.readFile(path17.join(workspacePath, ".ana-config.json"), "utf-8");
20958
20991
  configData = JSON.parse(raw);
20959
20992
  } catch {
20960
20993
  configData = null;
20961
20994
  }
20962
- const exercisesDir = path18.join(workspacePath, "exercises");
20995
+ const exercisesDir = path17.join(workspacePath, "exercises");
20963
20996
  let exerciseCount = 0;
20964
20997
  try {
20965
- const moduleDirs = await fs15.readdir(exercisesDir);
20998
+ const moduleDirs = await fs14.readdir(exercisesDir);
20966
20999
  for (const modDir of moduleDirs) {
20967
- const modPath = path18.join(exercisesDir, modDir);
20968
- const stat = await fs15.stat(modPath);
21000
+ const modPath = path17.join(exercisesDir, modDir);
21001
+ const stat = await fs14.stat(modPath);
20969
21002
  if (stat.isDirectory()) {
20970
- const lessonDirs = await fs15.readdir(modPath);
21003
+ const lessonDirs = await fs14.readdir(modPath);
20971
21004
  for (const lessonDir of lessonDirs) {
20972
- const lessonPath = path18.join(modPath, lessonDir);
20973
- const lstat = await fs15.stat(lessonPath);
21005
+ const lessonPath = path17.join(modPath, lessonDir);
21006
+ const lstat = await fs14.stat(lessonPath);
20974
21007
  if (lstat.isDirectory()) exerciseCount++;
20975
21008
  }
20976
21009
  }
20977
21010
  }
20978
21011
  } catch {
20979
21012
  }
20980
- const generatedDir = path18.join(workspacePath, "generated");
21013
+ const generatedDir = path17.join(workspacePath, "generated");
20981
21014
  let artifactCount = 0;
20982
21015
  try {
20983
- const files = await fs15.readdir(generatedDir);
21016
+ const files = await fs14.readdir(generatedDir);
20984
21017
  artifactCount = files.length;
20985
21018
  } catch {
20986
21019
  }
20987
- const diagramsDir = path18.join(workspacePath, "diagrams");
21020
+ const diagramsDir = path17.join(workspacePath, "diagrams");
20988
21021
  let diagramCount = 0;
20989
21022
  try {
20990
- const files = await fs15.readdir(diagramsDir);
21023
+ const files = await fs14.readdir(diagramsDir);
20991
21024
  diagramCount = files.length;
20992
21025
  } catch {
20993
21026
  }
@@ -21036,9 +21069,9 @@ Pr\xF3ximo passo: tostudy export
21036
21069
 
21037
21070
  // src/commands/export.ts
21038
21071
  import { Command as Command18 } from "commander";
21039
- import path19 from "node:path";
21072
+ import path18 from "node:path";
21040
21073
  import os9 from "node:os";
21041
- import fs16 from "node:fs/promises";
21074
+ import fs15 from "node:fs/promises";
21042
21075
  var logger19, exportCommand;
21043
21076
  var init_export = __esm({
21044
21077
  "src/commands/export.ts"() {
@@ -21051,7 +21084,7 @@ var init_export = __esm({
21051
21084
  init_resolve();
21052
21085
  init_errors();
21053
21086
  logger19 = createLogger("cli:export");
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) => {
21087
+ 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) => {
21055
21088
  try {
21056
21089
  const session = await requireSession();
21057
21090
  const activeCourse = await requireActiveCourse();
@@ -21069,21 +21102,21 @@ var init_export = __esm({
21069
21102
  opts.path
21070
21103
  );
21071
21104
  if (ws.found && ws.source === "cwd" && ws.workspacePath) {
21072
- const configPath = path19.join(ws.workspacePath, ".ana-config.json");
21105
+ const configPath = path18.join(ws.workspacePath, ".ana-config.json");
21073
21106
  let hasConfig = false;
21074
21107
  try {
21075
- await fs16.access(configPath);
21108
+ await fs15.access(configPath);
21076
21109
  hasConfig = true;
21077
21110
  } catch {
21078
21111
  }
21079
21112
  if (!hasConfig) {
21080
21113
  const slug = courseSlug(activeCourse.courseTitle);
21081
21114
  logger19.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
21082
- await fs16.mkdir(ws.workspacePath, { recursive: true });
21115
+ await fs15.mkdir(ws.workspacePath, { recursive: true });
21083
21116
  for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
21084
- await fs16.mkdir(path19.join(ws.workspacePath, dir), { recursive: true });
21117
+ await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
21085
21118
  }
21086
- await fs16.writeFile(
21119
+ await fs15.writeFile(
21087
21120
  configPath,
21088
21121
  JSON.stringify(
21089
21122
  {
@@ -21101,7 +21134,7 @@ var init_export = __esm({
21101
21134
  "utf-8"
21102
21135
  );
21103
21136
  await setCourseWorkspacePath(activeCourse.courseId, ws.workspacePath);
21104
- const isNamespaced = ws.workspacePath === path19.join(process.cwd(), ".tostudy");
21137
+ const isNamespaced = ws.workspacePath === path18.join(process.cwd(), ".tostudy");
21105
21138
  process.stderr.write(
21106
21139
  isNamespaced ? `\u2728 Workspace inicializado em .tostudy/ (isolado do projeto).
21107
21140
  ` : `\u2728 Workspace inicializado nesta pasta.
@@ -21155,7 +21188,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
21155
21188
  // src/commands/open.ts
21156
21189
  import { Command as Command19 } from "commander";
21157
21190
  import { execFile as execFile3 } from "node:child_process";
21158
- import path20 from "node:path";
21191
+ import path19 from "node:path";
21159
21192
  import os10 from "node:os";
21160
21193
  var logger20, openCommand;
21161
21194
  var init_open = __esm({
@@ -21167,7 +21200,7 @@ var init_open = __esm({
21167
21200
  init_resolve();
21168
21201
  init_errors();
21169
21202
  logger20 = createLogger("cli:open");
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) => {
21203
+ 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) => {
21171
21204
  try {
21172
21205
  const activeCourse = await requireActiveCourse();
21173
21206
  const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
@@ -21221,24 +21254,24 @@ var init_types = __esm({
21221
21254
  });
21222
21255
 
21223
21256
  // ../../packages/tostudy-core/src/vault/write-vault.ts
21224
- import fs17 from "node:fs/promises";
21225
- import path21 from "node:path";
21257
+ import fs16 from "node:fs/promises";
21258
+ import path20 from "node:path";
21226
21259
  async function writeVaultFiles(files, outputPath, courseId, courseSlug2) {
21227
21260
  for (const file2 of files) {
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");
21261
+ const fullPath = path20.join(outputPath, file2.relativePath);
21262
+ await fs16.mkdir(path20.dirname(fullPath), { recursive: true });
21263
+ await fs16.writeFile(fullPath, file2.content, "utf-8");
21231
21264
  }
21232
- const vaultPath = path21.join(outputPath, courseSlug2);
21265
+ const vaultPath = path20.join(outputPath, courseSlug2);
21233
21266
  const marker = {
21234
21267
  courseId,
21235
21268
  courseSlug: courseSlug2,
21236
21269
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
21237
21270
  version: VAULT_MARKER_VERSION
21238
21271
  };
21239
- await fs17.mkdir(vaultPath, { recursive: true });
21240
- await fs17.writeFile(
21241
- path21.join(vaultPath, VAULT_MARKER_FILENAME),
21272
+ await fs16.mkdir(vaultPath, { recursive: true });
21273
+ await fs16.writeFile(
21274
+ path20.join(vaultPath, VAULT_MARKER_FILENAME),
21242
21275
  JSON.stringify(marker, null, 2),
21243
21276
  "utf-8"
21244
21277
  );
@@ -21263,9 +21296,9 @@ var init_vault = __esm({
21263
21296
 
21264
21297
  // src/commands/vault.ts
21265
21298
  import { Command as Command20 } from "commander";
21266
- import path22 from "node:path";
21299
+ import path21 from "node:path";
21267
21300
  import os11 from "node:os";
21268
- import fs18 from "node:fs/promises";
21301
+ import fs17 from "node:fs/promises";
21269
21302
  var logger21, vaultCommand;
21270
21303
  var init_vault2 = __esm({
21271
21304
  "src/commands/vault.ts"() {
@@ -21281,7 +21314,7 @@ var init_vault2 = __esm({
21281
21314
  init_errors();
21282
21315
  logger21 = createLogger("cli:vault");
21283
21316
  vaultCommand = new Command20("vault").description("Gerenciar vault Obsidian do curso");
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) => {
21317
+ 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) => {
21285
21318
  try {
21286
21319
  const session = await requireSession();
21287
21320
  const activeCourse = await requireActiveCourse();
@@ -21362,7 +21395,7 @@ Para visualizar:
21362
21395
  process.exit(1);
21363
21396
  }
21364
21397
  });
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) => {
21398
+ 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) => {
21366
21399
  try {
21367
21400
  const session = await requireSession();
21368
21401
  const activeCourse = await requireActiveCourse();
@@ -21388,8 +21421,8 @@ Para visualizar:
21388
21421
  const data = createHttpProvider(session.apiUrl, session.token);
21389
21422
  const deps = { data, logger: logger21 };
21390
21423
  const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
21391
- const markerPath = path22.join(vaultPath, ".ana-vault.json");
21392
- const markerRaw = await fs18.readFile(markerPath, "utf-8");
21424
+ const markerPath = path21.join(vaultPath, ".ana-vault.json");
21425
+ const markerRaw = await fs17.readFile(markerPath, "utf-8");
21393
21426
  const marker = JSON.parse(markerRaw);
21394
21427
  marker.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
21395
21428
  marker.progress = {
@@ -21397,10 +21430,10 @@ Para visualizar:
21397
21430
  currentModule: progress.currentModule.title,
21398
21431
  currentLesson: progress.currentLesson.title
21399
21432
  };
21400
- await fs18.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
21401
- const courseIndexPath = path22.join(vaultPath, slug, "index.md");
21433
+ await fs17.writeFile(markerPath, JSON.stringify(marker, null, 2), "utf-8");
21434
+ const courseIndexPath = path21.join(vaultPath, slug, "index.md");
21402
21435
  try {
21403
- let indexContent = await fs18.readFile(courseIndexPath, "utf-8");
21436
+ let indexContent = await fs17.readFile(courseIndexPath, "utf-8");
21404
21437
  indexContent = indexContent.replace(/\n---\n\n> 📊 Progresso:.*\n/g, "");
21405
21438
  const titleEnd = indexContent.indexOf("\n");
21406
21439
  if (titleEnd !== -1) {
@@ -21411,7 +21444,7 @@ Para visualizar:
21411
21444
  `;
21412
21445
  indexContent = indexContent.slice(0, titleEnd) + banner + indexContent.slice(titleEnd);
21413
21446
  }
21414
- await fs18.writeFile(courseIndexPath, indexContent, "utf-8");
21447
+ await fs17.writeFile(courseIndexPath, indexContent, "utf-8");
21415
21448
  } catch {
21416
21449
  }
21417
21450
  const syncedAt = marker.lastSyncedAt;
@@ -21731,16 +21764,16 @@ var init_brief_open = __esm({
21731
21764
  });
21732
21765
 
21733
21766
  // src/sessions/storage.ts
21734
- import fs19 from "node:fs";
21735
- import path23 from "node:path";
21767
+ import fs18 from "node:fs";
21768
+ import path22 from "node:path";
21736
21769
  function sessionsDir(workspacePath) {
21737
- return path23.join(workspacePath, ".tostudy", "sessions");
21770
+ return path22.join(workspacePath, ".tostudy", "sessions");
21738
21771
  }
21739
21772
  async function saveModuleSummary(workspacePath, input2) {
21740
21773
  const dir = sessionsDir(workspacePath);
21741
- fs19.mkdirSync(dir, { recursive: true });
21774
+ fs18.mkdirSync(dir, { recursive: true });
21742
21775
  const filename = `module-${input2.moduleId}-summary.md`;
21743
- const filePath = path23.join(dir, filename);
21776
+ const filePath = path22.join(dir, filename);
21744
21777
  const header = [
21745
21778
  "---",
21746
21779
  `moduleId: ${input2.moduleId}`,
@@ -21749,17 +21782,17 @@ async function saveModuleSummary(workspacePath, input2) {
21749
21782
  "---",
21750
21783
  ""
21751
21784
  ].join("\n");
21752
- fs19.writeFileSync(filePath, header + input2.summary, { mode: 384 });
21785
+ fs18.writeFileSync(filePath, header + input2.summary, { mode: 384 });
21753
21786
  logger25.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
21754
21787
  return filePath;
21755
21788
  }
21756
21789
  async function loadSessionContext(workspacePath) {
21757
21790
  const dir = sessionsDir(workspacePath);
21758
- if (!fs19.existsSync(dir)) return { moduleSummaries: [] };
21759
- const files = fs19.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
21791
+ if (!fs18.existsSync(dir)) return { moduleSummaries: [] };
21792
+ const files = fs18.readdirSync(dir).filter((f) => f.startsWith("module-") && f.endsWith("-summary.md")).sort();
21760
21793
  const summaries = [];
21761
21794
  for (const file2 of files) {
21762
- const content = fs19.readFileSync(path23.join(dir, file2), "utf-8");
21795
+ const content = fs18.readFileSync(path22.join(dir, file2), "utf-8");
21763
21796
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
21764
21797
  if (!frontmatterMatch) continue;
21765
21798
  const meta3 = frontmatterMatch[1];
@@ -21781,7 +21814,7 @@ var init_storage = __esm({
21781
21814
  });
21782
21815
 
21783
21816
  // src/commands/compact.ts
21784
- import fs20 from "node:fs";
21817
+ import fs19 from "node:fs";
21785
21818
  import { Command as Command26 } from "commander";
21786
21819
  var logger26, compactCommand;
21787
21820
  var init_compact = __esm({
@@ -21801,7 +21834,7 @@ var init_compact = __esm({
21801
21834
  if (opts.json) jsonError("no_workspace");
21802
21835
  error("Nenhum workspace encontrado.");
21803
21836
  }
21804
- const summary = fs20.readFileSync("/dev/stdin", "utf-8").trim();
21837
+ const summary = fs19.readFileSync("/dev/stdin", "utf-8").trim();
21805
21838
  if (!summary) {
21806
21839
  if (opts.json) jsonError("empty_summary", { message: "Summary vazio" });
21807
21840
  error("Summary vazio. Envie o conte\xFAdo via stdin.");
@@ -22970,8 +23003,17 @@ __export(cli_exports, {
22970
23003
  import { Command as Command35 } from "commander";
22971
23004
  function createProgram() {
22972
23005
  const program2 = new Command35();
22973
- program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID").option("--locale <code>", "Output locale (pt-BR | en-US); defaults to LANG env then pt-BR");
22974
- program2.addCommand(setupCommand);
23006
+ program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID").option("--locale <code>", "Output locale (pt-BR | en-US); defaults to LANG env then pt-BR").addHelpText(
23007
+ "before",
23008
+ [
23009
+ "",
23010
+ " Come\xE7ando: login \u2192 courses \u2192 select <n> \u2192 start",
23011
+ " Estudando: next \xB7 lesson \xB7 hint \xB7 validate \xB7 theory \xB7 progress",
23012
+ " Com d\xFAvida: tostudy menu (mostra onde voc\xEA est\xE1 e o que fazer)",
23013
+ ""
23014
+ ].join("\n")
23015
+ );
23016
+ program2.addCommand(setupCommand, { hidden: true });
22975
23017
  program2.addCommand(doctorCommand);
22976
23018
  program2.addCommand(initCommand);
22977
23019
  program2.addCommand(loginCommand);
@@ -23055,11 +23097,11 @@ __export(auto_updater_exports, {
23055
23097
  detectManager: () => detectManager,
23056
23098
  maybeAutoUpdate: () => maybeAutoUpdate
23057
23099
  });
23058
- import fs21 from "node:fs";
23059
- import path24 from "node:path";
23100
+ import fs20 from "node:fs";
23101
+ import path23 from "node:path";
23060
23102
  import { spawn as spawn2 } from "node:child_process";
23061
23103
  function detectManager(entryRealPath) {
23062
- const p = entryRealPath.split(path24.sep).join("/");
23104
+ const p = entryRealPath.split(path23.sep).join("/");
23063
23105
  if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23064
23106
  if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
23065
23107
  if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
@@ -23067,10 +23109,10 @@ function detectManager(entryRealPath) {
23067
23109
  return null;
23068
23110
  }
23069
23111
  function installDir(entryRealPath) {
23070
- const normalized = entryRealPath.split(path24.sep).join("/");
23112
+ const normalized = entryRealPath.split(path23.sep).join("/");
23071
23113
  const marker = `/node_modules/${PACKAGE_NAME2}/`;
23072
23114
  const idx = normalized.indexOf(marker);
23073
- if (idx === -1) return path24.dirname(entryRealPath);
23115
+ if (idx === -1) return path23.dirname(entryRealPath);
23074
23116
  return entryRealPath.slice(0, idx + marker.length - 1);
23075
23117
  }
23076
23118
  function maybeAutoUpdate(currentVersion, opts = {}) {
@@ -23086,10 +23128,10 @@ function maybeAutoUpdate(currentVersion, opts = {}) {
23086
23128
  if (cache.attemptedVersion === cache.latest) return;
23087
23129
  const entryPath = opts.entryPath ?? argv[1];
23088
23130
  if (!entryPath) return;
23089
- const real = fs21.realpathSync(entryPath);
23131
+ const real = fs20.realpathSync(entryPath);
23090
23132
  const mgr = detectManager(real);
23091
23133
  if (!mgr) return;
23092
- fs21.accessSync(installDir(real), fs21.constants.W_OK);
23134
+ fs20.accessSync(installDir(real), fs20.constants.W_OK);
23093
23135
  writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
23094
23136
  process.stderr.write(
23095
23137
  `
@@ -23132,9 +23174,28 @@ Update at https://nodejs.org and run the command again.
23132
23174
  );
23133
23175
  process.exit(1);
23134
23176
  }
23177
+ if (!process.env["TOSTUDY_LOCALE"]) {
23178
+ try {
23179
+ const { readFileSync: readFileSync4 } = await import("node:fs");
23180
+ const { join: join3 } = await import("node:path");
23181
+ const { homedir: homedir2 } = await import("node:os");
23182
+ const xdg = process.env["XDG_CONFIG_HOME"];
23183
+ const dir = process.platform === "linux" && xdg ? join3(xdg, "tostudy") : join3(homedir2(), ".tostudy");
23184
+ const { locale } = JSON.parse(readFileSync4(join3(dir, "config.json"), "utf-8"));
23185
+ if (locale === "pt-BR" || locale === "en-US") process.env["TOSTUDY_LOCALE"] = locale;
23186
+ } catch {
23187
+ }
23188
+ }
23135
23189
  var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
23136
23190
  var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
23137
23191
  var { maybeAutoUpdate: maybeAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_updater(), auto_updater_exports));
23192
+ process.on("unhandledRejection", (reason) => {
23193
+ const msg = reason instanceof Error ? reason.message : String(reason);
23194
+ process.stderr.write(`Erro: ${msg}
23195
+ Se o problema continuar, rode: tostudy doctor
23196
+ `);
23197
+ process.exit(1);
23198
+ });
23138
23199
  var program = createProgram2();
23139
23200
  program.parse();
23140
23201
  checkForUpdates2(CLI_VERSION2);