eklavya-mcp 1.0.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 (55) hide show
  1. package/README.md +42 -0
  2. package/bin/eklavya-mcp.sh +25 -0
  3. package/dist/assets/tutor-skill.md +106 -0
  4. package/dist/cli.js +145 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/concurrency.js +41 -0
  7. package/dist/concurrency.js.map +1 -0
  8. package/dist/config.js +116 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/db.js +26 -0
  11. package/dist/db.js.map +1 -0
  12. package/dist/migrate.js +45 -0
  13. package/dist/migrate.js.map +1 -0
  14. package/dist/migrations/001_init.sql +71 -0
  15. package/dist/migrations/002_stop_markers.sql +14 -0
  16. package/dist/migrations/003_gate_repo.sql +5 -0
  17. package/dist/paths.js +25 -0
  18. package/dist/paths.js.map +1 -0
  19. package/dist/seed/git.json +49 -0
  20. package/dist/seed/node-backend.json +43 -0
  21. package/dist/seed/react.json +46 -0
  22. package/dist/seed/web-auth.json +78 -0
  23. package/dist/seed.js +112 -0
  24. package/dist/seed.js.map +1 -0
  25. package/dist/server.js +39 -0
  26. package/dist/server.js.map +1 -0
  27. package/dist/session.js +24 -0
  28. package/dist/session.js.map +1 -0
  29. package/dist/slug.js +75 -0
  30. package/dist/slug.js.map +1 -0
  31. package/dist/srs.js +130 -0
  32. package/dist/srs.js.map +1 -0
  33. package/dist/store.js +122 -0
  34. package/dist/store.js.map +1 -0
  35. package/dist/tools/config_tools.js +73 -0
  36. package/dist/tools/config_tools.js.map +1 -0
  37. package/dist/tools/get_concept_graph.js +104 -0
  38. package/dist/tools/get_concept_graph.js.map +1 -0
  39. package/dist/tools/get_gate_status.js +20 -0
  40. package/dist/tools/get_gate_status.js.map +1 -0
  41. package/dist/tools/get_learner_profile.js +78 -0
  42. package/dist/tools/get_learner_profile.js.map +1 -0
  43. package/dist/tools/get_session_quiz_plan.js +107 -0
  44. package/dist/tools/get_session_quiz_plan.js.map +1 -0
  45. package/dist/tools/index.js +45 -0
  46. package/dist/tools/index.js.map +1 -0
  47. package/dist/tools/log_session_concepts.js +83 -0
  48. package/dist/tools/log_session_concepts.js.map +1 -0
  49. package/dist/tools/record_attempt.js +69 -0
  50. package/dist/tools/record_attempt.js.map +1 -0
  51. package/dist/tools/types.js +4 -0
  52. package/dist/tools/types.js.map +1 -0
  53. package/dist/tools/upsert_concepts.js +89 -0
  54. package/dist/tools/upsert_concepts.js.map +1 -0
  55. package/package.json +68 -0
