@tostudy-ai/cli 0.7.0 → 0.7.1

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
@@ -1071,7 +1071,7 @@ function showUpdateNotification(current, latest) {
1071
1071
  [
1072
1072
  "",
1073
1073
  ` Atualiza\xE7\xE3o dispon\xEDvel: ${current} \u2192 ${latest}`,
1074
- " Atualize com: npm i -g @tostudy-ai/cli",
1074
+ " Atualize com: npm i -g @tostudy-ai/cli@latest",
1075
1075
  ""
1076
1076
  ].join("\n")
1077
1077
  );
@@ -1206,7 +1206,7 @@ var init_doctor = __esm({
1206
1206
  ` ${upToDate ? "\u2713" : "\u2717"} \xDAltima ${versionInfo.latest}${versionInfo.updateAvailable ? " (atualiza\xE7\xE3o dispon\xEDvel)" : ""}`
1207
1207
  );
1208
1208
  if (versionInfo.updateAvailable) {
1209
- console.log(" \u2192 npm i -g @tostudy-ai/cli");
1209
+ console.log(" \u2192 npm i -g @tostudy-ai/cli@latest");
1210
1210
  }
1211
1211
  } else {
1212
1212
  console.log(" \u25CB \xDAltima n\xE3o foi poss\xEDvel verificar");
@@ -2334,9 +2334,109 @@ var init_courses2 = __esm({
2334
2334
  }
2335
2335
  });
2336
2336
 
2337
+ // src/workspace/instruction-files.ts
2338
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
2339
+ import { join as join2 } from "node:path";
2340
+ function slugify(title) {
2341
+ return title.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
2342
+ }
2343
+ function buildInstructionContent(ctx, learner) {
2344
+ const sections = [];
2345
+ sections.push(`# ${ctx.courseTitle} \u2014 ToStudy
2346
+
2347
+ Voc\xEA \xE9 um tutor AI guiando o estudante neste curso.`);
2348
+ if (learner) {
2349
+ const levelLabels = {
2350
+ beginner: "Iniciante",
2351
+ intermediate: "Intermedi\xE1rio",
2352
+ advanced: "Avan\xE7ado"
2353
+ };
2354
+ sections.push(`## Contexto do Aluno
2355
+
2356
+ - **N\xEDvel**: ${levelLabels[learner.learnerLevel]}
2357
+ - **Objetivo**: ${learner.goal}
2358
+ - **Empresa**: ${learner.company}
2359
+ - **Segmento**: ${learner.segment}
2360
+ - **Produtos/Servi\xE7os**: ${learner.productsOrServices}
2361
+ - **Regi\xE3o**: ${learner.region}
2362
+ - **Time**: ${learner.team}
2363
+ - **Adaptar ao contexto real**: ${learner.adaptToRealContext ? "Sim \u2014 use exemplos do projeto real do aluno" : "N\xE3o \u2014 use exemplos gen\xE9ricos"}`);
2364
+ }
2365
+ sections.push(`## Progresso
2366
+
2367
+ - ${ctx.progress}% completo | ${ctx.moduleCount} m\xF3dulos | ${ctx.lessonCount} li\xE7\xF5es`);
2368
+ sections.push(`## Comandos Dispon\xEDveis
2369
+
2370
+ | Comando | Descri\xE7\xE3o |
2371
+ |---------|-----------|
2372
+ | \`tostudy start\` | Carrega o pr\xF3ximo m\xF3dulo |
2373
+ | \`tostudy next\` | Avan\xE7a para a pr\xF3xima li\xE7\xE3o |
2374
+ | \`tostudy lesson\` | Mostra a li\xE7\xE3o atual |
2375
+ | \`tostudy hint\` | Mostra dicas progressivas |
2376
+ | \`tostudy validate\` | Valida o exerc\xEDcio |
2377
+ | \`tostudy progress\` | Mostra o progresso |`);
2378
+ sections.push(`## Regras
2379
+
2380
+ - Sempre carregue a li\xE7\xE3o antes de propor exerc\xEDcios (\`tostudy lesson\`)
2381
+ - Use \`tostudy hint\` antes de entregar respostas diretas
2382
+ - Valide com \`tostudy validate\` antes de avan\xE7ar
2383
+ - Respeite a sequ\xEAncia dos m\xF3dulos${learner?.adaptToRealContext ? "\n- Adapte exemplos ao contexto real do projeto do estudante" : ""}`);
2384
+ sections.push(`<!-- tostudy-course-id: ${ctx.courseId} -->`);
2385
+ return sections.join("\n\n") + "\n";
2386
+ }
2387
+ function generateInstructionFiles(ctx, learner) {
2388
+ const cwd = process.cwd();
2389
+ const slug = slugify(ctx.courseTitle);
2390
+ const content = buildInstructionContent(ctx, learner);
2391
+ const written = [];
2392
+ const claudeDir = join2(cwd, ".claude", "commands");
2393
+ if (!existsSync2(claudeDir)) {
2394
+ mkdirSync2(claudeDir, { recursive: true });
2395
+ }
2396
+ const claudeFile = `tostudy-${slug}.md`;
2397
+ writeFileSync2(join2(claudeDir, claudeFile), content);
2398
+ written.push(`.claude/commands/${claudeFile}`);
2399
+ const cursorDir = join2(cwd, ".cursor", "rules");
2400
+ if (existsSync2(join2(cwd, ".cursor")) || existsSync2(cursorDir)) {
2401
+ if (!existsSync2(cursorDir)) {
2402
+ mkdirSync2(cursorDir, { recursive: true });
2403
+ }
2404
+ const mdcContent = `---
2405
+ description: ${ctx.courseTitle} \u2014 ToStudy Course Guide
2406
+ globs: ["**/*"]
2407
+ alwaysApply: true
2408
+ ---
2409
+
2410
+ ${content}`;
2411
+ const cursorFile = `tostudy-${slug}.mdc`;
2412
+ writeFileSync2(join2(cursorDir, cursorFile), mdcContent);
2413
+ written.push(`.cursor/rules/${cursorFile}`);
2414
+ }
2415
+ const courseDir = join2(cwd, ".tostudy", "courses", slug);
2416
+ if (!existsSync2(courseDir)) {
2417
+ mkdirSync2(courseDir, { recursive: true });
2418
+ }
2419
+ writeFileSync2(join2(courseDir, "AGENTS.md"), content);
2420
+ written.push(`.tostudy/courses/${slug}/AGENTS.md`);
2421
+ logger3.info("Generated instruction files", {
2422
+ slug,
2423
+ files: written,
2424
+ hasLearnerProfile: !!learner
2425
+ });
2426
+ return slug;
2427
+ }
2428
+ var logger3;
2429
+ var init_instruction_files = __esm({
2430
+ "src/workspace/instruction-files.ts"() {
2431
+ "use strict";
2432
+ init_src();
2433
+ logger3 = createLogger("cli:instruction-files");
2434
+ }
2435
+ });
2436
+
2337
2437
  // src/commands/select.ts
2338
2438
  import { Command as Command6 } from "commander";
