@tostudy-ai/cli 0.17.4 → 0.17.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +362 -140
- package/dist/cli.js.map +4 -4
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1496,6 +1496,11 @@ var init_slug = __esm({
|
|
|
1496
1496
|
function resolveWorkspaceTokensInText(text, paths) {
|
|
1497
1497
|
return text.replace(WORKSPACE_TOKEN, () => paths.workspace).replace(VAULT_TOKEN, () => paths.vault);
|
|
1498
1498
|
}
|
|
1499
|
+
function isPlainObject(value) {
|
|
1500
|
+
if (value === null || typeof value !== "object") return false;
|
|
1501
|
+
const proto = Object.getPrototypeOf(value);
|
|
1502
|
+
return proto === Object.prototype || proto === null;
|
|
1503
|
+
}
|
|
1499
1504
|
function resolveWorkspaceTokens(value, paths) {
|
|
1500
1505
|
if (typeof value === "string") {
|
|
1501
1506
|
return resolveWorkspaceTokensInText(value, paths);
|
|
@@ -1503,7 +1508,7 @@ function resolveWorkspaceTokens(value, paths) {
|
|
|
1503
1508
|
if (Array.isArray(value)) {
|
|
1504
1509
|
return value.map((item) => resolveWorkspaceTokens(item, paths));
|
|
1505
1510
|
}
|
|
1506
|
-
if (value
|
|
1511
|
+
if (isPlainObject(value)) {
|
|
1507
1512
|
const out = {};
|
|
1508
1513
|
for (const [key, item] of Object.entries(value)) {
|
|
1509
1514
|
out[key] = resolveWorkspaceTokens(item, paths);
|
|
@@ -2281,12 +2286,15 @@ var runtime_registry_exports = {};
|
|
|
2281
2286
|
__export(runtime_registry_exports, {
|
|
2282
2287
|
RUNTIMES: () => RUNTIMES,
|
|
2283
2288
|
detectedSlashHints: () => detectedSlashHints,
|
|
2289
|
+
isTrackedByGit: () => isTrackedByGit,
|
|
2284
2290
|
needsRootAgentsConsent: () => needsRootAgentsConsent,
|
|
2285
|
-
|
|
2291
|
+
parseTemplateVersion: () => parseTemplateVersion,
|
|
2292
|
+
upsertRootAgentsBlock: () => upsertRootAgentsBlock,
|
|
2293
|
+
writeProjectUniversalCommand: () => writeProjectUniversalCommand
|
|
2286
2294
|
});
|
|
2287
2295
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
2288
2296
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2289
|
-
import { join as join2 } from "node:path";
|
|
2297
|
+
import { dirname, join as join2 } from "node:path";
|
|
2290
2298
|
function hasBinary(name) {
|
|
2291
2299
|
try {
|
|
2292
2300
|
execFileSync2("which", [name], { encoding: "utf-8", stdio: "pipe" });
|
|
@@ -2299,6 +2307,42 @@ function writeFile2(absDir, fileName, body) {
|
|
|
2299
2307
|
mkdirSync2(absDir, { recursive: true });
|
|
2300
2308
|
writeFileSync2(join2(absDir, fileName), body);
|
|
2301
2309
|
}
|
|
2310
|
+
function parseTemplateVersion(text) {
|
|
2311
|
+
const match = text.match(/tostudy-template-version:\s*(\d+)/);
|
|
2312
|
+
if (!match || !match[1]) return null;
|
|
2313
|
+
const val = Number.parseInt(match[1], 10);
|
|
2314
|
+
return Number.isNaN(val) ? null : val;
|
|
2315
|
+
}
|
|
2316
|
+
function isTrackedByGit(cwd, relativePath) {
|
|
2317
|
+
try {
|
|
2318
|
+
execFileSync2("git", ["ls-files", "--error-unmatch", "--", relativePath], {
|
|
2319
|
+
cwd,
|
|
2320
|
+
stdio: "pipe"
|
|
2321
|
+
});
|
|
2322
|
+
return true;
|
|
2323
|
+
} catch {
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
function writeProjectUniversalCommand(cwd, relativePath, body, onKept) {
|
|
2328
|
+
const absPath = join2(cwd, relativePath);
|
|
2329
|
+
if (!existsSync2(absPath)) {
|
|
2330
|
+
mkdirSync2(dirname(absPath), { recursive: true });
|
|
2331
|
+
writeFileSync2(absPath, body);
|
|
2332
|
+
return [relativePath];
|
|
2333
|
+
}
|
|
2334
|
+
const existing = readFileSync(absPath, "utf-8");
|
|
2335
|
+
if (existing === body) {
|
|
2336
|
+
return [relativePath];
|
|
2337
|
+
}
|
|
2338
|
+
const version2 = parseTemplateVersion(existing);
|
|
2339
|
+
if (!isTrackedByGit(cwd, relativePath) && version2 !== null && version2 < UNIVERSAL_TEMPLATE_VERSION) {
|
|
2340
|
+
writeFileSync2(absPath, body);
|
|
2341
|
+
return [relativePath];
|
|
2342
|
+
}
|
|
2343
|
+
onKept?.(relativePath, version2, UNIVERSAL_TEMPLATE_VERSION);
|
|
2344
|
+
return [];
|
|
2345
|
+
}
|
|
2302
2346
|
function cursorMdc(title, content) {
|
|
2303
2347
|
return `---
|
|
2304
2348
|
description: ${title} \u2014 ToStudy Course Guide
|
|
@@ -2380,6 +2424,7 @@ var init_runtime_registry = __esm({
|
|
|
2380
2424
|
"use strict";
|
|
2381
2425
|
init_dist();
|
|
2382
2426
|
init_slug();
|
|
2427
|
+
init_instruction_template_v3();
|
|
2383
2428
|
logger2 = createLogger("cli:runtime-registry");
|
|
2384
2429
|
detectOpencode = (home) => existsSync2(join2(home, ".opencode")) || existsSync2(join2(home, ".config", "opencode"));
|
|
2385
2430
|
detectGrok = (home) => existsSync2(join2(home, ".grok")) || hasBinary("grok");
|
|
@@ -2393,9 +2438,8 @@ var init_runtime_registry = __esm({
|
|
|
2393
2438
|
writeFile2(join2(cwd, ".claude", "commands"), `tostudy-${slug}.md`, content);
|
|
2394
2439
|
return [`.claude/commands/tostudy-${slug}.md`];
|
|
2395
2440
|
},
|
|
2396
|
-
writeUniversal({ cwd, content }) {
|
|
2397
|
-
|
|
2398
|
-
return [".claude/commands/tostudy.md"];
|
|
2441
|
+
writeUniversal({ cwd, content, onKept }) {
|
|
2442
|
+
return writeProjectUniversalCommand(cwd, ".claude/commands/tostudy.md", content, onKept);
|
|
2399
2443
|
},
|
|
2400
2444
|
slashHint: (slug) => `No Claude Code, digite: /tostudy-${slug}`
|
|
2401
2445
|
},
|
|
@@ -2412,9 +2456,13 @@ var init_runtime_registry = __esm({
|
|
|
2412
2456
|
);
|
|
2413
2457
|
return [`.cursor/rules/tostudy-${slug}.mdc`];
|
|
2414
2458
|
},
|
|
2415
|
-
writeUniversal({ cwd, content }) {
|
|
2416
|
-
|
|
2417
|
-
|
|
2459
|
+
writeUniversal({ cwd, content, onKept }) {
|
|
2460
|
+
return writeProjectUniversalCommand(
|
|
2461
|
+
cwd,
|
|
2462
|
+
".cursor/rules/tostudy.mdc",
|
|
2463
|
+
cursorMdc("ToStudy", content),
|
|
2464
|
+
onKept
|
|
2465
|
+
);
|
|
2418
2466
|
}
|
|
2419
2467
|
},
|
|
2420
2468
|
{
|
|
@@ -2488,17 +2536,17 @@ ${content}`;
|
|
|
2488
2536
|
);
|
|
2489
2537
|
return [`.agents/skills/tostudy-${slug}/SKILL.md`];
|
|
2490
2538
|
},
|
|
2491
|
-
writeUniversal({ cwd, content }) {
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
"SKILL.md",
|
|
2539
|
+
writeUniversal({ cwd, content, onKept }) {
|
|
2540
|
+
return writeProjectUniversalCommand(
|
|
2541
|
+
cwd,
|
|
2542
|
+
".agents/skills/tostudy/SKILL.md",
|
|
2495
2543
|
antigravitySkillMd(
|
|
2496
2544
|
"tostudy",
|
|
2497
2545
|
"ToStudy \u2014 AI Tutor Guide. Use when the student asks to study or runs tostudy commands.",
|
|
2498
2546
|
content
|
|
2499
|
-
)
|
|
2547
|
+
),
|
|
2548
|
+
onKept
|
|
2500
2549
|
);
|
|
2501
|
-
return [".agents/skills/tostudy/SKILL.md"];
|
|
2502
2550
|
},
|
|
2503
2551
|
slashHint: (slug) => `No Antigravity, a skill tostudy-${slug} ativa automaticamente`
|
|
2504
2552
|
},
|
|
@@ -2515,9 +2563,8 @@ ${content}`;
|
|
|
2515
2563
|
if (rootAgents) written.push(rootAgents);
|
|
2516
2564
|
return written;
|
|
2517
2565
|
},
|
|
2518
|
-
writeUniversal({ cwd, content }) {
|
|
2519
|
-
|
|
2520
|
-
return [".tostudy/AGENTS.md"];
|
|
2566
|
+
writeUniversal({ cwd, content, onKept }) {
|
|
2567
|
+
return writeProjectUniversalCommand(cwd, ".tostudy/AGENTS.md", content, onKept);
|
|
2521
2568
|
}
|
|
2522
2569
|
}
|
|
2523
2570
|
];
|
|
@@ -2532,6 +2579,7 @@ var instruction_files_exports = {};
|
|
|
2532
2579
|
__export(instruction_files_exports, {
|
|
2533
2580
|
installUniversalCommand: () => installUniversalCommand,
|
|
2534
2581
|
needsRootAgentsConsent: () => needsRootAgentsConsent,
|
|
2582
|
+
parseTemplateVersion: () => parseTemplateVersion,
|
|
2535
2583
|
slugify: () => slugify,
|
|
2536
2584
|
upsertRootAgentsBlock: () => upsertRootAgentsBlock,
|
|
2537
2585
|
writeInstructionFiles: () => writeInstructionFiles
|
|
@@ -2548,11 +2596,11 @@ function writeInstructionFiles(cwd, ctx, content, opts = {}) {
|
|
|
2548
2596
|
logger3.info("Generated instruction files (v3)", { slug, files: written });
|
|
2549
2597
|
return written;
|
|
2550
2598
|
}
|
|
2551
|
-
function installUniversalCommand(platform2, cwd = process.cwd(), homeDir = homedir()) {
|
|
2599
|
+
function installUniversalCommand(platform2, cwd = process.cwd(), homeDir = homedir(), onKept) {
|
|
2552
2600
|
const entry = RUNTIMES.find((r) => r.id === platform2);
|
|
2553
2601
|
if (!entry?.writeUniversal) return [];
|
|
2554
2602
|
const content = renderUniversalInstruction();
|
|
2555
|
-
const written = entry.writeUniversal({ cwd, home: homeDir, content });
|
|
2603
|
+
const written = entry.writeUniversal({ cwd, home: homeDir, content, onKept });
|
|
2556
2604
|
logger3.info("Installed universal /tostudy command", { platform: platform2, files: written });
|
|
2557
2605
|
return written;
|
|
2558
2606
|
}
|
|
@@ -2744,9 +2792,13 @@ var init_login = __esm({
|
|
|
2744
2792
|
}
|
|
2745
2793
|
const cwd = process.cwd();
|
|
2746
2794
|
const home = homedir2();
|
|
2795
|
+
const keptFiles = [];
|
|
2796
|
+
const onKept = (file2, foundVersion, availableVersion) => {
|
|
2797
|
+
keptFiles.push({ file: file2, foundVersion, availableVersion });
|
|
2798
|
+
};
|
|
2747
2799
|
for (const runtime of RUNTIMES2) {
|
|
2748
2800
|
if (runtime.writeUniversal && runtime.detect(home, cwd)) {
|
|
2749
|
-
installedFiles.push(...installUniversalCommand2(runtime.id, cwd));
|
|
2801
|
+
installedFiles.push(...installUniversalCommand2(runtime.id, cwd, home, onKept));
|
|
2750
2802
|
}
|
|
2751
2803
|
}
|
|
2752
2804
|
if (installedFiles.length > 0) {
|
|
@@ -2755,6 +2807,16 @@ var init_login = __esm({
|
|
|
2755
2807
|
console.log(` \u2713 Instalado: ${file2}`);
|
|
2756
2808
|
}
|
|
2757
2809
|
}
|
|
2810
|
+
if (keptFiles.length > 0) {
|
|
2811
|
+
if (installedFiles.length === 0) {
|
|
2812
|
+
console.log("");
|
|
2813
|
+
}
|
|
2814
|
+
for (const { file: file2, foundVersion, availableVersion } of keptFiles) {
|
|
2815
|
+
console.log(
|
|
2816
|
+
` \u26A0 Mantido: ${file2} (template v${foundVersion ?? "?"} -> v${availableVersion} dispon\xEDvel -- apague o arquivo e rode tostudy login para atualizar)`
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2758
2820
|
} catch {
|
|
2759
2821
|
}
|
|
2760
2822
|
try {
|
|
@@ -2866,6 +2928,7 @@ var init_errors_pt_br = __esm({
|
|
|
2866
2928
|
workspaceCommandDescription: "Gerenciar workspace de estudo local",
|
|
2867
2929
|
workspaceSetupDescription: "Criar estrutura do workspace para o curso ativo",
|
|
2868
2930
|
insufficientCredits: "\u274C Voc\xEA est\xE1 sem cr\xE9ditos. Cada resposta do tutor de IA consome cr\xE9ditos para cobrir o processamento. Recarregue em https://tostudy.ai/student/credits para continuar de onde parou.\n",
|
|
2931
|
+
dailyAiLimit: "\u274C Voc\xEA atingiu o teto di\xE1rio de gastos com IA da sua conta. N\xE3o \xE9 falta de cr\xE9ditos: o teto reinicia \xE0 meia-noite (UTC). Para um teto maior, fa\xE7a upgrade do plano em https://tostudy.ai/student/plan.\n",
|
|
2869
2932
|
workspaceRefusedAtHome: "N\xE3o d\xE1 para configurar o estudo direto na sua pasta pessoal.\nCrie uma pasta para o curso e entre nela antes de continuar:\n\n mkdir ~/meus-cursos && cd ~/meus-cursos\n",
|
|
2870
2933
|
courseArchived: "Este curso foi arquivado e est\xE1 dispon\xEDvel somente para leitura.\nUse `tostudy lesson` para revisar o conte\xFAdo j\xE1 estudado.\n",
|
|
2871
2934
|
noActiveCourse: "Nenhum curso ativo nesta pasta.\nVeja seus cursos com `tostudy courses` e ative um com `tostudy select <n>`.\n",
|
|
@@ -2888,6 +2951,7 @@ var init_errors_en_us = __esm({
|
|
|
2888
2951
|
workspaceCommandDescription: "Manage local study workspace",
|
|
2889
2952
|
workspaceSetupDescription: "Create the workspace structure for the active course",
|
|
2890
2953
|
insufficientCredits: "\u274C You're out of credits. Every reply from the AI tutor uses credits to cover processing. Top up at https://tostudy.ai/student/credits to continue from where you stopped.\n",
|
|
2954
|
+
dailyAiLimit: "\u274C You have reached your account's daily AI spending limit. This is not an empty wallet: the limit resets at midnight (UTC). For a higher limit, upgrade your plan at https://tostudy.ai/student/plan.\n",
|
|
2891
2955
|
workspaceRefusedAtHome: "Study can't be set up directly in your home folder.\nCreate a folder for the course and switch into it first:\n\n mkdir ~/my-courses && cd ~/my-courses\n",
|
|
2892
2956
|
courseArchived: "This course has been archived and is available for reading only.\nUse `tostudy lesson` to review the content you've already studied.\n",
|
|
2893
2957
|
noActiveCourse: "No active course in this folder.\nList your courses with `tostudy courses` and activate one with `tostudy select <n>`.\n",
|
|
@@ -2934,6 +2998,9 @@ function resolveErrorCode(err) {
|
|
|
2934
2998
|
function isInsufficientCreditsError(err) {
|
|
2935
2999
|
return resolveErrorCode(err).includes("INSUFFICIENT_CREDITS");
|
|
2936
3000
|
}
|
|
3001
|
+
function isDailyAiLimitError(err) {
|
|
3002
|
+
return resolveErrorCode(err).includes("DAILY_AI_COST_LIMIT_EXCEEDED");
|
|
3003
|
+
}
|
|
2937
3004
|
function isCourseArchivedError(err) {
|
|
2938
3005
|
return resolveErrorCode(err).includes("COURSE_ARCHIVED");
|
|
2939
3006
|
}
|
|
@@ -3531,7 +3598,7 @@ var CLI_VERSION;
|
|
|
3531
3598
|
var init_version = __esm({
|
|
3532
3599
|
"src/version.ts"() {
|
|
3533
3600
|
"use strict";
|
|
3534
|
-
CLI_VERSION = true ? "0.17.
|
|
3601
|
+
CLI_VERSION = true ? "0.17.6" : "0.7.1";
|
|
3535
3602
|
}
|
|
3536
3603
|
});
|
|
3537
3604
|
|
|
@@ -3697,6 +3764,26 @@ var init_cache2 = __esm({
|
|
|
3697
3764
|
|
|
3698
3765
|
// src/commands/doctor.ts
|
|
3699
3766
|
import { Command as Command4 } from "commander";
|
|
3767
|
+
async function verifyToken(session, fetchImpl = fetch) {
|
|
3768
|
+
try {
|
|
3769
|
+
const res = await fetchImpl(`${session.apiUrl}/api/cli/courses`, {
|
|
3770
|
+
method: "GET",
|
|
3771
|
+
headers: {
|
|
3772
|
+
Authorization: `Bearer ${session.token}`
|
|
3773
|
+
},
|
|
3774
|
+
signal: AbortSignal.timeout(5e3)
|
|
3775
|
+
});
|
|
3776
|
+
if (res.ok) {
|
|
3777
|
+
return "valid";
|
|
3778
|
+
}
|
|
3779
|
+
if (res.status === 401 || res.status === 403) {
|
|
3780
|
+
return "rejected";
|
|
3781
|
+
}
|
|
3782
|
+
return "unverified";
|
|
3783
|
+
} catch {
|
|
3784
|
+
return "unverified";
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3700
3787
|
var doctorCommand;
|
|
3701
3788
|
var init_doctor = __esm({
|
|
3702
3789
|
"src/commands/doctor.ts"() {
|
|
@@ -3739,10 +3826,22 @@ var init_doctor = __esm({
|
|
|
3739
3826
|
session = await getSession();
|
|
3740
3827
|
} catch {
|
|
3741
3828
|
}
|
|
3829
|
+
let serverVerdict = null;
|
|
3830
|
+
if (session) {
|
|
3831
|
+
if (session.expiresAt && new Date(session.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
3832
|
+
serverVerdict = "expired";
|
|
3833
|
+
} else {
|
|
3834
|
+
serverVerdict = await verifyToken({
|
|
3835
|
+
apiUrl: session.apiUrl ?? "https://tostudy.ai",
|
|
3836
|
+
token: session.token
|
|
3837
|
+
});
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3742
3840
|
checks["auth"] = {
|
|
3743
3841
|
loggedIn: !!session,
|
|
3744
3842
|
userName: session?.userName ?? null,
|
|
3745
|
-
expiresAt: session?.expiresAt ?? null
|
|
3843
|
+
expiresAt: session?.expiresAt ?? null,
|
|
3844
|
+
serverVerdict
|
|
3746
3845
|
};
|
|
3747
3846
|
try {
|
|
3748
3847
|
const apiUrl = session?.apiUrl ?? "https://tostudy.ai";
|
|
@@ -3842,7 +3941,17 @@ var init_doctor = __esm({
|
|
|
3842
3941
|
console.log(` ${pnpmVersion ? "\u2713" : "\u25CB"} pnpm ${pnpmVersion ?? errs.notFound}`);
|
|
3843
3942
|
console.log(` ${gitVersion ? "\u2713" : "\u25CB"} git ${gitVersion ?? errs.notFound}`);
|
|
3844
3943
|
console.log("\n Autentica\xE7\xE3o");
|
|
3845
|
-
|
|
3944
|
+
if (!session) {
|
|
3945
|
+
console.log(" \u2717 Token n\xE3o logado");
|
|
3946
|
+
} else if (serverVerdict === "valid") {
|
|
3947
|
+
console.log(" \u2713 Token v\xE1lido");
|
|
3948
|
+
} else if (serverVerdict === "expired") {
|
|
3949
|
+
console.log(" \u25CB Token expirado (renova sozinho no pr\xF3ximo comando)");
|
|
3950
|
+
} else if (serverVerdict === "rejected") {
|
|
3951
|
+
console.log(" \u2717 Token recusado pelo servidor \u2014 rode: tostudy login");
|
|
3952
|
+
} else {
|
|
3953
|
+
console.log(" ? Token n\xE3o verificado (API indispon\xEDvel)");
|
|
3954
|
+
}
|
|
3846
3955
|
if (session) {
|
|
3847
3956
|
console.log(` \u2713 Usu\xE1rio ${session.userName}`);
|
|
3848
3957
|
}
|
|
@@ -4711,6 +4820,7 @@ async function runStart(opts, deps = defaultDeps3) {
|
|
|
4711
4820
|
if (ws)
|
|
4712
4821
|
await deps.updateWorkspaceState(ws.workspacePath, {
|
|
4713
4822
|
currentLessonId: moduleData.firstLesson.id,
|
|
4823
|
+
retryLessonId: void 0,
|
|
4714
4824
|
currentModuleId: moduleData.module.id
|
|
4715
4825
|
});
|
|
4716
4826
|
if (opts.json) {
|
|
@@ -4803,6 +4913,7 @@ var init_start_next = __esm({
|
|
|
4803
4913
|
if (ws)
|
|
4804
4914
|
await updateWorkspaceState(ws.workspacePath, {
|
|
4805
4915
|
currentLessonId: moduleData.firstLesson.id,
|
|
4916
|
+
retryLessonId: void 0,
|
|
4806
4917
|
currentModuleId: moduleData.module.id
|
|
4807
4918
|
});
|
|
4808
4919
|
if (opts.json) {
|
|
@@ -4863,6 +4974,7 @@ var init_next = __esm({
|
|
|
4863
4974
|
if (ws)
|
|
4864
4975
|
await updateWorkspaceState(ws.workspacePath, {
|
|
4865
4976
|
currentLessonId: lessonData.lesson.id,
|
|
4977
|
+
retryLessonId: void 0,
|
|
4866
4978
|
...lessonData.lesson.moduleId ? { currentModuleId: lessonData.lesson.moduleId } : {}
|
|
4867
4979
|
});
|
|
4868
4980
|
if (opts.json) {
|
|
@@ -5359,7 +5471,7 @@ __export(util_exports, {
|
|
|
5359
5471
|
getSizableOrigin: () => getSizableOrigin,
|
|
5360
5472
|
hexToUint8Array: () => hexToUint8Array,
|
|
5361
5473
|
isObject: () => isObject,
|
|
5362
|
-
isPlainObject: () =>
|
|
5474
|
+
isPlainObject: () => isPlainObject2,
|
|
5363
5475
|
issue: () => issue,
|
|
5364
5476
|
joinValues: () => joinValues,
|
|
5365
5477
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
@@ -5528,7 +5640,7 @@ function slugify2(input2) {
|
|
|
5528
5640
|
function isObject(data) {
|
|
5529
5641
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
5530
5642
|
}
|
|
5531
|
-
function
|
|
5643
|
+
function isPlainObject2(o) {
|
|
5532
5644
|
if (isObject(o) === false)
|
|
5533
5645
|
return false;
|
|
5534
5646
|
const ctor = o.constructor;
|
|
@@ -5545,7 +5657,7 @@ function isPlainObject(o) {
|
|
|
5545
5657
|
return true;
|
|
5546
5658
|
}
|
|
5547
5659
|
function shallowClone(o) {
|
|
5548
|
-
if (
|
|
5660
|
+
if (isPlainObject2(o))
|
|
5549
5661
|
return { ...o };
|
|
5550
5662
|
if (Array.isArray(o))
|
|
5551
5663
|
return [...o];
|
|
@@ -5681,7 +5793,7 @@ function omit(schema, mask) {
|
|
|
5681
5793
|
return clone(schema, def);
|
|
5682
5794
|
}
|
|
5683
5795
|
function extend(schema, shape) {
|
|
5684
|
-
if (!
|
|
5796
|
+
if (!isPlainObject2(shape)) {
|
|
5685
5797
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
5686
5798
|
}
|
|
5687
5799
|
const checks = schema._zod.def.checks;
|
|
@@ -5704,7 +5816,7 @@ function extend(schema, shape) {
|
|
|
5704
5816
|
return clone(schema, def);
|
|
5705
5817
|
}
|
|
5706
5818
|
function safeExtend(schema, shape) {
|
|
5707
|
-
if (!
|
|
5819
|
+
if (!isPlainObject2(shape)) {
|
|
5708
5820
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
5709
5821
|
}
|
|
5710
5822
|
const def = mergeDefs(schema._zod.def, {
|
|
@@ -7180,7 +7292,7 @@ function mergeValues(a, b) {
|
|
|
7180
7292
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
7181
7293
|
return { valid: true, data: a };
|
|
7182
7294
|
}
|
|
7183
|
-
if (
|
|
7295
|
+
if (isPlainObject2(a) && isPlainObject2(b)) {
|
|
7184
7296
|
const bKeys = Object.keys(b);
|
|
7185
7297
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
7186
7298
|
const newObj = { ...a, ...b };
|
|
@@ -8374,7 +8486,7 @@ var init_schemas = __esm({
|
|
|
8374
8486
|
$ZodType.init(inst, def);
|
|
8375
8487
|
inst._zod.parse = (payload, ctx) => {
|
|
8376
8488
|
const input2 = payload.value;
|
|
8377
|
-
if (!
|
|
8489
|
+
if (!isPlainObject2(input2)) {
|
|
8378
8490
|
payload.issues.push({
|
|
8379
8491
|
expected: "record",
|
|
8380
8492
|
code: "invalid_type",
|
|
@@ -20142,6 +20254,12 @@ var init_init_template = __esm({
|
|
|
20142
20254
|
import fs11 from "node:fs";
|
|
20143
20255
|
import path14 from "node:path";
|
|
20144
20256
|
import { Command as Command14 } from "commander";
|
|
20257
|
+
async function clearRetryPin() {
|
|
20258
|
+
const ws = await findWorkspaceState();
|
|
20259
|
+
if (ws?.state.retryLessonId) {
|
|
20260
|
+
await updateWorkspaceState(ws.workspacePath, { retryLessonId: void 0 });
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
20145
20263
|
var logger16, validateCommand;
|
|
20146
20264
|
var init_validate = __esm({
|
|
20147
20265
|
"src/commands/validate.ts"() {
|
|
@@ -20152,6 +20270,7 @@ var init_validate = __esm({
|
|
|
20152
20270
|
init_http();
|
|
20153
20271
|
init_guards();
|
|
20154
20272
|
init_course_state();
|
|
20273
|
+
init_workspace_state();
|
|
20155
20274
|
init_formatter();
|
|
20156
20275
|
init_init_template();
|
|
20157
20276
|
init_errors();
|
|
@@ -20165,7 +20284,8 @@ var init_validate = __esm({
|
|
|
20165
20284
|
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
20166
20285
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
20167
20286
|
const deps = { data, logger: logger16 };
|
|
20168
|
-
|
|
20287
|
+
const retryLessonId = activeCourse.retryLessonId;
|
|
20288
|
+
let lessonId = retryLessonId ?? activeCourse.currentLessonId;
|
|
20169
20289
|
if (!lessonId) {
|
|
20170
20290
|
try {
|
|
20171
20291
|
const prog = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
@@ -20249,8 +20369,19 @@ var init_validate = __esm({
|
|
|
20249
20369
|
} else {
|
|
20250
20370
|
output(formatValidation(result), { json: false });
|
|
20251
20371
|
}
|
|
20372
|
+
if (retryLessonId && result.passed) {
|
|
20373
|
+
await clearRetryPin();
|
|
20374
|
+
if (!opts.json) {
|
|
20375
|
+
process.stderr.write(
|
|
20376
|
+
"\n\u21A9\uFE0F Revalida\xE7\xE3o aprovada e registrada. Voltando para a sua li\xE7\xE3o atual.\n"
|
|
20377
|
+
);
|
|
20378
|
+
}
|
|
20379
|
+
}
|
|
20252
20380
|
process.exit(result.passed ? 0 : 1);
|
|
20253
20381
|
} catch (err) {
|
|
20382
|
+
if (err instanceof CliApiError && err.code === "LESSON_NOT_REACHED") {
|
|
20383
|
+
await clearRetryPin();
|
|
20384
|
+
}
|
|
20254
20385
|
if (isEnrollmentNotEntitledError(err)) {
|
|
20255
20386
|
const friendly = getErrors().enrollmentNotEntitled;
|
|
20256
20387
|
if (opts.json) jsonError("enrollment_not_entitled", { message: friendly });
|
|
@@ -20269,6 +20400,12 @@ var init_validate = __esm({
|
|
|
20269
20400
|
error(friendly);
|
|
20270
20401
|
return;
|
|
20271
20402
|
}
|
|
20403
|
+
if (isDailyAiLimitError(err)) {
|
|
20404
|
+
const friendly = getErrors().dailyAiLimit;
|
|
20405
|
+
if (opts.json) jsonError("daily_ai_limit", { message: friendly });
|
|
20406
|
+
error(friendly);
|
|
20407
|
+
return;
|
|
20408
|
+
}
|
|
20272
20409
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20273
20410
|
if (opts.json) jsonError(msg);
|
|
20274
20411
|
error(msg);
|
|
@@ -20277,8 +20414,81 @@ var init_validate = __esm({
|
|
|
20277
20414
|
}
|
|
20278
20415
|
});
|
|
20279
20416
|
|
|
20280
|
-
// src/commands/
|
|
20417
|
+
// src/commands/retry.ts
|
|
20281
20418
|
import { Command as Command15 } from "commander";
|
|
20419
|
+
var logger17, LESSON_ID_RE, retryCommand;
|
|
20420
|
+
var init_retry = __esm({
|
|
20421
|
+
"src/commands/retry.ts"() {
|
|
20422
|
+
"use strict";
|
|
20423
|
+
init_dist();
|
|
20424
|
+
init_lessons();
|
|
20425
|
+
init_http();
|
|
20426
|
+
init_guards();
|
|
20427
|
+
init_workspace_state();
|
|
20428
|
+
init_formatter();
|
|
20429
|
+
logger17 = createLogger("cli:retry");
|
|
20430
|
+
LESSON_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20431
|
+
retryCommand = new Command15("retry").description("Revalidate a lesson you already completed (your grade keeps the best attempt)").argument("[lessonId]", "Id of the completed lesson (see: tostudy grades --course <slug> --json)").option("--clear", "Cancel the pending retry; validate goes back to the current lesson").option("--json", "Output structured JSON").action(async (lessonId, opts) => {
|
|
20432
|
+
try {
|
|
20433
|
+
if (opts.clear) {
|
|
20434
|
+
const ws2 = await findWorkspaceState();
|
|
20435
|
+
if (ws2?.state.retryLessonId) {
|
|
20436
|
+
await updateWorkspaceState(ws2.workspacePath, { retryLessonId: void 0 });
|
|
20437
|
+
}
|
|
20438
|
+
if (opts.json) output({ retryLessonId: null }, { json: true });
|
|
20439
|
+
else
|
|
20440
|
+
output("Revalida\xE7\xE3o cancelada. `tostudy validate` volta a usar a sua li\xE7\xE3o atual.", {
|
|
20441
|
+
json: false
|
|
20442
|
+
});
|
|
20443
|
+
return;
|
|
20444
|
+
}
|
|
20445
|
+
if (!lessonId || !LESSON_ID_RE.test(lessonId)) {
|
|
20446
|
+
const message = "Informe o id da li\xE7\xE3o conclu\xEDda. Os ids aparecem em: tostudy grades --course <slug> --json";
|
|
20447
|
+
if (opts.json) jsonError("invalid_lesson_id", { message });
|
|
20448
|
+
error(message);
|
|
20449
|
+
}
|
|
20450
|
+
const session = await requireSession();
|
|
20451
|
+
const activeCourse = await requireActiveCourse();
|
|
20452
|
+
const ws = await findWorkspaceState();
|
|
20453
|
+
if (!ws) {
|
|
20454
|
+
const message = "Nenhum workspace ativo. Rode: tostudy select";
|
|
20455
|
+
if (opts.json) jsonError("no_workspace", { message });
|
|
20456
|
+
error(message);
|
|
20457
|
+
}
|
|
20458
|
+
const data = createHttpProvider(session.apiUrl, session.token);
|
|
20459
|
+
const content = await getContent(
|
|
20460
|
+
{ lessonId, enrollmentId: activeCourse.enrollmentId },
|
|
20461
|
+
{ data, logger: logger17 }
|
|
20462
|
+
);
|
|
20463
|
+
await updateWorkspaceState(ws.workspacePath, { retryLessonId: lessonId });
|
|
20464
|
+
if (opts.json) {
|
|
20465
|
+
output({ retryLessonId: lessonId, lesson: content }, { json: true });
|
|
20466
|
+
return;
|
|
20467
|
+
}
|
|
20468
|
+
output(
|
|
20469
|
+
[
|
|
20470
|
+
`\u21A9\uFE0F Revalida\xE7\xE3o da li\xE7\xE3o: ${content.title}`,
|
|
20471
|
+
"",
|
|
20472
|
+
content.content,
|
|
20473
|
+
...content.acceptanceCriteria ? ["", "Crit\xE9rios de aceita\xE7\xE3o:", content.acceptanceCriteria] : [],
|
|
20474
|
+
"",
|
|
20475
|
+
"Esta li\xE7\xE3o continua conclu\xEDda. A nova tentativa entra na sua nota se for a melhor.",
|
|
20476
|
+
"\u2192 tostudy validate <arquivo> para enviar a nova resposta",
|
|
20477
|
+
"\u2192 tostudy retry --clear para desistir e voltar \xE0 li\xE7\xE3o atual"
|
|
20478
|
+
].join("\n"),
|
|
20479
|
+
{ json: false }
|
|
20480
|
+
);
|
|
20481
|
+
} catch (err) {
|
|
20482
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
20483
|
+
if (opts.json) jsonError(message);
|
|
20484
|
+
error(message);
|
|
20485
|
+
}
|
|
20486
|
+
});
|
|
20487
|
+
}
|
|
20488
|
+
});
|
|
20489
|
+
|
|
20490
|
+
// src/commands/menu.ts
|
|
20491
|
+
import { Command as Command16 } from "commander";
|
|
20282
20492
|
var menuCommand;
|
|
20283
20493
|
var init_menu = __esm({
|
|
20284
20494
|
"src/commands/menu.ts"() {
|
|
@@ -20286,7 +20496,7 @@ var init_menu = __esm({
|
|
|
20286
20496
|
init_session_store();
|
|
20287
20497
|
init_workspace_state();
|
|
20288
20498
|
init_formatter();
|
|
20289
|
-
menuCommand = new
|
|
20499
|
+
menuCommand = new Command16("menu").description("Show available commands and current study context").action(async () => {
|
|
20290
20500
|
const session = await getSession();
|
|
20291
20501
|
const wsResult = session ? await findWorkspaceState() : null;
|
|
20292
20502
|
const activeCourse = wsResult?.state ?? null;
|
|
@@ -20463,7 +20673,7 @@ var init_learner_context = __esm({
|
|
|
20463
20673
|
});
|
|
20464
20674
|
|
|
20465
20675
|
// src/commands/init.ts
|
|
20466
|
-
import { Command as
|
|
20676
|
+
import { Command as Command17 } from "commander";
|
|
20467
20677
|
function isCompleteProfile(flags) {
|
|
20468
20678
|
return Boolean(
|
|
20469
20679
|
flags.segment && flags.company && flags.products && flags.region && flags.team && flags.goal && flags.level
|
|
@@ -20634,7 +20844,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
|
|
|
20634
20844
|
deps.output(artifacts.learnerBrief, { json: false });
|
|
20635
20845
|
}
|
|
20636
20846
|
}
|
|
20637
|
-
var
|
|
20847
|
+
var logger18, defaultDeps4, initCommand;
|
|
20638
20848
|
var init_init = __esm({
|
|
20639
20849
|
"src/commands/init.ts"() {
|
|
20640
20850
|
"use strict";
|
|
@@ -20651,7 +20861,7 @@ var init_init = __esm({
|
|
|
20651
20861
|
init_instruction_pipeline();
|
|
20652
20862
|
init_root_agents_consent();
|
|
20653
20863
|
init_pipeline_deps();
|
|
20654
|
-
|
|
20864
|
+
logger18 = createLogger("cli:init");
|
|
20655
20865
|
defaultDeps4 = {
|
|
20656
20866
|
// GH #1419 item 3 — `init` used the raw session, so an expired access
|
|
20657
20867
|
// token met the POST as a 401. Refresh through the guard when one exists.
|
|
@@ -20671,12 +20881,12 @@ var init_init = __esm({
|
|
|
20671
20881
|
saveCourseLearnerProfile,
|
|
20672
20882
|
buildInitArtifacts,
|
|
20673
20883
|
output,
|
|
20674
|
-
logger:
|
|
20884
|
+
logger: logger18,
|
|
20675
20885
|
createHttpProvider,
|
|
20676
20886
|
resolveAndGenerate,
|
|
20677
20887
|
buildPipelineDeps
|
|
20678
20888
|
};
|
|
20679
|
-
initCommand = new
|
|
20889
|
+
initCommand = new Command17("init").description("Generate tutor instructions and learner brief for the active course").option("--segment <segment>", "Learner segment/niche").option("--company <company>", "Company or business type").option("--products <products>", "Main products or services").option("--region <region>", "Operating region").option("--team <team>", "Team involved").option("--goal <goal>", "Primary learning goal").option("--level <level>", "Learner level: beginner, intermediate, advanced").option("--adapt-context", "Adapt examples to learner's real context").option(
|
|
20680
20890
|
"--root-agents",
|
|
20681
20891
|
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
20682
20892
|
).option("--json", "Output structured JSON").action(async (opts) => {
|
|
@@ -21004,11 +21214,11 @@ var init_workspace = __esm({
|
|
|
21004
21214
|
});
|
|
21005
21215
|
|
|
21006
21216
|
// src/commands/workspace.ts
|
|
21007
|
-
import { Command as
|
|
21217
|
+
import { Command as Command18 } from "commander";
|
|
21008
21218
|
import path17 from "node:path";
|
|
21009
21219
|
import os8 from "node:os";
|
|
21010
21220
|
import fs14 from "node:fs/promises";
|
|
21011
|
-
var
|
|
21221
|
+
var logger19, workspaceCommand;
|
|
21012
21222
|
var init_workspace2 = __esm({
|
|
21013
21223
|
"src/commands/workspace.ts"() {
|
|
21014
21224
|
"use strict";
|
|
@@ -21018,8 +21228,8 @@ var init_workspace2 = __esm({
|
|
|
21018
21228
|
init_course_state();
|
|
21019
21229
|
init_resolve();
|
|
21020
21230
|
init_errors();
|
|
21021
|
-
|
|
21022
|
-
workspaceCommand = new
|
|
21231
|
+
logger19 = createLogger("cli:workspace");
|
|
21232
|
+
workspaceCommand = new Command18("workspace").description(
|
|
21023
21233
|
getErrors().workspaceCommandDescription
|
|
21024
21234
|
);
|
|
21025
21235
|
workspaceCommand.command("setup").description(getErrors().workspaceSetupDescription).option("--path <dir>", "Diret\xF3rio base do workspace (omita para usar a pasta atual)").option("--json", "Output structured JSON").action(async (opts) => {
|
|
@@ -21084,7 +21294,7 @@ Pr\xF3ximo passo: tostudy export
|
|
|
21084
21294
|
);
|
|
21085
21295
|
}
|
|
21086
21296
|
} catch (err) {
|
|
21087
|
-
|
|
21297
|
+
logger19.error("workspace setup failed", { error: err });
|
|
21088
21298
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21089
21299
|
`);
|
|
21090
21300
|
process.exit(1);
|
|
@@ -21188,11 +21398,11 @@ Pr\xF3ximo passo: tostudy export
|
|
|
21188
21398
|
});
|
|
21189
21399
|
|
|
21190
21400
|
// src/commands/export.ts
|
|
21191
|
-
import { Command as
|
|
21401
|
+
import { Command as Command19 } from "commander";
|
|
21192
21402
|
import path18 from "node:path";
|
|
21193
21403
|
import os9 from "node:os";
|
|
21194
21404
|
import fs15 from "node:fs/promises";
|
|
21195
|
-
var
|
|
21405
|
+
var logger20, exportCommand;
|
|
21196
21406
|
var init_export = __esm({
|
|
21197
21407
|
"src/commands/export.ts"() {
|
|
21198
21408
|
"use strict";
|
|
@@ -21203,8 +21413,8 @@ var init_export = __esm({
|
|
|
21203
21413
|
init_course_state();
|
|
21204
21414
|
init_resolve();
|
|
21205
21415
|
init_errors();
|
|
21206
|
-
|
|
21207
|
-
exportCommand = new
|
|
21416
|
+
logger20 = createLogger("cli:export");
|
|
21417
|
+
exportCommand = new Command19("export").description("Extrair exerc\xEDcio atual para o workspace local").option("--tier <tier>", "Tier do exerc\xEDcio: guided, semiGuided, challenging", "guided").option("--path <dir>", "Diret\xF3rio base do workspace", path18.join(os9.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
21208
21418
|
try {
|
|
21209
21419
|
const session = await requireSession();
|
|
21210
21420
|
const activeCourse = await requireActiveCourse();
|
|
@@ -21231,7 +21441,7 @@ var init_export = __esm({
|
|
|
21231
21441
|
}
|
|
21232
21442
|
if (!hasConfig) {
|
|
21233
21443
|
const slug = courseSlug(activeCourse.courseTitle);
|
|
21234
|
-
|
|
21444
|
+
logger20.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
|
|
21235
21445
|
await fs15.mkdir(ws.workspacePath, { recursive: true });
|
|
21236
21446
|
for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
|
|
21237
21447
|
await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
|
|
@@ -21296,7 +21506,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
|
|
|
21296
21506
|
);
|
|
21297
21507
|
}
|
|
21298
21508
|
} catch (err) {
|
|
21299
|
-
|
|
21509
|
+
logger20.error("export failed", { error: err });
|
|
21300
21510
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21301
21511
|
`);
|
|
21302
21512
|
process.exit(1);
|
|
@@ -21306,11 +21516,11 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
|
|
|
21306
21516
|
});
|
|
21307
21517
|
|
|
21308
21518
|
// src/commands/open.ts
|
|
21309
|
-
import { Command as
|
|
21519
|
+
import { Command as Command20 } from "commander";
|
|
21310
21520
|
import { execFile as execFile3 } from "node:child_process";
|
|
21311
21521
|
import path19 from "node:path";
|
|
21312
21522
|
import os10 from "node:os";
|
|
21313
|
-
var
|
|
21523
|
+
var logger21, openCommand;
|
|
21314
21524
|
var init_open = __esm({
|
|
21315
21525
|
"src/commands/open.ts"() {
|
|
21316
21526
|
"use strict";
|
|
@@ -21319,8 +21529,8 @@ var init_open = __esm({
|
|
|
21319
21529
|
init_course_state();
|
|
21320
21530
|
init_resolve();
|
|
21321
21531
|
init_errors();
|
|
21322
|
-
|
|
21323
|
-
openCommand = new
|
|
21532
|
+
logger21 = createLogger("cli:open");
|
|
21533
|
+
openCommand = new Command20("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path19.join(os10.homedir(), "study")).action(async (opts) => {
|
|
21324
21534
|
try {
|
|
21325
21535
|
const activeCourse = await requireActiveCourse();
|
|
21326
21536
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
|
|
@@ -21337,7 +21547,7 @@ var init_open = __esm({
|
|
|
21337
21547
|
const editor = process.env["EDITOR"] ?? "code";
|
|
21338
21548
|
execFile3(editor, [ws.workspacePath], (err) => {
|
|
21339
21549
|
if (err) {
|
|
21340
|
-
|
|
21550
|
+
logger21.error("open failed", { editor, workspacePath: ws.workspacePath });
|
|
21341
21551
|
process.stderr.write(`\u274C Falha ao abrir: ${err.message}
|
|
21342
21552
|
`);
|
|
21343
21553
|
process.exit(1);
|
|
@@ -21346,7 +21556,7 @@ var init_open = __esm({
|
|
|
21346
21556
|
`);
|
|
21347
21557
|
});
|
|
21348
21558
|
} catch (err) {
|
|
21349
|
-
|
|
21559
|
+
logger21.error("open command failed", { error: err });
|
|
21350
21560
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21351
21561
|
`);
|
|
21352
21562
|
process.exit(1);
|
|
@@ -21415,11 +21625,11 @@ var init_vault = __esm({
|
|
|
21415
21625
|
});
|
|
21416
21626
|
|
|
21417
21627
|
// src/commands/vault.ts
|
|
21418
|
-
import { Command as
|
|
21628
|
+
import { Command as Command21 } from "commander";
|
|
21419
21629
|
import path21 from "node:path";
|
|
21420
21630
|
import os11 from "node:os";
|
|
21421
21631
|
import fs17 from "node:fs/promises";
|
|
21422
|
-
var
|
|
21632
|
+
var logger22, vaultCommand;
|
|
21423
21633
|
var init_vault2 = __esm({
|
|
21424
21634
|
"src/commands/vault.ts"() {
|
|
21425
21635
|
"use strict";
|
|
@@ -21432,8 +21642,8 @@ var init_vault2 = __esm({
|
|
|
21432
21642
|
init_course_state();
|
|
21433
21643
|
init_resolve();
|
|
21434
21644
|
init_errors();
|
|
21435
|
-
|
|
21436
|
-
vaultCommand = new
|
|
21645
|
+
logger22 = createLogger("cli:vault");
|
|
21646
|
+
vaultCommand = new Command21("vault").description("Gerenciar vault Obsidian do curso");
|
|
21437
21647
|
vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path21.join(os11.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
|
|
21438
21648
|
try {
|
|
21439
21649
|
const session = await requireSession();
|
|
@@ -21476,7 +21686,7 @@ var init_vault2 = __esm({
|
|
|
21476
21686
|
cliWorkspacePaths(workspacePath, activeCourse.courseTitle)
|
|
21477
21687
|
);
|
|
21478
21688
|
const result = await writeVaultFiles(files, vaultOutputPath, activeCourse.courseId, slug);
|
|
21479
|
-
|
|
21689
|
+
logger22.info("Vault generated", {
|
|
21480
21690
|
courseId: activeCourse.courseId,
|
|
21481
21691
|
vaultPath: result.vaultPath,
|
|
21482
21692
|
filesWritten: result.filesWritten
|
|
@@ -21509,7 +21719,7 @@ Para visualizar:
|
|
|
21509
21719
|
);
|
|
21510
21720
|
}
|
|
21511
21721
|
} catch (err) {
|
|
21512
|
-
|
|
21722
|
+
logger22.error("vault init failed", { error: err });
|
|
21513
21723
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21514
21724
|
`);
|
|
21515
21725
|
process.exit(1);
|
|
@@ -21539,7 +21749,7 @@ Para visualizar:
|
|
|
21539
21749
|
process.exit(1);
|
|
21540
21750
|
}
|
|
21541
21751
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
21542
|
-
const deps = { data, logger:
|
|
21752
|
+
const deps = { data, logger: logger22 };
|
|
21543
21753
|
const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
21544
21754
|
const markerPath = path21.join(vaultPath, ".ana-vault.json");
|
|
21545
21755
|
const markerRaw = await fs17.readFile(markerPath, "utf-8");
|
|
@@ -21593,7 +21803,7 @@ Para visualizar:
|
|
|
21593
21803
|
);
|
|
21594
21804
|
}
|
|
21595
21805
|
} catch (err) {
|
|
21596
|
-
|
|
21806
|
+
logger22.error("vault sync failed", { error: err });
|
|
21597
21807
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21598
21808
|
`);
|
|
21599
21809
|
process.exit(1);
|
|
@@ -21603,7 +21813,7 @@ Para visualizar:
|
|
|
21603
21813
|
});
|
|
21604
21814
|
|
|
21605
21815
|
// src/commands/profile.ts
|
|
21606
|
-
import { Command as
|
|
21816
|
+
import { Command as Command22 } from "commander";
|
|
21607
21817
|
var profileCommand;
|
|
21608
21818
|
var init_profile = __esm({
|
|
21609
21819
|
"src/commands/profile.ts"() {
|
|
@@ -21611,7 +21821,7 @@ var init_profile = __esm({
|
|
|
21611
21821
|
init_guards();
|
|
21612
21822
|
init_course_state();
|
|
21613
21823
|
init_user_profile();
|
|
21614
|
-
profileCommand = new
|
|
21824
|
+
profileCommand = new Command22("profile").description("Show your learner profile for the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
21615
21825
|
const activeCourse = await requireActiveCourse();
|
|
21616
21826
|
const onboarding = await getCourseOnboardingState(activeCourse.courseId);
|
|
21617
21827
|
const profile = onboarding?.learnerProfile ?? await getUserProfile();
|
|
@@ -21672,8 +21882,8 @@ var init_profile = __esm({
|
|
|
21672
21882
|
});
|
|
21673
21883
|
|
|
21674
21884
|
// src/commands/sync.ts
|
|
21675
|
-
import { Command as
|
|
21676
|
-
var
|
|
21885
|
+
import { Command as Command23 } from "commander";
|
|
21886
|
+
var logger23, syncCommand;
|
|
21677
21887
|
var init_sync = __esm({
|
|
21678
21888
|
"src/commands/sync.ts"() {
|
|
21679
21889
|
"use strict";
|
|
@@ -21684,8 +21894,8 @@ var init_sync = __esm({
|
|
|
21684
21894
|
init_workspace_state();
|
|
21685
21895
|
init_root_agents_consent();
|
|
21686
21896
|
init_pipeline_deps();
|
|
21687
|
-
|
|
21688
|
-
syncCommand = new
|
|
21897
|
+
logger23 = createLogger("cli:sync");
|
|
21898
|
+
syncCommand = new Command23("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").option(
|
|
21689
21899
|
"--all-runtimes",
|
|
21690
21900
|
"Write instruction files for every supported runtime, even undetected ones"
|
|
21691
21901
|
).option(
|
|
@@ -21749,7 +21959,7 @@ var init_sync = __esm({
|
|
|
21749
21959
|
} catch (err) {
|
|
21750
21960
|
const msg = err instanceof Error ? err.message : String(err);
|
|
21751
21961
|
if (msg.includes("process.exit")) return;
|
|
21752
|
-
|
|
21962
|
+
logger23.warn("sync failed", { error: msg });
|
|
21753
21963
|
error(msg);
|
|
21754
21964
|
}
|
|
21755
21965
|
});
|
|
@@ -21757,8 +21967,8 @@ var init_sync = __esm({
|
|
|
21757
21967
|
});
|
|
21758
21968
|
|
|
21759
21969
|
// src/commands/brief.ts
|
|
21760
|
-
import { Command as
|
|
21761
|
-
var
|
|
21970
|
+
import { Command as Command24 } from "commander";
|
|
21971
|
+
var logger24, briefCommand;
|
|
21762
21972
|
var init_brief = __esm({
|
|
21763
21973
|
"src/commands/brief.ts"() {
|
|
21764
21974
|
"use strict";
|
|
@@ -21767,8 +21977,8 @@ var init_brief = __esm({
|
|
|
21767
21977
|
init_cache();
|
|
21768
21978
|
init_api();
|
|
21769
21979
|
init_formatter();
|
|
21770
|
-
|
|
21771
|
-
briefCommand = new
|
|
21980
|
+
logger24 = createLogger("cli:brief");
|
|
21981
|
+
briefCommand = new Command24("brief").description("Show your base learner brief (T1) status and content").option("--json", "Output structured JSON").action(async (opts) => {
|
|
21772
21982
|
try {
|
|
21773
21983
|
const session = await requireSession();
|
|
21774
21984
|
const cached2 = await readBriefCache();
|
|
@@ -21801,7 +22011,7 @@ var init_brief = __esm({
|
|
|
21801
22011
|
}
|
|
21802
22012
|
output(lines.join("\n"), { json: false });
|
|
21803
22013
|
} catch (err) {
|
|
21804
|
-
|
|
22014
|
+
logger24.error("Failed to show brief", { err });
|
|
21805
22015
|
error(`Erro ao buscar brief: ${err instanceof Error ? err.message : String(err)}`);
|
|
21806
22016
|
}
|
|
21807
22017
|
});
|
|
@@ -21809,8 +22019,8 @@ var init_brief = __esm({
|
|
|
21809
22019
|
});
|
|
21810
22020
|
|
|
21811
22021
|
// src/commands/brief-create.ts
|
|
21812
|
-
import { Command as
|
|
21813
|
-
var
|
|
22022
|
+
import { Command as Command25 } from "commander";
|
|
22023
|
+
var logger25, briefCreateCommand;
|
|
21814
22024
|
var init_brief_create = __esm({
|
|
21815
22025
|
"src/commands/brief-create.ts"() {
|
|
21816
22026
|
"use strict";
|
|
@@ -21820,8 +22030,8 @@ var init_brief_create = __esm({
|
|
|
21820
22030
|
init_api();
|
|
21821
22031
|
init_cache();
|
|
21822
22032
|
init_formatter();
|
|
21823
|
-
|
|
21824
|
-
briefCreateCommand = new
|
|
22033
|
+
logger25 = createLogger("cli:brief-create");
|
|
22034
|
+
briefCreateCommand = new Command25("brief-create").description("Create your base learner brief via interactive prompts (T1 bootstrap)").action(async () => {
|
|
21825
22035
|
try {
|
|
21826
22036
|
const session = await requireSession();
|
|
21827
22037
|
const answers = await collectBootstrapAnswers({ userName: session.userName });
|
|
@@ -21845,7 +22055,7 @@ var init_brief_create = __esm({
|
|
|
21845
22055
|
];
|
|
21846
22056
|
output(doneLines.join("\n"), { json: false });
|
|
21847
22057
|
} catch (err) {
|
|
21848
|
-
|
|
22058
|
+
logger25.error("Failed to create brief", { err });
|
|
21849
22059
|
error(`Erro ao criar brief: ${err instanceof Error ? err.message : String(err)}`);
|
|
21850
22060
|
}
|
|
21851
22061
|
});
|
|
@@ -21853,7 +22063,7 @@ var init_brief_create = __esm({
|
|
|
21853
22063
|
});
|
|
21854
22064
|
|
|
21855
22065
|
// src/commands/brief-open.ts
|
|
21856
|
-
import { Command as
|
|
22066
|
+
import { Command as Command26 } from "commander";
|
|
21857
22067
|
import { execFile as execFile4 } from "node:child_process";
|
|
21858
22068
|
import { platform } from "node:process";
|
|
21859
22069
|
function openUrl(url2) {
|
|
@@ -21882,7 +22092,7 @@ var init_brief_open = __esm({
|
|
|
21882
22092
|
init_guards();
|
|
21883
22093
|
init_formatter();
|
|
21884
22094
|
BRIEF_URL = "https://tostudy.ai/student/settings/learner-brief";
|
|
21885
|
-
briefOpenCommand = new
|
|
22095
|
+
briefOpenCommand = new Command26("brief-open").description("Open the learner brief editor in your web browser").action(async () => {
|
|
21886
22096
|
await requireSession();
|
|
21887
22097
|
output(`Abrindo ${BRIEF_URL} no navegador...`, { json: false });
|
|
21888
22098
|
openUrl(BRIEF_URL);
|
|
@@ -21910,7 +22120,7 @@ async function saveModuleSummary(workspacePath, input2) {
|
|
|
21910
22120
|
""
|
|
21911
22121
|
].join("\n");
|
|
21912
22122
|
fs18.writeFileSync(filePath, header + input2.summary, { mode: 384 });
|
|
21913
|
-
|
|
22123
|
+
logger26.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
|
|
21914
22124
|
return filePath;
|
|
21915
22125
|
}
|
|
21916
22126
|
async function loadSessionContext(workspacePath) {
|
|
@@ -21931,19 +22141,19 @@ async function loadSessionContext(workspacePath) {
|
|
|
21931
22141
|
}
|
|
21932
22142
|
return { moduleSummaries: summaries };
|
|
21933
22143
|
}
|
|
21934
|
-
var
|
|
22144
|
+
var logger26;
|
|
21935
22145
|
var init_storage = __esm({
|
|
21936
22146
|
"src/sessions/storage.ts"() {
|
|
21937
22147
|
"use strict";
|
|
21938
22148
|
init_dist();
|
|
21939
|
-
|
|
22149
|
+
logger26 = createLogger("cli:sessions");
|
|
21940
22150
|
}
|
|
21941
22151
|
});
|
|
21942
22152
|
|
|
21943
22153
|
// src/commands/compact.ts
|
|
21944
22154
|
import fs19 from "node:fs";
|
|
21945
|
-
import { Command as
|
|
21946
|
-
var
|
|
22155
|
+
import { Command as Command27 } from "commander";
|
|
22156
|
+
var logger27, compactCommand;
|
|
21947
22157
|
var init_compact = __esm({
|
|
21948
22158
|
"src/commands/compact.ts"() {
|
|
21949
22159
|
"use strict";
|
|
@@ -21952,8 +22162,8 @@ var init_compact = __esm({
|
|
|
21952
22162
|
init_workspace_state();
|
|
21953
22163
|
init_storage();
|
|
21954
22164
|
init_formatter();
|
|
21955
|
-
|
|
21956
|
-
compactCommand = new
|
|
22165
|
+
logger27 = createLogger("cli:compact");
|
|
22166
|
+
compactCommand = new Command27("compact").description("Save a module study summary (LLM-generated, from stdin)").option("--module-id <id>", "Module ID").option("--module-title <title>", "Module title").option("--json", "Output structured JSON").action(async (opts) => {
|
|
21957
22167
|
try {
|
|
21958
22168
|
const activeCourse = await requireActiveCourse();
|
|
21959
22169
|
const ws = await findWorkspaceState();
|
|
@@ -21973,7 +22183,7 @@ var init_compact = __esm({
|
|
|
21973
22183
|
moduleTitle,
|
|
21974
22184
|
summary
|
|
21975
22185
|
});
|
|
21976
|
-
|
|
22186
|
+
logger27.debug("Compact summary saved", { moduleId, filePath });
|
|
21977
22187
|
if (opts.json) {
|
|
21978
22188
|
output({ saved: true, path: filePath, moduleId }, { json: true });
|
|
21979
22189
|
} else {
|
|
@@ -21982,7 +22192,7 @@ var init_compact = __esm({
|
|
|
21982
22192
|
} catch (err) {
|
|
21983
22193
|
const msg = err instanceof Error ? err.message : String(err);
|
|
21984
22194
|
if (msg.includes("process.exit")) return;
|
|
21985
|
-
|
|
22195
|
+
logger27.warn("compact failed", { error: msg });
|
|
21986
22196
|
if (opts.json) jsonError(msg);
|
|
21987
22197
|
error(msg);
|
|
21988
22198
|
}
|
|
@@ -21991,8 +22201,8 @@ var init_compact = __esm({
|
|
|
21991
22201
|
});
|
|
21992
22202
|
|
|
21993
22203
|
// src/commands/context.ts
|
|
21994
|
-
import { Command as
|
|
21995
|
-
var
|
|
22204
|
+
import { Command as Command28 } from "commander";
|
|
22205
|
+
var logger28, contextCommand;
|
|
21996
22206
|
var init_context = __esm({
|
|
21997
22207
|
"src/commands/context.ts"() {
|
|
21998
22208
|
"use strict";
|
|
@@ -22002,8 +22212,8 @@ var init_context = __esm({
|
|
|
22002
22212
|
init_course_state();
|
|
22003
22213
|
init_formatter();
|
|
22004
22214
|
init_errors();
|
|
22005
|
-
|
|
22006
|
-
contextCommand = new
|
|
22215
|
+
logger28 = createLogger("cli:context");
|
|
22216
|
+
contextCommand = new Command28("context").description("Load session context (workspace state + module summaries) for LLM consumption").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22007
22217
|
try {
|
|
22008
22218
|
const ws = await findWorkspaceState();
|
|
22009
22219
|
if (!ws) {
|
|
@@ -22033,7 +22243,7 @@ var init_context = __esm({
|
|
|
22033
22243
|
totalModulesCompleted: sessionCtx.moduleSummaries.length,
|
|
22034
22244
|
driftWarning: driftWarning ?? null
|
|
22035
22245
|
};
|
|
22036
|
-
|
|
22246
|
+
logger28.debug("Context loaded", {
|
|
22037
22247
|
courseId: ws.state.courseId,
|
|
22038
22248
|
moduleSummaries: sessionCtx.moduleSummaries.length
|
|
22039
22249
|
});
|
|
@@ -22047,7 +22257,7 @@ var init_context = __esm({
|
|
|
22047
22257
|
} catch (err) {
|
|
22048
22258
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22049
22259
|
if (msg.includes("process.exit")) return;
|
|
22050
|
-
|
|
22260
|
+
logger28.warn("context failed", { error: msg });
|
|
22051
22261
|
if (opts.json) jsonError(msg);
|
|
22052
22262
|
error(msg);
|
|
22053
22263
|
}
|
|
@@ -22056,8 +22266,8 @@ var init_context = __esm({
|
|
|
22056
22266
|
});
|
|
22057
22267
|
|
|
22058
22268
|
// src/commands/memory.ts
|
|
22059
|
-
import { Command as
|
|
22060
|
-
var
|
|
22269
|
+
import { Command as Command29 } from "commander";
|
|
22270
|
+
var logger29, memoryCommand;
|
|
22061
22271
|
var init_memory = __esm({
|
|
22062
22272
|
"src/commands/memory.ts"() {
|
|
22063
22273
|
"use strict";
|
|
@@ -22067,8 +22277,8 @@ var init_memory = __esm({
|
|
|
22067
22277
|
init_guards();
|
|
22068
22278
|
init_formatter();
|
|
22069
22279
|
init_errors();
|
|
22070
|
-
|
|
22071
|
-
memoryCommand = new
|
|
22280
|
+
logger29 = createLogger("cli:memory");
|
|
22281
|
+
memoryCommand = new Command29("memory").description("Load accumulated student memory (learning profile + recent lessons) for the tutor").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22072
22282
|
try {
|
|
22073
22283
|
const ws = await findWorkspaceState();
|
|
22074
22284
|
if (!ws) {
|
|
@@ -22091,7 +22301,7 @@ var init_memory = __esm({
|
|
|
22091
22301
|
} catch (err) {
|
|
22092
22302
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22093
22303
|
if (msg.includes("process.exit")) return;
|
|
22094
|
-
|
|
22304
|
+
logger29.warn("memory failed", { error: msg });
|
|
22095
22305
|
if (opts.json) jsonError(msg);
|
|
22096
22306
|
else error(msg);
|
|
22097
22307
|
}
|
|
@@ -22100,8 +22310,8 @@ var init_memory = __esm({
|
|
|
22100
22310
|
});
|
|
22101
22311
|
|
|
22102
22312
|
// src/commands/insight.ts
|
|
22103
|
-
import { Command as
|
|
22104
|
-
var
|
|
22313
|
+
import { Command as Command30 } from "commander";
|
|
22314
|
+
var logger30, VALID_TYPES, insightCommand;
|
|
22105
22315
|
var init_insight = __esm({
|
|
22106
22316
|
"src/commands/insight.ts"() {
|
|
22107
22317
|
"use strict";
|
|
@@ -22111,9 +22321,9 @@ var init_insight = __esm({
|
|
|
22111
22321
|
init_guards();
|
|
22112
22322
|
init_formatter();
|
|
22113
22323
|
init_errors();
|
|
22114
|
-
|
|
22324
|
+
logger30 = createLogger("cli:insight");
|
|
22115
22325
|
VALID_TYPES = ["difficulty", "breakthrough", "question"];
|
|
22116
|
-
insightCommand = new
|
|
22326
|
+
insightCommand = new Command30("insight").description(
|
|
22117
22327
|
"Persist a student cognitive insight (difficulty | breakthrough | question) into course memory"
|
|
22118
22328
|
).argument("<type>", "difficulty | breakthrough | question").argument("<content>", 'Short description, e.g. "confunde async/await com promises"').option("--module-id <id>", "Module the insight relates to").option("--json", "Output structured JSON").action(async (type, content, opts) => {
|
|
22119
22329
|
try {
|
|
@@ -22153,7 +22363,7 @@ var init_insight = __esm({
|
|
|
22153
22363
|
} catch (err) {
|
|
22154
22364
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22155
22365
|
if (msg.includes("process.exit")) return;
|
|
22156
|
-
|
|
22366
|
+
logger30.warn("insight failed", { error: msg });
|
|
22157
22367
|
if (opts.json) jsonError(msg);
|
|
22158
22368
|
else error(msg);
|
|
22159
22369
|
}
|
|
@@ -22162,11 +22372,11 @@ var init_insight = __esm({
|
|
|
22162
22372
|
});
|
|
22163
22373
|
|
|
22164
22374
|
// src/commands/level.ts
|
|
22165
|
-
import { Command as
|
|
22375
|
+
import { Command as Command31 } from "commander";
|
|
22166
22376
|
function isExerciseLevel2(value) {
|
|
22167
22377
|
return ALL_LEVELS.includes(value);
|
|
22168
22378
|
}
|
|
22169
|
-
var
|
|
22379
|
+
var logger31, ALL_LEVELS, LEVEL_LABELS2, levelCommand;
|
|
22170
22380
|
var init_level = __esm({
|
|
22171
22381
|
"src/commands/level.ts"() {
|
|
22172
22382
|
"use strict";
|
|
@@ -22175,7 +22385,7 @@ var init_level = __esm({
|
|
|
22175
22385
|
init_exercises();
|
|
22176
22386
|
init_guards();
|
|
22177
22387
|
init_formatter();
|
|
22178
|
-
|
|
22388
|
+
logger31 = createLogger("cli:level");
|
|
22179
22389
|
ALL_LEVELS = ["L0", "L1", "L2", "L3", "L4"];
|
|
22180
22390
|
LEVEL_LABELS2 = {
|
|
22181
22391
|
L0: "Pr\xE9-check (perguntas conceituais)",
|
|
@@ -22184,12 +22394,12 @@ var init_level = __esm({
|
|
|
22184
22394
|
L3: "Guiado (passo a passo + checkpoints)",
|
|
22185
22395
|
L4: "Desafio livre (folha em branco)"
|
|
22186
22396
|
};
|
|
22187
|
-
levelCommand = new
|
|
22397
|
+
levelCommand = new Command31("level").description("Show or set the exercise scaffolding level for the active course").argument("[level]", "Target level: L0, L1, L2, L3, or L4").option("--json", "Output structured JSON").action(async (rawLevel, opts) => {
|
|
22188
22398
|
try {
|
|
22189
22399
|
const session = await requireSession();
|
|
22190
22400
|
const activeCourse = await requireActiveCourse();
|
|
22191
22401
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
22192
|
-
const deps = { data, logger:
|
|
22402
|
+
const deps = { data, logger: logger31 };
|
|
22193
22403
|
if (!rawLevel) {
|
|
22194
22404
|
const levels = await getEnrollmentLevels({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
22195
22405
|
if (opts.json) {
|
|
@@ -22247,8 +22457,8 @@ var init_level = __esm({
|
|
|
22247
22457
|
});
|
|
22248
22458
|
|
|
22249
22459
|
// src/commands/theory.ts
|
|
22250
|
-
import { Command as
|
|
22251
|
-
var
|
|
22460
|
+
import { Command as Command32 } from "commander";
|
|
22461
|
+
var logger32, EXERCISE_LEVELS2, theoryCommand;
|
|
22252
22462
|
var init_theory = __esm({
|
|
22253
22463
|
"src/commands/theory.ts"() {
|
|
22254
22464
|
"use strict";
|
|
@@ -22256,9 +22466,9 @@ var init_theory = __esm({
|
|
|
22256
22466
|
init_guards();
|
|
22257
22467
|
init_formatter();
|
|
22258
22468
|
init_errors();
|
|
22259
|
-
|
|
22469
|
+
logger32 = createLogger("cli:theory");
|
|
22260
22470
|
EXERCISE_LEVELS2 = ["L0", "L1", "L2", "L3", "L4"];
|
|
22261
|
-
theoryCommand = new
|
|
22471
|
+
theoryCommand = new Command32("theory").description("Request more theory on the current lesson (escape hatch)").option("--focus <text>", "Optional focus area in PT-BR (max 500 chars)").option("--level <L0|L1|L2|L3|L4>", "Override current exercise level (otherwise auto-detect)").option("--lesson <uuid>", "Override lesson ID (defaults to current workspace lesson)").option("--json", "Output structured JSON").action(async (opts) => {
|
|
22262
22472
|
try {
|
|
22263
22473
|
const session = await requireSession();
|
|
22264
22474
|
const active = await requireActiveCourse();
|
|
@@ -22295,7 +22505,7 @@ var init_theory = __esm({
|
|
|
22295
22505
|
currentLevel = levels.currentLevel;
|
|
22296
22506
|
}
|
|
22297
22507
|
const requestUrl = `${session.apiUrl}/api/cli/exercises/more-theory`;
|
|
22298
|
-
|
|
22508
|
+
logger32.debug("Requesting more theory", { lessonId, currentLevel });
|
|
22299
22509
|
const res = await fetch(requestUrl, {
|
|
22300
22510
|
method: "POST",
|
|
22301
22511
|
headers: {
|
|
@@ -22316,6 +22526,11 @@ var init_theory = __esm({
|
|
|
22316
22526
|
if (opts.json) jsonError("insufficient_credits", { message: friendly });
|
|
22317
22527
|
error(friendly);
|
|
22318
22528
|
}
|
|
22529
|
+
if (body.error === "DAILY_AI_COST_LIMIT_EXCEEDED") {
|
|
22530
|
+
const friendly = getErrors().dailyAiLimit;
|
|
22531
|
+
if (opts.json) jsonError("daily_ai_limit", { message: friendly });
|
|
22532
|
+
error(friendly);
|
|
22533
|
+
}
|
|
22319
22534
|
const msg = body.error ?? body.message ?? `Falha ao pedir teoria (${res.status})`;
|
|
22320
22535
|
if (opts.json) jsonError("request_failed", { message: msg, code: res.status });
|
|
22321
22536
|
error(msg);
|
|
@@ -22333,6 +22548,12 @@ var init_theory = __esm({
|
|
|
22333
22548
|
error(friendly);
|
|
22334
22549
|
return;
|
|
22335
22550
|
}
|
|
22551
|
+
if (isDailyAiLimitError(err)) {
|
|
22552
|
+
const friendly = getErrors().dailyAiLimit;
|
|
22553
|
+
if (opts.json) jsonError("daily_ai_limit", { message: friendly });
|
|
22554
|
+
error(friendly);
|
|
22555
|
+
return;
|
|
22556
|
+
}
|
|
22336
22557
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22337
22558
|
if (opts.json) jsonError("unexpected_error", { message: msg });
|
|
22338
22559
|
error(msg);
|
|
@@ -22618,7 +22839,7 @@ var init_resolve_enrollment = __esm({
|
|
|
22618
22839
|
});
|
|
22619
22840
|
|
|
22620
22841
|
// src/commands/export-grades.ts
|
|
22621
|
-
import { Command as
|
|
22842
|
+
import { Command as Command33 } from "commander";
|
|
22622
22843
|
function isFormat(value) {
|
|
22623
22844
|
return SUPPORTED_FORMATS.includes(value);
|
|
22624
22845
|
}
|
|
@@ -22705,7 +22926,7 @@ async function runExportGrades(opts, deps = defaultDeps5) {
|
|
|
22705
22926
|
if (!rendered.endsWith("\n")) rendered += "\n";
|
|
22706
22927
|
deps.stdoutWrite(rendered);
|
|
22707
22928
|
}
|
|
22708
|
-
var
|
|
22929
|
+
var logger33, SUPPORTED_FORMATS, defaultDeps5, exportGradesCommand;
|
|
22709
22930
|
var init_export_grades = __esm({
|
|
22710
22931
|
"src/commands/export-grades.ts"() {
|
|
22711
22932
|
"use strict";
|
|
@@ -22713,7 +22934,7 @@ var init_export_grades = __esm({
|
|
|
22713
22934
|
init_guards();
|
|
22714
22935
|
init_grades();
|
|
22715
22936
|
init_resolve_enrollment();
|
|
22716
|
-
|
|
22937
|
+
logger33 = createLogger("cli:export-grades");
|
|
22717
22938
|
SUPPORTED_FORMATS = ["json", "csv", "md"];
|
|
22718
22939
|
defaultDeps5 = {
|
|
22719
22940
|
requireSession,
|
|
@@ -22726,10 +22947,10 @@ var init_export_grades = __esm({
|
|
|
22726
22947
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22727
22948
|
// process.exit is typed as `never` — wrap so the cast is local to one line.
|
|
22728
22949
|
exit: (code) => process.exit(code),
|
|
22729
|
-
logger:
|
|
22950
|
+
logger: logger33,
|
|
22730
22951
|
resolveBySlug: resolveEnrollmentBySlug
|
|
22731
22952
|
};
|
|
22732
|
-
exportGradesCommand = new
|
|
22953
|
+
exportGradesCommand = new Command33("export-grades").description("Exporta o hist\xF3rico de valida\xE7\xF5es (notas) do curso ativo").option(
|
|
22733
22954
|
"--course <slug>",
|
|
22734
22955
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, exporta o curso ativo."
|
|
22735
22956
|
).option("--format <json|csv|md>", "Formato de sa\xEDda (default: json)", "json").addHelpText(
|
|
@@ -22754,7 +22975,7 @@ C\xF3digos de sa\xEDda:
|
|
|
22754
22975
|
});
|
|
22755
22976
|
|
|
22756
22977
|
// src/commands/grades.ts
|
|
22757
|
-
import { Command as
|
|
22978
|
+
import { Command as Command34 } from "commander";
|
|
22758
22979
|
async function fetchAttempts(session, enrollmentId, deps) {
|
|
22759
22980
|
const url2 = `${session.apiUrl}/api/cli/validations/by-enrollment?enrollmentId=${encodeURIComponent(
|
|
22760
22981
|
enrollmentId
|
|
@@ -22789,15 +23010,13 @@ function summarizeAttempts(courseTitle, attempts) {
|
|
|
22789
23010
|
};
|
|
22790
23011
|
}
|
|
22791
23012
|
let passedCount = 0;
|
|
22792
|
-
let scoreSum = 0;
|
|
22793
|
-
let scoreCount = 0;
|
|
22794
23013
|
let latestTs = -Infinity;
|
|
22795
23014
|
let latestAt = attempts[0].attemptedAt;
|
|
23015
|
+
const bestByLesson = /* @__PURE__ */ new Map();
|
|
22796
23016
|
for (const a of attempts) {
|
|
22797
23017
|
if (a.passed) passedCount++;
|
|
22798
23018
|
if (a.score !== null) {
|
|
22799
|
-
|
|
22800
|
-
scoreCount++;
|
|
23019
|
+
bestByLesson.set(a.lessonId, Math.max(a.score, bestByLesson.get(a.lessonId) ?? -Infinity));
|
|
22801
23020
|
}
|
|
22802
23021
|
const t = (a.attemptedAt instanceof Date ? a.attemptedAt : new Date(a.attemptedAt)).getTime();
|
|
22803
23022
|
if (t > latestTs) {
|
|
@@ -22805,13 +23024,14 @@ function summarizeAttempts(courseTitle, attempts) {
|
|
|
22805
23024
|
latestAt = a.attemptedAt;
|
|
22806
23025
|
}
|
|
22807
23026
|
}
|
|
23027
|
+
const bestScores = [...bestByLesson.values()];
|
|
22808
23028
|
return {
|
|
22809
23029
|
courseTitle,
|
|
22810
23030
|
lastAttemptAt: latestAt,
|
|
22811
23031
|
attemptsCount: attempts.length,
|
|
22812
23032
|
passedCount,
|
|
22813
23033
|
failedCount: attempts.length - passedCount,
|
|
22814
|
-
averageScore:
|
|
23034
|
+
averageScore: bestScores.length === 0 ? null : bestScores.reduce((a, b) => a + b, 0) / bestScores.length
|
|
22815
23035
|
};
|
|
22816
23036
|
}
|
|
22817
23037
|
async function runGrades(opts, deps = defaultDeps6) {
|
|
@@ -22895,7 +23115,7 @@ async function runGrades(opts, deps = defaultDeps6) {
|
|
|
22895
23115
|
);
|
|
22896
23116
|
deps.stdoutWrite(formatGradesAllEnrollmentsTable(summaries) + "\n");
|
|
22897
23117
|
}
|
|
22898
|
-
var
|
|
23118
|
+
var logger34, defaultDeps6, gradesCommand;
|
|
22899
23119
|
var init_grades2 = __esm({
|
|
22900
23120
|
"src/commands/grades.ts"() {
|
|
22901
23121
|
"use strict";
|
|
@@ -22905,7 +23125,7 @@ var init_grades2 = __esm({
|
|
|
22905
23125
|
init_guards();
|
|
22906
23126
|
init_grades();
|
|
22907
23127
|
init_resolve_enrollment();
|
|
22908
|
-
|
|
23128
|
+
logger34 = createLogger("cli:grades");
|
|
22909
23129
|
defaultDeps6 = {
|
|
22910
23130
|
requireSession,
|
|
22911
23131
|
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
@@ -22915,12 +23135,12 @@ var init_grades2 = __esm({
|
|
|
22915
23135
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22916
23136
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22917
23137
|
exit: (code) => process.exit(code),
|
|
22918
|
-
logger:
|
|
23138
|
+
logger: logger34,
|
|
22919
23139
|
resolveBySlug: resolveEnrollmentBySlug,
|
|
22920
23140
|
buildDataProvider: createHttpProvider,
|
|
22921
23141
|
listCoursesFn: listCourses
|
|
22922
23142
|
};
|
|
22923
|
-
gradesCommand = new
|
|
23143
|
+
gradesCommand = new Command34("grades").description("Mostra um resumo das notas (todas as matr\xEDculas ou um curso espec\xEDfico)").option(
|
|
22924
23144
|
"--course <slug>",
|
|
22925
23145
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, lista todos os cursos."
|
|
22926
23146
|
).addHelpText(
|
|
@@ -22944,7 +23164,7 @@ C\xF3digos de sa\xEDda:
|
|
|
22944
23164
|
});
|
|
22945
23165
|
|
|
22946
23166
|
// src/commands/review.ts
|
|
22947
|
-
import { Command as
|
|
23167
|
+
import { Command as Command35 } from "commander";
|
|
22948
23168
|
import { createInterface } from "node:readline/promises";
|
|
22949
23169
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
22950
23170
|
async function defaultPromptLessonChoice(lessonIds) {
|
|
@@ -23080,7 +23300,7 @@ async function runReview(slug, opts, deps = defaultDeps7) {
|
|
|
23080
23300
|
}) + "\n"
|
|
23081
23301
|
);
|
|
23082
23302
|
}
|
|
23083
|
-
var
|
|
23303
|
+
var logger35, defaultDeps7, reviewCommand;
|
|
23084
23304
|
var init_review2 = __esm({
|
|
23085
23305
|
"src/commands/review.ts"() {
|
|
23086
23306
|
"use strict";
|
|
@@ -23088,7 +23308,7 @@ var init_review2 = __esm({
|
|
|
23088
23308
|
init_guards();
|
|
23089
23309
|
init_grades();
|
|
23090
23310
|
init_resolve_enrollment();
|
|
23091
|
-
|
|
23311
|
+
logger35 = createLogger("cli:review");
|
|
23092
23312
|
defaultDeps7 = {
|
|
23093
23313
|
requireSession,
|
|
23094
23314
|
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
@@ -23098,11 +23318,11 @@ var init_review2 = __esm({
|
|
|
23098
23318
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
23099
23319
|
stderrWrite: (message) => process.stderr.write(message),
|
|
23100
23320
|
exit: (code) => process.exit(code),
|
|
23101
|
-
logger:
|
|
23321
|
+
logger: logger35,
|
|
23102
23322
|
resolveBySlug: resolveEnrollmentBySlug,
|
|
23103
23323
|
promptLessonChoice: defaultPromptLessonChoice
|
|
23104
23324
|
};
|
|
23105
|
-
reviewCommand = new
|
|
23325
|
+
reviewCommand = new Command35("review").description("Revisa a \xFAltima tentativa de valida\xE7\xE3o de uma li\xE7\xE3o").argument("<slug>", "Slug do curso (gerado a partir do t\xEDtulo)").option("--json", "Sa\xEDda em JSON (sem prompt interativo)").addHelpText(
|
|
23106
23326
|
"after",
|
|
23107
23327
|
`
|
|
23108
23328
|
Exemplos:
|
|
@@ -23131,9 +23351,9 @@ __export(cli_exports, {
|
|
|
23131
23351
|
CLI_VERSION: () => CLI_VERSION,
|
|
23132
23352
|
createProgram: () => createProgram
|
|
23133
23353
|
});
|
|
23134
|
-
import { Command as
|
|
23354
|
+
import { Command as Command36 } from "commander";
|
|
23135
23355
|
function createProgram() {
|
|
23136
|
-
const program2 = new
|
|
23356
|
+
const program2 = new Command36();
|
|
23137
23357
|
program2.name("tostudy").description("ToStudy CLI \u2014 study courses from the terminal").version(CLI_VERSION).option("--verbose", "Enable debug output").option("--course <id>", "Override active course ID").option("--locale <code>", "Output locale (pt-BR | en-US); defaults to LANG env then pt-BR").addHelpText(
|
|
23138
23358
|
"before",
|
|
23139
23359
|
[
|
|
@@ -23159,6 +23379,7 @@ function createProgram() {
|
|
|
23159
23379
|
program2.addCommand(knowledgeCommand);
|
|
23160
23380
|
program2.addCommand(hintCommand);
|
|
23161
23381
|
program2.addCommand(validateCommand);
|
|
23382
|
+
program2.addCommand(retryCommand);
|
|
23162
23383
|
program2.addCommand(levelCommand);
|
|
23163
23384
|
program2.addCommand(theoryCommand);
|
|
23164
23385
|
program2.addCommand(menuCommand);
|
|
@@ -23197,6 +23418,7 @@ var init_cli = __esm({
|
|
|
23197
23418
|
init_knowledge();
|
|
23198
23419
|
init_hint();
|
|
23199
23420
|
init_validate();
|
|
23421
|
+
init_retry();
|
|
23200
23422
|
init_menu();
|
|
23201
23423
|
init_init();
|
|
23202
23424
|
init_workspace2();
|