@tostudy-ai/cli 0.2.0 → 0.3.0

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/bin/tostudy.ts CHANGED
@@ -3,7 +3,10 @@ if (!process.env.LOG_LEVEL) {
3
3
  process.env.LOG_LEVEL = "fatal";
4
4
  }
5
5
 
6
- import { createProgram } from "../src/cli.js";
6
+ import { createProgram, CLI_VERSION } from "../src/cli.js";
7
+ import { checkForUpdates } from "../src/update-checker.js";
7
8
 
8
9
  const program = createProgram();
9
10
  program.parse();
11
+
12
+ checkForUpdates(CLI_VERSION);
package/dist/cli.js CHANGED
@@ -835,6 +835,104 @@ var init_setup = __esm({
835
835
  }
836
836
  });
837
837
 
838
+ // src/update-checker.ts
839
+ var update_checker_exports = {};
840
+ __export(update_checker_exports, {
841
+ checkForUpdates: () => checkForUpdates,
842
+ checkVersionSync: () => checkVersionSync,
843
+ fetchLatestVersion: () => fetchLatestVersion,
844
+ isNewerVersion: () => isNewerVersion
845
+ });
846
+ import fs4 from "node:fs";
847
+ import path4 from "node:path";
848
+ import os4 from "node:os";
849
+ function getConfigDir2() {
850
+ if (process.platform === "linux" && process.env["XDG_CONFIG_HOME"]) {
851
+ return path4.join(process.env["XDG_CONFIG_HOME"], "tostudy");
852
+ }
853
+ return path4.join(os4.homedir(), ".tostudy");
854
+ }
855
+ function readCache() {
856
+ try {
857
+ const p = path4.join(getConfigDir2(), CACHE_FILE);
858
+ if (!fs4.existsSync(p)) return null;
859
+ return JSON.parse(fs4.readFileSync(p, "utf-8"));
860
+ } catch {
861
+ return null;
862
+ }
863
+ }
864
+ function writeCache(cache) {
865
+ try {
866
+ const dir = getConfigDir2();
867
+ fs4.mkdirSync(dir, { recursive: true });
868
+ fs4.writeFileSync(path4.join(dir, CACHE_FILE), JSON.stringify(cache));
869
+ } catch {
870
+ }
871
+ }
872
+ function isNewerVersion(latest, current) {
873
+ const parse3 = (v) => v.split(".").map(Number);
874
+ const [lMaj, lMin, lPatch] = parse3(latest);
875
+ const [cMaj, cMin, cPatch] = parse3(current);
876
+ if (lMaj !== cMaj) return lMaj > cMaj;
877
+ if (lMin !== cMin) return lMin > cMin;
878
+ return lPatch > cPatch;
879
+ }
880
+ async function fetchLatestVersion() {
881
+ try {
882
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
883
+ signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS)
884
+ });
885
+ if (!res.ok) return null;
886
+ const data = await res.json();
887
+ return data.version ?? null;
888
+ } catch {
889
+ return null;
890
+ }
891
+ }
892
+ function showUpdateNotification(current, latest) {
893
+ process.stderr.write(
894
+ [
895
+ "",
896
+ ` Atualiza\xE7\xE3o dispon\xEDvel: ${current} \u2192 ${latest}`,
897
+ " Atualize com: npm i -g @tostudy-ai/cli",
898
+ ""
899
+ ].join("\n")
900
+ );
901
+ }
902
+ function checkForUpdates(currentVersion) {
903
+ if (process.env["CI"] || process.argv.includes("--json")) return;
904
+ const cache = readCache();
905
+ if (cache && isNewerVersion(cache.latest, currentVersion)) {
906
+ showUpdateNotification(currentVersion, cache.latest);
907
+ }
908
+ const isStale = !cache || Date.now() - cache.checkedAt >= CHECK_INTERVAL_MS;
909
+ if (isStale) {
910
+ fetchLatestVersion().then((latest) => {
911
+ if (latest) writeCache({ latest, checkedAt: Date.now() });
912
+ }).catch(() => {
913
+ });
914
+ }
915
+ }
916
+ async function checkVersionSync(currentVersion) {
917
+ const latest = await fetchLatestVersion();
918
+ if (latest) writeCache({ latest, checkedAt: Date.now() });
919
+ return {
920
+ current: currentVersion,
921
+ latest,
922
+ updateAvailable: latest ? isNewerVersion(latest, currentVersion) : false
923
+ };
924
+ }
925
+ var PACKAGE_NAME, CACHE_FILE, CHECK_INTERVAL_MS, REGISTRY_TIMEOUT_MS;
926
+ var init_update_checker = __esm({
927
+ "src/update-checker.ts"() {
928
+ "use strict";
929
+ PACKAGE_NAME = "@tostudy-ai/cli";
930
+ CACHE_FILE = "update-check.json";
931
+ CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
932
+ REGISTRY_TIMEOUT_MS = 5e3;
933
+ }
934
+ });
935
+
838
936
  // src/commands/doctor.ts
