eklavya 1.9.4 → 1.10.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.
@@ -287,13 +287,15 @@ A pinned level (`pinned: true`) means someone set the band deliberately — an o
287
287
  Every plan item carries `ask_header`: one bracketed line naming the settings that asked the question.
288
288
 
289
289
  ```
290
- [ambient · concept · easy · tier 2 mechanism · q 1/3]
290
+ [mode: ambient · focus: concept · level: easy · tier: 2 mechanism · question: 1 of 3]
291
291
 
292
292
  Why is httpOnly set on the refresh cookie here but not on the access token?
293
293
  ```
294
294
 
295
- Mode, focus, level, what the tier is asking for, and — when the plan holds more
296
- than one — which question of how many this is.
295
+ Every part names its dial: mode, focus, level, what the tier is asking for, and
296
+ — when the plan holds more than one — which question of how many this is. The
297
+ labels are the point. Four bare values only read as a settings line to someone
298
+ who already knows there are four dials and what order they come in.
297
299
 
298
300
  **Print it as the first line of the `question` you pass to `AskUserQuestion`**, then a blank line, then the stem, exactly as given. It is the only thing on screen that explains why a question is pitched where it is — without it, a `concept`-focus question reads as vague and an `easy` question reads as shallow.
299
301
 
package/dist/dashboard.js CHANGED
@@ -3,19 +3,29 @@
3
3
  *
4
4
  * The terminal report has to fit in twenty lines, so it answers "what now?".
5
5
  * This answers "am I getting better?", which needs history, per-project
6
- * comparison and the full concept list — none of which fit in a paragraph.
6
+ * comparison, per-concept history and the full concept list — none of which fit
7
+ * in a paragraph.
7
8
  *
8
9
  * Deliberately a static page plus one JSON endpoint: no framework, no build
9
10
  * step, same rule the landing page follows. It binds to loopback only, which
10
11
  * is the whole security model — the data never leaves the machine, and that
11
12
  * is a promise the landing page makes on Eklavya's behalf.
13
+ *
14
+ * The endpoint ships mostly *flat rows* — every attempt, every logged context,
15
+ * one row per day — and lets the page derive the views. One aggregate query per
16
+ * card is a coupling to the layout: the sessions view, the streak, the per-day
17
+ * heatmap and the per-concept history are four readings of the same attempt
18
+ * rows, and adding a fifth view should not mean adding a fifth query. The whole
19
+ * payload for a heavy year of use is a few hundred kilobytes over loopback.
12
20
  */
13
21
  import http from 'node:http';
14
22
  import fs from 'node:fs';
15
23
  import path from 'node:path';
16
24
  import { fileURLToPath } from 'node:url';
17
- import { decayedScore, isDue, isKnown } from './srs.js';
18
- import { GLOBAL_PROJECT } from './store.js';
25
+ import { decayedScore, isDue, isKnown, MS_PER_DAY } from './srs.js';
26
+ import { GLOBAL_PROJECT, levelStanding, PASSING_GRADE } from './store.js';
27
+ import { loadConfig, DEFAULT_CONFIG } from './config.js';
28
+ import { dbPath } from './paths.js';
19
29
  const moduleDir = path.dirname(fileURLToPath(import.meta.url));
20
30
  /**
21
31
  * High, unassigned, and deliberately boring to collide with.
@@ -28,41 +38,80 @@ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
28
38
  * is as good a reason as any to remember it.)
29
39
  */
30
40
  const DEFAULT_PORT = 41729;
