minerva-cli 0.1.0 → 0.2.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 +217 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +217 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -48,7 +48,7 @@ var import_commander20 = require("commander");
|
|
|
48
48
|
// src/config/constants.ts
|
|
49
49
|
var APP_NAME = "minerva";
|
|
50
50
|
var APP_DESCRIPTION = "Organiza\xE7\xE3o acad\xEAmica para estudantes de ADS da FATEC \u{1F989}";
|
|
51
|
-
var APP_VERSION = "0.
|
|
51
|
+
var APP_VERSION = "0.2.0";
|
|
52
52
|
var DEFAULT_VAULT_DIRNAME = "fatec-ads";
|
|
53
53
|
var MATRIZ_URL = "https://fatectq.cps.sp.gov.br/matriz-curricular-ads/";
|
|
54
54
|
|
|
@@ -610,14 +610,20 @@ function disciplinaDir(root, semestre, disciplina) {
|
|
|
610
610
|
async function scaffoldDisciplina(root, semestre, disciplina) {
|
|
611
611
|
const dir = disciplinaDir(root, semestre, disciplina.nome);
|
|
612
612
|
await Promise.all(
|
|
613
|
-
DISCIPLINA_FOLDERS.map(
|
|
613
|
+
DISCIPLINA_FOLDERS.map(
|
|
614
|
+
(folder) => import_fs_extra5.default.ensureDir(import_node_path6.default.join(dir, folder))
|
|
615
|
+
)
|
|
614
616
|
);
|
|
615
617
|
const files = [
|
|
616
618
|
[
|
|
617
619
|
"README.md",
|
|
618
620
|
() => renderTemplate(
|
|
619
621
|
"disciplina",
|
|
620
|
-
{
|
|
622
|
+
{
|
|
623
|
+
disciplina: disciplina.nome,
|
|
624
|
+
semestre,
|
|
625
|
+
aulas: disciplina.aulas
|
|
626
|
+
},
|
|
621
627
|
root
|
|
622
628
|
)
|
|
623
629
|
],
|
|
@@ -870,7 +876,9 @@ function doctorCommand() {
|
|
|
870
876
|
{
|
|
871
877
|
label: "Node.js",
|
|
872
878
|
run: () => {
|
|
873
|
-
const major = Number(
|
|
879
|
+
const major = Number(
|
|
880
|
+
process.versions.node.split(".")[0]
|
|
881
|
+
);
|
|
874
882
|
if (major < 20) {
|
|
875
883
|
throw new Error(
|
|
876
884
|
`v${process.versions.node} \u2014 atualize para a LTS (>= 20)`
|
|
@@ -887,7 +895,9 @@ function doctorCommand() {
|
|
|
887
895
|
label: "Vault Minerva",
|
|
888
896
|
run: () => {
|
|
889
897
|
if (!root) {
|
|
890
|
-
throw new Error(
|
|
898
|
+
throw new Error(
|
|
899
|
+
'n\xE3o encontrado \u2014 rode "minerva init"'
|
|
900
|
+
);
|
|
891
901
|
}
|
|
892
902
|
return Promise.resolve(root);
|
|
893
903
|
}
|
|
@@ -917,7 +927,9 @@ function doctorCommand() {
|
|
|
917
927
|
label: "Reposit\xF3rio Git",
|
|
918
928
|
run: async () => {
|
|
919
929
|
if (!await import_fs_extra7.default.pathExists(import_node_path10.default.join(root, ".git"))) {
|
|
920
|
-
throw new Error(
|
|
930
|
+
throw new Error(
|
|
931
|
+
'ausente \u2014 rode "git init" no vault'
|
|
932
|
+
);
|
|
921
933
|
}
|
|
922
934
|
return "ok";
|
|
923
935
|
}
|
|
@@ -925,7 +937,9 @@ function doctorCommand() {
|
|
|
925
937
|
{
|
|
926
938
|
label: "Vault do Obsidian",
|
|
927
939
|
run: async () => {
|
|
928
|
-
if (!await import_fs_extra7.default.pathExists(
|
|
940
|
+
if (!await import_fs_extra7.default.pathExists(
|
|
941
|
+
import_node_path10.default.join(root, ".obsidian")
|
|
942
|
+
)) {
|
|
929
943
|
throw new Error(
|
|
930
944
|
'pasta .obsidian ausente \u2014 rode "minerva init" novamente'
|
|
931
945
|
);
|
|
@@ -978,7 +992,9 @@ async function loadEstudoLog(root) {
|
|
|
978
992
|
}
|
|
979
993
|
async function registrarEstudo(vault, horas, disciplina) {
|
|
980
994
|
if (!Number.isFinite(horas) || horas <= 0) {
|
|
981
|
-
throw new Error(
|
|
995
|
+
throw new Error(
|
|
996
|
+
"Informe um n\xFAmero de horas maior que zero (ex.: 1.5)."
|
|
997
|
+
);
|
|
982
998
|
}
|
|
983
999
|
const sessao = {
|
|
984
1000
|
data: todayISO(),
|
|
@@ -1024,12 +1040,16 @@ function estudoCommand() {
|
|
|
1024
1040
|
);
|
|
1025
1041
|
return;
|
|
1026
1042
|
}
|
|
1027
|
-
logger.title(
|
|
1043
|
+
logger.title(
|
|
1044
|
+
`${vault.config.horasEstudadas}h estudadas no total`
|
|
1045
|
+
);
|
|
1028
1046
|
for (const [disciplina2, total] of totals) {
|
|
1029
1047
|
logger.item(`${disciplina2}: ${total}h`);
|
|
1030
1048
|
}
|
|
1031
1049
|
if (totals.size === 0) {
|
|
1032
|
-
logger.info(
|
|
1050
|
+
logger.info(
|
|
1051
|
+
"Registre sua primeira sess\xE3o: minerva estudo 1.5"
|
|
1052
|
+
);
|
|
1033
1053
|
}
|
|
1034
1054
|
return;
|
|
1035
1055
|
}
|
|
@@ -1078,7 +1098,9 @@ async function collectMarkdownFiles(dir) {
|
|
|
1078
1098
|
const stack = [dir];
|
|
1079
1099
|
while (stack.length > 0) {
|
|
1080
1100
|
const current = stack.pop();
|
|
1081
|
-
for (const entry of await import_fs_extra9.default.readdir(current, {
|
|
1101
|
+
for (const entry of await import_fs_extra9.default.readdir(current, {
|
|
1102
|
+
withFileTypes: true
|
|
1103
|
+
})) {
|
|
1082
1104
|
const full = import_node_path12.default.join(current, entry.name);
|
|
1083
1105
|
if (entry.isDirectory()) stack.push(full);
|
|
1084
1106
|
else if (entry.name.endsWith(".md")) files.push(full);
|
|
@@ -1091,14 +1113,22 @@ async function collectAllFlashcards(vault) {
|
|
|
1091
1113
|
const stack = [vault.root];
|
|
1092
1114
|
while (stack.length > 0) {
|
|
1093
1115
|
const current = stack.pop();
|
|
1094
|
-
for (const entry of await import_fs_extra9.default.readdir(current, {
|
|
1095
|
-
|
|
1116
|
+
for (const entry of await import_fs_extra9.default.readdir(current, {
|
|
1117
|
+
withFileTypes: true
|
|
1118
|
+
})) {
|
|
1119
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
1120
|
+
continue;
|
|
1096
1121
|
const full = import_node_path12.default.join(current, entry.name);
|
|
1097
1122
|
if (!entry.isDirectory()) continue;
|
|
1098
1123
|
if (entry.name.toLowerCase() === "flashcards") {
|
|
1099
1124
|
const deck = import_node_path12.default.basename(import_node_path12.default.dirname(full));
|
|
1100
1125
|
for (const file of await collectMarkdownFiles(full)) {
|
|
1101
|
-
cards.push(
|
|
1126
|
+
cards.push(
|
|
1127
|
+
...parseFlashcards(
|
|
1128
|
+
await import_fs_extra9.default.readFile(file, "utf8"),
|
|
1129
|
+
deck
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1102
1132
|
}
|
|
1103
1133
|
} else {
|
|
1104
1134
|
stack.push(full);
|
|
@@ -1173,22 +1203,26 @@ async function createNote(vault, input5) {
|
|
|
1173
1203
|
|
|
1174
1204
|
// src/services/ai.ts
|
|
1175
1205
|
var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
1176
|
-
var
|
|
1206
|
+
var ANTHROPIC_MODEL = "claude-opus-4-8";
|
|
1207
|
+
var OLLAMA_DEFAULT_MODEL = "llama3.2";
|
|
1208
|
+
var OLLAMA_DEFAULT_URL = "http://localhost:11434";
|
|
1209
|
+
var SYSTEM_PROMPT = "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.";
|
|
1177
1210
|
function anthropicProvider() {
|
|
1178
1211
|
if (!process.env["ANTHROPIC_API_KEY"]) {
|
|
1179
1212
|
throw new Error(
|
|
1180
|
-
'Defina a vari\xE1vel de ambiente ANTHROPIC_API_KEY para usar
|
|
1213
|
+
'Defina a vari\xE1vel de ambiente ANTHROPIC_API_KEY para usar o provedor Anthropic.\n Ex.: export ANTHROPIC_API_KEY="sk-ant-..."'
|
|
1181
1214
|
);
|
|
1182
1215
|
}
|
|
1183
1216
|
const client = new import_sdk.default();
|
|
1184
1217
|
return {
|
|
1218
|
+
name: `anthropic (${ANTHROPIC_MODEL})`,
|
|
1185
1219
|
async generate(prompt, material) {
|
|
1186
1220
|
try {
|
|
1187
1221
|
const stream = client.messages.stream({
|
|
1188
|
-
model:
|
|
1222
|
+
model: ANTHROPIC_MODEL,
|
|
1189
1223
|
max_tokens: 64e3,
|
|
1190
1224
|
thinking: { type: "adaptive" },
|
|
1191
|
-
system:
|
|
1225
|
+
system: SYSTEM_PROMPT,
|
|
1192
1226
|
messages: [
|
|
1193
1227
|
{
|
|
1194
1228
|
role: "user",
|
|
@@ -1204,7 +1238,9 @@ ${material}
|
|
|
1204
1238
|
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
1205
1239
|
} catch (error) {
|
|
1206
1240
|
if (error instanceof import_sdk.default.AuthenticationError) {
|
|
1207
|
-
throw new Error(
|
|
1241
|
+
throw new Error(
|
|
1242
|
+
"ANTHROPIC_API_KEY inv\xE1lida. Verifique a chave."
|
|
1243
|
+
);
|
|
1208
1244
|
}
|
|
1209
1245
|
if (error instanceof import_sdk.default.RateLimitError) {
|
|
1210
1246
|
throw new Error(
|
|
@@ -1221,6 +1257,98 @@ ${material}
|
|
|
1221
1257
|
}
|
|
1222
1258
|
};
|
|
1223
1259
|
}
|
|
1260
|
+
function ollamaProvider() {
|
|
1261
|
+
const baseUrl = (process.env["OLLAMA_URL"] ?? OLLAMA_DEFAULT_URL).replace(
|
|
1262
|
+
/\/$/,
|
|
1263
|
+
""
|
|
1264
|
+
);
|
|
1265
|
+
const model = process.env["OLLAMA_MODEL"] ?? OLLAMA_DEFAULT_MODEL;
|
|
1266
|
+
const headers = {
|
|
1267
|
+
"content-type": "application/json"
|
|
1268
|
+
};
|
|
1269
|
+
if (process.env["OLLAMA_API_KEY"]) {
|
|
1270
|
+
headers["authorization"] = `Bearer ${process.env["OLLAMA_API_KEY"]}`;
|
|
1271
|
+
}
|
|
1272
|
+
return {
|
|
1273
|
+
name: `ollama (${model} @ ${baseUrl})`,
|
|
1274
|
+
async generate(prompt, material) {
|
|
1275
|
+
let response;
|
|
1276
|
+
try {
|
|
1277
|
+
response = await fetch(`${baseUrl}/api/chat`, {
|
|
1278
|
+
method: "POST",
|
|
1279
|
+
headers,
|
|
1280
|
+
body: JSON.stringify({
|
|
1281
|
+
model,
|
|
1282
|
+
stream: false,
|
|
1283
|
+
messages: [
|
|
1284
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
1285
|
+
{
|
|
1286
|
+
role: "user",
|
|
1287
|
+
content: `${prompt}
|
|
1288
|
+
|
|
1289
|
+
<material>
|
|
1290
|
+
${material}
|
|
1291
|
+
</material>`
|
|
1292
|
+
}
|
|
1293
|
+
]
|
|
1294
|
+
})
|
|
1295
|
+
});
|
|
1296
|
+
} catch (cause) {
|
|
1297
|
+
throw new Error(
|
|
1298
|
+
`N\xE3o consegui falar com o Ollama em ${baseUrl}.
|
|
1299
|
+
Local: instale com "curl -fsSL https://ollama.com/install.sh | sh" e rode "ollama serve".
|
|
1300
|
+
VPS: defina OLLAMA_URL apontando para o seu servidor.`,
|
|
1301
|
+
{ cause }
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (response.status === 404) {
|
|
1305
|
+
throw new Error(
|
|
1306
|
+
`O modelo "${model}" n\xE3o est\xE1 instalado no Ollama. Rode: ollama pull ${model}`
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
if (!response.ok) {
|
|
1310
|
+
throw new Error(
|
|
1311
|
+
`Ollama respondeu HTTP ${response.status}: ${await response.text()}`
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
const data = await response.json();
|
|
1315
|
+
const content = data.message?.content;
|
|
1316
|
+
if (!content) {
|
|
1317
|
+
throw new Error("O Ollama retornou uma resposta vazia.");
|
|
1318
|
+
}
|
|
1319
|
+
return content;
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
async function ollamaDisponivel() {
|
|
1324
|
+
const baseUrl = (process.env["OLLAMA_URL"] ?? OLLAMA_DEFAULT_URL).replace(
|
|
1325
|
+
/\/$/,
|
|
1326
|
+
""
|
|
1327
|
+
);
|
|
1328
|
+
try {
|
|
1329
|
+
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
1330
|
+
signal: AbortSignal.timeout(2e3)
|
|
1331
|
+
});
|
|
1332
|
+
return response.ok;
|
|
1333
|
+
} catch {
|
|
1334
|
+
return false;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
async function resolveProvider() {
|
|
1338
|
+
const forced = process.env["MINERVA_AI"]?.toLowerCase();
|
|
1339
|
+
if (forced === "anthropic") return anthropicProvider();
|
|
1340
|
+
if (forced === "ollama") return ollamaProvider();
|
|
1341
|
+
if (forced) {
|
|
1342
|
+
throw new Error(
|
|
1343
|
+
`Provedor desconhecido em MINERVA_AI: "${forced}". Use "anthropic" ou "ollama".`
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
if (process.env["ANTHROPIC_API_KEY"]) return anthropicProvider();
|
|
1347
|
+
if (await ollamaDisponivel()) return ollamaProvider();
|
|
1348
|
+
throw new Error(
|
|
1349
|
+
'Nenhum provedor de IA dispon\xEDvel. Escolha um:\n \u2022 Anthropic (pago): export ANTHROPIC_API_KEY="sk-ant-..."\n \u2022 Ollama (gr\xE1tis, local): instale em https://ollama.com e rode "ollama pull llama3.2"\n \u2022 Ollama (gr\xE1tis, na sua VPS): export OLLAMA_URL="http://SEU-IP:11434"'
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1224
1352
|
function resumoPrompt(disciplina, tema) {
|
|
1225
1353
|
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
1354
|
}
|
|
@@ -1245,7 +1373,9 @@ var DATE_HINT = "Use uma data v\xE1lida no formato YYYY-MM-DD.";
|
|
|
1245
1373
|
async function readMaterial(dir) {
|
|
1246
1374
|
if (!await import_fs_extra11.default.pathExists(dir)) return "";
|
|
1247
1375
|
const parts = [];
|
|
1248
|
-
for (const file of (await import_fs_extra11.default.readdir(dir)).filter(
|
|
1376
|
+
for (const file of (await import_fs_extra11.default.readdir(dir)).filter(
|
|
1377
|
+
(f) => f.endsWith(".md")
|
|
1378
|
+
)) {
|
|
1249
1379
|
parts.push(await import_fs_extra11.default.readFile(import_node_path14.default.join(dir, file), "utf8"));
|
|
1250
1380
|
}
|
|
1251
1381
|
return parts.join("\n\n---\n\n");
|
|
@@ -1261,7 +1391,7 @@ function noteCommand(spec) {
|
|
|
1261
1391
|
if (spec.ai) {
|
|
1262
1392
|
command.option(
|
|
1263
1393
|
"--ai",
|
|
1264
|
-
`gera o conte\xFAdo com IA a partir de ${spec.ai.sourceFolder}/ (
|
|
1394
|
+
`gera o conte\xFAdo com IA a partir de ${spec.ai.sourceFolder}/ (Anthropic ou Ollama)`
|
|
1265
1395
|
);
|
|
1266
1396
|
}
|
|
1267
1397
|
command.action(
|
|
@@ -1295,13 +1425,13 @@ function noteCommand(spec) {
|
|
|
1295
1425
|
}
|
|
1296
1426
|
let conteudoIA;
|
|
1297
1427
|
if (spec.ai && options["ai"]) {
|
|
1298
|
-
const provider =
|
|
1428
|
+
const provider = await resolveProvider();
|
|
1299
1429
|
const sourceDir = import_node_path14.default.join(
|
|
1300
1430
|
disciplinaDir(vault.root, semestre, disciplina),
|
|
1301
1431
|
spec.ai.sourceFolder
|
|
1302
1432
|
);
|
|
1303
1433
|
const spinAi = spinner(
|
|
1304
|
-
`Gerando conte\xFAdo com IA
|
|
1434
|
+
`Gerando conte\xFAdo com IA a partir de ${spec.ai.sourceFolder}/...`
|
|
1305
1435
|
).start();
|
|
1306
1436
|
try {
|
|
1307
1437
|
const material = await readMaterial(sourceDir);
|
|
@@ -1325,11 +1455,14 @@ function noteCommand(spec) {
|
|
|
1325
1455
|
extra
|
|
1326
1456
|
});
|
|
1327
1457
|
if (conteudoIA) {
|
|
1328
|
-
await import_fs_extra11.default.appendFile(
|
|
1458
|
+
await import_fs_extra11.default.appendFile(
|
|
1459
|
+
file,
|
|
1460
|
+
`
|
|
1329
1461
|
## Gerado por IA
|
|
1330
1462
|
|
|
1331
1463
|
${conteudoIA}
|
|
1332
|
-
`
|
|
1464
|
+
`
|
|
1465
|
+
);
|
|
1333
1466
|
}
|
|
1334
1467
|
spin.succeed(`Pronto! Nota criada em:`);
|
|
1335
1468
|
logger.item(import_node_path14.default.relative(vault.root, file));
|
|
@@ -1423,14 +1556,18 @@ function flashcardsCommand() {
|
|
|
1423
1556
|
);
|
|
1424
1557
|
const create = flashcardsNewCommand();
|
|
1425
1558
|
command.addCommand(create, { isDefault: true });
|
|
1426
|
-
command.command("export").description(
|
|
1559
|
+
command.command("export").description(
|
|
1560
|
+
"Exporta todos os flashcards para um TSV import\xE1vel no Anki"
|
|
1561
|
+
).action(async () => {
|
|
1427
1562
|
const vault = await requireVault();
|
|
1428
1563
|
const spin = spinner("Coletando flashcards do vault...").start();
|
|
1429
1564
|
try {
|
|
1430
1565
|
const { file, total } = await exportAnki(vault);
|
|
1431
1566
|
spin.succeed(`${total} flashcard(s) exportado(s)!`);
|
|
1432
1567
|
logger.item(import_node_path15.default.relative(vault.root, file));
|
|
1433
|
-
logger.info(
|
|
1568
|
+
logger.info(
|
|
1569
|
+
"No Anki: Arquivo \u2192 Importar e selecione esse arquivo."
|
|
1570
|
+
);
|
|
1434
1571
|
} catch (error) {
|
|
1435
1572
|
spin.fail("Falha na exporta\xE7\xE3o.");
|
|
1436
1573
|
throw error;
|
|
@@ -1481,7 +1618,10 @@ function initCommand() {
|
|
|
1481
1618
|
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
1619
|
async (pasta, options) => {
|
|
1483
1620
|
logger.title("Minerva CLI \u2014 vamos montar sua gradua\xE7\xE3o!");
|
|
1484
|
-
const aluno = options.aluno ?? await (0, import_prompts8.input)({
|
|
1621
|
+
const aluno = options.aluno ?? await (0, import_prompts8.input)({
|
|
1622
|
+
message: "Seu nome:",
|
|
1623
|
+
default: "Estudante"
|
|
1624
|
+
});
|
|
1485
1625
|
const semestreAtual = options.semestre !== void 0 ? Number(options.semestre) : await (0, import_prompts8.number)({
|
|
1486
1626
|
message: "Em qual semestre voc\xEA est\xE1? (1-6)",
|
|
1487
1627
|
default: 1,
|
|
@@ -1506,7 +1646,10 @@ function initCommand() {
|
|
|
1506
1646
|
spin.text = "Copiando templates...";
|
|
1507
1647
|
await scaffoldTemplates(root);
|
|
1508
1648
|
spin.text = "Criando semestres e disciplinas...";
|
|
1509
|
-
const totalDisciplinas = await scaffoldSemestres(
|
|
1649
|
+
const totalDisciplinas = await scaffoldSemestres(
|
|
1650
|
+
root,
|
|
1651
|
+
matrizOficial
|
|
1652
|
+
);
|
|
1510
1653
|
spin.text = "Preparando vault do Obsidian...";
|
|
1511
1654
|
await scaffoldObsidian(root);
|
|
1512
1655
|
spin.text = "Gerando README e dashboard...";
|
|
@@ -1519,7 +1662,10 @@ function initCommand() {
|
|
|
1519
1662
|
semestres: matrizOficial.semestres
|
|
1520
1663
|
})
|
|
1521
1664
|
);
|
|
1522
|
-
await writeIfMissing(
|
|
1665
|
+
await writeIfMissing(
|
|
1666
|
+
import_node_path16.default.join(root, ".gitignore"),
|
|
1667
|
+
GITIGNORE
|
|
1668
|
+
);
|
|
1523
1669
|
await writeIfMissing(
|
|
1524
1670
|
import_node_path16.default.join(root, "roadmap", "Roadmap.md"),
|
|
1525
1671
|
ROADMAP
|
|
@@ -1530,14 +1676,20 @@ function initCommand() {
|
|
|
1530
1676
|
spin.succeed("Vault criado com sucesso!");
|
|
1531
1677
|
logger.blank();
|
|
1532
1678
|
logger.success(`\u{1F4C1} ${root}`);
|
|
1533
|
-
logger.item(
|
|
1679
|
+
logger.item(
|
|
1680
|
+
`${totalDisciplinas} disciplinas em 6 semestres`
|
|
1681
|
+
);
|
|
1534
1682
|
logger.item("Dashboard em dashboard/Dashboard.md");
|
|
1535
1683
|
logger.item("Abra a pasta como vault no Obsidian \u{1F7E3}");
|
|
1536
1684
|
if (!gitOk) {
|
|
1537
|
-
logger.warn(
|
|
1685
|
+
logger.warn(
|
|
1686
|
+
"Git n\xE3o encontrado \u2014 reposit\xF3rio n\xE3o inicializado."
|
|
1687
|
+
);
|
|
1538
1688
|
}
|
|
1539
1689
|
logger.blank();
|
|
1540
|
-
logger.info(
|
|
1690
|
+
logger.info(
|
|
1691
|
+
`Pr\xF3ximo passo: cd ${pasta} && minerva dashboard`
|
|
1692
|
+
);
|
|
1541
1693
|
} catch (error) {
|
|
1542
1694
|
spin.fail("Falha ao criar o vault.");
|
|
1543
1695
|
throw error;
|
|
@@ -1632,7 +1784,9 @@ function notaCommand() {
|
|
|
1632
1784
|
`${linha.disciplina}: m\xE9dia ${import_chalk2.default.bold(linha.media)} \u2014 ${color(linha.situacao)}`
|
|
1633
1785
|
);
|
|
1634
1786
|
for (const nota of linha.notas) {
|
|
1635
|
-
logger.item(
|
|
1787
|
+
logger.item(
|
|
1788
|
+
`${nota.avaliacao}: ${nota.valor} (peso ${nota.peso})`
|
|
1789
|
+
);
|
|
1636
1790
|
}
|
|
1637
1791
|
}
|
|
1638
1792
|
});
|
|
@@ -1666,7 +1820,9 @@ function notaCommand() {
|
|
|
1666
1820
|
peso: Number(options.peso)
|
|
1667
1821
|
});
|
|
1668
1822
|
const notas = await loadNotas(vault.root);
|
|
1669
|
-
const linha = boletim(notas).find(
|
|
1823
|
+
const linha = boletim(notas).find(
|
|
1824
|
+
(b) => b.disciplina === disciplina
|
|
1825
|
+
);
|
|
1670
1826
|
logger.success(
|
|
1671
1827
|
`${avaliacao} = ${valorFinal} registrada em ${disciplina}.`
|
|
1672
1828
|
);
|
|
@@ -1741,7 +1897,9 @@ async function listPendencias(vault, matriz) {
|
|
|
1741
1897
|
const stack = [dir];
|
|
1742
1898
|
while (stack.length > 0) {
|
|
1743
1899
|
const current = stack.pop();
|
|
1744
|
-
for (const entry of await import_fs_extra14.default.readdir(current, {
|
|
1900
|
+
for (const entry of await import_fs_extra14.default.readdir(current, {
|
|
1901
|
+
withFileTypes: true
|
|
1902
|
+
})) {
|
|
1745
1903
|
const full = import_node_path19.default.join(current, entry.name);
|
|
1746
1904
|
if (entry.isDirectory()) stack.push(full);
|
|
1747
1905
|
else if (entry.name.endsWith(".md")) {
|
|
@@ -1841,9 +1999,15 @@ function portfolioCommand() {
|
|
|
1841
1999
|
),
|
|
1842
2000
|
""
|
|
1843
2001
|
];
|
|
1844
|
-
const destination = import_node_path20.default.join(
|
|
2002
|
+
const destination = import_node_path20.default.join(
|
|
2003
|
+
vault.root,
|
|
2004
|
+
"portfolio",
|
|
2005
|
+
"Portfolio.md"
|
|
2006
|
+
);
|
|
1845
2007
|
await import_fs_extra15.default.outputFile(destination, lines.join("\n"), "utf8");
|
|
1846
|
-
spin.succeed(
|
|
2008
|
+
spin.succeed(
|
|
2009
|
+
`Portf\xF3lio atualizado com ${items.length} projeto(s)!`
|
|
2010
|
+
);
|
|
1847
2011
|
logger.item(import_node_path20.default.relative(vault.root, destination));
|
|
1848
2012
|
} catch (error) {
|
|
1849
2013
|
spin.fail("Falha ao gerar o portf\xF3lio.");
|
|
@@ -1893,7 +2057,10 @@ async function proximosCompromissos(vault, matriz, dias = 7) {
|
|
|
1893
2057
|
compromissos.push({
|
|
1894
2058
|
tipo: spec.tipo,
|
|
1895
2059
|
disciplina: disciplina.nome,
|
|
1896
|
-
titulo: extractTitle(
|
|
2060
|
+
titulo: extractTitle(
|
|
2061
|
+
content,
|
|
2062
|
+
file.replace(/\.md$/, "")
|
|
2063
|
+
),
|
|
1897
2064
|
data,
|
|
1898
2065
|
arquivo: import_node_path21.default.relative(vault.root, full)
|
|
1899
2066
|
});
|
|
@@ -1950,7 +2117,9 @@ function semestreCommand() {
|
|
|
1950
2117
|
if (numero === void 0) {
|
|
1951
2118
|
const atual = getSemestre(matriz, vault.config.semestreAtual);
|
|
1952
2119
|
logger.title(`${atual.numero}\xBA semestre`);
|
|
1953
|
-
logger.info(
|
|
2120
|
+
logger.info(
|
|
2121
|
+
`Progresso do curso: ${progressBar(atual.numero - 1, 6)}`
|
|
2122
|
+
);
|
|
1954
2123
|
for (const d of atual.disciplinas) {
|
|
1955
2124
|
logger.item(`${d.nome} (${d.aulas} aulas)`);
|
|
1956
2125
|
}
|
|
@@ -1979,8 +2148,11 @@ async function collectDates(root) {
|
|
|
1979
2148
|
const stack = [root];
|
|
1980
2149
|
while (stack.length > 0) {
|
|
1981
2150
|
const current = stack.pop();
|
|
1982
|
-
for (const entry of await import_fs_extra17.default.readdir(current, {
|
|
1983
|
-
|
|
2151
|
+
for (const entry of await import_fs_extra17.default.readdir(current, {
|
|
2152
|
+
withFileTypes: true
|
|
2153
|
+
})) {
|
|
2154
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
2155
|
+
continue;
|
|
1984
2156
|
const full = import_node_path22.default.join(current, entry.name);
|
|
1985
2157
|
if (entry.isDirectory()) stack.push(full);
|
|
1986
2158
|
else {
|
|
@@ -2036,7 +2208,9 @@ function statsCommand() {
|
|
|
2036
2208
|
return;
|
|
2037
2209
|
}
|
|
2038
2210
|
logger.title("Suas estat\xEDsticas de estudo");
|
|
2039
|
-
logger.info(
|
|
2211
|
+
logger.info(
|
|
2212
|
+
`\u{1F525} Streak: ${import_chalk4.default.bold(stats.streak)} dia(s) seguidos`
|
|
2213
|
+
);
|
|
2040
2214
|
logger.info(`\u23F1\uFE0F Horas estudadas: ${stats.horasEstudadas}h`);
|
|
2041
2215
|
logger.info(`\u{1F4DD} Notas criadas: ${stats.totalNotas}`);
|
|
2042
2216
|
logger.blank();
|