839
937
  import { Command as Command4 } from "commander";
840
938
  var doctorCommand;
@@ -845,6 +943,8 @@ var init_doctor = __esm({
845
943
  init_ide_detector();
846
944
  init_session();
847
945
  init_formatter();
946
+ init_cli();
947
+ init_update_checker();
848
948
  doctorCommand = new Command4("doctor").description("Diagn\xF3stico do ambiente").option("--json", "Output JSON").option("--fix", "Auto-corrigir problemas (reservado para vers\xF5es futuras)").action(async (opts) => {
849
949
  const checks = {};
850
950
  const node = detectNode();
@@ -887,6 +987,8 @@ var init_doctor = __esm({
887
987
  } catch {
888
988
  checks["connectivity"] = { ok: false, latencyMs: null };
889
989
  }
990
+ const versionInfo = await checkVersionSync(CLI_VERSION);
991
+ checks["version"] = versionInfo;
890
992
  let ides = [];
891
993
  try {
892
994
  ides = detectIDEs();
@@ -919,6 +1021,19 @@ var init_doctor = __esm({
919
1021
  console.log(
920
1022
  ` ${connectivity.ok ? "\u2713" : "\u2717"} API ${connectivity.ok ? `OK (${connectivity.latencyMs}ms)` : "indispon\xEDvel"}`
921
1023
  );
1024
+ console.log("\n Vers\xE3o");
1025
+ console.log(` \u2713 Instalada ${versionInfo.current}`);
1026
+ if (versionInfo.latest) {
1027
+ const upToDate = !versionInfo.updateAvailable;
1028
+ console.log(
1029
+ ` ${upToDate ? "\u2713" : "\u2717"} \xDAltima ${versionInfo.latest}${versionInfo.updateAvailable ? " (atualiza\xE7\xE3o dispon\xEDvel)" : ""}`
1030
+ );
1031
+ if (versionInfo.updateAvailable) {
1032
+ console.log(" \u2192 npm i -g @tostudy-ai/cli");
1033
+ }
1034
+ } else {
1035
+ console.log(" \u25CB \xDAltima n\xE3o foi poss\xEDvel verificar");
1036
+ }
922
1037
  console.log("\n LLM Clients");
923
1038
  for (const ide of ides) {
924
1039
  console.log(
@@ -2677,10 +2792,10 @@ function mergeDefs(...defs) {
2677
2792
  function cloneDef(schema) {
2678
2793
  return mergeDefs(schema._zod.def);
2679
2794
  }
2680
- function getElementAtPath(obj, path4) {
2681
- if (!path4)
2795
+ function getElementAtPath(obj, path5) {
2796
+ if (!path5)
2682
2797
  return obj;
2683
- return path4.reduce((acc, key) => acc?.[key], obj);
2798
+ return path5.reduce((acc, key) => acc?.[key], obj);
2684
2799
  }
2685
2800
  function promiseAllObject(promisesObj) {
2686
2801
  const keys = Object.keys(promisesObj);
@@ -2992,11 +3107,11 @@ function aborted(x, startIndex = 0) {
2992
3107
  }
2993
3108
  return false;
2994
3109
  }
2995
- function prefixIssues(path4, issues) {
3110
+ function prefixIssues(path5, issues) {
2996
3111
  return issues.map((iss) => {
2997
3112
  var _a2;
2998
3113
  (_a2 = iss).path ?? (_a2.path = []);
2999
- iss.path.unshift(path4);
3114
+ iss.path.unshift(path5);
3000
3115
  return iss;
3001
3116
  });
3002
3117
  }
@@ -3238,7 +3353,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
3238
3353
  }
3239
3354
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
3240
3355
  const result = { errors: [] };
3241
- const processError = (error50, path4 = []) => {
3356
+ const processError = (error50, path5 = []) => {
3242
3357
  var _a2, _b;
3243
3358
  for (const issue2 of error50.issues) {
3244
3359
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -3248,7 +3363,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
3248
3363
  } else if (issue2.code === "invalid_element") {
3249
3364
  processError({ issues: issue2.issues }, issue2.path);
3250
3365
  } else {
3251
- const fullpath = [...path4, ...issue2.path];
3366
+ const fullpath = [...path5, ...issue2.path];
3252
3367
  if (fullpath.length === 0) {
3253
3368
  result.errors.push(mapper(issue2));
3254
3369
  continue;
@@ -3280,8 +3395,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
3280
3395
  }
3281
3396
  function toDotPath(_path) {
3282
3397
  const segs = [];
3283
- const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3284
- for (const seg of path4) {
3398
+ const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
3399
+ for (const seg of path5) {
3285
3400
  if (typeof seg === "number")
3286
3401
  segs.push(`[${seg}]`);
3287
3402
  else if (typeof seg === "symbol")
@@ -15975,13 +16090,13 @@ function resolveRef(ref, ctx) {
15975
16090
  if (!ref.startsWith("#")) {
15976
16091
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15977
16092
  }
15978
- const path4 = ref.slice(1).split("/").filter(Boolean);
15979
- if (path4.length === 0) {
16093
+ const path5 = ref.slice(1).split("/").filter(Boolean);
16094
+ if (path5.length === 0) {
15980
16095
  return ctx.rootSchema;
15981
16096
  }
15982
16097
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15983
- if (path4[0] === defsKey) {
15984
- const key = path4[1];
16098
+ if (path5[0] === defsKey) {
16099
+ const key = path5[1];
15985
16100
  if (!key || !ctx.defs[key]) {
15986
16101
  throw new Error(`Reference not found: ${ref}`);
15987
16102
  }
@@ -16972,7 +17087,7 @@ var init_exercises = __esm({
16972
17087
  });
16973
17088
 
16974
17089
  // src/commands/validate.ts
16975
- import fs4 from "node:fs";
17090
+ import fs5 from "node:fs";
16976
17091
  import { Command as Command13 } from "commander";
16977
17092
  var logger10, validateCommand;
16978
17093
  var init_validate = __esm({
@@ -16996,12 +17111,12 @@ var init_validate = __esm({
16996
17111
  }
16997
17112
  let solution;
16998
17113
  if (opts.stdin) {
16999
- solution = fs4.readFileSync("/dev/stdin", "utf-8");
17114
+ solution = fs5.readFileSync("/dev/stdin", "utf-8");
17000
17115
  } else if (file2) {
17001
- if (!fs4.existsSync(file2)) {
17116
+ if (!fs5.existsSync(file2)) {
17002
17117
  error(`Arquivo n\xE3o encontrado: ${file2}`);
17003
17118
  }
17004
- solution = fs4.readFileSync(file2, "utf-8");
17119
+ solution = fs5.readFileSync(file2, "utf-8");
17005
17120
  } else {
17006
17121
  error("Forne\xE7a um arquivo ou use --stdin.\n\nExemplo: tostudy validate resposta.md");
17007
17122
  }
@@ -17298,12 +17413,13 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
17298
17413
  // src/cli.ts
17299
17414
  var cli_exports = {};
17300
17415
  __export(cli_exports, {
17416
+ CLI_VERSION: () => CLI_VERSION,
17301
17417
  createProgram: () => createProgram
17302
17418
  });
17303
17419
  import { Command as Command16 } from "commander";
17304
17420
  function createProgram() {
17305
17421
  const program2 = new Command16();
17306
- program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version("0.1.3").option("--json", "Output structured JSON (for LLM agents)").option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID");
17422
+ program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--json", "Output structured JSON (for LLM agents)").option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID");
17307
17423
  program2.addCommand(setupCommand);
17308
17424
  program2.addCommand(doctorCommand);
17309
17425
  program2.addCommand(initCommand);
@@ -17321,6 +17437,7 @@ function createProgram() {
17321
17437
  program2.addCommand(menuCommand);
17322
17438
  return program2;
17323
17439
  }
17440
+ var CLI_VERSION;
17324
17441
  var init_cli = __esm({
17325
17442
  "src/cli.ts"() {
17326
17443
  "use strict";
@@ -17339,6 +17456,7 @@ var init_cli = __esm({
17339
17456
  init_validate();
17340
17457
  init_menu();
17341
17458
  init_init();
17459
+ CLI_VERSION = "0.3.0";
17342
17460
  }
17343
17461
  });
17344
17462
 
@@ -17346,7 +17464,9 @@ var init_cli = __esm({
17346
17464
  if (!process.env.LOG_LEVEL) {
17347
17465
  process.env.LOG_LEVEL = "fatal";
17348
17466
  }
17349
- var { createProgram: createProgram2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
17467
+ var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
17468
+ var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
17350
17469
  var program = createProgram2();
17351
17470
  program.parse();
17471
+ checkForUpdates2(CLI_VERSION2);
17352
17472
  //# sourceMappingURL=cli.js.map