@tostudy-ai/cli 0.16.1 → 0.16.3
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 +245 -153
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1304,8 +1304,68 @@ var init_session_store = __esm({
|
|
|
1304
1304
|
}
|
|
1305
1305
|
});
|
|
1306
1306
|
|
|
1307
|
+
// ../../packages/tostudy-core/src/slug.ts
|
|
1308
|
+
function slugify(text, maxLength = 60) {
|
|
1309
|
+
return text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLength).replace(/-+$/, "");
|
|
1310
|
+
}
|
|
1311
|
+
var init_slug = __esm({
|
|
1312
|
+
"../../packages/tostudy-core/src/slug.ts"() {
|
|
1313
|
+
"use strict";
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
// ../../packages/tostudy-core/src/lessons/workspace-tokens.ts
|
|
1318
|
+
function resolveWorkspaceTokensInText(text, paths) {
|
|
1319
|
+
return text.replace(WORKSPACE_TOKEN, () => paths.workspace).replace(VAULT_TOKEN, () => paths.vault);
|
|
1320
|
+
}
|
|
1321
|
+
function resolveWorkspaceTokens(value, paths) {
|
|
1322
|
+
if (typeof value === "string") {
|
|
1323
|
+
return resolveWorkspaceTokensInText(value, paths);
|
|
1324
|
+
}
|
|
1325
|
+
if (Array.isArray(value)) {
|
|
1326
|
+
return value.map((item) => resolveWorkspaceTokens(item, paths));
|
|
1327
|
+
}
|
|
1328
|
+
if (value !== null && typeof value === "object") {
|
|
1329
|
+
const out = {};
|
|
1330
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1331
|
+
out[key] = resolveWorkspaceTokens(item, paths);
|
|
1332
|
+
}
|
|
1333
|
+
return out;
|
|
1334
|
+
}
|
|
1335
|
+
return value;
|
|
1336
|
+
}
|
|
1337
|
+
var WORKSPACE_TOKEN, VAULT_TOKEN;
|
|
1338
|
+
var init_workspace_tokens = __esm({
|
|
1339
|
+
"../../packages/tostudy-core/src/lessons/workspace-tokens.ts"() {
|
|
1340
|
+
"use strict";
|
|
1341
|
+
init_slug();
|
|
1342
|
+
WORKSPACE_TOKEN = /\$WORKSPACE(?![A-Za-z0-9_])/g;
|
|
1343
|
+
VAULT_TOKEN = /\$VAULT(?![A-Za-z0-9_])/g;
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
|
|
1307
1347
|
// src/output/formatter.ts
|
|
1348
|
+
var formatter_exports = {};
|
|
1349
|
+
__export(formatter_exports, {
|
|
1350
|
+
error: () => error,
|
|
1351
|
+
filterExerciseContent: () => filterExerciseContent,
|
|
1352
|
+
formatCourseList: () => formatCourseList,
|
|
1353
|
+
formatHint: () => formatHint,
|
|
1354
|
+
formatLesson: () => formatLesson,
|
|
1355
|
+
formatModuleStart: () => formatModuleStart,
|
|
1356
|
+
formatMoreTheory: () => formatMoreTheory,
|
|
1357
|
+
formatProgress: () => formatProgress,
|
|
1358
|
+
formatValidation: () => formatValidation,
|
|
1359
|
+
jsonError: () => jsonError,
|
|
1360
|
+
output: () => output,
|
|
1361
|
+
progressBar: () => progressBar,
|
|
1362
|
+
setWorkspaceTokenPaths: () => setWorkspaceTokenPaths
|
|
1363
|
+
});
|
|
1364
|
+
function setWorkspaceTokenPaths(paths) {
|
|
1365
|
+
workspaceTokenPaths = paths;
|
|
1366
|
+
}
|
|
1308
1367
|
function output(data, opts) {
|
|
1368
|
+
if (workspaceTokenPaths) data = resolveWorkspaceTokens(data, workspaceTokenPaths);
|
|
1309
1369
|
if (opts.json) {
|
|
1310
1370
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
1311
1371
|
} else if (typeof data === "string") {
|
|
@@ -1547,10 +1607,12 @@ function formatMoreTheory(data) {
|
|
|
1547
1607
|
lines.push("", "\u2192 tostudy lesson voltar ao exerc\xEDcio atual");
|
|
1548
1608
|
return lines.join("\n");
|
|
1549
1609
|
}
|
|
1550
|
-
var TIER_HEADERS, UNEVALUATED_CRITERION_SENTINEL;
|
|
1610
|
+
var workspaceTokenPaths, TIER_HEADERS, UNEVALUATED_CRITERION_SENTINEL;
|
|
1551
1611
|
var init_formatter = __esm({
|
|
1552
1612
|
"src/output/formatter.ts"() {
|
|
1553
1613
|
"use strict";
|
|
1614
|
+
init_workspace_tokens();
|
|
1615
|
+
workspaceTokenPaths = null;
|
|
1554
1616
|
TIER_HEADERS = [/^## Prática Guiada/im, /^## Prática Semi-?[Gg]uiada/im, /^## Desafio/im];
|
|
1555
1617
|
UNEVALUATED_CRITERION_SENTINEL = "Crit\xE9rio n\xE3o avaliado explicitamente; tratado como n\xE3o atendido.";
|
|
1556
1618
|
}
|
|
@@ -1978,16 +2040,6 @@ var init_instruction_template_v3 = __esm({
|
|
|
1978
2040
|
}
|
|
1979
2041
|
});
|
|
1980
2042
|
|
|
1981
|
-
// ../../packages/tostudy-core/src/slug.ts
|
|
1982
|
-
function slugify(text, maxLength = 60) {
|
|
1983
|
-
return text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLength).replace(/-+$/, "");
|
|
1984
|
-
}
|
|
1985
|
-
var init_slug = __esm({
|
|
1986
|
-
"../../packages/tostudy-core/src/slug.ts"() {
|
|
1987
|
-
"use strict";
|
|
1988
|
-
}
|
|
1989
|
-
});
|
|
1990
|
-
|
|
1991
2043
|
// src/workspace/runtime-registry.ts
|
|
1992
2044
|
var runtime_registry_exports = {};
|
|
1993
2045
|
__export(runtime_registry_exports, {
|
|
@@ -2691,6 +2743,15 @@ var init_course_drift = __esm({
|
|
|
2691
2743
|
});
|
|
2692
2744
|
|
|
2693
2745
|
// src/onboarding/course-state.ts
|
|
2746
|
+
var course_state_exports = {};
|
|
2747
|
+
__export(course_state_exports, {
|
|
2748
|
+
checkCourseDrift: () => checkCourseDrift,
|
|
2749
|
+
clearAllCourseOnboarding: () => clearAllCourseOnboarding,
|
|
2750
|
+
getCourseOnboardingState: () => getCourseOnboardingState,
|
|
2751
|
+
saveCourseLearnerProfile: () => saveCourseLearnerProfile,
|
|
2752
|
+
setCourseWorkspacePath: () => setCourseWorkspacePath,
|
|
2753
|
+
setLastInitCourseId: () => setLastInitCourseId
|
|
2754
|
+
});
|
|
2694
2755
|
import fs6 from "node:fs";
|
|
2695
2756
|
async function getCourseOnboardingState(courseId, configDir) {
|
|
2696
2757
|
const state = readCourseOnboardingState(configDir);
|
|
@@ -2834,6 +2895,120 @@ var init_logout = __esm({
|
|
|
2834
2895
|
}
|
|
2835
2896
|
});
|
|
2836
2897
|
|
|
2898
|
+
// src/workspace/resolve.ts
|
|
2899
|
+
var resolve_exports = {};
|
|
2900
|
+
__export(resolve_exports, {
|
|
2901
|
+
cliWorkspacePaths: () => cliWorkspacePaths,
|
|
2902
|
+
courseSlug: () => courseSlug,
|
|
2903
|
+
findCwdWorkspaceUpwards: () => findCwdWorkspaceUpwards,
|
|
2904
|
+
findExistingVault: () => findExistingVault,
|
|
2905
|
+
isCwdWorkspace: () => isCwdWorkspace,
|
|
2906
|
+
resolveActiveCourseFromWorkspace: () => resolveActiveCourseFromWorkspace,
|
|
2907
|
+
resolveActiveCourseWithOverride: () => resolveActiveCourseWithOverride,
|
|
2908
|
+
resolveCwdWorkspacePath: () => resolveCwdWorkspacePath,
|
|
2909
|
+
resolveEffectiveWorkspace: () => resolveEffectiveWorkspace,
|
|
2910
|
+
resolveVaultPath: () => resolveVaultPath,
|
|
2911
|
+
resolveWorkspace: () => resolveWorkspace
|
|
2912
|
+
});
|
|
2913
|
+
import fs7 from "node:fs/promises";
|
|
2914
|
+
import path9 from "node:path";
|
|
2915
|
+
import os6 from "node:os";
|
|
2916
|
+
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
2917
|
+
const slug = courseSlug(courseTitle);
|
|
2918
|
+
const candidate = path9.join(basePath, slug);
|
|
2919
|
+
try {
|
|
2920
|
+
await fs7.access(path9.join(candidate, ".ana-config.json"));
|
|
2921
|
+
return { found: true, workspacePath: candidate, source: "default" };
|
|
2922
|
+
} catch {
|
|
2923
|
+
return { found: false, workspacePath: null };
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
async function isCwdWorkspace(cwd = process.cwd()) {
|
|
2927
|
+
return await resolveCwdWorkspacePath(cwd) !== null;
|
|
2928
|
+
}
|
|
2929
|
+
async function resolveCwdWorkspacePath(cwd = process.cwd()) {
|
|
2930
|
+
const tostudyDir = path9.join(cwd, ".tostudy");
|
|
2931
|
+
try {
|
|
2932
|
+
const stat = await fs7.stat(tostudyDir);
|
|
2933
|
+
if (stat.isDirectory()) return tostudyDir;
|
|
2934
|
+
} catch {
|
|
2935
|
+
}
|
|
2936
|
+
try {
|
|
2937
|
+
await fs7.access(path9.join(cwd, ".ana-config.json"));
|
|
2938
|
+
return cwd;
|
|
2939
|
+
} catch {
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
|
|
2944
|
+
const home = path9.resolve(homeDir);
|
|
2945
|
+
let current = path9.resolve(startCwd);
|
|
2946
|
+
while (true) {
|
|
2947
|
+
if (current === home) return null;
|
|
2948
|
+
const direct = await resolveCwdWorkspacePath(current);
|
|
2949
|
+
if (direct) return direct;
|
|
2950
|
+
const parent = path9.dirname(current);
|
|
2951
|
+
if (parent === current) return null;
|
|
2952
|
+
current = parent;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
function resolveVaultPath(workspacePath, slug) {
|
|
2956
|
+
const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
|
|
2957
|
+
return path9.join(base, `vault-${slug}`);
|
|
2958
|
+
}
|
|
2959
|
+
function cliWorkspacePaths(workspacePath, courseTitle) {
|
|
2960
|
+
const base = path9.basename(workspacePath) === ".tostudy" ? path9.dirname(workspacePath) : workspacePath;
|
|
2961
|
+
return { workspace: base, vault: resolveVaultPath(workspacePath, courseSlug(courseTitle)) };
|
|
2962
|
+
}
|
|
2963
|
+
async function findExistingVault(workspacePath, slug) {
|
|
2964
|
+
const slugged = resolveVaultPath(workspacePath, slug);
|
|
2965
|
+
try {
|
|
2966
|
+
await fs7.access(path9.join(slugged, ".ana-vault.json"));
|
|
2967
|
+
return slugged;
|
|
2968
|
+
} catch {
|
|
2969
|
+
}
|
|
2970
|
+
const legacy = path9.join(workspacePath, "vault");
|
|
2971
|
+
try {
|
|
2972
|
+
await fs7.access(path9.join(legacy, ".ana-vault.json"));
|
|
2973
|
+
return legacy;
|
|
2974
|
+
} catch {
|
|
2975
|
+
return null;
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.cwd(), defaultBasePath = DEFAULT_BASE, homeDir = os6.homedir()) {
|
|
2979
|
+
const cwdWorkspace = await findCwdWorkspaceUpwards(cwd, homeDir);
|
|
2980
|
+
if (cwdWorkspace) {
|
|
2981
|
+
return { found: true, workspacePath: cwdWorkspace, source: "cwd" };
|
|
2982
|
+
}
|
|
2983
|
+
if (storedPath) {
|
|
2984
|
+
try {
|
|
2985
|
+
await fs7.access(path9.join(storedPath, ".ana-config.json"));
|
|
2986
|
+
return { found: true, workspacePath: storedPath, source: "stored" };
|
|
2987
|
+
} catch {
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
const result = await resolveWorkspace(courseTitle, defaultBasePath);
|
|
2991
|
+
if (result.found) {
|
|
2992
|
+
return { ...result, source: "default" };
|
|
2993
|
+
}
|
|
2994
|
+
return { found: false, workspacePath: null };
|
|
2995
|
+
}
|
|
2996
|
+
async function resolveActiveCourseFromWorkspace(options = {}) {
|
|
2997
|
+
const { findWorkspaceState: findWorkspaceState2 } = await Promise.resolve().then(() => (init_workspace_state(), workspace_state_exports));
|
|
2998
|
+
const result = await findWorkspaceState2(options.cwd, options.homeDir);
|
|
2999
|
+
return result?.state ?? null;
|
|
3000
|
+
}
|
|
3001
|
+
var DEFAULT_BASE, courseSlug, resolveActiveCourseWithOverride;
|
|
3002
|
+
var init_resolve = __esm({
|
|
3003
|
+
"src/workspace/resolve.ts"() {
|
|
3004
|
+
"use strict";
|
|
3005
|
+
init_slug();
|
|
3006
|
+
DEFAULT_BASE = path9.join(os6.homedir(), "study");
|
|
3007
|
+
courseSlug = slugify;
|
|
3008
|
+
resolveActiveCourseWithOverride = resolveActiveCourseFromWorkspace;
|
|
3009
|
+
}
|
|
3010
|
+
});
|
|
3011
|
+
|
|
2837
3012
|
// src/auth/guards.ts
|
|
2838
3013
|
async function refreshCliSession(session, configDir) {
|
|
2839
3014
|
try {
|
|
@@ -2880,10 +3055,32 @@ async function requireSession(configDir) {
|
|
|
2880
3055
|
async function requireActiveCourse() {
|
|
2881
3056
|
const { findWorkspaceState: findWorkspaceState2 } = await Promise.resolve().then(() => (init_workspace_state(), workspace_state_exports));
|
|
2882
3057
|
const result = await findWorkspaceState2();
|
|
2883
|
-
if (result)
|
|
3058
|
+
if (result) {
|
|
3059
|
+
await primeWorkspaceTokenPaths(result.state);
|
|
3060
|
+
return result.state;
|
|
3061
|
+
}
|
|
2884
3062
|
process.stderr.write("Erro: Nenhum workspace configurado. Rode: tostudy setup\n");
|
|
2885
3063
|
process.exit(3);
|
|
2886
3064
|
}
|
|
3065
|
+
async function primeWorkspaceTokenPaths(state) {
|
|
3066
|
+
try {
|
|
3067
|
+
const [
|
|
3068
|
+
{ setWorkspaceTokenPaths: setWorkspaceTokenPaths2 },
|
|
3069
|
+
{ resolveEffectiveWorkspace: resolveEffectiveWorkspace2, cliWorkspacePaths: cliWorkspacePaths2 },
|
|
3070
|
+
onboarding
|
|
3071
|
+
] = await Promise.all([
|
|
3072
|
+
Promise.resolve().then(() => (init_formatter(), formatter_exports)),
|
|
3073
|
+
Promise.resolve().then(() => (init_resolve(), resolve_exports)),
|
|
3074
|
+
Promise.resolve().then(() => (init_course_state(), course_state_exports))
|
|
3075
|
+
]);
|
|
3076
|
+
const stored = await onboarding.getCourseOnboardingState(state.courseId);
|
|
3077
|
+
const ws = await resolveEffectiveWorkspace2(state.courseTitle, stored?.workspacePath);
|
|
3078
|
+
setWorkspaceTokenPaths2(
|
|
3079
|
+
ws.found && ws.workspacePath ? cliWorkspacePaths2(ws.workspacePath, state.courseTitle) : null
|
|
3080
|
+
);
|
|
3081
|
+
} catch {
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
2887
3084
|
var init_guards = __esm({
|
|
2888
3085
|
"src/auth/guards.ts"() {
|
|
2889
3086
|
"use strict";
|
|
@@ -2999,7 +3196,7 @@ var CLI_VERSION;
|
|
|
2999
3196
|
var init_version = __esm({
|
|
3000
3197
|
"src/version.ts"() {
|
|
3001
3198
|
"use strict";
|
|
3002
|
-
CLI_VERSION = true ? "0.16.
|
|
3199
|
+
CLI_VERSION = true ? "0.16.3" : "0.7.1";
|
|
3003
3200
|
}
|
|
3004
3201
|
});
|
|
3005
3202
|
|
|
@@ -3013,13 +3210,13 @@ __export(update_checker_exports, {
|
|
|
3013
3210
|
readCache: () => readCache,
|
|
3014
3211
|
writeCache: () => writeCache
|
|
3015
3212
|
});
|
|
3016
|
-
import
|
|
3017
|
-
import
|
|
3213
|
+
import fs8 from "node:fs";
|
|
3214
|
+
import path10 from "node:path";
|
|
3018
3215
|
function readCache(configDir) {
|
|
3019
3216
|
try {
|
|
3020
|
-
const p =
|
|
3021
|
-
if (!
|
|
3022
|
-
return JSON.parse(
|
|
3217
|
+
const p = path10.join(configDir ?? getConfigDir(), CACHE_FILE);
|
|
3218
|
+
if (!fs8.existsSync(p)) return null;
|
|
3219
|
+
return JSON.parse(fs8.readFileSync(p, "utf-8"));
|
|
3023
3220
|
} catch {
|
|
3024
3221
|
return null;
|
|
3025
3222
|
}
|
|
@@ -3027,8 +3224,8 @@ function readCache(configDir) {
|
|
|
3027
3224
|
function writeCache(cache, configDir) {
|
|
3028
3225
|
try {
|
|
3029
3226
|
const dir = configDir ?? getConfigDir();
|
|
3030
|
-
|
|
3031
|
-
|
|
3227
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3228
|
+
fs8.writeFileSync(path10.join(dir, CACHE_FILE), JSON.stringify(cache));
|
|
3032
3229
|
} catch {
|
|
3033
3230
|
}
|
|
3034
3231
|
}
|
|
@@ -3098,17 +3295,17 @@ var init_update_checker = __esm({
|
|
|
3098
3295
|
});
|
|
3099
3296
|
|
|
3100
3297
|
// src/learner-brief/cache.ts
|
|
3101
|
-
import
|
|
3102
|
-
import
|
|
3298
|
+
import fs9 from "node:fs";
|
|
3299
|
+
import path11 from "node:path";
|
|
3103
3300
|
function resolveCachePath(configDir) {
|
|
3104
|
-
return
|
|
3301
|
+
return path11.join(configDir, "student-brief.json");
|
|
3105
3302
|
}
|
|
3106
3303
|
async function readBriefCache(configDir) {
|
|
3107
3304
|
const dir = configDir ?? getConfigDir();
|
|
3108
3305
|
const p = resolveCachePath(dir);
|
|
3109
|
-
if (!
|
|
3306
|
+
if (!fs9.existsSync(p)) return null;
|
|
3110
3307
|
try {
|
|
3111
|
-
const raw =
|
|
3308
|
+
const raw = fs9.readFileSync(p, "utf-8");
|
|
3112
3309
|
return JSON.parse(raw);
|
|
3113
3310
|
} catch {
|
|
3114
3311
|
return null;
|
|
@@ -3116,8 +3313,8 @@ async function readBriefCache(configDir) {
|
|
|
3116
3313
|
}
|
|
3117
3314
|
async function writeBriefCache(configDir, cache) {
|
|
3118
3315
|
const dir = configDir ?? getConfigDir();
|
|
3119
|
-
|
|
3120
|
-
|
|
3316
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
3317
|
+
fs9.writeFileSync(resolveCachePath(dir), JSON.stringify(cache, null, 2), { mode: 384 });
|
|
3121
3318
|
}
|
|
3122
3319
|
var init_cache = __esm({
|
|
3123
3320
|
"src/learner-brief/cache.ts"() {
|
|
@@ -3127,23 +3324,23 @@ var init_cache = __esm({
|
|
|
3127
3324
|
});
|
|
3128
3325
|
|
|
3129
3326
|
// src/tutor-persona/cache.ts
|
|
3130
|
-
import
|
|
3131
|
-
import
|
|
3327
|
+
import fs10 from "node:fs";
|
|
3328
|
+
import path12 from "node:path";
|
|
3132
3329
|
function resolveCachePath2(configDir) {
|
|
3133
|
-
return
|
|
3330
|
+
return path12.join(configDir, "tutor-personalities.json");
|
|
3134
3331
|
}
|
|
3135
3332
|
function readAll(configDir) {
|
|
3136
3333
|
const p = resolveCachePath2(configDir);
|
|
3137
|
-
if (!
|
|
3334
|
+
if (!fs10.existsSync(p)) return {};
|
|
3138
3335
|
try {
|
|
3139
|
-
return JSON.parse(
|
|
3336
|
+
return JSON.parse(fs10.readFileSync(p, "utf-8"));
|
|
3140
3337
|
} catch {
|
|
3141
3338
|
return {};
|
|
3142
3339
|
}
|
|
3143
3340
|
}
|
|
3144
3341
|
function writeAll(configDir, cache) {
|
|
3145
|
-
|
|
3146
|
-
|
|
3342
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
3343
|
+
fs10.writeFileSync(resolveCachePath2(configDir), JSON.stringify(cache, null, 2), { mode: 384 });
|
|
3147
3344
|
}
|
|
3148
3345
|
async function readPersonaCacheForCourse(configDir, courseId) {
|
|
3149
3346
|
const dir = configDir ?? getConfigDir();
|
|
@@ -3413,97 +3610,6 @@ var init_courses2 = __esm({
|
|
|
3413
3610
|
}
|
|
3414
3611
|
});
|
|
3415
3612
|
|
|
3416
|
-
// src/workspace/resolve.ts
|
|
3417
|
-
import fs10 from "node:fs/promises";
|
|
3418
|
-
import path12 from "node:path";
|
|
3419
|
-
import os6 from "node:os";
|
|
3420
|
-
function courseSlug(title) {
|
|
3421
|
-
return title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
3422
|
-
}
|
|
3423
|
-
async function resolveWorkspace(courseTitle, basePath = DEFAULT_BASE) {
|
|
3424
|
-
const slug = courseSlug(courseTitle);
|
|
3425
|
-
const candidate = path12.join(basePath, slug);
|
|
3426
|
-
try {
|
|
3427
|
-
await fs10.access(path12.join(candidate, ".ana-config.json"));
|
|
3428
|
-
return { found: true, workspacePath: candidate, source: "default" };
|
|
3429
|
-
} catch {
|
|
3430
|
-
return { found: false, workspacePath: null };
|
|
3431
|
-
}
|
|
3432
|
-
}
|
|
3433
|
-
async function isCwdWorkspace(cwd = process.cwd()) {
|
|
3434
|
-
return await resolveCwdWorkspacePath(cwd) !== null;
|
|
3435
|
-
}
|
|
3436
|
-
async function resolveCwdWorkspacePath(cwd = process.cwd()) {
|
|
3437
|
-
const tostudyDir = path12.join(cwd, ".tostudy");
|
|
3438
|
-
try {
|
|
3439
|
-
const stat = await fs10.stat(tostudyDir);
|
|
3440
|
-
if (stat.isDirectory()) return tostudyDir;
|
|
3441
|
-
} catch {
|
|
3442
|
-
}
|
|
3443
|
-
try {
|
|
3444
|
-
await fs10.access(path12.join(cwd, ".ana-config.json"));
|
|
3445
|
-
return cwd;
|
|
3446
|
-
} catch {
|
|
3447
|
-
return null;
|
|
3448
|
-
}
|
|
3449
|
-
}
|
|
3450
|
-
async function findCwdWorkspaceUpwards(startCwd = process.cwd(), homeDir = os6.homedir()) {
|
|
3451
|
-
const home = path12.resolve(homeDir);
|
|
3452
|
-
let current = path12.resolve(startCwd);
|
|
3453
|
-
while (true) {
|
|
3454
|
-
if (current === home) return null;
|
|
3455
|
-
const direct = await resolveCwdWorkspacePath(current);
|
|
3456
|
-
if (direct) return direct;
|
|
3457
|
-
const parent = path12.dirname(current);
|
|
3458
|
-
if (parent === current) return null;
|
|
3459
|
-
current = parent;
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
|
-
function resolveVaultPath(workspacePath, slug) {
|
|
3463
|
-
const base = path12.basename(workspacePath) === ".tostudy" ? path12.dirname(workspacePath) : workspacePath;
|
|
3464
|
-
return path12.join(base, `vault-${slug}`);
|
|
3465
|
-
}
|
|
3466
|
-
async function findExistingVault(workspacePath, slug) {
|
|
3467
|
-
const slugged = resolveVaultPath(workspacePath, slug);
|
|
3468
|
-
try {
|
|
3469
|
-
await fs10.access(path12.join(slugged, ".ana-vault.json"));
|
|
3470
|
-
return slugged;
|
|
3471
|
-
} catch {
|
|
3472
|
-
}
|
|
3473
|
-
const legacy = path12.join(workspacePath, "vault");
|
|
3474
|
-
try {
|
|
3475
|
-
await fs10.access(path12.join(legacy, ".ana-vault.json"));
|
|
3476
|
-
return legacy;
|
|
3477
|
-
} catch {
|
|
3478
|
-
return null;
|
|
3479
|
-
}
|
|
3480
|
-
}
|
|
3481
|
-
async function resolveEffectiveWorkspace(courseTitle, storedPath, cwd = process.cwd(), defaultBasePath = DEFAULT_BASE, homeDir = os6.homedir()) {
|
|
3482
|
-
const cwdWorkspace = await findCwdWorkspaceUpwards(cwd, homeDir);
|
|
3483
|
-
if (cwdWorkspace) {
|
|
3484
|
-
return { found: true, workspacePath: cwdWorkspace, source: "cwd" };
|
|
3485
|
-
}
|
|
3486
|
-
if (storedPath) {
|
|
3487
|
-
try {
|
|
3488
|
-
await fs10.access(path12.join(storedPath, ".ana-config.json"));
|
|
3489
|
-
return { found: true, workspacePath: storedPath, source: "stored" };
|
|
3490
|
-
} catch {
|
|
3491
|
-
}
|
|
3492
|
-
}
|
|
3493
|
-
const result = await resolveWorkspace(courseTitle, defaultBasePath);
|
|
3494
|
-
if (result.found) {
|
|
3495
|
-
return { ...result, source: "default" };
|
|
3496
|
-
}
|
|
3497
|
-
return { found: false, workspacePath: null };
|
|
3498
|
-
}
|
|
3499
|
-
var DEFAULT_BASE;
|
|
3500
|
-
var init_resolve = __esm({
|
|
3501
|
-
"src/workspace/resolve.ts"() {
|
|
3502
|
-
"use strict";
|
|
3503
|
-
DEFAULT_BASE = path12.join(os6.homedir(), "study");
|
|
3504
|
-
}
|
|
3505
|
-
});
|
|
3506
|
-
|
|
3507
3613
|
// src/onboarding/status.ts
|
|
3508
3614
|
async function getCourseOnboardingStatus(activeCourse, configDir, cwd = process.cwd()) {
|
|
3509
3615
|
const onboardingState = await getCourseOnboardingState(activeCourse.courseId, configDir);
|
|
@@ -4187,6 +4293,7 @@ var init_lessons = __esm({
|
|
|
4187
4293
|
init_get_hint();
|
|
4188
4294
|
init_start_module();
|
|
4189
4295
|
init_start_next_module();
|
|
4296
|
+
init_workspace_tokens();
|
|
4190
4297
|
}
|
|
4191
4298
|
});
|
|
4192
4299
|
|
|
@@ -4431,12 +4538,6 @@ function adjustTimeEstimate(type, baseMinutes) {
|
|
|
4431
4538
|
function isCheckpoint(title) {
|
|
4432
4539
|
return /^checkpoint/i.test(title.trim());
|
|
4433
4540
|
}
|
|
4434
|
-
function replacePlaceholders(text, workspacePath, courseTitle) {
|
|
4435
|
-
const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
|
|
4436
|
-
const slug = courseSlug(courseTitle);
|
|
4437
|
-
const vaultPath = resolveVaultPath(workspacePath, slug);
|
|
4438
|
-
return text.replace(/\$WORKSPACE(?![A-Za-z0-9_])/g, () => base).replace(/\$VAULT(?![A-Za-z0-9_])/g, () => vaultPath);
|
|
4439
|
-
}
|
|
4440
4541
|
function formatLessonContent(data, workspacePath, courseTitle) {
|
|
4441
4542
|
const time3 = adjustTimeEstimate(data.type, data.estimatedTimeMinutes);
|
|
4442
4543
|
const isPractical = data.type === "exercise" || isCheckpoint(data.title);
|
|
@@ -4488,6 +4589,7 @@ var init_lesson = __esm({
|
|
|
4488
4589
|
init_course_state();
|
|
4489
4590
|
init_formatter();
|
|
4490
4591
|
init_resolve();
|
|
4592
|
+
init_workspace_tokens();
|
|
4491
4593
|
init_errors();
|
|
4492
4594
|
logger13 = createLogger("cli:lesson");
|
|
4493
4595
|
lessonCommand = new Command11("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").option(
|
|
@@ -4523,22 +4625,6 @@ var init_lesson = __esm({
|
|
|
4523
4625
|
activeCourse.courseTitle,
|
|
4524
4626
|
onboardingState?.workspacePath
|
|
4525
4627
|
);
|
|
4526
|
-
if (ws.found && ws.workspacePath) {
|
|
4527
|
-
if (content.content) {
|
|
4528
|
-
content.content = replacePlaceholders(
|
|
4529
|
-
content.content,
|
|
4530
|
-
ws.workspacePath,
|
|
4531
|
-
activeCourse.courseTitle
|
|
4532
|
-
);
|
|
4533
|
-
}
|
|
4534
|
-
if (content.acceptanceCriteria) {
|
|
4535
|
-
content.acceptanceCriteria = replacePlaceholders(
|
|
4536
|
-
content.acceptanceCriteria,
|
|
4537
|
-
ws.workspacePath,
|
|
4538
|
-
activeCourse.courseTitle
|
|
4539
|
-
);
|
|
4540
|
-
}
|
|
4541
|
-
}
|
|
4542
4628
|
if (opts.json) {
|
|
4543
4629
|
output(content, { json: true });
|
|
4544
4630
|
} else {
|
|
@@ -20317,7 +20403,9 @@ function getStarterCode(structuredData) {
|
|
|
20317
20403
|
return data?.starterCode ?? null;
|
|
20318
20404
|
}
|
|
20319
20405
|
async function extractExercise(input2) {
|
|
20320
|
-
const {
|
|
20406
|
+
const { exerciseTier, workspacePath } = input2;
|
|
20407
|
+
const paths = input2.workspacePaths ?? { workspace: workspacePath, vault: "$VAULT" };
|
|
20408
|
+
const lessonData = resolveWorkspaceTokens(input2.lessonData, paths);
|
|
20321
20409
|
const moduleDir = `${padOrder(lessonData.moduleOrder)}-${lessonData.moduleSlug}`;
|
|
20322
20410
|
const lessonDir = `${padOrder(lessonData.lessonOrder)}-${lessonData.lessonSlug}`;
|
|
20323
20411
|
const exercisePath = path16.join(workspacePath, "exercises", moduleDir, lessonDir);
|
|
@@ -20435,6 +20523,7 @@ var init_extract_exercise = __esm({
|
|
|
20435
20523
|
"../../packages/tostudy-core/src/workspace/extract-exercise.ts"() {
|
|
20436
20524
|
"use strict";
|
|
20437
20525
|
init_templates();
|
|
20526
|
+
init_workspace_tokens();
|
|
20438
20527
|
}
|
|
20439
20528
|
});
|
|
20440
20529
|
|
|
@@ -20735,7 +20824,10 @@ var init_export = __esm({
|
|
|
20735
20824
|
const result = await extractExercise({
|
|
20736
20825
|
lessonData,
|
|
20737
20826
|
exerciseTier: tier,
|
|
20738
|
-
workspacePath: ws.workspacePath
|
|
20827
|
+
workspacePath: ws.workspacePath,
|
|
20828
|
+
// Same pair `output()` prints with (GH #1278), so the exercise files on
|
|
20829
|
+
// disk and the lesson text on screen name the same folder (GH #1284).
|
|
20830
|
+
workspacePaths: cliWorkspacePaths(ws.workspacePath, activeCourse.courseTitle)
|
|
20739
20831
|
});
|
|
20740
20832
|
if (opts.json) {
|
|
20741
20833
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
@@ -20885,6 +20977,7 @@ var init_vault2 = __esm({
|
|
|
20885
20977
|
"use strict";
|
|
20886
20978
|
init_dist();
|
|
20887
20979
|
init_vault();
|
|
20980
|
+
init_workspace_tokens();
|
|
20888
20981
|
init_courses();
|
|
20889
20982
|
init_http();
|
|
20890
20983
|
init_guards();
|
|
@@ -20930,12 +21023,11 @@ var init_vault2 = __esm({
|
|
|
20930
21023
|
if (!data.success) {
|
|
20931
21024
|
throw new Error("API returned success: false");
|
|
20932
21025
|
}
|
|
20933
|
-
const
|
|
21026
|
+
const files = resolveWorkspaceTokens(
|
|
20934
21027
|
data.files,
|
|
20935
|
-
|
|
20936
|
-
activeCourse.courseId,
|
|
20937
|
-
slug
|
|
21028
|
+
cliWorkspacePaths(workspacePath, activeCourse.courseTitle)
|
|
20938
21029
|
);
|
|
21030
|
+
const result = await writeVaultFiles(files, vaultOutputPath, activeCourse.courseId, slug);
|
|
20939
21031
|
logger21.info("Vault generated", {
|
|
20940
21032
|
courseId: activeCourse.courseId,
|
|
20941
21033
|
vaultPath: result.vaultPath,
|