vibelearn 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibelearn",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Developer learning tool — analyze coding sessions, extract concepts, generate quiz questions",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibelearn",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Learn from every coding session — automatically extracts concepts from your Claude Code sessions, generates adaptive quiz questions across 7 formats, and tracks mastery with spaced repetition.",
5
5
  "author": {
6
6
  "name": "Ahun Atajanov"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibelearn",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for vibelearn bundled hooks",
6
6
  "type": "module",
@@ -114,7 +114,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
114
114
  ${c}`}var xP=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".rb",".java",".cs",".cpp",".c",".h",".hpp",".swift",".kt",".php",".vue",".svelte"]),kP=new Set(["node_modules",".git","dist","build",".next","__pycache__",".venv","venv","env",".env","target","vendor",".cache",".turbo","coverage",".nyc_output",".claude",".smart-file-read"]),SP=512*1024;async function*u$(t,e,r=20){if(r<=0)return;let n;try{n=await(0,zn.readdir)(t,{withFileTypes:!0})}catch{return}for(let o of n){if(o.name.startsWith(".")&&o.name!=="."||kP.has(o.name))continue;let i=(0,hi.join)(t,o.name);if(o.isDirectory())yield*u$(i,e,r-1);else if(o.isFile()){let a=o.name.slice(o.name.lastIndexOf("."));xP.has(a)&&(yield i)}}}async function wP(t){try{let e=await(0,zn.stat)(t);if(e.size>SP||e.size===0)return null;let r=await(0,zn.readFile)(t,"utf-8");return r.slice(0,1e3).includes("\0")?null:r}catch{return null}}async function l$(t,e,r={}){let n=r.maxResults||20,o=e.toLowerCase(),i=o.split(/[\s_\-./]+/).filter(h=>h.length>0),a=[];for await(let h of u$(t,t)){if(r.filePattern&&!(0,hi.relative)(t,h).toLowerCase().includes(r.filePattern.toLowerCase()))continue;let _=await wP(h);_&&a.push({absolutePath:h,relativePath:(0,hi.relative)(t,h),content:_})}let s=a$(a),c=[],u=[],l=0;for(let[h,_]of s){l+=zP(_);let E=Ns(h.toLowerCase(),i)>0,I=[],A=(j,Le)=>{for(let de of j){let Bt=0,Qe="",Wt=Ns(de.name.toLowerCase(),i);Wt>0&&(Bt+=Wt*3,Qe="name match"),de.signature.toLowerCase().includes(o)&&(Bt+=2,Qe=Qe?`${Qe} + signature`:"signature match"),de.jsdoc&&de.jsdoc.toLowerCase().includes(o)&&(Bt+=1,Qe=Qe?`${Qe} + jsdoc`:"jsdoc match"),Bt>0&&(E=!0,I.push({filePath:h,symbolName:Le?`${Le}.${de.name}`:de.name,kind:de.kind,signature:de.signature,jsdoc:de.jsdoc,lineStart:de.lineStart,lineEnd:de.lineEnd,matchReason:Qe})),de.children&&A(de.children,de.name)}};A(_.symbols),E&&(c.push(_),u.push(...I))}u.sort((h,_)=>{let b=Ns(h.symbolName.toLowerCase(),i);return Ns(_.symbolName.toLowerCase(),i)-b});let d=u.slice(0,n),p=new Set(d.map(h=>h.filePath)),f=c.filter(h=>p.has(h.filePath)).slice(0,n),g=f.reduce((h,_)=>h+_.foldedTokenEstimate,0);return{foldedFiles:f,matchingSymbols:d,totalFilesScanned:a.length,totalSymbolsFound:l,tokenEstimate:g}}function Ns(t,e){let r=0;for(let n of e)if(t===n)r+=10;else if(t.includes(n))r+=5;else{let o=0,i=0;for(let a of n){let s=t.indexOf(a,o);s!==-1&&(i++,o=s+1)}i===n.length&&(r+=1)}return r}function zP(t){let e=t.symbols.length;for(let r of t.symbols)r.children&&(e+=r.children.length);return e}function d$(t,e){let r=[];if(r.push(`\u{1F50D} Smart Search: "${e}"`),r.push(` Scanned ${t.totalFilesScanned} files, found ${t.totalSymbolsFound} symbols`),r.push(` ${t.matchingSymbols.length} matches across ${t.foldedFiles.length} files (~${t.tokenEstimate} tokens for folded view)`),r.push(""),t.matchingSymbols.length===0)return r.push(" No matching symbols found."),r.join(`
115
115
  `);r.push("\u2500\u2500 Matching Symbols \u2500\u2500"),r.push("");for(let n of t.matchingSymbols){if(r.push(` ${n.kind} ${n.symbolName} (${n.filePath}:${n.lineStart+1})`),r.push(` ${n.signature}`),n.jsdoc){let o=n.jsdoc.split(`
116
116
  `).find(i=>i.replace(/^[\s*/]+/,"").trim().length>0);o&&r.push(` \u{1F4AC} ${o.replace(/^[\s*/]+/,"").trim()}`)}r.push("")}r.push("\u2500\u2500 Folded File Views \u2500\u2500"),r.push("");for(let n of t.foldedFiles)r.push(wn(n)),r.push("");return r.push("\u2500\u2500 Actions \u2500\u2500"),r.push(" To see full implementation: use smart_unfold with file path and symbol name"),r.join(`
117
- `)}var jf=require("node:fs/promises"),Rs=require("node:path"),IP="0.1.3";console.log=(...t)=>{ve.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var p$={search:"/api/search",timeline:"/api/timeline"};async function f$(t,e){ve.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(e))s!=null&&r.append(a,String(s));let n=`${t}?${r}`,o=await Os(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return ve.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(r){return ve.error("SYSTEM","\u2190 Worker API error",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function EP(t,e){ve.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{let r=await Os(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return ve.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return ve.error("HTTP","Worker API error (POST)",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function TP(){try{return(await Os("/api/health")).ok}catch(t){return ve.debug("SYSTEM","Worker health check failed",{},t),!1}}var m$=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
117
+ `)}var jf=require("node:fs/promises"),Rs=require("node:path"),IP="0.1.6";console.log=(...t)=>{ve.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:t})};var p$={search:"/api/search",timeline:"/api/timeline"};async function f$(t,e){ve.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:t,params:e});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(e))s!=null&&r.append(a,String(s));let n=`${t}?${r}`,o=await Os(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return ve.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:t}),i}catch(r){return ve.error("SYSTEM","\u2190 Worker API error",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function EP(t,e){ve.debug("HTTP","Worker API request (POST)",void 0,{endpoint:t});try{let r=await Os(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return ve.debug("HTTP","Worker API success (POST)",void 0,{endpoint:t}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return ve.error("HTTP","Worker API error (POST)",{endpoint:t},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function TP(){try{return(await Os("/api/health")).ok}catch(t){return ve.debug("SYSTEM","Worker health check failed",{},t),!1}}var m$=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
118
118
  1. search(query) \u2192 Get index with IDs (~50-100 tokens/result)
119
119
  2. timeline(anchor=ID) \u2192 Get context around interesting results
120
120
  3. get_observations([IDs]) \u2192 Fetch full details ONLY for filtered IDs
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bun
2
- "use strict";var B=Object.create;var x=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty;var Z=(e,n,t,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of J(n))!X.call(e,r)&&r!==t&&x(e,r,{get:()=>n[r],enumerable:!(s=G(n,r))||s.enumerable});return e};var ee=(e,n,t)=>(t=e!=null?B(K(e)):{},Z(n||!e||!e.__esModule?x(t,"default",{value:e,enumerable:!0}):t,e));var j=require("readline"),N=require("bun:sqlite"),q=require("path"),T=require("os"),m=require("fs");function I(e,n){let t=e+n;return t===0?0:e/t}function O(e){return e>=.85?"senior":e>=.5?"mid":"junior"}function M(e,n){let t=Math.floor(Date.now()/1e3),s=e.query("SELECT * FROM vl_developer_profile WHERE concept_name = ?").get(n.conceptName);if(!s){let p=n.isCorrect?1:0,a=n.isCorrect?0:1,v=I(p,a),f=n.isCorrect?1:0,S=O(v);return e.run(`INSERT INTO vl_developer_profile
2
+ "use strict";var B=Object.create;var O=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var J=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty;var Z=(e,n,t,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of K(n))!X.call(e,r)&&r!==t&&O(e,r,{get:()=>n[r],enumerable:!(s=G(n,r))||s.enumerable});return e};var ee=(e,n,t)=>(t=e!=null?B(J(e)):{},Z(n||!e||!e.__esModule?O(t,"default",{value:e,enumerable:!0}):t,e));var j=require("readline"),N=require("bun:sqlite"),q=require("path"),T=require("os"),m=require("fs");function h(e,n){let t=e+n;return t===0?0:e/t}function x(e){return e>=.85?"senior":e>=.5?"mid":"junior"}function M(e,n){let t=Math.floor(Date.now()/1e3),s=e.query("SELECT * FROM vl_developer_profile WHERE concept_name = ?").get(n.conceptName);if(!s){let _=n.isCorrect?1:0,a=n.isCorrect?0:1,v=h(_,a),f=n.isCorrect?1:0,S=x(v);return e.run(`INSERT INTO vl_developer_profile
3
3
  (concept_name, category, first_seen_at, last_seen_at, encounter_count,
4
4
  correct_answers, incorrect_answers, current_level, streak_count, mastery_score)
5
- VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?)`,[n.conceptName,n.category,t,t,p,a,S,f,v]),{concept_name:n.conceptName,category:n.category,first_seen_at:t,last_seen_at:t,encounter_count:1,correct_answers:p,incorrect_answers:a,current_level:S,streak_count:f,mastery_score:v}}let r=s.correct_answers+(n.isCorrect?1:0),i=s.incorrect_answers+(n.isCorrect?0:1),c=I(r,i),l=n.isCorrect?s.streak_count+1:0,u=O(c);return e.run(`UPDATE vl_developer_profile SET
5
+ VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?)`,[n.conceptName,n.category,t,t,_,a,S,f,v]),{concept_name:n.conceptName,category:n.category,first_seen_at:t,last_seen_at:t,encounter_count:1,correct_answers:_,incorrect_answers:a,current_level:S,streak_count:f,mastery_score:v}}let r=s.correct_answers+(n.isCorrect?1:0),i=s.incorrect_answers+(n.isCorrect?0:1),c=h(r,i),l=n.isCorrect?s.streak_count+1:0,u=x(c);return e.run(`UPDATE vl_developer_profile SET
6
6
  last_seen_at = ?,
7
7
  encounter_count = encounter_count + 1,
8
8
  correct_answers = ?,
@@ -14,7 +14,7 @@
14
14
  questions_answered = questions_answered + 1,
15
15
  correct_answers = correct_answers + ?
16
16
  WHERE date = ?`,[n?1:0,t]):e.run(`INSERT INTO vl_daily_streaks (date, questions_answered, correct_answers, streak_continues)
17
- VALUES (?, 1, ?, 1)`,[t,n?1:0])}function $(e){let{isCorrect:n,currentEaseFactor:t,currentIntervalDays:s,currentRepetitions:r,nowEpoch:i}=e;if(!n){let p=Math.max(1.3,t-.2);return{nextReviewAt:i+86400,easeFactor:p,intervalDays:1,repetitions:0}}let c=t+.1,l=r+1,u;return l===1?u=1:l===2?u=6:u=Math.round(s*t),{nextReviewAt:i+u*86400,easeFactor:c,intervalDays:u,repetitions:l}}function F(e,n,t){e.run(`UPDATE vl_questions
17
+ VALUES (?, 1, ?, 1)`,[t,n?1:0])}function F(e){let{isCorrect:n,currentEaseFactor:t,currentIntervalDays:s,currentRepetitions:r,nowEpoch:i}=e;if(!n){let _=Math.max(1.3,t-.2);return{nextReviewAt:i+86400,easeFactor:_,intervalDays:1,repetitions:0}}let c=t+.1,l=r+1,u;return l===1?u=1:l===2?u=6:u=Math.round(s*t),{nextReviewAt:i+u*86400,easeFactor:c,intervalDays:u,repetitions:l}}function $(e,n,t){e.run(`UPDATE vl_questions
18
18
  SET next_review_at = ?,
19
19
  ease_factor = ?,
20
20
  interval_days = ?,
@@ -30,7 +30,7 @@
30
30
  ${r}
31
31
  ORDER BY q.next_review_at ASC NULLS FIRST, q.created_at DESC
32
32
  LIMIT ?
33
- `).all(...i)}var R=["junior","mid","senior"],ne=3;function te(e){let n=R.indexOf(e);return n===-1||n>=R.length-1?"senior":R[n+1]}function oe(e){let n=R.indexOf(e);return n<=0?"junior":R[n-1]}function P(e,n){let t=e.query("SELECT current_level FROM vl_developer_profile WHERE concept_name = ?").get(n.conceptName),s=!t,r=t?.current_level??"junior",i=M(e,n),c=i.current_level,l=!1,u=!1;if(!s)if(n.isCorrect&&i.streak_count>=ne){let a=te(r);a!==r?(c=a,l=!0,e.run("UPDATE vl_developer_profile SET current_level = ?, streak_count = 0 WHERE concept_name = ?",[c,n.conceptName]),i.streak_count=0):(c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]))}else if(n.isCorrect)c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]);else{let a=oe(r);a!==r?(c=a,u=!0,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName])):(c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]))}return{profile:{...i,current_level:c},levelChanged:!s&&c!==r,previousLevel:r,promoted:l,demoted:u}}function Q(e,n,t,s){if(!e.follow_up_mid||!e.follow_up_mid.trim()||e.difficulty!=="junior"||!n||t!=="junior")return!1;let r=`followup_${e.id}`;return!s.has(r)}function z(e){return{id:`followup_${e.id}`,concept_name:e.concept_name,question_type:"open_ended",difficulty:"mid",snippet:e.snippet??null,question:e.follow_up_mid??"",options_json:null,correct:null,explanation:"(Follow-up \u2014 no single correct answer. Reflect on your understanding.)",follow_up_mid:null,is_follow_up:!0}}var b=process.env.VIBELEARN_DATA_DIR?process.env.VIBELEARN_DATA_DIR.replace("~",(0,T.homedir)()):(0,q.join)((0,T.homedir)(),".vibelearn"),L=(0,q.join)(b,"vibelearn.db"),A=(0,q.join)(b,"config.json");function H(){try{if((0,m.existsSync)(A))return JSON.parse((0,m.readFileSync)(A,"utf-8"))}catch{}return{}}function re(e){(0,m.existsSync)(b)||(0,m.mkdirSync)(b,{recursive:!0}),(0,m.writeFileSync)(A,JSON.stringify(e,null,2),"utf-8")}function h(){return(0,m.existsSync)(L)?new N.Database(L,{readonly:!0}):(console.log("No VibeLearn database found. Start a coding session first!"),null)}async function se(){let e=h();if(!e)return;let n=e.query("SELECT COUNT(*) as count FROM vibelearn_session_summaries").get()?.count??0,t=e.query("SELECT COUNT(*) as count FROM vl_concepts").get()?.count??0,s=e.query("SELECT COUNT(*) as count FROM vl_questions").get()?.count??0,r=e.query(`
33
+ `).all(...i)}var R=["junior","mid","senior"],ne=3;function te(e){let n=R.indexOf(e);return n===-1||n>=R.length-1?"senior":R[n+1]}function oe(e){let n=R.indexOf(e);return n<=0?"junior":R[n-1]}function P(e,n){let t=e.query("SELECT current_level FROM vl_developer_profile WHERE concept_name = ?").get(n.conceptName),s=!t,r=t?.current_level??"junior",i=M(e,n),c=i.current_level,l=!1,u=!1;if(!s)if(n.isCorrect&&i.streak_count>=ne){let a=te(r);a!==r?(c=a,l=!0,e.run("UPDATE vl_developer_profile SET current_level = ?, streak_count = 0 WHERE concept_name = ?",[c,n.conceptName]),i.streak_count=0):(c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]))}else if(n.isCorrect)c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]);else{let a=oe(r);a!==r?(c=a,u=!0,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName])):(c=r,e.run("UPDATE vl_developer_profile SET current_level = ? WHERE concept_name = ?",[c,n.conceptName]))}return{profile:{...i,current_level:c},levelChanged:!s&&c!==r,previousLevel:r,promoted:l,demoted:u}}function Q(e,n,t,s){if(!e.follow_up_mid||!e.follow_up_mid.trim()||e.difficulty!=="junior"||!n||t!=="junior")return!1;let r=`followup_${e.id}`;return!s.has(r)}function z(e){return{id:`followup_${e.id}`,concept_name:e.concept_name,question_type:"open_ended",difficulty:"mid",snippet:e.snippet??null,question:e.follow_up_mid??"",options_json:null,correct:null,explanation:"(Follow-up \u2014 no single correct answer. Reflect on your understanding.)",follow_up_mid:null,is_follow_up:!0}}var re="0.1.6",b=process.env.VIBELEARN_DATA_DIR?process.env.VIBELEARN_DATA_DIR.replace("~",(0,T.homedir)()):(0,q.join)((0,T.homedir)(),".vibelearn"),A=(0,q.join)(b,"vibelearn.db"),L=(0,q.join)(b,"config.json");function H(){try{if((0,m.existsSync)(L))return JSON.parse((0,m.readFileSync)(L,"utf-8"))}catch{}return{}}function se(e){(0,m.existsSync)(b)||(0,m.mkdirSync)(b,{recursive:!0}),(0,m.writeFileSync)(L,JSON.stringify(e,null,2),"utf-8")}function I(){return(0,m.existsSync)(A)?new N.Database(A,{readonly:!0}):(console.log("No VibeLearn database found. Start a coding session first!"),null)}async function ie(){let e=I();if(!e)return;let n=e.query("SELECT COUNT(*) as count FROM vibelearn_session_summaries").get()?.count??0,t=e.query("SELECT COUNT(*) as count FROM vl_concepts").get()?.count??0,s=e.query("SELECT COUNT(*) as count FROM vl_questions").get()?.count??0,r=e.query(`
34
34
  SELECT COUNT(*) as count FROM vl_questions
35
35
  WHERE id NOT IN (SELECT DISTINCT question_id FROM vl_quiz_attempts)
36
36
  `).get()?.count??0,i=e.query(`
@@ -44,7 +44,7 @@
44
44
  `).get()?.count??0;console.log(`
45
45
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(" VibeLearn Status"),console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.log(` Sessions analyzed : ${n}`),console.log(` Concepts captured : ${t}`),console.log(` Quiz questions : ${s} (${r} pending)`),console.log(` Mastered concepts : ${c}`),i.length>0&&(console.log(`
46
46
  Top categories:`),i.forEach(l=>{console.log(` ${l.category.padEnd(20)} ${l.count}`)})),console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
47
- `),e.close()}async function ie(){let e=h();if(!e)return;let n=e.query(`
47
+ `),e.close()}async function ce(){let e=I();if(!e)return;let n=e.query(`
48
48
  SELECT concept_name, category, mastery_score, encounter_count as times_seen
49
49
  FROM vl_developer_profile
50
50
  WHERE mastery_score < 0.5
@@ -54,44 +54,44 @@
54
54
  No knowledge gaps found. Keep coding and learning!
55
55
  `),e.close();return}console.log(`
56
56
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(" Knowledge Gaps (mastery < 50%)"),console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),n.forEach(t=>{let s="\u2588".repeat(Math.round(t.mastery_score*10))+"\u2591".repeat(10-Math.round(t.mastery_score*10)),r=Math.round(t.mastery_score*100);console.log(` ${t.concept_name.padEnd(30)} [${s}] ${String(r).padStart(3)}% (${t.category})`)}),console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
57
- `),e.close()}async function ce(e){let n=h();if(!n)return;let t=Math.floor(Date.now()/1e3),s;if(e){let o=n.query(`
57
+ `),e.close()}async function le(e){let n=I();if(!n)return;let t=Math.floor(Date.now()/1e3),s;if(e){let o=n.query(`
58
58
  SELECT session_id FROM vibelearn_session_summaries
59
59
  ORDER BY generated_at DESC LIMIT 1
60
60
  `).get();if(!o){console.log(`
61
61
  No sessions found. Run a coding session first!
62
62
  `),n.close();return}s=o.session_id}let r=U(n,t,s,20);if(n.close(),r.length===0){console.log(`
63
63
  No pending questions! Great job staying on top of your learning.
64
- `);return}let i=new N.Database(L),c=(0,j.createInterface)({input:process.stdin,output:process.stdout}),l=o=>new Promise(C=>c.question(o,C)),u=0,p=0,a=[...r],v=new Set(a.map(o=>o.id));console.log(`
64
+ `);return}let i=new N.Database(A),c=(0,j.createInterface)({input:process.stdin,output:process.stdout}),l=o=>new Promise(C=>c.question(o,C)),u=0,_=0,a=[...r],v=new Set(a.map(o=>o.id));console.log(`
65
65
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(` VibeLearn Quiz \u2014 ${a.length} questions`),console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
66
66
  `);let f=0;for(;f<a.length;){let o=a[f],C=`Q${f+1}/${a.length}`,W=o.concept_name?` [${o.concept_name}]`:"";console.log(`
67
67
  ${C} (${o.difficulty})${W}`),o.snippet&&o.snippet.trim()&&(console.log(`
68
68
  Code:
69
69
  `),o.snippet.split(`
70
70
  `).forEach(y=>console.log(` ${y}`)),console.log("")),console.log(` ${o.question}
71
- `);let g="",Y=Date.now();if(o.question_type==="multiple_choice"&&o.options_json)try{JSON.parse(o.options_json).forEach((E,_)=>{let w=String.fromCharCode(65+_);console.log(` ${w}) ${E}`)}),console.log(""),g=(await l(" Your answer (A/B/C/D): ")).trim().toUpperCase()}catch{g=(await l(" Your answer: ")).trim()}else if(o.question_type==="fill_in_blank")g=(await l(" Fill in: ")).trim();else if(o.question_type==="ordering"&&o.options_json)try{JSON.parse(o.options_json).forEach((E,_)=>console.log(` ${_+1}. ${E}`)),console.log(""),g=(await l(" Enter correct order (e.g. 2,4,1,3): ")).trim()}catch{g=(await l(" Your answer: ")).trim()}else o.question_type==="true_false"?g=(await l(" True or False? ")).trim().toLowerCase():(o.question_type==="open_ended"&&console.log(" (Open-ended \u2014 describe your reasoning, then press Enter)"),g=(await l(" Your answer: ")).trim());let V=Date.now()-Y,D=o.question_type==="open_ended",d=!1;!D&&o.correct!==null&&(d=g.toLowerCase()===o.correct.toLowerCase()),D?console.log(`
71
+ `);let g="",V=Date.now();if(o.question_type==="multiple_choice"&&o.options_json)try{JSON.parse(o.options_json).forEach((E,p)=>{let w=String.fromCharCode(65+p);console.log(` ${w}) ${E}`)}),console.log(""),g=(await l(" Your answer (A/B/C/D): ")).trim().toUpperCase()}catch{g=(await l(" Your answer: ")).trim()}else if(o.question_type==="fill_in_blank")g=(await l(" Fill in: ")).trim();else if(o.question_type==="ordering"&&o.options_json)try{JSON.parse(o.options_json).forEach((E,p)=>console.log(` ${p+1}. ${E}`)),console.log(""),g=(await l(" Enter correct order (e.g. 2,4,1,3): ")).trim()}catch{g=(await l(" Your answer: ")).trim()}else o.question_type==="true_false"?g=(await l(" True or False? ")).trim().toLowerCase():(o.question_type==="open_ended"&&console.log(" (Open-ended \u2014 describe your reasoning, then press Enter)"),g=(await l(" Your answer: ")).trim());let Y=Date.now()-V,D=o.question_type==="open_ended",d=!1;!D&&o.correct!==null&&(d=g.toLowerCase()===o.correct.toLowerCase()),D?console.log(`
72
72
  \u270E Open-ended noted.
73
73
  `):d?(console.log(`
74
74
  \u2713 Correct!
75
75
  `),u++):console.log(`
76
76
  \u2717 Incorrect. Correct answer: ${o.correct??"(see explanation)"}
77
77
  `),console.log(` Explanation: ${o.explanation}
78
- `),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),p++;try{let{randomUUID:y}=await import("crypto");if(i.run(`
78
+ `),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),_++;try{let{randomUUID:y}=await import("crypto");if(i.run(`
79
79
  INSERT INTO vl_quiz_attempts (id, question_id, answer_given, is_correct, time_taken_ms, created_at)
80
80
  VALUES (?, ?, ?, ?, ?, ?)
81
- `,[y(),o.id,g,d?1:0,V,Math.floor(Date.now()/1e3)]),!o.is_follow_up){let E=$({isCorrect:d,currentEaseFactor:o.ease_factor??2.5,currentIntervalDays:o.interval_days??0,currentRepetitions:o.repetitions??0,nowEpoch:Math.floor(Date.now()/1e3)});F(i,o.id,E)}if(o.concept_name&&!D){let E=i.query("SELECT category FROM vl_concepts WHERE concept_name = ? LIMIT 1").get(o.concept_name),_=P(i,{conceptName:o.concept_name,category:E?.category??"general",isCorrect:d});if(_.promoted?console.log(` \u{1F389} Level up! ${o.concept_name}: ${_.previousLevel} \u2192 ${_.profile.current_level}
82
- `):_.demoted&&console.log(` \u{1F4C9} Level dropped: ${o.concept_name}: ${_.previousLevel} \u2192 ${_.profile.current_level}
83
- `),Q(o,d,_.profile.current_level,v)){let w=z(o);a.splice(f+1,0,w),v.add(w.id),console.log(` \u2795 Follow-up added: ${w.question.slice(0,60)}...
84
- `)}}k(i,d)}catch{}f++}c.close(),i.close();let S=p>0?Math.round(u/p*100):0;console.log(`
85
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(` Quiz complete: ${u}/${p} correct (${S}%)`),console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
86
- `)}async function le(e,n){if(n){let s=H();if(s.api_key){let r=s.api_key.slice(0,6)+"..."+s.api_key.slice(-4);console.log(`
81
+ `,[y(),o.id,g,d?1:0,Y,Math.floor(Date.now()/1e3)]),!o.is_follow_up){let E=F({isCorrect:d,currentEaseFactor:o.ease_factor??2.5,currentIntervalDays:o.interval_days??0,currentRepetitions:o.repetitions??0,nowEpoch:Math.floor(Date.now()/1e3)});$(i,o.id,E)}if(o.concept_name&&!D){let E=i.query("SELECT category FROM vl_concepts WHERE concept_name = ? LIMIT 1").get(o.concept_name),p=P(i,{conceptName:o.concept_name,category:E?.category??"general",isCorrect:d});if(p.promoted?console.log(` \u{1F389} Level up! ${o.concept_name}: ${p.previousLevel} \u2192 ${p.profile.current_level}
82
+ `):p.demoted&&console.log(` \u{1F4C9} Level dropped: ${o.concept_name}: ${p.previousLevel} \u2192 ${p.profile.current_level}
83
+ `),Q(o,d,p.profile.current_level,v)){let w=z(o);a.splice(f+1,0,w),v.add(w.id),console.log(` \u2795 Follow-up added: ${w.question.slice(0,60)}...
84
+ `)}}k(i,d)}catch{}f++}c.close(),i.close();let S=_>0?Math.round(u/_*100):0;console.log(`
85
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(` Quiz complete: ${u}/${_} correct (${S}%)`),console.log(`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
86
+ `)}async function ae(e,n){if(n){let s=H();if(s.api_key){let r=s.api_key.slice(0,6)+"..."+s.api_key.slice(-4);console.log(`
87
87
  Logged in: API key ${r}
88
88
  `)}else console.log(`
89
89
  Not logged in. Run: vl login <api-key>
90
90
  `);return}e||(console.error(`
91
91
  Usage: vl login <api-key>
92
- `),process.exit(1));let t=H();t.api_key=e,re(t),console.log(`
92
+ `),process.exit(1));let t=H();t.api_key=e,se(t),console.log(`
93
93
  API key saved to ~/.vibelearn/config.json
94
- `)}async function ae(){let e=process.argv.slice(2);switch(e[0]){case"quiz":await ce(e.includes("--session"));break;case"status":await se();break;case"gaps":await ie();break;case"login":{let t=e.includes("--status"),s=t?null:e[1]??null;await le(s,t);break}default:console.log(`
94
+ `)}async function ue(){let e=process.argv.slice(2);switch(e[0]){case"-v":case"--version":console.log(`vl ${re}`);break;case"quiz":await le(e.includes("--session"));break;case"status":await ie();break;case"gaps":await ce();break;case"login":{let t=e.includes("--status"),s=t?null:e[1]??null;await ae(s,t);break}default:console.log(`
95
95
  VibeLearn CLI \u2014 learn from your coding sessions
96
96
 
97
97
  Usage:
@@ -101,4 +101,5 @@ Usage:
101
101
  vl gaps Concepts you haven't mastered yet
102
102
  vl login <api-key> Connect to vibelearn.dev
103
103
  vl login --status Check login status
104
- `)}}ae().catch(e=>{console.error("Error:",e.message),process.exit(1)});
104
+ vl --version Show version
105
+ `)}}ue().catch(e=>{console.error("Error:",e.message),process.exit(1)});