@tostudy-ai/cli 0.17.5 → 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 +340 -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 {
|
|
@@ -3536,7 +3598,7 @@ var CLI_VERSION;
|
|
|
3536
3598
|
var init_version = __esm({
|
|
3537
3599
|
"src/version.ts"() {
|
|
3538
3600
|
"use strict";
|
|
3539
|
-
CLI_VERSION = true ? "0.17.
|
|
3601
|
+
CLI_VERSION = true ? "0.17.6" : "0.7.1";
|
|
3540
3602
|
}
|
|
3541
3603
|
});
|
|
3542
3604
|
|
|
@@ -3702,6 +3764,26 @@ var init_cache2 = __esm({
|
|
|
3702
3764
|
|
|
3703
3765
|
// src/commands/doctor.ts
|
|
3704
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
|
+
}
|
|
3705
3787
|
var doctorCommand;
|
|
3706
3788
|
var init_doctor = __esm({
|
|
3707
3789
|
"src/commands/doctor.ts"() {
|
|
@@ -3744,10 +3826,22 @@ var init_doctor = __esm({
|
|
|
3744
3826
|
session = await getSession();
|
|
3745
3827
|
} catch {
|
|
3746
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
|
+
}
|
|
3747
3840
|
checks["auth"] = {
|
|
3748
3841
|
loggedIn: !!session,
|
|
3749
3842
|
userName: session?.userName ?? null,
|
|
3750
|
-
expiresAt: session?.expiresAt ?? null
|
|
3843
|
+
expiresAt: session?.expiresAt ?? null,
|
|
3844
|
+
serverVerdict
|
|
3751
3845
|
};
|
|
3752
3846
|
try {
|
|
3753
3847
|
const apiUrl = session?.apiUrl ?? "https://tostudy.ai";
|
|
@@ -3847,7 +3941,17 @@ var init_doctor = __esm({
|
|
|
3847
3941
|
console.log(` ${pnpmVersion ? "\u2713" : "\u25CB"} pnpm ${pnpmVersion ?? errs.notFound}`);
|
|
3848
3942
|
console.log(` ${gitVersion ? "\u2713" : "\u25CB"} git ${gitVersion ?? errs.notFound}`);
|
|
3849
3943
|
console.log("\n Autentica\xE7\xE3o");
|
|
3850
|
-
|
|
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
|
+
}
|
|
3851
3955
|
if (session) {
|
|
3852
3956
|
console.log(` \u2713 Usu\xE1rio ${session.userName}`);
|
|
3853
3957
|
}
|
|
@@ -4716,6 +4820,7 @@ async function runStart(opts, deps = defaultDeps3) {
|
|
|
4716
4820
|
if (ws)
|
|
4717
4821
|
await deps.updateWorkspaceState(ws.workspacePath, {
|
|
4718
4822
|
currentLessonId: moduleData.firstLesson.id,
|
|
4823
|
+
retryLessonId: void 0,
|
|
4719
4824
|
currentModuleId: moduleData.module.id
|
|
4720
4825
|
});
|
|
4721
4826
|
if (opts.json) {
|
|
@@ -4808,6 +4913,7 @@ var init_start_next = __esm({
|
|
|
4808
4913
|
if (ws)
|
|
4809
4914
|
await updateWorkspaceState(ws.workspacePath, {
|
|
4810
4915
|
currentLessonId: moduleData.firstLesson.id,
|
|
4916
|
+
retryLessonId: void 0,
|
|
4811
4917
|
currentModuleId: moduleData.module.id
|
|
4812
4918
|
});
|
|
4813
4919
|
if (opts.json) {
|
|
@@ -4868,6 +4974,7 @@ var init_next = __esm({
|
|
|
4868
4974
|
if (ws)
|
|
4869
4975
|
await updateWorkspaceState(ws.workspacePath, {
|
|
4870
4976
|
currentLessonId: lessonData.lesson.id,
|
|
4977
|
+
retryLessonId: void 0,
|
|
4871
4978
|
...lessonData.lesson.moduleId ? { currentModuleId: lessonData.lesson.moduleId } : {}
|
|
4872
4979
|
});
|
|
4873
4980
|
if (opts.json) {
|
|
@@ -5364,7 +5471,7 @@ __export(util_exports, {
|
|
|
5364
5471
|
getSizableOrigin: () => getSizableOrigin,
|
|
5365
5472
|
hexToUint8Array: () => hexToUint8Array,
|
|
5366
5473
|
isObject: () => isObject,
|
|
5367
|
-
isPlainObject: () =>
|
|
5474
|
+
isPlainObject: () => isPlainObject2,
|
|
5368
5475
|
issue: () => issue,
|
|
5369
5476
|
joinValues: () => joinValues,
|
|
5370
5477
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
@@ -5533,7 +5640,7 @@ function slugify2(input2) {
|
|
|
5533
5640
|
function isObject(data) {
|
|
5534
5641
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
5535
5642
|
}
|
|
5536
|
-
function
|
|
5643
|
+
function isPlainObject2(o) {
|
|
5537
5644
|
if (isObject(o) === false)
|
|
5538
5645
|
return false;
|
|
5539
5646
|
const ctor = o.constructor;
|
|
@@ -5550,7 +5657,7 @@ function isPlainObject(o) {
|
|
|
5550
5657
|
return true;
|
|
5551
5658
|
}
|
|
5552
5659
|
function shallowClone(o) {
|
|
5553
|
-
if (
|
|
5660
|
+
if (isPlainObject2(o))
|
|
5554
5661
|
return { ...o };
|
|
5555
5662
|
if (Array.isArray(o))
|
|
5556
5663
|
return [...o];
|
|
@@ -5686,7 +5793,7 @@ function omit(schema, mask) {
|
|
|
5686
5793
|
return clone(schema, def);
|
|
5687
5794
|
}
|
|
5688
5795
|
function extend(schema, shape) {
|
|
5689
|
-
if (!
|
|
5796
|
+
if (!isPlainObject2(shape)) {
|
|
5690
5797
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
5691
5798
|
}
|
|
5692
5799
|
const checks = schema._zod.def.checks;
|
|
@@ -5709,7 +5816,7 @@ function extend(schema, shape) {
|
|
|
5709
5816
|
return clone(schema, def);
|
|
5710
5817
|
}
|
|
5711
5818
|
function safeExtend(schema, shape) {
|
|
5712
|
-
if (!
|
|
5819
|
+
if (!isPlainObject2(shape)) {
|
|
5713
5820
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
5714
5821
|
}
|
|
5715
5822
|
const def = mergeDefs(schema._zod.def, {
|
|
@@ -7185,7 +7292,7 @@ function mergeValues(a, b) {
|
|
|
7185
7292
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
7186
7293
|
return { valid: true, data: a };
|
|
7187
7294
|
}
|
|
7188
|
-
if (
|
|
7295
|
+
if (isPlainObject2(a) && isPlainObject2(b)) {
|
|
7189
7296
|
const bKeys = Object.keys(b);
|
|
7190
7297
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
7191
7298
|
const newObj = { ...a, ...b };
|
|
@@ -8379,7 +8486,7 @@ var init_schemas = __esm({
|
|
|
8379
8486
|
$ZodType.init(inst, def);
|
|
8380
8487
|
inst._zod.parse = (payload, ctx) => {
|
|
8381
8488
|
const input2 = payload.value;
|
|
8382
|
-
if (!
|
|
8489
|
+
if (!isPlainObject2(input2)) {
|
|
8383
8490
|
payload.issues.push({
|
|
8384
8491
|
expected: "record",
|
|
8385
8492
|
code: "invalid_type",
|
|
@@ -20147,6 +20254,12 @@ var init_init_template = __esm({
|
|
|
20147
20254
|
import fs11 from "node:fs";
|
|
20148
20255
|
import path14 from "node:path";
|
|
20149
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
|
+
}
|
|
20150
20263
|
var logger16, validateCommand;
|
|
20151
20264
|
var init_validate = __esm({
|
|
20152
20265
|
"src/commands/validate.ts"() {
|
|
@@ -20157,6 +20270,7 @@ var init_validate = __esm({
|
|
|
20157
20270
|
init_http();
|
|
20158
20271
|
init_guards();
|
|
20159
20272
|
init_course_state();
|
|
20273
|
+
init_workspace_state();
|
|
20160
20274
|
init_formatter();
|
|
20161
20275
|
init_init_template();
|
|
20162
20276
|
init_errors();
|
|
@@ -20170,7 +20284,8 @@ var init_validate = __esm({
|
|
|
20170
20284
|
if (driftWarning) process.stderr.write(driftWarning + "\n");
|
|
20171
20285
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
20172
20286
|
const deps = { data, logger: logger16 };
|
|
20173
|
-
|
|
20287
|
+
const retryLessonId = activeCourse.retryLessonId;
|
|
20288
|
+
let lessonId = retryLessonId ?? activeCourse.currentLessonId;
|
|
20174
20289
|
if (!lessonId) {
|
|
20175
20290
|
try {
|
|
20176
20291
|
const prog = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
@@ -20254,8 +20369,19 @@ var init_validate = __esm({
|
|
|
20254
20369
|
} else {
|
|
20255
20370
|
output(formatValidation(result), { json: false });
|
|
20256
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
|
+
}
|
|
20257
20380
|
process.exit(result.passed ? 0 : 1);
|
|
20258
20381
|
} catch (err) {
|
|
20382
|
+
if (err instanceof CliApiError && err.code === "LESSON_NOT_REACHED") {
|
|
20383
|
+
await clearRetryPin();
|
|
20384
|
+
}
|
|
20259
20385
|
if (isEnrollmentNotEntitledError(err)) {
|
|
20260
20386
|
const friendly = getErrors().enrollmentNotEntitled;
|
|
20261
20387
|
if (opts.json) jsonError("enrollment_not_entitled", { message: friendly });
|
|
@@ -20288,8 +20414,81 @@ var init_validate = __esm({
|
|
|
20288
20414
|
}
|
|
20289
20415
|
});
|
|
20290
20416
|
|
|
20291
|
-
// src/commands/
|
|
20417
|
+
// src/commands/retry.ts
|
|
20292
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";
|
|
20293
20492
|
var menuCommand;
|
|
20294
20493
|
var init_menu = __esm({
|
|
20295
20494
|
"src/commands/menu.ts"() {
|
|
@@ -20297,7 +20496,7 @@ var init_menu = __esm({
|
|
|
20297
20496
|
init_session_store();
|
|
20298
20497
|
init_workspace_state();
|
|
20299
20498
|
init_formatter();
|
|
20300
|
-
menuCommand = new
|
|
20499
|
+
menuCommand = new Command16("menu").description("Show available commands and current study context").action(async () => {
|
|
20301
20500
|
const session = await getSession();
|
|
20302
20501
|
const wsResult = session ? await findWorkspaceState() : null;
|
|
20303
20502
|
const activeCourse = wsResult?.state ?? null;
|
|
@@ -20474,7 +20673,7 @@ var init_learner_context = __esm({
|
|
|
20474
20673
|
});
|
|
20475
20674
|
|
|
20476
20675
|
// src/commands/init.ts
|
|
20477
|
-
import { Command as
|
|
20676
|
+
import { Command as Command17 } from "commander";
|
|
20478
20677
|
function isCompleteProfile(flags) {
|
|
20479
20678
|
return Boolean(
|
|
20480
20679
|
flags.segment && flags.company && flags.products && flags.region && flags.team && flags.goal && flags.level
|
|
@@ -20645,7 +20844,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
|
|
|
20645
20844
|
deps.output(artifacts.learnerBrief, { json: false });
|
|
20646
20845
|
}
|
|
20647
20846
|
}
|
|
20648
|
-
var
|
|
20847
|
+
var logger18, defaultDeps4, initCommand;
|
|
20649
20848
|
var init_init = __esm({
|
|
20650
20849
|
"src/commands/init.ts"() {
|
|
20651
20850
|
"use strict";
|
|
@@ -20662,7 +20861,7 @@ var init_init = __esm({
|
|
|
20662
20861
|
init_instruction_pipeline();
|
|
20663
20862
|
init_root_agents_consent();
|
|
20664
20863
|
init_pipeline_deps();
|
|
20665
|
-
|
|
20864
|
+
logger18 = createLogger("cli:init");
|
|
20666
20865
|
defaultDeps4 = {
|
|
20667
20866
|
// GH #1419 item 3 — `init` used the raw session, so an expired access
|
|
20668
20867
|
// token met the POST as a 401. Refresh through the guard when one exists.
|
|
@@ -20682,12 +20881,12 @@ var init_init = __esm({
|
|
|
20682
20881
|
saveCourseLearnerProfile,
|
|
20683
20882
|
buildInitArtifacts,
|
|
20684
20883
|
output,
|
|
20685
|
-
logger:
|
|
20884
|
+
logger: logger18,
|
|
20686
20885
|
createHttpProvider,
|
|
20687
20886
|
resolveAndGenerate,
|
|
20688
20887
|
buildPipelineDeps
|
|
20689
20888
|
};
|
|
20690
|
-
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(
|
|
20691
20890
|
"--root-agents",
|
|
20692
20891
|
"Add the ToStudy block to an existing AGENTS.md in this folder without asking"
|
|
20693
20892
|
).option("--json", "Output structured JSON").action(async (opts) => {
|
|
@@ -21015,11 +21214,11 @@ var init_workspace = __esm({
|
|
|
21015
21214
|
});
|
|
21016
21215
|
|
|
21017
21216
|
// src/commands/workspace.ts
|
|
21018
|
-
import { Command as
|
|
21217
|
+
import { Command as Command18 } from "commander";
|
|
21019
21218
|
import path17 from "node:path";
|
|
21020
21219
|
import os8 from "node:os";
|
|
21021
21220
|
import fs14 from "node:fs/promises";
|
|
21022
|
-
var
|
|
21221
|
+
var logger19, workspaceCommand;
|
|
21023
21222
|
var init_workspace2 = __esm({
|
|
21024
21223
|
"src/commands/workspace.ts"() {
|
|
21025
21224
|
"use strict";
|
|
@@ -21029,8 +21228,8 @@ var init_workspace2 = __esm({
|
|
|
21029
21228
|
init_course_state();
|
|
21030
21229
|
init_resolve();
|
|
21031
21230
|
init_errors();
|
|
21032
|
-
|
|
21033
|
-
workspaceCommand = new
|
|
21231
|
+
logger19 = createLogger("cli:workspace");
|
|
21232
|
+
workspaceCommand = new Command18("workspace").description(
|
|
21034
21233
|
getErrors().workspaceCommandDescription
|
|
21035
21234
|
);
|
|
21036
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) => {
|
|
@@ -21095,7 +21294,7 @@ Pr\xF3ximo passo: tostudy export
|
|
|
21095
21294
|
);
|
|
21096
21295
|
}
|
|
21097
21296
|
} catch (err) {
|
|
21098
|
-
|
|
21297
|
+
logger19.error("workspace setup failed", { error: err });
|
|
21099
21298
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21100
21299
|
`);
|
|
21101
21300
|
process.exit(1);
|
|
@@ -21199,11 +21398,11 @@ Pr\xF3ximo passo: tostudy export
|
|
|
21199
21398
|
});
|
|
21200
21399
|
|
|
21201
21400
|
// src/commands/export.ts
|
|
21202
|
-
import { Command as
|
|
21401
|
+
import { Command as Command19 } from "commander";
|
|
21203
21402
|
import path18 from "node:path";
|
|
21204
21403
|
import os9 from "node:os";
|
|
21205
21404
|
import fs15 from "node:fs/promises";
|
|
21206
|
-
var
|
|
21405
|
+
var logger20, exportCommand;
|
|
21207
21406
|
var init_export = __esm({
|
|
21208
21407
|
"src/commands/export.ts"() {
|
|
21209
21408
|
"use strict";
|
|
@@ -21214,8 +21413,8 @@ var init_export = __esm({
|
|
|
21214
21413
|
init_course_state();
|
|
21215
21414
|
init_resolve();
|
|
21216
21415
|
init_errors();
|
|
21217
|
-
|
|
21218
|
-
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) => {
|
|
21219
21418
|
try {
|
|
21220
21419
|
const session = await requireSession();
|
|
21221
21420
|
const activeCourse = await requireActiveCourse();
|
|
@@ -21242,7 +21441,7 @@ var init_export = __esm({
|
|
|
21242
21441
|
}
|
|
21243
21442
|
if (!hasConfig) {
|
|
21244
21443
|
const slug = courseSlug(activeCourse.courseTitle);
|
|
21245
|
-
|
|
21444
|
+
logger20.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
|
|
21246
21445
|
await fs15.mkdir(ws.workspacePath, { recursive: true });
|
|
21247
21446
|
for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
|
|
21248
21447
|
await fs15.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
|
|
@@ -21307,7 +21506,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
|
|
|
21307
21506
|
);
|
|
21308
21507
|
}
|
|
21309
21508
|
} catch (err) {
|
|
21310
|
-
|
|
21509
|
+
logger20.error("export failed", { error: err });
|
|
21311
21510
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21312
21511
|
`);
|
|
21313
21512
|
process.exit(1);
|
|
@@ -21317,11 +21516,11 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
|
|
|
21317
21516
|
});
|
|
21318
21517
|
|
|
21319
21518
|
// src/commands/open.ts
|
|
21320
|
-
import { Command as
|
|
21519
|
+
import { Command as Command20 } from "commander";
|
|
21321
21520
|
import { execFile as execFile3 } from "node:child_process";
|
|
21322
21521
|
import path19 from "node:path";
|
|
21323
21522
|
import os10 from "node:os";
|
|
21324
|
-
var
|
|
21523
|
+
var logger21, openCommand;
|
|
21325
21524
|
var init_open = __esm({
|
|
21326
21525
|
"src/commands/open.ts"() {
|
|
21327
21526
|
"use strict";
|
|
@@ -21330,8 +21529,8 @@ var init_open = __esm({
|
|
|
21330
21529
|
init_course_state();
|
|
21331
21530
|
init_resolve();
|
|
21332
21531
|
init_errors();
|
|
21333
|
-
|
|
21334
|
-
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) => {
|
|
21335
21534
|
try {
|
|
21336
21535
|
const activeCourse = await requireActiveCourse();
|
|
21337
21536
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId);
|
|
@@ -21348,7 +21547,7 @@ var init_open = __esm({
|
|
|
21348
21547
|
const editor = process.env["EDITOR"] ?? "code";
|
|
21349
21548
|
execFile3(editor, [ws.workspacePath], (err) => {
|
|
21350
21549
|
if (err) {
|
|
21351
|
-
|
|
21550
|
+
logger21.error("open failed", { editor, workspacePath: ws.workspacePath });
|
|
21352
21551
|
process.stderr.write(`\u274C Falha ao abrir: ${err.message}
|
|
21353
21552
|
`);
|
|
21354
21553
|
process.exit(1);
|
|
@@ -21357,7 +21556,7 @@ var init_open = __esm({
|
|
|
21357
21556
|
`);
|
|
21358
21557
|
});
|
|
21359
21558
|
} catch (err) {
|
|
21360
|
-
|
|
21559
|
+
logger21.error("open command failed", { error: err });
|
|
21361
21560
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21362
21561
|
`);
|
|
21363
21562
|
process.exit(1);
|
|
@@ -21426,11 +21625,11 @@ var init_vault = __esm({
|
|
|
21426
21625
|
});
|
|
21427
21626
|
|
|
21428
21627
|
// src/commands/vault.ts
|
|
21429
|
-
import { Command as
|
|
21628
|
+
import { Command as Command21 } from "commander";
|
|
21430
21629
|
import path21 from "node:path";
|
|
21431
21630
|
import os11 from "node:os";
|
|
21432
21631
|
import fs17 from "node:fs/promises";
|
|
21433
|
-
var
|
|
21632
|
+
var logger22, vaultCommand;
|
|
21434
21633
|
var init_vault2 = __esm({
|
|
21435
21634
|
"src/commands/vault.ts"() {
|
|
21436
21635
|
"use strict";
|
|
@@ -21443,8 +21642,8 @@ var init_vault2 = __esm({
|
|
|
21443
21642
|
init_course_state();
|
|
21444
21643
|
init_resolve();
|
|
21445
21644
|
init_errors();
|
|
21446
|
-
|
|
21447
|
-
vaultCommand = new
|
|
21645
|
+
logger22 = createLogger("cli:vault");
|
|
21646
|
+
vaultCommand = new Command21("vault").description("Gerenciar vault Obsidian do curso");
|
|
21448
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) => {
|
|
21449
21648
|
try {
|
|
21450
21649
|
const session = await requireSession();
|
|
@@ -21487,7 +21686,7 @@ var init_vault2 = __esm({
|
|
|
21487
21686
|
cliWorkspacePaths(workspacePath, activeCourse.courseTitle)
|
|
21488
21687
|
);
|
|
21489
21688
|
const result = await writeVaultFiles(files, vaultOutputPath, activeCourse.courseId, slug);
|
|
21490
|
-
|
|
21689
|
+
logger22.info("Vault generated", {
|
|
21491
21690
|
courseId: activeCourse.courseId,
|
|
21492
21691
|
vaultPath: result.vaultPath,
|
|
21493
21692
|
filesWritten: result.filesWritten
|
|
@@ -21520,7 +21719,7 @@ Para visualizar:
|
|
|
21520
21719
|
);
|
|
21521
21720
|
}
|
|
21522
21721
|
} catch (err) {
|
|
21523
|
-
|
|
21722
|
+
logger22.error("vault init failed", { error: err });
|
|
21524
21723
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21525
21724
|
`);
|
|
21526
21725
|
process.exit(1);
|
|
@@ -21550,7 +21749,7 @@ Para visualizar:
|
|
|
21550
21749
|
process.exit(1);
|
|
21551
21750
|
}
|
|
21552
21751
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
21553
|
-
const deps = { data, logger:
|
|
21752
|
+
const deps = { data, logger: logger22 };
|
|
21554
21753
|
const progress = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
21555
21754
|
const markerPath = path21.join(vaultPath, ".ana-vault.json");
|
|
21556
21755
|
const markerRaw = await fs17.readFile(markerPath, "utf-8");
|
|
@@ -21604,7 +21803,7 @@ Para visualizar:
|
|
|
21604
21803
|
);
|
|
21605
21804
|
}
|
|
21606
21805
|
} catch (err) {
|
|
21607
|
-
|
|
21806
|
+
logger22.error("vault sync failed", { error: err });
|
|
21608
21807
|
process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
|
|
21609
21808
|
`);
|
|
21610
21809
|
process.exit(1);
|
|
@@ -21614,7 +21813,7 @@ Para visualizar:
|
|
|
21614
21813
|
});
|
|
21615
21814
|
|
|
21616
21815
|
// src/commands/profile.ts
|
|
21617
|
-
import { Command as
|
|
21816
|
+
import { Command as Command22 } from "commander";
|
|
21618
21817
|
var profileCommand;
|
|
21619
21818
|
var init_profile = __esm({
|
|
21620
21819
|
"src/commands/profile.ts"() {
|
|
@@ -21622,7 +21821,7 @@ var init_profile = __esm({
|
|
|
21622
21821
|
init_guards();
|
|
21623
21822
|
init_course_state();
|
|
21624
21823
|
init_user_profile();
|
|
21625
|
-
profileCommand = new
|
|
21824
|
+
profileCommand = new Command22("profile").description("Show your learner profile for the active course").option("--json", "Output structured JSON").action(async (opts) => {
|
|
21626
21825
|
const activeCourse = await requireActiveCourse();
|
|
21627
21826
|
const onboarding = await getCourseOnboardingState(activeCourse.courseId);
|
|
21628
21827
|
const profile = onboarding?.learnerProfile ?? await getUserProfile();
|
|
@@ -21683,8 +21882,8 @@ var init_profile = __esm({
|
|
|
21683
21882
|
});
|
|
21684
21883
|
|
|
21685
21884
|
// src/commands/sync.ts
|
|
21686
|
-
import { Command as
|
|
21687
|
-
var
|
|
21885
|
+
import { Command as Command23 } from "commander";
|
|
21886
|
+
var logger23, syncCommand;
|
|
21688
21887
|
var init_sync = __esm({
|
|
21689
21888
|
"src/commands/sync.ts"() {
|
|
21690
21889
|
"use strict";
|
|
@@ -21695,8 +21894,8 @@ var init_sync = __esm({
|
|
|
21695
21894
|
init_workspace_state();
|
|
21696
21895
|
init_root_agents_consent();
|
|
21697
21896
|
init_pipeline_deps();
|
|
21698
|
-
|
|
21699
|
-
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(
|
|
21700
21899
|
"--all-runtimes",
|
|
21701
21900
|
"Write instruction files for every supported runtime, even undetected ones"
|
|
21702
21901
|
).option(
|
|
@@ -21760,7 +21959,7 @@ var init_sync = __esm({
|
|
|
21760
21959
|
} catch (err) {
|
|
21761
21960
|
const msg = err instanceof Error ? err.message : String(err);
|
|
21762
21961
|
if (msg.includes("process.exit")) return;
|
|
21763
|
-
|
|
21962
|
+
logger23.warn("sync failed", { error: msg });
|
|
21764
21963
|
error(msg);
|
|
21765
21964
|
}
|
|
21766
21965
|
});
|
|
@@ -21768,8 +21967,8 @@ var init_sync = __esm({
|
|
|
21768
21967
|
});
|
|
21769
21968
|
|
|
21770
21969
|
// src/commands/brief.ts
|
|
21771
|
-
import { Command as
|
|
21772
|
-
var
|
|
21970
|
+
import { Command as Command24 } from "commander";
|
|
21971
|
+
var logger24, briefCommand;
|
|
21773
21972
|
var init_brief = __esm({
|
|
21774
21973
|
"src/commands/brief.ts"() {
|
|
21775
21974
|
"use strict";
|
|
@@ -21778,8 +21977,8 @@ var init_brief = __esm({
|
|
|
21778
21977
|
init_cache();
|
|
21779
21978
|
init_api();
|
|
21780
21979
|
init_formatter();
|
|
21781
|
-
|
|
21782
|
-
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) => {
|
|
21783
21982
|
try {
|
|
21784
21983
|
const session = await requireSession();
|
|
21785
21984
|
const cached2 = await readBriefCache();
|
|
@@ -21812,7 +22011,7 @@ var init_brief = __esm({
|
|
|
21812
22011
|
}
|
|
21813
22012
|
output(lines.join("\n"), { json: false });
|
|
21814
22013
|
} catch (err) {
|
|
21815
|
-
|
|
22014
|
+
logger24.error("Failed to show brief", { err });
|
|
21816
22015
|
error(`Erro ao buscar brief: ${err instanceof Error ? err.message : String(err)}`);
|
|
21817
22016
|
}
|
|
21818
22017
|
});
|
|
@@ -21820,8 +22019,8 @@ var init_brief = __esm({
|
|
|
21820
22019
|
});
|
|
21821
22020
|
|
|
21822
22021
|
// src/commands/brief-create.ts
|
|
21823
|
-
import { Command as
|
|
21824
|
-
var
|
|
22022
|
+
import { Command as Command25 } from "commander";
|
|
22023
|
+
var logger25, briefCreateCommand;
|
|
21825
22024
|
var init_brief_create = __esm({
|
|
21826
22025
|
"src/commands/brief-create.ts"() {
|
|
21827
22026
|
"use strict";
|
|
@@ -21831,8 +22030,8 @@ var init_brief_create = __esm({
|
|
|
21831
22030
|
init_api();
|
|
21832
22031
|
init_cache();
|
|
21833
22032
|
init_formatter();
|
|
21834
|
-
|
|
21835
|
-
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 () => {
|
|
21836
22035
|
try {
|
|
21837
22036
|
const session = await requireSession();
|
|
21838
22037
|
const answers = await collectBootstrapAnswers({ userName: session.userName });
|
|
@@ -21856,7 +22055,7 @@ var init_brief_create = __esm({
|
|
|
21856
22055
|
];
|
|
21857
22056
|
output(doneLines.join("\n"), { json: false });
|
|
21858
22057
|
} catch (err) {
|
|
21859
|
-
|
|
22058
|
+
logger25.error("Failed to create brief", { err });
|
|
21860
22059
|
error(`Erro ao criar brief: ${err instanceof Error ? err.message : String(err)}`);
|
|
21861
22060
|
}
|
|
21862
22061
|
});
|
|
@@ -21864,7 +22063,7 @@ var init_brief_create = __esm({
|
|
|
21864
22063
|
});
|
|
21865
22064
|
|
|
21866
22065
|
// src/commands/brief-open.ts
|
|
21867
|
-
import { Command as
|
|
22066
|
+
import { Command as Command26 } from "commander";
|
|
21868
22067
|
import { execFile as execFile4 } from "node:child_process";
|
|
21869
22068
|
import { platform } from "node:process";
|
|
21870
22069
|
function openUrl(url2) {
|
|
@@ -21893,7 +22092,7 @@ var init_brief_open = __esm({
|
|
|
21893
22092
|
init_guards();
|
|
21894
22093
|
init_formatter();
|
|
21895
22094
|
BRIEF_URL = "https://tostudy.ai/student/settings/learner-brief";
|
|
21896
|
-
briefOpenCommand = new
|
|
22095
|
+
briefOpenCommand = new Command26("brief-open").description("Open the learner brief editor in your web browser").action(async () => {
|
|
21897
22096
|
await requireSession();
|
|
21898
22097
|
output(`Abrindo ${BRIEF_URL} no navegador...`, { json: false });
|
|
21899
22098
|
openUrl(BRIEF_URL);
|
|
@@ -21921,7 +22120,7 @@ async function saveModuleSummary(workspacePath, input2) {
|
|
|
21921
22120
|
""
|
|
21922
22121
|
].join("\n");
|
|
21923
22122
|
fs18.writeFileSync(filePath, header + input2.summary, { mode: 384 });
|
|
21924
|
-
|
|
22123
|
+
logger26.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
|
|
21925
22124
|
return filePath;
|
|
21926
22125
|
}
|
|
21927
22126
|
async function loadSessionContext(workspacePath) {
|
|
@@ -21942,19 +22141,19 @@ async function loadSessionContext(workspacePath) {
|
|
|
21942
22141
|
}
|
|
21943
22142
|
return { moduleSummaries: summaries };
|
|
21944
22143
|
}
|
|
21945
|
-
var
|
|
22144
|
+
var logger26;
|
|
21946
22145
|
var init_storage = __esm({
|
|
21947
22146
|
"src/sessions/storage.ts"() {
|
|
21948
22147
|
"use strict";
|
|
21949
22148
|
init_dist();
|
|
21950
|
-
|
|
22149
|
+
logger26 = createLogger("cli:sessions");
|
|
21951
22150
|
}
|
|
21952
22151
|
});
|
|
21953
22152
|
|
|
21954
22153
|
// src/commands/compact.ts
|
|
21955
22154
|
import fs19 from "node:fs";
|
|
21956
|
-
import { Command as
|
|
21957
|
-
var
|
|
22155
|
+
import { Command as Command27 } from "commander";
|
|
22156
|
+
var logger27, compactCommand;
|
|
21958
22157
|
var init_compact = __esm({
|
|
21959
22158
|
"src/commands/compact.ts"() {
|
|
21960
22159
|
"use strict";
|
|
@@ -21963,8 +22162,8 @@ var init_compact = __esm({
|
|
|
21963
22162
|
init_workspace_state();
|
|
21964
22163
|
init_storage();
|
|
21965
22164
|
init_formatter();
|
|
21966
|
-
|
|
21967
|
-
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) => {
|
|
21968
22167
|
try {
|
|
21969
22168
|
const activeCourse = await requireActiveCourse();
|
|
21970
22169
|
const ws = await findWorkspaceState();
|
|
@@ -21984,7 +22183,7 @@ var init_compact = __esm({
|
|
|
21984
22183
|
moduleTitle,
|
|
21985
22184
|
summary
|
|
21986
22185
|
});
|
|
21987
|
-
|
|
22186
|
+
logger27.debug("Compact summary saved", { moduleId, filePath });
|
|
21988
22187
|
if (opts.json) {
|
|
21989
22188
|
output({ saved: true, path: filePath, moduleId }, { json: true });
|
|
21990
22189
|
} else {
|
|
@@ -21993,7 +22192,7 @@ var init_compact = __esm({
|
|
|
21993
22192
|
} catch (err) {
|
|
21994
22193
|
const msg = err instanceof Error ? err.message : String(err);
|
|
21995
22194
|
if (msg.includes("process.exit")) return;
|
|
21996
|
-
|
|
22195
|
+
logger27.warn("compact failed", { error: msg });
|
|
21997
22196
|
if (opts.json) jsonError(msg);
|
|
21998
22197
|
error(msg);
|
|
21999
22198
|
}
|
|
@@ -22002,8 +22201,8 @@ var init_compact = __esm({
|
|
|
22002
22201
|
});
|
|
22003
22202
|
|
|
22004
22203
|
// src/commands/context.ts
|
|
22005
|
-
import { Command as
|
|
22006
|
-
var
|
|
22204
|
+
import { Command as Command28 } from "commander";
|
|
22205
|
+
var logger28, contextCommand;
|
|
22007
22206
|
var init_context = __esm({
|
|
22008
22207
|
"src/commands/context.ts"() {
|
|
22009
22208
|
"use strict";
|
|
@@ -22013,8 +22212,8 @@ var init_context = __esm({
|
|
|
22013
22212
|
init_course_state();
|
|
22014
22213
|
init_formatter();
|
|
22015
22214
|
init_errors();
|
|
22016
|
-
|
|
22017
|
-
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) => {
|
|
22018
22217
|
try {
|
|
22019
22218
|
const ws = await findWorkspaceState();
|
|
22020
22219
|
if (!ws) {
|
|
@@ -22044,7 +22243,7 @@ var init_context = __esm({
|
|
|
22044
22243
|
totalModulesCompleted: sessionCtx.moduleSummaries.length,
|
|
22045
22244
|
driftWarning: driftWarning ?? null
|
|
22046
22245
|
};
|
|
22047
|
-
|
|
22246
|
+
logger28.debug("Context loaded", {
|
|
22048
22247
|
courseId: ws.state.courseId,
|
|
22049
22248
|
moduleSummaries: sessionCtx.moduleSummaries.length
|
|
22050
22249
|
});
|
|
@@ -22058,7 +22257,7 @@ var init_context = __esm({
|
|
|
22058
22257
|
} catch (err) {
|
|
22059
22258
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22060
22259
|
if (msg.includes("process.exit")) return;
|
|
22061
|
-
|
|
22260
|
+
logger28.warn("context failed", { error: msg });
|
|
22062
22261
|
if (opts.json) jsonError(msg);
|
|
22063
22262
|
error(msg);
|
|
22064
22263
|
}
|
|
@@ -22067,8 +22266,8 @@ var init_context = __esm({
|
|
|
22067
22266
|
});
|
|
22068
22267
|
|
|
22069
22268
|
// src/commands/memory.ts
|
|
22070
|
-
import { Command as
|
|
22071
|
-
var
|
|
22269
|
+
import { Command as Command29 } from "commander";
|
|
22270
|
+
var logger29, memoryCommand;
|
|
22072
22271
|
var init_memory = __esm({
|
|
22073
22272
|
"src/commands/memory.ts"() {
|
|
22074
22273
|
"use strict";
|
|
@@ -22078,8 +22277,8 @@ var init_memory = __esm({
|
|
|
22078
22277
|
init_guards();
|
|
22079
22278
|
init_formatter();
|
|
22080
22279
|
init_errors();
|
|
22081
|
-
|
|
22082
|
-
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) => {
|
|
22083
22282
|
try {
|
|
22084
22283
|
const ws = await findWorkspaceState();
|
|
22085
22284
|
if (!ws) {
|
|
@@ -22102,7 +22301,7 @@ var init_memory = __esm({
|
|
|
22102
22301
|
} catch (err) {
|
|
22103
22302
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22104
22303
|
if (msg.includes("process.exit")) return;
|
|
22105
|
-
|
|
22304
|
+
logger29.warn("memory failed", { error: msg });
|
|
22106
22305
|
if (opts.json) jsonError(msg);
|
|
22107
22306
|
else error(msg);
|
|
22108
22307
|
}
|
|
@@ -22111,8 +22310,8 @@ var init_memory = __esm({
|
|
|
22111
22310
|
});
|
|
22112
22311
|
|
|
22113
22312
|
// src/commands/insight.ts
|
|
22114
|
-
import { Command as
|
|
22115
|
-
var
|
|
22313
|
+
import { Command as Command30 } from "commander";
|
|
22314
|
+
var logger30, VALID_TYPES, insightCommand;
|
|
22116
22315
|
var init_insight = __esm({
|
|
22117
22316
|
"src/commands/insight.ts"() {
|
|
22118
22317
|
"use strict";
|
|
@@ -22122,9 +22321,9 @@ var init_insight = __esm({
|
|
|
22122
22321
|
init_guards();
|
|
22123
22322
|
init_formatter();
|
|
22124
22323
|
init_errors();
|
|
22125
|
-
|
|
22324
|
+
logger30 = createLogger("cli:insight");
|
|
22126
22325
|
VALID_TYPES = ["difficulty", "breakthrough", "question"];
|
|
22127
|
-
insightCommand = new
|
|
22326
|
+
insightCommand = new Command30("insight").description(
|
|
22128
22327
|
"Persist a student cognitive insight (difficulty | breakthrough | question) into course memory"
|
|
22129
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) => {
|
|
22130
22329
|
try {
|
|
@@ -22164,7 +22363,7 @@ var init_insight = __esm({
|
|
|
22164
22363
|
} catch (err) {
|
|
22165
22364
|
const msg = err instanceof Error ? err.message : String(err);
|
|
22166
22365
|
if (msg.includes("process.exit")) return;
|
|
22167
|
-
|
|
22366
|
+
logger30.warn("insight failed", { error: msg });
|
|
22168
22367
|
if (opts.json) jsonError(msg);
|
|
22169
22368
|
else error(msg);
|
|
22170
22369
|
}
|
|
@@ -22173,11 +22372,11 @@ var init_insight = __esm({
|
|
|
22173
22372
|
});
|
|
22174
22373
|
|
|
22175
22374
|
// src/commands/level.ts
|
|
22176
|
-
import { Command as
|
|
22375
|
+
import { Command as Command31 } from "commander";
|
|
22177
22376
|
function isExerciseLevel2(value) {
|
|
22178
22377
|
return ALL_LEVELS.includes(value);
|
|
22179
22378
|
}
|
|
22180
|
-
var
|
|
22379
|
+
var logger31, ALL_LEVELS, LEVEL_LABELS2, levelCommand;
|
|
22181
22380
|
var init_level = __esm({
|
|
22182
22381
|
"src/commands/level.ts"() {
|
|
22183
22382
|
"use strict";
|
|
@@ -22186,7 +22385,7 @@ var init_level = __esm({
|
|
|
22186
22385
|
init_exercises();
|
|
22187
22386
|
init_guards();
|
|
22188
22387
|
init_formatter();
|
|
22189
|
-
|
|
22388
|
+
logger31 = createLogger("cli:level");
|
|
22190
22389
|
ALL_LEVELS = ["L0", "L1", "L2", "L3", "L4"];
|
|
22191
22390
|
LEVEL_LABELS2 = {
|
|
22192
22391
|
L0: "Pr\xE9-check (perguntas conceituais)",
|
|
@@ -22195,12 +22394,12 @@ var init_level = __esm({
|
|
|
22195
22394
|
L3: "Guiado (passo a passo + checkpoints)",
|
|
22196
22395
|
L4: "Desafio livre (folha em branco)"
|
|
22197
22396
|
};
|
|
22198
|
-
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) => {
|
|
22199
22398
|
try {
|
|
22200
22399
|
const session = await requireSession();
|
|
22201
22400
|
const activeCourse = await requireActiveCourse();
|
|
22202
22401
|
const data = createHttpProvider(session.apiUrl, session.token);
|
|
22203
|
-
const deps = { data, logger:
|
|
22402
|
+
const deps = { data, logger: logger31 };
|
|
22204
22403
|
if (!rawLevel) {
|
|
22205
22404
|
const levels = await getEnrollmentLevels({ enrollmentId: activeCourse.enrollmentId }, deps);
|
|
22206
22405
|
if (opts.json) {
|
|
@@ -22258,8 +22457,8 @@ var init_level = __esm({
|
|
|
22258
22457
|
});
|
|
22259
22458
|
|
|
22260
22459
|
// src/commands/theory.ts
|
|
22261
|
-
import { Command as
|
|
22262
|
-
var
|
|
22460
|
+
import { Command as Command32 } from "commander";
|
|
22461
|
+
var logger32, EXERCISE_LEVELS2, theoryCommand;
|
|
22263
22462
|
var init_theory = __esm({
|
|
22264
22463
|
"src/commands/theory.ts"() {
|
|
22265
22464
|
"use strict";
|
|
@@ -22267,9 +22466,9 @@ var init_theory = __esm({
|
|
|
22267
22466
|
init_guards();
|
|
22268
22467
|
init_formatter();
|
|
22269
22468
|
init_errors();
|
|
22270
|
-
|
|
22469
|
+
logger32 = createLogger("cli:theory");
|
|
22271
22470
|
EXERCISE_LEVELS2 = ["L0", "L1", "L2", "L3", "L4"];
|
|
22272
|
-
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) => {
|
|
22273
22472
|
try {
|
|
22274
22473
|
const session = await requireSession();
|
|
22275
22474
|
const active = await requireActiveCourse();
|
|
@@ -22306,7 +22505,7 @@ var init_theory = __esm({
|
|
|
22306
22505
|
currentLevel = levels.currentLevel;
|
|
22307
22506
|
}
|
|
22308
22507
|
const requestUrl = `${session.apiUrl}/api/cli/exercises/more-theory`;
|
|
22309
|
-
|
|
22508
|
+
logger32.debug("Requesting more theory", { lessonId, currentLevel });
|
|
22310
22509
|
const res = await fetch(requestUrl, {
|
|
22311
22510
|
method: "POST",
|
|
22312
22511
|
headers: {
|
|
@@ -22640,7 +22839,7 @@ var init_resolve_enrollment = __esm({
|
|
|
22640
22839
|
});
|
|
22641
22840
|
|
|
22642
22841
|
// src/commands/export-grades.ts
|
|
22643
|
-
import { Command as
|
|
22842
|
+
import { Command as Command33 } from "commander";
|
|
22644
22843
|
function isFormat(value) {
|
|
22645
22844
|
return SUPPORTED_FORMATS.includes(value);
|
|
22646
22845
|
}
|
|
@@ -22727,7 +22926,7 @@ async function runExportGrades(opts, deps = defaultDeps5) {
|
|
|
22727
22926
|
if (!rendered.endsWith("\n")) rendered += "\n";
|
|
22728
22927
|
deps.stdoutWrite(rendered);
|
|
22729
22928
|
}
|
|
22730
|
-
var
|
|
22929
|
+
var logger33, SUPPORTED_FORMATS, defaultDeps5, exportGradesCommand;
|
|
22731
22930
|
var init_export_grades = __esm({
|
|
22732
22931
|
"src/commands/export-grades.ts"() {
|
|
22733
22932
|
"use strict";
|
|
@@ -22735,7 +22934,7 @@ var init_export_grades = __esm({
|
|
|
22735
22934
|
init_guards();
|
|
22736
22935
|
init_grades();
|
|
22737
22936
|
init_resolve_enrollment();
|
|
22738
|
-
|
|
22937
|
+
logger33 = createLogger("cli:export-grades");
|
|
22739
22938
|
SUPPORTED_FORMATS = ["json", "csv", "md"];
|
|
22740
22939
|
defaultDeps5 = {
|
|
22741
22940
|
requireSession,
|
|
@@ -22748,10 +22947,10 @@ var init_export_grades = __esm({
|
|
|
22748
22947
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22749
22948
|
// process.exit is typed as `never` — wrap so the cast is local to one line.
|
|
22750
22949
|
exit: (code) => process.exit(code),
|
|
22751
|
-
logger:
|
|
22950
|
+
logger: logger33,
|
|
22752
22951
|
resolveBySlug: resolveEnrollmentBySlug
|
|
22753
22952
|
};
|
|
22754
|
-
exportGradesCommand = new
|
|
22953
|
+
exportGradesCommand = new Command33("export-grades").description("Exporta o hist\xF3rico de valida\xE7\xF5es (notas) do curso ativo").option(
|
|
22755
22954
|
"--course <slug>",
|
|
22756
22955
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, exporta o curso ativo."
|
|
22757
22956
|
).option("--format <json|csv|md>", "Formato de sa\xEDda (default: json)", "json").addHelpText(
|
|
@@ -22776,7 +22975,7 @@ C\xF3digos de sa\xEDda:
|
|
|
22776
22975
|
});
|
|
22777
22976
|
|
|
22778
22977
|
// src/commands/grades.ts
|
|
22779
|
-
import { Command as
|
|
22978
|
+
import { Command as Command34 } from "commander";
|
|
22780
22979
|
async function fetchAttempts(session, enrollmentId, deps) {
|
|
22781
22980
|
const url2 = `${session.apiUrl}/api/cli/validations/by-enrollment?enrollmentId=${encodeURIComponent(
|
|
22782
22981
|
enrollmentId
|
|
@@ -22811,15 +23010,13 @@ function summarizeAttempts(courseTitle, attempts) {
|
|
|
22811
23010
|
};
|
|
22812
23011
|
}
|
|
22813
23012
|
let passedCount = 0;
|
|
22814
|
-
let scoreSum = 0;
|
|
22815
|
-
let scoreCount = 0;
|
|
22816
23013
|
let latestTs = -Infinity;
|
|
22817
23014
|
let latestAt = attempts[0].attemptedAt;
|
|
23015
|
+
const bestByLesson = /* @__PURE__ */ new Map();
|
|
22818
23016
|
for (const a of attempts) {
|
|
22819
23017
|
if (a.passed) passedCount++;
|
|
22820
23018
|
if (a.score !== null) {
|
|
22821
|
-
|
|
22822
|
-
scoreCount++;
|
|
23019
|
+
bestByLesson.set(a.lessonId, Math.max(a.score, bestByLesson.get(a.lessonId) ?? -Infinity));
|
|
22823
23020
|
}
|
|
22824
23021
|
const t = (a.attemptedAt instanceof Date ? a.attemptedAt : new Date(a.attemptedAt)).getTime();
|
|
22825
23022
|
if (t > latestTs) {
|
|
@@ -22827,13 +23024,14 @@ function summarizeAttempts(courseTitle, attempts) {
|
|
|
22827
23024
|
latestAt = a.attemptedAt;
|
|
22828
23025
|
}
|
|
22829
23026
|
}
|
|
23027
|
+
const bestScores = [...bestByLesson.values()];
|
|
22830
23028
|
return {
|
|
22831
23029
|
courseTitle,
|
|
22832
23030
|
lastAttemptAt: latestAt,
|
|
22833
23031
|
attemptsCount: attempts.length,
|
|
22834
23032
|
passedCount,
|
|
22835
23033
|
failedCount: attempts.length - passedCount,
|
|
22836
|
-
averageScore:
|
|
23034
|
+
averageScore: bestScores.length === 0 ? null : bestScores.reduce((a, b) => a + b, 0) / bestScores.length
|
|
22837
23035
|
};
|
|
22838
23036
|
}
|
|
22839
23037
|
async function runGrades(opts, deps = defaultDeps6) {
|
|
@@ -22917,7 +23115,7 @@ async function runGrades(opts, deps = defaultDeps6) {
|
|
|
22917
23115
|
);
|
|
22918
23116
|
deps.stdoutWrite(formatGradesAllEnrollmentsTable(summaries) + "\n");
|
|
22919
23117
|
}
|
|
22920
|
-
var
|
|
23118
|
+
var logger34, defaultDeps6, gradesCommand;
|
|
22921
23119
|
var init_grades2 = __esm({
|
|
22922
23120
|
"src/commands/grades.ts"() {
|
|
22923
23121
|
"use strict";
|
|
@@ -22927,7 +23125,7 @@ var init_grades2 = __esm({
|
|
|
22927
23125
|
init_guards();
|
|
22928
23126
|
init_grades();
|
|
22929
23127
|
init_resolve_enrollment();
|
|
22930
|
-
|
|
23128
|
+
logger34 = createLogger("cli:grades");
|
|
22931
23129
|
defaultDeps6 = {
|
|
22932
23130
|
requireSession,
|
|
22933
23131
|
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
@@ -22937,12 +23135,12 @@ var init_grades2 = __esm({
|
|
|
22937
23135
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
22938
23136
|
stderrWrite: (message) => process.stderr.write(message),
|
|
22939
23137
|
exit: (code) => process.exit(code),
|
|
22940
|
-
logger:
|
|
23138
|
+
logger: logger34,
|
|
22941
23139
|
resolveBySlug: resolveEnrollmentBySlug,
|
|
22942
23140
|
buildDataProvider: createHttpProvider,
|
|
22943
23141
|
listCoursesFn: listCourses
|
|
22944
23142
|
};
|
|
22945
|
-
gradesCommand = new
|
|
23143
|
+
gradesCommand = new Command34("grades").description("Mostra um resumo das notas (todas as matr\xEDculas ou um curso espec\xEDfico)").option(
|
|
22946
23144
|
"--course <slug>",
|
|
22947
23145
|
"Slug do curso (gerado a partir do t\xEDtulo). Sem a flag, lista todos os cursos."
|
|
22948
23146
|
).addHelpText(
|
|
@@ -22966,7 +23164,7 @@ C\xF3digos de sa\xEDda:
|
|
|
22966
23164
|
});
|
|
22967
23165
|
|
|
22968
23166
|
// src/commands/review.ts
|
|
22969
|
-
import { Command as
|
|
23167
|
+
import { Command as Command35 } from "commander";
|
|
22970
23168
|
import { createInterface } from "node:readline/promises";
|
|
22971
23169
|
import { stdin as defaultStdin, stdout as defaultStdout } from "node:process";
|
|
22972
23170
|
async function defaultPromptLessonChoice(lessonIds) {
|
|
@@ -23102,7 +23300,7 @@ async function runReview(slug, opts, deps = defaultDeps7) {
|
|
|
23102
23300
|
}) + "\n"
|
|
23103
23301
|
);
|
|
23104
23302
|
}
|
|
23105
|
-
var
|
|
23303
|
+
var logger35, defaultDeps7, reviewCommand;
|
|
23106
23304
|
var init_review2 = __esm({
|
|
23107
23305
|
"src/commands/review.ts"() {
|
|
23108
23306
|
"use strict";
|
|
@@ -23110,7 +23308,7 @@ var init_review2 = __esm({
|
|
|
23110
23308
|
init_guards();
|
|
23111
23309
|
init_grades();
|
|
23112
23310
|
init_resolve_enrollment();
|
|
23113
|
-
|
|
23311
|
+
logger35 = createLogger("cli:review");
|
|
23114
23312
|
defaultDeps7 = {
|
|
23115
23313
|
requireSession,
|
|
23116
23314
|
// Lazy: ler `globalThis.fetch` no escopo do modulo executa no LOAD do arquivo,
|
|
@@ -23120,11 +23318,11 @@ var init_review2 = __esm({
|
|
|
23120
23318
|
stdoutWrite: (message) => process.stdout.write(message),
|
|
23121
23319
|
stderrWrite: (message) => process.stderr.write(message),
|
|
23122
23320
|
exit: (code) => process.exit(code),
|
|
23123
|
-
logger:
|
|
23321
|
+
logger: logger35,
|
|
23124
23322
|
resolveBySlug: resolveEnrollmentBySlug,
|
|
23125
23323
|
promptLessonChoice: defaultPromptLessonChoice
|
|
23126
23324
|
};
|
|
23127
|
-
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(
|
|
23128
23326
|
"after",
|
|
23129
23327
|
`
|
|
23130
23328
|
Exemplos:
|
|
@@ -23153,9 +23351,9 @@ __export(cli_exports, {
|
|
|
23153
23351
|
CLI_VERSION: () => CLI_VERSION,
|
|
23154
23352
|
createProgram: () => createProgram
|
|
23155
23353
|
});
|
|
23156
|
-
import { Command as
|
|
23354
|
+
import { Command as Command36 } from "commander";
|
|
23157
23355
|
function createProgram() {
|
|
23158
|
-
const program2 = new
|
|
23356
|
+
const program2 = new Command36();
|
|
23159
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(
|
|
23160
23358
|
"before",
|
|
23161
23359
|
[
|
|
@@ -23181,6 +23379,7 @@ function createProgram() {
|
|
|
23181
23379
|
program2.addCommand(knowledgeCommand);
|
|
23182
23380
|
program2.addCommand(hintCommand);
|
|
23183
23381
|
program2.addCommand(validateCommand);
|
|
23382
|
+
program2.addCommand(retryCommand);
|
|
23184
23383
|
program2.addCommand(levelCommand);
|
|
23185
23384
|
program2.addCommand(theoryCommand);
|
|
23186
23385
|
program2.addCommand(menuCommand);
|
|
@@ -23219,6 +23418,7 @@ var init_cli = __esm({
|
|
|
23219
23418
|
init_knowledge();
|
|
23220
23419
|
init_hint();
|
|
23221
23420
|
init_validate();
|
|
23421
|
+
init_retry();
|
|
23222
23422
|
init_menu();
|
|
23223
23423
|
init_init();
|
|
23224
23424
|
init_workspace2();
|