31
- /** Enough history to see a trend, short enough to stay one screen wide. */
32
- const TIMELINE_DAYS = 30;
41
+ /**
42
+ * How much history the per-day rows cover. A year, because the calendar heatmap
43
+ * shows half of one and the streak has to be able to run off the top of it —
44
+ * a thirty-day window would cap the longest streak at thirty.
45
+ */
46
+ const TIMELINE_DAYS = 365;
47
+ /**
48
+ * Cap on the attempt rows shipped for drill-down. Counts and per-day totals are
49
+ * aggregated in SQL over the whole table, so the cap can only ever shorten a
50
+ * history list, never make a number wrong.
51
+ */
52
+ const ATTEMPT_LIMIT = 2000;
53
+ /**
54
+ * Two timestamp shapes live in this database and both have to parse.
55
+ * `attempts.ts` defaults to SQLite's `2026-09-05 21:04:00` — UTC, no zone
56
+ * marker, which `Date.parse` would read as local time — while `next_review` is
57
+ * a JS ISO string that already carries its `Z`. Appending one unconditionally
58
+ * produced `...ZZ`, which parses to NaN, and every overdue count came out zero.
59
+ */
60
+ const parseTs = (s) => Date.parse(/([zZ]|[+-]\d\d:?\d\d)$/.test(s) ? s : s.replace(' ', 'T') + 'Z');
61
+ const days = (from, now) => {
62
+ if (from === null)
63
+ return null;
64
+ const t = parseTs(from);
65
+ return Number.isFinite(t) ? Math.floor((now.getTime() - t) / MS_PER_DAY) : null;
66
+ };
67
+ function readConfig() {
68
+ // A dashboard must open even if the config file is half-written or owned by
69
+ // another user; the defaults describe the same product.
70
+ try {
71
+ return loadConfig().config;
72
+ }
73
+ catch {
74
+ return DEFAULT_CONFIG;
75
+ }
76
+ }
33
77
  export function dashboardState(db) {
34
78
  const now = new Date();
35
- // One row per calendar day, split three ways. `grade >= 3` is the same pass
36
- // line the level ladder uses, so the chart and the promotion agree.
37
- const timeline = db
79
+ const config = readConfig();
80
+ // One row per calendar day per project, split three ways. `grade >= 3` is the
81
+ // same pass line the level ladder uses, so the chart and the promotion agree.
82
+ const daily = db
38
83
  .prepare(`SELECT date(ts) AS day,
39
84
  NULLIF(trim(COALESCE(repo, '')), '') AS repo,
40
- sum(CASE WHEN grade >= 3 THEN 1 ELSE 0 END) AS passed,
41
- sum(CASE WHEN grade < 3 AND (outcome IS NULL OR outcome = 'answered') THEN 1 ELSE 0 END) AS missed,
85
+ sum(CASE WHEN grade >= ? THEN 1 ELSE 0 END) AS passed,
86
+ sum(CASE WHEN grade < ? AND (outcome IS NULL OR outcome = 'answered') THEN 1 ELSE 0 END) AS missed,
42
87
  sum(CASE WHEN outcome IN ('declined','dont_know') THEN 1 ELSE 0 END) AS skipped
43
88
  FROM attempts
44
89
  WHERE ts >= date('now', ?)
45
90
  GROUP BY day, repo
46
91
  ORDER BY day`)
47
- .all(`-${TIMELINE_DAYS} days`);
92
+ .all(PASSING_GRADE, PASSING_GRADE, `-${TIMELINE_DAYS} days`);
48
93
  // A null repo is the pre-migration-003 bucket. It is kept, not dropped:
49
94
  // hiding those rows would silently shrink every total on the page.
50
95
  const projects = db
51
96
  .prepare(`SELECT NULLIF(trim(COALESCE(a.repo, '')), '') AS repo,
52
97
  count(*) AS answers,
53
- sum(CASE WHEN a.grade >= 3 THEN 1 ELSE 0 END) AS passed,
98
+ sum(CASE WHEN a.grade >= ? THEN 1 ELSE 0 END) AS passed,
54
99
  sum(CASE WHEN a.outcome IN ('declined','dont_know') THEN 1 ELSE 0 END) AS skipped,
55
100
  count(DISTINCT a.concept_id) AS concepts,
101
+ min(a.ts) AS first_active,
56
102
  max(a.ts) AS last_active
57
103
  FROM attempts a
58
104
  GROUP BY repo
59
105
  ORDER BY last_active DESC`)
60
- .all();
61
- const levels = new Map(db.prepare('SELECT repo, level, promoted_at FROM project_levels').all().map((r) => [r.repo, r]));
106
+ .all(PASSING_GRADE);
62
107
  const conceptRows = db
63
- .prepare(`SELECT c.slug, c.name, c.domain, c.tier,
64
- m.score, m.reps, m.next_review, m.last_seen,
108
+ .prepare(`SELECT c.id, c.slug, c.name, c.domain, c.description, c.tier, c.source,
109
+ m.score, m.ease, m.interval_d, m.reps, m.next_review, m.last_seen,
65
110
  (SELECT count(*) FROM attempts a WHERE a.concept_id = c.id) AS attempts,
111
+ (SELECT count(*) FROM attempts a WHERE a.concept_id = c.id AND a.grade >= ${PASSING_GRADE}) AS passed,
112
+ (SELECT count(*) FROM attempts a WHERE a.concept_id = c.id
113
+ AND a.outcome IN ('declined','dont_know')) AS skipped,
114
+ (SELECT min(a.ts) FROM attempts a WHERE a.concept_id = c.id) AS first_asked,
66
115
  (SELECT a.grade FROM attempts a WHERE a.concept_id = c.id ORDER BY a.id DESC LIMIT 1) AS last_grade,
67
116
  (SELECT sc.context FROM session_concepts sc
68
117
  WHERE sc.concept_id = c.id AND sc.context IS NOT NULL
@@ -71,93 +120,175 @@ export function dashboardState(db) {
71
120
  WHERE a.concept_id = c.id AND a.repo IS NOT NULL AND trim(a.repo) <> ''
72
121
  ORDER BY a.id DESC LIMIT 1) AS last_repo
73
122
  FROM concepts c
74
- LEFT JOIN mastery m ON m.concept_id = c.id`)
123
+ LEFT JOIN mastery m ON m.concept_id = c.id
124
+ ORDER BY c.domain, c.tier, c.name`)
75
125
  .all();
76
- const domains = new Map();
77
- const concepts = [];
78
- for (const row of conceptRows) {
79
- const bucket = domains.get(row.domain) ?? { domain: row.domain, mastered: 0, learning: 0, unseen: 0 };
80
- domains.set(row.domain, bucket);
81
- if (row.attempts === 0) {
82
- bucket.unseen += 1;
126
+ const byId = new Map(conceptRows.map((r) => [r.id, r.slug]));
127
+ // Edges travel as slugs so the page can link one concept card to another
128
+ // without carrying the id space around with it.
129
+ const edgeRows = db
130
+ .prepare('SELECT from_concept, to_concept, relation FROM edges')
131
+ .all();
132
+ const prereqs = new Map();
133
+ const unlocks = new Map();
134
+ const related = new Map();
135
+ const push = (m, k, v) => m.set(k, [...(m.get(k) ?? []), v]);
136
+ for (const e of edgeRows) {
137
+ const from = byId.get(e.from_concept);
138
+ const to = byId.get(e.to_concept);
139
+ if (!from || !to)
83
140
  continue;
141
+ if (e.relation === 'prerequisite_of') {
142
+ push(prereqs, to, from);
143
+ push(unlocks, from, to);
144
+ }
145
+ else {
146
+ push(related, from, to);
147
+ push(related, to, from);
84
148
  }
149
+ }
150
+ const domains = new Map();
151
+ const concepts = conceptRows.map((row) => {
152
+ const bucket = domains.get(row.domain) ?? {
153
+ domain: row.domain,
154
+ catalogue: 0,
155
+ touched: 0,
156
+ mastered: 0,
157
+ learning: 0,
158
+ unseen: 0,
159
+ };
160
+ domains.set(row.domain, bucket);
161
+ bucket.catalogue += 1;
85
162
  // Read-time decay only, exactly as the profile computes it — two surfaces
86
163
  // disagreeing about the same score is worse than either being wrong.
87
164
  const score = decayedScore(row.score ?? 0, row.next_review, now);
88
- const mastered = isKnown({ score, reps: row.reps ?? 0 });
89
- if (mastered)
90
- bucket.mastered += 1;
91
- else
92
- bucket.learning += 1;
93
- concepts.push({
165
+ const seen = row.attempts > 0;
166
+ const mastered = seen && isKnown({ score, reps: row.reps ?? 0 });
167
+ if (!seen)
168
+ bucket.unseen += 1;
169
+ else {
170
+ bucket.touched += 1;
171
+ if (mastered)
172
+ bucket.mastered += 1;
173
+ else
174
+ bucket.learning += 1;
175
+ }
176
+ const due = seen && isDue(row.next_review, now);
177
+ return {
94
178
  slug: row.slug,
95
179
  name: row.name,
96
180
  domain: row.domain,
181
+ description: row.description,
97
182
  tier: row.tier,
98
- score: Number(score.toFixed(2)),
183
+ source: row.source,
184
+ seen,
185
+ score: Number(score.toFixed(4)),
186
+ // The stored score, so a decayed concept can say how much of its score is
187
+ // rust rather than failure.
188
+ stored_score: Number((row.score ?? 0).toFixed(4)),
189
+ ease: row.ease ?? null,
190
+ interval_d: row.interval_d ?? 0,
99
191
  reps: row.reps ?? 0,
100
192
  attempts: row.attempts,
193
+ passed: row.passed,
194
+ skipped: row.skipped,
101
195
  last_grade: row.last_grade,
102
196
  mastered,
103
- due: isDue(row.next_review, now),
197
+ due,
198
+ overdue_days: due ? days(row.next_review, now) : null,
104
199
  next_review: row.next_review,
105
200
  last_seen: row.last_seen,
201
+ first_asked: row.first_asked,
106
202
  context: row.last_context,
107
203
  repo: row.last_repo,
108
- });
109
- }
110
- concepts.sort((a, b) => a.score - b.score);
111
- const skipped = db
112
- .prepare(`SELECT c.slug AS slug, c.name AS name, c.domain AS domain,
113
- a.outcome AS outcome, a.repo AS repo, a.ts AS ts, a.question AS question
204
+ prereqs: prereqs.get(row.slug) ?? [],
205
+ unlocks: unlocks.get(row.slug) ?? [],
206
+ related: related.get(row.slug) ?? [],
207
+ };
208
+ });
209
+ // Every graded response, newest first, with its question and the tutor's
210
+ // explanation. This is what makes a concept page a record of what you were
211
+ // actually asked rather than a score.
212
+ const attempts = db
213
+ .prepare(`SELECT a.id, c.slug, c.name, c.domain, a.session_id, a.question, a.answer, a.feedback,
214
+ a.grade, a.difficulty AS tier, a.outcome, a.format, a.options, a.ts,
215
+ NULLIF(trim(COALESCE(a.repo, '')), '') AS repo, a.level
114
216
  FROM attempts a JOIN concepts c ON c.id = a.concept_id
115
- WHERE a.outcome IN ('declined','dont_know')
116
- ORDER BY a.ts DESC`)
117
- .all();
118
- const recent = db
119
- .prepare(`SELECT c.slug AS slug, c.domain AS domain, sc.context AS context, sc.ts AS ts, g.repo AS repo
217
+ ORDER BY a.id DESC
218
+ LIMIT ?`)
219
+ .all(ATTEMPT_LIMIT);
220
+ // The context lines, which are what a concept means to *this* learner: the
221
+ // code that taught it. Grouped by session on the page.
222
+ const logged = db
223
+ .prepare(`SELECT sc.session_id, c.slug, c.name, c.domain, sc.context, sc.ts, sc.origin,
224
+ NULLIF(trim(COALESCE(g.repo, '')), '') AS repo
120
225
  FROM session_concepts sc
121
226
  JOIN concepts c ON c.id = sc.concept_id
122
227
  LEFT JOIN gates g ON g.session_id = sc.session_id
123
- WHERE sc.context IS NOT NULL AND trim(sc.context) <> ''
124
- ORDER BY sc.ts DESC
125
- LIMIT 40`)
228
+ ORDER BY sc.ts DESC`)
126
229
  .all();
127
- const totals = timeline.reduce((acc, d) => ({
128
- passed: acc.passed + d.passed,
129
- missed: acc.missed + d.missed,
130
- skipped: acc.skipped + d.skipped,
131
- }), { passed: 0, missed: 0, skipped: 0 });
132
230
  const allTime = db
133
231
  .prepare(`SELECT count(*) AS answers,
134
- sum(CASE WHEN grade >= 3 THEN 1 ELSE 0 END) AS passed,
135
- sum(CASE WHEN outcome IN ('declined','dont_know') THEN 1 ELSE 0 END) AS skipped
232
+ sum(CASE WHEN grade >= ? THEN 1 ELSE 0 END) AS passed,
233
+ sum(CASE WHEN grade < ? AND (outcome IS NULL OR outcome = 'answered') THEN 1 ELSE 0 END) AS missed,
234
+ sum(CASE WHEN outcome IN ('declined','dont_know') THEN 1 ELSE 0 END) AS skipped,
235
+ count(DISTINCT date(ts)) AS active_days,
236
+ min(ts) AS first_answer
136
237
  FROM attempts`)
137
- .get();
238
+ .get(PASSING_GRADE, PASSING_GRADE);
239
+ const sessionCount = db.prepare('SELECT count(DISTINCT session_id) AS n FROM session_concepts').get().n;
138
240
  return {
139
241
  generated_at: now.toISOString(),
242
+ db_path: dbPath(),
140
243
  timeline_days: TIMELINE_DAYS,
244
+ attempts_shown: attempts.length,
245
+ attempts_total: allTime.answers,
246
+ config: {
247
+ mode: config.mode,
248
+ focus: config.focus,
249
+ focus_topic: config.focus_topic,
250
+ cadence: config.cadence,
251
+ difficulty: config.difficulty,
252
+ pass_threshold: config.pass_threshold,
253
+ level_up_after: config.level_up_after,
254
+ level_up_accuracy: config.level_up_accuracy,
255
+ max_questions_per_task: config.max_questions_per_task,
256
+ },
141
257
  totals: {
142
258
  answers: allTime.answers,
143
259
  passed: allTime.passed ?? 0,
260
+ missed: allTime.missed ?? 0,
144
261
  skipped: allTime.skipped ?? 0,
145
262
  mastered: concepts.filter((c) => c.mastered).length,
146
263
  due: concepts.filter((c) => c.due).length,
147
- touched: concepts.length,
264
+ touched: concepts.filter((c) => c.seen).length,
148
265
  catalogue: conceptRows.length,
149
- window: totals,
266
+ sessions: sessionCount,
267
+ active_days: allTime.active_days,
268
+ first_answer: allTime.first_answer,
150
269
  },
151
- timeline,
152
- projects: projects.map((p) => ({
153
- ...p,
154
- level: levels.get(p.repo ?? GLOBAL_PROJECT)?.level ?? 'easy',
155
- promoted_at: levels.get(p.repo ?? GLOBAL_PROJECT)?.promoted_at ?? null,
156
- })),
157
- domains: [...domains.values()].sort((a, b) => b.mastered + b.learning - (a.mastered + a.learning)),
270
+ daily,
271
+ projects: projects.map((p) => {
272
+ // The promotion runway, said out loud rather than hinted at — the same
273
+ // numbers `/eklavya:progress` prints, so the two never disagree.
274
+ const standing = levelStanding(db, config, p.repo);
275
+ return {
276
+ ...p,
277
+ key: p.repo ?? GLOBAL_PROJECT,
278
+ level: standing.level,
279
+ pinned: standing.pinned,
280
+ promoted_at: standing.entered_at,
281
+ next_level: standing.next,
282
+ level_counts: standing.counts,
283
+ level_accuracy: standing.accuracy,
284
+ level_needed: standing.needed,
285
+ level_unmet: standing.unmet,
286
+ };
287
+ }),
288
+ domains: [...domains.values()].sort((a, b) => b.touched - a.touched || b.catalogue - a.catalogue),
158
289
  concepts,
159
- skipped,
160
- recent,
290
+ attempts,
291
+ logged,
161
292
  };
162
293
  }
163
294
  function send(res, status, type, body) {
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.9.4",
4
+ "version": "1.10.0",
5
5
  "description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
6
6
  "author": {
7
7
  "name": "Ajay Kumar"
@@ -43,7 +43,8 @@ Finish with **one** concrete next step: `/eklavya:quiz` when anything is due,
43
43
  mostly unseen.
44
44
 
45
45
  Then one line pointing at `eklavya dashboard` — the same data as a local page,
46
- with the history and the full concept list this report deliberately truncates.
46
+ with the history, the per-session record and every question ever asked on a
47
+ concept, all of which this report deliberately truncates.
47
48
  Mention it once, at the end; it is the deeper view, not a replacement for
48
49
  answering the question they just asked.
49
50
 
@@ -287,13 +287,15 @@ A pinned level (`pinned: true`) means someone set the band deliberately — an o
287
287
  Every plan item carries `ask_header`: one bracketed line naming the settings that asked the question.
288
288
 
289
289
  ```
290
- [ambient · concept · easy · tier 2 mechanism · q 1/3]
290
+ [mode: ambient · focus: concept · level: easy · tier: 2 mechanism · question: 1 of 3]
291
291
 
292
292
  Why is httpOnly set on the refresh cookie here but not on the access token?
293
293
  ```
294
294
 
295
- Mode, focus, level, what the tier is asking for, and — when the plan holds more
296
- than one — which question of how many this is.
295
+ Every part names its dial: mode, focus, level, what the tier is asking for, and
296
+ — when the plan holds more than one — which question of how many this is. The
297
+ labels are the point. Four bare values only read as a settings line to someone
298
+ who already knows there are four dials and what order they come in.
297
299
 
298
300
  **Print it as the first line of the `question` you pass to `AskUserQuestion`**, then a blank line, then the stem, exactly as given. It is the only thing on screen that explains why a question is pitched where it is — without it, a `concept`-focus question reads as vague and an `easy` question reads as shallow.
299
301
 
@@ -112,6 +112,18 @@ security model, and it is worth saying when someone asks where their data
112
112
  goes. The process runs until interrupted, so start it in the background and
113
113
  hand back the URL rather than blocking the session on it.
114
114
 
115
+ Six sections, each deep-linkable — hand back the one that answers what was
116
+ actually asked rather than the bare root:
117
+
118
+ | Ask | Link |
119
+ |---|---|
120
+ | how am I doing, streaks, activity | `/#/overview` |
121
+ | what am I learning, search a concept | `/#/concepts` (or `/#/concepts/due`, `/mastered`, `/unseen`), `/#/concept/<slug>` for one |
122
+ | what is due, what is scheduled, what I skipped | `/#/review`, `/#/review/upcoming`, `/#/review/skipped` |
123
+ | what did that session teach me | `/#/sessions` |
124
+ | how hard is this repo allowed to get | `/#/projects` |
125
+ | where are the gaps | `/#/domains` |
126
+
115
127
  ## When the plugin is loaded, point at the commands
116
128
 
117
129
  These do more than this skill should reimplement. Name the one that fits and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.9.4",
3
+ "version": "1.10.0",
4
4
  "description": "Learn while your agent works — local knowledge graph, spaced repetition, and quiz gating for agent-assisted development.",
5
5
  "license": "MIT",
6
6
  "repository": {