minerva-cli 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,2137 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/index.ts
32
+ var index_exports = {};
33
+ __export(index_exports, {
34
+ buildProgram: () => buildProgram
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // node_modules/tsup/assets/cjs_shims.js
39
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
40
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
41
+
42
+ // src/index.ts
43
+ var import_chalk5 = __toESM(require("chalk"), 1);
44
+
45
+ // src/program.ts
46
+ var import_commander20 = require("commander");
47
+
48
+ // src/config/constants.ts
49
+ var APP_NAME = "minerva";
50
+ var APP_DESCRIPTION = "Organiza\xE7\xE3o acad\xEAmica para estudantes de ADS da FATEC \u{1F989}";
51
+ var APP_VERSION = "0.1.0";
52
+ var DEFAULT_VAULT_DIRNAME = "fatec-ads";
53
+ var MATRIZ_URL = "https://fatectq.cps.sp.gov.br/matriz-curricular-ads/";
54
+
55
+ // src/commands/cheatsheet.ts
56
+ var import_node_path5 = __toESM(require("path"), 1);
57
+ var import_commander = require("commander");
58
+
59
+ // src/services/config.ts
60
+ var import_node_path2 = __toESM(require("path"), 1);
61
+ var import_fs_extra2 = __toESM(require("fs-extra"), 1);
62
+
63
+ // src/types/index.ts
64
+ var import_zod = require("zod");
65
+ var disciplinaSchema = import_zod.z.object({
66
+ nome: import_zod.z.string().min(1),
67
+ /** Carga horária em aulas, conforme a matriz oficial. */
68
+ aulas: import_zod.z.number().int().positive()
69
+ });
70
+ var semestreSchema = import_zod.z.object({
71
+ numero: import_zod.z.number().int().min(1).max(6),
72
+ disciplinas: import_zod.z.array(disciplinaSchema).min(1)
73
+ });
74
+ var matrizSchema = import_zod.z.object({
75
+ curso: import_zod.z.string(),
76
+ instituicao: import_zod.z.string(),
77
+ fonte: import_zod.z.string().url(),
78
+ atualizadaEm: import_zod.z.string(),
79
+ semestres: import_zod.z.array(semestreSchema).length(6)
80
+ });
81
+ var minervaConfigSchema = import_zod.z.object({
82
+ versao: import_zod.z.string(),
83
+ aluno: import_zod.z.string(),
84
+ semestreAtual: import_zod.z.number().int().min(1).max(6),
85
+ criadoEm: import_zod.z.string(),
86
+ horasEstudadas: import_zod.z.number().nonnegative().default(0)
87
+ });
88
+
89
+ // src/utils/paths.ts
90
+ var import_node_path = __toESM(require("path"), 1);
91
+ var import_node_url = require("url");
92
+ var import_fs_extra = __toESM(require("fs-extra"), 1);
93
+ var CONFIG_FILE = "minerva.json";
94
+ var VAULT_FOLDERS = [
95
+ "dashboard",
96
+ "roadmap",
97
+ "curriculo",
98
+ "portfolio",
99
+ "entrevistas",
100
+ "estagio",
101
+ "certificados",
102
+ "artigos",
103
+ "livros",
104
+ "snippets",
105
+ "flashcards",
106
+ "cheatsheets",
107
+ "laboratorio",
108
+ "desafios",
109
+ "templates",
110
+ "projetos-pessoais"
111
+ ];
112
+ var DISCIPLINA_FOLDERS = [
113
+ "Aulas",
114
+ "Projetos",
115
+ "Trabalhos",
116
+ "Exercicios",
117
+ "Resumos",
118
+ "Flashcards",
119
+ "Provas",
120
+ "Laboratorio",
121
+ "Referencias"
122
+ ];
123
+ function semestreDir(root, numero) {
124
+ return import_node_path.default.join(root, `${numero}-semestre`);
125
+ }
126
+ function builtinTemplatesDir() {
127
+ const here = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
128
+ const candidates = [
129
+ import_node_path.default.resolve(here, "../templates"),
130
+ import_node_path.default.resolve(here, "../../templates"),
131
+ import_node_path.default.resolve(here, "../../src/templates")
132
+ ];
133
+ for (const candidate of candidates) {
134
+ if (import_fs_extra.default.existsSync(import_node_path.default.join(candidate, "aula.hbs"))) return candidate;
135
+ }
136
+ throw new Error(
137
+ "Templates embutidos n\xE3o encontrados. Reinstale a minerva-cli."
138
+ );
139
+ }
140
+ async function findVaultRoot(cwd) {
141
+ let dir = import_node_path.default.resolve(cwd);
142
+ for (; ; ) {
143
+ if (await import_fs_extra.default.pathExists(import_node_path.default.join(dir, CONFIG_FILE))) return dir;
144
+ const parent = import_node_path.default.dirname(dir);
145
+ if (parent === dir) return null;
146
+ dir = parent;
147
+ }
148
+ }
149
+
150
+ // src/services/config.ts
151
+ async function loadConfig(root) {
152
+ const raw = await import_fs_extra2.default.readJson(import_node_path2.default.join(root, CONFIG_FILE));
153
+ return minervaConfigSchema.parse(raw);
154
+ }
155
+ async function saveConfig(root, config) {
156
+ await import_fs_extra2.default.writeJson(import_node_path2.default.join(root, CONFIG_FILE), config, { spaces: 4 });
157
+ }
158
+ async function requireVault(cwd = process.cwd()) {
159
+ const root = await findVaultRoot(cwd);
160
+ if (!root) {
161
+ throw new Error(
162
+ 'Nenhum vault Minerva encontrado. Rode "minerva init" primeiro (ou entre na pasta do seu vault).'
163
+ );
164
+ }
165
+ return { root, config: await loadConfig(root) };
166
+ }
167
+
168
+ // src/services/template.ts
169
+ var import_node_path3 = __toESM(require("path"), 1);
170
+ var import_fs_extra3 = __toESM(require("fs-extra"), 1);
171
+ var import_handlebars = __toESM(require("handlebars"), 1);
172
+
173
+ // src/utils/slug.ts
174
+ function slugify(text) {
175
+ return text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
176
+ }
177
+ function safeFolderName(text) {
178
+ return text.replace(/[\\/:*?"<>|]/g, " ").replace(/\s+/g, " ").trim();
179
+ }
180
+ function todayISO(date = /* @__PURE__ */ new Date()) {
181
+ return date.toISOString().slice(0, 10);
182
+ }
183
+
184
+ // src/services/template.ts
185
+ var handlebars = import_handlebars.default.create();
186
+ handlebars.registerHelper("hoje", () => todayISO());
187
+ async function resolveTemplate(name, vaultRoot) {
188
+ const filename = `${name}.hbs`;
189
+ if (vaultRoot) {
190
+ const custom = import_node_path3.default.join(vaultRoot, "templates", filename);
191
+ if (await import_fs_extra3.default.pathExists(custom)) return import_fs_extra3.default.readFile(custom, "utf8");
192
+ }
193
+ return import_fs_extra3.default.readFile(import_node_path3.default.join(builtinTemplatesDir(), filename), "utf8");
194
+ }
195
+ async function renderTemplate(name, data, vaultRoot) {
196
+ const source = await resolveTemplate(name, vaultRoot);
197
+ return handlebars.compile(source)(data);
198
+ }
199
+ async function renderToFile(name, data, destination, vaultRoot) {
200
+ if (await import_fs_extra3.default.pathExists(destination)) {
201
+ throw new Error(`Arquivo j\xE1 existe: ${destination}`);
202
+ }
203
+ const content = await renderTemplate(name, data, vaultRoot);
204
+ await import_fs_extra3.default.outputFile(destination, content, "utf8");
205
+ }
206
+
207
+ // src/utils/logger.ts
208
+ var import_chalk = __toESM(require("chalk"), 1);
209
+ var import_ora = __toESM(require("ora"), 1);
210
+ var logger = {
211
+ info(message) {
212
+ console.log(`${import_chalk.default.blue("\u2139")} ${message}`);
213
+ },
214
+ success(message) {
215
+ console.log(`${import_chalk.default.green("\u2714")} ${message}`);
216
+ },
217
+ warn(message) {
218
+ console.warn(`${import_chalk.default.yellow("\u26A0")} ${message}`);
219
+ },
220
+ error(message) {
221
+ console.error(`${import_chalk.default.red("\u2716")} ${import_chalk.default.red(message)}`);
222
+ },
223
+ title(message) {
224
+ console.log(`
225
+ ${import_chalk.default.bold.magenta("\u{1F989} " + message)}
226
+ `);
227
+ },
228
+ item(message) {
229
+ console.log(` ${import_chalk.default.dim("\u2022")} ${message}`);
230
+ },
231
+ blank() {
232
+ console.log();
233
+ }
234
+ };
235
+ function spinner(text) {
236
+ return (0, import_ora.default)({ text, color: "magenta" });
237
+ }
238
+ function progressBar(current, total, width = 20) {
239
+ const ratio = total === 0 ? 0 : Math.min(Math.max(current / total, 0), 1);
240
+ const filled = Math.round(ratio * width);
241
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
242
+ return `[${bar}] ${Math.round(ratio * 100)}%`;
243
+ }
244
+
245
+ // src/utils/prompts.ts
246
+ var import_prompts = require("@inquirer/prompts");
247
+
248
+ // src/services/matriz.ts
249
+ var import_node_path4 = __toESM(require("path"), 1);
250
+ var import_fs_extra4 = __toESM(require("fs-extra"), 1);
251
+
252
+ // src/data/matriz.ts
253
+ var matrizOficial = {
254
+ curso: "An\xE1lise e Desenvolvimento de Sistemas",
255
+ instituicao: "FATEC Taquaritinga",
256
+ fonte: "https://fatectq.cps.sp.gov.br/matriz-curricular-ads/",
257
+ atualizadaEm: "2026-07-13",
258
+ semestres: [
259
+ {
260
+ numero: 1,
261
+ disciplinas: [
262
+ { nome: "Programa\xE7\xE3o em Microinform\xE1tica", aulas: 80 },
263
+ { nome: "Sistemas de Informa\xE7\xE3o", aulas: 80 },
264
+ { nome: "Algoritmos e L\xF3gica de Programa\xE7\xE3o", aulas: 80 },
265
+ {
266
+ nome: "Arquitetura e Organiza\xE7\xE3o de Computadores",
267
+ aulas: 80
268
+ },
269
+ { nome: "Administra\xE7\xE3o Geral", aulas: 80 },
270
+ { nome: "Matem\xE1tica Discreta", aulas: 80 },
271
+ { nome: "Comunica\xE7\xE3o e Express\xE3o", aulas: 80 },
272
+ { nome: "Ingl\xEAs I", aulas: 40 }
273
+ ]
274
+ },
275
+ {
276
+ numero: 2,
277
+ disciplinas: [
278
+ { nome: "Engenharia de Software I", aulas: 80 },
279
+ { nome: "Linguagem de Programa\xE7\xE3o", aulas: 80 },
280
+ { nome: "Programa\xE7\xE3o de Scripts", aulas: 80 },
281
+ { nome: "Sistemas Operacionais I", aulas: 80 },
282
+ { nome: "Laborat\xF3rio de Hardware", aulas: 40 },
283
+ { nome: "Contabilidade", aulas: 40 },
284
+ { nome: "Estat\xEDstica Aplicada", aulas: 80 },
285
+ { nome: "C\xE1lculo", aulas: 80 },
286
+ { nome: "Ingl\xEAs II", aulas: 40 }
287
+ ]
288
+ },
289
+ {
290
+ numero: 3,
291
+ disciplinas: [
292
+ { nome: "Engenharia de Software II", aulas: 80 },
293
+ { nome: "Intera\xE7\xE3o Humano Computador", aulas: 40 },
294
+ { nome: "Programa\xE7\xE3o Web", aulas: 80 },
295
+ { nome: "Sistemas Operacionais II", aulas: 80 },
296
+ { nome: "Estruturas de Dados", aulas: 80 },
297
+ { nome: "Banco de Dados", aulas: 80 },
298
+ { nome: "Economia e Finan\xE7as", aulas: 40 },
299
+ { nome: "Programa\xE7\xE3o Linear e Aplica\xE7\xF5es", aulas: 80 },
300
+ { nome: "Ingl\xEAs III", aulas: 40 }
301
+ ]
302
+ },
303
+ {
304
+ numero: 4,
305
+ disciplinas: [
306
+ { nome: "Engenharia de Software III", aulas: 80 },
307
+ { nome: "Programa\xE7\xE3o Orientada a Objetos", aulas: 80 },
308
+ { nome: "Redes de Computadores", aulas: 80 },
309
+ { nome: "Seguran\xE7a da Informa\xE7\xE3o", aulas: 40 },
310
+ { nome: "Programa\xE7\xE3o para Banco de Dados", aulas: 80 },
311
+ { nome: "Laborat\xF3rio de Banco de Dados", aulas: 80 },
312
+ { nome: "Gest\xE3o de Projetos", aulas: 80 },
313
+ { nome: "Sociedade e Tecnologia", aulas: 40 },
314
+ { nome: "Ingl\xEAs IV", aulas: 40 }
315
+ ]
316
+ },
317
+ {
318
+ numero: 5,
319
+ disciplinas: [
320
+ { nome: "Laborat\xF3rio de Engenharia de Software", aulas: 80 },
321
+ { nome: "Gest\xE3o de Equipes", aulas: 40 },
322
+ { nome: "T\xF3picos Especiais em Inform\xE1tica", aulas: 80 },
323
+ { nome: "Empreendedorismo", aulas: 40 },
324
+ {
325
+ nome: "Metodologia da Pesquisa Cient\xEDfico-Tecnol\xF3gica",
326
+ aulas: 40
327
+ },
328
+ { nome: "Ingl\xEAs V", aulas: 40 },
329
+ { nome: "Trabalho de Gradua\xE7\xE3o I", aulas: 80 }
330
+ ]
331
+ },
332
+ {
333
+ numero: 6,
334
+ disciplinas: [
335
+ { nome: "\xC9tica e Responsabilidade Profissional", aulas: 40 },
336
+ { nome: "Auditoria de Sistemas", aulas: 80 },
337
+ {
338
+ nome: "Gest\xE3o e Governan\xE7a de Tecnologia da Informa\xE7\xE3o",
339
+ aulas: 80
340
+ },
341
+ { nome: "Ingl\xEAs VI", aulas: 40 },
342
+ { nome: "Trabalho de Gradua\xE7\xE3o II", aulas: 80 },
343
+ { nome: "Est\xE1gio Supervisionado", aulas: 240 }
344
+ ]
345
+ }
346
+ ]
347
+ };
348
+
349
+ // src/services/matriz.ts
350
+ var MATRIZ_FILE = import_node_path4.default.join("curriculo", "matriz.json");
351
+ async function loadMatriz(vaultRoot) {
352
+ if (vaultRoot) {
353
+ const file = import_node_path4.default.join(vaultRoot, MATRIZ_FILE);
354
+ if (await import_fs_extra4.default.pathExists(file)) {
355
+ const raw = await import_fs_extra4.default.readJson(file);
356
+ return matrizSchema.parse(raw);
357
+ }
358
+ }
359
+ return matrizOficial;
360
+ }
361
+ async function saveMatriz(vaultRoot, matriz) {
362
+ await import_fs_extra4.default.outputJson(import_node_path4.default.join(vaultRoot, MATRIZ_FILE), matriz, {
363
+ spaces: 4
364
+ });
365
+ }
366
+ function getSemestre(matriz, numero) {
367
+ const semestre = matriz.semestres.find((s) => s.numero === numero);
368
+ if (!semestre) {
369
+ throw new Error(`Semestre inv\xE1lido: ${numero}. Use um valor de 1 a 6.`);
370
+ }
371
+ return semestre;
372
+ }
373
+ function parseMatrizHtml(html, fonte) {
374
+ const text = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, "\n").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&#8211;|&ndash;/g, "\u2013");
375
+ const lines = text.split("\n").map((line) => line.trim()).filter(Boolean);
376
+ const semestres = [];
377
+ let atual = null;
378
+ for (const line of lines) {
379
+ const header = /^(\d)\s*[ºo°]?\s*semestre/i.exec(line);
380
+ if (header) {
381
+ const numero = Number(header[1]);
382
+ if (numero >= 1 && numero <= 6) {
383
+ atual = { numero, disciplinas: [] };
384
+ semestres.push(atual);
385
+ }
386
+ continue;
387
+ }
388
+ const item = /^(.+?)\s*[–—-]\s*(\d+)\s*aulas?/i.exec(line);
389
+ if (item && atual) {
390
+ atual.disciplinas.push({
391
+ nome: item[1].trim(),
392
+ aulas: Number(item[2])
393
+ });
394
+ }
395
+ }
396
+ return matrizSchema.parse({
397
+ curso: matrizOficial.curso,
398
+ instituicao: matrizOficial.instituicao,
399
+ fonte,
400
+ atualizadaEm: todayISO(),
401
+ semestres
402
+ });
403
+ }
404
+ async function fetchMatriz(url = matrizOficial.fonte) {
405
+ const response = await fetch(url);
406
+ if (!response.ok) {
407
+ throw new Error(`Falha ao baixar a matriz (HTTP ${response.status}).`);
408
+ }
409
+ try {
410
+ return parseMatrizHtml(await response.text(), url);
411
+ } catch (cause) {
412
+ throw new Error(
413
+ "N\xE3o consegui interpretar a p\xE1gina da FATEC (o layout pode ter mudado). A matriz embutida na CLI continua v\xE1lida.",
414
+ { cause }
415
+ );
416
+ }
417
+ }
418
+
419
+ // src/utils/prompts.ts
420
+ async function pickDisciplina(matriz, semestre, preset) {
421
+ const disciplinas = getSemestre(matriz, semestre).disciplinas;
422
+ if (preset) {
423
+ const found = disciplinas.find(
424
+ (d) => d.nome.toLowerCase() === preset.toLowerCase()
425
+ );
426
+ if (found) return found.nome;
427
+ }
428
+ return (0, import_prompts.select)({
429
+ message: `Disciplina (${semestre}\xBA semestre):`,
430
+ choices: disciplinas.map((d) => ({ value: d.nome }))
431
+ });
432
+ }
433
+ async function pickSemestre(defaultValue) {
434
+ const value = await (0, import_prompts.number)({
435
+ message: "Semestre (1-6):",
436
+ default: defaultValue,
437
+ min: 1,
438
+ max: 6
439
+ });
440
+ return value ?? defaultValue;
441
+ }
442
+ async function askTitulo(message) {
443
+ return (0, import_prompts.input)({
444
+ message,
445
+ validate: (value) => value.trim().length > 0 || "O t\xEDtulo n\xE3o pode ficar vazio."
446
+ });
447
+ }
448
+
449
+ // src/commands/cheatsheet.ts
450
+ function cheatsheetCommand() {
451
+ return new import_commander.Command("cheatsheet").description("Cria uma cheatsheet avulsa (ex.: Git, SQL, Linux)").argument("[titulo]", "assunto da cheatsheet").action(async (titulo) => {
452
+ const vault = await requireVault();
453
+ const tituloFinal = titulo ?? await askTitulo("Assunto da cheatsheet:");
454
+ const destination = import_node_path5.default.join(
455
+ vault.root,
456
+ "cheatsheets",
457
+ `${slugify(tituloFinal)}.md`
458
+ );
459
+ const spin = spinner("Criando cheatsheet...").start();
460
+ try {
461
+ await renderToFile(
462
+ "cheatsheet",
463
+ { titulo: tituloFinal, disciplina: "" },
464
+ destination,
465
+ vault.root
466
+ );
467
+ spin.succeed("Cheatsheet criada!");
468
+ logger.item(import_node_path5.default.relative(vault.root, destination));
469
+ } catch (error) {
470
+ spin.fail("N\xE3o consegui criar a cheatsheet.");
471
+ throw error;
472
+ }
473
+ });
474
+ }
475
+
476
+ // src/commands/commit.ts
477
+ var import_commander2 = require("commander");
478
+
479
+ // src/services/git.ts
480
+ var import_node_child_process = require("child_process");
481
+ var import_node_util = require("util");
482
+ var exec = (0, import_node_util.promisify)(import_node_child_process.execFile);
483
+ async function isGitRepo(cwd) {
484
+ try {
485
+ await exec("git", ["rev-parse", "--git-dir"], { cwd });
486
+ return true;
487
+ } catch {
488
+ return false;
489
+ }
490
+ }
491
+ async function commitVault(root, message) {
492
+ if (!await isGitRepo(root)) {
493
+ throw new Error(
494
+ 'O vault n\xE3o \xE9 um reposit\xF3rio Git. Rode "git init" dentro dele.'
495
+ );
496
+ }
497
+ await exec("git", ["add", "-A"], { cwd: root });
498
+ try {
499
+ await exec("git", ["commit", "-m", message], { cwd: root });
500
+ return true;
501
+ } catch (error) {
502
+ const stdout = error instanceof Error && "stdout" in error ? String(error.stdout) : "";
503
+ if (stdout.includes("nothing to commit")) return false;
504
+ throw error;
505
+ }
506
+ }
507
+
508
+ // src/commands/commit.ts
509
+ function commitCommand() {
510
+ return new import_commander2.Command("commit").description("Faz git add + commit de todas as mudan\xE7as do vault").argument("[mensagem]", "mensagem do commit").action(async (mensagem) => {
511
+ const vault = await requireVault();
512
+ const spin = spinner("Commitando o vault...").start();
513
+ try {
514
+ const committed = await commitVault(
515
+ vault.root,
516
+ mensagem ?? `minerva: estudos de ${todayISO()}`
517
+ );
518
+ if (committed) spin.succeed("Commit criado!");
519
+ else spin.info("Nada novo para commitar.");
520
+ } catch (error) {
521
+ spin.fail("Falha ao commitar.");
522
+ throw error;
523
+ }
524
+ });
525
+ }
526
+
527
+ // src/commands/completion.ts
528
+ var import_commander3 = require("commander");
529
+ function buildScript(shell, commands) {
530
+ const list = commands.join(" ");
531
+ if (shell === "bash") {
532
+ return `# minerva \u2014 bash completion (adicione ao ~/.bashrc)
533
+ _minerva_completions() {
534
+ if [ "\${COMP_CWORD}" -eq 1 ]; then
535
+ COMPREPLY=($(compgen -W "${list}" -- "\${COMP_WORDS[1]}"))
536
+ fi
537
+ }
538
+ complete -F _minerva_completions minerva
539
+ `;
540
+ }
541
+ return `# minerva \u2014 zsh completion (adicione ao ~/.zshrc)
542
+ _minerva() {
543
+ if (( CURRENT == 2 )); then
544
+ compadd ${list}
545
+ fi
546
+ }
547
+ compdef _minerva minerva
548
+ `;
549
+ }
550
+ function completionCommand() {
551
+ return new import_commander3.Command("completion").description("Gera script de autocomplete para bash ou zsh").argument("<shell>", "bash ou zsh").action(function(shell) {
552
+ if (shell !== "bash" && shell !== "zsh") {
553
+ throw new Error('Shell n\xE3o suportado. Use "bash" ou "zsh".');
554
+ }
555
+ const commands = (this.parent?.commands ?? []).map((c) => c.name()).filter((name) => name !== "completion");
556
+ console.log(buildScript(shell, commands));
557
+ });
558
+ }
559
+
560
+ // src/commands/config.ts
561
+ var import_commander4 = require("commander");
562
+ var EDITABLE_KEYS = ["aluno", "semestreAtual", "horasEstudadas"];
563
+ function configCommand() {
564
+ const command = new import_commander4.Command("config").description(
565
+ "Mostra ou edita a configura\xE7\xE3o do vault (minerva.json)"
566
+ );
567
+ command.command("show", { isDefault: true }).description("Mostra a configura\xE7\xE3o atual").option("--json", "sa\xEDda em JSON").action(async (options) => {
568
+ const vault = await requireVault();
569
+ if (options.json) {
570
+ console.log(JSON.stringify(vault.config, null, 2));
571
+ return;
572
+ }
573
+ logger.title("Configura\xE7\xE3o do vault");
574
+ for (const [key, value] of Object.entries(vault.config)) {
575
+ logger.item(`${key}: ${String(value)}`);
576
+ }
577
+ });
578
+ command.command("set").description(`Edita uma chave (${EDITABLE_KEYS.join(", ")})`).argument("<chave>").argument("<valor>").action(async (chave, valor) => {
579
+ const vault = await requireVault();
580
+ if (!EDITABLE_KEYS.includes(chave)) {
581
+ throw new Error(
582
+ `Chave inv\xE1lida: "${chave}". Chaves edit\xE1veis: ${EDITABLE_KEYS.join(", ")}.`
583
+ );
584
+ }
585
+ const parsed = chave === "aluno" ? valor : Number(valor.replace(",", "."));
586
+ const config = minervaConfigSchema.parse({
587
+ ...vault.config,
588
+ [chave]: parsed
589
+ });
590
+ await saveConfig(vault.root, config);
591
+ logger.success(`${chave} = ${String(parsed)}`);
592
+ });
593
+ return command;
594
+ }
595
+
596
+ // src/commands/dashboard.ts
597
+ var import_node_path8 = __toESM(require("path"), 1);
598
+ var import_commander5 = require("commander");
599
+
600
+ // src/services/dashboard.ts
601
+ var import_node_path7 = __toESM(require("path"), 1);
602
+ var import_fs_extra6 = __toESM(require("fs-extra"), 1);
603
+
604
+ // src/services/scaffold.ts
605
+ var import_node_path6 = __toESM(require("path"), 1);
606
+ var import_fs_extra5 = __toESM(require("fs-extra"), 1);
607
+ function disciplinaDir(root, semestre, disciplina) {
608
+ return import_node_path6.default.join(semestreDir(root, semestre), safeFolderName(disciplina));
609
+ }
610
+ async function scaffoldDisciplina(root, semestre, disciplina) {
611
+ const dir = disciplinaDir(root, semestre, disciplina.nome);
612
+ await Promise.all(
613
+ DISCIPLINA_FOLDERS.map((folder) => import_fs_extra5.default.ensureDir(import_node_path6.default.join(dir, folder)))
614
+ );
615
+ const files = [
616
+ [
617
+ "README.md",
618
+ () => renderTemplate(
619
+ "disciplina",
620
+ { disciplina: disciplina.nome, semestre, aulas: disciplina.aulas },
621
+ root
622
+ )
623
+ ],
624
+ [
625
+ "Cheatsheet.md",
626
+ () => renderTemplate(
627
+ "cheatsheet",
628
+ { titulo: disciplina.nome, disciplina: disciplina.nome },
629
+ root
630
+ )
631
+ ],
632
+ [
633
+ "Resumo-Final.md",
634
+ () => renderTemplate(
635
+ "resumo-final",
636
+ { disciplina: disciplina.nome, semestre },
637
+ root
638
+ )
639
+ ]
640
+ ];
641
+ for (const [filename, render] of files) {
642
+ const destination = import_node_path6.default.join(dir, filename);
643
+ if (!await import_fs_extra5.default.pathExists(destination)) {
644
+ await import_fs_extra5.default.outputFile(destination, await render(), "utf8");
645
+ }
646
+ }
647
+ return dir;
648
+ }
649
+ async function scaffoldVaultFolders(root) {
650
+ await Promise.all(
651
+ VAULT_FOLDERS.map((folder) => import_fs_extra5.default.ensureDir(import_node_path6.default.join(root, folder)))
652
+ );
653
+ }
654
+ async function scaffoldTemplates(root) {
655
+ await import_fs_extra5.default.copy(builtinTemplatesDir(), import_node_path6.default.join(root, "templates"), {
656
+ overwrite: false
657
+ });
658
+ }
659
+ async function scaffoldSemestres(root, matriz) {
660
+ let total = 0;
661
+ for (const semestre of matriz.semestres) {
662
+ for (const disciplina of semestre.disciplinas) {
663
+ await scaffoldDisciplina(root, semestre.numero, disciplina);
664
+ total += 1;
665
+ }
666
+ }
667
+ return total;
668
+ }
669
+ async function scaffoldObsidian(root) {
670
+ const obsidianDirs = [
671
+ "obsidian/daily-notes",
672
+ "obsidian/canvas",
673
+ "obsidian/attachments",
674
+ "obsidian/excalidraw"
675
+ ];
676
+ await Promise.all(
677
+ obsidianDirs.map((dir) => import_fs_extra5.default.ensureDir(import_node_path6.default.join(root, dir)))
678
+ );
679
+ const settings = import_node_path6.default.join(root, ".obsidian");
680
+ await import_fs_extra5.default.ensureDir(settings);
681
+ await import_fs_extra5.default.writeJson(
682
+ import_node_path6.default.join(settings, "app.json"),
683
+ { attachmentFolderPath: "obsidian/attachments" },
684
+ { spaces: 4 }
685
+ );
686
+ await import_fs_extra5.default.writeJson(
687
+ import_node_path6.default.join(settings, "daily-notes.json"),
688
+ { folder: "obsidian/daily-notes", format: "YYYY-MM-DD" },
689
+ { spaces: 4 }
690
+ );
691
+ await import_fs_extra5.default.writeJson(
692
+ import_node_path6.default.join(settings, "templates.json"),
693
+ { folder: "templates" },
694
+ { spaces: 4 }
695
+ );
696
+ await import_fs_extra5.default.writeJson(
697
+ import_node_path6.default.join(settings, "types.json"),
698
+ {
699
+ types: {
700
+ disciplina: "text",
701
+ semestre: "number",
702
+ tipo: "text",
703
+ status: "text",
704
+ data: "date"
705
+ }
706
+ },
707
+ { spaces: 4 }
708
+ );
709
+ }
710
+
711
+ // src/services/dashboard.ts
712
+ async function listMarkdown(dir) {
713
+ if (!await import_fs_extra6.default.pathExists(dir)) return [];
714
+ const entries = await import_fs_extra6.default.readdir(dir);
715
+ return entries.filter((entry) => entry.endsWith(".md"));
716
+ }
717
+ async function countPendencias(dir) {
718
+ if (!await import_fs_extra6.default.pathExists(dir)) return 0;
719
+ let count = 0;
720
+ const stack = [dir];
721
+ while (stack.length > 0) {
722
+ const current = stack.pop();
723
+ for (const entry of await import_fs_extra6.default.readdir(current, {
724
+ withFileTypes: true
725
+ })) {
726
+ const full = import_node_path7.default.join(current, entry.name);
727
+ if (entry.isDirectory()) stack.push(full);
728
+ else if (entry.name.endsWith(".md")) {
729
+ const content = await import_fs_extra6.default.readFile(full, "utf8");
730
+ count += (content.match(/^\s*[-*] \[ \]/gm) ?? []).length;
731
+ }
732
+ }
733
+ }
734
+ return count;
735
+ }
736
+ function extractProvaDate(content) {
737
+ const match = /^data-prova:\s*(\d{4}-\d{2}-\d{2})/m.exec(content);
738
+ return match ? match[1] : null;
739
+ }
740
+ async function collectDashboardData(vault, matriz) {
741
+ const semestre = getSemestre(matriz, vault.config.semestreAtual);
742
+ const disciplinas = [];
743
+ const proximasProvas = [];
744
+ const hoje = todayISO();
745
+ for (const disciplina of semestre.disciplinas) {
746
+ const dir = disciplinaDir(vault.root, semestre.numero, disciplina.nome);
747
+ const [aulas, resumos, provas, trabalhos, projetos, pendencias] = await Promise.all([
748
+ listMarkdown(import_node_path7.default.join(dir, "Aulas")),
749
+ listMarkdown(import_node_path7.default.join(dir, "Resumos")),
750
+ listMarkdown(import_node_path7.default.join(dir, "Provas")),
751
+ listMarkdown(import_node_path7.default.join(dir, "Trabalhos")),
752
+ listMarkdown(import_node_path7.default.join(dir, "Projetos")),
753
+ countPendencias(dir)
754
+ ]);
755
+ for (const arquivo of provas) {
756
+ const content = await import_fs_extra6.default.readFile(
757
+ import_node_path7.default.join(dir, "Provas", arquivo),
758
+ "utf8"
759
+ );
760
+ const data = extractProvaDate(content);
761
+ if (data && data >= hoje) {
762
+ proximasProvas.push({
763
+ disciplina: disciplina.nome,
764
+ arquivo,
765
+ data
766
+ });
767
+ }
768
+ }
769
+ disciplinas.push({
770
+ nome: disciplina.nome,
771
+ aulas: aulas.length,
772
+ resumos: resumos.length,
773
+ provas: provas.length,
774
+ trabalhos: trabalhos.length,
775
+ projetos: projetos.length,
776
+ pendencias
777
+ });
778
+ }
779
+ proximasProvas.sort((a, b) => a.data.localeCompare(b.data));
780
+ return {
781
+ geradoEm: hoje,
782
+ semestreAtual: semestre.numero,
783
+ horasEstudadas: vault.config.horasEstudadas,
784
+ progressoCurso: progressBar(semestre.numero - 1, 6),
785
+ disciplinas,
786
+ proximasProvas,
787
+ totalPendencias: disciplinas.reduce((sum, d) => sum + d.pendencias, 0)
788
+ };
789
+ }
790
+ async function generateDashboard(vault, matriz) {
791
+ const data = await collectDashboardData(vault, matriz);
792
+ const content = await renderTemplate(
793
+ "dashboard",
794
+ data,
795
+ vault.root
796
+ );
797
+ const destination = import_node_path7.default.join(vault.root, "dashboard", "Dashboard.md");
798
+ await import_fs_extra6.default.outputFile(destination, content, "utf8");
799
+ return destination;
800
+ }
801
+
802
+ // src/commands/dashboard.ts
803
+ function dashboardCommand() {
804
+ return new import_commander5.Command("dashboard").description("Gera/atualiza o dashboard de estudos").action(async () => {
805
+ const vault = await requireVault();
806
+ const matriz = await loadMatriz(vault.root);
807
+ const spin = spinner("Analisando seu vault...").start();
808
+ try {
809
+ const file = await generateDashboard(vault, matriz);
810
+ spin.succeed("Dashboard atualizado!");
811
+ logger.item(import_node_path8.default.relative(vault.root, file));
812
+ } catch (error) {
813
+ spin.fail("Falha ao gerar o dashboard.");
814
+ throw error;
815
+ }
816
+ });
817
+ }
818
+
819
+ // src/commands/disciplina.ts
820
+ var import_node_path9 = __toESM(require("path"), 1);
821
+ var import_commander6 = require("commander");
822
+ function disciplinaCommand() {
823
+ const command = new import_commander6.Command("disciplina").description(
824
+ "Lista disciplinas ou adiciona uma disciplina extra"
825
+ );
826
+ command.command("list", { isDefault: true }).description("Lista as disciplinas de todos os semestres").action(async () => {
827
+ const vault = await requireVault();
828
+ const matriz = await loadMatriz(vault.root);
829
+ logger.title(matriz.curso);
830
+ for (const semestre of matriz.semestres) {
831
+ const marker = semestre.numero === vault.config.semestreAtual ? " \u{1F448} atual" : "";
832
+ logger.info(`${semestre.numero}\xBA semestre${marker}`);
833
+ for (const d of semestre.disciplinas) {
834
+ logger.item(`${d.nome} (${d.aulas} aulas)`);
835
+ }
836
+ logger.blank();
837
+ }
838
+ });
839
+ command.command("add").description("Cria a estrutura de uma disciplina extra").argument("[nome]", "nome da disciplina").option("-s, --semestre <n>", "semestre onde criar").option("-a, --aulas <n>", "carga hor\xE1ria em aulas", "80").action(
840
+ async (nome, options) => {
841
+ const vault = await requireVault();
842
+ const nomeFinal = nome ?? await askTitulo("Nome da disciplina:");
843
+ const semestre = options.semestre ? Number(options.semestre) : await pickSemestre(vault.config.semestreAtual);
844
+ const spin = spinner("Criando disciplina...").start();
845
+ try {
846
+ const dir = await scaffoldDisciplina(vault.root, semestre, {
847
+ nome: nomeFinal,
848
+ aulas: Number(options.aulas)
849
+ });
850
+ spin.succeed(`Disciplina "${nomeFinal}" criada!`);
851
+ logger.item(import_node_path9.default.relative(vault.root, dir));
852
+ } catch (error) {
853
+ spin.fail("N\xE3o consegui criar a disciplina.");
854
+ throw error;
855
+ }
856
+ }
857
+ );
858
+ return command;
859
+ }
860
+
861
+ // src/commands/doctor.ts
862
+ var import_node_path10 = __toESM(require("path"), 1);
863
+ var import_commander7 = require("commander");
864
+ var import_fs_extra7 = __toESM(require("fs-extra"), 1);
865
+ function doctorCommand() {
866
+ return new import_commander7.Command("doctor").description("Verifica a sa\xFAde do ambiente e do vault").action(async () => {
867
+ logger.title("Minerva Doctor");
868
+ const root = await findVaultRoot(process.cwd());
869
+ const checks = [
870
+ {
871
+ label: "Node.js",
872
+ run: () => {
873
+ const major = Number(process.versions.node.split(".")[0]);
874
+ if (major < 20) {
875
+ throw new Error(
876
+ `v${process.versions.node} \u2014 atualize para a LTS (>= 20)`
877
+ );
878
+ }
879
+ return Promise.resolve(`v${process.versions.node}`);
880
+ }
881
+ },
882
+ {
883
+ label: "Templates embutidos",
884
+ run: () => Promise.resolve(builtinTemplatesDir())
885
+ },
886
+ {
887
+ label: "Vault Minerva",
888
+ run: () => {
889
+ if (!root) {
890
+ throw new Error('n\xE3o encontrado \u2014 rode "minerva init"');
891
+ }
892
+ return Promise.resolve(root);
893
+ }
894
+ }
895
+ ];
896
+ if (root) {
897
+ checks.push(
898
+ {
899
+ label: "Configura\xE7\xE3o (minerva.json)",
900
+ run: async () => {
901
+ const config = await loadConfig(root);
902
+ return `aluno: ${config.aluno}, ${config.semestreAtual}\xBA semestre`;
903
+ }
904
+ },
905
+ {
906
+ label: "Matriz curricular",
907
+ run: async () => {
908
+ const matriz = await loadMatriz(root);
909
+ const total = matriz.semestres.reduce(
910
+ (sum, s) => sum + s.disciplinas.length,
911
+ 0
912
+ );
913
+ return `${total} disciplinas (atualizada em ${matriz.atualizadaEm})`;
914
+ }
915
+ },
916
+ {
917
+ label: "Reposit\xF3rio Git",
918
+ run: async () => {
919
+ if (!await import_fs_extra7.default.pathExists(import_node_path10.default.join(root, ".git"))) {
920
+ throw new Error('ausente \u2014 rode "git init" no vault');
921
+ }
922
+ return "ok";
923
+ }
924
+ },
925
+ {
926
+ label: "Vault do Obsidian",
927
+ run: async () => {
928
+ if (!await import_fs_extra7.default.pathExists(import_node_path10.default.join(root, ".obsidian"))) {
929
+ throw new Error(
930
+ 'pasta .obsidian ausente \u2014 rode "minerva init" novamente'
931
+ );
932
+ }
933
+ return "ok";
934
+ }
935
+ }
936
+ );
937
+ }
938
+ let failures = 0;
939
+ for (const check of checks) {
940
+ try {
941
+ logger.success(`${check.label}: ${await check.run()}`);
942
+ } catch (error) {
943
+ failures += 1;
944
+ logger.error(
945
+ `${check.label}: ${error instanceof Error ? error.message : String(error)}`
946
+ );
947
+ }
948
+ }
949
+ logger.blank();
950
+ if (failures === 0) {
951
+ logger.success("Tudo certo! Bons estudos. \u{1F989}");
952
+ } else {
953
+ logger.warn(`${failures} problema(s) encontrado(s).`);
954
+ process.exitCode = 1;
955
+ }
956
+ });
957
+ }
958
+
959
+ // src/commands/estudo.ts
960
+ var import_commander8 = require("commander");
961
+
962
+ // src/services/estudo.ts
963
+ var import_node_path11 = __toESM(require("path"), 1);
964
+ var import_fs_extra8 = __toESM(require("fs-extra"), 1);
965
+ var import_zod2 = require("zod");
966
+ var ESTUDO_LOG_FILE = import_node_path11.default.join("dashboard", "estudo.json");
967
+ var sessaoSchema = import_zod2.z.object({
968
+ data: import_zod2.z.string(),
969
+ horas: import_zod2.z.number().positive(),
970
+ disciplina: import_zod2.z.string().optional()
971
+ });
972
+ var logSchema = import_zod2.z.array(sessaoSchema);
973
+ async function loadEstudoLog(root) {
974
+ const file = import_node_path11.default.join(root, ESTUDO_LOG_FILE);
975
+ if (!await import_fs_extra8.default.pathExists(file)) return [];
976
+ const raw = await import_fs_extra8.default.readJson(file);
977
+ return logSchema.parse(raw);
978
+ }
979
+ async function registrarEstudo(vault, horas, disciplina) {
980
+ if (!Number.isFinite(horas) || horas <= 0) {
981
+ throw new Error("Informe um n\xFAmero de horas maior que zero (ex.: 1.5).");
982
+ }
983
+ const sessao = {
984
+ data: todayISO(),
985
+ horas,
986
+ ...disciplina ? { disciplina } : {}
987
+ };
988
+ const log = await loadEstudoLog(vault.root);
989
+ log.push(sessao);
990
+ await import_fs_extra8.default.outputJson(import_node_path11.default.join(vault.root, ESTUDO_LOG_FILE), log, {
991
+ spaces: 4
992
+ });
993
+ vault.config.horasEstudadas = Math.round((vault.config.horasEstudadas + horas) * 100) / 100;
994
+ await saveConfig(vault.root, vault.config);
995
+ return sessao;
996
+ }
997
+ function horasPorDisciplina(log) {
998
+ const totals = /* @__PURE__ */ new Map();
999
+ for (const sessao of log) {
1000
+ const key = sessao.disciplina ?? "(geral)";
1001
+ totals.set(key, (totals.get(key) ?? 0) + sessao.horas);
1002
+ }
1003
+ return totals;
1004
+ }
1005
+
1006
+ // src/commands/estudo.ts
1007
+ function estudoCommand() {
1008
+ return new import_commander8.Command("estudo").description("Registra horas de estudo (alimenta o dashboard)").argument("[horas]", "horas estudadas (ex.: 1.5)").option("-d, --disciplina <nome>", "disciplina estudada").option("--json", "sa\xEDda em JSON").action(
1009
+ async (horas, options) => {
1010
+ const vault = await requireVault();
1011
+ if (horas === void 0) {
1012
+ const log = await loadEstudoLog(vault.root);
1013
+ const totals = horasPorDisciplina(log);
1014
+ if (options.json) {
1015
+ console.log(
1016
+ JSON.stringify(
1017
+ {
1018
+ total: vault.config.horasEstudadas,
1019
+ porDisciplina: Object.fromEntries(totals)
1020
+ },
1021
+ null,
1022
+ 2
1023
+ )
1024
+ );
1025
+ return;
1026
+ }
1027
+ logger.title(`${vault.config.horasEstudadas}h estudadas no total`);
1028
+ for (const [disciplina2, total] of totals) {
1029
+ logger.item(`${disciplina2}: ${total}h`);
1030
+ }
1031
+ if (totals.size === 0) {
1032
+ logger.info("Registre sua primeira sess\xE3o: minerva estudo 1.5");
1033
+ }
1034
+ return;
1035
+ }
1036
+ const valor = Number(horas.replace(",", "."));
1037
+ const matriz = await loadMatriz(vault.root);
1038
+ const disciplina = options.disciplina ? await pickDisciplina(
1039
+ matriz,
1040
+ vault.config.semestreAtual,
1041
+ options.disciplina
1042
+ ) : void 0;
1043
+ const sessao = await registrarEstudo(vault, valor, disciplina);
1044
+ await generateDashboard(vault, matriz);
1045
+ logger.success(
1046
+ `+${sessao.horas}h registradas${disciplina ? ` em ${disciplina}` : ""}. Total: ${vault.config.horasEstudadas}h. Dashboard atualizado!`
1047
+ );
1048
+ }
1049
+ );
1050
+ }
1051
+
1052
+ // src/commands/flashcards.ts
1053
+ var import_node_path15 = __toESM(require("path"), 1);
1054
+ var import_commander10 = require("commander");
1055
+
1056
+ // src/services/anki.ts
1057
+ var import_node_path12 = __toESM(require("path"), 1);
1058
+ var import_fs_extra9 = __toESM(require("fs-extra"), 1);
1059
+ function parseFlashcards(content, deck) {
1060
+ const cards = [];
1061
+ for (const line of content.split("\n")) {
1062
+ const trimmed = line.trim();
1063
+ if (trimmed.startsWith("#") || trimmed.startsWith(">")) continue;
1064
+ const match = /^(.+?)\s*::\s*(.+)$/.exec(trimmed);
1065
+ if (match) {
1066
+ cards.push({
1067
+ pergunta: match[1].trim(),
1068
+ resposta: match[2].trim(),
1069
+ deck
1070
+ });
1071
+ }
1072
+ }
1073
+ return cards;
1074
+ }
1075
+ async function collectMarkdownFiles(dir) {
1076
+ if (!await import_fs_extra9.default.pathExists(dir)) return [];
1077
+ const files = [];
1078
+ const stack = [dir];
1079
+ while (stack.length > 0) {
1080
+ const current = stack.pop();
1081
+ for (const entry of await import_fs_extra9.default.readdir(current, { withFileTypes: true })) {
1082
+ const full = import_node_path12.default.join(current, entry.name);
1083
+ if (entry.isDirectory()) stack.push(full);
1084
+ else if (entry.name.endsWith(".md")) files.push(full);
1085
+ }
1086
+ }
1087
+ return files;
1088
+ }
1089
+ async function collectAllFlashcards(vault) {
1090
+ const cards = [];
1091
+ const stack = [vault.root];
1092
+ while (stack.length > 0) {
1093
+ const current = stack.pop();
1094
+ for (const entry of await import_fs_extra9.default.readdir(current, { withFileTypes: true })) {
1095
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
1096
+ const full = import_node_path12.default.join(current, entry.name);
1097
+ if (!entry.isDirectory()) continue;
1098
+ if (entry.name.toLowerCase() === "flashcards") {
1099
+ const deck = import_node_path12.default.basename(import_node_path12.default.dirname(full));
1100
+ for (const file of await collectMarkdownFiles(full)) {
1101
+ cards.push(...parseFlashcards(await import_fs_extra9.default.readFile(file, "utf8"), deck));
1102
+ }
1103
+ } else {
1104
+ stack.push(full);
1105
+ }
1106
+ }
1107
+ }
1108
+ return cards;
1109
+ }
1110
+ async function exportAnki(vault) {
1111
+ const cards = await collectAllFlashcards(vault);
1112
+ const lines = [
1113
+ "#separator:tab",
1114
+ "#html:false",
1115
+ "#deck column:3",
1116
+ ...cards.map(
1117
+ (card) => `${card.pergunta.replace(/\t/g, " ")} ${card.resposta.replace(/\t/g, " ")} FATEC::${card.deck.replace(/\t/g, " ")}`
1118
+ )
1119
+ ];
1120
+ const file = import_node_path12.default.join(
1121
+ vault.root,
1122
+ "flashcards",
1123
+ `anki-export-${todayISO()}.txt`
1124
+ );
1125
+ await import_fs_extra9.default.outputFile(file, lines.join("\n"), "utf8");
1126
+ return { file, total: cards.length };
1127
+ }
1128
+
1129
+ // src/commands/note-factory.ts
1130
+ var import_node_path14 = __toESM(require("path"), 1);
1131
+ var import_commander9 = require("commander");
1132
+ var import_fs_extra11 = __toESM(require("fs-extra"), 1);
1133
+ var import_prompts6 = require("@inquirer/prompts");
1134
+
1135
+ // src/services/notes.ts
1136
+ var import_node_path13 = __toESM(require("path"), 1);
1137
+ var import_fs_extra10 = __toESM(require("fs-extra"), 1);
1138
+ var NOTE_SPECS = {
1139
+ aula: { template: "aula", folder: "Aulas", prefix: "aula" },
1140
+ resumo: { template: "resumo", folder: "Resumos", prefix: "resumo" },
1141
+ prova: { template: "prova", folder: "Provas", prefix: "prova" },
1142
+ trabalho: { template: "trabalho", folder: "Trabalhos", prefix: "trabalho" },
1143
+ projeto: { template: "projeto", folder: "Projetos", prefix: "projeto" },
1144
+ flashcard: {
1145
+ template: "flashcard",
1146
+ folder: "Flashcards",
1147
+ prefix: "flashcards"
1148
+ }
1149
+ };
1150
+ async function createNote(vault, input5) {
1151
+ const spec = NOTE_SPECS[input5.kind];
1152
+ const dir = import_node_path13.default.join(
1153
+ disciplinaDir(vault.root, input5.semestre, input5.disciplina),
1154
+ spec.folder
1155
+ );
1156
+ await import_fs_extra10.default.ensureDir(dir);
1157
+ const filename = `${todayISO()}-${spec.prefix}-${slugify(input5.titulo)}.md`;
1158
+ const destination = import_node_path13.default.join(dir, filename);
1159
+ await renderToFile(
1160
+ spec.template,
1161
+ {
1162
+ titulo: input5.titulo,
1163
+ disciplina: input5.disciplina,
1164
+ semestre: input5.semestre,
1165
+ data: todayISO(),
1166
+ ...input5.extra
1167
+ },
1168
+ destination,
1169
+ vault.root
1170
+ );
1171
+ return destination;
1172
+ }
1173
+
1174
+ // src/services/ai.ts
1175
+ var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
1176
+ var AI_MODEL = "claude-opus-4-8";
1177
+ function anthropicProvider() {
1178
+ if (!process.env["ANTHROPIC_API_KEY"]) {
1179
+ throw new Error(
1180
+ 'Defina a vari\xE1vel de ambiente ANTHROPIC_API_KEY para usar os recursos de IA.\n Ex.: export ANTHROPIC_API_KEY="sk-ant-..."'
1181
+ );
1182
+ }
1183
+ const client = new import_sdk.default();
1184
+ return {
1185
+ async generate(prompt, material) {
1186
+ try {
1187
+ const stream = client.messages.stream({
1188
+ model: AI_MODEL,
1189
+ max_tokens: 64e3,
1190
+ thinking: { type: "adaptive" },
1191
+ system: "Voc\xEA \xE9 um monitor de estudos da FATEC (curso de An\xE1lise e Desenvolvimento de Sistemas). Responda sempre em portugu\xEAs do Brasil, em Markdown puro, sem pre\xE2mbulos.",
1192
+ messages: [
1193
+ {
1194
+ role: "user",
1195
+ content: `${prompt}
1196
+
1197
+ <material>
1198
+ ${material}
1199
+ </material>`
1200
+ }
1201
+ ]
1202
+ });
1203
+ const message = await stream.finalMessage();
1204
+ return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
1205
+ } catch (error) {
1206
+ if (error instanceof import_sdk.default.AuthenticationError) {
1207
+ throw new Error("ANTHROPIC_API_KEY inv\xE1lida. Verifique a chave.");
1208
+ }
1209
+ if (error instanceof import_sdk.default.RateLimitError) {
1210
+ throw new Error(
1211
+ "Limite de requisi\xE7\xF5es da API atingido. Tente novamente em instantes."
1212
+ );
1213
+ }
1214
+ if (error instanceof import_sdk.default.APIConnectionError) {
1215
+ throw new Error(
1216
+ "Sem conex\xE3o com a API da Anthropic. Verifique sua internet."
1217
+ );
1218
+ }
1219
+ throw error;
1220
+ }
1221
+ }
1222
+ };
1223
+ }
1224
+ function resumoPrompt(disciplina, tema) {
1225
+ return `Gere um resumo de estudo sobre "${tema}" para a disciplina "${disciplina}", baseado nas anota\xE7\xF5es de aula fornecidas no material. Estruture com: vis\xE3o geral, pontos principais numerados, explica\xE7\xE3o com palavras simples e uma se\xE7\xE3o "Poss\xEDveis quest\xF5es de prova" com checkboxes (- [ ]). Se o material for insuficiente, complemente com seu conhecimento e sinalize o que n\xE3o veio das anota\xE7\xF5es.`;
1226
+ }
1227
+ function flashcardsPrompt(disciplina, tema) {
1228
+ return `Gere de 10 a 20 flashcards sobre "${tema}" para a disciplina "${disciplina}", baseados no material fornecido. Formato obrigat\xF3rio: uma linha por card, "pergunta :: resposta" (compat\xEDvel com o plugin Spaced Repetition do Obsidian). Perguntas objetivas, respostas curtas. N\xE3o use cabe\xE7alhos nem numera\xE7\xE3o \u2014 apenas as linhas de cards.`;
1229
+ }
1230
+
1231
+ // src/utils/dates.ts
1232
+ function isValidISODate(value) {
1233
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
1234
+ const date = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
1235
+ return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
1236
+ }
1237
+ function addDaysISO(iso, days) {
1238
+ const date = /* @__PURE__ */ new Date(`${iso}T00:00:00Z`);
1239
+ date.setUTCDate(date.getUTCDate() + days);
1240
+ return date.toISOString().slice(0, 10);
1241
+ }
1242
+ var DATE_HINT = "Use uma data v\xE1lida no formato YYYY-MM-DD.";
1243
+
1244
+ // src/commands/note-factory.ts
1245
+ async function readMaterial(dir) {
1246
+ if (!await import_fs_extra11.default.pathExists(dir)) return "";
1247
+ const parts = [];
1248
+ for (const file of (await import_fs_extra11.default.readdir(dir)).filter((f) => f.endsWith(".md"))) {
1249
+ parts.push(await import_fs_extra11.default.readFile(import_node_path14.default.join(dir, file), "utf8"));
1250
+ }
1251
+ return parts.join("\n\n---\n\n");
1252
+ }
1253
+ function noteCommand(spec) {
1254
+ const command = new import_commander9.Command(spec.name).description(spec.description).argument("[titulo]", "t\xEDtulo da nota").option("-d, --disciplina <nome>", "disciplina (evita o prompt)").option("-s, --semestre <n>", "semestre (padr\xE3o: semestre atual)").option("-c, --commit", "faz git commit no vault ap\xF3s criar");
1255
+ if (spec.extraField) {
1256
+ command.option(
1257
+ `--${spec.extraField.flag} <valor>`,
1258
+ spec.extraField.flagDescription
1259
+ );
1260
+ }
1261
+ if (spec.ai) {
1262
+ command.option(
1263
+ "--ai",
1264
+ `gera o conte\xFAdo com IA a partir de ${spec.ai.sourceFolder}/ (requer ANTHROPIC_API_KEY)`
1265
+ );
1266
+ }
1267
+ command.action(
1268
+ async (titulo, options) => {
1269
+ const vault = await requireVault();
1270
+ const matriz = await loadMatriz(vault.root);
1271
+ const semestre = options["semestre"] ? Number(options["semestre"]) : await pickSemestre(vault.config.semestreAtual);
1272
+ const disciplina = await pickDisciplina(
1273
+ matriz,
1274
+ semestre,
1275
+ options["disciplina"]
1276
+ );
1277
+ const tituloFinal = titulo ?? await askTitulo(spec.tituloPrompt);
1278
+ const extra = {};
1279
+ if (spec.extraField) {
1280
+ const field = spec.extraField;
1281
+ const camel = field.flag.replace(
1282
+ /-(\w)/g,
1283
+ (_, c) => c.toUpperCase()
1284
+ );
1285
+ let value = options[camel];
1286
+ if (value !== void 0 && field.isDate && !isValidISODate(value)) {
1287
+ throw new Error(`Data inv\xE1lida: "${value}". ${DATE_HINT}`);
1288
+ }
1289
+ value ??= await (0, import_prompts6.input)({
1290
+ message: field.prompt,
1291
+ default: todayISO(),
1292
+ validate: (v) => !field.isDate || isValidISODate(v) || DATE_HINT
1293
+ });
1294
+ extra[field.key] = value;
1295
+ }
1296
+ let conteudoIA;
1297
+ if (spec.ai && options["ai"]) {
1298
+ const provider = anthropicProvider();
1299
+ const sourceDir = import_node_path14.default.join(
1300
+ disciplinaDir(vault.root, semestre, disciplina),
1301
+ spec.ai.sourceFolder
1302
+ );
1303
+ const spinAi = spinner(
1304
+ `Gerando conte\xFAdo com IA (${spec.ai.sourceFolder}/)...`
1305
+ ).start();
1306
+ try {
1307
+ const material = await readMaterial(sourceDir);
1308
+ conteudoIA = await provider.generate(
1309
+ spec.ai.buildPrompt(disciplina, tituloFinal),
1310
+ material || "(sem anota\xE7\xF5es \u2014 use seu conhecimento)"
1311
+ );
1312
+ spinAi.succeed("Conte\xFAdo gerado pela IA!");
1313
+ } catch (error) {
1314
+ spinAi.fail("Falha na gera\xE7\xE3o com IA.");
1315
+ throw error;
1316
+ }
1317
+ }
1318
+ const spin = spinner("Criando nota...").start();
1319
+ try {
1320
+ const file = await createNote(vault, {
1321
+ kind: spec.kind,
1322
+ semestre,
1323
+ disciplina,
1324
+ titulo: tituloFinal,
1325
+ extra
1326
+ });
1327
+ if (conteudoIA) {
1328
+ await import_fs_extra11.default.appendFile(file, `
1329
+ ## Gerado por IA
1330
+
1331
+ ${conteudoIA}
1332
+ `);
1333
+ }
1334
+ spin.succeed(`Pronto! Nota criada em:`);
1335
+ logger.item(import_node_path14.default.relative(vault.root, file));
1336
+ if (options["commit"]) {
1337
+ const committed = await commitVault(
1338
+ vault.root,
1339
+ `minerva: ${spec.name} "${tituloFinal}" (${disciplina})`
1340
+ );
1341
+ if (committed) logger.success("Commit criado no vault.");
1342
+ else logger.info("Nada novo para commitar.");
1343
+ }
1344
+ } catch (error) {
1345
+ spin.fail("N\xE3o consegui criar a nota.");
1346
+ throw error;
1347
+ }
1348
+ }
1349
+ );
1350
+ return command;
1351
+ }
1352
+
1353
+ // src/commands/notes.ts
1354
+ function aulaCommand() {
1355
+ return noteCommand({
1356
+ kind: "aula",
1357
+ name: "aula",
1358
+ description: "Cria a anota\xE7\xE3o de uma aula",
1359
+ tituloPrompt: "T\xEDtulo da aula:"
1360
+ });
1361
+ }
1362
+ function resumoCommand() {
1363
+ return noteCommand({
1364
+ kind: "resumo",
1365
+ name: "resumo",
1366
+ description: "Cria um resumo de estudo",
1367
+ tituloPrompt: "Tema do resumo:",
1368
+ ai: { sourceFolder: "Aulas", buildPrompt: resumoPrompt }
1369
+ });
1370
+ }
1371
+ function provaCommand() {
1372
+ return noteCommand({
1373
+ kind: "prova",
1374
+ name: "prova",
1375
+ description: "Agenda e planeja uma prova",
1376
+ tituloPrompt: "Nome da prova (ex.: P1):",
1377
+ extraField: {
1378
+ key: "dataProva",
1379
+ prompt: "Data da prova (YYYY-MM-DD):",
1380
+ flag: "data",
1381
+ flagDescription: "data da prova (YYYY-MM-DD)",
1382
+ isDate: true
1383
+ }
1384
+ });
1385
+ }
1386
+ function trabalhoCommand() {
1387
+ return noteCommand({
1388
+ kind: "trabalho",
1389
+ name: "trabalho",
1390
+ description: "Cria um trabalho acad\xEAmico",
1391
+ tituloPrompt: "T\xEDtulo do trabalho:",
1392
+ extraField: {
1393
+ key: "entrega",
1394
+ prompt: "Data de entrega (YYYY-MM-DD):",
1395
+ flag: "entrega",
1396
+ flagDescription: "data de entrega (YYYY-MM-DD)",
1397
+ isDate: true
1398
+ }
1399
+ });
1400
+ }
1401
+ function projetoCommand() {
1402
+ return noteCommand({
1403
+ kind: "projeto",
1404
+ name: "projeto",
1405
+ description: "Cria um projeto",
1406
+ tituloPrompt: "Nome do projeto:"
1407
+ });
1408
+ }
1409
+ function flashcardsNewCommand() {
1410
+ return noteCommand({
1411
+ kind: "flashcard",
1412
+ name: "new",
1413
+ description: "Cria um deck de flashcards",
1414
+ tituloPrompt: "Tema dos flashcards:",
1415
+ ai: { sourceFolder: "Resumos", buildPrompt: flashcardsPrompt }
1416
+ });
1417
+ }
1418
+
1419
+ // src/commands/flashcards.ts
1420
+ function flashcardsCommand() {
1421
+ const command = new import_commander10.Command("flashcards").description(
1422
+ "Cria decks de flashcards ou exporta para o Anki"
1423
+ );
1424
+ const create = flashcardsNewCommand();
1425
+ command.addCommand(create, { isDefault: true });
1426
+ command.command("export").description("Exporta todos os flashcards para um TSV import\xE1vel no Anki").action(async () => {
1427
+ const vault = await requireVault();
1428
+ const spin = spinner("Coletando flashcards do vault...").start();
1429
+ try {
1430
+ const { file, total } = await exportAnki(vault);
1431
+ spin.succeed(`${total} flashcard(s) exportado(s)!`);
1432
+ logger.item(import_node_path15.default.relative(vault.root, file));
1433
+ logger.info("No Anki: Arquivo \u2192 Importar e selecione esse arquivo.");
1434
+ } catch (error) {
1435
+ spin.fail("Falha na exporta\xE7\xE3o.");
1436
+ throw error;
1437
+ }
1438
+ });
1439
+ return command;
1440
+ }
1441
+
1442
+ // src/commands/init.ts
1443
+ var import_node_path16 = __toESM(require("path"), 1);
1444
+ var import_node_child_process2 = require("child_process");
1445
+ var import_node_util2 = require("util");
1446
+ var import_commander11 = require("commander");
1447
+ var import_fs_extra12 = __toESM(require("fs-extra"), 1);
1448
+ var import_prompts8 = require("@inquirer/prompts");
1449
+ var exec2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
1450
+ var GITIGNORE = `.obsidian/workspace*
1451
+ .obsidian/cache
1452
+ .trash/
1453
+ .DS_Store
1454
+ `;
1455
+ var ROADMAP = `# \u{1F5FA}\uFE0F Roadmap da Gradua\xE7\xE3o
1456
+
1457
+ - [ ] 1\xBA semestre conclu\xEDdo
1458
+ - [ ] 2\xBA semestre conclu\xEDdo
1459
+ - [ ] 3\xBA semestre conclu\xEDdo
1460
+ - [ ] Est\xE1gio conquistado
1461
+ - [ ] 4\xBA semestre conclu\xEDdo
1462
+ - [ ] 5\xBA semestre conclu\xEDdo
1463
+ - [ ] Trabalho de Gradua\xE7\xE3o entregue
1464
+ - [ ] 6\xBA semestre conclu\xEDdo
1465
+ - [ ] \u{1F393} Formado!
1466
+ `;
1467
+ async function initGit(root) {
1468
+ try {
1469
+ await exec2("git", ["init"], { cwd: root });
1470
+ return true;
1471
+ } catch {
1472
+ return false;
1473
+ }
1474
+ }
1475
+ async function writeIfMissing(file, content) {
1476
+ if (!await import_fs_extra12.default.pathExists(file)) {
1477
+ await import_fs_extra12.default.outputFile(file, content, "utf8");
1478
+ }
1479
+ }
1480
+ function initCommand() {
1481
+ return new import_commander11.Command("init").description("Cria a estrutura completa do curso (vault de estudos)").argument("[pasta]", "pasta de destino", DEFAULT_VAULT_DIRNAME).option("-s, --semestre <n>", "semestre atual (1-6)").option("-a, --aluno <nome>", "seu nome").action(
1482
+ async (pasta, options) => {
1483
+ logger.title("Minerva CLI \u2014 vamos montar sua gradua\xE7\xE3o!");
1484
+ const aluno = options.aluno ?? await (0, import_prompts8.input)({ message: "Seu nome:", default: "Estudante" });
1485
+ const semestreAtual = options.semestre !== void 0 ? Number(options.semestre) : await (0, import_prompts8.number)({
1486
+ message: "Em qual semestre voc\xEA est\xE1? (1-6)",
1487
+ default: 1,
1488
+ min: 1,
1489
+ max: 6
1490
+ }) ?? 1;
1491
+ const root = import_node_path16.default.resolve(pasta);
1492
+ const config = {
1493
+ versao: APP_VERSION,
1494
+ aluno,
1495
+ semestreAtual,
1496
+ criadoEm: todayISO(),
1497
+ horasEstudadas: 0
1498
+ };
1499
+ const spin = spinner("Criando estrutura de pastas...").start();
1500
+ try {
1501
+ await import_fs_extra12.default.ensureDir(root);
1502
+ await scaffoldVaultFolders(root);
1503
+ await saveConfig(root, config);
1504
+ spin.text = "Salvando matriz curricular...";
1505
+ await saveMatriz(root, matrizOficial);
1506
+ spin.text = "Copiando templates...";
1507
+ await scaffoldTemplates(root);
1508
+ spin.text = "Criando semestres e disciplinas...";
1509
+ const totalDisciplinas = await scaffoldSemestres(root, matrizOficial);
1510
+ spin.text = "Preparando vault do Obsidian...";
1511
+ await scaffoldObsidian(root);
1512
+ spin.text = "Gerando README e dashboard...";
1513
+ await writeIfMissing(
1514
+ import_node_path16.default.join(root, "README.md"),
1515
+ await renderTemplate("readme", {
1516
+ ...config,
1517
+ curso: matrizOficial.curso,
1518
+ instituicao: matrizOficial.instituicao,
1519
+ semestres: matrizOficial.semestres
1520
+ })
1521
+ );
1522
+ await writeIfMissing(import_node_path16.default.join(root, ".gitignore"), GITIGNORE);
1523
+ await writeIfMissing(
1524
+ import_node_path16.default.join(root, "roadmap", "Roadmap.md"),
1525
+ ROADMAP
1526
+ );
1527
+ await generateDashboard({ root, config }, matrizOficial);
1528
+ spin.text = "Inicializando Git...";
1529
+ const gitOk = await initGit(root);
1530
+ spin.succeed("Vault criado com sucesso!");
1531
+ logger.blank();
1532
+ logger.success(`\u{1F4C1} ${root}`);
1533
+ logger.item(`${totalDisciplinas} disciplinas em 6 semestres`);
1534
+ logger.item("Dashboard em dashboard/Dashboard.md");
1535
+ logger.item("Abra a pasta como vault no Obsidian \u{1F7E3}");
1536
+ if (!gitOk) {
1537
+ logger.warn("Git n\xE3o encontrado \u2014 reposit\xF3rio n\xE3o inicializado.");
1538
+ }
1539
+ logger.blank();
1540
+ logger.info(`Pr\xF3ximo passo: cd ${pasta} && minerva dashboard`);
1541
+ } catch (error) {
1542
+ spin.fail("Falha ao criar o vault.");
1543
+ throw error;
1544
+ }
1545
+ }
1546
+ );
1547
+ }
1548
+
1549
+ // src/commands/nota.ts
1550
+ var import_chalk2 = __toESM(require("chalk"), 1);
1551
+ var import_commander12 = require("commander");
1552
+ var import_prompts9 = require("@inquirer/prompts");
1553
+
1554
+ // src/services/notas.ts
1555
+ var import_node_path17 = __toESM(require("path"), 1);
1556
+ var import_fs_extra13 = __toESM(require("fs-extra"), 1);
1557
+ var import_zod3 = require("zod");
1558
+ var NOTAS_FILE = import_node_path17.default.join("curriculo", "notas.json");
1559
+ var MEDIA_APROVACAO = 6;
1560
+ var notaSchema = import_zod3.z.object({
1561
+ disciplina: import_zod3.z.string(),
1562
+ avaliacao: import_zod3.z.string(),
1563
+ valor: import_zod3.z.number().min(0).max(10),
1564
+ peso: import_zod3.z.number().positive().default(1)
1565
+ });
1566
+ var notasSchema = import_zod3.z.array(notaSchema);
1567
+ async function loadNotas(root) {
1568
+ const file = import_node_path17.default.join(root, NOTAS_FILE);
1569
+ if (!await import_fs_extra13.default.pathExists(file)) return [];
1570
+ const raw = await import_fs_extra13.default.readJson(file);
1571
+ return notasSchema.parse(raw);
1572
+ }
1573
+ async function registrarNota(root, nota) {
1574
+ const notas = await loadNotas(root);
1575
+ const existing = notas.findIndex(
1576
+ (n) => n.disciplina === nota.disciplina && n.avaliacao.toLowerCase() === nota.avaliacao.toLowerCase()
1577
+ );
1578
+ if (existing >= 0) notas[existing] = nota;
1579
+ else notas.push(nota);
1580
+ await import_fs_extra13.default.outputJson(import_node_path17.default.join(root, NOTAS_FILE), notas, { spaces: 4 });
1581
+ }
1582
+ function media(notas) {
1583
+ const pesoTotal = notas.reduce((sum, n) => sum + n.peso, 0);
1584
+ if (pesoTotal === 0) return 0;
1585
+ const soma = notas.reduce((sum, n) => sum + n.valor * n.peso, 0);
1586
+ return Math.round(soma / pesoTotal * 100) / 100;
1587
+ }
1588
+ function boletim(notas) {
1589
+ const porDisciplina = /* @__PURE__ */ new Map();
1590
+ for (const nota of notas) {
1591
+ const list = porDisciplina.get(nota.disciplina) ?? [];
1592
+ list.push(nota);
1593
+ porDisciplina.set(nota.disciplina, list);
1594
+ }
1595
+ return [...porDisciplina.entries()].map(([disciplina, list]) => {
1596
+ const m = media(list);
1597
+ return {
1598
+ disciplina,
1599
+ notas: list,
1600
+ media: m,
1601
+ situacao: m >= MEDIA_APROVACAO ? "aprovado" : m >= MEDIA_APROVACAO - 2 ? "em risco" : "reprovado"
1602
+ };
1603
+ });
1604
+ }
1605
+
1606
+ // src/commands/nota.ts
1607
+ var SITUACAO_COLOR = {
1608
+ aprovado: import_chalk2.default.green,
1609
+ "em risco": import_chalk2.default.yellow,
1610
+ reprovado: import_chalk2.default.red
1611
+ };
1612
+ function notaCommand() {
1613
+ const command = new import_commander12.Command("nota").description(
1614
+ "Registra notas e mostra o boletim com m\xE9dias"
1615
+ );
1616
+ command.command("boletim", { isDefault: true }).description("Mostra m\xE9dias e situa\xE7\xE3o por disciplina").option("--json", "sa\xEDda em JSON").action(async (options) => {
1617
+ const vault = await requireVault();
1618
+ const notas = await loadNotas(vault.root);
1619
+ const linhas = boletim(notas);
1620
+ if (options.json) {
1621
+ console.log(JSON.stringify(linhas, null, 2));
1622
+ return;
1623
+ }
1624
+ if (linhas.length === 0) {
1625
+ logger.info("Nenhuma nota registrada. Use: minerva nota add");
1626
+ return;
1627
+ }
1628
+ logger.title(`Boletim (m\xE9dia m\xEDnima: ${MEDIA_APROVACAO})`);
1629
+ for (const linha of linhas) {
1630
+ const color = SITUACAO_COLOR[linha.situacao];
1631
+ logger.info(
1632
+ `${linha.disciplina}: m\xE9dia ${import_chalk2.default.bold(linha.media)} \u2014 ${color(linha.situacao)}`
1633
+ );
1634
+ for (const nota of linha.notas) {
1635
+ logger.item(`${nota.avaliacao}: ${nota.valor} (peso ${nota.peso})`);
1636
+ }
1637
+ }
1638
+ });
1639
+ command.command("add").description("Registra a nota de uma avalia\xE7\xE3o").argument("[valor]", "nota de 0 a 10").option("-d, --disciplina <nome>", "disciplina").option("-a, --avaliacao <nome>", "nome da avalia\xE7\xE3o (ex.: P1)").option("-p, --peso <n>", "peso da avalia\xE7\xE3o", "1").option("-s, --semestre <n>", "semestre da disciplina").action(
1640
+ async (valor, options) => {
1641
+ const vault = await requireVault();
1642
+ const matriz = await loadMatriz(vault.root);
1643
+ const semestre = options.semestre ? Number(options.semestre) : vault.config.semestreAtual;
1644
+ const disciplina = await pickDisciplina(
1645
+ matriz,
1646
+ semestre,
1647
+ options.disciplina
1648
+ );
1649
+ const avaliacao = options.avaliacao ?? await (0, import_prompts9.input)({
1650
+ message: "Avalia\xE7\xE3o (ex.: P1, Trabalho 1):",
1651
+ validate: (v) => v.trim().length > 0 || "Informe um nome."
1652
+ });
1653
+ const valorFinal = Number(
1654
+ (valor ?? await (0, import_prompts9.input)({
1655
+ message: "Nota (0-10):",
1656
+ validate: (v) => {
1657
+ const n = Number(v.replace(",", "."));
1658
+ return Number.isFinite(n) && n >= 0 && n <= 10 || "Informe uma nota entre 0 e 10.";
1659
+ }
1660
+ })).replace(",", ".")
1661
+ );
1662
+ await registrarNota(vault.root, {
1663
+ disciplina,
1664
+ avaliacao,
1665
+ valor: valorFinal,
1666
+ peso: Number(options.peso)
1667
+ });
1668
+ const notas = await loadNotas(vault.root);
1669
+ const linha = boletim(notas).find((b) => b.disciplina === disciplina);
1670
+ logger.success(
1671
+ `${avaliacao} = ${valorFinal} registrada em ${disciplina}.`
1672
+ );
1673
+ logger.info(
1674
+ `M\xE9dia atual: ${linha.media} (${SITUACAO_COLOR[linha.situacao](linha.situacao)})`
1675
+ );
1676
+ }
1677
+ );
1678
+ return command;
1679
+ }
1680
+
1681
+ // src/commands/open.ts
1682
+ var import_node_path18 = __toESM(require("path"), 1);
1683
+ var import_node_child_process3 = require("child_process");
1684
+ var import_node_util3 = require("util");
1685
+ var import_commander13 = require("commander");
1686
+ var exec3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
1687
+ async function openUrl(url) {
1688
+ const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1689
+ await exec3(opener, [url], { shell: process.platform === "win32" });
1690
+ }
1691
+ function openCommand() {
1692
+ return new import_commander13.Command("open").description("Abre o dashboard (ou um arquivo) no Obsidian").argument("[arquivo]", "caminho relativo dentro do vault").action(async (arquivo) => {
1693
+ const vault = await requireVault();
1694
+ const target = import_node_path18.default.join(
1695
+ vault.root,
1696
+ arquivo ?? import_node_path18.default.join("dashboard", "Dashboard.md")
1697
+ );
1698
+ const url = `obsidian://open?path=${encodeURIComponent(target)}`;
1699
+ try {
1700
+ await openUrl(url);
1701
+ logger.success("Abrindo no Obsidian...");
1702
+ } catch {
1703
+ logger.error(
1704
+ "N\xE3o consegui abrir o Obsidian. Ele est\xE1 instalado? O vault j\xE1 foi aberto nele ao menos uma vez?"
1705
+ );
1706
+ logger.info(`Arquivo: ${target}`);
1707
+ process.exitCode = 1;
1708
+ }
1709
+ });
1710
+ }
1711
+
1712
+ // src/commands/pendencias.ts
1713
+ var import_chalk3 = __toESM(require("chalk"), 1);
1714
+ var import_commander14 = require("commander");
1715
+
1716
+ // src/services/pendencias.ts
1717
+ var import_node_path19 = __toESM(require("path"), 1);
1718
+ var import_fs_extra14 = __toESM(require("fs-extra"), 1);
1719
+ async function scanFile(file, root, disciplina) {
1720
+ const content = await import_fs_extra14.default.readFile(file, "utf8");
1721
+ const items = [];
1722
+ content.split("\n").forEach((line, index) => {
1723
+ const match = /^\s*[-*] \[ \]\s*(.*)$/.exec(line);
1724
+ if (match) {
1725
+ items.push({
1726
+ disciplina,
1727
+ arquivo: import_node_path19.default.relative(root, file),
1728
+ linha: index + 1,
1729
+ texto: match[1]?.trim() ?? ""
1730
+ });
1731
+ }
1732
+ });
1733
+ return items;
1734
+ }
1735
+ async function listPendencias(vault, matriz) {
1736
+ const semestre = getSemestre(matriz, vault.config.semestreAtual);
1737
+ const pendencias = [];
1738
+ for (const disciplina of semestre.disciplinas) {
1739
+ const dir = disciplinaDir(vault.root, semestre.numero, disciplina.nome);
1740
+ if (!await import_fs_extra14.default.pathExists(dir)) continue;
1741
+ const stack = [dir];
1742
+ while (stack.length > 0) {
1743
+ const current = stack.pop();
1744
+ for (const entry of await import_fs_extra14.default.readdir(current, { withFileTypes: true })) {
1745
+ const full = import_node_path19.default.join(current, entry.name);
1746
+ if (entry.isDirectory()) stack.push(full);
1747
+ else if (entry.name.endsWith(".md")) {
1748
+ pendencias.push(
1749
+ ...await scanFile(full, vault.root, disciplina.nome)
1750
+ );
1751
+ }
1752
+ }
1753
+ }
1754
+ }
1755
+ return pendencias;
1756
+ }
1757
+
1758
+ // src/commands/pendencias.ts
1759
+ function pendenciasCommand() {
1760
+ return new import_commander14.Command("pendencias").description("Lista as tarefas pendentes (- [ ]) do semestre atual").option("--json", "sa\xEDda em JSON").action(async (options) => {
1761
+ const vault = await requireVault();
1762
+ const matriz = await loadMatriz(vault.root);
1763
+ const pendencias = await listPendencias(vault, matriz);
1764
+ if (options.json) {
1765
+ console.log(JSON.stringify(pendencias, null, 2));
1766
+ return;
1767
+ }
1768
+ if (pendencias.length === 0) {
1769
+ logger.success("Nenhuma pend\xEAncia aberta. \u{1F389}");
1770
+ return;
1771
+ }
1772
+ logger.title(`${pendencias.length} pend\xEAncia(s) aberta(s)`);
1773
+ let atual = "";
1774
+ for (const p of pendencias) {
1775
+ if (p.disciplina !== atual) {
1776
+ atual = p.disciplina;
1777
+ logger.info(import_chalk3.default.bold(atual));
1778
+ }
1779
+ logger.item(
1780
+ `${p.texto || "(sem descri\xE7\xE3o)"} ${import_chalk3.default.dim(`\u2014 ${p.arquivo}:${p.linha}`)}`
1781
+ );
1782
+ }
1783
+ });
1784
+ }
1785
+
1786
+ // src/commands/portfolio.ts
1787
+ var import_node_path20 = __toESM(require("path"), 1);
1788
+ var import_commander15 = require("commander");
1789
+ var import_fs_extra15 = __toESM(require("fs-extra"), 1);
1790
+ async function collectProjects(root) {
1791
+ const items = [];
1792
+ const pessoais = import_node_path20.default.join(root, "projetos-pessoais");
1793
+ if (await import_fs_extra15.default.pathExists(pessoais)) {
1794
+ for (const entry of await import_fs_extra15.default.readdir(pessoais, {
1795
+ withFileTypes: true
1796
+ })) {
1797
+ if (entry.isDirectory() || entry.name.endsWith(".md")) {
1798
+ items.push({
1799
+ titulo: entry.name.replace(/\.md$/, ""),
1800
+ origem: "Projeto pessoal",
1801
+ caminho: import_node_path20.default.join("projetos-pessoais", entry.name)
1802
+ });
1803
+ }
1804
+ }
1805
+ }
1806
+ const matriz = await loadMatriz(root);
1807
+ for (const semestre of matriz.semestres) {
1808
+ for (const disciplina of semestre.disciplinas) {
1809
+ const dir = import_node_path20.default.join(
1810
+ disciplinaDir(root, semestre.numero, disciplina.nome),
1811
+ "Projetos"
1812
+ );
1813
+ if (!await import_fs_extra15.default.pathExists(dir)) continue;
1814
+ for (const file of await import_fs_extra15.default.readdir(dir)) {
1815
+ if (!file.endsWith(".md")) continue;
1816
+ items.push({
1817
+ titulo: file.replace(/\.md$/, ""),
1818
+ origem: `${disciplina.nome} (${semestre.numero}\xBA sem.)`,
1819
+ caminho: import_node_path20.default.relative(root, import_node_path20.default.join(dir, file))
1820
+ });
1821
+ }
1822
+ }
1823
+ }
1824
+ return items;
1825
+ }
1826
+ function portfolioCommand() {
1827
+ return new import_commander15.Command("portfolio").description("Gera o \xEDndice do portf\xF3lio com todos os seus projetos").action(async () => {
1828
+ const vault = await requireVault();
1829
+ const spin = spinner("Reunindo seus projetos...").start();
1830
+ try {
1831
+ const items = await collectProjects(vault.root);
1832
+ const lines = [
1833
+ "# \u{1F4BC} Portf\xF3lio",
1834
+ "",
1835
+ `> Gerado por \`minerva portfolio\` em ${todayISO()}.`,
1836
+ "",
1837
+ "| Projeto | Origem | Nota |",
1838
+ "| ------- | ------ | ---- |",
1839
+ ...items.map(
1840
+ (item) => `| ${item.titulo} | ${item.origem} | [abrir](../${item.caminho.split(import_node_path20.default.sep).join("/")}) |`
1841
+ ),
1842
+ ""
1843
+ ];
1844
+ const destination = import_node_path20.default.join(vault.root, "portfolio", "Portfolio.md");
1845
+ await import_fs_extra15.default.outputFile(destination, lines.join("\n"), "utf8");
1846
+ spin.succeed(`Portf\xF3lio atualizado com ${items.length} projeto(s)!`);
1847
+ logger.item(import_node_path20.default.relative(vault.root, destination));
1848
+ } catch (error) {
1849
+ spin.fail("Falha ao gerar o portf\xF3lio.");
1850
+ throw error;
1851
+ }
1852
+ });
1853
+ }
1854
+
1855
+ // src/commands/semana.ts
1856
+ var import_commander16 = require("commander");
1857
+
1858
+ // src/services/agenda.ts
1859
+ var import_node_path21 = __toESM(require("path"), 1);
1860
+ var import_fs_extra16 = __toESM(require("fs-extra"), 1);
1861
+ var FRONTMATTER_DATES = [
1862
+ { folder: "Provas", field: "data-prova", tipo: "prova" },
1863
+ { folder: "Trabalhos", field: "entrega", tipo: "entrega" }
1864
+ ];
1865
+ function extractDate(content, field) {
1866
+ const match = new RegExp(`^${field}:\\s*(\\d{4}-\\d{2}-\\d{2})`, "m").exec(
1867
+ content
1868
+ );
1869
+ return match ? match[1] : null;
1870
+ }
1871
+ function extractTitle(content, fallback) {
1872
+ const match = /^# .*?[—-]\s*(.+)$/m.exec(content);
1873
+ return match ? match[1].trim() : fallback;
1874
+ }
1875
+ async function proximosCompromissos(vault, matriz, dias = 7) {
1876
+ const semestre = getSemestre(matriz, vault.config.semestreAtual);
1877
+ const inicio = todayISO();
1878
+ const fim = addDaysISO(inicio, dias);
1879
+ const compromissos = [];
1880
+ for (const disciplina of semestre.disciplinas) {
1881
+ for (const spec of FRONTMATTER_DATES) {
1882
+ const dir = import_node_path21.default.join(
1883
+ disciplinaDir(vault.root, semestre.numero, disciplina.nome),
1884
+ spec.folder
1885
+ );
1886
+ if (!await import_fs_extra16.default.pathExists(dir)) continue;
1887
+ for (const file of await import_fs_extra16.default.readdir(dir)) {
1888
+ if (!file.endsWith(".md")) continue;
1889
+ const full = import_node_path21.default.join(dir, file);
1890
+ const content = await import_fs_extra16.default.readFile(full, "utf8");
1891
+ const data = extractDate(content, spec.field);
1892
+ if (data && data >= inicio && data <= fim) {
1893
+ compromissos.push({
1894
+ tipo: spec.tipo,
1895
+ disciplina: disciplina.nome,
1896
+ titulo: extractTitle(content, file.replace(/\.md$/, "")),
1897
+ data,
1898
+ arquivo: import_node_path21.default.relative(vault.root, full)
1899
+ });
1900
+ }
1901
+ }
1902
+ }
1903
+ }
1904
+ return compromissos.sort((a, b) => a.data.localeCompare(b.data));
1905
+ }
1906
+
1907
+ // src/commands/semana.ts
1908
+ function semanaCommand() {
1909
+ return new import_commander16.Command("semana").description("Mostra provas, entregas e pend\xEAncias dos pr\xF3ximos dias").option("--dias <n>", "janela em dias", "7").option("--json", "sa\xEDda em JSON").action(async (options) => {
1910
+ const vault = await requireVault();
1911
+ const matriz = await loadMatriz(vault.root);
1912
+ const dias = Number(options.dias);
1913
+ const [compromissos, pendencias] = await Promise.all([
1914
+ proximosCompromissos(vault, matriz, dias),
1915
+ listPendencias(vault, matriz)
1916
+ ]);
1917
+ if (options.json) {
1918
+ console.log(
1919
+ JSON.stringify(
1920
+ { compromissos, totalPendencias: pendencias.length },
1921
+ null,
1922
+ 2
1923
+ )
1924
+ );
1925
+ return;
1926
+ }
1927
+ logger.title(`Sua semana (pr\xF3ximos ${dias} dias)`);
1928
+ if (compromissos.length === 0) {
1929
+ logger.success("Nenhuma prova ou entrega no per\xEDodo. \u{1F389}");
1930
+ }
1931
+ for (const c of compromissos) {
1932
+ const icon = c.tipo === "prova" ? "\u{1F3AF}" : "\u{1F4E6}";
1933
+ logger.item(`${c.data} ${icon} ${c.disciplina} \u2014 ${c.titulo}`);
1934
+ }
1935
+ logger.blank();
1936
+ if (pendencias.length > 0) {
1937
+ logger.warn(
1938
+ `${pendencias.length} pend\xEAncia(s) aberta(s) \u2014 veja "minerva pendencias".`
1939
+ );
1940
+ }
1941
+ });
1942
+ }
1943
+
1944
+ // src/commands/semestre.ts
1945
+ var import_commander17 = require("commander");
1946
+ function semestreCommand() {
1947
+ return new import_commander17.Command("semestre").description("Mostra ou define o semestre atual").argument("[numero]", "novo semestre (1-6)").action(async (numero) => {
1948
+ const vault = await requireVault();
1949
+ const matriz = await loadMatriz(vault.root);
1950
+ if (numero === void 0) {
1951
+ const atual = getSemestre(matriz, vault.config.semestreAtual);
1952
+ logger.title(`${atual.numero}\xBA semestre`);
1953
+ logger.info(`Progresso do curso: ${progressBar(atual.numero - 1, 6)}`);
1954
+ for (const d of atual.disciplinas) {
1955
+ logger.item(`${d.nome} (${d.aulas} aulas)`);
1956
+ }
1957
+ return;
1958
+ }
1959
+ const novo = getSemestre(matriz, Number(numero));
1960
+ vault.config.semestreAtual = novo.numero;
1961
+ await saveConfig(vault.root, vault.config);
1962
+ await generateDashboard(vault, matriz);
1963
+ logger.success(
1964
+ `Semestre atual definido para ${novo.numero}\xBA. Dashboard atualizado!`
1965
+ );
1966
+ });
1967
+ }
1968
+
1969
+ // src/commands/stats.ts
1970
+ var import_chalk4 = __toESM(require("chalk"), 1);
1971
+ var import_commander18 = require("commander");
1972
+
1973
+ // src/services/stats.ts
1974
+ var import_node_path22 = __toESM(require("path"), 1);
1975
+ var import_fs_extra17 = __toESM(require("fs-extra"), 1);
1976
+ var NOTE_FILENAME = /^(\d{4}-\d{2}-\d{2})-(aula|resumo|prova|trabalho|projeto|flashcards)-/;
1977
+ async function collectDates(root) {
1978
+ const dates = [];
1979
+ const stack = [root];
1980
+ while (stack.length > 0) {
1981
+ const current = stack.pop();
1982
+ for (const entry of await import_fs_extra17.default.readdir(current, { withFileTypes: true })) {
1983
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
1984
+ const full = import_node_path22.default.join(current, entry.name);
1985
+ if (entry.isDirectory()) stack.push(full);
1986
+ else {
1987
+ const match = NOTE_FILENAME.exec(entry.name);
1988
+ if (match) dates.push([match[1], match[2]]);
1989
+ }
1990
+ }
1991
+ }
1992
+ return dates;
1993
+ }
1994
+ async function collectStats(vault, dias = 30) {
1995
+ const entries = await collectDates(vault.root);
1996
+ const estudoLog = await loadEstudoLog(vault.root);
1997
+ const hoje = todayISO();
1998
+ const inicio = addDaysISO(hoje, -dias);
1999
+ const notasPorDia = {};
2000
+ const notasPorTipo = {};
2001
+ const diasAtivos = new Set(estudoLog.map((s) => s.data));
2002
+ for (const [data, tipo] of entries) {
2003
+ notasPorTipo[tipo] = (notasPorTipo[tipo] ?? 0) + 1;
2004
+ diasAtivos.add(data);
2005
+ if (data >= inicio && data <= hoje) {
2006
+ notasPorDia[data] = (notasPorDia[data] ?? 0) + 1;
2007
+ }
2008
+ }
2009
+ let streak = 0;
2010
+ for (let dia = hoje; diasAtivos.has(dia); dia = addDaysISO(dia, -1)) {
2011
+ streak += 1;
2012
+ }
2013
+ return {
2014
+ notasPorDia,
2015
+ notasPorTipo,
2016
+ streak,
2017
+ horasEstudadas: vault.config.horasEstudadas,
2018
+ totalNotas: entries.length
2019
+ };
2020
+ }
2021
+
2022
+ // src/commands/stats.ts
2023
+ function cell(count) {
2024
+ if (count === 0) return import_chalk4.default.dim("\xB7");
2025
+ if (count === 1) return import_chalk4.default.green("\u25AA");
2026
+ if (count <= 3) return import_chalk4.default.green("\u25AE");
2027
+ return import_chalk4.default.bold.green("\u2588");
2028
+ }
2029
+ function statsCommand() {
2030
+ return new import_commander18.Command("stats").description("Mostra estat\xEDsticas de const\xE2ncia (notas, streak, horas)").option("--dias <n>", "janela do gr\xE1fico em dias", "30").option("--json", "sa\xEDda em JSON").action(async (options) => {
2031
+ const vault = await requireVault();
2032
+ const dias = Number(options.dias);
2033
+ const stats = await collectStats(vault, dias);
2034
+ if (options.json) {
2035
+ console.log(JSON.stringify(stats, null, 2));
2036
+ return;
2037
+ }
2038
+ logger.title("Suas estat\xEDsticas de estudo");
2039
+ logger.info(`\u{1F525} Streak: ${import_chalk4.default.bold(stats.streak)} dia(s) seguidos`);
2040
+ logger.info(`\u23F1\uFE0F Horas estudadas: ${stats.horasEstudadas}h`);
2041
+ logger.info(`\u{1F4DD} Notas criadas: ${stats.totalNotas}`);
2042
+ logger.blank();
2043
+ const hoje = todayISO();
2044
+ let graph = "";
2045
+ for (let i = dias - 1; i >= 0; i -= 1) {
2046
+ graph += cell(stats.notasPorDia[addDaysISO(hoje, -i)] ?? 0);
2047
+ }
2048
+ logger.info(`\xDAltimos ${dias} dias: ${graph}`);
2049
+ logger.blank();
2050
+ for (const [tipo, total] of Object.entries(stats.notasPorTipo).sort(
2051
+ (a, b) => b[1] - a[1]
2052
+ )) {
2053
+ logger.item(`${tipo}: ${total}`);
2054
+ }
2055
+ });
2056
+ }
2057
+
2058
+ // src/commands/update-matriz.ts
2059
+ var import_commander19 = require("commander");
2060
+ function updateMatrizCommand() {
2061
+ return new import_commander19.Command("update-matriz").description("Atualiza a matriz curricular a partir do site da FATEC").option("--url <url>", "URL alternativa da matriz", MATRIZ_URL).action(async (options) => {
2062
+ const vault = await requireVault();
2063
+ const spin = spinner("Baixando matriz curricular...").start();
2064
+ try {
2065
+ const matriz = await fetchMatriz(options.url);
2066
+ await saveMatriz(vault.root, matriz);
2067
+ spin.succeed("Matriz curricular atualizada!");
2068
+ const total = matriz.semestres.reduce(
2069
+ (sum, s) => sum + s.disciplinas.length,
2070
+ 0
2071
+ );
2072
+ logger.item(`${total} disciplinas em 6 semestres`);
2073
+ logger.item(`Fonte: ${matriz.fonte}`);
2074
+ } catch (error) {
2075
+ spin.fail("N\xE3o foi poss\xEDvel atualizar a matriz.");
2076
+ logger.warn(
2077
+ "A matriz embutida na CLI continua funcionando normalmente."
2078
+ );
2079
+ throw error;
2080
+ }
2081
+ });
2082
+ }
2083
+
2084
+ // src/program.ts
2085
+ function buildProgram() {
2086
+ const program = new import_commander20.Command(APP_NAME).description(APP_DESCRIPTION).version(APP_VERSION, "-v, --version", "mostra a vers\xE3o").helpCommand("help [comando]", "mostra ajuda de um comando").showSuggestionAfterError();
2087
+ program.addCommand(initCommand());
2088
+ program.addCommand(dashboardCommand());
2089
+ program.addCommand(aulaCommand());
2090
+ program.addCommand(resumoCommand());
2091
+ program.addCommand(provaCommand());
2092
+ program.addCommand(trabalhoCommand());
2093
+ program.addCommand(projetoCommand());
2094
+ program.addCommand(portfolioCommand());
2095
+ program.addCommand(cheatsheetCommand());
2096
+ program.addCommand(flashcardsCommand());
2097
+ program.addCommand(semestreCommand());
2098
+ program.addCommand(disciplinaCommand());
2099
+ program.addCommand(estudoCommand());
2100
+ program.addCommand(pendenciasCommand());
2101
+ program.addCommand(semanaCommand());
2102
+ program.addCommand(notaCommand());
2103
+ program.addCommand(statsCommand());
2104
+ program.addCommand(openCommand());
2105
+ program.addCommand(commitCommand());
2106
+ program.addCommand(configCommand());
2107
+ program.addCommand(completionCommand());
2108
+ program.addCommand(updateMatrizCommand());
2109
+ program.addCommand(doctorCommand());
2110
+ program.command("version").description("mostra a vers\xE3o da Minerva CLI").action(() => {
2111
+ console.log(APP_VERSION);
2112
+ });
2113
+ return program;
2114
+ }
2115
+
2116
+ // src/index.ts
2117
+ async function main() {
2118
+ try {
2119
+ await buildProgram().parseAsync(process.argv);
2120
+ } catch (error) {
2121
+ if (error instanceof Error && error.name === "ExitPromptError") {
2122
+ logger.info("At\xE9 a pr\xF3xima! \u{1F44B}");
2123
+ return;
2124
+ }
2125
+ logger.error(error instanceof Error ? error.message : String(error));
2126
+ if (process.env["DEBUG"] && error instanceof Error) {
2127
+ console.error(import_chalk5.default.dim(error.stack ?? ""));
2128
+ }
2129
+ process.exitCode = 1;
2130
+ }
2131
+ }
2132
+ void main();
2133
+ // Annotate the CommonJS export names for ESM import in node:
2134
+ 0 && (module.exports = {
2135
+ buildProgram
2136
+ });
2137
+ //# sourceMappingURL=index.cjs.map