@bis-code/study-dash 0.2.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 (89) hide show
  1. package/.claude-plugin/marketplace.json +18 -0
  2. package/.claude-plugin/plugin.json +19 -0
  3. package/.mcp.json +8 -0
  4. package/LICENSE +21 -0
  5. package/commands/dashboard.md +8 -0
  6. package/commands/import.md +12 -0
  7. package/commands/learn.md +13 -0
  8. package/hooks/hooks.json +27 -0
  9. package/package.json +36 -0
  10. package/rules/tutor-mode.md +38 -0
  11. package/server/dist/bundle.mjs +1240 -0
  12. package/server/dist/dashboard/api.d.ts +16 -0
  13. package/server/dist/dashboard/api.js +150 -0
  14. package/server/dist/dashboard/api.js.map +1 -0
  15. package/server/dist/dashboard/server.d.ts +21 -0
  16. package/server/dist/dashboard/server.js +171 -0
  17. package/server/dist/dashboard/server.js.map +1 -0
  18. package/server/dist/index.d.ts +2 -0
  19. package/server/dist/index.js +40 -0
  20. package/server/dist/index.js.map +1 -0
  21. package/server/dist/services/curriculum.d.ts +25 -0
  22. package/server/dist/services/curriculum.js +110 -0
  23. package/server/dist/services/curriculum.js.map +1 -0
  24. package/server/dist/services/exercises.d.ts +35 -0
  25. package/server/dist/services/exercises.js +215 -0
  26. package/server/dist/services/exercises.js.map +1 -0
  27. package/server/dist/services/qa.d.ts +15 -0
  28. package/server/dist/services/qa.js +30 -0
  29. package/server/dist/services/qa.js.map +1 -0
  30. package/server/dist/services/viz.d.ts +8 -0
  31. package/server/dist/services/viz.js +21 -0
  32. package/server/dist/services/viz.js.map +1 -0
  33. package/server/dist/storage/db.d.ts +11 -0
  34. package/server/dist/storage/db.js +51 -0
  35. package/server/dist/storage/db.js.map +1 -0
  36. package/server/dist/storage/files.d.ts +10 -0
  37. package/server/dist/storage/files.js +34 -0
  38. package/server/dist/storage/files.js.map +1 -0
  39. package/server/dist/storage/schema.d.ts +3 -0
  40. package/server/dist/storage/schema.js +126 -0
  41. package/server/dist/storage/schema.js.map +1 -0
  42. package/server/dist/tools/curriculum.d.ts +4 -0
  43. package/server/dist/tools/curriculum.js +137 -0
  44. package/server/dist/tools/curriculum.js.map +1 -0
  45. package/server/dist/tools/exercises.d.ts +4 -0
  46. package/server/dist/tools/exercises.js +76 -0
  47. package/server/dist/tools/exercises.js.map +1 -0
  48. package/server/dist/tools/qa.d.ts +4 -0
  49. package/server/dist/tools/qa.js +56 -0
  50. package/server/dist/tools/qa.js.map +1 -0
  51. package/server/dist/tools/viz.d.ts +4 -0
  52. package/server/dist/tools/viz.js +54 -0
  53. package/server/dist/tools/viz.js.map +1 -0
  54. package/server/dist/types.d.ts +103 -0
  55. package/server/dist/types.js +2 -0
  56. package/server/dist/types.js.map +1 -0
  57. package/server/node_modules/better-sqlite3/LICENSE +21 -0
  58. package/server/node_modules/better-sqlite3/README.md +99 -0
  59. package/server/node_modules/better-sqlite3/binding.gyp +38 -0
  60. package/server/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
  61. package/server/node_modules/better-sqlite3/deps/common.gypi +68 -0
  62. package/server/node_modules/better-sqlite3/deps/copy.js +31 -0
  63. package/server/node_modules/better-sqlite3/deps/defines.gypi +41 -0
  64. package/server/node_modules/better-sqlite3/deps/download.sh +122 -0
  65. package/server/node_modules/better-sqlite3/deps/patches/1208.patch +15 -0
  66. package/server/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +261480 -0
  67. package/server/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +13715 -0
  68. package/server/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +719 -0
  69. package/server/node_modules/better-sqlite3/deps/sqlite3.gyp +80 -0
  70. package/server/node_modules/better-sqlite3/deps/test_extension.c +21 -0
  71. package/server/node_modules/better-sqlite3/lib/database.js +90 -0
  72. package/server/node_modules/better-sqlite3/lib/index.js +3 -0
  73. package/server/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
  74. package/server/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
  75. package/server/node_modules/better-sqlite3/lib/methods/function.js +31 -0
  76. package/server/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
  77. package/server/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
  78. package/server/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
  79. package/server/node_modules/better-sqlite3/lib/methods/table.js +189 -0
  80. package/server/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
  81. package/server/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
  82. package/server/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
  83. package/server/node_modules/better-sqlite3/lib/util.js +12 -0
  84. package/server/node_modules/better-sqlite3/package.json +54 -0
  85. package/server/node_modules/better-sqlite3/src/better_sqlite3.cpp +2186 -0
  86. package/server/node_modules/better-sqlite3/src/better_sqlite3.hpp +1036 -0
  87. package/server/package.json +31 -0
  88. package/skills/import/SKILL.md +19 -0
  89. package/skills/learn/SKILL.md +17 -0
