learngraph 0.4.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/dist/cjs/api/routes/analytics.js +288 -0
  2. package/dist/cjs/api/routes/analytics.js.map +1 -0
  3. package/dist/cjs/api/routes/assessments.js +269 -0
  4. package/dist/cjs/api/routes/assessments.js.map +1 -0
  5. package/dist/cjs/api/routes/curriculum.js +345 -0
  6. package/dist/cjs/api/routes/curriculum.js.map +1 -0
  7. package/dist/cjs/api/routes/edges.js +162 -0
  8. package/dist/cjs/api/routes/edges.js.map +1 -0
  9. package/dist/cjs/api/routes/explore.js +224 -0
  10. package/dist/cjs/api/routes/explore.js.map +1 -0
  11. package/dist/cjs/api/routes/learners.js +324 -0
  12. package/dist/cjs/api/routes/learners.js.map +1 -0
  13. package/dist/cjs/api/routes/me.js +404 -0
  14. package/dist/cjs/api/routes/me.js.map +1 -0
  15. package/dist/cjs/api/routes/skills.js +319 -0
  16. package/dist/cjs/api/routes/skills.js.map +1 -0
  17. package/dist/cjs/api/server.js +185 -0
  18. package/dist/cjs/api/server.js.map +1 -0
  19. package/dist/cjs/api/types.js +10 -0
  20. package/dist/cjs/api/types.js.map +1 -0
  21. package/dist/cjs/assessment/adaptive.js +390 -0
  22. package/dist/cjs/assessment/adaptive.js.map +1 -0
  23. package/dist/cjs/assessment/bkt.js +362 -0
  24. package/dist/cjs/assessment/bkt.js.map +1 -0
  25. package/dist/cjs/assessment/index.js +54 -0
  26. package/dist/cjs/assessment/index.js.map +1 -0
  27. package/dist/cjs/assessment/irt.js +420 -0
  28. package/dist/cjs/assessment/irt.js.map +1 -0
  29. package/dist/cjs/assessment/mastery-engine.js +411 -0
  30. package/dist/cjs/assessment/mastery-engine.js.map +1 -0
  31. package/dist/cjs/components/LearningPathView.js +320 -0
  32. package/dist/cjs/components/LearningPathView.js.map +1 -0
  33. package/dist/cjs/components/ProgressDashboard.js +308 -0
  34. package/dist/cjs/components/ProgressDashboard.js.map +1 -0
  35. package/dist/cjs/components/SkillCard.js +264 -0
  36. package/dist/cjs/components/SkillCard.js.map +1 -0
  37. package/dist/cjs/components/SkillExplorer.js +401 -0
  38. package/dist/cjs/components/SkillExplorer.js.map +1 -0
  39. package/dist/cjs/components/SkillGraph.js +435 -0
  40. package/dist/cjs/components/SkillGraph.js.map +1 -0
  41. package/dist/cjs/components/hooks.js +510 -0
  42. package/dist/cjs/components/hooks.js.map +1 -0
  43. package/dist/cjs/components/index.js +77 -0
  44. package/dist/cjs/components/index.js.map +1 -0
  45. package/dist/cjs/components/types.js +34 -0
  46. package/dist/cjs/components/types.js.map +1 -0
  47. package/dist/cjs/index.js +36 -2
  48. package/dist/cjs/index.js.map +1 -1
  49. package/dist/cjs/llm/adapters/anthropic.js +91 -3
  50. package/dist/cjs/llm/adapters/anthropic.js.map +1 -1
  51. package/dist/cjs/llm/adapters/gemini.js +101 -8
  52. package/dist/cjs/llm/adapters/gemini.js.map +1 -1
  53. package/dist/cjs/llm/adapters/index.js +42 -1
  54. package/dist/cjs/llm/adapters/index.js.map +1 -1
  55. package/dist/cjs/llm/adapters/ollama.js +120 -3
  56. package/dist/cjs/llm/adapters/ollama.js.map +1 -1
  57. package/dist/cjs/llm/adapters/openai.js +108 -2
  58. package/dist/cjs/llm/adapters/openai.js.map +1 -1
  59. package/dist/cjs/mcp/cli.js +302 -0
  60. package/dist/cjs/mcp/cli.js.map +1 -0
  61. package/dist/cjs/mcp/index.js +79 -0
  62. package/dist/cjs/mcp/index.js.map +1 -0
  63. package/dist/cjs/mcp/prompts.js +425 -0
  64. package/dist/cjs/mcp/prompts.js.map +1 -0
  65. package/dist/cjs/mcp/resources.js +371 -0
  66. package/dist/cjs/mcp/resources.js.map +1 -0
  67. package/dist/cjs/mcp/server.js +410 -0
  68. package/dist/cjs/mcp/server.js.map +1 -0
  69. package/dist/cjs/mcp/tools.js +612 -0
  70. package/dist/cjs/mcp/tools.js.map +1 -0
  71. package/dist/cjs/mcp/types.js +10 -0
  72. package/dist/cjs/mcp/types.js.map +1 -0
  73. package/dist/cjs/query/index.js +23 -4
  74. package/dist/cjs/query/index.js.map +1 -1
  75. package/dist/cjs/query/path.js +313 -0
  76. package/dist/cjs/query/path.js.map +1 -0
  77. package/dist/cjs/query/spaced-repetition.js +298 -0
  78. package/dist/cjs/query/spaced-repetition.js.map +1 -0
  79. package/dist/cjs/query/zpd.js +216 -0
  80. package/dist/cjs/query/zpd.js.map +1 -0
  81. package/dist/cjs/types/assessment.js +46 -0
  82. package/dist/cjs/types/assessment.js.map +1 -0
  83. package/dist/cjs/types/bloom.js +12 -1
  84. package/dist/cjs/types/bloom.js.map +1 -1
  85. package/dist/cjs/types/index.js +7 -1
  86. package/dist/cjs/types/index.js.map +1 -1
  87. package/dist/esm/api/routes/analytics.js +285 -0
  88. package/dist/esm/api/routes/analytics.js.map +1 -0
  89. package/dist/esm/api/routes/assessments.js +266 -0
  90. package/dist/esm/api/routes/assessments.js.map +1 -0
  91. package/dist/esm/api/routes/curriculum.js +342 -0
  92. package/dist/esm/api/routes/curriculum.js.map +1 -0
  93. package/dist/esm/api/routes/edges.js +159 -0
  94. package/dist/esm/api/routes/edges.js.map +1 -0
  95. package/dist/esm/api/routes/explore.js +221 -0
  96. package/dist/esm/api/routes/explore.js.map +1 -0
  97. package/dist/esm/api/routes/learners.js +321 -0
  98. package/dist/esm/api/routes/learners.js.map +1 -0
  99. package/dist/esm/api/routes/me.js +401 -0
  100. package/dist/esm/api/routes/me.js.map +1 -0
  101. package/dist/esm/api/routes/skills.js +316 -0
  102. package/dist/esm/api/routes/skills.js.map +1 -0
  103. package/dist/esm/api/server.js +179 -0
  104. package/dist/esm/api/server.js.map +1 -0
  105. package/dist/esm/api/types.js +9 -0
  106. package/dist/esm/api/types.js.map +1 -0
  107. package/dist/esm/assessment/adaptive.js +384 -0
  108. package/dist/esm/assessment/adaptive.js.map +1 -0
  109. package/dist/esm/assessment/bkt.js +354 -0
  110. package/dist/esm/assessment/bkt.js.map +1 -0
  111. package/dist/esm/assessment/index.js +21 -0
  112. package/dist/esm/assessment/index.js.map +1 -0
  113. package/dist/esm/assessment/irt.js +406 -0
  114. package/dist/esm/assessment/irt.js.map +1 -0
  115. package/dist/esm/assessment/mastery-engine.js +406 -0
  116. package/dist/esm/assessment/mastery-engine.js.map +1 -0
  117. package/dist/esm/components/LearningPathView.js +316 -0
  118. package/dist/esm/components/LearningPathView.js.map +1 -0
  119. package/dist/esm/components/ProgressDashboard.js +304 -0
  120. package/dist/esm/components/ProgressDashboard.js.map +1 -0
  121. package/dist/esm/components/SkillCard.js +260 -0
  122. package/dist/esm/components/SkillCard.js.map +1 -0
  123. package/dist/esm/components/SkillExplorer.js +397 -0
  124. package/dist/esm/components/SkillExplorer.js.map +1 -0
  125. package/dist/esm/components/SkillGraph.js +398 -0
  126. package/dist/esm/components/SkillGraph.js.map +1 -0
  127. package/dist/esm/components/hooks.js +502 -0
  128. package/dist/esm/components/hooks.js.map +1 -0
  129. package/dist/esm/components/index.js +61 -0
  130. package/dist/esm/components/index.js.map +1 -0
  131. package/dist/esm/components/types.js +31 -0
  132. package/dist/esm/components/types.js.map +1 -0
  133. package/dist/esm/index.js +13 -1
  134. package/dist/esm/index.js.map +1 -1
  135. package/dist/esm/llm/adapters/anthropic.js +88 -2
  136. package/dist/esm/llm/adapters/anthropic.js.map +1 -1
  137. package/dist/esm/llm/adapters/gemini.js +98 -7
  138. package/dist/esm/llm/adapters/gemini.js.map +1 -1
  139. package/dist/esm/llm/adapters/index.js +15 -4
  140. package/dist/esm/llm/adapters/index.js.map +1 -1
  141. package/dist/esm/llm/adapters/ollama.js +117 -2
  142. package/dist/esm/llm/adapters/ollama.js.map +1 -1
  143. package/dist/esm/llm/adapters/openai.js +105 -1
  144. package/dist/esm/llm/adapters/openai.js.map +1 -1
  145. package/dist/esm/mcp/cli.js +267 -0
  146. package/dist/esm/mcp/cli.js.map +1 -0
  147. package/dist/esm/mcp/index.js +39 -0
  148. package/dist/esm/mcp/index.js.map +1 -0
  149. package/dist/esm/mcp/prompts.js +419 -0
  150. package/dist/esm/mcp/prompts.js.map +1 -0
  151. package/dist/esm/mcp/resources.js +359 -0
  152. package/dist/esm/mcp/resources.js.map +1 -0
  153. package/dist/esm/mcp/server.js +372 -0
  154. package/dist/esm/mcp/server.js.map +1 -0
  155. package/dist/esm/mcp/tools.js +598 -0
  156. package/dist/esm/mcp/tools.js.map +1 -0
  157. package/dist/esm/mcp/types.js +9 -0
  158. package/dist/esm/mcp/types.js.map +1 -0
  159. package/dist/esm/query/index.js +11 -5
  160. package/dist/esm/query/index.js.map +1 -1
  161. package/dist/esm/query/path.js +308 -0
  162. package/dist/esm/query/path.js.map +1 -0
  163. package/dist/esm/query/spaced-repetition.js +292 -0
  164. package/dist/esm/query/spaced-repetition.js.map +1 -0
  165. package/dist/esm/query/zpd.js +211 -0
  166. package/dist/esm/query/zpd.js.map +1 -0
  167. package/dist/esm/types/assessment.js +40 -0
  168. package/dist/esm/types/assessment.js.map +1 -0
  169. package/dist/esm/types/bloom.js +11 -0
  170. package/dist/esm/types/bloom.js.map +1 -1
  171. package/dist/esm/types/index.js +2 -1
  172. package/dist/esm/types/index.js.map +1 -1
  173. package/dist/types/api/routes/analytics.d.ts +14 -0
  174. package/dist/types/api/routes/analytics.d.ts.map +1 -0
  175. package/dist/types/api/routes/assessments.d.ts +14 -0
  176. package/dist/types/api/routes/assessments.d.ts.map +1 -0
  177. package/dist/types/api/routes/curriculum.d.ts +14 -0
  178. package/dist/types/api/routes/curriculum.d.ts.map +1 -0
  179. package/dist/types/api/routes/edges.d.ts +14 -0
  180. package/dist/types/api/routes/edges.d.ts.map +1 -0
  181. package/dist/types/api/routes/explore.d.ts +14 -0
  182. package/dist/types/api/routes/explore.d.ts.map +1 -0
  183. package/dist/types/api/routes/learners.d.ts +14 -0
  184. package/dist/types/api/routes/learners.d.ts.map +1 -0
  185. package/dist/types/api/routes/me.d.ts +14 -0
  186. package/dist/types/api/routes/me.d.ts.map +1 -0
  187. package/dist/types/api/routes/skills.d.ts +14 -0
  188. package/dist/types/api/routes/skills.d.ts.map +1 -0
  189. package/dist/types/api/server.d.ts +147 -0
  190. package/dist/types/api/server.d.ts.map +1 -0
  191. package/dist/types/api/types.d.ts +443 -0
  192. package/dist/types/api/types.d.ts.map +1 -0
  193. package/dist/types/assessment/adaptive.d.ts +155 -0
  194. package/dist/types/assessment/adaptive.d.ts.map +1 -0
  195. package/dist/types/assessment/bkt.d.ts +185 -0
  196. package/dist/types/assessment/bkt.d.ts.map +1 -0
  197. package/dist/types/assessment/index.d.ts +18 -0
  198. package/dist/types/assessment/index.d.ts.map +1 -0
  199. package/dist/types/assessment/irt.d.ts +159 -0
  200. package/dist/types/assessment/irt.d.ts.map +1 -0
  201. package/dist/types/assessment/mastery-engine.d.ts +178 -0
  202. package/dist/types/assessment/mastery-engine.d.ts.map +1 -0
  203. package/dist/types/components/LearningPathView.d.ts +40 -0
  204. package/dist/types/components/LearningPathView.d.ts.map +1 -0
  205. package/dist/types/components/ProgressDashboard.d.ts +49 -0
  206. package/dist/types/components/ProgressDashboard.d.ts.map +1 -0
  207. package/dist/types/components/SkillCard.d.ts +34 -0
  208. package/dist/types/components/SkillCard.d.ts.map +1 -0
  209. package/dist/types/components/SkillExplorer.d.ts +39 -0
  210. package/dist/types/components/SkillExplorer.d.ts.map +1 -0
  211. package/dist/types/components/SkillGraph.d.ts +38 -0
  212. package/dist/types/components/SkillGraph.d.ts.map +1 -0
  213. package/dist/types/components/hooks.d.ts +187 -0
  214. package/dist/types/components/hooks.d.ts.map +1 -0
  215. package/dist/types/components/index.d.ts +59 -0
  216. package/dist/types/components/index.d.ts.map +1 -0
  217. package/dist/types/components/types.d.ts +410 -0
  218. package/dist/types/components/types.d.ts.map +1 -0
  219. package/dist/types/index.d.ts +2 -1
  220. package/dist/types/index.d.ts.map +1 -1
  221. package/dist/types/llm/adapters/anthropic.d.ts +84 -1
  222. package/dist/types/llm/adapters/anthropic.d.ts.map +1 -1
  223. package/dist/types/llm/adapters/gemini.d.ts +93 -6
  224. package/dist/types/llm/adapters/gemini.d.ts.map +1 -1
  225. package/dist/types/llm/adapters/index.d.ts +13 -4
  226. package/dist/types/llm/adapters/index.d.ts.map +1 -1
  227. package/dist/types/llm/adapters/ollama.d.ts +126 -1
  228. package/dist/types/llm/adapters/ollama.d.ts.map +1 -1
  229. package/dist/types/llm/adapters/openai.d.ts +104 -1
  230. package/dist/types/llm/adapters/openai.d.ts.map +1 -1
  231. package/dist/types/mcp/cli.d.ts +15 -0
  232. package/dist/types/mcp/cli.d.ts.map +1 -0
  233. package/dist/types/mcp/index.d.ts +32 -0
  234. package/dist/types/mcp/index.d.ts.map +1 -0
  235. package/dist/types/mcp/prompts.d.ts +27 -0
  236. package/dist/types/mcp/prompts.d.ts.map +1 -0
  237. package/dist/types/mcp/resources.d.ts +59 -0
  238. package/dist/types/mcp/resources.d.ts.map +1 -0
  239. package/dist/types/mcp/server.d.ts +136 -0
  240. package/dist/types/mcp/server.d.ts.map +1 -0
  241. package/dist/types/mcp/tools.d.ts +344 -0
  242. package/dist/types/mcp/tools.d.ts.map +1 -0
  243. package/dist/types/mcp/types.d.ts +137 -0
  244. package/dist/types/mcp/types.d.ts.map +1 -0
  245. package/dist/types/query/index.d.ts +8 -0
  246. package/dist/types/query/index.d.ts.map +1 -1
  247. package/dist/types/query/path.d.ts +102 -0
  248. package/dist/types/query/path.d.ts.map +1 -0
  249. package/dist/types/query/spaced-repetition.d.ts +135 -0
  250. package/dist/types/query/spaced-repetition.d.ts.map +1 -0
  251. package/dist/types/query/zpd.d.ts +97 -0
  252. package/dist/types/query/zpd.d.ts.map +1 -0
  253. package/dist/types/types/assessment.d.ts +512 -0
  254. package/dist/types/types/assessment.d.ts.map +1 -0
  255. package/dist/types/types/bloom.d.ts +4 -0
  256. package/dist/types/types/bloom.d.ts.map +1 -1
  257. package/dist/types/types/index.d.ts +3 -1
  258. package/dist/types/types/index.d.ts.map +1 -1
  259. package/package.json +48 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;;AAmBH,+CAA+C;AAC/C,4CAA4C;AAC5C,6CAA6C;AAC7C,oDAAoD"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmBH,iBAAiB;AACjB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,YAAY,GAEb,MAAM,UAAU,CAAC;AAElB,0BAA0B;AAC1B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,aAAa,GAEd,MAAM,WAAW,CAAC;AAEnB,8BAA8B;AAC9B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,YAAY,EACZ,eAAe,EACf,aAAa,GAId,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Learning Path Generator
