@tostudy-ai/cli 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -41,137 +41,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
41
41
  mod
42
42
  ));
43
43
 
44
- // ../../node_modules/picocolors/picocolors.js
45
- var require_picocolors = __commonJS({
46
- "../../node_modules/picocolors/picocolors.js"(exports, module) {
47
- var p = process || {};
48
- var argv = p.argv || [];
49
- var env = p.env || {};
50
- var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
51
- var formatter = (open, close, replace = open) => (input2) => {
52
- let string4 = "" + input2, index2 = string4.indexOf(close, open.length);
53
- return ~index2 ? open + replaceClose(string4, close, replace, index2) + close : open + string4 + close;
54
- };
55
- var replaceClose = (string4, close, replace, index2) => {
56
- let result = "", cursor = 0;
57
- do {
58
- result += string4.substring(cursor, index2) + replace;
59
- cursor = index2 + close.length;
60
- index2 = string4.indexOf(close, cursor);
61
- } while (~index2);
62
- return result + string4.substring(cursor);
63
- };
64
- var createColors = (enabled = isColorSupported) => {
65
- let f = enabled ? formatter : () => String;
66
- return {
67
- isColorSupported: enabled,
68
- reset: f("\x1B[0m", "\x1B[0m"),
69
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
70
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
71
- italic: f("\x1B[3m", "\x1B[23m"),
72
- underline: f("\x1B[4m", "\x1B[24m"),
73
- inverse: f("\x1B[7m", "\x1B[27m"),
74
- hidden: f("\x1B[8m", "\x1B[28m"),
75
- strikethrough: f("\x1B[9m", "\x1B[29m"),
76
- black: f("\x1B[30m", "\x1B[39m"),
77
- red: f("\x1B[31m", "\x1B[39m"),
78
- green: f("\x1B[32m", "\x1B[39m"),
79
- yellow: f("\x1B[33m", "\x1B[39m"),
80
- blue: f("\x1B[34m", "\x1B[39m"),
81
- magenta: f("\x1B[35m", "\x1B[39m"),
82
- cyan: f("\x1B[36m", "\x1B[39m"),
83
- white: f("\x1B[37m", "\x1B[39m"),
84
- gray: f("\x1B[90m", "\x1B[39m"),
85
- bgBlack: f("\x1B[40m", "\x1B[49m"),
86
- bgRed: f("\x1B[41m", "\x1B[49m"),
87
- bgGreen: f("\x1B[42m", "\x1B[49m"),
88
- bgYellow: f("\x1B[43m", "\x1B[49m"),
89
- bgBlue: f("\x1B[44m", "\x1B[49m"),
90
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
91
- bgCyan: f("\x1B[46m", "\x1B[49m"),
92
- bgWhite: f("\x1B[47m", "\x1B[49m"),
93
- blackBright: f("\x1B[90m", "\x1B[39m"),
94
- redBright: f("\x1B[91m", "\x1B[39m"),
95
- greenBright: f("\x1B[92m", "\x1B[39m"),
96
- yellowBright: f("\x1B[93m", "\x1B[39m"),
97
- blueBright: f("\x1B[94m", "\x1B[39m"),
98
- magentaBright: f("\x1B[95m", "\x1B[39m"),
99
- cyanBright: f("\x1B[96m", "\x1B[39m"),
100
- whiteBright: f("\x1B[97m", "\x1B[39m"),
101
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
102
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
103
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
104
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
105
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
106
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
107
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
108
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
109
- };
110
- };
111
- module.exports = createColors();
112
- module.exports.createColors = createColors;
113
- }
114
- });
115
-
116
- // ../../node_modules/@parisgroup-ai/logger/dist/logger.base-DJVL2Rjl.mjs
117
- function formatJson(entry) {
118
- const { timestamp: timestamp2, level, message, service, environment, context, data, error: error49 } = entry;
119
- const output3 = {
120
- timestamp: timestamp2,
121
- level,
122
- message,
123
- service,
124
- environment,
125
- ...context,
126
- ...data
127
- };
128
- if (error49) output3.error = error49;
129
- return JSON.stringify(output3);
130
- }
131
- function formatTime(timestamp2) {
132
- const date6 = new Date(timestamp2);
133
- return `${String(date6.getUTCHours()).padStart(2, "0")}:${String(date6.getUTCMinutes()).padStart(2, "0")}:${String(date6.getUTCSeconds()).padStart(2, "0")}`;
134
- }
135
- function formatLevel(level) {
136
- const colorFn = LEVEL_COLORS[level];
137
- return colorFn(level.toUpperCase().padEnd(LEVEL_WIDTH));
138
- }
139
- function formatData(data) {
140
- const entries = Object.entries(data);
141
- if (entries.length === 0) return "";
142
- return "\n" + entries.map(([key, value]) => {
143
- const formatted = typeof value === "object" ? JSON.stringify(value) : String(value);
144
- return ` ${import_picocolors.default.dim(key + ":")} ${formatted}`;
145
- }).join("\n");
146
- }
147
- function formatPretty(entry) {
148
- const { timestamp: timestamp2, level, message, service, context, data, error: error49 } = entry;
149
- const time4 = formatTime(timestamp2);
150
- const lvl = formatLevel(level);
151
- const mod = context.module ? import_picocolors.default.dim(`[${context.module}]`) + " " : "";
152
- const svc = import_picocolors.default.dim(`[${service}]`) + " ";
153
- let output3 = `${import_picocolors.default.dim(time4)} ${lvl} ${svc}${mod}${message}`;
154
- const traceInfo = {};
155
- if (context.traceId) traceInfo.traceId = context.traceId;
156
- if (context.requestId && context.requestId !== context.traceId) traceInfo.requestId = context.requestId;
157
- if (context.caller) traceInfo.caller = context.caller;
158
- if (context.duration !== void 0) traceInfo.duration = `${context.duration}ms`;
159
- const allData = {
160
- ...traceInfo,
161
- ...data
162
- };
163
- if (Object.keys(allData).length > 0) output3 += formatData(allData);
164
- if (error49) {
165
- output3 += "\n" + import_picocolors.default.red(` ${error49.name}: ${error49.message}`);
166
- if (error49.code) output3 += import_picocolors.default.dim(` (${error49.code})`);
167
- if (error49.stack) {
168
- const stackLines = error49.stack.split("\n").slice(1, 4);
169
- output3 += "\n" + stackLines.map((l) => import_picocolors.default.dim(` ${l.trim()}`)).join("\n");
170
- }
171
- if (error49.cause) output3 += "\n" + import_picocolors.default.dim(` Caused by: ${error49.cause.name}: ${error49.cause.message}`);
172
- }
173
- return output3;
174
- }
44
+ // ../../node_modules/@parisgroup-ai/logger/dist/sanitize-u1LaSkAO.mjs
175
45
  function email(value) {
176
46
  if (!value || typeof value !== "string") return "[INVALID_EMAIL]";
177
47
  const atIndex = value.indexOf("@");
@@ -296,42 +166,9 @@ function headers(hdrs) {
296
166
  for (const key of Object.keys(sanitized)) if (sensitiveHeaders.some((h) => key.toLowerCase() === h)) sanitized[key] = "[REDACTED]";
297
167
  return sanitized;
298
168
  }
299
- function extractErrorInfo(error49, includeStack, visited = /* @__PURE__ */ new WeakSet()) {
300
- const info = {
301
- name: error49.name,
302
- message: error49.message
303
- };
304
- if (includeStack && error49.stack) info.stack = error49.stack;
305
- if ("code" in error49 && typeof error49.code === "string") info.code = error49.code;
306
- visited.add(error49);
307
- if (error49.cause instanceof Error) if (visited.has(error49.cause)) info.cause = {
308
- name: error49.cause.name,
309
- message: "[Circular]"
310
- };
311
- else info.cause = extractErrorInfo(error49.cause, includeStack, visited);
312
- return info;
313
- }
314
- var import_picocolors, LOG_LEVELS, LEVEL_COLORS, LEVEL_WIDTH, SENSITIVE_FIELDS, sanitize, BaseLogger;
315
- var init_logger_base_DJVL2Rjl = __esm({
316
- "../../node_modules/@parisgroup-ai/logger/dist/logger.base-DJVL2Rjl.mjs"() {
317
- import_picocolors = __toESM(require_picocolors(), 1);
318
- LOG_LEVELS = {
319
- trace: 0,
320
- debug: 1,
321
- info: 2,
322
- warn: 3,
323
- error: 4,
324
- fatal: 5
325
- };
326
- LEVEL_COLORS = {
327
- trace: import_picocolors.default.gray,
328
- debug: import_picocolors.default.cyan,
329
- info: import_picocolors.default.green,
330
- warn: import_picocolors.default.yellow,
331
- error: import_picocolors.default.red,
332
- fatal: (s) => import_picocolors.default.bgRed(import_picocolors.default.white(s))
333
- };
334
- LEVEL_WIDTH = 5;
169
+ var SENSITIVE_FIELDS, sanitize;
170
+ var init_sanitize_u1LaSkAO = __esm({
171
+ "../../node_modules/@parisgroup-ai/logger/dist/sanitize-u1LaSkAO.mjs"() {
335
172
  SENSITIVE_FIELDS = [
336
173
  "password",
337
174
  "senha",
@@ -398,6 +235,177 @@ var init_logger_base_DJVL2Rjl = __esm({
398
235
  headers,
399
236
  isSensitiveKey
400
237
  };
238
+ }
239
+ });
240
+
241
+ // ../../node_modules/picocolors/picocolors.js
242
+ var require_picocolors = __commonJS({
243
+ "../../node_modules/picocolors/picocolors.js"(exports, module) {
244
+ var p = process || {};
245
+ var argv = p.argv || [];
246
+ var env = p.env || {};
247
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
248
+ var formatter = (open, close, replace = open) => (input2) => {
249
+ let string4 = "" + input2, index2 = string4.indexOf(close, open.length);
250
+ return ~index2 ? open + replaceClose(string4, close, replace, index2) + close : open + string4 + close;
251
+ };
252
+ var replaceClose = (string4, close, replace, index2) => {
253
+ let result = "", cursor = 0;
254
+ do {
255
+ result += string4.substring(cursor, index2) + replace;
256
+ cursor = index2 + close.length;
257
+ index2 = string4.indexOf(close, cursor);
258
+ } while (~index2);
259
+ return result + string4.substring(cursor);
260
+ };
261
+ var createColors = (enabled = isColorSupported) => {
262
+ let f = enabled ? formatter : () => String;
263
+ return {
264
+ isColorSupported: enabled,
265
+ reset: f("\x1B[0m", "\x1B[0m"),
266
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
267
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
268
+ italic: f("\x1B[3m", "\x1B[23m"),
269
+ underline: f("\x1B[4m", "\x1B[24m"),
270
+ inverse: f("\x1B[7m", "\x1B[27m"),
271
+ hidden: f("\x1B[8m", "\x1B[28m"),
272
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
273
+ black: f("\x1B[30m", "\x1B[39m"),
274
+ red: f("\x1B[31m", "\x1B[39m"),
275
+ green: f("\x1B[32m", "\x1B[39m"),
276
+ yellow: f("\x1B[33m", "\x1B[39m"),
277
+ blue: f("\x1B[34m", "\x1B[39m"),
278
+ magenta: f("\x1B[35m", "\x1B[39m"),
279
+ cyan: f("\x1B[36m", "\x1B[39m"),
280
+ white: f("\x1B[37m", "\x1B[39m"),
281
+ gray: f("\x1B[90m", "\x1B[39m"),
282
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
283
+ bgRed: f("\x1B[41m", "\x1B[49m"),
284
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
285
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
286
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
287
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
288
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
289
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
290
+ blackBright: f("\x1B[90m", "\x1B[39m"),
291
+ redBright: f("\x1B[91m", "\x1B[39m"),
292
+ greenBright: f("\x1B[92m", "\x1B[39m"),
293
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
294
+ blueBright: f("\x1B[94m", "\x1B[39m"),
295
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
296
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
297
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
298
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
299
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
300
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
301
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
302
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
303
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
304
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
305
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
306
+ };
307
+ };
308
+ module.exports = createColors();
309
+ module.exports.createColors = createColors;
310
+ }
311
+ });
312
+
313
+ // ../../node_modules/@parisgroup-ai/logger/dist/logger.base-W8CRO9aE.mjs
314
+ function formatJson(entry) {
315
+ const { timestamp: timestamp2, level, message, service, environment, context, data, error: error49 } = entry;
316
+ const output3 = {
317
+ timestamp: timestamp2,
318
+ level,
319
+ message,
320
+ service,
321
+ environment,
322
+ ...context,
323
+ ...data
324
+ };
325
+ if (error49) output3.error = error49;
326
+ return JSON.stringify(output3);
327
+ }
328
+ function formatTime(timestamp2) {
329
+ const date6 = new Date(timestamp2);
330
+ return `${String(date6.getUTCHours()).padStart(2, "0")}:${String(date6.getUTCMinutes()).padStart(2, "0")}:${String(date6.getUTCSeconds()).padStart(2, "0")}`;
331
+ }
332
+ function formatLevel(level) {
333
+ const colorFn = LEVEL_COLORS[level];
334
+ return colorFn(level.toUpperCase().padEnd(LEVEL_WIDTH));
335
+ }
336
+ function formatData(data) {
337
+ const entries = Object.entries(data);
338
+ if (entries.length === 0) return "";
339
+ return "\n" + entries.map(([key, value]) => {
340
+ const formatted = typeof value === "object" ? JSON.stringify(value) : String(value);
341
+ return ` ${import_picocolors.default.dim(key + ":")} ${formatted}`;
342
+ }).join("\n");
343
+ }
344
+ function formatPretty(entry) {
345
+ const { timestamp: timestamp2, level, message, service, context, data, error: error49 } = entry;
346
+ const time4 = formatTime(timestamp2);
347
+ const lvl = formatLevel(level);
348
+ const mod = context.module ? import_picocolors.default.dim(`[${context.module}]`) + " " : "";
349
+ const svc = import_picocolors.default.dim(`[${service}]`) + " ";
350
+ let output3 = `${import_picocolors.default.dim(time4)} ${lvl} ${svc}${mod}${message}`;
351
+ const traceInfo = {};
352
+ if (context.traceId) traceInfo.traceId = context.traceId;
353
+ if (context.requestId && context.requestId !== context.traceId) traceInfo.requestId = context.requestId;
354
+ if (context.caller) traceInfo.caller = context.caller;
355
+ if (context.duration !== void 0) traceInfo.duration = `${context.duration}ms`;
356
+ const allData = {
357
+ ...traceInfo,
358
+ ...data
359
+ };
360
+ if (Object.keys(allData).length > 0) output3 += formatData(allData);
361
+ if (error49) {
362
+ output3 += "\n" + import_picocolors.default.red(` ${error49.name}: ${error49.message}`);
363
+ if (error49.code) output3 += import_picocolors.default.dim(` (${error49.code})`);
364
+ if (error49.stack) {
365
+ const stackLines = error49.stack.split("\n").slice(1, 4);
366
+ output3 += "\n" + stackLines.map((l) => import_picocolors.default.dim(` ${l.trim()}`)).join("\n");
367
+ }
368
+ if (error49.cause) output3 += "\n" + import_picocolors.default.dim(` Caused by: ${error49.cause.name}: ${error49.cause.message}`);
369
+ }
370
+ return output3;
371
+ }
372
+ function extractErrorInfo(error49, includeStack, visited = /* @__PURE__ */ new WeakSet()) {
373
+ const info = {
374
+ name: error49.name,
375
+ message: error49.message
376
+ };
377
+ if (includeStack && error49.stack) info.stack = error49.stack;
378
+ if ("code" in error49 && typeof error49.code === "string") info.code = error49.code;
379
+ visited.add(error49);
380
+ if (error49.cause instanceof Error) if (visited.has(error49.cause)) info.cause = {
381
+ name: error49.cause.name,
382
+ message: "[Circular]"
383
+ };
384
+ else info.cause = extractErrorInfo(error49.cause, includeStack, visited);
385
+ return info;
386
+ }
387
+ var import_picocolors, LOG_LEVELS, LEVEL_COLORS, LEVEL_WIDTH, BaseLogger;
388
+ var init_logger_base_W8CRO9aE = __esm({
389
+ "../../node_modules/@parisgroup-ai/logger/dist/logger.base-W8CRO9aE.mjs"() {
390
+ init_sanitize_u1LaSkAO();
391
+ import_picocolors = __toESM(require_picocolors(), 1);
392
+ LOG_LEVELS = {
393
+ trace: 0,
394
+ debug: 1,
395
+ info: 2,
396
+ warn: 3,
397
+ error: 4,
398
+ fatal: 5
399
+ };
400
+ LEVEL_COLORS = {
401
+ trace: import_picocolors.default.gray,
402
+ debug: import_picocolors.default.cyan,
403
+ info: import_picocolors.default.green,
404
+ warn: import_picocolors.default.yellow,
405
+ error: import_picocolors.default.red,
406
+ fatal: (s) => import_picocolors.default.bgRed(import_picocolors.default.white(s))
407
+ };
408
+ LEVEL_WIDTH = 5;
401
409
  BaseLogger = class {
402
410
  level;
403
411
  format;
@@ -639,7 +647,8 @@ function createLogger(module, options) {
639
647
  var asyncLocalStorage, ConsoleTransport, FileTransport, HttpTransport, SINGLETON_KEY, Logger, logger;
640
648
  var init_dist = __esm({
641
649
  "../../node_modules/@parisgroup-ai/logger/dist/index.mjs"() {
642
- init_logger_base_DJVL2Rjl();
650
+ init_logger_base_W8CRO9aE();
651
+ init_sanitize_u1LaSkAO();
643
652
  asyncLocalStorage = new AsyncLocalStorage();
644
653
  ConsoleTransport = class {
645
654
  log(entry, formatted) {
@@ -1575,13 +1584,27 @@ function detectIDEs(home = os3.homedir()) {
1575
1584
  detected: checkExists(path4.join(home, ".codex")),
1576
1585
  configPath: path4.join(home, ".codex", "config.toml")
1577
1586
  },
1578
- // OpenCode: ~/.opencode
1587
+ // OpenCode: ~/.opencode or XDG ~/.config/opencode
1579
1588
  {
1580
1589
  name: "OpenCode",
1581
1590
  id: "opencode",
1582
- detected: checkExists(path4.join(home, ".opencode")),
1591
+ detected: checkExists(path4.join(home, ".opencode")) || checkExists(path4.join(home, ".config", "opencode")),
1583
1592
  configPath: path4.join(home, ".opencode", "opencode.json")
1584
1593
  },
1594
+ // Grok CLI: ~/.grok
1595
+ {
1596
+ name: "Grok CLI",
1597
+ id: "grok",
1598
+ detected: checkExists(path4.join(home, ".grok")),
1599
+ configPath: path4.join(home, ".grok", "config.toml")
1600
+ },
1601
+ // Antigravity: ~/.gemini/antigravity-cli
1602
+ {
1603
+ name: "Antigravity",
1604
+ id: "antigravity",
1605
+ detected: checkExists(path4.join(home, ".gemini", "antigravity-cli")),
1606
+ configPath: path4.join(home, ".gemini", "antigravity-cli", "mcp_config.json")
1607
+ },
1585
1608
  // Manual (always available as fallback)
1586
1609
  {
1587
1610
  name: "Manual",
@@ -1598,6 +1621,19 @@ function detectIDEs(home = os3.homedir()) {
1598
1621
  } catch {
1599
1622
  }
1600
1623
  }
1624
+ for (const [id, bin] of [
1625
+ ["grok", "grok"],
1626
+ ["antigravity", "agy"]
1627
+ ]) {
1628
+ const ide = ides.find((i) => i.id === id);
1629
+ if (ide && !ide.detected) {
1630
+ try {
1631
+ execFileSync("which", [bin], { encoding: "utf-8" });
1632
+ ide.detected = true;
1633
+ } catch {
1634
+ }
1635
+ }
1636
+ }
1601
1637
  return ides;
1602
1638
  }
1603
1639
  var init_ide_detector = __esm({
@@ -1906,64 +1942,51 @@ var init_instruction_template_v3 = __esm({
1906
1942
  }
1907
1943
  });
1908
1944
 
1909
- // src/workspace/instruction-files.ts
1910
- var instruction_files_exports = {};
1911
- __export(instruction_files_exports, {
1912
- installUniversalCommand: () => installUniversalCommand,
1913
- slugify: () => slugify,
1914
- upsertRootAgentsBlock: () => upsertRootAgentsBlock,
1915
- writeInstructionFiles: () => writeInstructionFiles
1945
+ // src/workspace/runtime-registry.ts
1946
+ var runtime_registry_exports = {};
1947
+ __export(runtime_registry_exports, {
1948
+ RUNTIMES: () => RUNTIMES,
1949
+ detectedSlashHints: () => detectedSlashHints,
1950
+ upsertRootAgentsBlock: () => upsertRootAgentsBlock
1916
1951
  });
1952
+ import { execFileSync as execFileSync2 } from "node:child_process";
1917
1953
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
1918
- import { homedir } from "node:os";
1919
1954
  import { join as join2 } from "node:path";
1920
- function slugify(title) {
1921
- return title.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
1955
+ function hasBinary(name) {
1956
+ try {
1957
+ execFileSync2("which", [name], { encoding: "utf-8", stdio: "pipe" });
1958
+ return true;
1959
+ } catch {
1960
+ return false;
1961
+ }
1922
1962
  }
1923
- function writeInstructionFiles(cwd, ctx, content) {
1924
- const slug = slugify(ctx.courseTitle);
1925
- const written = [];
1926
- const claudeDir = join2(cwd, ".claude", "commands");
1927
- if (!existsSync2(claudeDir)) {
1928
- mkdirSync2(claudeDir, { recursive: true });
1929
- }
1930
- const claudeFile = `tostudy-${slug}.md`;
1931
- writeFileSync2(join2(claudeDir, claudeFile), content);
1932
- written.push(`.claude/commands/${claudeFile}`);
1933
- const cursorDir = join2(cwd, ".cursor", "rules");
1934
- if (existsSync2(join2(cwd, ".cursor")) || existsSync2(cursorDir)) {
1935
- if (!existsSync2(cursorDir)) {
1936
- mkdirSync2(cursorDir, { recursive: true });
1937
- }
1938
- const mdcContent = `---
1939
- description: ${ctx.courseTitle} \u2014 ToStudy Course Guide
1963
+ function writeFile2(absDir, fileName, body) {
1964
+ mkdirSync2(absDir, { recursive: true });
1965
+ writeFileSync2(join2(absDir, fileName), body);
1966
+ }
1967
+ function cursorMdc(title, content) {
1968
+ return `---
1969
+ description: ${title} \u2014 ToStudy Course Guide
1940
1970
  globs: ["**/*"]
1941
1971
  alwaysApply: true
1942
1972
  ---
1943
1973
 
1944
1974
  ${content}`;
1945
- const cursorFile = `tostudy-${slug}.mdc`;
1946
- writeFileSync2(join2(cursorDir, cursorFile), mdcContent);
1947
- written.push(`.cursor/rules/${cursorFile}`);
1948
- }
1949
- const courseDir = join2(cwd, ".tostudy", "courses", slug);
1950
- if (!existsSync2(courseDir)) {
1951
- mkdirSync2(courseDir, { recursive: true });
1952
- }
1953
- writeFileSync2(join2(courseDir, "AGENTS.md"), content);
1954
- written.push(`.tostudy/courses/${slug}/AGENTS.md`);
1955
- const rootAgents = upsertRootAgentsBlock(cwd, ctx);
1956
- if (rootAgents) {
1957
- written.push(rootAgents);
1958
- }
1959
- logger2.info("Generated instruction files (v3)", {
1960
- slug,
1961
- files: written
1962
- });
1963
- return written;
1975
+ }
1976
+ function antigravitySkillMd(name, description, content) {
1977
+ return `---
1978
+ name: ${name}
1979
+ description: >-
1980
+ ${description}
1981
+ ---
1982
+
1983
+ ${content}`;
1984
+ }
1985
+ function detectedSlashHints(slug, home, cwd) {
1986
+ return RUNTIMES.filter((r) => r.slashHint && r.detect(home, cwd)).map((r) => r.slashHint(slug));
1964
1987
  }
1965
1988
  function upsertRootAgentsBlock(cwd, ctx) {
1966
- const slug = slugify(ctx.courseTitle);
1989
+ const slug = slugifyTitle(ctx.courseTitle);
1967
1990
  const block = [
1968
1991
  "<!-- tostudy:start -->",
1969
1992
  "",
@@ -2007,67 +2030,198 @@ function upsertRootAgentsBlock(cwd, ctx) {
2007
2030
  return null;
2008
2031
  }
2009
2032
  }
2010
- function installUniversalCommand(platform2, cwd = process.cwd(), homeDir = homedir()) {
2011
- const content = renderUniversalInstruction();
2012
- const written = [];
2013
- if (platform2 === "claude") {
2014
- const dir = join2(cwd, ".claude", "commands");
2015
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
2016
- writeFileSync2(join2(dir, "tostudy.md"), content);
2017
- written.push(".claude/commands/tostudy.md");
2018
- }
2019
- if (platform2 === "codex") {
2020
- const dir = join2(homeDir, ".codex", "prompts");
2021
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
2022
- const file2 = join2(dir, "tostudy.md");
2023
- writeFileSync2(file2, content);
2024
- written.push(file2);
2025
- }
2026
- if (platform2 === "opencode") {
2027
- const dir = join2(homeDir, ".config", "opencode", "command");
2028
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
2029
- const file2 = join2(dir, "tostudy.md");
2030
- writeFileSync2(
2031
- file2,
2032
- `---
2033
- description: ToStudy \u2014 AI Tutor Guide
2033
+ function slugifyTitle(title) {
2034
+ return title.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
2035
+ }
2036
+ var logger2, detectOpencode, detectGrok, detectAntigravity, RUNTIMES, TOSTUDY_BLOCK_PATTERN, TOSTUDY_BLOCK_PATTERN_GLOBAL;
2037
+ var init_runtime_registry = __esm({
2038
+ "src/workspace/runtime-registry.ts"() {
2039
+ "use strict";
2040
+ init_dist();
2041
+ logger2 = createLogger("cli:runtime-registry");
2042
+ detectOpencode = (home) => existsSync2(join2(home, ".opencode")) || existsSync2(join2(home, ".config", "opencode"));
2043
+ detectGrok = (home) => existsSync2(join2(home, ".grok")) || hasBinary("grok");
2044
+ detectAntigravity = (home) => existsSync2(join2(home, ".gemini", "antigravity-cli")) || hasBinary("agy");
2045
+ RUNTIMES = [
2046
+ {
2047
+ id: "claude",
2048
+ name: "Claude Code",
2049
+ detect: () => hasBinary("claude"),
2050
+ writePerCourse({ cwd, slug, content }) {
2051
+ writeFile2(join2(cwd, ".claude", "commands"), `tostudy-${slug}.md`, content);
2052
+ return [`.claude/commands/tostudy-${slug}.md`];
2053
+ },
2054
+ writeUniversal({ cwd, content }) {
2055
+ writeFile2(join2(cwd, ".claude", "commands"), "tostudy.md", content);
2056
+ return [".claude/commands/tostudy.md"];
2057
+ },
2058
+ slashHint: (slug) => `No Claude Code, digite: /tostudy-${slug}`
2059
+ },
2060
+ {
2061
+ id: "cursor",
2062
+ name: "Cursor",
2063
+ detect: (home, cwd) => existsSync2(join2(home, ".cursor")) || existsSync2(join2(cwd, ".cursor")),
2064
+ writePerCourse({ cwd, slug, ctx, content, force }) {
2065
+ if (!force && !existsSync2(join2(cwd, ".cursor"))) return [];
2066
+ writeFile2(
2067
+ join2(cwd, ".cursor", "rules"),
2068
+ `tostudy-${slug}.mdc`,
2069
+ cursorMdc(ctx.courseTitle, content)
2070
+ );
2071
+ return [`.cursor/rules/tostudy-${slug}.mdc`];
2072
+ },
2073
+ writeUniversal({ cwd, content }) {
2074
+ writeFile2(join2(cwd, ".cursor", "rules"), "tostudy.mdc", cursorMdc("ToStudy", content));
2075
+ return [".cursor/rules/tostudy.mdc"];
2076
+ }
2077
+ },
2078
+ {
2079
+ id: "codex",
2080
+ name: "Codex",
2081
+ detect: (home) => existsSync2(join2(home, ".codex")),
2082
+ // No writePerCourse: Codex has no project-level command channel — the
2083
+ // root AGENTS.md managed block (generic entry) is its per-course path.
2084
+ writeUniversal({ home, content }) {
2085
+ const file2 = join2(home, ".codex", "prompts", "tostudy.md");
2086
+ writeFile2(join2(home, ".codex", "prompts"), "tostudy.md", content);
2087
+ return [file2];
2088
+ }
2089
+ },
2090
+ {
2091
+ id: "opencode",
2092
+ name: "OpenCode",
2093
+ detect: (home) => detectOpencode(home),
2094
+ writePerCourse({ cwd, home, slug, ctx, content, force }) {
2095
+ if (!force && !detectOpencode(home)) return [];
2096
+ const body = `---
2097
+ description: ${ctx.courseTitle} \u2014 ToStudy Course Guide
2034
2098
  ---
2035
2099
 
2036
- ${content}`
2037
- );
2038
- written.push(file2);
2039
- }
2040
- if (platform2 === "cursor") {
2041
- const dir = join2(cwd, ".cursor", "rules");
2042
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
2043
- const mdcContent = `---
2100
+ ${content}`;
2101
+ writeFile2(join2(cwd, ".opencode", "command"), `tostudy-${slug}.md`, body);
2102
+ return [`.opencode/command/tostudy-${slug}.md`];
2103
+ },
2104
+ writeUniversal({ home, content }) {
2105
+ const dir = join2(home, ".config", "opencode", "command");
2106
+ const body = `---
2044
2107
  description: ToStudy \u2014 AI Tutor Guide
2045
- globs: ["**/*"]
2046
- alwaysApply: true
2047
2108
  ---
2048
2109
 
2049
2110
  ${content}`;
2050
- writeFileSync2(join2(dir, "tostudy.mdc"), mdcContent);
2051
- written.push(".cursor/rules/tostudy.mdc");
2052
- }
2053
- if (platform2 === "generic") {
2054
- const dir = join2(cwd, ".tostudy");
2055
- if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
2056
- writeFileSync2(join2(dir, "AGENTS.md"), content);
2057
- written.push(".tostudy/AGENTS.md");
2111
+ writeFile2(dir, "tostudy.md", body);
2112
+ return [join2(dir, "tostudy.md")];
2113
+ },
2114
+ slashHint: (slug) => `No OpenCode, digite: /tostudy-${slug}`
2115
+ },
2116
+ {
2117
+ id: "grok",
2118
+ name: "Grok CLI",
2119
+ detect: (home) => detectGrok(home),
2120
+ writePerCourse({ cwd, home, slug, content, force }) {
2121
+ if (!force && !detectGrok(home)) return [];
2122
+ writeFile2(join2(cwd, ".grok", "commands"), `tostudy-${slug}.md`, content);
2123
+ return [`.grok/commands/tostudy-${slug}.md`];
2124
+ },
2125
+ writeUniversal({ home, content }) {
2126
+ const dir = join2(home, ".grok", "commands");
2127
+ writeFile2(dir, "tostudy.md", content);
2128
+ return [join2(dir, "tostudy.md")];
2129
+ },
2130
+ slashHint: (slug) => `No Grok, digite: /tostudy-${slug}`
2131
+ },
2132
+ {
2133
+ id: "antigravity",
2134
+ name: "Antigravity",
2135
+ detect: (home) => detectAntigravity(home),
2136
+ writePerCourse({ cwd, home, slug, ctx, content, force }) {
2137
+ if (!force && !detectAntigravity(home)) return [];
2138
+ writeFile2(
2139
+ join2(cwd, ".agents", "skills", `tostudy-${slug}`),
2140
+ "SKILL.md",
2141
+ antigravitySkillMd(
2142
+ `tostudy-${slug}`,
2143
+ `${ctx.courseTitle} \u2014 ToStudy Course Guide. Use when the student asks to study, continue the course, or runs tostudy commands.`,
2144
+ content
2145
+ )
2146
+ );
2147
+ return [`.agents/skills/tostudy-${slug}/SKILL.md`];
2148
+ },
2149
+ writeUniversal({ cwd, content }) {
2150
+ writeFile2(
2151
+ join2(cwd, ".agents", "skills", "tostudy"),
2152
+ "SKILL.md",
2153
+ antigravitySkillMd(
2154
+ "tostudy",
2155
+ "ToStudy \u2014 AI Tutor Guide. Use when the student asks to study or runs tostudy commands.",
2156
+ content
2157
+ )
2158
+ );
2159
+ return [".agents/skills/tostudy/SKILL.md"];
2160
+ },
2161
+ slashHint: (slug) => `No Antigravity, a skill tostudy-${slug} ativa automaticamente`
2162
+ },
2163
+ {
2164
+ id: "generic",
2165
+ name: "Generic (AGENTS.md)",
2166
+ // Windsurf is the remaining consumer of the generic universal install.
2167
+ detect: (home) => existsSync2(join2(home, ".codeium", "windsurf")),
2168
+ writePerCourse({ cwd, slug, ctx, content }) {
2169
+ writeFile2(join2(cwd, ".tostudy", "courses", slug), "AGENTS.md", content);
2170
+ const written = [`.tostudy/courses/${slug}/AGENTS.md`];
2171
+ const rootAgents = upsertRootAgentsBlock(cwd, ctx);
2172
+ if (rootAgents) written.push(rootAgents);
2173
+ return written;
2174
+ },
2175
+ writeUniversal({ cwd, content }) {
2176
+ writeFile2(join2(cwd, ".tostudy"), "AGENTS.md", content);
2177
+ return [".tostudy/AGENTS.md"];
2178
+ }
2179
+ }
2180
+ ];
2181
+ TOSTUDY_BLOCK_PATTERN = /<!-- tostudy:start -->[\s\S]*?<!-- tostudy:end -->/;
2182
+ TOSTUDY_BLOCK_PATTERN_GLOBAL = /<!-- tostudy:start -->[\s\S]*?<!-- tostudy:end -->/g;
2058
2183
  }
2059
- logger2.info("Installed universal /tostudy command", { platform: platform2, files: written });
2184
+ });
2185
+
2186
+ // src/workspace/instruction-files.ts
2187
+ var instruction_files_exports = {};
2188
+ __export(instruction_files_exports, {
2189
+ installUniversalCommand: () => installUniversalCommand,
2190
+ slugify: () => slugify,
2191
+ upsertRootAgentsBlock: () => upsertRootAgentsBlock,
2192
+ writeInstructionFiles: () => writeInstructionFiles
2193
+ });
2194
+ import { homedir } from "node:os";
2195
+ function slugify(title) {
2196
+ return title.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
2197
+ }
2198
+ function writeInstructionFiles(cwd, ctx, content, opts = {}) {
2199
+ const home = opts.home ?? homedir();
2200
+ const force = opts.allRuntimes ?? false;
2201
+ const slug = slugify(ctx.courseTitle);
2202
+ const written = RUNTIMES.flatMap(
2203
+ (r) => r.writePerCourse?.({ cwd, home, slug, ctx, content, force }) ?? []
2204
+ );
2205
+ logger3.info("Generated instruction files (v3)", { slug, files: written });
2206
+ return written;
2207
+ }
2208
+ function installUniversalCommand(platform2, cwd = process.cwd(), homeDir = homedir()) {
2209
+ const entry = RUNTIMES.find((r) => r.id === platform2);
2210
+ if (!entry?.writeUniversal) return [];
2211
+ const content = renderUniversalInstruction();
2212
+ const written = entry.writeUniversal({ cwd, home: homeDir, content });
2213
+ logger3.info("Installed universal /tostudy command", { platform: platform2, files: written });
2060
2214
  return written;
2061
2215
  }
2062
- var logger2, TOSTUDY_BLOCK_PATTERN, TOSTUDY_BLOCK_PATTERN_GLOBAL;
2216
+ var logger3;
2063
2217
  var init_instruction_files = __esm({
2064
2218
  "src/workspace/instruction-files.ts"() {
2065
2219
  "use strict";
2066
2220
  init_dist();
2067
2221
  init_instruction_template_v3();
2068
- logger2 = createLogger("cli:instruction-files");
2069
- TOSTUDY_BLOCK_PATTERN = /<!-- tostudy:start -->[\s\S]*?<!-- tostudy:end -->/;
2070
- TOSTUDY_BLOCK_PATTERN_GLOBAL = /<!-- tostudy:start -->[\s\S]*?<!-- tostudy:end -->/g;
2222
+ init_runtime_registry();
2223
+ init_runtime_registry();
2224
+ logger3 = createLogger("cli:instruction-files");
2071
2225
  }
2072
2226
  });
2073
2227
 
@@ -2140,16 +2294,7 @@ async function loginWithMagicLink(magicLinkUrl, apiUrl) {
2140
2294
  await persistSession(data, apiUrl);
2141
2295
  return data;
2142
2296
  }
2143
- function ideToPlatform(ideName) {
2144
- const lower = ideName.toLowerCase();
2145
- if (lower.includes("claude")) return "claude";
2146
- if (lower.includes("codex")) return "codex";
2147
- if (lower.includes("cursor")) return "cursor";
2148
- if (lower.includes("opencode")) return "opencode";
2149
- if (lower.includes("windsurf") || lower.includes("antigravity")) return "generic";
2150
- return null;
2151
- }
2152
- var logger3, DEFAULT_API_URL, PORT, loginCommand;
2297
+ var logger4, DEFAULT_API_URL, PORT, loginCommand;
2153
2298
  var init_login = __esm({
2154
2299
  "src/commands/login.ts"() {
2155
2300
  "use strict";
@@ -2159,7 +2304,7 @@ var init_login = __esm({
2159
2304
  init_oauth_server();
2160
2305
  init_session_store();
2161
2306
  init_formatter();
2162
- logger3 = createLogger("cli:login");
2307
+ logger4 = createLogger("cli:login");
2163
2308
  DEFAULT_API_URL = "https://tostudy.ai";
2164
2309
  PORT = 9876;
2165
2310
  loginCommand = new Command("login").description("Autentica no ToStudy via browser").option("--manual", "Login manual (sem browser)").option("--code <code>", "Login n\xE3o-interativo via c\xF3digo de autentica\xE7\xE3o").option("--magic-link <url>", "Login via URL de magic-link (recupera\xE7\xE3o)").option("--api-url <url>", "API URL override", DEFAULT_API_URL).action(async (opts) => {
@@ -2224,6 +2369,8 @@ var init_login = __esm({
2224
2369
  try {
2225
2370
  const { detectIDEs: detectIDEs2 } = await Promise.resolve().then(() => (init_ide_detector(), ide_detector_exports));
2226
2371
  const { installUniversalCommand: installUniversalCommand2 } = await Promise.resolve().then(() => (init_instruction_files(), instruction_files_exports));
2372
+ const { RUNTIMES: RUNTIMES2 } = await Promise.resolve().then(() => (init_runtime_registry(), runtime_registry_exports));
2373
+ const { homedir: homedir2 } = await import("node:os");
2227
2374
  const ides = detectIDEs2();
2228
2375
  const detected = ides.filter((ide) => ide.detected);
2229
2376
  if (detected.length > 0) {
@@ -2231,27 +2378,26 @@ var init_login = __esm({
2231
2378
  for (const ide of detected) {
2232
2379
  console.log(` \u2713 ${ide.name} detectado`);
2233
2380
  }
2234
- const cwd = process.cwd();
2235
- const installedFiles = [];
2236
- for (const ide of detected) {
2237
- const platform2 = ideToPlatform(ide.name);
2238
- if (platform2) {
2239
- const files = installUniversalCommand2(platform2, cwd);
2240
- installedFiles.push(...files);
2241
- }
2381
+ }
2382
+ const cwd = process.cwd();
2383
+ const home = homedir2();
2384
+ const installedFiles = [];
2385
+ for (const runtime of RUNTIMES2) {
2386
+ if (runtime.writeUniversal && runtime.detect(home, cwd)) {
2387
+ installedFiles.push(...installUniversalCommand2(runtime.id, cwd));
2242
2388
  }
2243
- if (installedFiles.length > 0) {
2244
- console.log("");
2245
- for (const file2 of [...new Set(installedFiles)]) {
2246
- console.log(` \u2713 Instalado: ${file2}`);
2247
- }
2389
+ }
2390
+ if (installedFiles.length > 0) {
2391
+ console.log("");
2392
+ for (const file2 of [...new Set(installedFiles)]) {
2393
+ console.log(` \u2713 Instalado: ${file2}`);
2248
2394
  }
2249
2395
  }
2250
2396
  } catch {
2251
2397
  }
2252
2398
  try {
2253
2399
  const data = createHttpProvider(apiUrl, token2);
2254
- const courses3 = await listCourses({ userId }, { data, logger: logger3 });
2400
+ const courses3 = await listCourses({ userId }, { data, logger: logger4 });
2255
2401
  if (courses3.length > 0) {
2256
2402
  console.log(`
2257
2403
  \u2713 ${courses3.length} curso(s) matriculado(s)`);
@@ -2775,16 +2921,16 @@ var init_setup = __esm({
2775
2921
  });
2776
2922
 
2777
2923
  // src/installer/node-detector.ts
2778
- import { execFileSync as execFileSync2 } from "node:child_process";
2924
+ import { execFileSync as execFileSync3 } from "node:child_process";
2779
2925
  function detectNode() {
2780
2926
  try {
2781
- const version3 = execFileSync2("node", ["--version"], {
2927
+ const version3 = execFileSync3("node", ["--version"], {
2782
2928
  encoding: "utf-8"
2783
2929
  }).trim();
2784
2930
  let nodePath = null;
2785
2931
  try {
2786
2932
  const whichCmd = process.platform === "win32" ? "where" : "which";
2787
- nodePath = execFileSync2(whichCmd, ["node"], { encoding: "utf-8" }).trim();
2933
+ nodePath = execFileSync3(whichCmd, ["node"], { encoding: "utf-8" }).trim();
2788
2934
  } catch {
2789
2935
  }
2790
2936
  const major = parseInt(version3.replace("v", ""), 10);
@@ -2804,7 +2950,7 @@ var CLI_VERSION;
2804
2950
  var init_version = __esm({
2805
2951
  "src/version.ts"() {
2806
2952
  "use strict";
2807
- CLI_VERSION = true ? "0.13.0" : "0.7.1";
2953
+ CLI_VERSION = true ? "0.15.0" : "0.7.1";
2808
2954
  }
2809
2955
  });
2810
2956
 
@@ -2814,22 +2960,24 @@ __export(update_checker_exports, {
2814
2960
  checkForUpdates: () => checkForUpdates,
2815
2961
  checkVersionSync: () => checkVersionSync,
2816
2962
  fetchLatestVersion: () => fetchLatestVersion,
2817
- isNewerVersion: () => isNewerVersion
2963
+ isNewerVersion: () => isNewerVersion,
2964
+ readCache: () => readCache,
2965
+ writeCache: () => writeCache
2818
2966
  });
2819
2967
  import fs7 from "node:fs";
2820
2968
  import path9 from "node:path";
2821
- function readCache() {
2969
+ function readCache(configDir) {
2822
2970
  try {
2823
- const p = path9.join(getConfigDir(), CACHE_FILE);
2971
+ const p = path9.join(configDir ?? getConfigDir(), CACHE_FILE);
2824
2972
  if (!fs7.existsSync(p)) return null;
2825
2973
  return JSON.parse(fs7.readFileSync(p, "utf-8"));
2826
2974
  } catch {
2827
2975
  return null;
2828
2976
  }
2829
2977
  }
2830
- function writeCache(cache) {
2978
+ function writeCache(cache, configDir) {
2831
2979
  try {
2832
- const dir = getConfigDir();
2980
+ const dir = configDir ?? getConfigDir();
2833
2981
  fs7.mkdirSync(dir, { recursive: true });
2834
2982
  fs7.writeFileSync(path9.join(dir, CACHE_FILE), JSON.stringify(cache));
2835
2983
  } catch {
@@ -2993,14 +3141,14 @@ var init_doctor = __esm({
2993
3141
  git: null
2994
3142
  };
2995
3143
  try {
2996
- const { execFileSync: execFileSync3 } = await import("node:child_process");
2997
- envChecks["pnpm"] = execFileSync3("pnpm", ["--version"], { encoding: "utf-8" }).trim();
3144
+ const { execFileSync: execFileSync4 } = await import("node:child_process");
3145
+ envChecks["pnpm"] = execFileSync4("pnpm", ["--version"], { encoding: "utf-8" }).trim();
2998
3146
  } catch {
2999
3147
  envChecks["pnpm"] = null;
3000
3148
  }
3001
3149
  try {
3002
- const { execFileSync: execFileSync3 } = await import("node:child_process");
3003
- envChecks["git"] = execFileSync3("git", ["--version"], { encoding: "utf-8" }).trim().replace("git version ", "");
3150
+ const { execFileSync: execFileSync4 } = await import("node:child_process");
3151
+ envChecks["git"] = execFileSync4("git", ["--version"], { encoding: "utf-8" }).trim().replace("git version ", "");
3004
3152
  } catch {
3005
3153
  envChecks["git"] = null;
3006
3154
  }
@@ -3179,7 +3327,7 @@ var init_doctor = __esm({
3179
3327
 
3180
3328
  // src/commands/courses.ts
3181
3329
  import { Command as Command5 } from "commander";
3182
- var logger4, coursesCommand;
3330
+ var logger5, coursesCommand;
3183
3331
  var init_courses2 = __esm({
3184
3332
  "src/commands/courses.ts"() {
3185
3333
  "use strict";
@@ -3188,12 +3336,12 @@ var init_courses2 = __esm({
3188
3336
  init_http();
3189
3337
  init_guards();
3190
3338
  init_formatter();
3191
- logger4 = createLogger("cli:courses");
3339
+ logger5 = createLogger("cli:courses");
3192
3340
  coursesCommand = new Command5("courses").description("List your enrolled courses with progress").option("--json", "Output structured JSON").option("--mine", "List only your own courses, including drafts").action(async (opts) => {
3193
3341
  try {
3194
3342
  const session = await requireSession();
3195
3343
  const data = createHttpProvider(session.apiUrl, session.token);
3196
- const deps = { data, logger: logger4 };
3344
+ const deps = { data, logger: logger5 };
3197
3345
  let coursesToList;
3198
3346
  if (opts.mine) {
3199
3347
  coursesToList = await data.courses.listCreatorCourses({
@@ -3525,7 +3673,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3525
3673
  });
3526
3674
  }
3527
3675
  } catch (err) {
3528
- logger5.warn("T0 fetch failed \u2014 rendering without persona", { err });
3676
+ logger6.warn("T0 fetch failed \u2014 rendering without persona", { err });
3529
3677
  tutorPersona = null;
3530
3678
  }
3531
3679
  }
@@ -3549,7 +3697,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3549
3697
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
3550
3698
  });
3551
3699
  } catch (err) {
3552
- logger5.warn("T1 fetch failed \u2014 rendering without brief", { err });
3700
+ logger6.warn("T1 fetch failed \u2014 rendering without brief", { err });
3553
3701
  }
3554
3702
  }
3555
3703
  if (!studentBrief && options.collectT1Interactive) {
@@ -3569,7 +3717,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3569
3717
  });
3570
3718
  bootstrapped = "t1";
3571
3719
  } catch (err) {
3572
- logger5.warn("T1 bootstrap failed", { err });
3720
+ logger6.warn("T1 bootstrap failed", { err });
3573
3721
  }
3574
3722
  }
3575
3723
  let enrollmentProfile = null;
@@ -3583,7 +3731,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3583
3731
  enrollmentProfile = onboarding.learnerProfile;
3584
3732
  }
3585
3733
  } catch (err) {
3586
- logger5.warn("T2 fetch failed \u2014 rendering without enrollment profile", { err });
3734
+ logger6.warn("T2 fetch failed \u2014 rendering without enrollment profile", { err });
3587
3735
  }
3588
3736
  const content = renderCourseInstruction({
3589
3737
  course: {
@@ -3607,7 +3755,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3607
3755
  courseTitle: courseMeta.courseTitle
3608
3756
  });
3609
3757
  } catch (err) {
3610
- logger5.warn("Failed to write workspace marker", { err });
3758
+ logger6.warn("Failed to write workspace marker", { err });
3611
3759
  }
3612
3760
  if (markerPath) {
3613
3761
  try {
@@ -3617,7 +3765,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3617
3765
  lastInitCourseId: input2.courseId
3618
3766
  });
3619
3767
  } catch (err) {
3620
- logger5.warn("Failed to update workspace state v2 fields", {
3768
+ logger6.warn("Failed to update workspace state v2 fields", {
3621
3769
  error: err instanceof Error ? err.message : String(err)
3622
3770
  });
3623
3771
  }
@@ -3632,7 +3780,7 @@ async function resolveAndGenerate(input2, options = {}, context) {
3632
3780
  renderedInstruction: content
3633
3781
  };
3634
3782
  }
3635
- var logger5, defaultDeps2;
3783
+ var logger6, defaultDeps2;
3636
3784
  var init_instruction_pipeline = __esm({
3637
3785
  "src/instruction-pipeline.ts"() {
3638
3786
  "use strict";
@@ -3647,7 +3795,7 @@ var init_instruction_pipeline = __esm({
3647
3795
  init_api3();
3648
3796
  init_session_store();
3649
3797
  init_instruction_template_v3();
3650
- logger5 = createLogger("cli:instruction-pipeline");
3798
+ logger6 = createLogger("cli:instruction-pipeline");
3651
3799
  defaultDeps2 = {
3652
3800
  fetchTutorPersona: fetchTutorPersonality,
3653
3801
  fetchLearnerBrief,
@@ -3670,7 +3818,7 @@ var init_instruction_pipeline = __esm({
3670
3818
  async function fetchCourseMetaViaHttp(input2) {
3671
3819
  const { session, courseId, enrollmentId } = input2;
3672
3820
  const data = createHttpProvider(session.apiUrl, session.token);
3673
- const coreDeps = { data, logger: logger6 };
3821
+ const coreDeps = { data, logger: logger7 };
3674
3822
  const detail = await selectCourse({ userId: session.userId, courseId }, coreDeps);
3675
3823
  let currentModuleTitle;
3676
3824
  let currentLessonTitle;
@@ -3679,7 +3827,7 @@ async function fetchCourseMetaViaHttp(input2) {
3679
3827
  currentModuleTitle = progress3.currentModule?.title;
3680
3828
  currentLessonTitle = progress3.currentLesson?.title;
3681
3829
  } catch (err) {
3682
- logger6.debug("getProgress failed \u2014 falling back to basic course meta", {
3830
+ logger7.debug("getProgress failed \u2014 falling back to basic course meta", {
3683
3831
  error: err instanceof Error ? err.message : String(err)
3684
3832
  });
3685
3833
  }
@@ -3693,7 +3841,7 @@ async function fetchCourseMetaViaHttp(input2) {
3693
3841
  courseLevel = match.level;
3694
3842
  }
3695
3843
  } catch (err) {
3696
- logger6.debug("listCourses failed \u2014 skipping tags/level", {
3844
+ logger7.debug("listCourses failed \u2014 skipping tags/level", {
3697
3845
  error: err instanceof Error ? err.message : String(err)
3698
3846
  });
3699
3847
  }
@@ -3713,7 +3861,7 @@ async function fetchCourseMetaViaHttp(input2) {
3713
3861
  }
3714
3862
  };
3715
3863
  }
3716
- async function writeFilesAdapter(cwd, courseMeta, content) {
3864
+ async function writeFilesAdapter(cwd, courseMeta, content, opts = {}) {
3717
3865
  return writeInstructionFiles(
3718
3866
  cwd,
3719
3867
  {
@@ -3726,17 +3874,18 @@ async function writeFilesAdapter(cwd, courseMeta, content) {
3726
3874
  currentLessonTitle: courseMeta.progress.currentLessonTitle,
3727
3875
  courseDescription: courseMeta.courseDescription
3728
3876
  },
3729
- content
3877
+ content,
3878
+ { allRuntimes: opts.allRuntimes }
3730
3879
  );
3731
3880
  }
3732
- function buildPipelineDeps() {
3881
+ function buildPipelineDeps(opts = {}) {
3733
3882
  return {
3734
3883
  ...defaultDeps2,
3735
3884
  fetchCourseMeta: fetchCourseMetaViaHttp,
3736
- writeFiles: writeFilesAdapter
3885
+ writeFiles: (cwd, courseMeta, content) => writeFilesAdapter(cwd, courseMeta, content, opts)
3737
3886
  };
3738
3887
  }
3739
- var logger6;
3888
+ var logger7;
3740
3889
  var init_pipeline_deps = __esm({
3741
3890
  "src/commands/_shared/pipeline-deps.ts"() {
3742
3891
  "use strict";
@@ -3745,14 +3894,14 @@ var init_pipeline_deps = __esm({
3745
3894
  init_http();
3746
3895
  init_instruction_pipeline();
3747
3896
  init_instruction_files();
3748
- logger6 = createLogger("cli:pipeline-deps");
3897
+ logger7 = createLogger("cli:pipeline-deps");
3749
3898
  }
3750
3899
  });
3751
3900
 
3752
3901
  // src/commands/select.ts
3753
3902
  import { Command as Command6 } from "commander";
3754
3903
  import os7 from "node:os";
3755
- var logger7, selectCommand;
3904
+ var logger8, selectCommand;
3756
3905
  var init_select = __esm({
3757
3906
  "src/commands/select.ts"() {
3758
3907
  "use strict";
@@ -3762,17 +3911,18 @@ var init_select = __esm({
3762
3911
  init_guards();
3763
3912
  init_workspace_marker();
3764
3913
  init_resolve();
3914
+ init_runtime_registry();
3765
3915
  init_formatter();
3766
3916
  init_errors();
3767
3917
  init_status();
3768
3918
  init_instruction_pipeline();
3769
3919
  init_pipeline_deps();
3770
- logger7 = createLogger("cli:select");
3920
+ logger8 = createLogger("cli:select");
3771
3921
  selectCommand = new Command6("select").description("Activate a course by ID or list index number").argument("<course>", "Course ID (UUID) or index number from `tostudy courses`").option("--json", "Output structured JSON").action(async (course, opts) => {
3772
3922
  try {
3773
3923
  const session = await requireSession();
3774
3924
  const data = createHttpProvider(session.apiUrl, session.token);
3775
- const deps = { data, logger: logger7 };
3925
+ const deps = { data, logger: logger8 };
3776
3926
  const courses3 = await listCourses({ userId: session.userId }, deps);
3777
3927
  let courseId = course;
3778
3928
  let enrollmentId = "";
@@ -3811,7 +3961,7 @@ var init_select = __esm({
3811
3961
  { cwd: process.cwd(), deps: buildPipelineDeps() }
3812
3962
  );
3813
3963
  } catch (err) {
3814
- logger7.warn("Failed to run instruction pipeline", {
3964
+ logger8.warn("Failed to run instruction pipeline", {
3815
3965
  error: err instanceof Error ? err.message : String(err)
3816
3966
  });
3817
3967
  }
@@ -3862,12 +4012,18 @@ var init_select = __esm({
3862
4012
  if (pipelineResult && !pipelineResult.usedT1) {
3863
4013
  lines.push(" (sem brief base \u2014 rode `tostudy init` para configurar)");
3864
4014
  }
3865
- lines.push(
3866
- "",
3867
- "\u2192 Abra sua plataforma (claude, codex, cursor...)",
3868
- `\u2192 No Claude Code, digite: ${slashCmd}`,
3869
- "\u2192 Em outras plataformas: tostudy init"
3870
- );
4015
+ lines.push("", "\u2192 Abra sua plataforma (claude, codex, cursor...)");
4016
+ if (courseSlug2) {
4017
+ const hints = detectedSlashHints(courseSlug2, home, cwd);
4018
+ if (hints.length > 0) {
4019
+ lines.push(...hints.map((h) => `\u2192 ${h}`));
4020
+ } else {
4021
+ lines.push(`\u2192 No Claude Code, digite: ${slashCmd}`);
4022
+ }
4023
+ } else {
4024
+ lines.push(`\u2192 No Claude Code, digite: ${slashCmd}`);
4025
+ }
4026
+ lines.push("\u2192 Em outras plataformas: tostudy init");
3871
4027
  output(lines.join("\n"), { json: false });
3872
4028
  }
3873
4029
  } catch (err) {
@@ -3884,7 +4040,7 @@ var init_select = __esm({
3884
4040
 
3885
4041
  // src/commands/progress.ts
3886
4042
  import { Command as Command7 } from "commander";
3887
- var logger8, progressCommand;
4043
+ var logger9, progressCommand;
3888
4044
  var init_progress = __esm({
3889
4045
  "src/commands/progress.ts"() {
3890
4046
  "use strict";
@@ -3895,7 +4051,7 @@ var init_progress = __esm({
3895
4051
  init_course_state();
3896
4052
  init_formatter();
3897
4053
  init_errors();
3898
- logger8 = createLogger("cli:progress");
4054
+ logger9 = createLogger("cli:progress");
3899
4055
  progressCommand = new Command7("progress").description("Show your progress in the active course").option("--json", "Output structured JSON").action(async (opts) => {
3900
4056
  try {
3901
4057
  const session = await requireSession();
@@ -3903,7 +4059,7 @@ var init_progress = __esm({
3903
4059
  const driftWarning = await checkCourseDrift();
3904
4060
  if (driftWarning) process.stderr.write(driftWarning + "\n");
3905
4061
  const data = createHttpProvider(session.apiUrl, session.token);
3906
- const deps = { data, logger: logger8 };
4062
+ const deps = { data, logger: logger9 };
3907
4063
  const progress3 = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
3908
4064
  if (opts.json) {
3909
4065
  output(progress3, { json: true });
@@ -5468,7 +5624,7 @@ var init_query_promise = __esm({
5468
5624
  function mapResultRow(columns, row, joinsNotNullableMap) {
5469
5625
  const nullifyMap = {};
5470
5626
  const result = columns.reduce(
5471
- (result2, { path: path23, field }, columnIndex) => {
5627
+ (result2, { path: path24, field }, columnIndex) => {
5472
5628
  let decoder;
5473
5629
  if (is(field, Column)) {
5474
5630
  decoder = field;
@@ -5480,8 +5636,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5480
5636
  decoder = field.sql.decoder;
5481
5637
  }
5482
5638
  let node = result2;
5483
- for (const [pathChunkIndex, pathChunk] of path23.entries()) {
5484
- if (pathChunkIndex < path23.length - 1) {
5639
+ for (const [pathChunkIndex, pathChunk] of path24.entries()) {
5640
+ if (pathChunkIndex < path24.length - 1) {
5485
5641
  if (!(pathChunk in node)) {
5486
5642
  node[pathChunk] = {};
5487
5643
  }
@@ -5489,8 +5645,8 @@ function mapResultRow(columns, row, joinsNotNullableMap) {
5489
5645
  } else {
5490
5646
  const rawValue = row[columnIndex];
5491
5647
  const value = node[pathChunk] = rawValue === null ? null : decoder.mapFromDriverValue(rawValue);
5492
- if (joinsNotNullableMap && is(field, Column) && path23.length === 2) {
5493
- const objectName = path23[0];
5648
+ if (joinsNotNullableMap && is(field, Column) && path24.length === 2) {
5649
+ const objectName = path24[0];
5494
5650
  if (!(objectName in nullifyMap)) {
5495
5651
  nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
5496
5652
  } else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
@@ -9437,13 +9593,13 @@ function Subscribe(postgres2, options) {
9437
9593
  }
9438
9594
  }
9439
9595
  function handle(a, b2) {
9440
- const path23 = b2.relation.schema + "." + b2.relation.table;
9596
+ const path24 = b2.relation.schema + "." + b2.relation.table;
9441
9597
  call("*", a, b2);
9442
- call("*:" + path23, a, b2);
9443
- b2.relation.keys.length && call("*:" + path23 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
9598
+ call("*:" + path24, a, b2);
9599
+ b2.relation.keys.length && call("*:" + path24 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
9444
9600
  call(b2.command, a, b2);
9445
- call(b2.command + ":" + path23, a, b2);
9446
- b2.relation.keys.length && call(b2.command + ":" + path23 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
9601
+ call(b2.command + ":" + path24, a, b2);
9602
+ b2.relation.keys.length && call(b2.command + ":" + path24 + "=" + b2.relation.keys.map((x2) => a[x2.name]), a, b2);
9447
9603
  }
9448
9604
  function pong() {
9449
9605
  const x2 = Buffer.alloc(34);
@@ -9556,8 +9712,8 @@ function parseEvent(x) {
9556
9712
  const xs = x.match(/^(\*|insert|update|delete)?:?([^.]+?\.?[^=]+)?=?(.+)?/i) || [];
9557
9713
  if (!xs)
9558
9714
  throw new Error("Malformed subscribe pattern: " + x);
9559
- const [, command, path23, key] = xs;
9560
- return (command || "*") + (path23 ? ":" + (path23.indexOf(".") === -1 ? "public." + path23 : path23) : "") + (key ? "=" + key : "");
9715
+ const [, command, path24, key] = xs;
9716
+ return (command || "*") + (path24 ? ":" + (path24.indexOf(".") === -1 ? "public." + path24 : path24) : "") + (key ? "=" + key : "");
9561
9717
  }
9562
9718
  var noop2;
9563
9719
  var init_subscribe = __esm({
@@ -9695,10 +9851,10 @@ function Postgres(a, b2) {
9695
9851
  });
9696
9852
  return query;
9697
9853
  }
9698
- function file2(path23, args = [], options2 = {}) {
9854
+ function file2(path24, args = [], options2 = {}) {
9699
9855
  arguments.length === 2 && !Array.isArray(args) && (options2 = args, args = []);
9700
9856
  const query = new Query([], args, (query2) => {
9701
- fs11.readFile(path23, "utf8", (err, string4) => {
9857
+ fs11.readFile(path24, "utf8", (err, string4) => {
9702
9858
  if (err)
9703
9859
  return query2.reject(err);
9704
9860
  query2.strings = [string4];
@@ -14277,12 +14433,12 @@ var init_session2 = __esm({
14277
14433
  init_tracing();
14278
14434
  init_utils();
14279
14435
  PostgresJsPreparedQuery = class extends PgPreparedQuery {
14280
- constructor(client, queryString, params, logger34, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
14436
+ constructor(client, queryString, params, logger35, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
14281
14437
  super({ sql: queryString, params }, cache, queryMetadata, cacheConfig);
14282
14438
  this.client = client;
14283
14439
  this.queryString = queryString;
14284
14440
  this.params = params;
14285
- this.logger = logger34;
14441
+ this.logger = logger35;
14286
14442
  this.fields = fields;
14287
14443
  this._isResponseInArrayMode = _isResponseInArrayMode;
14288
14444
  this.customResultMapper = customResultMapper;
@@ -14423,11 +14579,11 @@ function construct(client, config2 = {}) {
14423
14579
  client.options.serializers["114"] = transparentParser;
14424
14580
  client.options.serializers["3802"] = transparentParser;
14425
14581
  const dialect = new PgDialect({ casing: config2.casing });
14426
- let logger34;
14582
+ let logger35;
14427
14583
  if (config2.logger === true) {
14428
- logger34 = new DefaultLogger();
14584
+ logger35 = new DefaultLogger();
14429
14585
  } else if (config2.logger !== false) {
14430
- logger34 = config2.logger;
14586
+ logger35 = config2.logger;
14431
14587
  }
14432
14588
  let schema;
14433
14589
  if (config2.schema) {
@@ -14441,7 +14597,7 @@ function construct(client, config2 = {}) {
14441
14597
  tableNamesMap: tablesConfig.tableNamesMap
14442
14598
  };
14443
14599
  }
14444
- const session = new PostgresJsSession(client, dialect, schema, { logger: logger34, cache: config2.cache });
14600
+ const session = new PostgresJsSession(client, dialect, schema, { logger: logger35, cache: config2.cache });
14445
14601
  const db2 = new PostgresJsDatabase(dialect, session, schema);
14446
14602
  db2.$client = client;
14447
14603
  db2.$cache = config2.cache;
@@ -14756,10 +14912,10 @@ function mergeDefs(...defs) {
14756
14912
  function cloneDef(schema) {
14757
14913
  return mergeDefs(schema._zod.def);
14758
14914
  }
14759
- function getElementAtPath(obj, path23) {
14760
- if (!path23)
14915
+ function getElementAtPath(obj, path24) {
14916
+ if (!path24)
14761
14917
  return obj;
14762
- return path23.reduce((acc, key) => acc?.[key], obj);
14918
+ return path24.reduce((acc, key) => acc?.[key], obj);
14763
14919
  }
14764
14920
  function promiseAllObject(promisesObj) {
14765
14921
  const keys = Object.keys(promisesObj);
@@ -15071,11 +15227,11 @@ function aborted(x, startIndex = 0) {
15071
15227
  }
15072
15228
  return false;
15073
15229
  }
15074
- function prefixIssues(path23, issues) {
15230
+ function prefixIssues(path24, issues) {
15075
15231
  return issues.map((iss) => {
15076
15232
  var _a2;
15077
15233
  (_a2 = iss).path ?? (_a2.path = []);
15078
- iss.path.unshift(path23);
15234
+ iss.path.unshift(path24);
15079
15235
  return iss;
15080
15236
  });
15081
15237
  }
@@ -15317,7 +15473,7 @@ function formatError(error49, mapper = (issue2) => issue2.message) {
15317
15473
  }
15318
15474
  function treeifyError(error49, mapper = (issue2) => issue2.message) {
15319
15475
  const result = { errors: [] };
15320
- const processError = (error50, path23 = []) => {
15476
+ const processError = (error50, path24 = []) => {
15321
15477
  var _a2, _b;
15322
15478
  for (const issue2 of error50.issues) {
15323
15479
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -15327,7 +15483,7 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
15327
15483
  } else if (issue2.code === "invalid_element") {
15328
15484
  processError({ issues: issue2.issues }, issue2.path);
15329
15485
  } else {
15330
- const fullpath = [...path23, ...issue2.path];
15486
+ const fullpath = [...path24, ...issue2.path];
15331
15487
  if (fullpath.length === 0) {
15332
15488
  result.errors.push(mapper(issue2));
15333
15489
  continue;
@@ -15359,8 +15515,8 @@ function treeifyError(error49, mapper = (issue2) => issue2.message) {
15359
15515
  }
15360
15516
  function toDotPath(_path) {
15361
15517
  const segs = [];
15362
- const path23 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
15363
- for (const seg of path23) {
15518
+ const path24 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
15519
+ for (const seg of path24) {
15364
15520
  if (typeof seg === "number")
15365
15521
  segs.push(`[${seg}]`);
15366
15522
  else if (typeof seg === "symbol")
@@ -28054,13 +28210,13 @@ function resolveRef(ref, ctx) {
28054
28210
  if (!ref.startsWith("#")) {
28055
28211
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
28056
28212
  }
28057
- const path23 = ref.slice(1).split("/").filter(Boolean);
28058
- if (path23.length === 0) {
28213
+ const path24 = ref.slice(1).split("/").filter(Boolean);
28214
+ if (path24.length === 0) {
28059
28215
  return ctx.rootSchema;
28060
28216
  }
28061
28217
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
28062
- if (path23[0] === defsKey) {
28063
- const key = path23[1];
28218
+ if (path24[0] === defsKey) {
28219
+ const key = path24[1];
28064
28220
  if (!key || !ctx.defs[key]) {
28065
28221
  throw new Error(`Reference not found: ${ref}`);
28066
28222
  }
@@ -29167,7 +29323,7 @@ var init_user_preferences = __esm({
29167
29323
  });
29168
29324
 
29169
29325
  // ../../packages/database/src/schema/mcp.ts
29170
- var mcpSessions, syncLogs, platformConfig;
29326
+ var mcpSessions, syncLogs;
29171
29327
  var init_mcp = __esm({
29172
29328
  "../../packages/database/src/schema/mcp.ts"() {
29173
29329
  "use strict";
@@ -29230,11 +29386,6 @@ var init_mcp = __esm({
29230
29386
  syncedAtIdx: index("idx_sync_logs_synced_at").on(table.syncedAt)
29231
29387
  })
29232
29388
  );
29233
- platformConfig = pgTable("platform_config", {
29234
- key: varchar("key", { length: 50 }).primaryKey(),
29235
- value: text("value").notNull(),
29236
- updatedAt: timestamp("updated_at").defaultNow().notNull()
29237
- });
29238
29389
  }
29239
29390
  });
29240
29391
 
@@ -29568,6 +29719,13 @@ var init_tutor_feedback = __esm({
29568
29719
  }
29569
29720
  });
29570
29721
 
29722
+ // ../../packages/types/src/lesson-review.ts
29723
+ var init_lesson_review = __esm({
29724
+ "../../packages/types/src/lesson-review.ts"() {
29725
+ "use strict";
29726
+ }
29727
+ });
29728
+
29571
29729
  // ../../packages/types/src/errors.ts
29572
29730
  var init_errors6 = __esm({
29573
29731
  "../../packages/types/src/errors.ts"() {
@@ -29599,7 +29757,8 @@ var init_credit_payment_enums = __esm({
29599
29757
  // course_sale_credit (legacy Stripe 20/85) so reconciliation/clawback never
29600
29758
  // conflate the two funding models.
29601
29759
  "course_enrollment_debit",
29602
- "course_enrollment_credit"
29760
+ "course_enrollment_credit",
29761
+ "referral_bonus"
29603
29762
  ];
29604
29763
  CREDIT_SUBSCRIPTION_STATUS_VALUES = [
29605
29764
  "active",
@@ -29619,6 +29778,13 @@ var init_credit_payment_enums = __esm({
29619
29778
  }
29620
29779
  });
29621
29780
 
29781
+ // ../../packages/types/src/referral.ts
29782
+ var init_referral = __esm({
29783
+ "../../packages/types/src/referral.ts"() {
29784
+ "use strict";
29785
+ }
29786
+ });
29787
+
29622
29788
  // ../../packages/types/src/index.ts
29623
29789
  var init_src2 = __esm({
29624
29790
  "../../packages/types/src/index.ts"() {
@@ -29632,8 +29798,10 @@ var init_src2 = __esm({
29632
29798
  init_exercise_type();
29633
29799
  init_lesson_contract();
29634
29800
  init_tutor_feedback();
29801
+ init_lesson_review();
29635
29802
  init_errors6();
29636
29803
  init_credit_payment_enums();
29804
+ init_referral();
29637
29805
  }
29638
29806
  });
29639
29807
 
@@ -30016,7 +30184,7 @@ var init_llm_analytics = __esm({
30016
30184
  // Operation type
30017
30185
  operationType: varchar("operation_type", { length: 50 }).notNull(),
30018
30186
  // Provider (references llm_provider enum from llm-pricing.ts)
30019
- provider: llmProviderEnum("provider").notNull().default("anthropic"),
30187
+ provider: llmProviderEnum("provider").notNull().default("other"),
30020
30188
  // Tracing
30021
30189
  correlationId: varchar("correlation_id", { length: 64 }),
30022
30190
  // Relationships
@@ -30036,6 +30204,11 @@ var init_llm_analytics = __esm({
30036
30204
  responseTokens: integer("response_tokens"),
30037
30205
  // Cost & Revenue
30038
30206
  costUsd: decimal("cost_usd", { precision: 10, scale: 6 }),
30207
+ // spec 2026-07-10 phase 3.3: where cost_usd came from - 'table' (pricing
30208
+ // table rate), 'gateway' (LiteLLM-reported cost fallback), 'unpriced'
30209
+ // (no rate anywhere: cost_usd=0, alertable). Nullable: rows predating the
30210
+ // marker stay null.
30211
+ costSource: varchar("cost_source", { length: 16 }),
30039
30212
  revenueUsd: decimal("revenue_usd", { precision: 10, scale: 6 }).default("0"),
30040
30213
  // Cache metrics (prompt caching)
30041
30214
  cacheCreationTokens: integer("cache_creation_tokens").default(0),
@@ -30062,6 +30235,12 @@ var init_llm_analytics = __esm({
30062
30235
  "llm_interactions_status_check",
30063
30236
  sql`${table.status} IN ('pending', 'success', 'error', 'timeout', 'abandoned')`
30064
30237
  ),
30238
+ // spec 2026-07-10 phase 3.3: cost_source vocabulary — NULL allowed for
30239
+ // rows predating the marker.
30240
+ costSourceVocabularyCheck: check(
30241
+ "llm_interactions_cost_source_check",
30242
+ sql`${table.costSource} IS NULL OR ${table.costSource} IN ('table', 'gateway', 'unpriced')`
30243
+ ),
30065
30244
  creatorIdx: index("idx_llm_interactions_creator").on(table.creatorId, table.createdAt),
30066
30245
  operationIdx: index("idx_llm_interactions_operation").on(table.operationType, table.createdAt),
30067
30246
  correlationIdx: index("idx_llm_interactions_correlation").on(table.correlationId),
@@ -30148,7 +30327,9 @@ var init_credit_wallets = __esm({
30148
30327
  // Credits-first (WS3/WS4): student debit (-), creator full-price credit (+).
30149
30328
  course_enrollment_debit: "negative",
30150
30329
  course_enrollment_credit: "positive",
30151
- adjustment: "either"
30330
+ adjustment: "either",
30331
+ // Referral program (spec 2026-07-17): both-sides fixed grant on first purchase.
30332
+ referral_bonus: "positive"
30152
30333
  };
30153
30334
  WITHDRAWABLE_LEDGER_TYPES = [
30154
30335
  "course_sale_credit",
@@ -30194,6 +30375,7 @@ var init_credit_wallets = __esm({
30194
30375
  "subscription",
30195
30376
  "package",
30196
30377
  "withdrawal",
30378
+ "referral",
30197
30379
  "backfill",
30198
30380
  "credit_transaction_migration"
30199
30381
  ];
@@ -30248,6 +30430,12 @@ var init_credit_wallets = __esm({
30248
30430
  // the PK). Raw `sql` literal (not eq()) to dodge the drizzle-kit `$N`
30249
30431
  // placeholder DDL bug — see packages/database/AGENTS.md.
30250
30432
  courseSaleCreditEnrollmentUnique: uniqueIndex("cwt_course_sale_credit_enrollment_unique").on(table.referenceId, table.referenceType, table.type).where(sql`${table.type} = 'course_sale_credit' AND ${table.referenceType} = 'enrollment'`),
30433
+ cwtPurchasePaymentIntentUnique: uniqueIndex("cwt_purchase_payment_intent_unique").on(sql`(${table.metadata}->>'stripe_payment_intent_id')`).where(sql`${table.type} = 'purchase'`),
30434
+ // Idempotency guard for subscription reloads via stripe invoice.
30435
+ // Closes the double-reload window when Stripe invoice webhook fires concurrently
30436
+ // or re-delivers. Raw `sql` literal (not eq()) to dodge the drizzle-kit `$N`
30437
+ // placeholder DDL bug — see packages/database/AGENTS.md.
30438
+ cwtSubscriptionReloadInvoiceUnique: uniqueIndex("cwt_subscription_reload_invoice_unique").on(sql`(${table.metadata}->>'stripe_invoice_id')`).where(sql`${table.type} = 'subscription_reload'`),
30251
30439
  // Idempotency guard for the AI-consumption reconciliation sweep (CHORE-credit-recon).
30252
30440
  // The sweep must not double-charge a debit the inline path already committed;
30253
30441
  // this index makes the DB the final arbiter — a second ai_consumption insert
@@ -30257,23 +30445,34 @@ var init_credit_wallets = __esm({
30257
30445
  // Raw `sql` literal (not eq()) to dodge the drizzle-kit `$N` placeholder DDL
30258
30446
  // bug (packages/database/AGENTS.md).
30259
30447
  aiConsumptionInteractionUniq: uniqueIndex("cwt_ai_consumption_interaction_uniq").on(table.interactionId).where(sql`${table.type} = 'ai_consumption' AND ${table.interactionId} IS NOT NULL`),
30260
- // Credits-first (WS3/WS4): NO partial-unique DB index for course_enrollment_debit
30261
- // / course_enrollment_credit. A partial index whose predicate references a
30262
- // freshly-added enum value cannot be created in this repo's one-transaction
30263
- // `pnpm db:migrate` (AGENTS.md): the enum literal trips check_safe_enum_use
30264
- // (enum.c) and a `::text` cast is non-IMMUTABLE so it trips CheckPredicate.
30265
- // Unlike the webhook-driven legacy paths (course_sale_credit / refund_debit),
30266
- // which need a DB race-guard against concurrent Stripe deliveries, the
30267
- // credit-debit enrollment path is a SYNCHRONOUS user mutation — idempotency is
30268
- // enforced at the application layer (EnrollWithCreditsUseCase: find-existing
30269
- // enrollment + atomic debit/credit/enrollment in one tx). If post-launch
30270
- // reconciliation surfaces a need, add the partial index in a standalone
30271
- // migration once these enum values are universally committed.
30448
+ // Credits-first ramp gate (credits-ssot-ux WS1): partial UNIQUE closes the
30449
+ // double-debit window in EnrollWithCreditsUseCase before the
30450
+ // `feature.credit_debit_enrollment_enabled` ramp. The advisory lock + re-check
30451
+ // covers the double-click case, but the DB is the final arbiter against any
30452
+ // path that bypasses the lock (retry storms, future callers); the use-case
30453
+ // catches 23505 as AlreadyEnrolled. This is the "standalone migration once
30454
+ // these enum values are universally committed" the previous note here
30455
+ // anticipated: the enum values landed in 0044 (13 migrations back, deployed
30456
+ // to prod), and a cold-boot single-transaction `pnpm db:migrate` is safe
30457
+ // because the TYPE itself is created in that same transaction (enum.c's
30458
+ // check_safe_enum_use only restricts values added to a PRE-EXISTING type).
30459
+ // Scoped to type='course_enrollment_debit' AND reference_type='enrollment' —
30460
+ // predicate-disjoint from every other partial index on this table, so the
30461
+ // `undefined`-constraint branches of the sibling 23505 detectors stay safe.
30462
+ // course_enrollment_credit needs no index of its own: it is written in the
30463
+ // SAME transaction as the debit, so the debit index aborts both. Raw `sql`
30464
+ // literal (not eq()) to dodge the drizzle-kit `$N` placeholder DDL bug —
30465
+ // see packages/database/AGENTS.md.
30466
+ courseEnrollmentDebitEnrollmentUnique: uniqueIndex(
30467
+ "cwt_course_enrollment_debit_enrollment_unique"
30468
+ ).on(table.referenceId, table.referenceType, table.type).where(
30469
+ sql`${table.type} = 'course_enrollment_debit' AND ${table.referenceType} = 'enrollment'`
30470
+ ),
30272
30471
  referenceTypeCheck: check(
30273
30472
  "cwt_reference_type_check",
30274
30473
  sql`${table.referenceType} IS NULL OR ${table.referenceType} IN (
30275
30474
  'ai_operation', 'enrollment', 'booking',
30276
- 'subscription', 'package', 'withdrawal',
30475
+ 'subscription', 'package', 'withdrawal', 'referral',
30277
30476
  'backfill', 'credit_transaction_migration'
30278
30477
  )`
30279
30478
  )
@@ -30323,6 +30522,10 @@ var init_credit_packages = __esm({
30323
30522
  }).notNull(),
30324
30523
  isActive: boolean("is_active").notNull().default(true),
30325
30524
  includesCatalog: boolean("includes_catalog").notNull().default(true),
30525
+ // Subscription tiers 2026-07-07 spec: % off AI-usage debits (0 = none).
30526
+ consumptionDiscountPercent: integer("consumption_discount_percent").notNull().default(0),
30527
+ // Course enrollments redeemable per billing cycle (0 = none; Pro = 1).
30528
+ courseEnrollmentsPerMonth: integer("course_enrollments_per_month").notNull().default(0),
30326
30529
  sortOrder: integer("sort_order").notNull().default(0),
30327
30530
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
30328
30531
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
@@ -30330,7 +30533,12 @@ var init_credit_packages = __esm({
30330
30533
  (table) => ({
30331
30534
  creditsPositive: check("csp_credits_positive", sql`${table.creditsPerMonth} > 0`),
30332
30535
  pricePositive: check("csp_price_positive", sql`${table.priceUsdMonthly} > 0`),
30333
- nameSlugPattern: check("csp_name_slug_pattern", sql`${table.name} ~ '^[a-z][a-z0-9_]{0,49}$'`)
30536
+ nameSlugPattern: check("csp_name_slug_pattern", sql`${table.name} ~ '^[a-z][a-z0-9_]{0,49}$'`),
30537
+ discountRange: check(
30538
+ "csp_discount_range",
30539
+ sql`${table.consumptionDiscountPercent} >= 0 AND ${table.consumptionDiscountPercent} <= 90`
30540
+ ),
30541
+ quotaNonNegative: check("csp_quota_non_negative", sql`${table.courseEnrollmentsPerMonth} >= 0`)
30334
30542
  })
30335
30543
  );
30336
30544
  creditSubscriptions = pgTable(
@@ -30363,7 +30571,13 @@ var init_credit_packages = __esm({
30363
30571
  (table) => ({
30364
30572
  userIdx: index("cs_user_idx").on(table.userId),
30365
30573
  stripeSubIdx: index("cs_stripe_sub_idx").on(table.stripeSubscriptionId),
30366
- lastInvoiceIdx: index("cs_last_invoice_idx").on(table.lastInvoiceId)
30574
+ lastInvoiceIdx: index("cs_last_invoice_idx").on(table.lastInvoiceId),
30575
+ // #793: one live billable row per user. createSubscription's active/past_due
30576
+ // guard checks-then-inserts without a lock; this closes the double-submit
30577
+ // race (deferred "while latent" in credit-wallet.router.ts — the rm-030
30578
+ // acquisition UI un-latents it). Raw sql in .where() — drizzle-kit
30579
+ // serializes eq() as a $N placeholder in partial-index DDL (42P02).
30580
+ activeUserUnique: uniqueIndex("cs_active_user_unique").on(table.userId).where(sql`status = 'active'`)
30367
30581
  })
30368
30582
  );
30369
30583
  }
@@ -31026,7 +31240,7 @@ var init_enrollment_channel_changes = __esm({
31026
31240
  });
31027
31241
 
31028
31242
  // ../../packages/database/migrations/schema.ts
31029
- var applicableTo, badgeCriteriaType, badgeTier, badgeType, brainstormStatus, cancellationReason, challengeType, communityPostType, courseReviewStatus, courseStatus, discountType, enrollmentStatus, funnelEventType, knowledgeProcessingStatus, knowledgeResourceType, knowledgeUrlMode, marketingContentType, mentorshipCreditStatus, mentorshipTransactionStatus, mentorshipTransactionType, moderationAction, moderationTarget, participantStatus, progressStatus, proposalStatus, refundReason, refundType, reportReason, reportStatus, resourceType, serviceType, sessionCancelledReason, sessionStatus, slaStatus, studentRole, testimonialStatus, threadStatus, transactionType, userRole, userStatus, goalType, skillLevel, pathStatus, milestoneStatus, tsvector, slashCommandAnalytics, notificationLogs, couponUsages, validationAttempts, users2, accounts2, platformConfig2, concepts2, mentorshipMessages, courseReviews2, webhookEvents, sessions2, courses2, badges2, userBadges2, challenges, userChallengeScores, leaderboardPreferences, onboardingAnalytics, creatorApplications, reviews, certificates2, userPreferences2, progress2, mentorshipTemplates, notifications, lessons2, syncLogs2, mentorshipThreads, courseReports2, mcpSessions2, transactions, enrollments2, reviewHelpfulVotes, modules2, coupons, funnelEvents, moderationLog, mentorshipServices, mentorAvailability, mentorAvailabilityExceptions, sessionWaitlist, sessionParticipants, mentorshipTransactions, mentorshipSessions, sessionResources, sessionReviews, mentorshipPackages, mentorshipCredits, refundAuditLog, sessionReviewVotes, communityPosts, communityComments, communityLikes, userFollows, sparkChatSessions, platformSettings, generationState, creatorTestimonials, marketingContent, studentNotes, userCredits, userUsageQuotas, usageTierLimits, brainstormSessions, creditTransactions, courseProposals, brainstormMessages, knowledgeResources, knowledgeChunks, verificationTokens2, studentTutorContext, tutorInteractionHistory, studentLearningPreferences, learningStreaks, dailyGoals, dailyGoalProgress, learningPaths, pathCourses, pathMilestones;
31243
+ var applicableTo, badgeCriteriaType, badgeTier, badgeType, brainstormStatus, cancellationReason, challengeType, communityPostType, courseReviewStatus, courseStatus, discountType, enrollmentStatus, funnelEventType, knowledgeProcessingStatus, knowledgeResourceType, knowledgeUrlMode, marketingContentType, mentorshipCreditStatus, mentorshipTransactionStatus, mentorshipTransactionType, moderationAction, moderationTarget, participantStatus, progressStatus, proposalStatus, refundReason, refundType, reportReason, reportStatus, resourceType, serviceType, sessionCancelledReason, sessionStatus, slaStatus, studentRole, testimonialStatus, threadStatus, transactionType, userRole, userStatus, goalType, skillLevel, pathStatus, milestoneStatus, tsvector, slashCommandAnalytics, notificationLogs, couponUsages, validationAttempts, users2, accounts2, platformConfig, concepts2, mentorshipMessages, courseReviews2, webhookEvents, sessions2, courses2, badges2, userBadges2, challenges, userChallengeScores, leaderboardPreferences, onboardingAnalytics, creatorApplications, reviews, certificates2, userPreferences2, progress2, mentorshipTemplates, notifications, lessons2, syncLogs2, mentorshipThreads, courseReports2, mcpSessions2, transactions, enrollments2, reviewHelpfulVotes, modules2, coupons, funnelEvents, moderationLog, mentorshipServices, mentorAvailability, mentorAvailabilityExceptions, sessionWaitlist, sessionParticipants, mentorshipTransactions, mentorshipSessions, sessionResources, sessionReviews, mentorshipPackages, mentorshipCredits, refundAuditLog, sessionReviewVotes, communityPosts, communityComments, communityLikes, userFollows, sparkChatSessions, platformSettings, generationState, creatorTestimonials, marketingContent, studentNotes, userCredits, userUsageQuotas, usageTierLimits, brainstormSessions, creditTransactions, courseProposals, brainstormMessages, knowledgeResources, knowledgeChunks, verificationTokens2, studentTutorContext, tutorInteractionHistory, studentLearningPreferences, learningStreaks, dailyGoals, dailyGoalProgress, learningPaths, pathCourses, pathMilestones;
31030
31244
  var init_schema2 = __esm({
31031
31245
  "../../packages/database/migrations/schema.ts"() {
31032
31246
  "use strict";
@@ -31489,7 +31703,7 @@ var init_schema2 = __esm({
31489
31703
  }).onDelete("cascade")
31490
31704
  ]
31491
31705
  );
31492
- platformConfig2 = pgTable("platform_config", {
31706
+ platformConfig = pgTable("platform_config", {
31493
31707
  key: varchar({ length: 50 }).primaryKey().notNull(),
31494
31708
  value: text().notNull(),
31495
31709
  updatedAt: timestamp("updated_at", { mode: "string" }).defaultNow().notNull()
@@ -35885,13 +36099,17 @@ var init_mentorship_credits = __esm({
35885
36099
  // Package lookup
35886
36100
  packageIdIdx: index("mentorship_credits_package_id_idx").on(table.packageId),
35887
36101
  // Stripe payment intent lookup (for webhook idempotency)
35888
- stripePaymentIntentIdIdx: index("mentorship_credits_stripe_payment_intent_idx").on(
35889
- table.stripePaymentIntentId
35890
- ),
36102
+ stripePaymentIntentIdUnique: uniqueIndex("mentorship_credits_stripe_payment_intent_unique").on(table.stripePaymentIntentId).where(sql`${table.stripePaymentIntentId} IS NOT NULL`),
35891
36103
  // ORDER BY purchasedAt DESC (credit transaction history)
35892
36104
  purchasedAtIdx: index("mentorship_credits_purchased_at_idx").on(table.purchasedAt),
35893
36105
  // FEAT-1288: refund revoke looks up course-granted credits by enrollment
35894
36106
  enrollmentIdIdx: index("mentorship_credits_enrollment_id_idx").on(table.enrollmentId),
36107
+ // GH #905 F5: one course-purchase grant per enrollment. The check-then-write
36108
+ // in GrantCourseMentoriaCreditsUseCase races on Stripe webhook re-delivery
36109
+ // (stripe_payment_intent_id is null for these rows, so the existing intent
36110
+ // unique index doesn't cover them). This partial unique index is the DB-side
36111
+ // idempotency guard; the use case catches 23505 as a no-op.
36112
+ courseEnrollmentUnique: uniqueIndex("mentorship_credits_course_enrollment_unique").on(table.enrollmentId).where(sql`${table.origin} = 'course_purchase' AND ${table.enrollmentId} IS NOT NULL`),
35895
36113
  // FEAT-1288: each origin carries its tracing FK — package rows keep a
35896
36114
  // package_id, course-purchase rows keep an enrollment_id.
35897
36115
  originConsistencyCheck: check(
@@ -35985,6 +36203,7 @@ var init_mentorship_transactions = __esm({
35985
36203
  "../../packages/database/src/schema/mentorship-transactions.ts"() {
35986
36204
  "use strict";
35987
36205
  init_pg_core();
36206
+ init_drizzle_orm();
35988
36207
  init_users();
35989
36208
  init_mentorship_sessions();
35990
36209
  init_mentorship_packages();
@@ -36085,9 +36304,9 @@ var init_mentorship_transactions = __esm({
36085
36304
  studentIdIdx: index("mentorship_transactions_student_id_idx").on(table.studentId),
36086
36305
  mentorIdIdx: index("mentorship_transactions_mentor_id_idx").on(table.mentorId),
36087
36306
  statusIdx: index("mentorship_transactions_status_idx").on(table.status),
36088
- stripePaymentIntentIdIdx: index("mentorship_transactions_stripe_payment_intent_id_idx").on(
36089
- table.stripePaymentIntentId
36090
- ),
36307
+ stripePaymentIntentIdUnique: uniqueIndex(
36308
+ "mentorship_transactions_stripe_payment_intent_id_unique"
36309
+ ).on(table.stripePaymentIntentId).where(sql`${table.stripePaymentIntentId} IS NOT NULL`),
36091
36310
  createdAtIdx: index("mentorship_transactions_created_at_idx").on(table.createdAt),
36092
36311
  // Story 19.3: Additional indexes for split and aggregation
36093
36312
  typeIdx: index("mentorship_transactions_type_idx").on(table.transactionType),
@@ -37515,6 +37734,7 @@ var init_transactions = __esm({
37515
37734
  "../../packages/database/src/schema/transactions.ts"() {
37516
37735
  "use strict";
37517
37736
  init_pg_core();
37737
+ init_drizzle_orm();
37518
37738
  init_courses3();
37519
37739
  init_users();
37520
37740
  init_enrollments();
@@ -37523,9 +37743,13 @@ var init_transactions = __esm({
37523
37743
  {
37524
37744
  id: uuid("id").defaultRandom().primaryKey(),
37525
37745
  // Stripe references
37526
- stripePaymentIntentId: varchar("stripe_payment_intent_id", { length: 255 }).notNull().unique(),
37746
+ stripePaymentIntentId: varchar("stripe_payment_intent_id", { length: 255 }).unique(),
37527
37747
  stripeTransferId: varchar("stripe_transfer_id", { length: 255 }),
37528
37748
  // Stripe Transfer ID (if separate transfer)
37749
+ // spec 2026-07-31-pix-modobank: second inbound provider. 'stripe' rows keep
37750
+ // stripe_payment_intent_id; 'modobank' rows carry the e2eid here instead.
37751
+ provider: varchar("provider", { length: 20 }).notNull().default("stripe"),
37752
+ providerPaymentId: varchar("provider_payment_id", { length: 255 }),
37529
37753
  // Foreign keys
37530
37754
  courseId: uuid("course_id").notNull().references(() => courses.id, { onDelete: "restrict" }),
37531
37755
  creatorId: uuid("creator_id").notNull().references(() => users.id, { onDelete: "restrict" }),
@@ -37597,6 +37821,56 @@ var init_transactions = __esm({
37597
37821
  table.studentId,
37598
37822
  table.status,
37599
37823
  table.createdAt
37824
+ ),
37825
+ // spec 2026-07-31-pix-modobank: dedupes per-provider payment refs; NULL
37826
+ // provider_payment_id (legacy Stripe rows, pre-webhook-write) is excluded.
37827
+ providerPaymentIdUnique: uniqueIndex("transactions_provider_payment_id_unique").on(table.provider, table.providerPaymentId).where(sql`${table.providerPaymentId} IS NOT NULL`)
37828
+ })
37829
+ );
37830
+ }
37831
+ });
37832
+
37833
+ // ../../packages/database/src/schema/pix-charges.ts
37834
+ var PIX_CHARGE_STATUSES, PIX_CHARGE_PURPOSES, pixCharges;
37835
+ var init_pix_charges = __esm({
37836
+ "../../packages/database/src/schema/pix-charges.ts"() {
37837
+ "use strict";
37838
+ init_drizzle_orm();
37839
+ init_pg_core();
37840
+ init_users();
37841
+ PIX_CHARGE_STATUSES = ["pending", "paid", "fulfilled", "expired", "refunded"];
37842
+ PIX_CHARGE_PURPOSES = ["course_sale", "credit_topup"];
37843
+ pixCharges = pgTable(
37844
+ "pix_charges",
37845
+ {
37846
+ id: uuid("id").defaultRandom().primaryKey(),
37847
+ txid: varchar("txid", { length: 35 }).notNull(),
37848
+ e2eid: varchar("e2eid", { length: 64 }),
37849
+ status: varchar("status", { length: 20 }).notNull().default("pending"),
37850
+ purpose: varchar("purpose", { length: 20 }).notNull(),
37851
+ userId: uuid("user_id").references(() => users.id, { onDelete: "restrict" }).notNull(),
37852
+ /** courseId (course_sale) or credit package id (credit_topup). */
37853
+ referenceId: uuid("reference_id"),
37854
+ amountBrlCents: integer("amount_brl_cents").notNull(),
37855
+ pixCopiaECola: text("pix_copia_e_cola").notNull(),
37856
+ /** course_sale: money breakdown replayed at fulfillment (creatorId, fees, couponId...). */
37857
+ metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
37858
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
37859
+ paidAt: timestamp("paid_at", { withTimezone: true }),
37860
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
37861
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull().$onUpdate(() => /* @__PURE__ */ new Date())
37862
+ },
37863
+ (table) => ({
37864
+ txidUnique: uniqueIndex("pix_charges_txid_unique").on(table.txid),
37865
+ userIdIdx: index("pix_charges_user_id_idx").on(table.userId),
37866
+ sweepIdx: index("pix_charges_sweep_idx").on(table.status, table.expiresAt),
37867
+ statusCheck: check(
37868
+ "pxc_status_check",
37869
+ sql`${table.status} IN ('pending', 'paid', 'fulfilled', 'expired', 'refunded')`
37870
+ ),
37871
+ purposeCheck: check(
37872
+ "pxc_purpose_check",
37873
+ sql`${table.purpose} IN ('course_sale', 'credit_topup')`
37600
37874
  )
37601
37875
  })
37602
37876
  );
@@ -38516,140 +38790,6 @@ var init_study_index = __esm({
38516
38790
  }
38517
38791
  });
38518
38792
 
38519
- // ../../packages/database/src/schema/platform-settings.ts
38520
- function parseSettingValue(value, valueType) {
38521
- switch (valueType) {
38522
- case "boolean":
38523
- return value === "true";
38524
- case "number":
38525
- return Number(value);
38526
- case "json":
38527
- try {
38528
- return JSON.parse(value);
38529
- } catch {
38530
- return null;
38531
- }
38532
- default:
38533
- return value;
38534
- }
38535
- }
38536
- function stringifySettingValue(value, valueType) {
38537
- switch (valueType) {
38538
- case "boolean":
38539
- return value ? "true" : "false";
38540
- case "number":
38541
- return String(value);
38542
- case "json":
38543
- return JSON.stringify(value);
38544
- default:
38545
- return String(value);
38546
- }
38547
- }
38548
- var settingValueTypes, settingCategories, platformSettings2, insertPlatformSettingSchema, selectPlatformSettingSchema, settingKeys;
38549
- var init_platform_settings = __esm({
38550
- "../../packages/database/src/schema/platform-settings.ts"() {
38551
- "use strict";
38552
- init_pg_core();
38553
- init_zod();
38554
- settingValueTypes = ["string", "boolean", "number", "json"];
38555
- settingCategories = [
38556
- "platform",
38557
- "notifications",
38558
- "security",
38559
- "email",
38560
- "financial"
38561
- ];
38562
- platformSettings2 = pgTable(
38563
- "platform_settings",
38564
- {
38565
- key: varchar("key", { length: 100 }).primaryKey(),
38566
- value: text("value").notNull(),
38567
- valueType: varchar("value_type", { length: 20 }).notNull().default("string"),
38568
- category: varchar("category", { length: 50 }).notNull().default("general"),
38569
- description: text("description"),
38570
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
38571
- updatedBy: varchar("updated_by", { length: 255 })
38572
- },
38573
- (table) => [index("idx_platform_settings_category").on(table.category)]
38574
- );
38575
- insertPlatformSettingSchema = external_exports.object({
38576
- key: external_exports.string().min(1).max(100),
38577
- value: external_exports.string(),
38578
- valueType: external_exports.enum(settingValueTypes).default("string"),
38579
- category: external_exports.enum(settingCategories).default("platform"),
38580
- description: external_exports.string().nullable().optional(),
38581
- updatedBy: external_exports.string().nullable().optional()
38582
- });
38583
- selectPlatformSettingSchema = external_exports.object({
38584
- key: external_exports.string(),
38585
- value: external_exports.string(),
38586
- valueType: external_exports.string(),
38587
- category: external_exports.string(),
38588
- description: external_exports.string().nullable(),
38589
- updatedAt: external_exports.date().nullable(),
38590
- updatedBy: external_exports.string().nullable()
38591
- });
38592
- settingKeys = {
38593
- // Platform
38594
- platformName: "platform_name",
38595
- platformUrl: "platform_url",
38596
- platformLogoUrl: "platform_logo_url",
38597
- platformDescription: "platform_description",
38598
- maintenanceMode: "maintenance_mode",
38599
- // Notifications
38600
- emailWelcome: "email_welcome",
38601
- emailCourseApproved: "email_course_approved",
38602
- emailNewEnrollment: "email_new_enrollment",
38603
- adminSystemAlerts: "admin_system_alerts",
38604
- // Security
38605
- mfaRequiredAdmins: "mfa_required_admins",
38606
- mfaRequiredCreators: "mfa_required_creators",
38607
- sessionTimeoutMinutes: "session_timeout_minutes",
38608
- maxLoginAttempts: "max_login_attempts",
38609
- // Email
38610
- emailFrom: "email_from",
38611
- emailFromName: "email_from_name"
38612
- };
38613
- }
38614
- });
38615
-
38616
- // ../../packages/database/src/schema/admin-settings-audit.ts
38617
- var adminSettingsAudit, adminSettingsAuditRelations;
38618
- var init_admin_settings_audit = __esm({
38619
- "../../packages/database/src/schema/admin-settings-audit.ts"() {
38620
- "use strict";
38621
- init_pg_core();
38622
- init_drizzle_orm();
38623
- init_users();
38624
- adminSettingsAudit = pgTable(
38625
- "admin_settings_audit",
38626
- {
38627
- id: uuid("id").defaultRandom().primaryKey(),
38628
- // What changed
38629
- settingKey: varchar("setting_key", { length: 100 }).notNull(),
38630
- previousValue: jsonb("previous_value"),
38631
- // nullable for new settings
38632
- newValue: jsonb("new_value").notNull(),
38633
- // Who changed it
38634
- adminId: uuid("admin_id").references(() => users.id, { onDelete: "set null" }),
38635
- // When
38636
- createdAt: timestamp("created_at").defaultNow().notNull()
38637
- },
38638
- (table) => ({
38639
- settingKeyIdx: index("admin_settings_audit_setting_key_idx").on(table.settingKey),
38640
- adminIdIdx: index("admin_settings_audit_admin_id_idx").on(table.adminId),
38641
- createdAtIdx: index("admin_settings_audit_created_at_idx").on(table.createdAt)
38642
- })
38643
- );
38644
- adminSettingsAuditRelations = relations(adminSettingsAudit, ({ one }) => ({
38645
- admin: one(users, {
38646
- fields: [adminSettingsAudit.adminId],
38647
- references: [users.id]
38648
- })
38649
- }));
38650
- }
38651
- });
38652
-
38653
38793
  // ../../packages/database/src/schema/admin-activity-feed.ts
38654
38794
  var adminActivityFeed, adminActivityFeedRelations;
38655
38795
  var init_admin_activity_feed = __esm({
@@ -39859,26 +39999,19 @@ var init_system_config = __esm({
39859
39999
  }));
39860
40000
  configKeys = {
39861
40001
  // AI - Generation
39862
- "ai.generation.model": "ai.generation.model",
40002
+ // GH #942: ai.*.model keys (generation/chat/study) retired — model comes from
40003
+ // the routing rule + gateway tier (GH #799 2b); DB keeps inference params only.
39863
40004
  "ai.generation.max_tokens": "ai.generation.max_tokens",
39864
40005
  "ai.generation.temperature": "ai.generation.temperature",
39865
40006
  // AI - Chat
39866
- "ai.chat.model": "ai.chat.model",
39867
40007
  "ai.chat.max_tokens": "ai.chat.max_tokens",
39868
40008
  "ai.chat.temperature": "ai.chat.temperature",
39869
40009
  // AI - Study
39870
- "ai.study.chat_model": "ai.study.chat_model",
39871
- "ai.study.hint_model": "ai.study.hint_model",
39872
40010
  "ai.study.max_tokens": "ai.study.max_tokens",
39873
40011
  "ai.study.temperature": "ai.study.temperature",
39874
- // AI - Costs
40012
+ // AI - Costs (GH #947: cache_optimization_enabled and ai.context.* purged;
40013
+ // GH #974: requests_per_minute purged — reader never mounted)
39875
40014
  "ai.costs.daily_limit_usd": "ai.costs.daily_limit_usd",
39876
- "ai.costs.requests_per_minute": "ai.costs.requests_per_minute",
39877
- "ai.costs.cache_optimization_enabled": "ai.costs.cache_optimization_enabled",
39878
- // AI - Context Window Management
39879
- "ai.context.window_size": "ai.context.window_size",
39880
- "ai.context.output_reserve": "ai.context.output_reserve",
39881
- "ai.context.compression_threshold": "ai.context.compression_threshold",
39882
40015
  // Business - Pricing
39883
40016
  "business.pricing.min_course_price_brl": "business.pricing.min_course_price_brl",
39884
40017
  "business.pricing.course_mentoria_creator_share": "business.pricing.course_mentoria_creator_share",
@@ -39895,6 +40028,8 @@ var init_system_config = __esm({
39895
40028
  "business.monitoring.prompt_caching_discount": "business.monitoring.prompt_caching_discount",
39896
40029
  "business.monitoring.llm_cost_warning_usd": "business.monitoring.llm_cost_warning_usd",
39897
40030
  "business.monitoring.llm_cost_critical_usd": "business.monitoring.llm_cost_critical_usd",
40031
+ // GH #948: margin tolerance for the daily credit-reconciliation job
40032
+ "business.monitoring.margin_tolerance_percent": "business.monitoring.margin_tolerance_percent",
39898
40033
  // Business - Limits
39899
40034
  "business.limits.max_courses_per_creator": "business.limits.max_courses_per_creator",
39900
40035
  "business.limits.max_modules_per_course": "business.limits.max_modules_per_course",
@@ -39907,21 +40042,11 @@ var init_system_config = __esm({
39907
40042
  "feature.community_enabled": "feature.community_enabled",
39908
40043
  "feature.certificates_enabled": "feature.certificates_enabled",
39909
40044
  "feature.maintenance_mode": "feature.maintenance_mode",
39910
- "feature.new_user_registration": "feature.new_user_registration",
39911
40045
  "feature.require_creator_mfa": "feature.require_creator_mfa",
39912
40046
  "feature.require_admin_mfa": "feature.require_admin_mfa",
39913
- "feature.security.session_timeout_minutes": "feature.security.session_timeout_minutes",
39914
- "feature.security.max_login_attempts": "feature.security.max_login_attempts",
39915
- // Platform Identity
39916
- "platform.identity.name": "platform.identity.name",
39917
- "platform.identity.url": "platform.identity.url",
39918
- "platform.identity.logo_url": "platform.identity.logo_url",
39919
- "platform.identity.description": "platform.identity.description",
40047
+ // GH #947: feature.security.*, platform.identity.* and the four
40048
+ // notification.email.* toggles purged — zero functional readers.
39920
40049
  // Notification Settings
39921
- "notification.email.welcome_enabled": "notification.email.welcome_enabled",
39922
- "notification.email.course_approved_enabled": "notification.email.course_approved_enabled",
39923
- "notification.email.new_enrollment_enabled": "notification.email.new_enrollment_enabled",
39924
- "notification.email.admin_system_alerts": "notification.email.admin_system_alerts",
39925
40050
  "notification.sender.from_address": "notification.sender.from_address",
39926
40051
  "notification.sender.from_name": "notification.sender.from_name"
39927
40052
  };
@@ -41192,7 +41317,6 @@ var init_relations_auth = __esm({
41192
41317
  init_user_regional_settings();
41193
41318
  init_users();
41194
41319
  init_validation_attempts();
41195
- init_admin_settings_audit();
41196
41320
  init_brainstormings();
41197
41321
  init_course_review_snapshots();
41198
41322
  init_learning_paths();
@@ -41250,7 +41374,6 @@ var init_relations_auth = __esm({
41250
41374
  dailyGoal: one(dailyGoals2),
41251
41375
  dailyGoalProgress: many(dailyGoalProgress2),
41252
41376
  regionalSettings: one(userRegionalSettings),
41253
- adminSettingsAuditLogs: many(adminSettingsAudit),
41254
41377
  creatorAuditLogs: many(creatorAuditLog),
41255
41378
  lessonRefinements: many(lessonRefinements),
41256
41379
  courseReviewSnapshots: many(courseReviewSnapshots),
@@ -43035,6 +43158,77 @@ var init_idempotency = __esm({
43035
43158
  }
43036
43159
  });
43037
43160
 
43161
+ // ../../packages/database/src/schema/referrals.ts
43162
+ var referralCodes, referrals;
43163
+ var init_referrals = __esm({
43164
+ "../../packages/database/src/schema/referrals.ts"() {
43165
+ "use strict";
43166
+ init_pg_core();
43167
+ init_users();
43168
+ referralCodes = pgTable("referral_codes", {
43169
+ userId: uuid("user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
43170
+ code: varchar("code", { length: 16 }).notNull().unique(),
43171
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
43172
+ });
43173
+ referrals = pgTable("referrals", {
43174
+ // PK = one referrer per person, one bonus per person, DB-enforced.
43175
+ referredUserId: uuid("referred_user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
43176
+ // null = referrer account deleted; referred keeps their referral record.
43177
+ referrerUserId: uuid("referrer_user_id").references(() => users.id, { onDelete: "set null" }),
43178
+ code: varchar("code", { length: 16 }).notNull(),
43179
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43180
+ // Set exactly once by the bonus claim (UPDATE ... WHERE qualified_at IS NULL).
43181
+ qualifiedAt: timestamp("qualified_at", { withTimezone: true })
43182
+ });
43183
+ }
43184
+ });
43185
+
43186
+ // ../../packages/database/src/schema/user-consents.ts
43187
+ var userConsentDocumentEnum, userConsentSurfaceEnum, userConsents;
43188
+ var init_user_consents = __esm({
43189
+ "../../packages/database/src/schema/user-consents.ts"() {
43190
+ "use strict";
43191
+ init_pg_core();
43192
+ init_users();
43193
+ userConsentDocumentEnum = pgEnum("user_consent_document", ["terms", "privacy"]);
43194
+ userConsentSurfaceEnum = pgEnum("user_consent_surface", [
43195
+ "signup_form",
43196
+ // password signup, /signup
43197
+ "oauth",
43198
+ // provider buttons, on /signup AND on /login
43199
+ "magic_link",
43200
+ // magic-link request form
43201
+ "login_form"
43202
+ // /login, login-time capture for pre-chokepoint accounts (GH #1007)
43203
+ ]);
43204
+ userConsents = pgTable(
43205
+ "user_consents",
43206
+ {
43207
+ id: uuid("id").primaryKey().defaultRandom(),
43208
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
43209
+ document: userConsentDocumentEnum("document").notNull(),
43210
+ // Policy version the person was shown, e.g. "v1.0" or "v1.0-BR". Width matches
43211
+ // countries.terms_version so a per-jurisdiction scheme stays possible later.
43212
+ version: varchar("version", { length: 20 }).notNull(),
43213
+ // Which translation was on screen — 5 locales means 5 different texts.
43214
+ locale: varchar("locale", { length: 10 }).notNull(),
43215
+ surface: userConsentSurfaceEnum("surface").notNull(),
43216
+ acknowledgedAt: timestamp("acknowledged_at", { withTimezone: true }).defaultNow().notNull()
43217
+ },
43218
+ (table) => ({
43219
+ // Idempotency, not just a constraint: the OAuth callback runs on EVERY login,
43220
+ // so the writer must be safe to call repeatedly. The left-hand prefix also
43221
+ // serves every "what has this user acknowledged?" read, so no second index.
43222
+ userDocumentVersionUnique: uniqueIndex("user_consents_user_document_version_unique").on(
43223
+ table.userId,
43224
+ table.document,
43225
+ table.version
43226
+ )
43227
+ })
43228
+ );
43229
+ }
43230
+ });
43231
+
43038
43232
  // ../../packages/database/src/schema/user-deletions.ts
43039
43233
  var userDeletionStatusEnum, userDeletions;
43040
43234
  var init_user_deletions = __esm({
@@ -43206,6 +43400,87 @@ var init_email_delivery = __esm({
43206
43400
  }
43207
43401
  });
43208
43402
 
43403
+ // ../../packages/database/src/schema/subscription-redemptions.ts
43404
+ var subscriptionCourseRedemptions;
43405
+ var init_subscription_redemptions = __esm({
43406
+ "../../packages/database/src/schema/subscription-redemptions.ts"() {
43407
+ "use strict";
43408
+ init_pg_core();
43409
+ init_users();
43410
+ init_courses3();
43411
+ init_enrollments();
43412
+ init_credit_packages();
43413
+ subscriptionCourseRedemptions = pgTable(
43414
+ "subscription_course_redemptions",
43415
+ {
43416
+ id: uuid("id").defaultRandom().primaryKey(),
43417
+ subscriptionId: uuid("subscription_id").references(() => creditSubscriptions.id, { onDelete: "restrict" }).notNull(),
43418
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
43419
+ courseId: uuid("course_id").references(() => courses.id, { onDelete: "restrict" }).notNull(),
43420
+ enrollmentId: uuid("enrollment_id").references(() => enrollments.id, { onDelete: "restrict" }).notNull(),
43421
+ cycleStart: timestamp("cycle_start", { withTimezone: true }).notNull(),
43422
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
43423
+ },
43424
+ (table) => ({
43425
+ oncePerCycle: uniqueIndex("scr_subscription_cycle_uq").on(
43426
+ table.subscriptionId,
43427
+ table.cycleStart
43428
+ ),
43429
+ userIdx: index("scr_user_idx").on(table.userId)
43430
+ })
43431
+ );
43432
+ }
43433
+ });
43434
+
43435
+ // ../../packages/database/src/schema/operation-billing-rates.ts
43436
+ var operationBillingRates, operationBillingRatesHistory;
43437
+ var init_operation_billing_rates = __esm({
43438
+ "../../packages/database/src/schema/operation-billing-rates.ts"() {
43439
+ "use strict";
43440
+ init_pg_core();
43441
+ init_users();
43442
+ operationBillingRates = pgTable(
43443
+ "operation_billing_rates",
43444
+ {
43445
+ id: uuid("id").primaryKey().defaultRandom(),
43446
+ operationType: varchar("operation_type", { length: 100 }).notNull().unique(),
43447
+ chargedInputPer1M: decimal("charged_input_per_1m", {
43448
+ precision: 10,
43449
+ scale: 4,
43450
+ mode: "number"
43451
+ }).notNull(),
43452
+ chargedOutputPer1M: decimal("charged_output_per_1m", {
43453
+ precision: 10,
43454
+ scale: 4,
43455
+ mode: "number"
43456
+ }).notNull(),
43457
+ isActive: boolean("is_active").notNull().default(true),
43458
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
43459
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
43460
+ },
43461
+ (table) => [index("idx_operation_billing_rates_active").on(table.isActive)]
43462
+ );
43463
+ operationBillingRatesHistory = pgTable(
43464
+ "operation_billing_rates_history",
43465
+ {
43466
+ id: uuid("id").primaryKey().defaultRandom(),
43467
+ operationType: varchar("operation_type", { length: 100 }).notNull(),
43468
+ action: varchar("action", { length: 20 }).notNull(),
43469
+ // 'create' | 'update' | 'delete'
43470
+ oldValues: jsonb("old_values"),
43471
+ newValues: jsonb("new_values"),
43472
+ changedBy: uuid("changed_by").notNull().references(() => users.id),
43473
+ changedAt: timestamp("changed_at", { withTimezone: true }).notNull().defaultNow(),
43474
+ reason: varchar("reason", { length: 500 })
43475
+ },
43476
+ (table) => [
43477
+ index("idx_obr_history_operation").on(table.operationType),
43478
+ index("idx_obr_history_date").on(table.changedAt)
43479
+ ]
43480
+ );
43481
+ }
43482
+ });
43483
+
43209
43484
  // ../../packages/database/src/schema/index.ts
43210
43485
  var schema_exports = {};
43211
43486
  __export(schema_exports, {
@@ -43221,6 +43496,8 @@ __export(schema_exports, {
43221
43496
  PACKAGE_MINIMUM_PRICES: () => PACKAGE_MINIMUM_PRICES,
43222
43497
  PACKAGE_SESSION_OPTIONS: () => PACKAGE_SESSION_OPTIONS,
43223
43498
  PACKAGE_VALIDITY_OPTIONS: () => PACKAGE_VALIDITY_OPTIONS,
43499
+ PIX_CHARGE_PURPOSES: () => PIX_CHARGE_PURPOSES,
43500
+ PIX_CHARGE_STATUSES: () => PIX_CHARGE_STATUSES,
43224
43501
  REVIEW_TAGS: () => REVIEW_TAGS,
43225
43502
  SUGGESTED_DISCOUNTS: () => SUGGESTED_DISCOUNTS,
43226
43503
  V3_CANCEL_REASON_TTL_SECONDS: () => V3_CANCEL_REASON_TTL_SECONDS,
@@ -43230,8 +43507,6 @@ __export(schema_exports, {
43230
43507
  actionTypeEnum: () => actionTypeEnum,
43231
43508
  adminActivityFeed: () => adminActivityFeed,
43232
43509
  adminActivityFeedRelations: () => adminActivityFeedRelations,
43233
- adminSettingsAudit: () => adminSettingsAudit,
43234
- adminSettingsAuditRelations: () => adminSettingsAuditRelations,
43235
43510
  announcementTypes: () => announcementTypes,
43236
43511
  announcements: () => announcements,
43237
43512
  applicableToEnum: () => applicableToEnum,
@@ -43398,7 +43673,6 @@ __export(schema_exports, {
43398
43673
  insertCourseMentoriaOfferSchema: () => insertCourseMentoriaOfferSchema,
43399
43674
  insertMentorshipCreditSchema: () => insertMentorshipCreditSchema,
43400
43675
  insertMentorshipPackageSchema: () => insertMentorshipPackageSchema,
43401
- insertPlatformSettingSchema: () => insertPlatformSettingSchema,
43402
43676
  isAmountSignValid: () => isAmountSignValid,
43403
43677
  isCreditUsable: () => isCreditUsable,
43404
43678
  isWithdrawableLedgerType: () => isWithdrawableLedgerType,
@@ -43486,9 +43760,10 @@ __export(schema_exports, {
43486
43760
  notificationsRelations: () => notificationsRelations2,
43487
43761
  onboardingAnalytics: () => onboardingAnalytics2,
43488
43762
  onboardingAnalyticsRelations: () => onboardingAnalyticsRelations2,
43763
+ operationBillingRates: () => operationBillingRates,
43764
+ operationBillingRatesHistory: () => operationBillingRatesHistory,
43489
43765
  optimizerRoleEnum: () => optimizerRoleEnum,
43490
43766
  parisAuthorizedCreators: () => parisAuthorizedCreators,
43491
- parseSettingValue: () => parseSettingValue,
43492
43767
  participantStatusEnum: () => participantStatusEnum,
43493
43768
  pathCourses: () => pathCourses2,
43494
43769
  pathCoursesRelations: () => pathCoursesRelations2,
@@ -43502,9 +43777,8 @@ __export(schema_exports, {
43502
43777
  payoutWithdrawalTransitions: () => payoutWithdrawalTransitions,
43503
43778
  payoutWithdrawals: () => payoutWithdrawals,
43504
43779
  pgBackendIdempotencyKeys: () => pgBackendIdempotencyKeys,
43780
+ pixCharges: () => pixCharges,
43505
43781
  pixKeyTypeEnum: () => pixKeyTypeEnum,
43506
- platformConfig: () => platformConfig,
43507
- platformSettings: () => platformSettings2,
43508
43782
  playgroundEvalScores: () => playgroundEvalScores,
43509
43783
  playgroundEvalScoresRelations: () => playgroundEvalScoresRelations,
43510
43784
  playgroundEvaluations: () => playgroundEvaluations,
@@ -43548,6 +43822,8 @@ __export(schema_exports, {
43548
43822
  pushTokens: () => pushTokens,
43549
43823
  pushTokensRelations: () => pushTokensRelations,
43550
43824
  quotaChangeHistory: () => quotaChangeHistory,
43825
+ referralCodes: () => referralCodes,
43826
+ referrals: () => referrals,
43551
43827
  refinementStatusEnum: () => refinementStatusEnum,
43552
43828
  refundAuditLog: () => refundAuditLog2,
43553
43829
  refundAuditLogRelations: () => refundAuditLogRelations2,
@@ -43570,7 +43846,6 @@ __export(schema_exports, {
43570
43846
  securityEventCategoryEnum: () => securityEventCategoryEnum,
43571
43847
  securityEventResultEnum: () => securityEventResultEnum,
43572
43848
  securityEventTypeEnum: () => securityEventTypeEnum,
43573
- selectPlatformSettingSchema: () => selectPlatformSettingSchema,
43574
43849
  serviceTypeEnum: () => serviceTypeEnum,
43575
43850
  sessionCancelledReasonEnum: () => sessionCancelledReasonEnum,
43576
43851
  sessionParticipants: () => sessionParticipants2,
@@ -43586,9 +43861,6 @@ __export(schema_exports, {
43586
43861
  sessionWaitlistRelations: () => sessionWaitlistRelations2,
43587
43862
  sessions: () => sessions,
43588
43863
  sessionsRelations: () => sessionsRelations2,
43589
- settingCategories: () => settingCategories,
43590
- settingKeys: () => settingKeys,
43591
- settingValueTypes: () => settingValueTypes,
43592
43864
  skillLevelEnum: () => skillLevelEnum,
43593
43865
  slaStatusEnum: () => slaStatusEnum,
43594
43866
  slashCommandAnalytics: () => slashCommandAnalytics2,
@@ -43598,7 +43870,6 @@ __export(schema_exports, {
43598
43870
  sparkChatSessions: () => sparkChatSessions2,
43599
43871
  sparkChatSessionsRelations: () => sparkChatSessionsRelations2,
43600
43872
  sparkPhaseValues: () => sparkPhaseValues,
43601
- stringifySettingValue: () => stringifySettingValue,
43602
43873
  studentCourseMemory: () => studentCourseMemory,
43603
43874
  studentCourseMemoryRelations: () => studentCourseMemoryRelations,
43604
43875
  studentFeedback: () => studentFeedback,
@@ -43628,6 +43899,7 @@ __export(schema_exports, {
43628
43899
  studyModuleSessionsRelations: () => studyModuleSessionsRelations,
43629
43900
  studyPhaseV2Values: () => studyPhaseV2Values,
43630
43901
  studyPhaseValues: () => studyPhaseValues,
43902
+ subscriptionCourseRedemptions: () => subscriptionCourseRedemptions,
43631
43903
  subscriptionPoolStatusEnum: () => subscriptionPoolStatusEnum,
43632
43904
  subscriptionSplitStatusEnum: () => subscriptionSplitStatusEnum,
43633
43905
  supportedCurrencies: () => supportedCurrencies,
@@ -43655,6 +43927,9 @@ __export(schema_exports, {
43655
43927
  userBadgesRelations: () => userBadgesRelations2,
43656
43928
  userChallengeScores: () => userChallengeScores,
43657
43929
  userChallengeScoresRelations: () => userChallengeScoresRelations,
43930
+ userConsentDocumentEnum: () => userConsentDocumentEnum,
43931
+ userConsentSurfaceEnum: () => userConsentSurfaceEnum,
43932
+ userConsents: () => userConsents,
43658
43933
  userDeletionStatusEnum: () => userDeletionStatusEnum,
43659
43934
  userDeletions: () => userDeletions,
43660
43935
  userFollows: () => userFollows2,
@@ -43714,6 +43989,7 @@ var init_schema3 = __esm({
43714
43989
  init_creator_index();
43715
43990
  init_webhook_events();
43716
43991
  init_transactions();
43992
+ init_pix_charges();
43717
43993
  init_coupons();
43718
43994
  init_coupon_usages();
43719
43995
  init_onboarding_analytics();
@@ -43726,8 +44002,6 @@ var init_schema3 = __esm({
43726
44002
  init_user_follows();
43727
44003
  init_spark_chat_sessions();
43728
44004
  init_study_index();
43729
- init_platform_settings();
43730
- init_admin_settings_audit();
43731
44005
  init_admin_activity_feed();
43732
44006
  init_generation_state();
43733
44007
  init_security_audit_log();
@@ -43775,7 +44049,6 @@ var init_schema3 = __esm({
43775
44049
  init_relations_community();
43776
44050
  init_relations_gamification();
43777
44051
  init_moderation_log();
43778
- init_admin_settings_audit();
43779
44052
  init_creator_audit_log();
43780
44053
  init_banks();
43781
44054
  init_course_qa_reports();
@@ -43797,10 +44070,14 @@ var init_schema3 = __esm({
43797
44070
  init_course_visibility();
43798
44071
  init_course_allowlist();
43799
44072
  init_idempotency();
44073
+ init_referrals();
44074
+ init_user_consents();
43800
44075
  init_user_deletions();
43801
44076
  init_user_profiles();
43802
44077
  init_user_resource_audits();
43803
44078
  init_email_delivery();
44079
+ init_subscription_redemptions();
44080
+ init_operation_billing_rates();
43804
44081
  }
43805
44082
  });
43806
44083
 
@@ -43862,7 +44139,6 @@ var init_src3 = __esm({
43862
44139
  init_user_follows();
43863
44140
  init_generation_state();
43864
44141
  init_community();
43865
- init_platform_settings();
43866
44142
  init_marketing();
43867
44143
  init_security_audit_log();
43868
44144
  init_credit_movement_audit_log();
@@ -44201,7 +44477,7 @@ async function runStart(opts, deps = defaultDeps3) {
44201
44477
  deps.error(msg);
44202
44478
  }
44203
44479
  }
44204
- var logger9, defaultDeps3, startCommand;
44480
+ var logger10, defaultDeps3, startCommand;
44205
44481
  var init_start = __esm({
44206
44482
  "src/commands/start.ts"() {
44207
44483
  "use strict";
@@ -44214,7 +44490,7 @@ var init_start = __esm({
44214
44490
  init_status();
44215
44491
  init_formatter();
44216
44492
  init_errors();
44217
- logger9 = createLogger("cli:start");
44493
+ logger10 = createLogger("cli:start");
44218
44494
  defaultDeps3 = {
44219
44495
  requireSession,
44220
44496
  requireActiveCourse,
@@ -44228,7 +44504,7 @@ var init_start = __esm({
44228
44504
  output,
44229
44505
  error,
44230
44506
  stderrWrite: (message) => process.stderr.write(message),
44231
- logger: logger9
44507
+ logger: logger10
44232
44508
  };
44233
44509
  startCommand = new Command8("start").description("Start (or resume) the current module of the active course").option("--json", "Output structured JSON").action(async (opts) => {
44234
44510
  await runStart(opts);
@@ -44238,7 +44514,7 @@ var init_start = __esm({
44238
44514
 
44239
44515
  // src/commands/start-next.ts
44240
44516
  import { Command as Command9 } from "commander";
44241
- var logger10, startNextCommand;
44517
+ var logger11, startNextCommand;
44242
44518
  var init_start_next = __esm({
44243
44519
  "src/commands/start-next.ts"() {
44244
44520
  "use strict";
@@ -44250,7 +44526,7 @@ var init_start_next = __esm({
44250
44526
  init_workspace_state();
44251
44527
  init_formatter();
44252
44528
  init_errors();
44253
- logger10 = createLogger("cli:start-next");
44529
+ logger11 = createLogger("cli:start-next");
44254
44530
  startNextCommand = new Command9("start-next").description("Transition to the next module after completing the current one").option("--json", "Output structured JSON").action(async (opts) => {
44255
44531
  try {
44256
44532
  const session = await requireSession();
@@ -44258,7 +44534,7 @@ var init_start_next = __esm({
44258
44534
  const driftWarning = await checkCourseDrift();
44259
44535
  if (driftWarning) process.stderr.write(driftWarning + "\n");
44260
44536
  const data = createHttpProvider(session.apiUrl, session.token);
44261
- const deps = { data, logger: logger10 };
44537
+ const deps = { data, logger: logger11 };
44262
44538
  const moduleData = await startNextModule({ enrollmentId: activeCourse.enrollmentId }, deps);
44263
44539
  const ws = await findWorkspaceState();
44264
44540
  if (ws)
@@ -44295,7 +44571,7 @@ var init_start_next = __esm({
44295
44571
 
44296
44572
  // src/commands/next.ts
44297
44573
  import { Command as Command10 } from "commander";
44298
- var logger11, nextCommand;
44574
+ var logger12, nextCommand;
44299
44575
  var init_next = __esm({
44300
44576
  "src/commands/next.ts"() {
44301
44577
  "use strict";
@@ -44307,7 +44583,7 @@ var init_next = __esm({
44307
44583
  init_workspace_state();
44308
44584
  init_formatter();
44309
44585
  init_errors();
44310
- logger11 = createLogger("cli:next");
44586
+ logger12 = createLogger("cli:next");
44311
44587
  nextCommand = new Command10("next").description("Advance to the next lesson in the active course").option("--json", "Output structured JSON").action(async (opts) => {
44312
44588
  try {
44313
44589
  const session = await requireSession();
@@ -44315,7 +44591,7 @@ var init_next = __esm({
44315
44591
  const driftWarning = await checkCourseDrift();
44316
44592
  if (driftWarning) process.stderr.write(driftWarning + "\n");
44317
44593
  const data = createHttpProvider(session.apiUrl, session.token);
44318
- const deps = { data, logger: logger11 };
44594
+ const deps = { data, logger: logger12 };
44319
44595
  const lessonData = await nextLesson(
44320
44596
  { enrollmentId: activeCourse.enrollmentId, userConfirmation: "cli-next" },
44321
44597
  deps
@@ -44388,7 +44664,7 @@ function replacePlaceholders(text2, workspacePath, courseTitle) {
44388
44664
  const base = path13.basename(workspacePath) === ".tostudy" ? path13.dirname(workspacePath) : workspacePath;
44389
44665
  const slug = courseSlug(courseTitle);
44390
44666
  const vaultPath = resolveVaultPath(workspacePath, slug);
44391
- return text2.replaceAll("$WORKSPACE", base).replaceAll("$VAULT", vaultPath);
44667
+ return text2.replace(/\$WORKSPACE(?![A-Za-z0-9_])/g, () => base).replace(/\$VAULT(?![A-Za-z0-9_])/g, () => vaultPath);
44392
44668
  }
44393
44669
  function formatLessonContent(data, workspacePath, courseTitle) {
44394
44670
  const time4 = adjustTimeEstimate(data.type, data.estimatedTimeMinutes);
@@ -44429,7 +44705,7 @@ function formatLessonContent(data, workspacePath, courseTitle) {
44429
44705
  }
44430
44706
  return lines.join("\n");
44431
44707
  }
44432
- var logger12, lessonCommand;
44708
+ var logger13, lessonCommand;
44433
44709
  var init_lesson = __esm({
44434
44710
  "src/commands/lesson.ts"() {
44435
44711
  "use strict";
@@ -44442,7 +44718,7 @@ var init_lesson = __esm({
44442
44718
  init_formatter();
44443
44719
  init_resolve();
44444
44720
  init_errors();
44445
- logger12 = createLogger("cli:lesson");
44721
+ logger13 = createLogger("cli:lesson");
44446
44722
  lessonCommand = new Command11("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").option(
44447
44723
  "--read <lessonId>",
44448
44724
  "Read a specific lesson by id (works on archived / read-only courses)"
@@ -44453,7 +44729,7 @@ var init_lesson = __esm({
44453
44729
  const driftWarning = await checkCourseDrift();
44454
44730
  if (driftWarning) process.stderr.write(driftWarning + "\n");
44455
44731
  const data = createHttpProvider(session.apiUrl, session.token);
44456
- const deps = { data, logger: logger12 };
44732
+ const deps = { data, logger: logger13 };
44457
44733
  let lessonId = opts.read ?? activeCourse.currentLessonId;
44458
44734
  if (!lessonId) {
44459
44735
  try {
@@ -44548,7 +44824,7 @@ function formatKnowledge(data) {
44548
44824
  });
44549
44825
  return lines.join("\n").trimEnd();
44550
44826
  }
44551
- var logger13, knowledgeCommand;
44827
+ var logger14, knowledgeCommand;
44552
44828
  var init_knowledge2 = __esm({
44553
44829
  "src/commands/knowledge.ts"() {
44554
44830
  "use strict";
@@ -44556,7 +44832,7 @@ var init_knowledge2 = __esm({
44556
44832
  init_http();
44557
44833
  init_guards();
44558
44834
  init_formatter();
44559
- logger13 = createLogger("cli:knowledge");
44835
+ logger14 = createLogger("cli:knowledge");
44560
44836
  knowledgeCommand = new Command12("knowledge").description("Show the active course's knowledge base (base de conhecimento)").option("--json", "Output structured JSON").action(async (opts) => {
44561
44837
  try {
44562
44838
  const activeCourse = await requireActiveCourse();
@@ -44580,7 +44856,7 @@ var init_knowledge2 = __esm({
44580
44856
  else error(msg);
44581
44857
  return;
44582
44858
  }
44583
- logger13.warn("knowledge failed", { error: msg });
44859
+ logger14.warn("knowledge failed", { error: msg });
44584
44860
  if (opts.json) jsonError(msg);
44585
44861
  else error(msg);
44586
44862
  }
@@ -44590,7 +44866,7 @@ var init_knowledge2 = __esm({
44590
44866
 
44591
44867
  // src/commands/hint.ts
44592
44868
  import { Command as Command13 } from "commander";
44593
- var logger14, hintCommand;
44869
+ var logger15, hintCommand;
44594
44870
  var init_hint = __esm({
44595
44871
  "src/commands/hint.ts"() {
44596
44872
  "use strict";
@@ -44601,7 +44877,7 @@ var init_hint = __esm({
44601
44877
  init_course_state();
44602
44878
  init_formatter();
44603
44879
  init_errors();
44604
- logger14 = createLogger("cli:hint");
44880
+ logger15 = createLogger("cli:hint");
44605
44881
  hintCommand = new Command13("hint").description("Get a progressive hint for the current exercise").option("--json", "Output structured JSON").action(async (opts) => {
44606
44882
  try {
44607
44883
  const session = await requireSession();
@@ -44609,7 +44885,7 @@ var init_hint = __esm({
44609
44885
  const driftWarning = await checkCourseDrift();
44610
44886
  if (driftWarning) process.stderr.write(driftWarning + "\n");
44611
44887
  const data = createHttpProvider(session.apiUrl, session.token);
44612
- const deps = { data, logger: logger14 };
44888
+ const deps = { data, logger: logger15 };
44613
44889
  const hint = await getHint(
44614
44890
  { userId: session.userId, enrollmentId: activeCourse.enrollmentId },
44615
44891
  deps
@@ -44963,7 +45239,7 @@ var init_init_template = __esm({
44963
45239
  import fs12 from "node:fs";
44964
45240
  import path14 from "node:path";
44965
45241
  import { Command as Command14 } from "commander";
44966
- var logger15, validateCommand;
45242
+ var logger16, validateCommand;
44967
45243
  var init_validate = __esm({
44968
45244
  "src/commands/validate.ts"() {
44969
45245
  "use strict";
@@ -44976,7 +45252,7 @@ var init_validate = __esm({
44976
45252
  init_formatter();
44977
45253
  init_init_template();
44978
45254
  init_errors();
44979
- logger15 = createLogger("cli:validate");
45255
+ logger16 = createLogger("cli:validate");
44980
45256
  validateCommand = new Command14("validate").description("Validate your solution for the current exercise").argument("[file]", "Path to the solution file to read").option("--stdin", "Read solution from stdin instead of a file").option("--json", "Output structured JSON").action(async (file2, opts) => {
44981
45257
  try {
44982
45258
  const session = await requireSession();
@@ -44984,7 +45260,7 @@ var init_validate = __esm({
44984
45260
  const driftWarning = await checkCourseDrift();
44985
45261
  if (driftWarning) process.stderr.write(driftWarning + "\n");
44986
45262
  const data = createHttpProvider(session.apiUrl, session.token);
44987
- const deps = { data, logger: logger15 };
45263
+ const deps = { data, logger: logger16 };
44988
45264
  let lessonId = activeCourse.currentLessonId;
44989
45265
  if (!lessonId) {
44990
45266
  try {
@@ -45433,7 +45709,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
45433
45709
  deps.output(artifacts.learnerBrief, { json: false });
45434
45710
  }
45435
45711
  }
45436
- var logger16, defaultDeps4, initCommand;
45712
+ var logger17, defaultDeps4, initCommand;
45437
45713
  var init_init = __esm({
45438
45714
  "src/commands/init.ts"() {
45439
45715
  "use strict";
@@ -45449,7 +45725,7 @@ var init_init = __esm({
45449
45725
  init_api3();
45450
45726
  init_instruction_pipeline();
45451
45727
  init_pipeline_deps();
45452
- logger16 = createLogger("cli:init");
45728
+ logger17 = createLogger("cli:init");
45453
45729
  defaultDeps4 = {
45454
45730
  getSession,
45455
45731
  findWorkspaceState,
@@ -45463,7 +45739,7 @@ var init_init = __esm({
45463
45739
  saveCourseLearnerProfile,
45464
45740
  buildInitArtifacts,
45465
45741
  output,
45466
- logger: logger16,
45742
+ logger: logger17,
45467
45743
  createHttpProvider,
45468
45744
  resolveAndGenerate,
45469
45745
  buildPipelineDeps
@@ -45785,7 +46061,7 @@ import { Command as Command17 } from "commander";
45785
46061
  import path17 from "node:path";
45786
46062
  import os9 from "node:os";
45787
46063
  import fs15 from "node:fs/promises";
45788
- var logger17, workspaceCommand;
46064
+ var logger18, workspaceCommand;
45789
46065
  var init_workspace2 = __esm({
45790
46066
  "src/commands/workspace.ts"() {
45791
46067
  "use strict";
@@ -45795,7 +46071,7 @@ var init_workspace2 = __esm({
45795
46071
  init_course_state();
45796
46072
  init_resolve();
45797
46073
  init_errors();
45798
- logger17 = createLogger("cli:workspace");
46074
+ logger18 = createLogger("cli:workspace");
45799
46075
  workspaceCommand = new Command17("workspace").description(
45800
46076
  getErrors().workspaceCommandDescription
45801
46077
  );
@@ -45861,7 +46137,7 @@ Pr\xF3ximo passo: tostudy export
45861
46137
  );
45862
46138
  }
45863
46139
  } catch (err) {
45864
- logger17.error("workspace setup failed", { error: err });
46140
+ logger18.error("workspace setup failed", { error: err });
45865
46141
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
45866
46142
  `);
45867
46143
  process.exit(1);
@@ -45969,7 +46245,7 @@ import { Command as Command18 } from "commander";
45969
46245
  import path18 from "node:path";
45970
46246
  import os10 from "node:os";
45971
46247
  import fs16 from "node:fs/promises";
45972
- var logger18, exportCommand;
46248
+ var logger19, exportCommand;
45973
46249
  var init_export = __esm({
45974
46250
  "src/commands/export.ts"() {
45975
46251
  "use strict";
@@ -45980,7 +46256,7 @@ var init_export = __esm({
45980
46256
  init_course_state();
45981
46257
  init_resolve();
45982
46258
  init_errors();
45983
- logger18 = createLogger("cli:export");
46259
+ logger19 = createLogger("cli:export");
45984
46260
  exportCommand = new Command18("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(os10.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
45985
46261
  try {
45986
46262
  const session = await requireSession();
@@ -46008,7 +46284,7 @@ var init_export = __esm({
46008
46284
  }
46009
46285
  if (!hasConfig) {
46010
46286
  const slug = courseSlug(activeCourse.courseTitle);
46011
- logger18.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
46287
+ logger19.info("Auto-initializing workspace", { workspacePath: ws.workspacePath });
46012
46288
  await fs16.mkdir(ws.workspacePath, { recursive: true });
46013
46289
  for (const dir of ["exercises", "generated", "notes", "diagrams"]) {
46014
46290
  await fs16.mkdir(path18.join(ws.workspacePath, dir), { recursive: true });
@@ -46070,7 +46346,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
46070
46346
  );
46071
46347
  }
46072
46348
  } catch (err) {
46073
- logger18.error("export failed", { error: err });
46349
+ logger19.error("export failed", { error: err });
46074
46350
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
46075
46351
  `);
46076
46352
  process.exit(1);
@@ -46084,7 +46360,7 @@ import { Command as Command19 } from "commander";
46084
46360
  import { execFile as execFile3 } from "node:child_process";
46085
46361
  import path19 from "node:path";
46086
46362
  import os11 from "node:os";
46087
- var logger19, openCommand;
46363
+ var logger20, openCommand;
46088
46364
  var init_open = __esm({
46089
46365
  "src/commands/open.ts"() {
46090
46366
  "use strict";
@@ -46093,7 +46369,7 @@ var init_open = __esm({
46093
46369
  init_course_state();
46094
46370
  init_resolve();
46095
46371
  init_errors();
46096
- logger19 = createLogger("cli:open");
46372
+ logger20 = createLogger("cli:open");
46097
46373
  openCommand = new Command19("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path19.join(os11.homedir(), "study")).action(async (opts) => {
46098
46374
  try {
46099
46375
  const activeCourse = await requireActiveCourse();
@@ -46111,7 +46387,7 @@ var init_open = __esm({
46111
46387
  const editor = process.env["EDITOR"] ?? "code";
46112
46388
  execFile3(editor, [ws.workspacePath], (err) => {
46113
46389
  if (err) {
46114
- logger19.error("open failed", { editor, workspacePath: ws.workspacePath });
46390
+ logger20.error("open failed", { editor, workspacePath: ws.workspacePath });
46115
46391
  process.stderr.write(`\u274C Falha ao abrir: ${err.message}
46116
46392
  `);
46117
46393
  process.exit(1);
@@ -46120,7 +46396,7 @@ var init_open = __esm({
46120
46396
  `);
46121
46397
  });
46122
46398
  } catch (err) {
46123
- logger19.error("open command failed", { error: err });
46399
+ logger20.error("open command failed", { error: err });
46124
46400
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
46125
46401
  `);
46126
46402
  process.exit(1);
@@ -46192,7 +46468,7 @@ import { Command as Command20 } from "commander";
46192
46468
  import path21 from "node:path";
46193
46469
  import os12 from "node:os";
46194
46470
  import fs18 from "node:fs/promises";
46195
- var logger20, vaultCommand;
46471
+ var logger21, vaultCommand;
46196
46472
  var init_vault2 = __esm({
46197
46473
  "src/commands/vault.ts"() {
46198
46474
  "use strict";
@@ -46204,7 +46480,7 @@ var init_vault2 = __esm({
46204
46480
  init_course_state();
46205
46481
  init_resolve();
46206
46482
  init_errors();
46207
- logger20 = createLogger("cli:vault");
46483
+ logger21 = createLogger("cli:vault");
46208
46484
  vaultCommand = new Command20("vault").description("Gerenciar vault Obsidian do curso");
46209
46485
  vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path21.join(os12.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
46210
46486
  try {
@@ -46249,7 +46525,7 @@ var init_vault2 = __esm({
46249
46525
  activeCourse.courseId,
46250
46526
  slug
46251
46527
  );
46252
- logger20.info("Vault generated", {
46528
+ logger21.info("Vault generated", {
46253
46529
  courseId: activeCourse.courseId,
46254
46530
  vaultPath: result.vaultPath,
46255
46531
  filesWritten: result.filesWritten
@@ -46282,7 +46558,7 @@ Para visualizar:
46282
46558
  );
46283
46559
  }
46284
46560
  } catch (err) {
46285
- logger20.error("vault init failed", { error: err });
46561
+ logger21.error("vault init failed", { error: err });
46286
46562
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
46287
46563
  `);
46288
46564
  process.exit(1);
@@ -46312,7 +46588,7 @@ Para visualizar:
46312
46588
  process.exit(1);
46313
46589
  }
46314
46590
  const data = createHttpProvider(session.apiUrl, session.token);
46315
- const deps = { data, logger: logger20 };
46591
+ const deps = { data, logger: logger21 };
46316
46592
  const progress3 = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
46317
46593
  const markerPath = path21.join(vaultPath, ".ana-vault.json");
46318
46594
  const markerRaw = await fs18.readFile(markerPath, "utf-8");
@@ -46366,7 +46642,7 @@ Para visualizar:
46366
46642
  );
46367
46643
  }
46368
46644
  } catch (err) {
46369
- logger20.error("vault sync failed", { error: err });
46645
+ logger21.error("vault sync failed", { error: err });
46370
46646
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
46371
46647
  `);
46372
46648
  process.exit(1);
@@ -46446,7 +46722,7 @@ var init_profile = __esm({
46446
46722
 
46447
46723
  // src/commands/sync.ts
46448
46724
  import { Command as Command22 } from "commander";
46449
- var logger21, syncCommand;
46725
+ var logger22, syncCommand;
46450
46726
  var init_sync = __esm({
46451
46727
  "src/commands/sync.ts"() {
46452
46728
  "use strict";
@@ -46455,15 +46731,18 @@ var init_sync = __esm({
46455
46731
  init_formatter();
46456
46732
  init_instruction_pipeline();
46457
46733
  init_pipeline_deps();
46458
- logger21 = createLogger("cli:sync");
46459
- syncCommand = new Command22("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").action(async (opts) => {
46734
+ logger22 = createLogger("cli:sync");
46735
+ syncCommand = new Command22("sync").description("Regenerate instruction files with updated progress").option("--json", "Output structured JSON").option(
46736
+ "--all-runtimes",
46737
+ "Write instruction files for every supported runtime, even undetected ones"
46738
+ ).action(async (opts) => {
46460
46739
  try {
46461
46740
  await requireSession();
46462
46741
  const activeCourse = await requireActiveCourse();
46463
46742
  const pipelineResult = await resolveAndGenerate(
46464
46743
  { courseId: activeCourse.courseId, enrollmentId: activeCourse.enrollmentId },
46465
46744
  { forceRefresh: true, collectT1Interactive: false, collectT2Interactive: false },
46466
- { cwd: process.cwd(), deps: buildPipelineDeps() }
46745
+ { cwd: process.cwd(), deps: buildPipelineDeps({ allRuntimes: opts.allRuntimes }) }
46467
46746
  );
46468
46747
  if (opts.json) {
46469
46748
  output(
@@ -46494,7 +46773,7 @@ var init_sync = __esm({
46494
46773
  } catch (err) {
46495
46774
  const msg = err instanceof Error ? err.message : String(err);
46496
46775
  if (msg.includes("process.exit")) return;
46497
- logger21.warn("sync failed", { error: msg });
46776
+ logger22.warn("sync failed", { error: msg });
46498
46777
  error(msg);
46499
46778
  }
46500
46779
  });
@@ -46503,7 +46782,7 @@ var init_sync = __esm({
46503
46782
 
46504
46783
  // src/commands/brief.ts
46505
46784
  import { Command as Command23 } from "commander";
46506
- var logger22, briefCommand;
46785
+ var logger23, briefCommand;
46507
46786
  var init_brief = __esm({
46508
46787
  "src/commands/brief.ts"() {
46509
46788
  "use strict";
@@ -46512,7 +46791,7 @@ var init_brief = __esm({
46512
46791
  init_cache();
46513
46792
  init_api();
46514
46793
  init_formatter();
46515
- logger22 = createLogger("cli:brief");
46794
+ logger23 = createLogger("cli:brief");
46516
46795
  briefCommand = new Command23("brief").description("Show your base learner brief (T1) status and content").option("--json", "Output structured JSON").action(async (opts) => {
46517
46796
  try {
46518
46797
  const session = await requireSession();
@@ -46546,7 +46825,7 @@ var init_brief = __esm({
46546
46825
  }
46547
46826
  output(lines.join("\n"), { json: false });
46548
46827
  } catch (err) {
46549
- logger22.error("Failed to show brief", { err });
46828
+ logger23.error("Failed to show brief", { err });
46550
46829
  error(`Erro ao buscar brief: ${err instanceof Error ? err.message : String(err)}`);
46551
46830
  }
46552
46831
  });
@@ -46555,7 +46834,7 @@ var init_brief = __esm({
46555
46834
 
46556
46835
  // src/commands/brief-create.ts
46557
46836
  import { Command as Command24 } from "commander";
46558
- var logger23, briefCreateCommand;
46837
+ var logger24, briefCreateCommand;
46559
46838
  var init_brief_create = __esm({
46560
46839
  "src/commands/brief-create.ts"() {
46561
46840
  "use strict";
@@ -46565,7 +46844,7 @@ var init_brief_create = __esm({
46565
46844
  init_api();
46566
46845
  init_cache();
46567
46846
  init_formatter();
46568
- logger23 = createLogger("cli:brief-create");
46847
+ logger24 = createLogger("cli:brief-create");
46569
46848
  briefCreateCommand = new Command24("brief-create").description("Create your base learner brief via interactive prompts (T1 bootstrap)").action(async () => {
46570
46849
  try {
46571
46850
  const session = await requireSession();
@@ -46590,7 +46869,7 @@ var init_brief_create = __esm({
46590
46869
  ];
46591
46870
  output(doneLines.join("\n"), { json: false });
46592
46871
  } catch (err) {
46593
- logger23.error("Failed to create brief", { err });
46872
+ logger24.error("Failed to create brief", { err });
46594
46873
  error(`Erro ao criar brief: ${err instanceof Error ? err.message : String(err)}`);
46595
46874
  }
46596
46875
  });
@@ -46655,7 +46934,7 @@ async function saveModuleSummary(workspacePath, input2) {
46655
46934
  ""
46656
46935
  ].join("\n");
46657
46936
  fs19.writeFileSync(filePath, header + input2.summary, { mode: 384 });
46658
- logger24.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
46937
+ logger25.debug("Module summary saved", { moduleId: input2.moduleId, path: filePath });
46659
46938
  return filePath;
46660
46939
  }
46661
46940
  async function loadSessionContext(workspacePath) {
@@ -46676,19 +46955,19 @@ async function loadSessionContext(workspacePath) {
46676
46955
  }
46677
46956
  return { moduleSummaries: summaries };
46678
46957
  }
46679
- var logger24;
46958
+ var logger25;
46680
46959
  var init_storage = __esm({
46681
46960
  "src/sessions/storage.ts"() {
46682
46961
  "use strict";
46683
46962
  init_dist();
46684
- logger24 = createLogger("cli:sessions");
46963
+ logger25 = createLogger("cli:sessions");
46685
46964
  }
46686
46965
  });
46687
46966
 
46688
46967
  // src/commands/compact.ts
46689
46968
  import fs20 from "node:fs";
46690
46969
  import { Command as Command26 } from "commander";
46691
- var logger25, compactCommand;
46970
+ var logger26, compactCommand;
46692
46971
  var init_compact = __esm({
46693
46972
  "src/commands/compact.ts"() {
46694
46973
  "use strict";
@@ -46697,7 +46976,7 @@ var init_compact = __esm({
46697
46976
  init_workspace_state();
46698
46977
  init_storage();
46699
46978
  init_formatter();
46700
- logger25 = createLogger("cli:compact");
46979
+ logger26 = createLogger("cli:compact");
46701
46980
  compactCommand = new Command26("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) => {
46702
46981
  try {
46703
46982
  const activeCourse = await requireActiveCourse();
@@ -46718,7 +46997,7 @@ var init_compact = __esm({
46718
46997
  moduleTitle,
46719
46998
  summary
46720
46999
  });
46721
- logger25.debug("Compact summary saved", { moduleId, filePath });
47000
+ logger26.debug("Compact summary saved", { moduleId, filePath });
46722
47001
  if (opts.json) {
46723
47002
  output({ saved: true, path: filePath, moduleId }, { json: true });
46724
47003
  } else {
@@ -46727,7 +47006,7 @@ var init_compact = __esm({
46727
47006
  } catch (err) {
46728
47007
  const msg = err instanceof Error ? err.message : String(err);
46729
47008
  if (msg.includes("process.exit")) return;
46730
- logger25.warn("compact failed", { error: msg });
47009
+ logger26.warn("compact failed", { error: msg });
46731
47010
  if (opts.json) jsonError(msg);
46732
47011
  error(msg);
46733
47012
  }
@@ -46737,7 +47016,7 @@ var init_compact = __esm({
46737
47016
 
46738
47017
  // src/commands/context.ts
46739
47018
  import { Command as Command27 } from "commander";
46740
- var logger26, contextCommand;
47019
+ var logger27, contextCommand;
46741
47020
  var init_context = __esm({
46742
47021
  "src/commands/context.ts"() {
46743
47022
  "use strict";
@@ -46745,7 +47024,7 @@ var init_context = __esm({
46745
47024
  init_workspace_state();
46746
47025
  init_storage();
46747
47026
  init_formatter();
46748
- logger26 = createLogger("cli:context");
47027
+ logger27 = createLogger("cli:context");
46749
47028
  contextCommand = new Command27("context").description("Load session context (workspace state + module summaries) for LLM consumption").option("--json", "Output structured JSON").action(async (opts) => {
46750
47029
  try {
46751
47030
  const ws = await findWorkspaceState();
@@ -46773,7 +47052,7 @@ var init_context = __esm({
46773
47052
  })),
46774
47053
  totalModulesCompleted: sessionCtx.moduleSummaries.length
46775
47054
  };
46776
- logger26.debug("Context loaded", {
47055
+ logger27.debug("Context loaded", {
46777
47056
  courseId: ws.state.courseId,
46778
47057
  moduleSummaries: sessionCtx.moduleSummaries.length
46779
47058
  });
@@ -46787,7 +47066,7 @@ var init_context = __esm({
46787
47066
  } catch (err) {
46788
47067
  const msg = err instanceof Error ? err.message : String(err);
46789
47068
  if (msg.includes("process.exit")) return;
46790
- logger26.warn("context failed", { error: msg });
47069
+ logger27.warn("context failed", { error: msg });
46791
47070
  if (opts.json) jsonError(msg);
46792
47071
  error(msg);
46793
47072
  }
@@ -46797,7 +47076,7 @@ var init_context = __esm({
46797
47076
 
46798
47077
  // src/commands/memory.ts
46799
47078
  import { Command as Command28 } from "commander";
46800
- var logger27, memoryCommand;
47079
+ var logger28, memoryCommand;
46801
47080
  var init_memory = __esm({
46802
47081
  "src/commands/memory.ts"() {
46803
47082
  "use strict";
@@ -46806,7 +47085,7 @@ var init_memory = __esm({
46806
47085
  init_workspace_state();
46807
47086
  init_guards();
46808
47087
  init_formatter();
46809
- logger27 = createLogger("cli:memory");
47088
+ logger28 = createLogger("cli:memory");
46810
47089
  memoryCommand = new Command28("memory").description("Load accumulated student memory (learning profile + recent lessons) for the tutor").option("--json", "Output structured JSON").action(async (opts) => {
46811
47090
  try {
46812
47091
  const ws = await findWorkspaceState();
@@ -46830,7 +47109,7 @@ var init_memory = __esm({
46830
47109
  } catch (err) {
46831
47110
  const msg = err instanceof Error ? err.message : String(err);
46832
47111
  if (msg.includes("process.exit")) return;
46833
- logger27.warn("memory failed", { error: msg });
47112
+ logger28.warn("memory failed", { error: msg });
46834
47113
  if (opts.json) jsonError(msg);
46835
47114
  else error(msg);
46836
47115
  }
@@ -46840,7 +47119,7 @@ var init_memory = __esm({
46840
47119
 
46841
47120
  // src/commands/insight.ts
46842
47121
  import { Command as Command29 } from "commander";
46843
- var logger28, VALID_TYPES, insightCommand;
47122
+ var logger29, VALID_TYPES, insightCommand;
46844
47123
  var init_insight = __esm({
46845
47124
  "src/commands/insight.ts"() {
46846
47125
  "use strict";
@@ -46849,7 +47128,7 @@ var init_insight = __esm({
46849
47128
  init_workspace_state();
46850
47129
  init_guards();
46851
47130
  init_formatter();
46852
- logger28 = createLogger("cli:insight");
47131
+ logger29 = createLogger("cli:insight");
46853
47132
  VALID_TYPES = ["difficulty", "breakthrough", "question"];
46854
47133
  insightCommand = new Command29("insight").description(
46855
47134
  "Persist a student cognitive insight (difficulty | breakthrough | question) into course memory"
@@ -46891,7 +47170,7 @@ var init_insight = __esm({
46891
47170
  } catch (err) {
46892
47171
  const msg = err instanceof Error ? err.message : String(err);
46893
47172
  if (msg.includes("process.exit")) return;
46894
- logger28.warn("insight failed", { error: msg });
47173
+ logger29.warn("insight failed", { error: msg });
46895
47174
  if (opts.json) jsonError(msg);
46896
47175
  else error(msg);
46897
47176
  }
@@ -46904,7 +47183,7 @@ import { Command as Command30 } from "commander";
46904
47183
  function isExerciseLevel2(value) {
46905
47184
  return ALL_LEVELS.includes(value);
46906
47185
  }
46907
- var logger29, ALL_LEVELS, LEVEL_LABELS2, levelCommand;
47186
+ var logger30, ALL_LEVELS, LEVEL_LABELS2, levelCommand;
46908
47187
  var init_level = __esm({
46909
47188
  "src/commands/level.ts"() {
46910
47189
  "use strict";
@@ -46913,7 +47192,7 @@ var init_level = __esm({
46913
47192
  init_exercises();
46914
47193
  init_guards();
46915
47194
  init_formatter();
46916
- logger29 = createLogger("cli:level");
47195
+ logger30 = createLogger("cli:level");
46917
47196
  ALL_LEVELS = ["L0", "L1", "L2", "L3", "L4"];
46918
47197
  LEVEL_LABELS2 = {
46919
47198
  L0: "Pr\xE9-check (perguntas conceituais)",
@@ -46927,7 +47206,7 @@ var init_level = __esm({
46927
47206
  const session = await requireSession();
46928
47207
  const activeCourse = await requireActiveCourse();
46929
47208
  const data = createHttpProvider(session.apiUrl, session.token);
46930
- const deps = { data, logger: logger29 };
47209
+ const deps = { data, logger: logger30 };
46931
47210
  if (!rawLevel) {
46932
47211
  const levels = await getEnrollmentLevels({ enrollmentId: activeCourse.enrollmentId }, deps);
46933
47212
  if (opts.json) {
@@ -46986,7 +47265,7 @@ var init_level = __esm({
46986
47265
 
46987
47266
  // src/commands/theory.ts
46988
47267
  import { Command as Command31 } from "commander";
46989
- var logger30, EXERCISE_LEVELS2, theoryCommand;
47268
+ var logger31, EXERCISE_LEVELS2, theoryCommand;
46990
47269
  var init_theory = __esm({
46991
47270
  "src/commands/theory.ts"() {
46992
47271
  "use strict";
@@ -46994,7 +47273,7 @@ var init_theory = __esm({
46994
47273
  init_guards();
46995
47274
  init_formatter();
46996
47275
  init_errors();
46997
- logger30 = createLogger("cli:theory");
47276
+ logger31 = createLogger("cli:theory");
46998
47277
  EXERCISE_LEVELS2 = ["L0", "L1", "L2", "L3", "L4"];
46999
47278
  theoryCommand = new Command31("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) => {
47000
47279
  try {
@@ -47033,7 +47312,7 @@ var init_theory = __esm({
47033
47312
  currentLevel = levels.currentLevel;
47034
47313
  }
47035
47314
  const requestUrl = `${session.apiUrl}/api/cli/exercises/more-theory`;
47036
- logger30.debug("Requesting more theory", { lessonId, currentLevel });
47315
+ logger31.debug("Requesting more theory", { lessonId, currentLevel });
47037
47316
  const res = await fetch(requestUrl, {
47038
47317
  method: "POST",
47039
47318
  headers: {
@@ -47438,7 +47717,7 @@ async function runExportGrades(opts, deps = defaultDeps5) {
47438
47717
  if (!rendered.endsWith("\n")) rendered += "\n";
47439
47718
  deps.stdoutWrite(rendered);
47440
47719
  }
47441
- var logger31, SUPPORTED_FORMATS, defaultDeps5, exportGradesCommand;
47720
+ var logger32, SUPPORTED_FORMATS, defaultDeps5, exportGradesCommand;
47442
47721
  var init_export_grades = __esm({
47443
47722
  "src/commands/export-grades.ts"() {
47444
47723
  "use strict";
@@ -47446,7 +47725,7 @@ var init_export_grades = __esm({
47446
47725
  init_guards();
47447
47726
  init_grades();
47448
47727
  init_resolve_enrollment();
47449
- logger31 = createLogger("cli:export-grades");
47728
+ logger32 = createLogger("cli:export-grades");
47450
47729
  SUPPORTED_FORMATS = ["json", "csv", "md"];
47451
47730
  defaultDeps5 = {
47452
47731
  requireSession,
@@ -47456,7 +47735,7 @@ var init_export_grades = __esm({
47456
47735
  stderrWrite: (message) => process.stderr.write(message),
47457
47736
  // process.exit is typed as `never` — wrap so the cast is local to one line.
47458
47737
  exit: (code) => process.exit(code),
47459
- logger: logger31,
47738
+ logger: logger32,
47460
47739
  resolveBySlug: resolveEnrollmentBySlug
47461
47740
  };
47462
47741
  exportGradesCommand = new Command32("export-grades").description("Exporta o hist\xF3rico de valida\xE7\xF5es (notas) do curso ativo").option(
@@ -47625,7 +47904,7 @@ async function runGrades(opts, deps = defaultDeps6) {
47625
47904
  );
47626
47905
  deps.stdoutWrite(formatGradesAllEnrollmentsTable(summaries) + "\n");
47627
47906
  }
47628
- var logger32, defaultDeps6, gradesCommand;
47907
+ var logger33, defaultDeps6, gradesCommand;
47629
47908
  var init_grades2 = __esm({
47630
47909
  "src/commands/grades.ts"() {
47631
47910
  "use strict";
@@ -47635,14 +47914,14 @@ var init_grades2 = __esm({
47635
47914
  init_guards();
47636
47915
  init_grades();
47637
47916
  init_resolve_enrollment();
47638
- logger32 = createLogger("cli:grades");
47917
+ logger33 = createLogger("cli:grades");
47639
47918
  defaultDeps6 = {
47640
47919
  requireSession,
47641
47920
  fetch: globalThis.fetch.bind(globalThis),
47642
47921
  stdoutWrite: (message) => process.stdout.write(message),
47643
47922
  stderrWrite: (message) => process.stderr.write(message),
47644
47923
  exit: (code) => process.exit(code),
47645
- logger: logger32,
47924
+ logger: logger33,
47646
47925
  resolveBySlug: resolveEnrollmentBySlug,
47647
47926
  buildDataProvider: createHttpProvider,
47648
47927
  listCoursesFn: listCourses
@@ -47807,7 +48086,7 @@ async function runReview(slug, opts, deps = defaultDeps7) {
47807
48086
  }) + "\n"
47808
48087
  );
47809
48088
  }
47810
- var logger33, defaultDeps7, reviewCommand;
48089
+ var logger34, defaultDeps7, reviewCommand;
47811
48090
  var init_review2 = __esm({
47812
48091
  "src/commands/review.ts"() {
47813
48092
  "use strict";
@@ -47815,14 +48094,14 @@ var init_review2 = __esm({
47815
48094
  init_guards();
47816
48095
  init_grades();
47817
48096
  init_resolve_enrollment();
47818
- logger33 = createLogger("cli:review");
48097
+ logger34 = createLogger("cli:review");
47819
48098
  defaultDeps7 = {
47820
48099
  requireSession,
47821
48100
  fetch: globalThis.fetch.bind(globalThis),
47822
48101
  stdoutWrite: (message) => process.stdout.write(message),
47823
48102
  stderrWrite: (message) => process.stderr.write(message),
47824
48103
  exit: (code) => process.exit(code),
47825
- logger: logger33,
48104
+ logger: logger34,
47826
48105
  resolveBySlug: resolveEnrollmentBySlug,
47827
48106
  promptLessonChoice: defaultPromptLessonChoice
47828
48107
  };
@@ -47937,13 +48216,77 @@ var init_cli = __esm({
47937
48216
  }
47938
48217
  });
47939
48218
 
48219
+ // src/auto-updater.ts
48220
+ var auto_updater_exports = {};
48221
+ __export(auto_updater_exports, {
48222
+ detectManager: () => detectManager,
48223
+ maybeAutoUpdate: () => maybeAutoUpdate
48224
+ });
48225
+ import fs21 from "node:fs";
48226
+ import path23 from "node:path";
48227
+ import { spawn as spawn2 } from "node:child_process";
48228
+ function detectManager(entryRealPath) {
48229
+ const p = entryRealPath.split(path23.sep).join("/");
48230
+ if (p.includes("/pnpm/")) return { cmd: "pnpm", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
48231
+ if (p.includes("/.bun/")) return { cmd: "bun", args: ["add", "-g", `${PACKAGE_NAME2}@latest`] };
48232
+ if (p.includes(`/node_modules/${PACKAGE_NAME2}/`))
48233
+ return { cmd: "npm", args: ["i", "-g", `${PACKAGE_NAME2}@latest`] };
48234
+ return null;
48235
+ }
48236
+ function installDir(entryRealPath) {
48237
+ const normalized = entryRealPath.split(path23.sep).join("/");
48238
+ const marker = `/node_modules/${PACKAGE_NAME2}/`;
48239
+ const idx = normalized.indexOf(marker);
48240
+ if (idx === -1) return path23.dirname(entryRealPath);
48241
+ return entryRealPath.slice(0, idx + marker.length - 1);
48242
+ }
48243
+ function maybeAutoUpdate(currentVersion, opts = {}) {
48244
+ try {
48245
+ const env = opts.env ?? process.env;
48246
+ const argv = opts.argv ?? process.argv;
48247
+ const isTTY = opts.isTTY ?? process.stderr.isTTY === true;
48248
+ if (env["TOSTUDY_NO_AUTO_UPDATE"]) return;
48249
+ if (env["CI"] || !isTTY || argv.includes("--json")) return;
48250
+ const configDir = opts.configDir ?? getConfigDir();
48251
+ const cache = readCache(configDir);
48252
+ if (!cache || !isNewerVersion(cache.latest, currentVersion)) return;
48253
+ if (cache.attemptedVersion === cache.latest) return;
48254
+ const entryPath = opts.entryPath ?? argv[1];
48255
+ if (!entryPath) return;
48256
+ const real2 = fs21.realpathSync(entryPath);
48257
+ const mgr = detectManager(real2);
48258
+ if (!mgr) return;
48259
+ fs21.accessSync(installDir(real2), fs21.constants.W_OK);
48260
+ writeCache({ ...cache, attemptedVersion: cache.latest }, configDir);
48261
+ process.stderr.write(
48262
+ `
48263
+ Atualizando ${PACKAGE_NAME2} para ${cache.latest} em segundo plano\u2026
48264
+ `
48265
+ );
48266
+ const spawnFn = opts.spawnFn ?? spawn2;
48267
+ spawnFn(mgr.cmd, mgr.args, { detached: true, stdio: "ignore" }).unref();
48268
+ } catch {
48269
+ }
48270
+ }
48271
+ var PACKAGE_NAME2;
48272
+ var init_auto_updater = __esm({
48273
+ "src/auto-updater.ts"() {
48274
+ "use strict";
48275
+ init_config_dir();
48276
+ init_update_checker();
48277
+ PACKAGE_NAME2 = "@tostudy-ai/cli";
48278
+ }
48279
+ });
48280
+
47940
48281
  // src/cli-entry.ts
47941
48282
  if (!process.env.LOG_LEVEL) {
47942
48283
  process.env.LOG_LEVEL = "fatal";
47943
48284
  }
47944
48285
  var { createProgram: createProgram2, CLI_VERSION: CLI_VERSION2 } = await Promise.resolve().then(() => (init_cli(), cli_exports));
47945
48286
  var { checkForUpdates: checkForUpdates2 } = await Promise.resolve().then(() => (init_update_checker(), update_checker_exports));
48287
+ var { maybeAutoUpdate: maybeAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_updater(), auto_updater_exports));
47946
48288
  var program = createProgram2();
47947
48289
  program.parse();
47948
48290
  checkForUpdates2(CLI_VERSION2);
48291
+ maybeAutoUpdate2(CLI_VERSION2);
47949
48292
  //# sourceMappingURL=cli.js.map