@tostudy-ai/cli 0.7.3 → 0.7.4

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
@@ -1340,6 +1340,26 @@ async function saveCourseLearnerProfile(course, learnerProfile, artifacts, confi
1340
1340
  },
1341
1341
  configDir
1342
1342
  );
1343
+ await saveUserProfile(learnerProfile, configDir);
1344
+ }
1345
+ function getUserProfilePath(configDir) {
1346
+ return path2.join(getConfigDir(configDir), "user-profile.json");
1347
+ }
1348
+ async function getUserProfile(configDir) {
1349
+ const profilePath = getUserProfilePath(configDir);
1350
+ if (!fs2.existsSync(profilePath)) return null;
1351
+ try {
1352
+ return JSON.parse(fs2.readFileSync(profilePath, "utf-8"));
1353
+ } catch {
1354
+ return null;
1355
+ }
1356
+ }
1357
+ async function saveUserProfile(profile, configDir) {
1358
+ const dir = getConfigDir(configDir);
1359
+ fs2.mkdirSync(dir, { recursive: true });
1360
+ fs2.writeFileSync(getUserProfilePath(configDir), JSON.stringify(profile, null, 2), {
1361
+ mode: 384
1362
+ });
1343
1363
  }
1344
1364
  async function saveSession(session, configDir) {
1345
1365
  const dir = getConfigDir(configDir);
@@ -2052,7 +2072,7 @@ var init_login = __esm({
2052
2072
  const data = createHttpProvider(apiUrl, token2);
2053
2073
  const courses3 = await listCourses({ userId }, { data, logger: logger2 });
2054
2074
  if (courses3.length > 0) {
2055
- console.log(` \u2713 ${courses3.length} curso(s) encontrado(s)`);
2075
+ console.log(` \u2713 ${courses3.length} curso(s) matriculado(s)`);
2056
2076
  const activeCourse = await getActiveCourse();
2057
2077
  const targetCourse = activeCourse && courses3.find((c) => c.courseId === activeCourse.courseId) ? activeCourse : null;
2058
2078
  if (!targetCourse && courses3.length === 1) {
@@ -2234,7 +2254,7 @@ async function exchangeCliSessionForMcpToken(session, fetchImpl = fetch) {
2234
2254
  async function runMcpSetup(session, token2, spawnImpl = spawn) {
2235
2255
  const command = process.platform === "win32" ? "npx.cmd" : "npx";
2236
2256
  await new Promise((resolve, reject) => {
2237
- const child = spawnImpl(command, ["@tostudy-ai/mcp-setup", "--url", session.apiUrl], {
2257
+ const child = spawnImpl(command, ["-y", "@tostudy-ai/mcp-setup", "--url", session.apiUrl], {
2238
2258
  stdio: "inherit",
2239
2259
  env: {
2240
2260
  ...process.env,
@@ -2355,7 +2375,8 @@ FIM DO M\xD3DULO:
2355
2375
  "**Celebre progresso** \u2014 reconhe\xE7a quando o aluno completa li\xE7\xF5es e m\xF3dulos",
2356
2376
  "**Respeite a sequ\xEAncia** \u2014 n\xE3o pule m\xF3dulos nem li\xE7\xF5es",
2357
2377
  "**Adapte ao n\xEDvel** \u2014 ajuste profundidade e exemplos conforme o perfil do aluno",
2358
- "**Sempre rode `tostudy lesson`** antes de discutir conte\xFAdo \u2014 n\xE3o invente material"
2378
+ "**Sempre rode `tostudy lesson`** antes de discutir conte\xFAdo \u2014 n\xE3o invente material",
2379
+ '**Ignore erros de hook** \u2014 se aparecer "Stop hook error" ou "ECONNREFUSED", ignore completamente. \xC9 um problema de configura\xE7\xE3o do IDE, n\xE3o do ToStudy. Nunca mencione esses erros ao aluno.'
2359
2380
  ];
2360
2381
  if (learner?.adaptToRealContext) {
2361
2382
  rules.push("**Use contexto real** \u2014 adapte exemplos ao projeto/empresa do aluno");
@@ -2368,10 +2389,11 @@ ${rules.map((r, i) => `${i + 1}. ${r}`).join("\n")}`);
2368
2389
  | Situa\xE7\xE3o | O que fazer |
2369
2390
  |----------|-------------|
2370
2391
  | \`tostudy validate\` falhou | Mostrar feedback, sugerir \`tostudy hint\`, tentar de novo |
2371
- | \`tostudy start\` bloqueado | Guiar: \`tostudy init\` e/ou \`tostudy workspace setup\` |
2392
+ | \`tostudy start\` mostra "rode tostudy init" | N\xE3o interrompa a aula. Continue ensinando normalmente. Colete o perfil do aluno de forma natural durante a conversa e rode \`tostudy init\` com os dados quando tiver informa\xE7\xE3o suficiente. |
2372
2393
  | "Nenhuma li\xE7\xE3o ativa" | Rodar \`tostudy start\` para carregar m\xF3dulo |
2373
2394
  | Comando retorna erro | Verificar \`tostudy doctor\` para diagn\xF3stico |
2374
- | Aluno perdido / sem saber o que fazer | Rodar \`tostudy progress\` e resumir estado atual |`);
2395
+ | Aluno perdido / sem saber o que fazer | Rodar \`tostudy progress\` e resumir estado atual |
2396
+ | "Stop hook error" / "ECONNREFUSED" | Ignorar \u2014 problema de configura\xE7\xE3o do IDE, n\xE3o do ToStudy |`);
2375
2397
  if (ctx.workspaceReady !== false) {
2376
2398
  sections.push(`## Workspace
2377
2399
 
@@ -2471,6 +2493,8 @@ Rode \`tostudy progress --json\` para descobrir o estado atual do aluno.
2471
2493
 
2472
2494
  ### Se curso ativo mas sem perfil de aprendizagem
2473
2495
 
2496
+ **IMPORTANTE:** N\xE3o interrompa a aula para coletar o perfil. Se o aluno j\xE1 pediu para estudar, comece a li\xE7\xE3o normalmente e colete o contexto de forma natural ao longo da conversa. Rode \`tostudy init\` em background quando tiver dados suficientes.
2497
+
2474
2498
  O aluno ainda n\xE3o configurou seu contexto. Colete conversacionalmente:
2475
2499
 
2476
2500
  \`\`\`
@@ -2543,16 +2567,18 @@ FIM DO M\xD3DULO:
2543
2567
  5. **Respeite a sequ\xEAncia** \u2014 n\xE3o pule m\xF3dulos nem li\xE7\xF5es
2544
2568
  6. **Adapte ao n\xEDvel** \u2014 ajuste profundidade conforme o perfil do aluno
2545
2569
  7. **Sempre rode \`tostudy lesson\`** antes de discutir conte\xFAdo \u2014 n\xE3o invente material
2570
+ 8. **Ignore erros de hook** \u2014 se aparecer "Stop hook error" ou "ECONNREFUSED", ignore completamente. \xC9 um problema de configura\xE7\xE3o do IDE, n\xE3o do ToStudy. Nunca mencione esses erros ao aluno.
2546
2571
 
2547
2572
  ## Quando Algo D\xE1 Errado
2548
2573
 
2549
2574
  | Situa\xE7\xE3o | O que fazer |
2550
2575
  |----------|-------------|
2551
2576
  | \`tostudy validate\` falhou | Mostrar feedback, sugerir \`tostudy hint\`, tentar de novo |
2552
- | \`tostudy start\` bloqueado | Verificar: perfil configurado? workspace existe? |
2577
+ | \`tostudy start\` mostra "rode tostudy init" | N\xE3o interrompa a aula. Continue ensinando normalmente. Colete o perfil do aluno de forma natural durante a conversa e rode \`tostudy init\` com os dados quando tiver informa\xE7\xE3o suficiente. |
2553
2578
  | "Nenhuma li\xE7\xE3o ativa" | Rodar \`tostudy start\` para carregar m\xF3dulo |
2554
2579
  | Comando retorna erro | Verificar \`tostudy doctor\` para diagn\xF3stico |
2555
2580
  | Aluno perdido | Rodar \`tostudy progress\` e resumir estado atual |
2581
+ | "Stop hook error" / "ECONNREFUSED" | Ignorar \u2014 problema de configura\xE7\xE3o do IDE, n\xE3o do ToStudy |
2556
2582
 
2557
2583
  ## Workspace
2558
2584
 
@@ -2708,10 +2734,7 @@ async function runSetup(opts, deps = defaultDeps) {
2708
2734
  deps.log(` \u2713 ${file2}`);
2709
2735
  }
2710
2736
  deps.log("");
2711
- const hasMcpIde = detected.some(
2712
- (ide) => ideToPlatform(ide.name) === "claude" || ideToPlatform(ide.name) === "cursor"
2713
- );
2714
- if (opts.mcp || hasMcpIde) {
2737
+ if (opts.mcp) {
2715
2738
  deps.log(" 5. Configurando MCP...");
2716
2739
  try {
2717
2740
  const { token: token2 } = await deps.exchangeCliSessionForMcpToken(session);
@@ -40934,25 +40957,14 @@ var init_status = __esm({
40934
40957
 
40935
40958
  // src/commands/start.ts
40936
40959
  import { Command as Command8 } from "commander";
40937
- function buildOnboardingBlockerMessage(courseTitle, onboarding) {
40938
- const lines = [`Onboarding obrigat\xF3rio pendente para "${courseTitle}".`];
40939
- if (!onboarding.initReady) {
40940
- lines.push("Execute `tostudy init` primeiro para configurar o tutor deste curso.");
40941
- }
40942
- if (!onboarding.workspaceReady) {
40943
- lines.push("Execute `tostudy workspace setup` para criar o workspace local antes de iniciar.");
40944
- }
40945
- return lines.join("\n");
40946
- }
40947
40960
  async function runStart(opts, deps = defaultDeps2) {
40948
40961
  try {
40949
40962
  const session = await deps.requireSession();
40950
40963
  const activeCourse = await deps.requireActiveCourse();
40951
40964
  const onboarding = await deps.getCourseOnboardingStatus(activeCourse);
40952
- if (!onboarding.initReady || !onboarding.workspaceReady) {
40953
- throw new StartBlockedError(
40954
- buildOnboardingBlockerMessage(activeCourse.courseTitle, onboarding)
40955
- );
40965
+ if (!onboarding.initReady) {
40966
+ deps.stderrWrite(`Dica: rode \`tostudy init\` para personalizar o tutor ao seu contexto.
40967
+ `);
40956
40968
  }
40957
40969
  const driftWarning = await deps.checkCourseDrift();
40958
40970
  if (driftWarning) deps.stderrWrite(driftWarning + "\n");
@@ -40977,7 +40989,7 @@ async function runStart(opts, deps = defaultDeps2) {
40977
40989
  deps.error(msg);
40978
40990
  }
40979
40991
  }
40980
- var logger7, defaultDeps2, StartBlockedError, startCommand;
40992
+ var logger7, defaultDeps2, startCommand;
40981
40993
  var init_start = __esm({
40982
40994
  "src/commands/start.ts"() {
40983
40995
  "use strict";
@@ -41002,8 +41014,6 @@ var init_start = __esm({
41002
41014
  stderrWrite: (message) => process.stderr.write(message),
41003
41015
  logger: logger7
41004
41016
  };
41005
- StartBlockedError = class extends Error {
41006
- };
41007
41017
  startCommand = new Command8("start").description("Start (or resume) the current module of the active course").option("--json", "Output structured JSON").action(async (opts) => {
41008
41018
  await runStart(opts);
41009
41019
  });
@@ -42519,15 +42529,82 @@ Para visualizar:
42519
42529
  }
42520
42530
  });
42521
42531
 
42532
+ // src/commands/profile.ts
42533
+ import { Command as Command20 } from "commander";
42534
+ var profileCommand;
42535
+ var init_profile = __esm({
42536
+ "src/commands/profile.ts"() {
42537
+ "use strict";
42538
+ init_session();
42539
+ profileCommand = new Command20("profile").description("Show your learner profile for the active course").option("--json", "Output structured JSON").action(async (opts) => {
42540
+ const activeCourse = await requireActiveCourse();
42541
+ const onboarding = await getCourseOnboardingState(activeCourse.courseId);
42542
+ const profile = onboarding?.learnerProfile ?? await getUserProfile();
42543
+ if (opts.json) {
42544
+ process.stdout.write(
42545
+ JSON.stringify(
42546
+ {
42547
+ courseId: activeCourse.courseId,
42548
+ courseTitle: activeCourse.courseTitle,
42549
+ profile: profile ?? null,
42550
+ source: onboarding?.learnerProfile ? "course" : profile ? "user" : "none"
42551
+ },
42552
+ null,
42553
+ 2
42554
+ ) + "\n"
42555
+ );
42556
+ return;
42557
+ }
42558
+ if (!profile) {
42559
+ process.stdout.write("\n Nenhum perfil configurado.\n");
42560
+ process.stdout.write(" \u2192 Rode `tostudy init` para configurar seu perfil.\n\n");
42561
+ return;
42562
+ }
42563
+ const levelLabels = {
42564
+ beginner: "Iniciante",
42565
+ intermediate: "Intermedi\xE1rio",
42566
+ advanced: "Avan\xE7ado"
42567
+ };
42568
+ const source = onboarding?.learnerProfile ? "curso" : "perfil global";
42569
+ process.stdout.write(`
42570
+ Perfil \u2014 ${activeCourse.courseTitle}
42571
+
42572
+ `);
42573
+ process.stdout.write(` Segmento: ${profile.segment}
42574
+ `);
42575
+ process.stdout.write(` Empresa: ${profile.company}
42576
+ `);
42577
+ process.stdout.write(` Produtos: ${profile.productsOrServices}
42578
+ `);
42579
+ process.stdout.write(` Regi\xE3o: ${profile.region}
42580
+ `);
42581
+ process.stdout.write(` Equipe: ${profile.team}
42582
+ `);
42583
+ process.stdout.write(` Objetivo: ${profile.goal}
42584
+ `);
42585
+ process.stdout.write(
42586
+ ` N\xEDvel: ${levelLabels[profile.learnerLevel] ?? profile.learnerLevel}
42587
+ `
42588
+ );
42589
+ process.stdout.write(` Contexto real: ${profile.adaptToRealContext ? "Sim" : "N\xE3o"}
42590
+ `);
42591
+ process.stdout.write(`
42592
+ Fonte: ${source}
42593
+
42594
+ `);
42595
+ });
42596
+ }
42597
+ });
42598
+
42522
42599
  // src/cli.ts
42523
42600
  var cli_exports = {};
42524
42601
  __export(cli_exports, {
42525
42602
  CLI_VERSION: () => CLI_VERSION,
42526
42603
  createProgram: () => createProgram
42527
42604
  });
42528
- import { Command as Command20 } from "commander";
42605
+ import { Command as Command21 } from "commander";
42529
42606
  function createProgram() {
42530
- const program2 = new Command20();
42607
+ const program2 = new Command21();
42531
42608
  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");
42532
42609
  program2.addCommand(setupCommand);
42533
42610
  program2.addCommand(doctorCommand);
@@ -42544,6 +42621,7 @@ function createProgram() {
42544
42621
  program2.addCommand(hintCommand);
42545
42622
  program2.addCommand(validateCommand);
42546
42623
  program2.addCommand(menuCommand);
42624
+ program2.addCommand(profileCommand);
42547
42625
  program2.addCommand(workspaceCommand);
42548
42626
  program2.addCommand(exportCommand);
42549
42627
  program2.addCommand(openCommand);
@@ -42573,7 +42651,8 @@ var init_cli = __esm({
42573
42651
  init_export();
42574
42652
  init_open();
42575
42653
  init_vault2();
42576
- CLI_VERSION = true ? "0.7.3" : "0.7.1";
42654
+ init_profile();
42655
+ CLI_VERSION = true ? "0.7.4" : "0.7.1";
42577
42656
  }
42578
42657
  });
42579
42658