eklavya 1.9.3 → 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.
- package/dist/ask.js +14 -9
- package/dist/ask.js.map +1 -1
- package/dist/assets/dashboard.html +1351 -300
- package/dist/assets/tokens.css +14 -6
- package/dist/assets/tutor-skill.md +5 -3
- package/dist/dashboard.js +196 -65
- package/dist/dashboard.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/skills/progress/SKILL.md +2 -1
- package/dist/plugin/skills/tutor/SKILL.md +5 -3
- package/dist/user-skill/eklavya/SKILL.md +12 -0
- package/package.json +1 -1
package/dist/assets/tokens.css
CHANGED
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
--vd-300: #79D5C4; /* accent on ink — 10.33:1 */
|
|
35
35
|
--vd-400: #3FB8A3;
|
|
36
36
|
--vd-500: #199688;
|
|
37
|
-
--vd-600: #0E6E66;
|
|
38
|
-
--vd-700: #0A5751;
|
|
37
|
+
--vd-600: #0E6E66;
|
|
38
|
+
--vd-700: #0A5751; /* accent on paper — 7.20:1 on the ground, 5.10:1 on a tint */
|
|
39
39
|
--vd-800: #07403C;
|
|
40
40
|
--vd-900: #052E2B;
|
|
41
41
|
|
|
42
42
|
/* ---------- SEMANTIC (real errors and warnings only) ----------
|
|
43
|
-
Kept off the accent hue so state never reads as branding.
|
|
44
|
-
|
|
43
|
+
Kept off the accent hue so state never reads as branding. Both are
|
|
44
|
+
redefined per ground below: an orange that carries on ink is 2.3:1 on
|
|
45
|
+
cream, which fails as text and as a chart fill alike. */
|
|
45
46
|
--error: #E5484D;
|
|
46
47
|
--info: #4A9EFF;
|
|
47
48
|
|
|
@@ -142,6 +143,8 @@
|
|
|
142
143
|
--glow: rgba(25,150,136,.22); /* the terminal's halo */
|
|
143
144
|
--glow-low: rgba(25,150,136,.10);
|
|
144
145
|
--shot-op: .28; /* the arrow scene, as ambient art */
|
|
146
|
+
--warning: #E8920C;
|
|
147
|
+
--code-bg: var(--mass); /* the well behind a code block */
|
|
145
148
|
--shadow-lg: 0 12px 28px rgba(0,0,0,.45);
|
|
146
149
|
--ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--spot);
|
|
147
150
|
}
|
|
@@ -153,13 +156,13 @@
|
|
|
153
156
|
|
|
154
157
|
--ink: #16150f;
|
|
155
158
|
--dim: #57534a;
|
|
156
|
-
--faint: #
|
|
159
|
+
--faint: #635E52; /* 5.04:1 on --panel, the darkest ground it sits on */
|
|
157
160
|
--faint-2: #a09a8c;
|
|
158
161
|
|
|
159
162
|
--line: #e3dfd4;
|
|
160
163
|
--line-2: #cec8b9;
|
|
161
164
|
|
|
162
|
-
--spot: var(--vd-
|
|
165
|
+
--spot: var(--vd-700);
|
|
163
166
|
--spot-ink: #ffffff;
|
|
164
167
|
--spot-soft: color-mix(in srgb, var(--spot) 10%, transparent);
|
|
165
168
|
|
|
@@ -169,6 +172,11 @@
|
|
|
169
172
|
--glow: rgba(14,110,102,.10);
|
|
170
173
|
--glow-low: rgba(14,110,102,.05);
|
|
171
174
|
--shot-op: .5;
|
|
175
|
+
--warning: #7A4B00; /* 5.21:1 on --mass */
|
|
176
|
+
/* A syntax theme's dimmest token (comments) lands at 3.9:1 on --mass. The
|
|
177
|
+
well is lightened rather than the theme repainted, which lifts every
|
|
178
|
+
token at once; the frame's border is what still reads it as a block. */
|
|
179
|
+
--code-bg: #EDEAE2;
|
|
172
180
|
--shadow-lg: 0 12px 28px rgba(22,21,15,.10);
|
|
173
181
|
--ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--spot);
|
|
174
182
|
}
|
|
@@ -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 ·
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
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 >=
|
|
41
|
-
sum(CASE WHEN grade <
|
|
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 >=
|
|
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
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
.all();
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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 >=
|
|
135
|
-
sum(CASE WHEN outcome
|
|
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
|
-
|
|
266
|
+
sessions: sessionCount,
|
|
267
|
+
active_days: allTime.active_days,
|
|
268
|
+
first_answer: allTime.first_answer,
|
|
150
269
|
},
|
|
151
|
-
|
|
152
|
-
projects: projects.map((p) =>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
160
|
-
|
|
290
|
+
attempts,
|
|
291
|
+
logged,
|
|
161
292
|
};
|
|
162
293
|
}
|
|
163
294
|
function send(res, status, type, body) {
|
package/dist/dashboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA
|
|
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.
|
|
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
|
|
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 ·
|
|
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
|
-
|
|
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