3
+ *
4
+ * Generates personalized learning paths from current state to target skills.
5
+ * Uses topological sorting and groups skills into manageable sessions.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Default options for path generation
11
+ */
12
+ export const PATH_DEFAULTS = {
13
+ sessionMinutes: 30,
14
+ maxPathLength: 100,
15
+ includeReview: false,
16
+ };
17
+ /**
18
+ * Learning Path Generator
19
+ *
20
+ * Generates optimal learning paths to reach target skills by:
21
+ * 1. Finding all unmastered prerequisites
22
+ * 2. Topologically sorting based on dependencies
23
+ * 3. Grouping into sessions based on cognitive load
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const generator = new PathGenerator(storage);
28
+ *
29
+ * const path = await generator.generatePath(
30
+ * 'advanced-skill',
31
+ * learnerState,
32
+ * { sessionMinutes: 45 }
33
+ * );
34
+ *
35
+ * console.log(`Path has ${path.skills.length} skills`);
36
+ * console.log(`Estimated time: ${path.totalMinutes} minutes`);
37
+ * ```
38
+ */
39
+ export class PathGenerator {
40
+ storage;
41
+ constructor(storage) {
42
+ this.storage = storage;
43
+ }
44
+ /**
45
+ * Generate a learning path to a target skill
46
+ *
47
+ * @param targetId - The skill to learn
48
+ * @param learner - Current learner state
49
+ * @param options - Path generation options
50
+ * @returns Learning path with skills grouped into sessions
51
+ */
52
+ async generatePath(targetId, learner, options = {}) {
53
+ const opts = { ...PATH_DEFAULTS, ...options };
54
+ const target = await this.storage.getSkill(targetId);
55
+ if (!target) {
56
+ return null;
57
+ }
58
+ // Check if already mastered
59
+ const targetMastery = learner.masteryStates.get(targetId);
60
+ if (targetMastery && targetMastery.mastery >= target.masteryThreshold) {
61
+ // Already mastered - return empty path
62
+ return {
63
+ target,
64
+ skills: [],
65
+ totalMinutes: 0,
66
+ sessions: [],
67
+ checkpoints: [],
68
+ };
69
+ }
70
+ // Get all prerequisites (transitive)
71
+ const allPrereqs = await this.getTransitivePrerequisites(targetId);
72
+ // Filter to unmastered skills
73
+ const skillsToLearn = await this.filterUnmastered([...allPrereqs, target], learner, opts.includeReview);
74
+ // Topologically sort
75
+ const sorted = await this.topologicalSort(skillsToLearn);
76
+ // Apply max length limit
77
+ const limited = opts.maxPathLength
78
+ ? sorted.slice(0, opts.maxPathLength)
79
+ : sorted;
80
+ // Calculate total time
81
+ const totalMinutes = limited.reduce((sum, skill) => sum + skill.estimatedMinutes, 0);
82
+ // Group into sessions
83
+ const sessions = this.groupIntoSessions(limited, opts.sessionMinutes);
84
+ // Identify checkpoints (threshold concepts)
85
+ const checkpoints = limited.filter((skill) => skill.isThresholdConcept);
86
+ return {
87
+ target,
88
+ skills: limited,
89
+ totalMinutes,
90
+ sessions,
91
+ checkpoints,
92
+ };
93
+ }
94
+ /**
95
+ * Generate paths to multiple target skills
96
+ *
97
+ * @param targetIds - Skills to learn
98
+ * @param learner - Current learner state
99
+ * @param options - Path generation options
100
+ * @returns Array of learning paths
101
+ */
102
+ async generatePaths(targetIds, learner, options = {}) {
103
+ const paths = [];
104
+ for (const targetId of targetIds) {
105
+ const path = await this.generatePath(targetId, learner, options);
106
+ if (path) {
107
+ paths.push(path);
108
+ }
109
+ }
110
+ return paths;
111
+ }
112
+ /**
113
+ * Get a merged learning path for multiple targets
114
+ * Combines all required skills and eliminates duplicates
115
+ */
116
+ async generateMergedPath(targetIds, learner, options = {}) {
117
+ const opts = { ...PATH_DEFAULTS, ...options };
118
+ // Get all target skills
119
+ const targets = [];
120
+ for (const id of targetIds) {
121
+ const skill = await this.storage.getSkill(id);
122
+ if (skill) {
123
+ targets.push(skill);
124
+ }
125
+ }
126
+ if (targets.length === 0) {
127
+ return null;
128
+ }
129
+ // Collect all unmastered prerequisites from all targets
130
+ const allSkillsSet = new Set();
131
+ const allSkills = [];
132
+ for (const target of targets) {
133
+ const prereqs = await this.getTransitivePrerequisites(target.id);
134
+ const allForTarget = [...prereqs, target];
135
+ for (const skill of allForTarget) {
136
+ if (!allSkillsSet.has(skill.id)) {
137
+ allSkillsSet.add(skill.id);
138
+ allSkills.push(skill);
139
+ }
140
+ }
141
+ }
142
+ // Filter to unmastered
143
+ const skillsToLearn = await this.filterUnmastered(allSkills, learner, opts.includeReview);
144
+ // Topologically sort
145
+ const sorted = await this.topologicalSort(skillsToLearn);
146
+ // Apply max length
147
+ const limited = opts.maxPathLength
148
+ ? sorted.slice(0, opts.maxPathLength)
149
+ : sorted;
150
+ const totalMinutes = limited.reduce((sum, skill) => sum + skill.estimatedMinutes, 0);
151
+ const sessions = this.groupIntoSessions(limited, opts.sessionMinutes);
152
+ const checkpoints = limited.filter((skill) => skill.isThresholdConcept);
153
+ // Use first target as the primary target
154
+ return {
155
+ target: targets[0],
156
+ skills: limited,
157
+ totalMinutes,
158
+ sessions,
159
+ checkpoints,
160
+ };
161
+ }
162
+ /**
163
+ * Get all transitive prerequisites of a skill
164
+ */
165
+ async getTransitivePrerequisites(skillId) {
166
+ const visited = new Set();
167
+ const result = [];
168
+ const visit = async (id) => {
169
+ if (visited.has(id)) {
170
+ return;
171
+ }
172
+ visited.add(id);
173
+ const prereqs = await this.storage.getPrerequisitesOf(id);
174
+ for (const prereq of prereqs) {
175
+ await visit(prereq.id);
176
+ if (!result.some((s) => s.id === prereq.id)) {
177
+ result.push(prereq);
178
+ }
179
+ }
180
+ };
181
+ await visit(skillId);
182
+ return result;
183
+ }
184
+ /**
185
+ * Filter skills to only unmastered ones
186
+ */
187
+ async filterUnmastered(skills, learner, includeReview) {
188
+ return skills.filter((skill) => {
189
+ const mastery = learner.masteryStates.get(skill.id);
190
+ const level = mastery?.mastery ?? 0;
191
+ if (includeReview) {
192
+ // Include if below 100% mastery
193
+ return level < 1.0;
194
+ }
195
+ // Include if below mastery threshold
196
+ return level < skill.masteryThreshold;
197
+ });
198
+ }
199
+ /**
200
+ * Topologically sort skills based on prerequisites
201
+ * Skills with no unmastered prerequisites come first
202
+ */
203
+ async topologicalSort(skills) {
204
+ const skillMap = new Map(skills.map((s) => [s.id, s]));
205
+ const result = [];
206
+ const visited = new Set();
207
+ const visiting = new Set(); // For cycle detection
208
+ const visit = async (skill) => {
209
+ if (visited.has(skill.id)) {
210
+ return;
211
+ }
212
+ if (visiting.has(skill.id)) {
213
+ // Cycle detected - skip to avoid infinite loop
214
+ return;
215
+ }
216
+ visiting.add(skill.id);
217
+ // Visit prerequisites first (only those in our skill set)
218
+ const prereqs = await this.storage.getPrerequisitesOf(skill.id);
219
+ for (const prereq of prereqs) {
220
+ const prereqSkill = skillMap.get(prereq.id);
221
+ if (prereqSkill && !visited.has(prereq.id)) {
222
+ await visit(prereqSkill);
223
+ }
224
+ }
225
+ visiting.delete(skill.id);
226
+ visited.add(skill.id);
227
+ result.push(skill);
228
+ };
229
+ // Visit all skills
230
+ for (const skill of skills) {
231
+ if (!visited.has(skill.id)) {
232
+ await visit(skill);
233
+ }
234
+ }
235
+ return result;
236
+ }
237
+ /**
238
+ * Group skills into learning sessions based on target duration
239
+ */
240
+ groupIntoSessions(skills, targetMinutes) {
241
+ const sessions = [];
242
+ let currentSession = [];
243
+ let currentDuration = 0;
244
+ for (const skill of skills) {
245
+ // Start new session if current would exceed target
246
+ if (currentDuration + skill.estimatedMinutes > targetMinutes &&
247
+ currentSession.length > 0) {
248
+ sessions.push(this.createSession(sessions.length + 1, currentSession));
249
+ currentSession = [];
250
+ currentDuration = 0;
251
+ }
252
+ currentSession.push(skill);
253
+ currentDuration += skill.estimatedMinutes;
254
+ }
255
+ // Add final session if not empty
256
+ if (currentSession.length > 0) {
257
+ sessions.push(this.createSession(sessions.length + 1, currentSession));
258
+ }
259
+ return sessions;
260
+ }
261
+ /**
262
+ * Create a learning session from a group of skills
263
+ */
264
+ createSession(sessionNumber, skills) {
265
+ const durationMinutes = skills.reduce((sum, s) => sum + s.estimatedMinutes, 0);
266
+ // Determine focus from most common tag or domain
267
+ const focus = this.determineFocus(skills);
268
+ // Build session object, only include focus if defined
269
+ const session = {
270
+ sessionNumber,
271
+ skills,
272
+ durationMinutes,
273
+ };
274
+ if (focus !== undefined) {
275
+ session.focus = focus;
276
+ }
277
+ return session;
278
+ }
279
+ /**
280
+ * Determine the focus/theme of a session
281
+ */
282
+ determineFocus(skills) {
283
+ // Count tag occurrences
284
+ const tagCounts = new Map();
285
+ for (const skill of skills) {
286
+ for (const tag of skill.tags) {
287
+ tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
288
+ }
289
+ }
290
+ // Find most common tag
291
+ let maxCount = 0;
292
+ let focus;
293
+ for (const [tag, count] of tagCounts) {
294
+ if (count > maxCount) {
295
+ maxCount = count;
296
+ focus = tag;
297
+ }
298
+ }
299
+ return focus;
300
+ }
301
+ }
302
+ /**
303
+ * Create a path generator
304
+ */
305
+ export function createPathGenerator(storage) {
306
+ return new PathGenerator(storage);
307
+ }
308
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/query/path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Spaced Repetition Scheduler (SM-2 Algorithm)
3
+ *
4
+ * Implements the SuperMemo SM-2 algorithm for optimal review scheduling.
5
+ * Schedules reviews at increasing intervals based on performance.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Default options for review scheduling
11
+ */
12
+ export const REVIEW_DEFAULTS = {
13
+ includeUpcoming: true,
14
+ upcomingDays: 7,
15
+ maxReviews: 50,
16
+ };
17
+ /**
18
+ * SM-2 algorithm constants
19
+ */
20
+ export const SM2_CONSTANTS = {
21
+ /** Minimum easiness factor */
22
+ MIN_EASINESS: 1.3,
23
+ /** Default easiness factor */
24
+ DEFAULT_EASINESS: 2.5,
25
+ /** Maximum easiness factor */
26
+ MAX_EASINESS: 3.5,
27
+ /** Initial interval (days) after first review */
28
+ INITIAL_INTERVAL: 1,
29
+ /** Second interval (days) after second review */
30
+ SECOND_INTERVAL: 6,
31
+ };
32
+ /**
33
+ * Spaced Repetition Scheduler
34
+ *
35
+ * Uses the SM-2 algorithm to schedule optimal review times.
36
+ * The algorithm adjusts intervals based on recall quality:
37
+ * - Quality 5: Perfect response
38
+ * - Quality 4: Correct with hesitation
39
+ * - Quality 3: Correct with difficulty
40
+ * - Quality 2: Incorrect but easily recalled
41
+ * - Quality 1: Incorrect, remembered when shown
42
+ * - Quality 0: Complete blackout
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const scheduler = new SpacedRepetitionScheduler(storage);
47
+ *
48
+ * // Get review schedule
49
+ * const schedule = await scheduler.getSchedule(learnerState);
50
+ * console.log(`${schedule.dueNow.length} reviews due now`);
51
+ *
52
+ * // Update after a review
53
+ * const result = scheduler.calculateNextReview(masteryState, 4); // quality 4
54
+ * console.log(`Next review in ${result.intervalDays} days`);
55
+ * ```
56
+ */
57
+ export class SpacedRepetitionScheduler {
58
+ storage;
59
+ constructor(storage) {
60
+ this.storage = storage;
61
+ }
62
+ /**
63
+ * Get the review schedule for a learner
64
+ *
65
+ * @param learner - Current learner state
66
+ * @param options - Schedule options
67
+ * @returns Review schedule with due and upcoming items
68
+ */
69
+ async getSchedule(learner, options = {}) {
70
+ const opts = { ...REVIEW_DEFAULTS, ...options };
71
+ const now = new Date();
72
+ const upcomingCutoff = new Date(now.getTime() + opts.upcomingDays * 24 * 60 * 60 * 1000);
73
+ // Get all skills that have been practiced
74
+ const allSkills = await this.storage.findSkills({});
75
+ const dueNow = [];
76
+ const upcoming = [];
77
+ for (const skill of allSkills) {
78
+ const mastery = learner.masteryStates.get(skill.id);
79
+ if (!mastery) {
80
+ continue; // Never practiced
81
+ }
82
+ // Only schedule reviews for skills with some mastery
83
+ if (mastery.mastery < 0.1) {
84
+ continue;
85
+ }
86
+ const nextReview = this.calculateNextReviewDate(mastery);
87
+ const priority = this.calculatePriority(mastery, nextReview, now);
88
+ const reviewItem = {
89
+ skill,
90
+ state: {
91
+ mastery: mastery.mastery,
92
+ lastAttempt: mastery.lastAttempt,
93
+ streak: mastery.streak,
94
+ },
95
+ nextReview,
96
+ priority,
97
+ };
98
+ if (nextReview <= now) {
99
+ dueNow.push(reviewItem);
100
+ }
101
+ else if (opts.includeUpcoming && nextReview <= upcomingCutoff) {
102
+ upcoming.push(reviewItem);
103
+ }
104
+ }
105
+ // Sort by priority (highest first)
106
+ dueNow.sort((a, b) => b.priority - a.priority);
107
+ upcoming.sort((a, b) => a.nextReview.getTime() - b.nextReview.getTime());
108
+ // Apply limits
109
+ const limitedDue = opts.maxReviews ? dueNow.slice(0, opts.maxReviews) : dueNow;
110
+ const remainingLimit = opts.maxReviews
111
+ ? Math.max(0, opts.maxReviews - limitedDue.length)
112
+ : undefined;
113
+ const limitedUpcoming = remainingLimit !== undefined
114
+ ? upcoming.slice(0, remainingLimit)
115
+ : upcoming;
116
+ // Calculate total review time
117
+ const totalReviewMinutes = [...limitedDue, ...limitedUpcoming].reduce((sum, item) => sum + Math.ceil(item.skill.estimatedMinutes * 0.3), // Reviews are ~30% of initial learning time
118
+ 0);
119
+ return {
120
+ dueNow: limitedDue,
121
+ upcoming: limitedUpcoming,
122
+ totalReviewMinutes,
123
+ };
124
+ }
125
+ /**
126
+ * Calculate the next review interval using SM-2 algorithm
127
+ *
128
+ * @param currentState - Current mastery state
129
+ * @param quality - Review quality (0-5)
130
+ * @returns Updated SM-2 parameters
131
+ */
132
+ calculateNextReview(currentState, quality) {
133
+ const now = new Date();
134
+ // Get current SM-2 state
135
+ let easiness = currentState.easinessFactor ?? SM2_CONSTANTS.DEFAULT_EASINESS;
136
+ let repetitions = currentState.streak;
137
+ let interval;
138
+ // Quality must be >= 3 for successful recall
139
+ if (quality >= 3) {
140
+ // Successful recall
141
+ if (repetitions === 0) {
142
+ interval = SM2_CONSTANTS.INITIAL_INTERVAL;
143
+ }
144
+ else if (repetitions === 1) {
145
+ interval = SM2_CONSTANTS.SECOND_INTERVAL;
146
+ }
147
+ else {
148
+ // Get previous interval from last attempt
149
+ const lastInterval = this.estimatePreviousInterval(currentState);
150
+ interval = Math.round(lastInterval * easiness);
151
+ }
152
+ repetitions++;
153
+ }
154
+ else {
155
+ // Failed recall - reset to beginning
156
+ repetitions = 0;
157
+ interval = SM2_CONSTANTS.INITIAL_INTERVAL;
158
+ }
159
+ // Update easiness factor
160
+ // EF = EF + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02))
161
+ const easinessChange = 0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02);
162
+ easiness = Math.max(SM2_CONSTANTS.MIN_EASINESS, Math.min(SM2_CONSTANTS.MAX_EASINESS, easiness + easinessChange));
163
+ // Calculate next review date
164
+ const nextReview = new Date(now.getTime() + interval * 24 * 60 * 60 * 1000);
165
+ return {
166
+ easinessFactor: easiness,
167
+ intervalDays: interval,
168
+ nextReview,
169
+ repetitions,
170
+ };
171
+ }
172
+ /**
173
+ * Convert review quality to mastery update
174
+ *
175
+ * @param quality - Review quality (0-5)
176
+ * @returns Mastery level adjustment
177
+ */
178
+ qualityToMasteryAdjustment(quality) {
179
+ // Map quality to mastery change
180
+ switch (quality) {
181
+ case 5: return 0.1; // Perfect - increase mastery
182
+ case 4: return 0.05; // Correct with hesitation
183
+ case 3: return 0.0; // Correct with difficulty - maintain
184
+ case 2: return -0.1; // Incorrect but close
185
+ case 1: return -0.2; // Poor recall
186
+ case 0: return -0.3; // Complete failure
187
+ }
188
+ }
189
+ /**
190
+ * Calculate next review date based on current mastery state
191
+ */
192
+ calculateNextReviewDate(mastery) {
193
+ if (!mastery.lastAttempt) {
194
+ return new Date(); // Due immediately
195
+ }
196
+ const easiness = mastery.easinessFactor ?? SM2_CONSTANTS.DEFAULT_EASINESS;
197
+ const streak = mastery.streak;
198
+ // Calculate interval based on streak
199
+ let interval;
200
+ if (streak === 0) {
201
+ interval = SM2_CONSTANTS.INITIAL_INTERVAL;
202
+ }
203
+ else if (streak === 1) {
204
+ interval = SM2_CONSTANTS.SECOND_INTERVAL;
205
+ }
206
+ else {
207
+ // Exponential growth based on streak and easiness
208
+ interval = Math.round(SM2_CONSTANTS.SECOND_INTERVAL * Math.pow(easiness, streak - 1));
209
+ }
210
+ // Clamp to reasonable bounds (1 day to 1 year)
211
+ interval = Math.max(1, Math.min(365, interval));
212
+ return new Date(mastery.lastAttempt.getTime() + interval * 24 * 60 * 60 * 1000);
213
+ }
214
+ /**
215
+ * Estimate previous interval from mastery state
216
+ */
217
+ estimatePreviousInterval(mastery) {
218
+ const streak = mastery.streak;
219
+ const easiness = mastery.easinessFactor ?? SM2_CONSTANTS.DEFAULT_EASINESS;
220
+ if (streak <= 1) {
221
+ return SM2_CONSTANTS.INITIAL_INTERVAL;
222
+ }
223
+ else if (streak === 2) {
224
+ return SM2_CONSTANTS.SECOND_INTERVAL;
225
+ }
226
+ else {
227
+ return Math.round(SM2_CONSTANTS.SECOND_INTERVAL * Math.pow(easiness, streak - 2));
228
+ }
229
+ }
230
+ /**
231
+ * Calculate review priority
232
+ * Higher priority = more urgent to review
233
+ */
234
+ calculatePriority(mastery, nextReview, now) {
235
+ // Base priority on how overdue the review is
236
+ const overdueDays = (now.getTime() - nextReview.getTime()) / (24 * 60 * 60 * 1000);
237
+ // Higher mastery items get slightly lower priority (we want to maintain them)
238
+ const masteryFactor = 1 - mastery.mastery * 0.3;
239
+ // Combine factors
240
+ // Overdue items get positive priority, upcoming items get negative
241
+ return overdueDays * masteryFactor * 10;
242
+ }
243
+ }
244
+ /**
245
+ * Create a spaced repetition scheduler
246
+ */
247
+ export function createSpacedRepetitionScheduler(storage) {
248
+ return new SpacedRepetitionScheduler(storage);
249
+ }
250
+ /**
251
+ * Standalone SM-2 calculation (for use without storage)
252
+ *
253
+ * @param quality - Review quality (0-5)
254
+ * @param previousEasiness - Previous easiness factor
255
+ * @param previousInterval - Previous interval in days
256
+ * @param repetitions - Number of previous successful repetitions
257
+ * @returns Updated SM-2 parameters
258
+ */
259
+ export function calculateSM2(quality, previousEasiness = SM2_CONSTANTS.DEFAULT_EASINESS, previousInterval = 0, repetitions = 0) {
260
+ const now = new Date();
261
+ let easiness = previousEasiness;
262
+ let interval;
263
+ let newRepetitions = repetitions;
264
+ if (quality >= 3) {
265
+ // Successful recall
266
+ if (newRepetitions === 0) {
267
+ interval = SM2_CONSTANTS.INITIAL_INTERVAL;
268
+ }
269
+ else if (newRepetitions === 1) {
270
+ interval = SM2_CONSTANTS.SECOND_INTERVAL;
271
+ }
272
+ else {
273
+ interval = Math.round(previousInterval * easiness);
274
+ }
275
+ newRepetitions++;
276
+ }
277
+ else {
278
+ // Failed - reset
279
+ interval = SM2_CONSTANTS.INITIAL_INTERVAL;
280
+ newRepetitions = 0;
281
+ }
282
+ // Update easiness
283
+ const easinessChange = 0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02);
284
+ easiness = Math.max(SM2_CONSTANTS.MIN_EASINESS, Math.min(SM2_CONSTANTS.MAX_EASINESS, easiness + easinessChange));
285
+ return {
286
+ easinessFactor: easiness,
287
+ intervalDays: interval,
288
+ nextReview: new Date(now.getTime() + interval * 24 * 60 * 60 * 1000),
289
+ repetitions: newRepetitions,
290
+ };
291
+ }
292
+ //# sourceMappingURL=spaced-repetition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spaced-repetition.js","sourceRoot":"","sources":["../../../src/query/spaced-repetition.ts"],"names":[],"mappings":"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"}