@@ -0,0 +1,137 @@
1
+ import { z } from 'zod';
2
+ function getSession(sessions, sessionId) {
3
+ const key = sessionId || '_default';
4
+ if (!sessions.has(key)) {
5
+ sessions.set(key, { subjectId: null, topicId: null });
6
+ }
7
+ return sessions.get(key);
8
+ }
9
+ function err(text) {
10
+ return { content: [{ type: 'text', text }], isError: true };
11
+ }
12
+ function ok(text) {
13
+ return { content: [{ type: 'text', text }] };
14
+ }
15
+ export function registerCurriculumTools(server, svc, sessions, notify) {
16
+ // 1. learn_create_subject
17
+ server.tool('learn_create_subject', 'Create a new subject to study', {
18
+ name: z.string().describe('Subject name'),
19
+ language: z.string().optional().describe('Programming language (optional)'),
20
+ source: z.enum(['manual', 'roadmap', 'pdf']).optional().describe('Curriculum source'),
21
+ }, async ({ name, language, source }) => {
22
+ const subject = svc.createSubject(name, language, source);
23
+ notify();
24
+ return ok(`Created subject "${subject.name}" (id=${subject.id}, slug=${subject.slug})`);
25
+ });
26
+ // 2. learn_import_curriculum
27
+ server.tool('learn_import_curriculum', 'Import a curriculum (phases + topics) for a subject from JSON', {
28
+ subject_id: z.number().describe('Subject ID to import curriculum into'),
29
+ phases_json: z.string().describe('JSON array of phases, each with name, description, and topics array'),
30
+ }, async ({ subject_id, phases_json }) => {
31
+ let phases;
32
+ try {
33
+ phases = JSON.parse(phases_json);
34
+ }
35
+ catch {
36
+ return err('Invalid JSON in phases_json');
37
+ }
38
+ if (!Array.isArray(phases)) {
39
+ return err('phases_json must be a JSON array');
40
+ }
41
+ svc.importCurriculum(subject_id, phases);
42
+ notify();
43
+ return ok(`Imported ${phases.length} phase(s) into subject id=${subject_id}`);
44
+ });
45
+ // 3. learn_switch_subject
46
+ server.tool('learn_switch_subject', 'Switch the active subject for the session (by name or numeric ID)', {
47
+ subject: z.string().describe('Subject name or numeric ID'),
48
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
49
+ }, async ({ subject, session_id }) => {
50
+ const numId = Number(subject);
51
+ let resolved = isNaN(numId)
52
+ ? svc.findSubjectByName(subject)
53
+ : svc.getSubject(numId) ?? svc.findSubjectByName(subject);
54
+ if (!resolved) {
55
+ return err(`Subject not found: "${subject}"`);
56
+ }
57
+ const session = getSession(sessions, session_id);
58
+ session.subjectId = resolved.id;
59
+ session.topicId = null;
60
+ return ok(`Active subject: "${resolved.name}" (id=${resolved.id})`);
61
+ });
62
+ // 4. learn_set_topic
63
+ server.tool('learn_set_topic', 'Set the active topic for the session and mark it in_progress', {
64
+ topic: z.string().describe('Topic name or numeric ID'),
65
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
66
+ }, async ({ topic, session_id }) => {
67
+ const session = getSession(sessions, session_id);
68
+ if (session.subjectId === null) {
69
+ return err('No active subject. Use learn_switch_subject first.');
70
+ }
71
+ const numId = Number(topic);
72
+ let resolved = isNaN(numId)
73
+ ? svc.findTopic(session.subjectId, topic)
74
+ : svc.getTopic(numId) ?? svc.findTopic(session.subjectId, topic);
75
+ if (!resolved) {
76
+ return err(`Topic not found: "${topic}"`);
77
+ }
78
+ svc.setTopicStatus(resolved.id, 'in_progress');
79
+ session.topicId = resolved.id;
80
+ notify();
81
+ return ok(`Active topic: "${resolved.name}" (id=${resolved.id}, status=in_progress)`);
82
+ });
83
+ // 5. learn_mark_done
84
+ server.tool('learn_mark_done', 'Mark a topic as done (defaults to the active session topic)', {
85
+ topic: z.string().optional().describe('Topic name or numeric ID (uses session topic if omitted)'),
86
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
87
+ }, async ({ topic, session_id }) => {
88
+ const session = getSession(sessions, session_id);
89
+ let topicId = null;
90
+ if (topic !== undefined) {
91
+ const numId = Number(topic);
92
+ const resolved = isNaN(numId)
93
+ ? (session.subjectId !== null ? svc.findTopic(session.subjectId, topic) : undefined)
94
+ : svc.getTopic(numId);
95
+ if (!resolved) {
96
+ return err(`Topic not found: "${topic}"`);
97
+ }
98
+ topicId = resolved.id;
99
+ }
100
+ else {
101
+ topicId = session.topicId;
102
+ }
103
+ if (topicId === null) {
104
+ return err('No topic specified and no active topic in session.');
105
+ }
106
+ const resolved = svc.getTopic(topicId);
107
+ if (!resolved) {
108
+ return err(`Topic id=${topicId} not found.`);
109
+ }
110
+ svc.setTopicStatus(topicId, 'done');
111
+ notify();
112
+ return ok(`Topic "${resolved.name}" marked as done.`);
113
+ });
114
+ // 6. learn_get_progress
115
+ server.tool('learn_get_progress', 'Get progress statistics for the active subject', {
116
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
117
+ }, async ({ session_id }) => {
118
+ const session = getSession(sessions, session_id);
119
+ if (session.subjectId === null) {
120
+ return err('No active subject. Use learn_switch_subject first.');
121
+ }
122
+ const progress = svc.getProgress(session.subjectId);
123
+ return ok(JSON.stringify(progress, null, 2));
124
+ });
125
+ // 7. learn_get_curriculum
126
+ server.tool('learn_get_curriculum', 'Get the full curriculum (phases + topics) for the active subject', {
127
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
128
+ }, async ({ session_id }) => {
129
+ const session = getSession(sessions, session_id);
130
+ if (session.subjectId === null) {
131
+ return err('No active subject. Use learn_switch_subject first.');
132
+ }
133
+ const curriculum = svc.getCurriculum(session.subjectId);
134
+ return ok(JSON.stringify(curriculum, null, 2));
135
+ });
136
+ }
137
+ //# sourceMappingURL=curriculum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curriculum.js","sourceRoot":"","sources":["../../src/tools/curriculum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,SAAS,UAAU,CAAC,QAAmC,EAAE,SAAkB;IACzE,MAAM,GAAG,GAAG,SAAS,IAAI,UAAU,CAAC;IACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;AAC5B,CAAC;AAED,SAAS,GAAG,CAAC,IAAY;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAa,EAAE,CAAC;AAChF,CAAC;AAED,SAAS,EAAE,CAAC,IAAY;IACtB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,MAAiB,EACjB,GAAsB,EACtB,QAAmC,EACnC,MAAkB;IAElB,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,+BAA+B,EAC/B;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;QACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QAC3E,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;KACtF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1D,MAAM,EAAE,CAAC;QACT,OAAO,EAAE,CAAC,oBAAoB,OAAO,CAAC,IAAI,SAAS,OAAO,CAAC,EAAE,UAAU,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;IAC1F,CAAC,CACF,CAAC;IAEF,6BAA6B;IAC7B,MAAM,CAAC,IAAI,CACT,yBAAyB,EACzB,+DAA+D,EAC/D;QACE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;QACvE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qEAAqE,CAAC;KACxG,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE;QACpC,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,CAAC,6BAA6B,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,GAAG,CAAC,kCAAkC,CAAC,CAAC;QACjD,CAAC;QACD,GAAG,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAA8D,CAAC,CAAC;QACjG,MAAM,EAAE,CAAC;QACT,OAAO,EAAE,CAAC,YAAY,MAAM,CAAC,MAAM,6BAA6B,UAAU,EAAE,CAAC,CAAC;IAChF,CAAC,CACF,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,mEAAmE,EACnE;QACE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QAC1D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC;YACzB,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC;YAChC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAE5D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,uBAAuB,OAAO,GAAG,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACjD,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QAEvB,OAAO,EAAE,CAAC,oBAAoB,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;IACtE,CAAC,CACF,CAAC;IAEF,qBAAqB;IACrB,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,8DAA8D,EAC9D;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;QACtD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO,GAAG,CAAC,oDAAoD,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC;YACzB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC;YACzC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAEnE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC,CAAC;QAC5C,CAAC;QAED,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QAC/C,OAAO,CAAC,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,EAAE,CAAC;QAET,OAAO,EAAE,CAAC,kBAAkB,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,EAAE,uBAAuB,CAAC,CAAC;IACxF,CAAC,CACF,CAAC;IAEF,qBAAqB;IACrB,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,6DAA6D,EAC7D;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;QACjG,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,OAAO,GAAkB,IAAI,CAAC;QAElC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC;gBAC3B,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACpF,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC5B,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,oDAAoD,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,YAAY,OAAO,aAAa,CAAC,CAAC;QAC/C,CAAC;QAED,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,EAAE,CAAC;QAET,OAAO,EAAE,CAAC,UAAU,QAAQ,CAAC,IAAI,mBAAmB,CAAC,CAAC;IACxD,CAAC,CACF,CAAC;IAEF,wBAAwB;IACxB,MAAM,CAAC,IAAI,CACT,oBAAoB,EACpB,gDAAgD,EAChD;QACE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO,GAAG,CAAC,oDAAoD,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpD,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CACF,CAAC;IAEF,0BAA0B;IAC1B,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,kEAAkE,EAClE;QACE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;KACxF,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACjD,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO,GAAG,CAAC,oDAAoD,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ExerciseService } from '../services/exercises.js';
3
+ import type { SessionState } from '../types.js';
4
+ export declare function registerExerciseTools(server: McpServer, svc: ExerciseService, sessions: Map<string, SessionState>, notify: () => void): void;
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ function getSession(sessions, sessionId) {
3
+ const key = sessionId || '_default';
4
+ if (!sessions.has(key)) {
5
+ sessions.set(key, { subjectId: null, topicId: null });
6
+ }
7
+ return sessions.get(key);
8
+ }
9
+ function err(text) {
10
+ return { content: [{ type: 'text', text }], isError: true };
11
+ }
12
+ function ok(text) {
13
+ return { content: [{ type: 'text', text }] };
14
+ }
15
+ export function registerExerciseTools(server, svc, sessions, notify) {
16
+ // 1. learn_create_exercise
17
+ server.tool('learn_create_exercise', 'Create an exercise (coding, quiz, project, assignment) for the active topic', {
18
+ title: z.string().describe('Exercise title'),
19
+ type: z.enum(['coding', 'quiz', 'project', 'assignment']).describe('Exercise type'),
20
+ description: z.string().describe('Exercise description / instructions'),
21
+ difficulty: z.enum(['easy', 'medium', 'hard']).optional().describe('Difficulty level'),
22
+ est_minutes: z.number().optional().describe('Estimated time in minutes'),
23
+ source: z.enum(['ai', 'pdf_import']).optional().describe('Source of the exercise'),
24
+ starter_code: z.string().optional().describe('Starter code for coding/project exercises'),
25
+ test_content: z.string().optional().describe('Test code for coding/project exercises'),
26
+ quiz_json: z
27
+ .string()
28
+ .optional()
29
+ .describe('JSON string of QuizPayload for quiz exercises'),
30
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
31
+ }, async ({ title, type, description, difficulty, est_minutes, source, starter_code, test_content, quiz_json, session_id }) => {
32
+ const session = getSession(sessions, session_id);
33
+ if (session.topicId === null) {
34
+ return err('No active topic. Use learn_set_topic first.');
35
+ }
36
+ const exercise = svc.createExercise(session.topicId, {
37
+ title,
38
+ type,
39
+ description,
40
+ difficulty,
41
+ est_minutes,
42
+ source,
43
+ starter_code,
44
+ test_content,
45
+ quiz_json,
46
+ });
47
+ notify();
48
+ return ok(`Created exercise "${exercise.title}" (id=${exercise.id}, type=${exercise.type})`);
49
+ });
50
+ // 2. learn_run_tests
51
+ server.tool('learn_run_tests', 'Run tests for a coding/project exercise and return results', {
52
+ exercise_id: z.number().describe('ID of the exercise to run tests for'),
53
+ }, async ({ exercise_id }) => {
54
+ try {
55
+ const results = await svc.runTests(exercise_id);
56
+ notify();
57
+ return ok(JSON.stringify(results, null, 2));
58
+ }
59
+ catch (error) {
60
+ const msg = error instanceof Error ? error.message : String(error);
61
+ return err(`Failed to run tests: ${msg}`);
62
+ }
63
+ });
64
+ // 3. learn_get_exercises
65
+ server.tool('learn_get_exercises', 'List all exercises for the active topic', {
66
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
67
+ }, async ({ session_id }) => {
68
+ const session = getSession(sessions, session_id);
69
+ if (session.topicId === null) {
70
+ return err('No active topic. Use learn_set_topic first.');
71
+ }
72
+ const exercises = svc.listForTopic(session.topicId);
73
+ return ok(JSON.stringify(exercises, null, 2));
74
+ });
75
+ }
76
+ //# sourceMappingURL=exercises.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exercises.js","sourceRoot":"","sources":["../../src/tools/exercises.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { QAService } from '../services/qa.js';
3
+ import type { SessionState } from '../types.js';
4
+ export declare function registerQATools(server: McpServer, svc: QAService, sessions: Map<string, SessionState>, notify: () => void): void;
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ function getSession(sessions, sessionId) {
3
+ const key = sessionId || '_default';
4
+ if (!sessions.has(key)) {
5
+ sessions.set(key, { subjectId: null, topicId: null });
6
+ }
7
+ return sessions.get(key);
8
+ }
9
+ function err(text) {
10
+ return { content: [{ type: 'text', text }], isError: true };
11
+ }
12
+ function ok(text) {
13
+ return { content: [{ type: 'text', text }] };
14
+ }
15
+ export function registerQATools(server, svc, sessions, notify) {
16
+ // 1. learn_log_question
17
+ server.tool('learn_log_question', 'Log a question for the active topic', {
18
+ content: z.string().describe('The question text'),
19
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
20
+ }, async ({ content, session_id }) => {
21
+ const session = getSession(sessions, session_id);
22
+ if (session.topicId === null) {
23
+ return err('No active topic. Use learn_set_topic first.');
24
+ }
25
+ const entry = svc.logEntry(session.topicId, 'question', content, session_id);
26
+ notify();
27
+ return ok(`Logged question (id=${entry.id})`);
28
+ });
29
+ // 2. learn_log_answer
30
+ server.tool('learn_log_answer', 'Log an answer or note for the active topic', {
31
+ content: z.string().describe('The answer or note text'),
32
+ question_id: z.number().optional().describe('ID of the question this answers (optional)'),
33
+ kind: z.enum(['answer', 'note']).optional().describe('Entry kind: answer or note (defaults to answer)'),
34
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
35
+ }, async ({ content, question_id, kind, session_id }) => {
36
+ const session = getSession(sessions, session_id);
37
+ if (session.topicId === null) {
38
+ return err('No active topic. Use learn_set_topic first.');
39
+ }
40
+ const entryKind = kind ?? 'answer';
41
+ const entry = svc.logEntry(session.topicId, entryKind, content, session_id, question_id);
42
+ notify();
43
+ return ok(`Logged ${entryKind} (id=${entry.id})`);
44
+ });
45
+ // 3. learn_search
46
+ server.tool('learn_search', 'Full-text search across all entries', {
47
+ query: z.string().describe('Search query'),
48
+ }, async ({ query }) => {
49
+ const results = svc.search(query);
50
+ if (results.length === 0) {
51
+ return ok('No results found.');
52
+ }
53
+ return ok(JSON.stringify(results, null, 2));
54
+ });
55
+ }
56
+ //# sourceMappingURL=qa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa.js","sourceRoot":"","sources":["../../src/tools/qa.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { VizService } from '../services/viz.js';
3
+ import type { SessionState } from '../types.js';
4
+ export declare function registerVizTools(server: McpServer, svc: VizService, sessions: Map<string, SessionState>, notify: () => void): void;
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ function getSession(sessions, sessionId) {
3
+ const key = sessionId || '_default';
4
+ if (!sessions.has(key)) {
5
+ sessions.set(key, { subjectId: null, topicId: null });
6
+ }
7
+ return sessions.get(key);
8
+ }
9
+ function err(text) {
10
+ return { content: [{ type: 'text', text }], isError: true };
11
+ }
12
+ function ok(text) {
13
+ return { content: [{ type: 'text', text }] };
14
+ }
15
+ export function registerVizTools(server, svc, sessions, notify) {
16
+ // 1. learn_create_viz
17
+ server.tool('learn_create_viz', 'Create a step-by-step HTML visualization for the active topic', {
18
+ title: z.string().describe('Title for the visualization'),
19
+ steps: z
20
+ .string()
21
+ .describe('JSON array of steps, each with { html: string, description: string }'),
22
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
23
+ }, async ({ title, steps, session_id }) => {
24
+ const session = getSession(sessions, session_id);
25
+ if (session.topicId === null) {
26
+ return err('No active topic. Use learn_set_topic first.');
27
+ }
28
+ let parsedSteps;
29
+ try {
30
+ parsedSteps = JSON.parse(steps);
31
+ }
32
+ catch {
33
+ return err('Invalid JSON in steps parameter');
34
+ }
35
+ if (!Array.isArray(parsedSteps)) {
36
+ return err('steps must be a JSON array');
37
+ }
38
+ const viz = svc.create(session.topicId, title, parsedSteps);
39
+ notify();
40
+ return ok(`Created visualization "${viz.title}" (id=${viz.id})`);
41
+ });
42
+ // 2. learn_get_viz
43
+ server.tool('learn_get_viz', 'Get all visualizations for the active topic', {
44
+ session_id: z.string().optional().describe('Session identifier (defaults to _default)'),
45
+ }, async ({ session_id }) => {
46
+ const session = getSession(sessions, session_id);
47
+ if (session.topicId === null) {
48
+ return err('No active topic. Use learn_set_topic first.');
49
+ }
50
+ const vizList = svc.listForTopic(session.topicId);
51
+ return ok(JSON.stringify(vizList, null, 2));
52
+ });
53
+ }
54
+ //# sourceMappingURL=viz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viz.js","sourceRoot":"","sources":["../../src/tools/viz.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ export interface Subject {
2
+ id: number;
3
+ name: string;
4
+ slug: string;
5
+ language: string;
6
+ source: 'manual' | 'roadmap' | 'pdf';
7
+ created_at: string;
8
+ }
9
+ export interface Phase {
10
+ id: number;
11
+ subject_id: number;
12
+ name: string;
13
+ description: string;
14
+ sort_order: number;
15
+ }
16
+ export interface Topic {
17
+ id: number;
18
+ phase_id: number;
19
+ name: string;
20
+ description: string;
21
+ sort_order: number;
22
+ status: 'todo' | 'in_progress' | 'done';
23
+ updated_at: string;
24
+ }
25
+ export interface Entry {
26
+ id: number;
27
+ topic_id: number;
28
+ kind: 'question' | 'answer' | 'note';
29
+ content: string;
30
+ session_id: string;
31
+ question_id: number | null;
32
+ created_at: string;
33
+ }
34
+ export interface Visualization {
35
+ id: number;
36
+ topic_id: number;
37
+ title: string;
38
+ steps_json: string;
39
+ created_at: string;
40
+ }
41
+ export interface VizStep {
42
+ html: string;
43
+ description: string;
44
+ }
45
+ export interface Exercise {
46
+ id: number;
47
+ topic_id: number;
48
+ title: string;
49
+ type: 'coding' | 'quiz' | 'project' | 'assignment';
50
+ description: string;
51
+ difficulty: 'easy' | 'medium' | 'hard';
52
+ est_minutes: number;
53
+ source: 'ai' | 'pdf_import';
54
+ starter_code: string;
55
+ test_content: string;
56
+ quiz_json: string;
57
+ file_path: string;
58
+ status: 'pending' | 'in_progress' | 'passed' | 'failed';
59
+ created_at: string;
60
+ }
61
+ export interface ExerciseResult {
62
+ id: number;
63
+ exercise_id: number;
64
+ test_name: string;
65
+ passed: boolean;
66
+ output: string;
67
+ ran_at: string;
68
+ }
69
+ export interface QuizQuestion {
70
+ id: number;
71
+ text: string;
72
+ type: 'multiple_choice' | 'true_false' | 'fill_in';
73
+ options?: string[];
74
+ correct: number | boolean | string;
75
+ explanation: string;
76
+ }
77
+ export interface QuizPayload {
78
+ questions: QuizQuestion[];
79
+ }
80
+ export interface ProgressStats {
81
+ total_topics: number;
82
+ done: number;
83
+ in_progress: number;
84
+ todo: number;
85
+ total_entries: number;
86
+ total_exercises: number;
87
+ total_viz: number;
88
+ }
89
+ export interface PhaseWithTopics extends Phase {
90
+ topics: Topic[];
91
+ }
92
+ export interface TopicWithEntries extends Topic {
93
+ entries: Entry[];
94
+ }
95
+ export interface SessionState {
96
+ subjectId: number | null;
97
+ topicId: number | null;
98
+ }
99
+ export interface SSEEvent {
100
+ type: 'connected' | 'update' | 'test_result';
101
+ payload: Record<string, unknown>;
102
+ ts: string;
103
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Joshua Wise
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,99 @@
1
+ # better-sqlite3 [![Build Status](https://github.com/JoshuaWise/better-sqlite3/actions/workflows/build.yml/badge.svg)](https://github.com/JoshuaWise/better-sqlite3/actions/workflows/build.yml?query=branch%3Amaster)
2
+
3
+ The fastest and simplest library for SQLite in Node.js.
4
+
5
+ - Full transaction support
6
+ - High performance, efficiency, and safety
7
+ - Easy-to-use synchronous API *(better concurrency than an asynchronous API... yes, you read that correctly)*
8
+ - Support for user-defined functions, aggregates, virtual tables, and extensions
9
+ - 64-bit integers *(invisible until you need them)*
10
+ - Worker thread support *(for large/slow queries)*
11
+
12
+ ## Help this project stay strong! &#128170;
13
+
14
+ `better-sqlite3` is used by thousands of developers and engineers on a daily basis. Long nights and weekends were spent keeping this project strong and dependable, with no ask for compensation or funding, until now. If your company uses `better-sqlite3`, ask your manager to consider supporting the project:
15
+
16
+ - [Become a GitHub sponsor](https://github.com/sponsors/JoshuaWise)
17
+ - [Become a backer on Patreon](https://www.patreon.com/joshuawise)
18
+ - [Make a one-time donation on PayPal](https://www.paypal.me/joshuathomaswise)
19
+
20
+ ## How other libraries compare
21
+
22
+ | |select 1 row &nbsp;`get()`&nbsp;|select 100 rows &nbsp;&nbsp;`all()`&nbsp;&nbsp;|select 100 rows `iterate()` 1-by-1|insert 1 row `run()`|insert 100 rows in a transaction|
23
+ |---|---|---|---|---|---|
24
+ |better-sqlite3|1x|1x|1x|1x|1x|
25
+ |[sqlite](https://www.npmjs.com/package/sqlite) and [sqlite3](https://www.npmjs.com/package/sqlite3)|11.7x slower|2.9x slower|24.4x slower|2.8x slower|15.6x slower|
26
+
27
+ > You can verify these results by [running the benchmark yourself](./docs/benchmark.md).
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install better-sqlite3
33
+ ```
34
+
35
+ > Requires Node.js v14.21.1 or later. Prebuilt binaries are available for [LTS versions](https://nodejs.org/en/about/releases/). If you have trouble installing, check the [troubleshooting guide](./docs/troubleshooting.md).
36
+
37
+ ## Usage
38
+
39
+ ```js
40
+ const db = require('better-sqlite3')('foobar.db', options);
41
+
42
+ const row = db.prepare('SELECT * FROM users WHERE id = ?').get(userId);
43
+ console.log(row.firstName, row.lastName, row.email);
44
+ ```
45
+
46
+ Though not required, [it is generally important to set the WAL pragma for performance reasons](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/performance.md).
47
+
48
+ ```js
49
+ db.pragma('journal_mode = WAL');
50
+ ```
51
+
52
+ ##### In ES6 module notation:
53
+
54
+ ```js
55
+ import Database from 'better-sqlite3';
56
+ const db = new Database('foobar.db', options);
57
+ db.pragma('journal_mode = WAL');
58
+ ```
59
+
60
+ ## Why should I use this instead of [node-sqlite3](https://github.com/mapbox/node-sqlite3)?
61
+
62
+ - `node-sqlite3` uses asynchronous APIs for tasks that are either CPU-bound or serialized. That's not only bad design, but it wastes tons of resources. It also causes [mutex thrashing](https://en.wikipedia.org/wiki/Resource_contention) which has devastating effects on performance.
63
+ - `node-sqlite3` exposes low-level (C language) memory management functions. `better-sqlite3` does it the JavaScript way, allowing the garbage collector to worry about memory management.
64
+ - `better-sqlite3` is simpler to use, and it provides nice utilities for some operations that are very difficult or impossible in `node-sqlite3`.
65
+ - `better-sqlite3` is much faster than `node-sqlite3` in most cases, and just as fast in all other cases.
66
+
67
+ #### When is this library not appropriate?
68
+
69
+ In most cases, if you're attempting something that cannot be reasonably accomplished with `better-sqlite3`, it probably cannot be reasonably accomplished with SQLite in general. For example, if you're executing queries that take one second to complete, and you expect to have many concurrent users executing those queries, no amount of asynchronicity will save you from SQLite's serialized nature. Fortunately, SQLite is very *very* fast. With proper indexing, we've been able to achieve upward of 2000 queries per second with 5-way-joins in a 60 GB database, where each query was handling 5–50 kilobytes of real data.
70
+
71
+ If you have a performance problem, the most likely causes are inefficient queries, improper indexing, or a lack of [WAL mode](./docs/performance.md)—not `better-sqlite3` itself. However, there are some cases where `better-sqlite3` could be inappropriate:
72
+
73
+ - If you expect a high volume of concurrent reads each returning many megabytes of data (i.e., videos)
74
+ - If you expect a high volume of concurrent writes (i.e., a social media site)
75
+ - If your database's size is near the terabyte range
76
+
77
+ For these situations, you should probably use a full-fledged RDBMS such as [PostgreSQL](https://www.postgresql.org/).
78
+
79
+ ## Upgrading
80
+
81
+ Upgrading your `better-sqlite3` dependency can potentially introduce breaking changes, either in the `better-sqlite3` API (if you upgrade to a new [major version](https://semver.org/)), or between your existing database(s) and the underlying version of SQLite. Before upgrading, review:
82
+
83
+ * [`better-sqlite3` release notes](https://github.com/WiseLibs/better-sqlite3/releases)
84
+ * [SQLite release history](https://www.sqlite.org/changes.html)
85
+
86
+ # Documentation
87
+
88
+ - [API documentation](./docs/api.md)
89
+ - [Performance](./docs/performance.md) (also see [benchmark results](./docs/benchmark.md))
90
+ - [64-bit integer support](./docs/integer.md)
91
+ - [Worker thread support](./docs/threads.md)
92
+ - [Unsafe mode (advanced)](./docs/unsafe.md)
93
+ - [SQLite compilation (advanced)](./docs/compilation.md)
94
+ - [Contribution rules](./docs/contribution.md)
95
+ - [Code of conduct](./docs/conduct.md)
96
+
97
+ # License
98
+
99
+ [MIT](./LICENSE)
@@ -0,0 +1,38 @@
1
+ # ===
2
+ # This is the main GYP file, which builds better-sqlite3 with SQLite itself.
3
+ # ===
4
+
5
+ {
6
+ 'includes': ['deps/common.gypi'],
7
+ 'targets': [
8
+ {
9
+ 'target_name': 'better_sqlite3',
10
+ 'dependencies': ['deps/sqlite3.gyp:sqlite3'],
11
+ 'sources': ['src/better_sqlite3.cpp'],
12
+ 'cflags_cc': ['-std=c++20'],
13
+ 'xcode_settings': {
14
+ 'OTHER_CPLUSPLUSFLAGS': ['-std=c++20', '-stdlib=libc++'],
15
+ },
16
+ 'msvs_settings': {
17
+ 'VCCLCompilerTool': {
18
+ 'AdditionalOptions': [
19
+ '/std:c++20',
20
+ ],
21
+ },
22
+ },
23
+ 'conditions': [
24
+ ['OS=="linux"', {
25
+ 'ldflags': [
26
+ '-Wl,-Bsymbolic',
27
+ '-Wl,--exclude-libs,ALL',
28
+ ],
29
+ }],
30
+ ],
31
+ },
32
+ {
33
+ 'target_name': 'test_extension',
34
+ 'dependencies': ['deps/sqlite3.gyp:sqlite3'],
35
+ 'conditions': [['sqlite3 == ""', { 'sources': ['deps/test_extension.c'] }]],
36
+ },
37
+ ],
38
+ }