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
|
+
"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"
|
package/plugin/package.json
CHANGED
|
@@ -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.
|
|
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
|
|
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,
|
|
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
|
|
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"),
|
|
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
|
|
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
|
|
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(
|
|
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="",
|
|
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"),
|
|
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,
|
|
82
|
-
`):
|
|
83
|
-
`),Q(o,d,
|
|
84
|
-
`)}}k(i,d)}catch{}f++}c.close(),i.close();let S=
|
|
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}/${
|
|
86
|
-
`)}async function
|
|
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,
|
|
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
|
|
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
|
-
|
|
104
|
+
vl --version Show version
|
|
105
|
+
`)}}ue().catch(e=>{console.error("Error:",e.message),process.exit(1)});
|