vibelearn 0.1.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/LICENSE +630 -0
- package/README.md +287 -0
- package/package.json +129 -0
- package/plugin/.claude-plugin/CLAUDE.md +4 -0
- package/plugin/.claude-plugin/plugin.json +23 -0
- package/plugin/.cli-installed +1 -0
- package/plugin/CLAUDE.md +6 -0
- package/plugin/hooks/CLAUDE.md +6 -0
- package/plugin/hooks/bugfixes-2026-01-10.md +92 -0
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/modes/code--ar.json +24 -0
- package/plugin/modes/code--bn.json +24 -0
- package/plugin/modes/code--chill.json +8 -0
- package/plugin/modes/code--cs.json +24 -0
- package/plugin/modes/code--da.json +24 -0
- package/plugin/modes/code--de.json +24 -0
- package/plugin/modes/code--el.json +24 -0
- package/plugin/modes/code--es.json +24 -0
- package/plugin/modes/code--fi.json +24 -0
- package/plugin/modes/code--fr.json +24 -0
- package/plugin/modes/code--he.json +24 -0
- package/plugin/modes/code--hi.json +24 -0
- package/plugin/modes/code--hu.json +24 -0
- package/plugin/modes/code--id.json +24 -0
- package/plugin/modes/code--it.json +24 -0
- package/plugin/modes/code--ja.json +24 -0
- package/plugin/modes/code--ko.json +24 -0
- package/plugin/modes/code--nl.json +24 -0
- package/plugin/modes/code--no.json +24 -0
- package/plugin/modes/code--pl.json +24 -0
- package/plugin/modes/code--pt-br.json +24 -0
- package/plugin/modes/code--ro.json +24 -0
- package/plugin/modes/code--ru.json +24 -0
- package/plugin/modes/code--sv.json +24 -0
- package/plugin/modes/code--th.json +24 -0
- package/plugin/modes/code--tr.json +24 -0
- package/plugin/modes/code--uk.json +24 -0
- package/plugin/modes/code--ur.json +25 -0
- package/plugin/modes/code--vi.json +24 -0
- package/plugin/modes/code--zh.json +24 -0
- package/plugin/modes/code.json +125 -0
- package/plugin/modes/email-investigation.json +120 -0
- package/plugin/modes/law-study--chill.json +7 -0
- package/plugin/modes/law-study-CLAUDE.md +85 -0
- package/plugin/modes/law-study.json +120 -0
- package/plugin/package.json +23 -0
- package/plugin/scripts/CLAUDE.md +5 -0
- package/plugin/scripts/bun-runner.js +176 -0
- package/plugin/scripts/mcp-server.cjs +141 -0
- package/plugin/scripts/smart-install.js +592 -0
- package/plugin/scripts/statusline-counts.js +61 -0
- package/plugin/scripts/vl-cli.cjs +104 -0
- package/plugin/scripts/worker-cli.js +19 -0
- package/plugin/scripts/worker-service.cjs +1919 -0
- package/plugin/scripts/worker-wrapper.cjs +2 -0
- package/plugin/skills/smart-explore/SKILL.md +145 -0
- package/plugin/skills/timeline-report/SKILL.md +91 -0
|
@@ -0,0 +1,104 @@
|
|
|
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
|
|
3
|
+
(concept_name, category, first_seen_at, last_seen_at, encounter_count,
|
|
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
|
|
6
|
+
last_seen_at = ?,
|
|
7
|
+
encounter_count = encounter_count + 1,
|
|
8
|
+
correct_answers = ?,
|
|
9
|
+
incorrect_answers = ?,
|
|
10
|
+
current_level = ?,
|
|
11
|
+
streak_count = ?,
|
|
12
|
+
mastery_score = ?
|
|
13
|
+
WHERE concept_name = ?`,[t,r,i,u,l,c,n.conceptName]),{...s,last_seen_at:t,encounter_count:s.encounter_count+1,correct_answers:r,incorrect_answers:i,current_level:u,streak_count:l,mastery_score:c}}function k(e,n){let t=new Date().toISOString().slice(0,10);e.query("SELECT questions_answered, correct_answers FROM vl_daily_streaks WHERE date = ?").get(t)?e.run(`UPDATE vl_daily_streaks SET
|
|
14
|
+
questions_answered = questions_answered + 1,
|
|
15
|
+
correct_answers = correct_answers + ?
|
|
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
|
|
18
|
+
SET next_review_at = ?,
|
|
19
|
+
ease_factor = ?,
|
|
20
|
+
interval_days = ?,
|
|
21
|
+
repetitions = ?
|
|
22
|
+
WHERE id = ?`,[t.nextReviewAt,t.easeFactor,t.intervalDays,t.repetitions,n])}function U(e,n,t,s=20){let r=t?"AND q.session_id = ?":"",i=t?[n,t,s]:[n,s];return e.query(`
|
|
23
|
+
SELECT q.id, q.session_id, q.question_type, q.difficulty,
|
|
24
|
+
q.question, q.options_json, q.correct, q.explanation, q.snippet,
|
|
25
|
+
q.ease_factor, q.interval_days, q.repetitions, q.follow_up_mid,
|
|
26
|
+
c.concept_name
|
|
27
|
+
FROM vl_questions q
|
|
28
|
+
LEFT JOIN vl_concepts c ON q.concept_id = c.id
|
|
29
|
+
WHERE (q.next_review_at IS NULL OR q.next_review_at <= ?)
|
|
30
|
+
${r}
|
|
31
|
+
ORDER BY q.next_review_at ASC NULLS FIRST, q.created_at DESC
|
|
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(`
|
|
34
|
+
SELECT COUNT(*) as count FROM vl_questions
|
|
35
|
+
WHERE id NOT IN (SELECT DISTINCT question_id FROM vl_quiz_attempts)
|
|
36
|
+
`).get()?.count??0,i=e.query(`
|
|
37
|
+
SELECT category, COUNT(*) as count
|
|
38
|
+
FROM vl_concepts
|
|
39
|
+
GROUP BY category
|
|
40
|
+
ORDER BY count DESC
|
|
41
|
+
LIMIT 10
|
|
42
|
+
`).all(),c=e.query(`
|
|
43
|
+
SELECT COUNT(*) as count FROM vl_developer_profile WHERE mastery_score > 0.85
|
|
44
|
+
`).get()?.count??0;console.log(`
|
|
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
|
+
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(`
|
|
48
|
+
SELECT concept_name, category, mastery_score, encounter_count as times_seen
|
|
49
|
+
FROM vl_developer_profile
|
|
50
|
+
WHERE mastery_score < 0.5
|
|
51
|
+
ORDER BY mastery_score ASC, times_seen DESC
|
|
52
|
+
LIMIT 20
|
|
53
|
+
`).all();if(n.length===0){console.log(`
|
|
54
|
+
No knowledge gaps found. Keep coding and learning!
|
|
55
|
+
`),e.close();return}console.log(`
|
|
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(`
|
|
58
|
+
SELECT session_id FROM vibelearn_session_summaries
|
|
59
|
+
ORDER BY generated_at DESC LIMIT 1
|
|
60
|
+
`).get();if(!o){console.log(`
|
|
61
|
+
No sessions found. Run a coding session first!
|
|
62
|
+
`),n.close();return}s=o.session_id}let r=U(n,t,s,20);if(n.close(),r.length===0){console.log(`
|
|
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(`
|
|
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
|
+
`);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
|
+
${C} (${o.difficulty})${W}`),o.snippet&&o.snippet.trim()&&(console.log(`
|
|
68
|
+
Code:
|
|
69
|
+
`),o.snippet.split(`
|
|
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(`
|
|
72
|
+
\u270E Open-ended noted.
|
|
73
|
+
`):d?(console.log(`
|
|
74
|
+
\u2713 Correct!
|
|
75
|
+
`),u++):console.log(`
|
|
76
|
+
\u2717 Incorrect. Correct answer: ${o.correct??"(see explanation)"}
|
|
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(`
|
|
79
|
+
INSERT INTO vl_quiz_attempts (id, question_id, answer_given, is_correct, time_taken_ms, created_at)
|
|
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(`
|
|
87
|
+
Logged in: API key ${r}
|
|
88
|
+
`)}else console.log(`
|
|
89
|
+
Not logged in. Run: vl login <api-key>
|
|
90
|
+
`);return}e||(console.error(`
|
|
91
|
+
Usage: vl login <api-key>
|
|
92
|
+
`),process.exit(1));let t=H();t.api_key=e,re(t),console.log(`
|
|
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(`
|
|
95
|
+
VibeLearn CLI \u2014 learn from your coding sessions
|
|
96
|
+
|
|
97
|
+
Usage:
|
|
98
|
+
vl quiz Interactive quiz (all pending questions)
|
|
99
|
+
vl quiz --session Quiz only the last session's questions
|
|
100
|
+
vl status Sessions analyzed, concepts by category
|
|
101
|
+
vl gaps Concepts you haven't mastered yet
|
|
102
|
+
vl login <api-key> Connect to vibelearn.dev
|
|
103
|
+
vl login --status Check login status
|
|
104
|
+
`)}}ae().catch(e=>{console.error("Error:",e.message),process.exit(1)});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import{existsSync as w,readFileSync as rt,writeFileSync as nt,unlinkSync as st,mkdirSync as $}from"fs";import{createWriteStream as ot}from"fs";import{join as S}from"path";import{spawn as it,spawnSync as at}from"child_process";import{homedir as ct}from"os";import{join as E,dirname as q,basename as Lt}from"path";import{homedir as z}from"os";import{fileURLToPath as Q}from"url";import{readFileSync as V,writeFileSync as j,existsSync as X}from"fs";import{join as Y}from"path";import{homedir as J}from"os";var b="bugfix,feature,refactor,discovery,decision,change",k="how-it-works,why-it-exists,what-changed,problem-solution,gotcha,pattern,trade-off";var D=(s=>(s[s.DEBUG=0]="DEBUG",s[s.INFO=1]="INFO",s[s.WARN=2]="WARN",s[s.ERROR=3]="ERROR",s[s.SILENT=4]="SILENT",s))(D||{}),C=class{level=null;useColor;constructor(){this.useColor=process.stdout.isTTY??!1}getLevel(){if(this.level===null){let t=l.get("VIBELEARN_LOG_LEVEL").toUpperCase();this.level=D[t]??1}return this.level}correlationId(t,e){return`obs-${t}-${e}`}sessionId(t){return`session-${t}`}formatData(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return t.toString();if(typeof t=="object"){if(t instanceof Error)return this.getLevel()===0?`${t.message}
|
|
3
|
+
${t.stack}`:t.message;if(Array.isArray(t))return`[${t.length} items]`;let e=Object.keys(t);return e.length===0?"{}":e.length<=3?JSON.stringify(t):`{${e.length} keys: ${e.slice(0,3).join(", ")}...}`}return String(t)}formatTool(t,e){if(!e)return t;let r=typeof e=="string"?JSON.parse(e):e;if(t==="Bash"&&r.command)return`${t}(${r.command})`;if(r.file_path)return`${t}(${r.file_path})`;if(r.notebook_path)return`${t}(${r.notebook_path})`;if(t==="Glob"&&r.pattern)return`${t}(${r.pattern})`;if(t==="Grep"&&r.pattern)return`${t}(${r.pattern})`;if(r.url)return`${t}(${r.url})`;if(r.query)return`${t}(${r.query})`;if(t==="Task"){if(r.subagent_type)return`${t}(${r.subagent_type})`;if(r.description)return`${t}(${r.description})`}return t==="Skill"&&r.skill?`${t}(${r.skill})`:t==="LSP"&&r.operation?`${t}(${r.operation})`:t}formatTimestamp(t){let e=t.getFullYear(),r=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0"),s=String(t.getHours()).padStart(2,"0"),o=String(t.getMinutes()).padStart(2,"0"),a=String(t.getSeconds()).padStart(2,"0"),c=String(t.getMilliseconds()).padStart(3,"0");return`${e}-${r}-${n} ${s}:${o}:${a}.${c}`}log(t,e,r,n,s){if(t<this.getLevel())return;let o=this.formatTimestamp(new Date),a=D[t].padEnd(5),c=e.padEnd(6),p="";n?.correlationId?p=`[${n.correlationId}] `:n?.sessionId&&(p=`[session-${n.sessionId}] `);let _="";s!=null&&(this.getLevel()===0&&typeof s=="object"?_=`
|
|
4
|
+
`+JSON.stringify(s,null,2):_=" "+this.formatData(s));let f="";if(n){let{sessionId:h,sdkSessionId:gt,correlationId:_t,...U}=n;Object.keys(U).length>0&&(f=` {${Object.entries(U).map(([K,B])=>`${K}=${B}`).join(", ")}}`)}let m=`[${o}] [${a}] [${c}] ${p}${r}${f}${_}`;t===3?console.error(m):console.log(m)}debug(t,e,r,n){this.log(0,t,e,r,n)}info(t,e,r,n){this.log(1,t,e,r,n)}warn(t,e,r,n){this.log(2,t,e,r,n)}error(t,e,r,n){this.log(3,t,e,r,n)}dataIn(t,e,r,n){this.info(t,`\u2192 ${e}`,r,n)}dataOut(t,e,r,n){this.info(t,`\u2190 ${e}`,r,n)}success(t,e,r,n){this.info(t,`\u2713 ${e}`,r,n)}failure(t,e,r,n){this.error(t,`\u2717 ${e}`,r,n)}timing(t,e,r,n){this.info(t,`\u23F1 ${e}`,n,{duration:`${r}ms`})}happyPathError(t,e,r,n,s=""){let p=((new Error().stack||"").split(`
|
|
5
|
+
`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),_=p?`${p[1].split("/").pop()}:${p[2]}`:"unknown",f={...r,location:_};return this.warn(t,`[HAPPY-PATH] ${e}`,f,n),s}},T=new C;var l=class{static DEFAULTS={VIBELEARN_MODEL:"claude-sonnet-4-5",VIBELEARN_CONTEXT_OBSERVATIONS:"50",VIBELEARN_WORKER_PORT:"37777",VIBELEARN_WORKER_HOST:"127.0.0.1",VIBELEARN_SKIP_TOOLS:"ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion",VIBELEARN_PROVIDER:"claude",VIBELEARN_GEMINI_API_KEY:"",VIBELEARN_GEMINI_MODEL:"gemini-2.5-flash-lite",VIBELEARN_GEMINI_RATE_LIMITING_ENABLED:"true",VIBELEARN_OPENROUTER_API_KEY:"",VIBELEARN_OPENROUTER_MODEL:"anthropic/claude-3.5-sonnet",VIBELEARN_OPENROUTER_SITE_URL:"",VIBELEARN_OPENROUTER_APP_NAME:"vibelearn",VIBELEARN_OPENROUTER_MAX_CONTEXT_MESSAGES:"20",VIBELEARN_OPENROUTER_MAX_TOKENS:"100000",VIBELEARN_DATA_DIR:Y(J(),".vibelearn"),VIBELEARN_LOG_LEVEL:"INFO",VIBELEARN_PYTHON_VERSION:"3.13",CLAUDE_CODE_PATH:"",VIBELEARN_MODE:"code",VIBELEARN_CONTEXT_SHOW_READ_TOKENS:"true",VIBELEARN_CONTEXT_SHOW_WORK_TOKENS:"true",VIBELEARN_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",VIBELEARN_CONTEXT_SHOW_SAVINGS_PERCENT:"true",VIBELEARN_CONTEXT_OBSERVATION_TYPES:b,VIBELEARN_CONTEXT_OBSERVATION_CONCEPTS:k,VIBELEARN_CONTEXT_FULL_COUNT:"5",VIBELEARN_CONTEXT_FULL_FIELD:"narrative",VIBELEARN_CONTEXT_SESSION_COUNT:"10",VIBELEARN_CONTEXT_SHOW_LAST_SUMMARY:"true",VIBELEARN_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return this.DEFAULTS[t]}static getInt(t){let e=this.get(t);return parseInt(e,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){try{if(!X(t))return this.getAllDefaults();let e=V(t,"utf-8"),r=JSON.parse(e),n=r;if(r.env&&typeof r.env=="object"){n=r.env;try{j(t,JSON.stringify(n,null,2),"utf-8"),T.info("SETTINGS","Migrated settings file from nested to flat schema",{settingsPath:t})}catch(o){T.warn("SETTINGS","Failed to auto-migrate settings file",{settingsPath:t},o)}}let s={...this.DEFAULTS};for(let o of Object.keys(this.DEFAULTS))n[o]!==void 0&&(s[o]=n[o]);return s}catch(e){return T.warn("SETTINGS","Failed to load settings, using defaults",{settingsPath:t},e),this.getAllDefaults()}}};function Z(){return typeof __dirname<"u"?__dirname:q(Q(import.meta.url))}var Ut=Z(),u=l.get("VIBELEARN_DATA_DIR"),L=process.env.CLAUDE_CONFIG_DIR||E(z(),".claude"),bt=E(u,"archives"),kt=E(u,"logs"),yt=E(u,"trash"),vt=E(u,"backups"),Nt=E(u,"modes"),$t=E(u,"settings.json"),xt=E(u,"vibelearn.db"),Wt=E(u,"vector-db"),Ft=E(L,"settings.json"),Ht=E(L,"commands"),Gt=E(L,"CLAUDE.md");import{spawnSync as tt}from"child_process";import{existsSync as et}from"fs";import{join as y}from"path";import{homedir as v}from"os";function R(){let i=process.platform==="win32";try{if(tt("bun",["--version"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:!1}).status===0)return"bun"}catch{}let t=i?[y(v(),".bun","bin","bun.exe")]:[y(v(),".bun","bin","bun"),"/usr/local/bin/bun","/opt/homebrew/bin/bun","/home/linuxbrew/.linuxbrew/bin/bun"];for(let e of t)if(et(e))return e;return null}function N(){return R()!==null}var d=S(u,"worker.pid"),x=S(u,"logs"),P=S(ct(),".claude","plugins","marketplaces","anergcorp"),g=class{static async start(t){if(isNaN(t)||t<1024||t>65535)return{success:!1,error:`Invalid port ${t}. Must be between 1024 and 65535`};if(await this.isRunning())return{success:!0,pid:this.getPidInfo()?.pid};$(x,{recursive:!0});let e=process.platform==="win32"?"worker-wrapper.cjs":"worker-service.cjs",r=S(P,"plugin","scripts",e);if(!w(r))return{success:!1,error:`Worker script not found at ${r}`};let n=this.getLogFilePath();return this.startWithBun(r,n,t)}static isBunAvailable(){return N()}static escapePowerShellString(t){return t.replace(/'/g,"''")}static async startWithBun(t,e,r){let n=R();if(!n)return{success:!1,error:"Bun is required but not found in PATH or common installation paths. Install from https://bun.sh"};try{if(process.platform==="win32"){let o=this.escapePowerShellString(n),a=this.escapePowerShellString(t),c=this.escapePowerShellString(P),p=this.escapePowerShellString(e),f=`${`$env:VIBELEARN_WORKER_PORT='${r}'`}; Start-Process -FilePath '${o}' -ArgumentList '${a}' -WorkingDirectory '${c}' -WindowStyle Hidden -RedirectStandardOutput '${p}' -RedirectStandardError '${p}.err' -PassThru | Select-Object -ExpandProperty Id`,m=at("powershell",["-Command",f],{stdio:"pipe",timeout:1e4,windowsHide:!0});if(m.status!==0)return{success:!1,error:`PowerShell spawn failed: ${m.stderr?.toString()||"unknown error"}`};let h=parseInt(m.stdout.toString().trim(),10);return isNaN(h)?{success:!1,error:"Failed to get PID from PowerShell"}:(this.writePidFile({pid:h,port:r,startedAt:new Date().toISOString(),version:process.env.npm_package_version||"unknown"}),this.waitForHealth(h,r))}else{let o=it(n,[t],{detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,VIBELEARN_WORKER_PORT:String(r)},cwd:P}),a=ot(e,{flags:"a"});return o.stdout?.pipe(a),o.stderr?.pipe(a),o.unref(),o.pid?(this.writePidFile({pid:o.pid,port:r,startedAt:new Date().toISOString(),version:process.env.npm_package_version||"unknown"}),this.waitForHealth(o.pid,r)):{success:!1,error:"Failed to get PID from spawned process"}}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}static async stop(t=5e3){let e=this.getPidInfo();if(process.platform==="win32"){let r=e?.port??this.getPortFromSettings();if(await this.tryHttpShutdown(r))return this.removePidFile(),!0;if(!e)return!0;let{execSync:s}=await import("child_process");try{s(`taskkill /PID ${e.pid} /T /F`,{timeout:1e4,stdio:"ignore"})}catch{}try{await this.waitForExit(e.pid,t)}catch{}return this.isProcessAlive(e.pid)||this.removePidFile(),!0}else{if(!e)return!0;try{process.kill(e.pid,"SIGTERM"),await this.waitForExit(e.pid,t)}catch{try{process.kill(e.pid,"SIGKILL")}catch{}}return this.removePidFile(),!0}}static async restart(t){return await this.stop(),this.start(t)}static async status(){let t=this.getPidInfo();if(!t)return{running:!1};let e=this.isProcessAlive(t.pid);return{running:e,pid:e?t.pid:void 0,port:e?t.port:void 0,uptime:e?this.formatUptime(t.startedAt):void 0}}static async isRunning(){let t=this.getPidInfo();if(!t)return!1;let e=this.isProcessAlive(t.pid);return e||this.removePidFile(),e}static getPortFromSettings(){try{let t=S(u,"settings.json"),e=l.loadFromFile(t);return parseInt(e.VIBELEARN_WORKER_PORT,10)}catch{return parseInt(l.get("VIBELEARN_WORKER_PORT"),10)}}static async tryHttpShutdown(t){try{return(await fetch(`http://127.0.0.1:${t}/api/admin/shutdown`,{method:"POST",signal:AbortSignal.timeout(2e3)})).ok?await this.waitForWorkerDown(t,5e3):!1}catch{return!1}}static async waitForWorkerDown(t,e){let r=Date.now();for(;Date.now()-r<e;)try{await fetch(`http://127.0.0.1:${t}/api/health`,{signal:AbortSignal.timeout(500)}),await new Promise(n=>setTimeout(n,100))}catch{return!0}return!1}static getPidInfo(){try{if(!w(d))return null;let t=rt(d,"utf-8"),e=JSON.parse(t);return typeof e.pid!="number"||typeof e.port!="number"?(logger.warn("PROCESS","Malformed PID file: missing or invalid pid/port fields",{},{parsed:e}),null):e}catch(t){return logger.warn("PROCESS","Failed to read PID file",{},{error:t instanceof Error?t.message:String(t),path:d}),null}}static writePidFile(t){$(u,{recursive:!0}),nt(d,JSON.stringify(t,null,2))}static removePidFile(){try{w(d)&&st(d)}catch{}}static isProcessAlive(t){try{return process.kill(t,0),!0}catch{return!1}}static async waitForHealth(t,e,r=1e4){let n=Date.now(),s=process.platform==="win32",o=s?r*2:r;for(;Date.now()-n<o;){if(!this.isProcessAlive(t))return{success:!1,error:s?`Process died during startup
|
|
6
|
+
|
|
7
|
+
Troubleshooting:
|
|
8
|
+
1. Check Task Manager for zombie 'bun.exe' or 'node.exe' processes
|
|
9
|
+
2. Verify port ${e} is not in use: netstat -ano | findstr ${e}
|
|
10
|
+
3. Check worker logs in ~/.vibelearn/logs/
|
|
11
|
+
4. See GitHub issues: #363, #367, #371, #373
|
|
12
|
+
5. Docs: https://docs.vibelearn.dev/troubleshooting/windows-issues`:"Process died during startup"};try{if((await fetch(`http://127.0.0.1:${e}/api/readiness`,{signal:AbortSignal.timeout(1e3)})).ok)return{success:!0,pid:t}}catch{}await new Promise(c=>setTimeout(c,200))}return{success:!1,error:s?`Worker failed to start on Windows (readiness check timed out after ${o}ms)
|
|
13
|
+
|
|
14
|
+
Troubleshooting:
|
|
15
|
+
1. Check Task Manager for zombie 'bun.exe' or 'node.exe' processes
|
|
16
|
+
2. Verify port ${e} is not in use: netstat -ano | findstr ${e}
|
|
17
|
+
3. Check worker logs in ~/.vibelearn/logs/
|
|
18
|
+
4. See GitHub issues: #363, #367, #371, #373
|
|
19
|
+
5. Docs: https://docs.vibelearn.dev/troubleshooting/windows-issues`:`Readiness check timed out after ${o}ms`}}static async waitForExit(t,e){let r=Date.now();for(;Date.now()-r<e;){if(!this.isProcessAlive(t))return;await new Promise(n=>setTimeout(n,100))}throw new Error("Process did not exit within timeout")}static getLogFilePath(){let t=new Date().toISOString().slice(0,10);return S(x,`worker-${t}.log`)}static formatUptime(t){let e=new Date(t).getTime(),n=Date.now()-e,s=Math.floor(n/1e3),o=Math.floor(s/60),a=Math.floor(o/60),c=Math.floor(a/24);return c>0?`${c}d ${a%24}h`:a>0?`${a}h ${o%60}m`:o>0?`${o}m ${s%60}s`:`${s}s`}};import F from"path";import{homedir as ut}from"os";var I={DEFAULT:12e4,HEALTH_CHECK:1e3,WORKER_STARTUP_WAIT:1e3,WORKER_STARTUP_RETRIES:15,PRE_RESTART_SETTLE_DELAY:2e3,WINDOWS_MULTIPLIER:1.5};function W(i){return process.platform==="win32"?Math.round(i*I.WINDOWS_MULTIPLIER):i}var ge=F.join(ut(),".claude","plugins","marketplaces","anergcorp"),_e=W(I.HEALTH_CHECK),M=null;function H(){if(M!==null)return M;let i=F.join(l.get("VIBELEARN_DATA_DIR"),"settings.json"),t=l.loadFromFile(i);return M=parseInt(t.VIBELEARN_WORKER_PORT,10),M}import{stdin as lt}from"process";var Et=process.argv[2],G=H(),O='{"continue": true, "suppressOutput": true}',A=lt.isTTY;async function pt(){switch(Et){case"start":{let i=await g.start(G);if(i.success){if(A){console.log(`Worker started (PID: ${i.pid})`);let t=new Date().toISOString().slice(0,10);console.log(`Logs: ~/.vibelearn/logs/worker-${t}.log`)}else console.log(O);process.exit(0)}else console.error(`Failed to start: ${i.error}`),process.exit(1)}case"stop":await g.stop(),console.log(A?"Worker stopped":O),process.exit(0);case"restart":{let i=await g.restart(G);i.success?(console.log(A?`Worker restarted (PID: ${i.pid})`:O),process.exit(0)):(console.error(`Failed to restart: ${i.error}`),process.exit(1))}case"status":{let i=await g.status();A?i.running?(console.log("Worker is running"),console.log(` PID: ${i.pid}`),console.log(` Port: ${i.port}`),console.log(` Uptime: ${i.uptime}`)):console.log("Worker is not running"):console.log(O),process.exit(0)}default:console.log("Usage: worker-cli.js <start|stop|restart|status>"),process.exit(1)}}pt().catch(i=>{console.error(i),process.exit(1)});
|