2339
- var logger3, selectCommand;
2439
+ var logger4, selectCommand;
2340
2440
  var init_select = __esm({
2341
2441
  "src/commands/select.ts"() {
2342
2442
  "use strict";
@@ -2345,12 +2445,13 @@ var init_select = __esm({
2345
2445
  init_http2();
2346
2446
  init_session();
2347
2447
  init_formatter();
2348
- logger3 = createLogger("cli:select");
2448
+ init_instruction_files();
2449
+ logger4 = createLogger("cli:select");
2349
2450
  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) => {
2350
2451
  try {
2351
2452
  const session = await requireSession();
2352
2453
  const data = createHttpProvider(session.apiUrl, session.token);
2353
- const deps = { data, logger: logger3 };
2454
+ const deps = { data, logger: logger4 };
2354
2455
  const courses3 = await listCourses({ userId: session.userId }, deps);
2355
2456
  let courseId = course;
2356
2457
  let enrollmentId = "";
@@ -2377,17 +2478,34 @@ var init_select = __esm({
2377
2478
  courseTags: matched?.tags,
2378
2479
  courseLevel: matched?.level
2379
2480
  });
2481
+ let courseSlug2 = "";
2482
+ try {
2483
+ courseSlug2 = generateInstructionFiles({
2484
+ courseTitle: detail.courseTitle,
2485
+ courseId: detail.courseId,
2486
+ progress: detail.progress,
2487
+ moduleCount: detail.moduleCount,
2488
+ lessonCount: detail.lessonCount
2489
+ });
2490
+ } catch (err) {
2491
+ logger4.warn("Failed to generate instruction files", {
2492
+ error: err instanceof Error ? err.message : String(err)
2493
+ });
2494
+ }
2380
2495
  if (opts.json) {
2381
- output({ ...detail, enrollmentId }, { json: true });
2496
+ output({ ...detail, enrollmentId, courseSlug: courseSlug2 }, { json: true });
2382
2497
  } else {
2498
+ const slashCmd = courseSlug2 ? `/tostudy-${courseSlug2}` : "/tostudy";
2383
2499
  output(
2384
2500
  [
2385
2501
  `\u2713 Curso ativado: ${detail.courseTitle}`,
2386
2502
  ` Progresso: ${detail.progress}% | ${detail.moduleCount} m\xF3dulos | ${detail.lessonCount} li\xE7\xF5es`,
2387
2503
  "",
2388
- "\u2192 tostudy progress para ver seu progresso detalhado",
2389
- "\u2192 tostudy start para come\xE7ar o m\xF3dulo atual",
2390
- "\u2192 tostudy next para avan\xE7ar para a pr\xF3xima li\xE7\xE3o"
2504
+ " Arquivos de contexto criados para seu assistente AI.",
2505
+ "",
2506
+ "\u2192 Abra sua plataforma (claude, codex, cursor...)",
2507
+ `\u2192 No Claude Code, digite: ${slashCmd}`,
2508
+ "\u2192 Em outras plataformas: tostudy init"
2391
2509
  ].join("\n"),
2392
2510
  { json: false }
2393
2511
  );
@@ -2402,7 +2520,7 @@ var init_select = __esm({
2402
2520
 
2403
2521
  // src/commands/progress.ts
2404
2522
  import { Command as Command7 } from "commander";
2405
- var logger4, progressCommand;
2523
+ var logger5, progressCommand;
2406
2524
  var init_progress = __esm({
2407
2525
  "src/commands/progress.ts"() {
2408
2526
  "use strict";
@@ -2411,7 +2529,7 @@ var init_progress = __esm({
2411
2529
  init_http2();
2412
2530
  init_session();
2413
2531
  init_formatter();
2414
- logger4 = createLogger("cli:progress");
2532
+ logger5 = createLogger("cli:progress");
2415
2533
  progressCommand = new Command7("progress").description("Show your progress in the active course").option("--json", "Output structured JSON").action(async (opts) => {
2416
2534
  try {
2417
2535
  const session = await requireSession();
@@ -2419,7 +2537,7 @@ var init_progress = __esm({
2419
2537
  const driftWarning = await checkCourseDrift();
2420
2538
  if (driftWarning) process.stderr.write(driftWarning + "\n");
2421
2539
  const data = createHttpProvider(session.apiUrl, session.token);
2422
- const deps = { data, logger: logger4 };
2540
+ const deps = { data, logger: logger5 };
2423
2541
  const progress3 = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
2424
2542
  if (opts.json) {
2425
2543
  output(progress3, { json: true });
@@ -3678,7 +3796,7 @@ var init_sql = __esm({
3678
3796
  return new SQL([new StringChunk(str)]);
3679
3797
  }
3680
3798
  sql2.raw = raw;
3681
- function join2(chunks, separator) {
3799
+ function join3(chunks, separator) {
3682
3800
  const result = [];
3683
3801
  for (const [i, chunk] of chunks.entries()) {
3684
3802
  if (i > 0 && separator !== void 0) {
@@ -3688,7 +3806,7 @@ var init_sql = __esm({
3688
3806
  }
3689
3807
  return new SQL(result);
3690
3808
  }
3691
- sql2.join = join2;
3809
+ sql2.join = join3;
3692
3810
  function identifier(value) {
3693
3811
  return new Name(value);
3694
3812
  }
@@ -10220,7 +10338,7 @@ var init_select3 = __esm({
10220
10338
  const baseTableName = this.tableName;
10221
10339
  const tableName = getTableLikeName(table);
10222
10340
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
10223
- if (typeof tableName === "string" && this.config.joins?.some((join2) => join2.alias === tableName)) {
10341
+ if (typeof tableName === "string" && this.config.joins?.some((join3) => join3.alias === tableName)) {
10224
10342
  throw new Error(`Alias "${tableName}" is already used in this query`);
10225
10343
  }
10226
10344
  if (!this.isPartialSelect) {
@@ -11747,7 +11865,7 @@ var init_update = __esm({
11747
11865
  createJoin(joinType) {
11748
11866
  return (table, on) => {
11749
11867
  const tableName = getTableLikeName(table);
11750
- if (typeof tableName === "string" && this.config.joins.some((join2) => join2.alias === tableName)) {
11868
+ if (typeof tableName === "string" && this.config.joins.some((join3) => join3.alias === tableName)) {
11751
11869
  throw new Error(`Alias "${tableName}" is already used in this query`);
11752
11870
  }
11753
11871
  if (typeof on === "function") {
@@ -11843,10 +11961,10 @@ var init_update = __esm({
11843
11961
  const fromFields = this.getTableLikeFields(this.config.from);
11844
11962
  fields[tableName] = fromFields;
11845
11963
  }
11846
- for (const join2 of this.config.joins) {
11847
- const tableName2 = getTableLikeName(join2.table);
11848
- if (typeof tableName2 === "string" && !is(join2.table, SQL)) {
11849
- const fromFields = this.getTableLikeFields(join2.table);
11964
+ for (const join3 of this.config.joins) {
11965
+ const tableName2 = getTableLikeName(join3.table);
11966
+ if (typeof tableName2 === "string" && !is(join3.table, SQL)) {
11967
+ const fromFields = this.getTableLikeFields(join3.table);
11850
11968
  fields[tableName2] = fromFields;
11851
11969
  }
11852
11970
  }
@@ -12789,12 +12907,12 @@ var init_session3 = __esm({
12789
12907
  init_tracing();
12790
12908
  init_utils();
12791
12909
  PostgresJsPreparedQuery = class extends PgPreparedQuery {
12792
- constructor(client, queryString, params, logger16, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
12910
+ constructor(client, queryString, params, logger17, cache, queryMetadata, cacheConfig, fields, _isResponseInArrayMode, customResultMapper) {
12793
12911
  super({ sql: queryString, params }, cache, queryMetadata, cacheConfig);
12794
12912
  this.client = client;
12795
12913
  this.queryString = queryString;
12796
12914
  this.params = params;
12797
- this.logger = logger16;
12915
+ this.logger = logger17;
12798
12916
  this.fields = fields;
12799
12917
  this._isResponseInArrayMode = _isResponseInArrayMode;
12800
12918
  this.customResultMapper = customResultMapper;
@@ -12935,11 +13053,11 @@ function construct(client, config2 = {}) {
12935
13053
  client.options.serializers["114"] = transparentParser;
12936
13054
  client.options.serializers["3802"] = transparentParser;
12937
13055
  const dialect = new PgDialect({ casing: config2.casing });
12938
- let logger16;
13056
+ let logger17;
12939
13057
  if (config2.logger === true) {
12940
- logger16 = new DefaultLogger();
13058
+ logger17 = new DefaultLogger();
12941
13059
  } else if (config2.logger !== false) {
12942
- logger16 = config2.logger;
13060
+ logger17 = config2.logger;
12943
13061
  }
12944
13062
  let schema;
12945
13063
  if (config2.schema) {
@@ -12953,7 +13071,7 @@ function construct(client, config2 = {}) {
12953
13071
  tableNamesMap: tablesConfig.tableNamesMap
12954
13072
  };
12955
13073
  }
12956
- const session = new PostgresJsSession(client, dialect, schema, { logger: logger16, cache: config2.cache });
13074
+ const session = new PostgresJsSession(client, dialect, schema, { logger: logger17, cache: config2.cache });
12957
13075
  const db2 = new PostgresJsDatabase(dialect, session, schema);
12958
13076
  db2.$client = client;
12959
13077
  db2.$cache = config2.cache;
@@ -13155,7 +13273,7 @@ __export(util_exports, {
13155
13273
  required: () => required,
13156
13274
  safeExtend: () => safeExtend,
13157
13275
  shallowClone: () => shallowClone,
13158
- slugify: () => slugify,
13276
+ slugify: () => slugify3,
13159
13277
  stringifyPrimitive: () => stringifyPrimitive,
13160
13278
  uint8ArrayToBase64: () => uint8ArrayToBase64,
13161
13279
  uint8ArrayToBase64url: () => uint8ArrayToBase64url,
@@ -13295,7 +13413,7 @@ function randomString(length = 10) {
13295
13413
  function esc(str) {
13296
13414
  return JSON.stringify(str);
13297
13415
  }
13298
- function slugify(input2) {
13416
+ function slugify3(input2) {
13299
13417
  return input2.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
13300
13418
  }
13301
13419
  function isObject(data) {
@@ -23429,7 +23547,7 @@ function _toUpperCase() {
23429
23547
  }
23430
23548
  // @__NO_SIDE_EFFECTS__
23431
23549
  function _slugify() {
23432
- return /* @__PURE__ */ _overwrite((input2) => slugify(input2));
23550
+ return /* @__PURE__ */ _overwrite((input2) => slugify3(input2));
23433
23551
  }
23434
23552
  // @__NO_SIDE_EFFECTS__
23435
23553
  function _array(Class2, element, params) {
@@ -27494,6 +27612,10 @@ var init_users = __esm({
27494
27612
  // Public portfolio active
27495
27613
  followerDiscountPct: smallint("follower_discount_pct").default(0).notNull(),
27496
27614
  // Follower discount: 0, 5, 10, 15, 20
27615
+ // Stripe Integration
27616
+ stripeCustomerId: varchar("stripe_customer_id", { length: 255 }),
27617
+ // Stripe Customer ID (cus_xxx) for payment method reuse
27618
+ metadata: jsonb("metadata").$type().default({}),
27497
27619
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
27498
27620
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
27499
27621
  },
@@ -33953,77 +34075,6 @@ var init_marketing = __esm({
33953
34075
  }
33954
34076
  });
33955
34077
 
33956
- // ../../packages/database/src/schema/user-credits.ts
33957
- var transactionTypeEnum, userCredits2, creditTransactions2;
33958
- var init_user_credits = __esm({
33959
- "../../packages/database/src/schema/user-credits.ts"() {
33960
- "use strict";
33961
- init_pg_core();
33962
- init_users();
33963
- transactionTypeEnum = pgEnum("transaction_type", [
33964
- "initial_grant",
33965
- // Credito inicial de $5
33966
- "purchase",
33967
- // Compra de creditos
33968
- "consumption",
33969
- // Consumo por uso de IA
33970
- "refund",
33971
- // Reembolso
33972
- "adjustment"
33973
- // Ajuste manual admin
33974
- ]);
33975
- userCredits2 = pgTable(
33976
- "user_credits",
33977
- {
33978
- id: uuid("id").primaryKey().defaultRandom(),
33979
- userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }).notNull().unique(),
33980
- /** Current balance in USD */
33981
- balance: decimal("balance", { precision: 10, scale: 4 }).notNull().default("0"),
33982
- /** Total consumed in USD (absolute value, always positive) */
33983
- totalConsumed: decimal("total_consumed", { precision: 10, scale: 4 }).notNull().default("0"),
33984
- /** Total purchased in USD */
33985
- totalPurchased: decimal("total_purchased", { precision: 10, scale: 4 }).notNull().default("0"),
33986
- createdAt: timestamp("created_at").defaultNow().notNull(),
33987
- updatedAt: timestamp("updated_at").defaultNow().notNull()
33988
- },
33989
- (table) => ({
33990
- userIdIdx: index("user_credits_user_id_idx").on(table.userId)
33991
- })
33992
- );
33993
- creditTransactions2 = pgTable(
33994
- "credit_transactions",
33995
- {
33996
- id: uuid("id").primaryKey().defaultRandom(),
33997
- userId: uuid("user_id").references(() => users.id, { onDelete: "restrict" }).notNull(),
33998
- type: transactionTypeEnum("type").notNull(),
33999
- /** Amount in USD (negative for consumption, positive for purchase/grant) */
34000
- amount: decimal("amount", { precision: 10, scale: 4 }).notNull(),
34001
- /** Balance after this transaction in USD */
34002
- balanceAfter: decimal("balance_after", { precision: 10, scale: 4 }).notNull(),
34003
- description: text("description"),
34004
- metadata: text("metadata"),
34005
- // JSON com detalhes (tokens, modelo, etc)
34006
- // Granular usage tracking columns
34007
- operation: varchar("operation", { length: 50 }),
34008
- // 'brainstorm_message', 'generation_outline', etc.
34009
- model: varchar("model", { length: 100 }),
34010
- // 'claude-sonnet-4-5-20250929'
34011
- inputTokens: integer("input_tokens"),
34012
- outputTokens: integer("output_tokens"),
34013
- latencyMs: integer("latency_ms"),
34014
- // DB FK to course_proposals preserved in migration; Drizzle ref removed for schema archival
34015
- proposalId: uuid("proposal_id"),
34016
- sessionId: uuid("session_id"),
34017
- createdAt: timestamp("created_at").defaultNow().notNull()
34018
- },
34019
- (table) => ({
34020
- userIdIdx: index("credit_transactions_user_id_idx").on(table.userId),
34021
- createdAtIdx: index("credit_transactions_created_at_idx").on(table.createdAt)
34022
- })
34023
- );
34024
- }
34025
- });
34026
-
34027
34078
  // ../../packages/database/src/schema/usage-quotas.ts
34028
34079
  var quotaChangeHistory, userUsageQuotas2, usageTierLimits2;
34029
34080
  var init_usage_quotas = __esm({
@@ -34469,74 +34520,72 @@ var init_course_variants = __esm({
34469
34520
  }
34470
34521
  });
34471
34522
 
34472
- // ../../packages/database/src/schema/_archived/matrix-translations.ts
34473
- var translationStatusEnum, translationEntityTypeEnum, matrixTranslations;
34474
- var init_matrix_translations = __esm({
34475
- "../../packages/database/src/schema/_archived/matrix-translations.ts"() {
34523
+ // ../../packages/database/src/schema/_archived/user-credits.ts
34524
+ var transactionTypeEnum, userCredits2, creditTransactions2;
34525
+ var init_user_credits = __esm({
34526
+ "../../packages/database/src/schema/_archived/user-credits.ts"() {
34476
34527
  "use strict";
34477
34528
  init_pg_core();
34478
34529
  init_users();
34479
- init_course_matrices();
34480
- translationStatusEnum = pgEnum("translation_status", [
34481
- "pending",
34482
- // Translation requested
34483
- "generating",
34484
- // AI translation in progress
34485
- "draft",
34486
- // AI generated, awaiting review
34487
- "approved",
34488
- // Creator approved
34489
- "rejected"
34490
- // Creator rejected, needs regeneration
34491
- ]);
34492
- translationEntityTypeEnum = pgEnum("translation_entity_type", [
34493
- "matrix",
34494
- // CourseMatrix (title, description, coreDocument)
34495
- "module",
34496
- // MatrixModule (title, description, lessonsContent)
34497
- "variant"
34498
- // CourseVariant (name, description)
34530
+ transactionTypeEnum = pgEnum("transaction_type", [
34531
+ "initial_grant",
34532
+ // Credito inicial de $5
34533
+ "purchase",
34534
+ // Compra de creditos
34535
+ "consumption",
34536
+ // Consumo por uso de IA
34537
+ "refund",
34538
+ // Reembolso
34539
+ "adjustment"
34540
+ // Ajuste manual admin
34499
34541
  ]);
34500
- matrixTranslations = pgTable(
34501
- "matrix_translations",
34542
+ userCredits2 = pgTable(
34543
+ "user_credits",
34502
34544
  {
34503
34545
  id: uuid("id").primaryKey().defaultRandom(),
34504
- matrixId: uuid("matrix_id").notNull().references(() => courseMatrices.id, { onDelete: "cascade" }),
34505
- creatorId: uuid("creator_id").notNull().references(() => users.id),
34506
- // Target entity
34507
- entityType: translationEntityTypeEnum("entity_type").notNull(),
34508
- entityId: uuid("entity_id").notNull(),
34509
- // ID of matrix, module, or variant
34510
- // Language
34511
- sourceLanguage: text("source_language").notNull().default("en"),
34512
- targetLanguage: text("target_language").notNull(),
34513
- // Field being translated
34514
- field: text("field").notNull(),
34515
- // 'title', 'description', 'content', 'coreDocument', etc.
34516
- // Translation content
34517
- originalContent: text("original_content").notNull(),
34518
- translatedContent: text("translated_content"),
34519
- // Status
34520
- status: translationStatusEnum("status").notNull().default("pending"),
34521
- // Review
34522
- reviewedAt: timestamp("reviewed_at"),
34523
- reviewNotes: text("review_notes"),
34524
- // Timestamps
34546
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }).notNull().unique(),
34547
+ /** Current balance in USD */
34548
+ balance: decimal("balance", { precision: 10, scale: 4 }).notNull().default("0"),
34549
+ /** Total consumed in USD (absolute value, always positive) */
34550
+ totalConsumed: decimal("total_consumed", { precision: 10, scale: 4 }).notNull().default("0"),
34551
+ /** Total purchased in USD */
34552
+ totalPurchased: decimal("total_purchased", { precision: 10, scale: 4 }).notNull().default("0"),
34525
34553
  createdAt: timestamp("created_at").defaultNow().notNull(),
34526
34554
  updatedAt: timestamp("updated_at").defaultNow().notNull()
34527
34555
  },
34528
34556
  (table) => ({
34529
- matrixIdIdx: index("matrix_translations_matrix_id_idx").on(table.matrixId),
34530
- entityIdx: index("matrix_translations_entity_idx").on(table.entityType, table.entityId),
34531
- languageIdx: index("matrix_translations_language_idx").on(table.targetLanguage),
34532
- statusIdx: index("matrix_translations_status_idx").on(table.status),
34533
- // Unique constraint: one translation per entity/field/language combination
34534
- entityFieldLangUniq: uniqueIndex("matrix_translations_entity_field_lang_uniq").on(
34535
- table.entityType,
34536
- table.entityId,
34537
- table.field,
34538
- table.targetLanguage
34539
- )
34557
+ userIdIdx: index("user_credits_user_id_idx").on(table.userId)
34558
+ })
34559
+ );
34560
+ creditTransactions2 = pgTable(
34561
+ "credit_transactions",
34562
+ {
34563
+ id: uuid("id").primaryKey().defaultRandom(),
34564
+ userId: uuid("user_id").references(() => users.id, { onDelete: "restrict" }).notNull(),
34565
+ type: transactionTypeEnum("type").notNull(),
34566
+ /** Amount in USD (negative for consumption, positive for purchase/grant) */
34567
+ amount: decimal("amount", { precision: 10, scale: 4 }).notNull(),
34568
+ /** Balance after this transaction in USD */
34569
+ balanceAfter: decimal("balance_after", { precision: 10, scale: 4 }).notNull(),
34570
+ description: text("description"),
34571
+ metadata: text("metadata"),
34572
+ // JSON com detalhes (tokens, modelo, etc)
34573
+ // Granular usage tracking columns
34574
+ operation: varchar("operation", { length: 50 }),
34575
+ // 'brainstorm_message', 'generation_outline', etc.
34576
+ model: varchar("model", { length: 100 }),
34577
+ // 'claude-sonnet-4-5-20250929'
34578
+ inputTokens: integer("input_tokens"),
34579
+ outputTokens: integer("output_tokens"),
34580
+ latencyMs: integer("latency_ms"),
34581
+ // DB FK to course_proposals preserved in migration; Drizzle ref removed for schema archival
34582
+ proposalId: uuid("proposal_id"),
34583
+ sessionId: uuid("session_id"),
34584
+ createdAt: timestamp("created_at").defaultNow().notNull()
34585
+ },
34586
+ (table) => ({
34587
+ userIdIdx: index("credit_transactions_user_id_idx").on(table.userId),
34588
+ createdAtIdx: index("credit_transactions_created_at_idx").on(table.createdAt)
34540
34589
  })
34541
34590
  );
34542
34591
  }
@@ -34550,7 +34599,7 @@ var init_archived = __esm({
34550
34599
  init_course_matrices();
34551
34600
  init_matrix_modules();
34552
34601
  init_course_variants();
34553
- init_matrix_translations();
34602
+ init_user_credits();
34554
34603
  }
34555
34604
  });
34556
34605
 
@@ -35325,76 +35374,89 @@ var init_llm_analytics = __esm({
35325
35374
  init_users();
35326
35375
  init_courses3();
35327
35376
  init_llm_pricing();
35328
- llmInteractions = pgTable("llm_interactions", {
35329
- id: uuid("id").primaryKey().defaultRandom(),
35330
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
35331
- // Operation type
35332
- operationType: varchar("operation_type", { length: 50 }).notNull(),
35333
- // Provider (references llm_provider enum from llm-pricing.ts)
35334
- provider: llmProviderEnum("provider").notNull().default("anthropic"),
35335
- // Tracing
35336
- correlationId: varchar("correlation_id", { length: 64 }),
35337
- // Relationships
35338
- userId: uuid("user_id").references(() => users.id, { onDelete: "set null" }),
35339
- creatorId: uuid("creator_id").references(() => users.id, { onDelete: "set null" }),
35340
- courseId: uuid("course_id").references(() => courses.id, { onDelete: "set null" }),
35341
- sessionId: uuid("session_id"),
35342
- variantId: uuid("variant_id"),
35343
- // Denormalized from metadata for faster queries
35344
- // Request
35345
- model: varchar("model", { length: 50 }).notNull(),
35346
- promptText: text("prompt_text"),
35347
- promptTokens: integer("prompt_tokens").notNull(),
35348
- // Response
35349
- responseText: text("response_text"),
35350
- responseTokens: integer("response_tokens").notNull(),
35351
- // Cost & Revenue
35352
- costUsd: decimal("cost_usd", { precision: 10, scale: 6 }).notNull(),
35353
- revenueUsd: decimal("revenue_usd", { precision: 10, scale: 6 }).notNull().default("0"),
35354
- // Cache metrics (prompt caching)
35355
- cacheCreationTokens: integer("cache_creation_tokens").default(0),
35356
- cacheReadTokens: integer("cache_read_tokens").default(0),
35357
- cacheHit: integer("cache_hit").default(0),
35358
- // 0 = false, 1 = true (SQLite-compatible)
35359
- // Performance
35360
- latencyMs: integer("latency_ms").notNull(),
35361
- status: varchar("status", { length: 20 }).notNull(),
35362
- errorMessage: text("error_message"),
35363
- // Streaming metrics (null for non-streaming calls)
35364
- timeToFirstTokenMs: integer("time_to_first_token_ms"),
35365
- streamDurationMs: integer("stream_duration_ms"),
35366
- // Model version and metadata
35367
- modelVersion: varchar("model_version", { length: 100 }),
35368
- metadata: jsonb("metadata").$type()
35369
- }, (table) => ({
35370
- creatorIdx: index("idx_llm_interactions_creator").on(table.creatorId, table.createdAt),
35371
- operationIdx: index("idx_llm_interactions_operation").on(table.operationType, table.createdAt),
35372
- correlationIdx: index("idx_llm_interactions_correlation").on(table.correlationId),
35373
- providerIdx: index("idx_llm_interactions_provider").on(table.provider),
35374
- variantIdx: index("idx_llm_interactions_variant").on(table.variantId),
35375
- creatorVariantDateIdx: index("idx_llm_interactions_creator_variant_date").on(table.creatorId, table.variantId, table.createdAt)
35376
- }));
35377
- llmUsageDaily = pgTable("llm_usage_daily", {
35378
- date: timestamp("date", { mode: "date" }).notNull(),
35379
- operationType: varchar("operation_type", { length: 50 }).notNull(),
35380
- creatorId: uuid("creator_id").references(() => users.id, { onDelete: "restrict" }).notNull(),
35381
- // Aggregated metrics
35382
- totalRequests: integer("total_requests").notNull().default(0),
35383
- totalPromptTokens: integer("total_prompt_tokens").notNull().default(0),
35384
- totalResponseTokens: integer("total_response_tokens").notNull().default(0),
35385
- totalCostUsd: decimal("total_cost_usd", { precision: 12, scale: 4 }).notNull().default("0"),
35386
- totalRevenueUsd: decimal("total_revenue_usd", { precision: 12, scale: 4 }).notNull().default("0"),
35387
- avgLatencyMs: integer("avg_latency_ms"),
35388
- errorCount: integer("error_count").notNull().default(0),
35389
- // Cache aggregated metrics
35390
- totalCacheCreationTokens: integer("total_cache_creation_tokens").notNull().default(0),
35391
- totalCacheReadTokens: integer("total_cache_read_tokens").notNull().default(0),
35392
- totalCacheHits: integer("total_cache_hits").notNull().default(0)
35393
- }, (table) => ({
35394
- pk: primaryKey({ columns: [table.date, table.operationType, table.creatorId] }),
35395
- dateIdx: index("idx_llm_usage_daily_date").on(table.date),
35396
- creatorIdx: index("idx_llm_usage_daily_creator").on(table.creatorId, table.date)
35397
- }));
35377
+ llmInteractions = pgTable(
35378
+ "llm_interactions",
35379
+ {
35380
+ id: uuid("id").primaryKey().defaultRandom(),
35381
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
35382
+ // Operation type
35383
+ operationType: varchar("operation_type", { length: 50 }).notNull(),
35384
+ // Provider (references llm_provider enum from llm-pricing.ts)
35385
+ provider: llmProviderEnum("provider").notNull().default("anthropic"),
35386
+ // Tracing
35387
+ correlationId: varchar("correlation_id", { length: 64 }),
35388
+ // Relationships
35389
+ userId: uuid("user_id").references(() => users.id, { onDelete: "set null" }),
35390
+ creatorId: uuid("creator_id").references(() => users.id, { onDelete: "set null" }),
35391
+ courseId: uuid("course_id").references(() => courses.id, { onDelete: "set null" }),
35392
+ sessionId: uuid("session_id"),
35393
+ variantId: uuid("variant_id"),
35394
+ // Denormalized from metadata for faster queries
35395
+ // Request
35396
+ model: varchar("model", { length: 50 }).notNull(),
35397
+ promptText: text("prompt_text"),
35398
+ promptTokens: integer("prompt_tokens").notNull(),
35399
+ // Response
35400
+ responseText: text("response_text"),
35401
+ responseTokens: integer("response_tokens").notNull(),
35402
+ // Cost & Revenue
35403
+ costUsd: decimal("cost_usd", { precision: 10, scale: 6 }).notNull(),
35404
+ revenueUsd: decimal("revenue_usd", { precision: 10, scale: 6 }).notNull().default("0"),
35405
+ // Cache metrics (prompt caching)
35406
+ cacheCreationTokens: integer("cache_creation_tokens").default(0),
35407
+ cacheReadTokens: integer("cache_read_tokens").default(0),
35408
+ cacheHit: integer("cache_hit").default(0),
35409
+ // 0 = false, 1 = true (SQLite-compatible)
35410
+ // Performance
35411
+ latencyMs: integer("latency_ms").notNull(),
35412
+ status: varchar("status", { length: 20 }).notNull(),
35413
+ errorMessage: text("error_message"),
35414
+ // Streaming metrics (null for non-streaming calls)
35415
+ timeToFirstTokenMs: integer("time_to_first_token_ms"),
35416
+ streamDurationMs: integer("stream_duration_ms"),
35417
+ // Model version and metadata
35418
+ modelVersion: varchar("model_version", { length: 100 }),
35419
+ metadata: jsonb("metadata").$type()
35420
+ },
35421
+ (table) => ({
35422
+ creatorIdx: index("idx_llm_interactions_creator").on(table.creatorId, table.createdAt),
35423
+ operationIdx: index("idx_llm_interactions_operation").on(table.operationType, table.createdAt),
35424
+ correlationIdx: index("idx_llm_interactions_correlation").on(table.correlationId),
35425
+ providerIdx: index("idx_llm_interactions_provider").on(table.provider),
35426
+ variantIdx: index("idx_llm_interactions_variant").on(table.variantId),
35427
+ creatorVariantDateIdx: index("idx_llm_interactions_creator_variant_date").on(
35428
+ table.creatorId,
35429
+ table.variantId,
35430
+ table.createdAt
35431
+ )
35432
+ })
35433
+ );
35434
+ llmUsageDaily = pgTable(
35435
+ "llm_usage_daily",
35436
+ {
35437
+ date: timestamp("date", { mode: "date" }).notNull(),
35438
+ operationType: varchar("operation_type", { length: 50 }).notNull(),
35439
+ creatorId: uuid("creator_id").notNull(),
35440
+ // No FK — sentinel UUID '00000000-...' used for system-level aggregation
35441
+ // Aggregated metrics
35442
+ totalRequests: integer("total_requests").notNull().default(0),
35443
+ totalPromptTokens: integer("total_prompt_tokens").notNull().default(0),
35444
+ totalResponseTokens: integer("total_response_tokens").notNull().default(0),
35445
+ totalCostUsd: decimal("total_cost_usd", { precision: 12, scale: 4 }).notNull().default("0"),
35446
+ totalRevenueUsd: decimal("total_revenue_usd", { precision: 12, scale: 4 }).notNull().default("0"),
35447
+ avgLatencyMs: integer("avg_latency_ms"),
35448
+ errorCount: integer("error_count").notNull().default(0),
35449
+ // Cache aggregated metrics
35450
+ totalCacheCreationTokens: integer("total_cache_creation_tokens").notNull().default(0),
35451
+ totalCacheReadTokens: integer("total_cache_read_tokens").notNull().default(0),
35452
+ totalCacheHits: integer("total_cache_hits").notNull().default(0)
35453
+ },
35454
+ (table) => ({
35455
+ pk: primaryKey({ columns: [table.date, table.operationType, table.creatorId] }),
35456
+ dateIdx: index("idx_llm_usage_daily_date").on(table.date),
35457
+ creatorIdx: index("idx_llm_usage_daily_creator").on(table.creatorId, table.date)
35458
+ })
35459
+ );
35398
35460
  }
35399
35461
  });
35400
35462
 
@@ -36958,7 +37020,12 @@ var init_video_intros = __esm({
36958
37020
  "minimal-dark",
36959
37021
  "gradient-bold",
36960
37022
  "corporate-clean",
36961
- "warm-organic"
37023
+ "warm-organic",
37024
+ "neon-tech",
37025
+ "playful-bright",
37026
+ "culinary-warm",
37027
+ "academic-serif",
37028
+ "music-rhythm"
36962
37029
  ]);
36963
37030
  renderJobStatusEnum = pgEnum("render_job_status", [
36964
37031
  "queued",
@@ -36984,6 +37051,8 @@ var init_video_intros = __esm({
36984
37051
  durationSeconds: integer("duration_seconds"),
36985
37052
  metadata: jsonb("metadata"),
36986
37053
  sceneConfig: jsonb("scene_config").$type(),
37054
+ generationHistory: jsonb("generation_history").default([]).$type(),
37055
+ themeOverrides: jsonb("theme_overrides").$type(),
36987
37056
  createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
36988
37057
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull()
36989
37058
  },
@@ -38217,7 +38286,6 @@ __export(schema_exports, {
38217
38286
  creditSubscriptionStatusEnum: () => creditSubscriptionStatusEnum,
38218
38287
  creditSubscriptions: () => creditSubscriptions,
38219
38288
  creditTransactions: () => creditTransactions2,
38220
- creditTransactionsRelations: () => creditTransactionsRelations2,
38221
38289
  creditWalletTransactionTypeEnum: () => creditWalletTransactionTypeEnum,
38222
38290
  creditWalletTransactions: () => creditWalletTransactions,
38223
38291
  creditWallets: () => creditWallets,
@@ -38298,7 +38366,6 @@ __export(schema_exports, {
38298
38366
  marketingContentTypeEnum: () => marketingContentTypeEnum,
38299
38367
  matrixModules: () => matrixModules,
38300
38368
  matrixStatusEnum: () => matrixStatusEnum,
38301
- matrixTranslations: () => matrixTranslations,
38302
38369
  mcpSessions: () => mcpSessions2,
38303
38370
  mcpSessionsRelations: () => mcpSessionsRelations2,
38304
38371
  mediaAssetDomainEnum: () => mediaAssetDomainEnum,
@@ -38509,8 +38576,6 @@ __export(schema_exports, {
38509
38576
  transactionsRelations: () => transactionsRelations2,
38510
38577
  transferStatusEnum: () => transferStatusEnum,
38511
38578
  transferTypeEnum: () => transferTypeEnum,
38512
- translationEntityTypeEnum: () => translationEntityTypeEnum,
38513
- translationStatusEnum: () => translationStatusEnum,
38514
38579
  tutorContentIssues: () => tutorContentIssues,
38515
38580
  tutorContentIssuesRelations: () => tutorContentIssuesRelations,
38516
38581
  tutorEvaluations: () => tutorEvaluations,
@@ -38524,7 +38589,6 @@ __export(schema_exports, {
38524
38589
  userChallengeScores: () => userChallengeScores,
38525
38590
  userChallengeScoresRelations: () => userChallengeScoresRelations,
38526
38591
  userCredits: () => userCredits2,
38527
- userCreditsRelations: () => userCreditsRelations2,
38528
38592
  userFollows: () => userFollows2,
38529
38593
  userFollowsRelations: () => userFollowsRelations2,
38530
38594
  userPathEnrollments: () => userPathEnrollments,
@@ -38561,7 +38625,7 @@ __export(schema_exports, {
38561
38625
  webhookEvents: () => webhookEvents2,
38562
38626
  wishlists: () => wishlists
38563
38627
  });
38564
- var usersRelations2, onboardingAnalyticsRelations2, accountsRelations2, sessionsRelations2, browserSessionsRelations, courseCategoriesRelations, coursesRelations2, certificatesRelations2, enrollmentsRelations2, modulesRelations2, lessonsRelations2, progressRelations2, mentorshipThreadsRelations2, mentorshipMessagesRelations2, reviewsRelations2, payoutsRelations2, payoutTransactionsRelations2, creatorApplicationsRelations2, transactionsRelations2, couponsRelations2, couponUsagesRelations2, userPreferencesRelations2, mcpSessionsRelations2, syncLogsRelations2, slashCommandAnalyticsRelations2, conceptsRelations2, badgesRelations2, userBadgesRelations2, validationAttemptsRelations2, notificationLogsRelations2, mentorshipTemplatesRelations2, notificationsRelations2, funnelEventsRelations2, courseReviewsRelations2, courseReportsRelations2, mentorshipServicesRelations2, mentorAvailabilityRelations2, mentorAvailabilityExceptionsRelations2, mentorshipSessionsRelations2, sessionParticipantsRelations2, sessionResourcesRelations2, sessionWaitlistRelations2, mentorshipTransactionsRelations2, mentorshipPackagesRelations2, mentorshipCreditsRelations2, refundAuditLogRelations2, sessionReviewsRelations2, sessionReviewVotesRelations2, communityPostsRelations2, communityCommentsRelations2, communityLikesRelations2, userFollowsRelations2, sparkChatSessionsRelations2, studyChatSessionsRelations, studyChatMessagesRelations, studyChatArtifactsRelations, studyModuleSessionsIndexRelations, studyLessonContextsIndexRelations, studyChatMessagesV2Relations, generationStateRelations2, creatorTestimonialsRelations2, marketingContentRelations2, userCreditsRelations2, creditTransactionsRelations2, brainstormingsRelations, ideasRelations, coursePublicationsRelations, publicationReviewHistoryRelations, studentNotesRelations2, learningStreaksRelations, dailyGoalsRelations, dailyGoalProgressRelations, countriesRelations, userRegionalSettingsRelations, courseProjectsRelations, sourceExtractionsRelations, projectGenerationStatesRelations, projectContentPoolRelations, projectVariantModulesRelations, exchangeRatesRelations, lessonRefinementsRelations, courseReviewSnapshotsRelations, learningPathsRelations2, pathCoursesRelations2, pathMilestonesRelations2, userPathEnrollmentsRelations, courseGraphsRelations, graphNodesRelations, graphEdgesRelations, projectTopicsRelations, graphNodeProgressRelations, creatorPortfolioCoursesRelations, portfolioViewsRelations, variationAxesRelations, creatorVariationAxesRelations, courseQaReportsRelations, studentWorkspacesRelations, projectSnapshotsRelations, tutorEvaluationsRelations, tutorContentIssuesRelations, mobileRefreshTokensRelations;
38628
+ var usersRelations2, onboardingAnalyticsRelations2, accountsRelations2, sessionsRelations2, browserSessionsRelations, courseCategoriesRelations, coursesRelations2, certificatesRelations2, enrollmentsRelations2, modulesRelations2, lessonsRelations2, progressRelations2, mentorshipThreadsRelations2, mentorshipMessagesRelations2, reviewsRelations2, payoutsRelations2, payoutTransactionsRelations2, creatorApplicationsRelations2, transactionsRelations2, couponsRelations2, couponUsagesRelations2, userPreferencesRelations2, mcpSessionsRelations2, syncLogsRelations2, slashCommandAnalyticsRelations2, conceptsRelations2, badgesRelations2, userBadgesRelations2, validationAttemptsRelations2, notificationLogsRelations2, mentorshipTemplatesRelations2, notificationsRelations2, funnelEventsRelations2, courseReviewsRelations2, courseReportsRelations2, mentorshipServicesRelations2, mentorAvailabilityRelations2, mentorAvailabilityExceptionsRelations2, mentorshipSessionsRelations2, sessionParticipantsRelations2, sessionResourcesRelations2, sessionWaitlistRelations2, mentorshipTransactionsRelations2, mentorshipPackagesRelations2, mentorshipCreditsRelations2, refundAuditLogRelations2, sessionReviewsRelations2, sessionReviewVotesRelations2, communityPostsRelations2, communityCommentsRelations2, communityLikesRelations2, userFollowsRelations2, sparkChatSessionsRelations2, studyChatSessionsRelations, studyChatMessagesRelations, studyChatArtifactsRelations, studyModuleSessionsIndexRelations, studyLessonContextsIndexRelations, studyChatMessagesV2Relations, generationStateRelations2, creatorTestimonialsRelations2, marketingContentRelations2, brainstormingsRelations, ideasRelations, coursePublicationsRelations, publicationReviewHistoryRelations, studentNotesRelations2, learningStreaksRelations, dailyGoalsRelations, dailyGoalProgressRelations, countriesRelations, userRegionalSettingsRelations, courseProjectsRelations, sourceExtractionsRelations, projectGenerationStatesRelations, projectContentPoolRelations, projectVariantModulesRelations, exchangeRatesRelations, lessonRefinementsRelations, courseReviewSnapshotsRelations, learningPathsRelations2, pathCoursesRelations2, pathMilestonesRelations2, userPathEnrollmentsRelations, courseGraphsRelations, graphNodesRelations, graphEdgesRelations, projectTopicsRelations, graphNodeProgressRelations, creatorPortfolioCoursesRelations, portfolioViewsRelations, variationAxesRelations, creatorVariationAxesRelations, courseQaReportsRelations, studentWorkspacesRelations, projectSnapshotsRelations, tutorEvaluationsRelations, tutorContentIssuesRelations, mobileRefreshTokensRelations;
38565
38629
  var init_schema3 = __esm({
38566
38630
  "../../packages/database/src/schema/index.ts"() {
38567
38631
  "use strict";
@@ -38621,7 +38685,6 @@ var init_schema3 = __esm({
38621
38685
  init_generation_state();
38622
38686
  init_security_audit_log();
38623
38687
  init_marketing();
38624
- init_user_credits();
38625
38688
  init_usage_quotas();
38626
38689
  init_brainstormings();
38627
38690
  init_ideas();
@@ -38721,7 +38784,6 @@ var init_schema3 = __esm({
38721
38784
  init_study_module_sessions();
38722
38785
  init_generation_state();
38723
38786
  init_marketing();
38724
- init_user_credits();
38725
38787
  init_brainstormings();
38726
38788
  init_ideas();
38727
38789
  init_course_publications();
@@ -38835,9 +38897,6 @@ var init_schema3 = __esm({
38835
38897
  // Users this user follows
38836
38898
  // Story 21.1: Spark chat sessions
38837
38899
  sparkChatSessions: many(sparkChatSessions2),
38838
- // Creator flow: Token credits
38839
- credits: one(userCredits2),
38840
- creditTransactions: many(creditTransactions2),
38841
38900
  // Creator flow: New brainstormings
38842
38901
  brainstormings: many(brainstormings),
38843
38902
  // Creator flow: Ideas notepad
@@ -39586,18 +39645,6 @@ var init_schema3 = __esm({
39586
39645
  references: [users.id]
39587
39646
  })
39588
39647
  }));
39589
- userCreditsRelations2 = relations(userCredits2, ({ one }) => ({
39590
- user: one(users, {
39591
- fields: [userCredits2.userId],
39592
- references: [users.id]
39593
- })
39594
- }));
39595
- creditTransactionsRelations2 = relations(creditTransactions2, ({ one }) => ({
39596
- user: one(users, {
39597
- fields: [creditTransactions2.userId],
39598
- references: [users.id]
39599
- })
39600
- }));
39601
39648
  brainstormingsRelations = relations(brainstormings, ({ one }) => ({
39602
39649
  creator: one(users, {
39603
39650
  fields: [brainstormings.creatorId],
@@ -40024,7 +40071,6 @@ var init_src4 = __esm({
40024
40071
  init_platform_settings();
40025
40072
  init_marketing();
40026
40073
  init_security_audit_log();
40027
- init_user_credits();
40028
40074
  init_gamification();
40029
40075
  init_creator_portfolio_courses();
40030
40076
  init_portfolio_views();
@@ -40144,6 +40190,15 @@ var init_next_lesson_full = __esm({
40144
40190
  }
40145
40191
  });
40146
40192
 
40193
+ // ../../packages/tostudy-core/src/memory/student-memory.ts
40194
+ var init_student_memory = __esm({
40195
+ "../../packages/tostudy-core/src/memory/student-memory.ts"() {
40196
+ "use strict";
40197
+ init_src4();
40198
+ init_src();
40199
+ }
40200
+ });
40201
+
40147
40202
  // ../../packages/tostudy-core/src/learning/validate-solution-full.ts
40148
40203
  var init_validate_solution_full = __esm({
40149
40204
  "../../packages/tostudy-core/src/learning/validate-solution-full.ts"() {
@@ -40153,6 +40208,7 @@ var init_validate_solution_full = __esm({
40153
40208
  init_exercise_types();
40154
40209
  init_study_state_sync();
40155
40210
  init_sync_enrollment_progress();
40211
+ init_student_memory();
40156
40212
  }
40157
40213
  });
40158
40214
 
@@ -40274,7 +40330,7 @@ async function runStart(opts, deps = defaultDeps2) {
40274
40330
  deps.error(msg);
40275
40331
  }
40276
40332
  }
40277
- var logger5, defaultDeps2, StartBlockedError, startCommand;
40333
+ var logger6, defaultDeps2, StartBlockedError, startCommand;
40278
40334
  var init_start = __esm({
40279
40335
  "src/commands/start.ts"() {
40280
40336
  "use strict";
@@ -40284,7 +40340,7 @@ var init_start = __esm({
40284
40340
  init_session();
40285
40341
  init_status();
40286
40342
  init_formatter();
40287
- logger5 = createLogger("cli:start");
40343
+ logger6 = createLogger("cli:start");
40288
40344
  defaultDeps2 = {
40289
40345
  requireSession,
40290
40346
  requireActiveCourse,
@@ -40297,7 +40353,7 @@ var init_start = __esm({
40297
40353
  output,
40298
40354
  error,
40299
40355
  stderrWrite: (message) => process.stderr.write(message),
40300
- logger: logger5
40356
+ logger: logger6
40301
40357
  };
40302
40358
  StartBlockedError = class extends Error {
40303
40359
  };
@@ -40309,7 +40365,7 @@ var init_start = __esm({
40309
40365
 
40310
40366
  // src/commands/start-next.ts
40311
40367
  import { Command as Command9 } from "commander";
40312
- var logger6, startNextCommand;
40368
+ var logger7, startNextCommand;
40313
40369
  var init_start_next = __esm({
40314
40370
  "src/commands/start-next.ts"() {
40315
40371
  "use strict";
@@ -40318,7 +40374,7 @@ var init_start_next = __esm({
40318
40374
  init_http2();
40319
40375
  init_session();
40320
40376
  init_formatter();
40321
- logger6 = createLogger("cli:start-next");
40377
+ logger7 = createLogger("cli:start-next");
40322
40378
  startNextCommand = new Command9("start-next").description("Transition to the next module after completing the current one").option("--json", "Output structured JSON").action(async (opts) => {
40323
40379
  try {
40324
40380
  const session = await requireSession();
@@ -40326,7 +40382,7 @@ var init_start_next = __esm({
40326
40382
  const driftWarning = await checkCourseDrift();
40327
40383
  if (driftWarning) process.stderr.write(driftWarning + "\n");
40328
40384
  const data = createHttpProvider(session.apiUrl, session.token);
40329
- const deps = { data, logger: logger6 };
40385
+ const deps = { data, logger: logger7 };
40330
40386
  const moduleData = await startNextModule({ enrollmentId: activeCourse.enrollmentId }, deps);
40331
40387
  await setActiveCourse({ ...activeCourse, currentLessonId: moduleData.firstLesson.id });
40332
40388
  if (opts.json) {
@@ -40349,7 +40405,7 @@ var init_start_next = __esm({
40349
40405
 
40350
40406
  // src/commands/next.ts
40351
40407
  import { Command as Command10 } from "commander";
40352
- var logger7, nextCommand;
40408
+ var logger8, nextCommand;
40353
40409
  var init_next = __esm({
40354
40410
  "src/commands/next.ts"() {
40355
40411
  "use strict";
@@ -40358,7 +40414,7 @@ var init_next = __esm({
40358
40414
  init_http2();
40359
40415
  init_session();
40360
40416
  init_formatter();
40361
- logger7 = createLogger("cli:next");
40417
+ logger8 = createLogger("cli:next");
40362
40418
  nextCommand = new Command10("next").description("Advance to the next lesson in the active course").option("--json", "Output structured JSON").action(async (opts) => {
40363
40419
  try {
40364
40420
  const session = await requireSession();
@@ -40366,7 +40422,7 @@ var init_next = __esm({
40366
40422
  const driftWarning = await checkCourseDrift();
40367
40423
  if (driftWarning) process.stderr.write(driftWarning + "\n");
40368
40424
  const data = createHttpProvider(session.apiUrl, session.token);
40369
- const deps = { data, logger: logger7 };
40425
+ const deps = { data, logger: logger8 };
40370
40426
  const lessonData = await nextLesson(
40371
40427
  { enrollmentId: activeCourse.enrollmentId, userConfirmation: "cli-next" },
40372
40428
  deps
@@ -40447,7 +40503,7 @@ function formatLessonContent(data) {
40447
40503
  }
40448
40504
  return lines.join("\n");
40449
40505
  }
40450
- var logger8, lessonCommand;
40506
+ var logger9, lessonCommand;
40451
40507
  var init_lesson = __esm({
40452
40508
  "src/commands/lesson.ts"() {
40453
40509
  "use strict";
@@ -40457,7 +40513,7 @@ var init_lesson = __esm({
40457
40513
  init_session();
40458
40514
  init_formatter();
40459
40515
  init_resolve();
40460
- logger8 = createLogger("cli:lesson");
40516
+ logger9 = createLogger("cli:lesson");
40461
40517
  lessonCommand = new Command11("lesson").description("Show the content of the current lesson").option("--json", "Output structured JSON").action(async (opts) => {
40462
40518
  try {
40463
40519
  const session = await requireSession();
@@ -40465,7 +40521,7 @@ var init_lesson = __esm({
40465
40521
  const driftWarning = await checkCourseDrift();
40466
40522
  if (driftWarning) process.stderr.write(driftWarning + "\n");
40467
40523
  const data = createHttpProvider(session.apiUrl, session.token);
40468
- const deps = { data, logger: logger8 };
40524
+ const deps = { data, logger: logger9 };
40469
40525
  const lessonId = activeCourse.currentLessonId;
40470
40526
  if (!lessonId) {
40471
40527
  error("Nenhuma li\xE7\xE3o ativa encontrada. Rode: tostudy start ou tostudy next");
@@ -40494,7 +40550,7 @@ var init_lesson = __esm({
40494
40550
 
40495
40551
  // src/commands/hint.ts
40496
40552
  import { Command as Command12 } from "commander";
40497
- var logger9, hintCommand;
40553
+ var logger10, hintCommand;
40498
40554
  var init_hint = __esm({
40499
40555
  "src/commands/hint.ts"() {
40500
40556
  "use strict";
@@ -40503,7 +40559,7 @@ var init_hint = __esm({
40503
40559
  init_http2();
40504
40560
  init_session();
40505
40561
  init_formatter();
40506
- logger9 = createLogger("cli:hint");
40562
+ logger10 = createLogger("cli:hint");
40507
40563
  hintCommand = new Command12("hint").description("Get a progressive hint for the current exercise").option("--json", "Output structured JSON").action(async (opts) => {
40508
40564
  try {
40509
40565
  const session = await requireSession();
@@ -40511,7 +40567,7 @@ var init_hint = __esm({
40511
40567
  const driftWarning = await checkCourseDrift();
40512
40568
  if (driftWarning) process.stderr.write(driftWarning + "\n");
40513
40569
  const data = createHttpProvider(session.apiUrl, session.token);
40514
- const deps = { data, logger: logger9 };
40570
+ const deps = { data, logger: logger10 };
40515
40571
  const hint = await getHint(
40516
40572
  { userId: session.userId, enrollmentId: activeCourse.enrollmentId },
40517
40573
  deps
@@ -40802,7 +40858,7 @@ var init_init_template = __esm({
40802
40858
  import fs8 from "node:fs";
40803
40859
  import path7 from "node:path";
40804
40860
  import { Command as Command13 } from "commander";
40805
- var logger10, validateCommand;
40861
+ var logger11, validateCommand;
40806
40862
  var init_validate = __esm({
40807
40863
  "src/commands/validate.ts"() {
40808
40864
  "use strict";
@@ -40812,7 +40868,7 @@ var init_validate = __esm({
40812
40868
  init_session();
40813
40869
  init_formatter();
40814
40870
  init_init_template();
40815
- logger10 = createLogger("cli:validate");
40871
+ logger11 = createLogger("cli:validate");
40816
40872
  validateCommand = new Command13("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) => {
40817
40873
  try {
40818
40874
  const session = await requireSession();
@@ -40871,7 +40927,7 @@ var init_validate = __esm({
40871
40927
  }
40872
40928
  }
40873
40929
  const data = createHttpProvider(session.apiUrl, session.token);
40874
- const deps = { data, logger: logger10 };
40930
+ const deps = { data, logger: logger11 };
40875
40931
  const result = await validateSolution(
40876
40932
  {
40877
40933
  lessonId,
@@ -41217,6 +41273,23 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
41217
41273
  source: "cli"
41218
41274
  });
41219
41275
  await deps.saveCourseLearnerProfile(activeCourse, learnerProfile, artifacts);
41276
+ try {
41277
+ generateInstructionFiles(
41278
+ {
41279
+ courseTitle: matchedCourse.title,
41280
+ courseId: activeCourse.courseId,
41281
+ progress: progressData?.coursePercent ?? matchedCourse.progress ?? 0,
41282
+ moduleCount: progressData?.currentModule?.totalModules ?? 0,
41283
+ lessonCount: progressData?.currentLesson?.totalLessons ?? 0
41284
+ },
41285
+ learnerProfile
41286
+ );
41287
+ deps.logger.info("Instruction files enriched with learner profile");
41288
+ } catch (err) {
41289
+ deps.logger.warn("Failed to update instruction files", {
41290
+ error: err instanceof Error ? err.message : String(err)
41291
+ });
41292
+ }
41220
41293
  try {
41221
41294
  await deps.setLastInitCourseId(activeCourse.courseId);
41222
41295
  } catch (err) {
@@ -41225,7 +41298,7 @@ Rode \`tostudy select <n\xFAmero>\` para ativar um curso.`,
41225
41298
  deps.output(artifacts.tutorInstructions, { json: false });
41226
41299
  deps.output(artifacts.learnerBrief, { json: false });
41227
41300
  }
41228
- var logger11, defaultDeps3, initCommand;
41301
+ var logger12, defaultDeps3, initCommand;
41229
41302
  var init_init = __esm({
41230
41303
  "src/commands/init.ts"() {
41231
41304
  "use strict";
@@ -41237,7 +41310,8 @@ var init_init = __esm({
41237
41310
  init_init_template();
41238
41311
  init_learner_context();
41239
41312
  init_api2();
41240
- logger11 = createLogger("cli:init");
41313
+ init_instruction_files();
41314
+ logger12 = createLogger("cli:init");
41241
41315
  defaultDeps3 = {
41242
41316
  getSession,
41243
41317
  getActiveCourse,
@@ -41251,7 +41325,7 @@ var init_init = __esm({
41251
41325
  saveCourseLearnerProfile,
41252
41326
  buildInitArtifacts,
41253
41327
  output,
41254
- logger: logger11,
41328
+ logger: logger12,
41255
41329
  createHttpProvider
41256
41330
  };
41257
41331
  initCommand = new Command15("init").description("Generate tutor instructions and learner brief for the active course").action(async () => {
@@ -41571,14 +41645,14 @@ import { Command as Command16 } from "commander";
41571
41645
  import path10 from "node:path";
41572
41646
  import os7 from "node:os";
41573
41647
  import fs11 from "node:fs/promises";
41574
- var logger12, workspaceCommand;
41648
+ var logger13, workspaceCommand;
41575
41649
  var init_workspace2 = __esm({
41576
41650
  "src/commands/workspace.ts"() {
41577
41651
  "use strict";
41578
41652
  init_src();
41579
41653
  init_workspace();
41580
41654
  init_session();
41581
- logger12 = createLogger("cli:workspace");
41655
+ logger13 = createLogger("cli:workspace");
41582
41656
  workspaceCommand = new Command16("workspace").description(
41583
41657
  "Gerenciar workspace de estudo local"
41584
41658
  );
@@ -41609,7 +41683,7 @@ Pr\xF3ximo passo: tostudy export
41609
41683
  );
41610
41684
  }
41611
41685
  } catch (err) {
41612
- logger12.error("workspace setup failed", { error: err });
41686
+ logger13.error("workspace setup failed", { error: err });
41613
41687
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
41614
41688
  `);
41615
41689
  process.exit(1);
@@ -41711,7 +41785,7 @@ Pr\xF3ximo passo: tostudy export
41711
41785
  import { Command as Command17 } from "commander";
41712
41786
  import path11 from "node:path";
41713
41787
  import os8 from "node:os";
41714
- var logger13, exportCommand;
41788
+ var logger14, exportCommand;
41715
41789
  var init_export = __esm({
41716
41790
  "src/commands/export.ts"() {
41717
41791
  "use strict";
@@ -41720,7 +41794,7 @@ var init_export = __esm({
41720
41794
  init_http2();
41721
41795
  init_session();
41722
41796
  init_resolve();
41723
- logger13 = createLogger("cli:export");
41797
+ logger14 = createLogger("cli:export");
41724
41798
  exportCommand = new Command17("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", path11.join(os8.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
41725
41799
  try {
41726
41800
  const session = await requireSession();
@@ -41765,7 +41839,7 @@ ${result.files.map((f) => ` \u{1F4C4} ${f}`).join("\n")}
41765
41839
  );
41766
41840
  }
41767
41841
  } catch (err) {
41768
- logger13.error("export failed", { error: err });
41842
+ logger14.error("export failed", { error: err });
41769
41843
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
41770
41844
  `);
41771
41845
  process.exit(1);
@@ -41790,13 +41864,13 @@ async function findWorkspacePath(courseTitle, basePath) {
41790
41864
  return null;
41791
41865
  }
41792
41866
  }
41793
- var logger14, openCommand;
41867
+ var logger15, openCommand;
41794
41868
  var init_open = __esm({
41795
41869
  "src/commands/open.ts"() {
41796
41870
  "use strict";
41797
41871
  init_src();
41798
41872
  init_session();
41799
- logger14 = createLogger("cli:open");
41873
+ logger15 = createLogger("cli:open");
41800
41874
  openCommand = new Command18("open").description("Abrir workspace do curso na IDE").option("--path <dir>", "Diret\xF3rio base do workspace", path12.join(os9.homedir(), "study")).action(async (opts) => {
41801
41875
  try {
41802
41876
  const activeCourse = await requireActiveCourse();
@@ -41810,7 +41884,7 @@ var init_open = __esm({
41810
41884
  const editor = process.env["EDITOR"] ?? "code";
41811
41885
  execFile3(editor, [workspacePath], (err) => {
41812
41886
  if (err) {
41813
- logger14.error("open failed", { editor, workspacePath });
41887
+ logger15.error("open failed", { editor, workspacePath });
41814
41888
  process.stderr.write(`\u274C Falha ao abrir: ${err.message}
41815
41889
  `);
41816
41890
  process.exit(1);
@@ -41819,7 +41893,7 @@ var init_open = __esm({
41819
41893
  `);
41820
41894
  });
41821
41895
  } catch (err) {
41822
- logger14.error("open command failed", { error: err });
41896
+ logger15.error("open command failed", { error: err });
41823
41897
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
41824
41898
  `);
41825
41899
  process.exit(1);
@@ -41891,7 +41965,7 @@ import { Command as Command19 } from "commander";
41891
41965
  import path14 from "node:path";
41892
41966
  import os10 from "node:os";
41893
41967
  import fs14 from "node:fs/promises";
41894
- var logger15, vaultCommand;
41968
+ var logger16, vaultCommand;
41895
41969
  var init_vault2 = __esm({
41896
41970
  "src/commands/vault.ts"() {
41897
41971
  "use strict";
@@ -41900,7 +41974,7 @@ var init_vault2 = __esm({
41900
41974
  init_courses();
41901
41975
  init_http2();
41902
41976
  init_session();
41903
- logger15 = createLogger("cli:vault");
41977
+ logger16 = createLogger("cli:vault");
41904
41978
  vaultCommand = new Command19("vault").description("Gerenciar vault Obsidian do curso");
41905
41979
  vaultCommand.command("init").description("Gerar vault Obsidian para o curso ativo").option("--path <dir>", "Diret\xF3rio base do workspace", path14.join(os10.homedir(), "study")).option("--json", "Output structured JSON").action(async (opts) => {
41906
41980
  try {
@@ -41934,7 +42008,7 @@ var init_vault2 = __esm({
41934
42008
  activeCourse.courseId,
41935
42009
  courseSlug2
41936
42010
  );
41937
- logger15.info("Vault generated", {
42011
+ logger16.info("Vault generated", {
41938
42012
  courseId: activeCourse.courseId,
41939
42013
  vaultPath: result.vaultPath,
41940
42014
  filesWritten: result.filesWritten
@@ -41967,7 +42041,7 @@ Para visualizar:
41967
42041
  );
41968
42042
  }
41969
42043
  } catch (err) {
41970
- logger15.error("vault init failed", { error: err });
42044
+ logger16.error("vault init failed", { error: err });
41971
42045
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
41972
42046
  `);
41973
42047
  process.exit(1);
@@ -41988,7 +42062,7 @@ Para visualizar:
41988
42062
  process.exit(1);
41989
42063
  }
41990
42064
  const data = createHttpProvider(session.apiUrl, session.token);
41991
- const deps = { data, logger: logger15 };
42065
+ const deps = { data, logger: logger16 };
41992
42066
  const progress3 = await getProgress({ enrollmentId: activeCourse.enrollmentId }, deps);
41993
42067
  const markerPath = path14.join(vaultPath, ".ana-vault.json");
41994
42068
  const markerRaw = await fs14.readFile(markerPath, "utf-8");
@@ -42042,7 +42116,7 @@ Para visualizar:
42042
42116
  );
42043
42117
  }
42044
42118
  } catch (err) {
42045
- logger15.error("vault sync failed", { error: err });
42119
+ logger16.error("vault sync failed", { error: err });
42046
42120
  process.stderr.write(`\u274C ${err instanceof Error ? err.message : String(err)}
42047
42121
  `);
42048
42122
  process.exit(1);
@@ -42105,7 +42179,7 @@ var init_cli = __esm({
42105
42179
  init_export();
42106
42180
  init_open();
42107
42181
  init_vault2();
42108
- CLI_VERSION = "0.6.0";
42182
+ CLI_VERSION = true ? "0.7.1" : "0.7.1";
42109
42183
  }
42110
42184
  });
42111
42185