eklavya 1.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.
- package/README.md +69 -0
- package/dist/ask.js +58 -0
- package/dist/ask.js.map +1 -0
- package/dist/assets/dashboard.html +407 -0
- package/dist/assets/tokens.css +135 -0
- package/dist/assets/tutor-skill.md +345 -0
- package/dist/cli.js +223 -0
- package/dist/cli.js.map +1 -0
- package/dist/concurrency.js +41 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config.js +158 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard.js +214 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/db.js +26 -0
- package/dist/db.js.map +1 -0
- package/dist/hooks/checkpoint-quiz.js +152 -0
- package/dist/hooks/checkpoint-quiz.js.map +1 -0
- package/dist/hooks/lib.js +177 -0
- package/dist/hooks/lib.js.map +1 -0
- package/dist/hooks/pre-tool-gate.js +53 -0
- package/dist/hooks/pre-tool-gate.js.map +1 -0
- package/dist/hooks/session-start.js +128 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/stop-quiz-check.js +147 -0
- package/dist/hooks/stop-quiz-check.js.map +1 -0
- package/dist/install.js +313 -0
- package/dist/install.js.map +1 -0
- package/dist/mcq.js +34 -0
- package/dist/mcq.js.map +1 -0
- package/dist/migrate.js +45 -0
- package/dist/migrate.js.map +1 -0
- package/dist/migrations/001_init.sql +71 -0
- package/dist/migrations/002_stop_markers.sql +14 -0
- package/dist/migrations/003_gate_repo.sql +5 -0
- package/dist/migrations/004_attempt_outcome.sql +15 -0
- package/dist/migrations/005_session_concept_origin.sql +24 -0
- package/dist/migrations/006_attempt_format.sql +31 -0
- package/dist/migrations/007_checkpoints.sql +23 -0
- package/dist/migrations/008_difficulty_levels.sql +57 -0
- package/dist/paths.js +25 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugin/.claude-plugin/marketplace.json +14 -0
- package/dist/plugin/.claude-plugin/plugin.json +17 -0
- package/dist/plugin/.mcp.json +11 -0
- package/dist/plugin/agents/tutor.md +63 -0
- package/dist/plugin/cli/eklavya-gate +67 -0
- package/dist/plugin/hooks/hooks.json +57 -0
- package/dist/plugin/hooks/run.mjs +169 -0
- package/dist/plugin/scripts/install-git-hook.sh +81 -0
- package/dist/plugin/skills/gate/SKILL.md +17 -0
- package/dist/plugin/skills/learn/SKILL.md +28 -0
- package/dist/plugin/skills/level/SKILL.md +53 -0
- package/dist/plugin/skills/mode/SKILL.md +56 -0
- package/dist/plugin/skills/progress/SKILL.md +72 -0
- package/dist/plugin/skills/quiz/SKILL.md +31 -0
- package/dist/plugin/skills/setup/SKILL.md +49 -0
- package/dist/plugin/skills/tutor/SKILL.md +345 -0
- package/dist/seed/git.json +49 -0
- package/dist/seed/node-backend.json +43 -0
- package/dist/seed/react.json +46 -0
- package/dist/seed/web-auth.json +78 -0
- package/dist/seed.js +112 -0
- package/dist/seed.js.map +1 -0
- package/dist/server.js +52 -0
- package/dist/server.js.map +1 -0
- package/dist/session.js +24 -0
- package/dist/session.js.map +1 -0
- package/dist/slug.js +75 -0
- package/dist/slug.js.map +1 -0
- package/dist/srs.js +206 -0
- package/dist/srs.js.map +1 -0
- package/dist/store.js +450 -0
- package/dist/store.js.map +1 -0
- package/dist/tools/config_tools.js +119 -0
- package/dist/tools/config_tools.js.map +1 -0
- package/dist/tools/get_concept_graph.js +104 -0
- package/dist/tools/get_concept_graph.js.map +1 -0
- package/dist/tools/get_gate_status.js +20 -0
- package/dist/tools/get_gate_status.js.map +1 -0
- package/dist/tools/get_learner_profile.js +142 -0
- package/dist/tools/get_learner_profile.js.map +1 -0
- package/dist/tools/get_session_quiz_plan.js +393 -0
- package/dist/tools/get_session_quiz_plan.js.map +1 -0
- package/dist/tools/index.js +45 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/log_session_concepts.js +103 -0
- package/dist/tools/log_session_concepts.js.map +1 -0
- package/dist/tools/record_attempt.js +158 -0
- package/dist/tools/record_attempt.js.map +1 -0
- package/dist/tools/types.js +4 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/upsert_concepts.js +89 -0
- package/dist/tools/upsert_concepts.js.map +1 -0
- package/package.json +66 -0
|
@@ -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,142 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { loadConfig, isDomainEnabled } from '../config.js';
|
|
3
|
+
import { clampToLevel, decayedScore, isKnown, isDue, nextTierToAsk, suggestedTier } from '../srs.js';
|
|
4
|
+
import { levelStanding } from '../store.js';
|
|
5
|
+
import { CWD_HINT } from './types.js';
|
|
6
|
+
const LIST_CAP = 8;
|
|
7
|
+
/** Mastered concepts are the "don't ask this" list, so it gets more room than the rest. */
|
|
8
|
+
const KNOWN_CAP = 30;
|
|
9
|
+
const WEAK_THRESHOLD = 0.5;
|
|
10
|
+
export const getLearnerProfile = {
|
|
11
|
+
name: 'get_learner_profile',
|
|
12
|
+
title: 'Get learner profile',
|
|
13
|
+
description: 'What this developer already knows. Call before teaching or quizzing so you never ask about a mastered concept. Returns mode, per-domain counts, the concepts already mastered, weak concepts, what is due for review, the tier to pitch at, and this project\'s difficulty level with the progress toward the next one. Every tier here is already clamped to that level. Also returns the three things a progress report needs and mastery counts cannot give: `projects` (per-repo answered/passed, so a report can say *which* codebase), `recent_concepts` (what was logged, with the context line naming the real code it came from) and `skipped` (declined or blanked, the actionable backlog).',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
domain: z.string().optional().describe('Restrict to one domain, e.g. "web-auth".'),
|
|
16
|
+
cwd: z.string().optional().describe(CWD_HINT),
|
|
17
|
+
},
|
|
18
|
+
handler: (args, { db }) => {
|
|
19
|
+
const now = new Date();
|
|
20
|
+
const { config, repoRoot } = loadConfig(args.cwd);
|
|
21
|
+
// Tiers reported here are what the tutor calibrates against, so they are
|
|
22
|
+
// clamped to the project's band exactly as the planner clamps them. An
|
|
23
|
+
// unclamped `suggested_tier` is how a learner on `easy` gets pitched at 3.
|
|
24
|
+
const standing = levelStanding(db, config, repoRoot);
|
|
25
|
+
const rows = db
|
|
26
|
+
.prepare(`SELECT c.id, c.slug, c.domain, c.tier,
|
|
27
|
+
m.score, m.reps, m.next_review,
|
|
28
|
+
(SELECT count(*) FROM attempts a WHERE a.concept_id = c.id) AS attempts,
|
|
29
|
+
(SELECT a.grade FROM attempts a WHERE a.concept_id = c.id ORDER BY a.id DESC LIMIT 1) AS last_grade,
|
|
30
|
+
(SELECT a.difficulty FROM attempts a WHERE a.concept_id = c.id ORDER BY a.id DESC LIMIT 1) AS last_difficulty
|
|
31
|
+
FROM concepts c
|
|
32
|
+
LEFT JOIN mastery m ON m.concept_id = c.id
|
|
33
|
+
${args.domain ? 'WHERE c.domain = ?' : ''}`)
|
|
34
|
+
.all(...(args.domain ? [args.domain] : []));
|
|
35
|
+
const domains = new Map();
|
|
36
|
+
const knownTiers = [];
|
|
37
|
+
const weak = [];
|
|
38
|
+
const known = [];
|
|
39
|
+
const due = [];
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
if (!isDomainEnabled(config, row.domain))
|
|
42
|
+
continue;
|
|
43
|
+
const bucket = domains.get(row.domain) ?? { domain: row.domain, known: 0, learning: 0, unseen: 0 };
|
|
44
|
+
domains.set(row.domain, bucket);
|
|
45
|
+
if (row.attempts === 0) {
|
|
46
|
+
bucket.unseen += 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Decay is a read-time view only — never written back (PRD §7).
|
|
50
|
+
const score = decayedScore(row.score ?? 0, row.next_review, now);
|
|
51
|
+
const reps = row.reps ?? 0;
|
|
52
|
+
if (isKnown({ score, reps })) {
|
|
53
|
+
bucket.known += 1;
|
|
54
|
+
knownTiers.push(row.tier);
|
|
55
|
+
// Named, not just counted: the tutor is told never to re-ask a mastered
|
|
56
|
+
// concept, which it can only honor if it is told which ones those are.
|
|
57
|
+
known.push({ slug: row.slug, score });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
bucket.learning += 1;
|
|
61
|
+
}
|
|
62
|
+
if (score < WEAK_THRESHOLD)
|
|
63
|
+
weak.push({ slug: row.slug, score: Number(score.toFixed(2)), tier: row.tier });
|
|
64
|
+
if (isDue(row.next_review, now)) {
|
|
65
|
+
due.push({
|
|
66
|
+
slug: row.slug,
|
|
67
|
+
tier: row.tier,
|
|
68
|
+
tier_to_ask: clampToLevel(nextTierToAsk({
|
|
69
|
+
conceptTier: row.tier,
|
|
70
|
+
lastDifficulty: row.last_difficulty,
|
|
71
|
+
lastGrade: row.last_grade,
|
|
72
|
+
score,
|
|
73
|
+
}), standing.level),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const domainFilter = args.domain ? 'AND c.domain = ?' : '';
|
|
78
|
+
const domainArg = args.domain ? [args.domain] : [];
|
|
79
|
+
// Three things mastery counts cannot answer, and a progress report must:
|
|
80
|
+
// which project this happened on, what was actually learned there, and
|
|
81
|
+
// what was ducked. All three are already in the schema; nothing read them.
|
|
82
|
+
const projects = db
|
|
83
|
+
.prepare(`SELECT NULLIF(trim(COALESCE(a.repo, '')), '') AS repo, count(*) AS answers,
|
|
84
|
+
sum(CASE WHEN a.grade >= 3 THEN 1 ELSE 0 END) AS passed,
|
|
85
|
+
max(a.ts) AS last_active
|
|
86
|
+
FROM attempts a JOIN concepts c ON c.id = a.concept_id
|
|
87
|
+
WHERE 1 = 1 ${domainFilter}
|
|
88
|
+
GROUP BY repo
|
|
89
|
+
ORDER BY last_active DESC
|
|
90
|
+
LIMIT ?`)
|
|
91
|
+
.all(...domainArg, LIST_CAP);
|
|
92
|
+
// `session_concepts` has no repo of its own; the session's gate row carries
|
|
93
|
+
// it. LEFT JOIN because a session that never opened a gate still logged work.
|
|
94
|
+
const recent = db
|
|
95
|
+
.prepare(`SELECT c.slug AS slug, sc.context AS context, sc.ts AS ts, g.repo AS repo
|
|
96
|
+
FROM session_concepts sc
|
|
97
|
+
JOIN concepts c ON c.id = sc.concept_id
|
|
98
|
+
LEFT JOIN gates g ON g.session_id = sc.session_id
|
|
99
|
+
WHERE sc.context IS NOT NULL AND trim(sc.context) <> '' ${domainFilter}
|
|
100
|
+
ORDER BY sc.ts DESC
|
|
101
|
+
LIMIT ?`)
|
|
102
|
+
.all(...domainArg, LIST_CAP);
|
|
103
|
+
// A NULL outcome is a row written before migration 004, not a skip. `IN`
|
|
104
|
+
// excludes it: counting those as ducked would invent a backlog.
|
|
105
|
+
const skipped = db
|
|
106
|
+
.prepare(`SELECT c.slug AS slug, a.outcome AS outcome, a.repo AS repo, a.ts AS ts
|
|
107
|
+
FROM attempts a JOIN concepts c ON c.id = a.concept_id
|
|
108
|
+
WHERE a.outcome IN ('declined','dont_know') ${domainFilter}
|
|
109
|
+
ORDER BY a.ts DESC
|
|
110
|
+
LIMIT ?`)
|
|
111
|
+
.all(...domainArg, LIST_CAP);
|
|
112
|
+
weak.sort((a, b) => a.score - b.score);
|
|
113
|
+
due.sort((a, b) => b.tier - a.tier);
|
|
114
|
+
known.sort((a, b) => b.score - a.score);
|
|
115
|
+
return {
|
|
116
|
+
mode: config.mode,
|
|
117
|
+
domains: [...domains.values()].sort((a, b) => a.domain.localeCompare(b.domain)),
|
|
118
|
+
known: known.slice(0, KNOWN_CAP).map((k) => k.slug),
|
|
119
|
+
known_total: known.length,
|
|
120
|
+
weak: weak.slice(0, LIST_CAP).map((w) => w.slug),
|
|
121
|
+
due_for_review: due.slice(0, LIST_CAP),
|
|
122
|
+
projects,
|
|
123
|
+
recent_concepts: recent,
|
|
124
|
+
skipped,
|
|
125
|
+
suggested_tier: clampToLevel(suggestedTier(knownTiers), standing.level),
|
|
126
|
+
level: {
|
|
127
|
+
level: standing.level,
|
|
128
|
+
next: standing.next,
|
|
129
|
+
passed: standing.counts.passed,
|
|
130
|
+
needed: standing.needed.answers,
|
|
131
|
+
concepts: standing.counts.concepts,
|
|
132
|
+
needed_concepts: standing.needed.concepts,
|
|
133
|
+
accuracy: standing.accuracy,
|
|
134
|
+
min_accuracy: standing.needed.accuracy,
|
|
135
|
+
repo: standing.repo,
|
|
136
|
+
...(standing.pinned ? { pinned: true } : {}),
|
|
137
|
+
},
|
|
138
|
+
truncated: weak.length > LIST_CAP || due.length > LIST_CAP || known.length > KNOWN_CAP,
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
//# 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":"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"}
|