package/dist/srs.js ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * SM-2 spaced repetition, mastery scoring and difficulty escalation (PRD §7).
3
+ *
4
+ * Everything here is pure: no database, no clock. `now` is always a parameter so
5
+ * the whole scheduling model is testable without freezing time.
6
+ */
7
+ export const MIN_EASE = 1.3;
8
+ export const DEFAULT_EASE = 2.5;
9
+ export const MASTERY_THRESHOLD = 0.7;
10
+ export const MIN_REPS_FOR_KNOWN = 2;
11
+ /** Read-time decay (PRD §7): how fast an ignored concept loses credibility, and its floor. */
12
+ export const DECAY_PER_WEEK = 0.05;
13
+ export const DECAY_FLOOR = 0.3;
14
+ /** Score smoothing (phase-1 decision G2). */
15
+ export const SCORE_WINDOW = 5;
16
+ export const SCORE_ALPHA = 0.6;
17
+ export const MS_PER_DAY = 86_400_000;
18
+ export function initialMastery() {
19
+ return { score: 0, ease: DEFAULT_EASE, interval_d: 0, reps: 0, last_seen: null, next_review: null };
20
+ }
21
+ const clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
22
+ /**
23
+ * Recency-weighted mean of the last few grades, normalized to 0..1.
24
+ * A single attempt scores exactly `grade / 5`; older attempts fade at `SCORE_ALPHA`.
25
+ * Alpha is above 0.5 deliberately: at exactly 0.5 the weighting is symmetric, so a
26
+ * learner who is improving would score the same as one who is falling apart.
27
+ */
28
+ export function scoreFromGrades(grades) {
29
+ const recent = grades.slice(-SCORE_WINDOW);
30
+ if (recent.length === 0)
31
+ return 0;
32
+ let score = clamp(recent[0], 0, 5) / 5;
33
+ for (let i = 1; i < recent.length; i += 1) {
34
+ score = SCORE_ALPHA * (clamp(recent[i], 0, 5) / 5) + (1 - SCORE_ALPHA) * score;
35
+ }
36
+ return Number(score.toFixed(6));
37
+ }
38
+ /**
39
+ * Standard SM-2 ease adjustment. Applied on every grade, not only on passes:
40
+ * getting a concept wrong should make it come back harder, which is the whole
41
+ * point of tracking ease separately from the interval.
42
+ */
43
+ export function nextEase(ease, grade) {
44
+ const q = clamp(grade, 0, 5);
45
+ const adjusted = ease + (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02));
46
+ return Number(Math.max(MIN_EASE, adjusted).toFixed(6));
47
+ }
48
+ /** Applies one graded attempt, returning the new mastery state. */
49
+ export function applyGrade({ state, grade, grades, now }) {
50
+ const q = clamp(Math.round(grade), 0, 5);
51
+ const ease = nextEase(state.ease, q);
52
+ let reps;
53
+ let interval;
54
+ if (q >= 3) {
55
+ reps = state.reps + 1;
56
+ if (reps === 1)
57
+ interval = 1;
58
+ else if (reps === 2)
59
+ interval = 6;
60
+ else
61
+ interval = Math.round(state.interval_d * ease);
62
+ }
63
+ else {
64
+ // A failed recall restarts the ladder — the learner sees it again tomorrow.
65
+ reps = 0;
66
+ interval = 1;
67
+ }
68
+ return {
69
+ score: scoreFromGrades(grades),
70
+ ease,
71
+ interval_d: interval,
72
+ reps,
73
+ last_seen: now.toISOString(),
74
+ next_review: new Date(now.getTime() + interval * MS_PER_DAY).toISOString(),
75
+ };
76
+ }
77
+ /**
78
+ * Soft decay for concepts whose review date is long past. Computed at read time
79
+ * and never persisted (PRD §7), so an ignored concept resurfaces without its
80
+ * history being rewritten.
81
+ */
82
+ export function decayedScore(score, nextReview, now) {
83
+ if (!nextReview)
84
+ return score;
85
+ const overdueMs = now.getTime() - Date.parse(nextReview);
86
+ if (!Number.isFinite(overdueMs) || overdueMs <= 0)
87
+ return score;
88
+ const weeks = Math.floor(overdueMs / (7 * MS_PER_DAY));
89
+ if (weeks <= 0)
90
+ return score;
91
+ const decayed = score - DECAY_PER_WEEK * weeks;
92
+ // The floor must never *raise* a score that was already below it.
93
+ return Number((decayed < DECAY_FLOOR ? Math.min(score, DECAY_FLOOR) : decayed).toFixed(6));
94
+ }
95
+ export function isKnown(state) {
96
+ return state.score >= MASTERY_THRESHOLD && state.reps >= MIN_REPS_FOR_KNOWN;
97
+ }
98
+ export function isDue(nextReview, now) {
99
+ if (!nextReview)
100
+ return false;
101
+ const t = Date.parse(nextReview);
102
+ return Number.isFinite(t) && t <= now.getTime();
103
+ }
104
+ /**
105
+ * Difficulty escalation (phase-1 decision G3). Tier belongs to the *question*,
106
+ * not the concept: a fundamentals concept gets asked at tier 4 of someone who
107
+ * keeps nailing it, which is how "never ask the same question twice" is honored
108
+ * without running out of concepts.
109
+ */
110
+ export function nextTierToAsk(input) {
111
+ const { conceptTier, lastDifficulty, lastGrade, score = 0 } = input;
112
+ if (lastDifficulty == null || lastGrade == null) {
113
+ return clamp(Math.round(conceptTier), 1, 5);
114
+ }
115
+ if (lastGrade >= 4 && score >= MASTERY_THRESHOLD)
116
+ return clamp(lastDifficulty + 1, 1, 5);
117
+ if (lastGrade <= 2)
118
+ return clamp(lastDifficulty - 1, 1, 5);
119
+ return clamp(lastDifficulty, 1, 5);
120
+ }
121
+ /** Median tier of what the learner already knows (phase-1 decision G4). */
122
+ export function suggestedTier(knownTiers) {
123
+ if (knownTiers.length === 0)
124
+ return 1;
125
+ const sorted = [...knownTiers].sort((a, b) => a - b);
126
+ const mid = Math.floor(sorted.length / 2);
127
+ const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
128
+ return clamp(Math.round(median), 1, 5);
129
+ }
130
+ //# sourceMappingURL=srs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srs.js","sourceRoot":"","sources":["../src/srs.ts"],"names":[],"mappings":"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"}
package/dist/store.js ADDED
@@ -0,0 +1,122 @@
1
+ import { applyGrade, initialMastery, SCORE_WINDOW } from './srs.js';
2
+ export function conceptBySlug(db, slug) {
3
+ return db.prepare('SELECT * FROM concepts WHERE slug = ?').get(slug);
4
+ }
5
+ export function allConceptSlugs(db) {
6
+ return db.prepare('SELECT id, slug, domain FROM concepts').all();
7
+ }
8
+ export function insertConcept(db, c) {
9
+ db.prepare(`INSERT INTO concepts (slug, name, domain, description, tier, source)
10
+ VALUES (?, ?, ?, ?, ?, ?)`).run(c.slug, c.name, c.domain, c.description ?? null, c.tier, c.source ?? 'llm');
11
+ return conceptBySlug(db, c.slug);
12
+ }
13
+ export function masteryFor(db, conceptId) {
14
+ const row = db.prepare('SELECT * FROM mastery WHERE concept_id = ?').get(conceptId);
15
+ if (!row)
16
+ return initialMastery();
17
+ return {
18
+ score: row.score,
19
+ ease: row.ease,
20
+ interval_d: row.interval_d,
21
+ reps: row.reps,
22
+ last_seen: row.last_seen,
23
+ next_review: row.next_review,
24
+ };
25
+ }
26
+ export function writeMastery(db, conceptId, state) {
27
+ db.prepare(`INSERT INTO mastery (concept_id, score, ease, interval_d, reps, last_seen, next_review)
28
+ VALUES (@id, @score, @ease, @interval_d, @reps, @last_seen, @next_review)
29
+ ON CONFLICT(concept_id) DO UPDATE SET
30
+ score = excluded.score, ease = excluded.ease, interval_d = excluded.interval_d,
31
+ reps = excluded.reps, last_seen = excluded.last_seen, next_review = excluded.next_review`).run({ id: conceptId, ...state });
32
+ }
33
+ /** Chronological grades, oldest first, windowed to what the score actually uses. */
34
+ export function gradesFor(db, conceptId, limit = SCORE_WINDOW) {
35
+ const rows = db
36
+ .prepare('SELECT grade FROM attempts WHERE concept_id = ? ORDER BY id DESC LIMIT ?')
37
+ .all(conceptId, limit);
38
+ return rows.map((r) => r.grade).reverse();
39
+ }
40
+ export function lastAttempt(db, conceptId) {
41
+ return db
42
+ .prepare('SELECT grade, difficulty, ts FROM attempts WHERE concept_id = ? ORDER BY id DESC LIMIT 1')
43
+ .get(conceptId);
44
+ }
45
+ export function recordAttemptRow(db, a) {
46
+ db.prepare(`INSERT INTO attempts (concept_id, session_id, question, answer, grade, difficulty, feedback)
47
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(a.conceptId, a.sessionId, a.question, a.answer, a.grade, a.difficulty, a.feedback);
48
+ }
49
+ /** Applies one grade end to end: attempt row, then recomputed SM-2 state. */
50
+ export function gradeConcept(db, input) {
51
+ const before = masteryFor(db, input.conceptId);
52
+ recordAttemptRow(db, input);
53
+ const grades = gradesFor(db, input.conceptId);
54
+ const after = applyGrade({ state: before, grade: input.grade, grades, now: input.now });
55
+ writeMastery(db, input.conceptId, after);
56
+ return after;
57
+ }
58
+ export function logSessionConcept(db, sessionId, conceptId, context) {
59
+ db.prepare(`INSERT INTO session_concepts (session_id, concept_id, context)
60
+ VALUES (?, ?, ?)
61
+ ON CONFLICT(session_id, concept_id) DO UPDATE SET
62
+ context = COALESCE(excluded.context, session_concepts.context)`).run(sessionId, conceptId, context);
63
+ }
64
+ export function sessionConcepts(db, sessionId) {
65
+ return db
66
+ .prepare(`SELECT c.*, sc.context, sc.ts AS logged_at
67
+ FROM session_concepts sc JOIN concepts c ON c.id = sc.concept_id
68
+ WHERE sc.session_id = ?
69
+ ORDER BY sc.ts ASC, c.tier ASC, c.slug ASC`)
70
+ .all(sessionId);
71
+ }
72
+ export function newConceptsThisSession(db, sessionId) {
73
+ return db
74
+ .prepare(`SELECT count(*) n FROM session_concepts sc
75
+ JOIN concepts c ON c.id = sc.concept_id
76
+ WHERE sc.session_id = ? AND c.source = 'llm'`)
77
+ .get(sessionId).n;
78
+ }
79
+ export function lastAttemptAt(db, sessionId) {
80
+ const row = db
81
+ .prepare('SELECT ts FROM attempts WHERE session_id = ? ORDER BY id DESC LIMIT 1')
82
+ .get(sessionId);
83
+ return row?.ts ?? null;
84
+ }
85
+ export const PASSING_GRADE = 3;
86
+ export function gateRow(db, sessionId) {
87
+ return db.prepare('SELECT * FROM gates WHERE session_id = ?').get(sessionId);
88
+ }
89
+ function countAnswered(db, sessionId) {
90
+ const row = db
91
+ .prepare(`SELECT
92
+ count(DISTINCT a.concept_id) AS answered,
93
+ count(DISTINCT CASE WHEN a.grade >= ? THEN a.concept_id END) AS passedCount
94
+ FROM attempts a
95
+ JOIN session_concepts sc
96
+ ON sc.concept_id = a.concept_id AND sc.session_id = a.session_id
97
+ WHERE a.session_id = ?`)
98
+ .get(PASSING_GRADE, sessionId);
99
+ return row;
100
+ }
101
+ /**
102
+ * Recomputes and persists the gate for a session. `requiredHint` raises the bar
103
+ * (never lowers it) when new unmastered concepts show up mid-session.
104
+ */
105
+ export function syncGate(db, sessionId, config, opts = {}) {
106
+ const existing = gateRow(db, sessionId);
107
+ const { answered, passedCount } = countAnswered(db, sessionId);
108
+ const required = Math.max(existing?.required ?? 0, opts.requiredHint ?? 0);
109
+ // Keep a repo once known: a later call from a different cwd must not blank it,
110
+ // or the git pre-commit hook loses the row it enforces against.
111
+ const repo = opts.repo ?? existing?.repo ?? null;
112
+ const needed = Math.ceil(required * config.pass_threshold);
113
+ const passed = required === 0 ? true : passedCount >= needed;
114
+ db.prepare(`INSERT INTO gates (session_id, mode, required, answered, passed, updated_at, repo)
115
+ VALUES (?, ?, ?, ?, ?, datetime('now'), ?)
116
+ ON CONFLICT(session_id) DO UPDATE SET
117
+ mode = excluded.mode, required = excluded.required,
118
+ answered = excluded.answered, passed = excluded.passed,
119
+ updated_at = excluded.updated_at, repo = excluded.repo`).run(sessionId, config.mode, required, answered, passed ? 1 : 0, repo);
120
+ return { mode: config.mode, required, answered, passed, pass_threshold: config.pass_threshold, repo };
121
+ }
122
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { z } from 'zod';
2
+ import path from 'node:path';
3
+ import { loadConfig, writeConfigFile, REPO_CONFIG_FILE } from '../config.js';
4
+ import { CWD_HINT } from './types.js';
5
+ export const getConfig = {
6
+ name: 'get_config',
7
+ title: 'Get config',
8
+ description: 'The effective Eklavya config: global ~/.eklavya/config.json merged with the repo .eklavya.json, repo winning.',
9
+ inputSchema: { cwd: z.string().optional().describe(CWD_HINT) },
10
+ handler: (args) => {
11
+ const resolved = loadConfig(args.cwd);
12
+ return {
13
+ config: resolved.config,
14
+ global_path: resolved.globalPath,
15
+ repo_path: resolved.repoPath,
16
+ repo_root: resolved.repoRoot,
17
+ };
18
+ },
19
+ };
20
+ export const setConfig = {
21
+ name: 'set_config',
22
+ title: 'Set config',
23
+ description: 'Update Eklavya config. Scope "global" writes ~/.eklavya/config.json; scope "repo" writes .eklavya.json at the repo root, which is how a team lead pins enforced mode for one project.',
24
+ inputSchema: {
25
+ scope: z.enum(['global', 'repo']).optional().describe('Defaults to global.'),
26
+ cwd: z.string().optional().describe(CWD_HINT),
27
+ mode: z.enum(['ambient', 'enforced', 'off']).optional(),
28
+ pass_threshold: z.number().min(0).max(1).optional(),
29
+ max_questions_per_task: z.number().int().min(1).max(10).optional(),
30
+ min_minutes_between_quizzes: z.number().int().min(0).optional(),
31
+ max_new_concepts_per_session: z.number().int().min(0).max(50).optional(),
32
+ quiet: z.boolean().optional(),
33
+ domains_enabled: z.array(z.string()).optional(),
34
+ },
35
+ handler: (args) => {
36
+ const scope = args.scope ?? 'global';
37
+ const cwd = args.cwd;
38
+ const resolved = loadConfig(cwd);
39
+ const patch = {};
40
+ for (const key of [
41
+ 'mode',
42
+ 'pass_threshold',
43
+ 'max_questions_per_task',
44
+ 'min_minutes_between_quizzes',
45
+ 'max_new_concepts_per_session',
46
+ 'quiet',
47
+ 'domains_enabled',
48
+ ]) {
49
+ if (args[key] !== undefined)
50
+ patch[key] = args[key];
51
+ }
52
+ if (Object.keys(patch).length === 0) {
53
+ return { error: 'nothing_to_set', detail: 'Pass at least one setting to change.' };
54
+ }
55
+ let target;
56
+ if (scope === 'repo') {
57
+ const root = resolved.repoRoot;
58
+ if (!root) {
59
+ return {
60
+ error: 'no_repo_root',
61
+ detail: 'No git repository found from this directory, so there is nowhere to write .eklavya.json.',
62
+ };
63
+ }
64
+ target = resolved.repoPath ?? path.join(root, REPO_CONFIG_FILE);
65
+ }
66
+ else {
67
+ target = resolved.globalPath;
68
+ }
69
+ writeConfigFile(target, patch);
70
+ return { written_to: target, scope, config: loadConfig(cwd).config };
71
+ },
72
+ };
73
+ //# sourceMappingURL=config_tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config_tools.js","sourceRoot":"","sources":["../../src/tools/config_tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { z } from 'zod';
2
+ import { decayedScore, isKnown } from '../srs.js';
3
+ const NODE_CAP = 200;
4
+ /**
5
+ * Prerequisite-first ordering (decision G10). Cycles are broken by tier then
6
+ * slug so the order is stable rather than dependent on insertion order.
7
+ */
8
+ function topoSort(nodes, edges) {
9
+ const indegree = new Map(nodes.map((n) => [n.id, 0]));
10
+ const out = new Map(nodes.map((n) => [n.id, []]));
11
+ for (const e of edges) {
12
+ if (!indegree.has(e.from) || !indegree.has(e.to))
13
+ continue;
14
+ out.get(e.from).push(e.to);
15
+ indegree.set(e.to, indegree.get(e.to) + 1);
16
+ }
17
+ const byId = new Map(nodes.map((n) => [n.id, n]));
18
+ const rank = (id) => {
19
+ const n = byId.get(id);
20
+ return `${n.tier}-${n.slug}`;
21
+ };
22
+ const ready = nodes.filter((n) => indegree.get(n.id) === 0).map((n) => n.id);
23
+ ready.sort((a, b) => rank(a).localeCompare(rank(b)));
24
+ const ordered = [];
25
+ while (ready.length > 0) {
26
+ const id = ready.shift();
27
+ ordered.push(id);
28
+ for (const next of out.get(id) ?? []) {
29
+ indegree.set(next, indegree.get(next) - 1);
30
+ if (indegree.get(next) === 0) {
31
+ ready.push(next);
32
+ ready.sort((a, b) => rank(a).localeCompare(rank(b)));
33
+ }
34
+ }
35
+ }
36
+ // Anything left sits in a cycle; append it deterministically rather than drop it.
37
+ const placed = new Set(ordered);
38
+ const leftovers = nodes.filter((n) => !placed.has(n.id)).sort((a, b) => rank(a.id).localeCompare(rank(b.id)));
39
+ return [...ordered, ...leftovers.map((n) => n.id)];
40
+ }
41
+ export const getConceptGraph = {
42
+ name: 'get_concept_graph',
43
+ title: 'Get concept graph',
44
+ description: 'Concepts and edges for a domain in prerequisite order — the order to teach them in. Optionally annotated with this learner\'s mastery.',
45
+ inputSchema: {
46
+ domain: z.string().describe('e.g. "web-auth", "react", "git", "node-backend"'),
47
+ include_mastery: z.boolean().optional(),
48
+ unmastered_only: z.boolean().optional().describe('Drop concepts the learner already knows.'),
49
+ },
50
+ handler: (args, { db }) => {
51
+ const now = new Date();
52
+ const rows = db
53
+ .prepare(`SELECT c.id, c.slug, c.name, c.tier,
54
+ m.score, m.reps, m.next_review,
55
+ (SELECT count(*) FROM attempts a WHERE a.concept_id = c.id) AS attempts
56
+ FROM concepts c
57
+ LEFT JOIN mastery m ON m.concept_id = c.id
58
+ WHERE c.domain = ?`)
59
+ .all(args.domain);
60
+ if (rows.length === 0) {
61
+ return { domain: args.domain, concepts: [], edges: [], truncated: false };
62
+ }
63
+ const ids = rows.map((r) => r.id);
64
+ const placeholders = ids.map(() => '?').join(',');
65
+ const edgeRows = db
66
+ .prepare(`SELECT from_concept, to_concept, relation FROM edges
67
+ WHERE from_concept IN (${placeholders}) AND to_concept IN (${placeholders})`)
68
+ .all(...ids, ...ids);
69
+ const order = topoSort(rows.map((r) => ({ id: r.id, slug: r.slug, tier: r.tier })), edgeRows
70
+ .filter((e) => e.relation === 'prerequisite_of')
71
+ .map((e) => ({ from: e.from_concept, to: e.to_concept })));
72
+ const byId = new Map(rows.map((r) => [r.id, r]));
73
+ const slugOf = new Map(rows.map((r) => [r.id, r.slug]));
74
+ let nodes = order.map((id) => {
75
+ const r = byId.get(id);
76
+ const node = { slug: r.slug, name: r.name, tier: r.tier };
77
+ if (args.include_mastery) {
78
+ const score = decayedScore(r.score ?? 0, r.next_review, now);
79
+ node.score = Number(score.toFixed(2));
80
+ node.known = r.attempts > 0 && isKnown({ score, reps: r.reps ?? 0 });
81
+ node.attempts = r.attempts;
82
+ }
83
+ return node;
84
+ });
85
+ if (args.unmastered_only) {
86
+ const now2 = new Date();
87
+ const knownIds = new Set(rows
88
+ .filter((r) => r.attempts > 0 &&
89
+ isKnown({ score: decayedScore(r.score ?? 0, r.next_review, now2), reps: r.reps ?? 0 }))
90
+ .map((r) => r.slug));
91
+ nodes = nodes.filter((n) => !knownIds.has(n.slug));
92
+ }
93
+ const truncated = nodes.length > NODE_CAP;
94
+ return {
95
+ domain: args.domain,
96
+ concepts: nodes.slice(0, NODE_CAP),
97
+ edges: edgeRows
98
+ .map((e) => ({ from: slugOf.get(e.from_concept), to: slugOf.get(e.to_concept), relation: e.relation }))
99
+ .slice(0, NODE_CAP * 2),
100
+ truncated,
101
+ };
102
+ },
103
+ };
104
+ //# sourceMappingURL=get_concept_graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_concept_graph.js","sourceRoot":"","sources":["../../src/tools/get_concept_graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import { loadConfig } from '../config.js';
3
+ import { resolveSessionId } from '../session.js';
4
+ import { syncGate } from '../store.js';
5
+ import { CWD_HINT, SESSION_HINT } from './types.js';
6
+ export const getGateStatus = {
7
+ name: 'get_gate_status',
8
+ title: 'Get gate status',
9
+ description: 'Whether this session has passed its quiz gate. In enforced mode commits stay blocked until it has.',
10
+ inputSchema: {
11
+ session_id: z.string().optional().describe(SESSION_HINT),
12
+ cwd: z.string().optional().describe(CWD_HINT),
13
+ },
14
+ handler: (args, { db }) => {
15
+ const { config, repoRoot } = loadConfig(args.cwd);
16
+ const sessionId = resolveSessionId(db, args.session_id);
17
+ return { session_id: sessionId, ...syncGate(db, sessionId, config, { repo: repoRoot }) };
18
+ },
19
+ };
20
+ //# sourceMappingURL=get_gate_status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_gate_status.js","sourceRoot":"","sources":["../../src/tools/get_gate_status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAElE,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,iBAAiB;IACxB,WAAW,EACT,oGAAoG;IACtG,WAAW,EAAE;QACX,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC9C;IACD,OAAO,EAAE,CAAC,IAA2C,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC/D,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC3F,CAAC;CACF,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { z } from 'zod';
2
+ import { loadConfig, isDomainEnabled } from '../config.js';
3
+ import { decayedScore, isKnown, isDue, nextTierToAsk, suggestedTier } from '../srs.js';
4
+ import { CWD_HINT } from './types.js';
5
+ const LIST_CAP = 8;
6
+ const WEAK_THRESHOLD = 0.5;
7
+ export const getLearnerProfile = {
8
+ name: 'get_learner_profile',
9
+ title: 'Get learner profile',
10
+ description: 'What this developer already knows. Call before teaching or quizzing so you never ask about a mastered concept. Returns mode, per-domain counts, weak concepts, what is due for review, and the tier to pitch at.',
11
+ inputSchema: {
12
+ domain: z.string().optional().describe('Restrict to one domain, e.g. "web-auth".'),
13
+ cwd: z.string().optional().describe(CWD_HINT),
14
+ },
15
+ handler: (args, { db }) => {
16
+ const now = new Date();
17
+ const { config } = loadConfig(args.cwd);
18
+ const rows = db
19
+ .prepare(`SELECT c.id, c.slug, c.domain, c.tier,
20
+ m.score, m.reps, m.next_review,
21
+ (SELECT count(*) FROM attempts a WHERE a.concept_id = c.id) AS attempts,
22
+ (SELECT a.grade FROM attempts a WHERE a.concept_id = c.id ORDER BY a.id DESC LIMIT 1) AS last_grade,
23
+ (SELECT a.difficulty FROM attempts a WHERE a.concept_id = c.id ORDER BY a.id DESC LIMIT 1) AS last_difficulty
24
+ FROM concepts c
25
+ LEFT JOIN mastery m ON m.concept_id = c.id
26
+ ${args.domain ? 'WHERE c.domain = ?' : ''}`)
27
+ .all(...(args.domain ? [args.domain] : []));
28
+ const domains = new Map();
29
+ const knownTiers = [];
30
+ const weak = [];
31
+ const due = [];
32
+ for (const row of rows) {
33
+ if (!isDomainEnabled(config, row.domain))
34
+ continue;
35
+ const bucket = domains.get(row.domain) ?? { domain: row.domain, known: 0, learning: 0, unseen: 0 };
36
+ domains.set(row.domain, bucket);
37
+ if (row.attempts === 0) {
38
+ bucket.unseen += 1;
39
+ continue;
40
+ }
41
+ // Decay is a read-time view only — never written back (PRD §7).
42
+ const score = decayedScore(row.score ?? 0, row.next_review, now);
43
+ const reps = row.reps ?? 0;
44
+ if (isKnown({ score, reps })) {
45
+ bucket.known += 1;
46
+ knownTiers.push(row.tier);
47
+ }
48
+ else {
49
+ bucket.learning += 1;
50
+ }
51
+ if (score < WEAK_THRESHOLD)
52
+ weak.push({ slug: row.slug, score: Number(score.toFixed(2)), tier: row.tier });
53
+ if (isDue(row.next_review, now)) {
54
+ due.push({
55
+ slug: row.slug,
56
+ tier: row.tier,
57
+ tier_to_ask: nextTierToAsk({
58
+ conceptTier: row.tier,
59
+ lastDifficulty: row.last_difficulty,
60
+ lastGrade: row.last_grade,
61
+ score,
62
+ }),
63
+ });
64
+ }
65
+ }
66
+ weak.sort((a, b) => a.score - b.score);
67
+ due.sort((a, b) => b.tier - a.tier);
68
+ return {
69
+ mode: config.mode,
70
+ domains: [...domains.values()].sort((a, b) => a.domain.localeCompare(b.domain)),
71
+ weak: weak.slice(0, LIST_CAP).map((w) => w.slug),
72
+ due_for_review: due.slice(0, LIST_CAP),
73
+ suggested_tier: suggestedTier(knownTiers),
74
+ truncated: weak.length > LIST_CAP || due.length > LIST_CAP,
75
+ };
76
+ },
77
+ };
78
+ //# sourceMappingURL=get_learner_profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_learner_profile.js","sourceRoot":"","sources":["../../src/tools/get_learner_profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAEpD,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,MAAM,cAAc,GAAG,GAAG,CAAC;AAe3B,MAAM,CAAC,MAAM,iBAAiB,GAAY;IACxC,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EACT,kNAAkN;IACpN,WAAW,EAAE;QACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QAClF,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC9C;IACD,OAAO,EAAE,CAAC,IAAuC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC3D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAExC,MAAM,IAAI,GAAG,EAAE;aACZ,OAAO,CACN;;;;;;;WAOG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7C;aACA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAU,CAAC;QAEvD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA+E,CAAC;QACvG,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,IAAI,GAAoD,EAAE,CAAC;QACjE,MAAM,GAAG,GAA0D,EAAE,CAAC;QAEtE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC;gBAAE,SAAS;YAEnD,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACtF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAEhC,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YAED,gEAAgE;YAChE,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YACjE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;YAE3B,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;gBAClB,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;YACvB,CAAC;YAED,IAAI,KAAK,GAAG,cAAc;gBAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAE3G,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CAAC;oBACP,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,WAAW,EAAE,aAAa,CAAC;wBACzB,WAAW,EAAE,GAAG,CAAC,IAAI;wBACrB,cAAc,EAAE,GAAG,CAAC,eAAe;wBACnC,SAAS,EAAE,GAAG,CAAC,UAAU;wBACzB,KAAK;qBACN,CAAC;iBACH,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAEpC,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/E,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAChD,cAAc,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;YACtC,cAAc,EAAE,aAAa,CAAC,UAAU,CAAC;YACzC,SAAS,EAAE,IAAI,CAAC,MAAM,GAAG,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,QAAQ;SAC3D,CAAC;IACJ,CAAC;CACF,CAAC"}