eklavya 1.14.0 → 1.15.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 (46) hide show
  1. package/dist/assets/tutor/SKILL.md +180 -0
  2. package/dist/assets/tutor/references/focus-and-level.md +156 -0
  3. package/dist/assets/tutor/references/grading.md +113 -0
  4. package/dist/assets/tutor/references/writing-mcq.md +133 -0
  5. package/dist/cli.js +66 -17
  6. package/dist/cli.js.map +1 -1
  7. package/dist/eval/extract-json.js +81 -0
  8. package/dist/eval/extract-json.js.map +1 -0
  9. package/dist/eval/extraction-score.js +115 -0
  10. package/dist/eval/extraction-score.js.map +1 -0
  11. package/dist/eval/history-stats.js +230 -0
  12. package/dist/eval/history-stats.js.map +1 -0
  13. package/dist/eval/question-checks.js +220 -0
  14. package/dist/eval/question-checks.js.map +1 -0
  15. package/dist/hooks/checkpoint-quiz.js +1 -1
  16. package/dist/hooks/lib.js +38 -13
  17. package/dist/hooks/lib.js.map +1 -1
  18. package/dist/hooks/prompt-submit-nudge.js +155 -0
  19. package/dist/hooks/prompt-submit-nudge.js.map +1 -0
  20. package/dist/hooks/session-start.js +32 -14
  21. package/dist/hooks/session-start.js.map +1 -1
  22. package/dist/hooks/stop-quiz-check.js +4 -3
  23. package/dist/hooks/stop-quiz-check.js.map +1 -1
  24. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  25. package/dist/plugin/agents/tutor.md +19 -5
  26. package/dist/plugin/hooks/CLAUDE.md +83 -2
  27. package/dist/plugin/hooks/hooks.json +12 -0
  28. package/dist/plugin/hooks/run.mjs +3 -3
  29. package/dist/plugin/skills/CLAUDE.md +107 -15
  30. package/dist/plugin/skills/setup/SKILL.md +1 -1
  31. package/dist/plugin/skills/tutor/SKILL.md +119 -304
  32. package/dist/plugin/skills/tutor/references/focus-and-level.md +156 -0
  33. package/dist/plugin/skills/tutor/references/grading.md +113 -0
  34. package/dist/plugin/skills/tutor/references/writing-mcq.md +133 -0
  35. package/dist/slug.js +63 -0
  36. package/dist/slug.js.map +1 -1
  37. package/dist/stdin.js +131 -0
  38. package/dist/stdin.js.map +1 -0
  39. package/dist/store.js +16 -2
  40. package/dist/store.js.map +1 -1
  41. package/dist/tools/get_session_quiz_plan.js +1 -1
  42. package/dist/tools/get_session_quiz_plan.js.map +1 -1
  43. package/dist/tools/record_attempt.js +15 -3
  44. package/dist/tools/record_attempt.js.map +1 -1
  45. package/package.json +1 -1
  46. package/dist/assets/tutor-skill.md +0 -365
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The deterministic half of the question-quality eval.
3
+ *
4
+ * Eklavya's claim is that the developer learns, and until now nothing measured
5
+ * it: the 426 tests next door cover SM-2 arithmetic, plan sizing, gate maths and
6
+ * migrations -- the machinery -- while the product is a question, and whether a
7
+ * question is any good was checked by nobody.
8
+ *
9
+ * These are the checks that need no model. That matters more than it sounds:
10
+ * an LLM judge is soft, costs money, disagrees with itself between runs, and is
11
+ * biased toward output from its own family. Everything that can be settled by
12
+ * counting is settled here instead, for free and reproducibly, and the judge is
13
+ * left with only the three questions that genuinely need reading comprehension
14
+ * (see `eval/README.md`).
15
+ *
16
+ * Pure, like `srs.ts`: no database, no clock, no file reads. A check returns
17
+ * what it found, never a fix.
18
+ */
19
+ /** Words, for length checks. Punctuation-only tokens do not count. */
20
+ function words(text) {
21
+ return text.split(/\s+/).filter((w) => /[\p{L}\p{N}]/u.test(w));
22
+ }
23
+ /**
24
+ * Lowercased words, with identifiers kept whole.
25
+ *
26
+ * `_`, `-`, `.` and `/` stay inside a token on purpose. Splitting on them turns
27
+ * `max_questions_per_task` into four words and `mcp/src/eval/question-checks.ts`
28
+ * into six, so a stem and an answer that merely name the same identifier -- the
29
+ * ordinary way to write these questions -- would share a four-word run and trip
30
+ * `answer_not_in_stem` on its own. The fixtures here are this repo's own code,
31
+ * so that is the common case, not the corner one.
32
+ */
33
+ function normalized(text) {
34
+ return words(text.toLowerCase().replace(/[^\p{L}\p{N}_\-./]+/gu, ' '));
35
+ }
36
+ /**
37
+ * The longest run of consecutive words the two strings share.
38
+ *
39
+ * Used for "the answer is not restated in the stem". A bag-of-words overlap
40
+ * would fire on every question, because a stem is *supposed* to share its
41
+ * subject with the answer -- "httpOnly" appears in both by design. A shared
42
+ * four-word run is a different thing: that is the sentence, not the subject.
43
+ */
44
+ export function longestSharedRun(a, b) {
45
+ const x = normalized(a);
46
+ const y = normalized(b);
47
+ if (x.length === 0 || y.length === 0)
48
+ return 0;
49
+ // Classic LCS-of-substrings table. Both inputs are one sentence, so the
50
+ // quadratic cost is a few thousand operations.
51
+ let best = 0;
52
+ const prev = new Array(y.length + 1).fill(0);
53
+ for (let i = 1; i <= x.length; i++) {
54
+ let diagonal = 0;
55
+ for (let j = 1; j <= y.length; j++) {
56
+ const above = prev[j] ?? 0;
57
+ const run = x[i - 1] === y[j - 1] ? diagonal + 1 : 0;
58
+ diagonal = above;
59
+ prev[j] = run;
60
+ if (run > best)
61
+ best = run;
62
+ }
63
+ }
64
+ return best;
65
+ }
66
+ /** Stems this long stop being read and start being pattern-matched. */
67
+ export const STEM_WORD_LIMIT = 25;
68
+ /** A shared run this long means the stem contains the answer's sentence. */
69
+ export const ANSWER_RUN_LIMIT = 4;
70
+ /**
71
+ * Ratio between the longest and shortest option. Four options of visibly
72
+ * different length leak the answer through shape alone, whichever one is
73
+ * correct.
74
+ */
75
+ export const OPTION_RATIO_LIMIT = 2.5;
76
+ /**
77
+ * Below this many words in the longest option, the ratio says nothing: four
78
+ * answers like `O(1)` / `O(n log n)` sit at 3.0 and are perfectly matched to a
79
+ * reader. Shape is a tell in sentences, not in tokens.
80
+ */
81
+ export const PARITY_MIN_WORDS = 6;
82
+ /**
83
+ * How many words longer the correct option may be than the next-longest before
84
+ * the length itself is a hint. Below this it is noise; a reader does not count
85
+ * words, they see a shape.
86
+ */
87
+ export const LENGTH_MARGIN_WORDS = 3;
88
+ /**
89
+ * Negation that makes a stem a reading test.
90
+ *
91
+ * Deliberately narrow. A plain "not" is often legitimate and contrastive --
92
+ * "why is this set on the refresh cookie but not on the access token" is a good
93
+ * stem -- so flagging every "not" would flag good questions and train whoever
94
+ * reads the report to ignore it. These three forms are the ones that turn the
95
+ * question itself inside out.
96
+ */
97
+ const NEGATED_STEM = [
98
+ // "Which of these is not / will not / can't / is never ..."
99
+ /\bwhich\b(?:\s+of\s+(?:these|the\s+following))?\s+\w*\s*\b(?:is|are|does|do|will|would|can|could)(?:n't|\s+not|\s+never)\b/i,
100
+ // "... except which of these", but not the ordinary prose "except a corrupt file"
101
+ /\bexcept\b\s+(?:for\s+)?(?:which|one|these|the\s+following)\b/i,
102
+ /\b(?:is|are)\s+(?:not|never)\s+true\b/i,
103
+ /\b(?:incorrect|least\s+likely)\b/i,
104
+ /\bnone\s+of\s+(?:these|the\s+above)\b/i,
105
+ ];
106
+ /**
107
+ * The tool renders the labels; a stem that numbers them is showing them twice.
108
+ *
109
+ * Two markers, not one. A single `b)` is `retry(db, b)` and a single `3.` is
110
+ * "the plan asked for tier 3. Why..." -- both ordinary, and both were flagged by
111
+ * the first version of this. An actual enumeration always has at least two.
112
+ */
113
+ const OPTION_MARKER = /(^|\s)(?:[a-d]\)|\(?[1-4][).]\s)/gim;
114
+ function numberedOptions(stem) {
115
+ return (stem.match(OPTION_MARKER) ?? []).length >= 2;
116
+ }
117
+ export function checkQuestion(q) {
118
+ const checks = [];
119
+ const push = (id, ok, detail) => checks.push({ id, ok, detail });
120
+ const labels = q.options.map((o) => o.trim()).filter((o) => o.length > 0);
121
+ const unique = new Set(labels.map((o) => o.toLowerCase()));
122
+ push('four_options', labels.length === 4 && unique.size === 4, `${labels.length} non-empty option(s), ${unique.size} distinct`);
123
+ // The obedience check, and the reason the whole harness drives the real
124
+ // planner rather than inventing plan items: `answer_position` is assigned
125
+ // server-side by a hash so the answer cannot cluster, and that only works if
126
+ // the model actually puts it there.
127
+ const obeyed = q.correct === q.answer_position;
128
+ push('answer_at_position', obeyed, obeyed
129
+ ? `correct option at ${q.answer_position}, as asked`
130
+ : `plan said ${q.answer_position}, model used ${q.correct}`);
131
+ const stemWords = words(q.stem).length;
132
+ push('stem_length', stemWords <= STEM_WORD_LIMIT, `${stemWords} words (limit ${STEM_WORD_LIMIT})`);
133
+ // Indexed into `q.options`, never `labels`. `labels` drops empty options, so
134
+ // one blank before the answer shifts every later index by one -- and the
135
+ // conspicuousness check below then compared the correct option against
136
+ // itself and always passed, on exactly the malformed question it should have
137
+ // been loudest about.
138
+ const inRange = Number.isInteger(q.correct) && q.correct >= 1 && q.correct <= q.options.length;
139
+ push('correct_in_range', inRange, inRange ? `correct = ${q.correct}` : `correct = ${JSON.stringify(q.correct)}, options = ${q.options.length}`);
140
+ const correct = inRange ? (q.options[q.correct - 1] ?? '') : '';
141
+ const run = longestSharedRun(q.stem, correct);
142
+ push('answer_not_in_stem', run < ANSWER_RUN_LIMIT, `longest run shared with the correct option: ${run} word(s)`);
143
+ const lengths = q.options.map((o) => words(o).length);
144
+ const longest = Math.max(...lengths, 0);
145
+ const shortest = Math.min(...lengths, Infinity);
146
+ const ratio = shortest > 0 ? longest / shortest : Infinity;
147
+ const shortEnoughToNotMatter = longest <= PARITY_MIN_WORDS;
148
+ push('option_parity', shortEnoughToNotMatter || ratio <= OPTION_RATIO_LIMIT, shortEnoughToNotMatter
149
+ ? `all four options are ${longest} words or fewer, so shape carries nothing`
150
+ : `longest/shortest = ${ratio.toFixed(2)} (limit ${OPTION_RATIO_LIMIT})`);
151
+ // Separate from parity on purpose. Four options can sit inside the ratio and
152
+ // the correct one still stand out, which is the tell a learner notices first:
153
+ // pick the most careful-sounding answer without reading it.
154
+ //
155
+ // The margin is why this is not simply "is it the longest". The first live
156
+ // run flagged all three of its questions, and one of the three was the
157
+ // correct option beating the next by two words -- 17 against 15, which nobody
158
+ // can see. The other two were four-word margins and still flag today. A check
159
+ // that fires on noise is a check whose report gets skipped. Being the longest
160
+ // at all is still worth counting, but as a rate across the run
161
+ // (`correctLongest` below), not as a verdict on one question.
162
+ const correctLength = words(correct).length;
163
+ const others = q.options.filter((_, i) => i !== q.correct - 1).map((o) => words(o).length);
164
+ const runnerUp = Math.max(...others, 0);
165
+ const margin = correctLength - runnerUp;
166
+ push('correct_not_conspicuous', margin < LENGTH_MARGIN_WORDS, `correct option is ${correctLength} words against a next-longest ${runnerUp} (margin ${margin}, limit ${LENGTH_MARGIN_WORDS})`);
167
+ const negated = NEGATED_STEM.find((re) => re.test(q.stem));
168
+ push('positive_form', !negated, negated ? `stem is negated: ${negated}` : 'stem asks the positive form');
169
+ const numbered = numberedOptions(q.stem);
170
+ push('options_not_numbered', !numbered, numbered ? 'stem enumerates the options itself' : 'stem does not letter or number the options');
171
+ // 1.14 removed the settings line from the stem. A stem that opens with a
172
+ // bracketed readout is the model composing one anyway, and it would change
173
+ // the fingerprint the repeat check depends on.
174
+ push('no_settings_line', !q.stem.trimStart().startsWith('['), 'stem opens with the question');
175
+ return checks;
176
+ }
177
+ export function scoreAll(questions) {
178
+ const scored = questions.map((question) => {
179
+ const checks = checkQuestion(question);
180
+ return { question, checks, passed: checks.every((c) => c.ok) };
181
+ });
182
+ const byCheck = {};
183
+ for (const { checks } of scored) {
184
+ for (const c of checks) {
185
+ const row = (byCheck[c.id] ??= { passed: 0, failed: 0 });
186
+ if (c.ok)
187
+ row.passed++;
188
+ else
189
+ row.failed++;
190
+ }
191
+ }
192
+ const slots = [0, 0, 0, 0];
193
+ let correctLongest = 0;
194
+ for (const { question } of scored) {
195
+ const i = question.correct - 1;
196
+ if (i === 0 || i === 1 || i === 2 || i === 3)
197
+ slots[i] += 1;
198
+ // Inside the same guard as `slots`: an out-of-range `correct` is a
199
+ // malformed question, and counting it as "not the longest" reports a
200
+ // property of a question that does not exist.
201
+ if (i === 0 || i === 1 || i === 2 || i === 3) {
202
+ const lengths = question.options.map((o) => words(o).length);
203
+ const mine = lengths[i] ?? 0;
204
+ if (mine === Math.max(...lengths, 0) && lengths.filter((l) => l === mine).length === 1) {
205
+ correctLongest++;
206
+ }
207
+ }
208
+ }
209
+ return {
210
+ scored,
211
+ summary: {
212
+ questions: scored.length,
213
+ clean: scored.filter((s) => s.passed).length,
214
+ byCheck,
215
+ slots,
216
+ correctLongest,
217
+ },
218
+ };
219
+ }
220
+ //# sourceMappingURL=question-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-checks.js","sourceRoot":"","sources":["../../src/eval/question-checks.ts"],"names":[],"mappings":"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"}
@@ -129,7 +129,7 @@ Concept: ${row.concept}
129
129
  Do exactly this, then get straight back to the task:
130
130
  1. get_session_quiz_plan with max: 1 and ignore_cooldown: true (the pacing is already decided -- this hook is the cooldown).
131
131
  2. Ask that ONE question with AskUserQuestion: four options, one correct, three plausible, and put the correct one in the slot answer_position names. Header "Eklavya", so it is clear who is asking.
132
- Ask the stem on its own -- the dials that chose this question live in the status bar, so do not prepend a settings line to it.
132
+ Ask the stem on its own -- the dials that chose this question live in the status bar, so what you ask is the question and nothing else.
133
133
  3. Grade it with record_attempt: format "mcq", the labels in "options", the stem alone in "question".
134
134
  4. Resume the task exactly where you left off. Do not summarise, do not re-plan, do not ask a second question.
135
135
 
package/dist/hooks/lib.js CHANGED
@@ -19,22 +19,23 @@ import fs from 'node:fs';
19
19
  import Database from 'better-sqlite3';
20
20
  import { dbPath } from '../paths.js';
21
21
  import { loadConfig } from '../config.js';
22
+ import { readStdinBounded, stripBom, HOOK_STDIN } from '../stdin.js';
22
23
  /**
23
- * Reads stdin to EOF. Hooks are always given JSON, but "always" is doing a lot
24
- * of work on a critical path — a hook invoked by hand, or by a harness version
25
- * that changes its mind, gets an empty object rather than an exception.
24
+ * The JSON the host piped in, or `{}`.
25
+ *
26
+ * Hooks are always given JSON, but "always" is doing a lot of work on a
27
+ * critical path -- a hook invoked by hand, or by a harness version that changes
28
+ * its mind, gets an empty object rather than an exception.
29
+ *
30
+ * The read is bounded (`readStdinBounded`). It used to be `for await (const
31
+ * chunk of process.stdin)`, which waits for EOF and has no other exit: on
32
+ * Windows the host may run a hook through a PowerShell block that swallows the
33
+ * pipe, so `end` never fires and the hook blocks the session on every tool call
34
+ * that triggers it. A hook that throws is survivable; a hook that waits is not.
26
35
  */
27
- async function readStdin() {
28
- if (process.stdin.isTTY)
29
- return '';
30
- const chunks = [];
31
- for await (const chunk of process.stdin)
32
- chunks.push(chunk);
33
- return Buffer.concat(chunks).toString('utf8');
34
- }
35
36
  export async function readInput() {
36
37
  try {
37
- const raw = await readStdin();
38
+ const raw = stripBom(await readStdinBounded(HOOK_STDIN));
38
39
  if (!raw.trim())
39
40
  return {};
40
41
  const parsed = JSON.parse(raw);
@@ -49,7 +50,7 @@ export async function readInput() {
49
50
  *
50
51
  * `openDb()` is the server's entry point and does both, which is right for a
51
52
  * server and wrong here: a hook is not the thing that should be migrating a
52
- * schema, and four hooks racing a migration on session start is a corruption
53
+ * schema, and several hooks racing a migration on session start is a corruption
53
54
  * story rather than a feature. A hook that finds no database has nothing to say,
54
55
  * which is the same answer `eklavya_have_deps` gave.
55
56
  */
@@ -67,6 +68,30 @@ export function openExisting() {
67
68
  return null;
68
69
  }
69
70
  }
71
+ /**
72
+ * `meta` key holding the UserPromptSubmit nudge's per-session bookkeeping.
73
+ * Shared here rather than exported from the hook, because `session-start`
74
+ * clears it and two hooks spelling the same key differently would be a bug that
75
+ * only shows up as a nudge that never stops.
76
+ */
77
+ export const NUDGE_KEY_PREFIX = 'prompt_nudge:';
78
+ /**
79
+ * Forget what the nudge knows about this session.
80
+ *
81
+ * Called by `session-start`, which fires on resume and after a compaction as
82
+ * well as at startup, and reprints the standing directive every time. Without
83
+ * this the nudge's row survives with its original first-seen stamp, the grace
84
+ * window is already spent, and the first prompt of a resumed session restates a
85
+ * directive printed seconds earlier.
86
+ */
87
+ export function clearNudgeState(db, sessionId) {
88
+ try {
89
+ db.prepare('DELETE FROM meta WHERE key = ?').run(`${NUDGE_KEY_PREFIX}${sessionId}`);
90
+ }
91
+ catch {
92
+ /* Bookkeeping. A session that keeps its old row gets one extra nudge. */
93
+ }
94
+ }
70
95
  export function config(cwd) {
71
96
  return loadConfig(cwd);
72
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/hooks/lib.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../../src/hooks/lib.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * UserPromptSubmit: say the standing instruction again, once, when a session
3
+ * has plainly stopped hearing it.
4
+ *
5
+ * `session-start.ts` injects the directive that starts the whole loop -- call
6
+ * `log_session_concepts` with what this task exercises -- and its own comment
7
+ * explains why it lives in a hook rather than only in the tutor skill: the skill
8
+ * is model-invocable, it competes with every other skill on the machine, and it
9
+ * may never load. The same argument applies one level up. A directive injected
10
+ * once is the oldest thing in the context window by turn forty, competing with
11
+ * everything that arrived after it.
12
+ *
13
+ * That failure is the worst-shaped one Eklavya has, because nothing errors.
14
+ * Nothing gets logged, so `session_concepts` stays empty, so the Stop hook finds
15
+ * no candidates and exits silently. From the outside it reads as *Eklavya is
16
+ * broken* -- which is unreportable, and the reason both comparable plugins
17
+ * (ponytail, claude-mem) re-inject on every prompt rather than once per session.
18
+ *
19
+ * Where this deliberately differs from them: they re-emit their whole ruleset
20
+ * every turn. This emits ONE line, and only when it has been earned. A hook that
21
+ * spends tokens on every prompt to repeat something the model is already doing
22
+ * is a hook that makes every session more expensive to no end -- and a nudge
23
+ * that arrives while logging is working teaches the model to ignore nudges.
24
+ *
25
+ * So the predicate is narrow, and it is the one case worth catching: this
26
+ * session has logged **nothing at all**, and it has been going long enough that
27
+ * the session-start directive can no longer be blamed on not having had a chance.
28
+ * A session that logged once and then went quiet for a while is *not* nudged --
29
+ * concepts are logged per task and a task can legitimately run long, so that
30
+ * signal is noise.
31
+ *
32
+ * State lives in `meta` rather than a table of its own: one row per session,
33
+ * `prompt_nudge:<sid>` -> `<first-seen>|<last-nudge>|<count>`, pipe-delimited so
34
+ * the prune below is one statement and needs no JSON parsing in SQL. A
35
+ * migration for a rate limiter would be a schema change for a comment.
36
+ * `session-start` deletes the row when it reprints the directive, which is what
37
+ * re-arms the grace window on a resume or after a compaction.
38
+ *
39
+ * Failure is silent, as everywhere here: exit 0, no output. This runs on every
40
+ * prompt the developer types, so it is also the hook with the least right to be
41
+ * slow -- the common case (a session that is logging) is one COUNT and out.
42
+ */
43
+ import { run, openExisting, config, cwdOf, sessionId, minutesSince, NUDGE_KEY_PREFIX, } from './lib.js';
44
+ /**
45
+ * Long enough that the session-start directive has had a fair chance, short
46
+ * enough to still be the same piece of work. Not configurable on purpose: a
47
+ * knob for this would be a config key, a schema entry, a CLI line and four doc
48
+ * updates to tune something nobody can feel.
49
+ */
50
+ const GRACE_MINUTES = 12;
51
+ /** And having said it once, do not say it again this soon. */
52
+ const COOLDOWN_MINUTES = 25;
53
+ /**
54
+ * And having said it three times, stop.
55
+ *
56
+ * The cooldown alone is not a bound. "This session has logged nothing at all"
57
+ * is not only the signature of a model that forgot the directive -- it is also
58
+ * exactly what a dead MCP server looks like from here, and that state never
59
+ * resolves. Without a cap an eight-hour session against an unreachable server
60
+ * gets nineteen injections telling the model to call a tool that is not
61
+ * registered, each one likely drawing a visible failure in the transcript.
62
+ *
63
+ * `max_stop_blocks_per_session` is the same kind of backstop for the Stop hook,
64
+ * for the same reason. A constant rather than a config key: the difference
65
+ * between three nudges and four is not something anyone needs to tune.
66
+ */
67
+ const MAX_NUDGES = 3;
68
+ /**
69
+ * `<first-seen ISO>|<last-nudge ISO or empty>|<nudges so far>`
70
+ *
71
+ * The count was appended rather than inserted, so a row written by the previous
72
+ * shape reads back as count 0 instead of throwing. It buys such a row at most
73
+ * MAX_NUDGES extra nudges, once, which is cheaper than a migration.
74
+ */
75
+ function readState(db, key) {
76
+ const row = db.prepare('SELECT value FROM meta WHERE key = ?').get(key);
77
+ if (!row)
78
+ return null;
79
+ const [first, nudged, count] = row.value.split('|');
80
+ if (!first)
81
+ return null;
82
+ return { first, nudged: nudged || null, count: Number(count) || 0 };
83
+ }
84
+ function writeState(db, key, first, nudged, count) {
85
+ db.prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, `${first}|${nudged ?? ''}|${count}`);
86
+ }
87
+ /**
88
+ * One row per session would otherwise be one row per session forever. The value
89
+ * starts with an ISO date, which compares lexicographically, so a week's cutoff
90
+ * is a substring comparison rather than a parse.
91
+ */
92
+ function prune(db) {
93
+ db.prepare(`DELETE FROM meta
94
+ WHERE key LIKE ?
95
+ AND substr(value, 1, 10) < date('now', '-7 day')`).run(`${NUDGE_KEY_PREFIX}%`);
96
+ }
97
+ await run(async (input) => {
98
+ // A subagent's prompts are not the developer's, and the parent is the thread
99
+ // that logs. Nudging here would spend context inside a transcript nobody is
100
+ // reading. Same reasoning as checkpoint-quiz.ts.
101
+ if (input.agent_id)
102
+ return 0;
103
+ // Config before the database, unlike the other hooks: this one runs on every
104
+ // prompt the developer types, so a dormant or quiet session should not pay to
105
+ // open SQLite before finding out it had nothing to do. Reading one or two
106
+ // small JSON files is the cheaper question, so it is asked first.
107
+ const { mode } = config(cwdOf(input)).config;
108
+ if (mode === 'off')
109
+ return 0;
110
+ // `quiet` is deliberately NOT consulted. It suppresses the banner and the
111
+ // status bar -- things the developer looks at -- and this is an
112
+ // additionalContext line the model reads, exactly like the session-start
113
+ // directive it restates. Honouring it here used to be defended as consistency
114
+ // with that directive, which was itself being dropped under `quiet`: between
115
+ // them they turned a preference about greetings into a silent off switch.
116
+ // `mode: off` is the off switch.
117
+ const db = openExisting();
118
+ if (!db)
119
+ return 0;
120
+ const sid = sessionId(input, db);
121
+ if (!sid)
122
+ return 0;
123
+ // The fast path, and the one that runs on almost every prompt: a session that
124
+ // is logging needs nothing said to it.
125
+ const logged = db
126
+ .prepare('SELECT count(*) AS n FROM session_concepts WHERE session_id = ?')
127
+ .get(sid);
128
+ if ((logged?.n ?? 0) > 0)
129
+ return 0;
130
+ const key = `${NUDGE_KEY_PREFIX}${sid}`;
131
+ const now = new Date().toISOString();
132
+ const state = readState(db, key);
133
+ // First prompt we have seen for this session. The directive was injected
134
+ // seconds ago; repeating it now would be noise. Start the clock instead.
135
+ if (!state) {
136
+ prune(db);
137
+ writeState(db, key, now, null, 0);
138
+ return 0;
139
+ }
140
+ if (state.count >= MAX_NUDGES)
141
+ return 0;
142
+ if (minutesSince(state.first) < GRACE_MINUTES)
143
+ return 0;
144
+ if (state.nudged && minutesSince(state.nudged) < COOLDOWN_MINUTES)
145
+ return 0;
146
+ writeState(db, key, state.first, now, state.count + 1);
147
+ process.stdout.write(`${JSON.stringify({
148
+ hookSpecificOutput: {
149
+ hookEventName: 'UserPromptSubmit',
150
+ additionalContext: '[Eklavya] Nothing logged this session. Once you know what the current task involves, call log_session_concepts with the 3-8 concepts it genuinely exercises, each with a context line naming the real code — without it there is nothing to quiz on.',
151
+ },
152
+ })}\n`);
153
+ return 0;
154
+ });
155
+ //# sourceMappingURL=prompt-submit-nudge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-submit-nudge.js","sourceRoot":"","sources":["../../src/hooks/prompt-submit-nudge.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { setCurrentSession } from '../session.js';
8
8
  import { levelStanding } from '../store.js';
9
- import { run, openExisting, config, cwdOf, sessionId } from './lib.js';
9
+ import { run, openExisting, config, cwdOf, sessionId, clearNudgeState } from './lib.js';
10
10
  /**
11
11
  * The whole tutoring loop starts at log_session_concepts: it is the only writer
12
12
  * of session_concepts, and the Stop hook quizzes from nothing else. An unlogged
@@ -94,15 +94,36 @@ await run(async (input) => {
94
94
  // `levelStanding` is the server's own function, so the banner cannot drift
95
95
  // from what the quiz planner actually does — the shell version reimplemented
96
96
  // this query and had to keep it in step by hand.
97
- let levelLabel = `${difficulty} (pinned)`;
98
- if (difficulty === 'auto') {
99
- const standing = levelStanding(db, resolved.config, resolved.repoRoot);
100
- levelLabel = `${standing.level} (${standing.counts.passed}/${standing.needed.answers} on this project)`;
97
+ const out = [];
98
+ // `quiet` suppresses the banner, and only the banner. It used to return here,
99
+ // which also dropped the directive below -- so a developer who turned the
100
+ // greeting off silently turned the whole product off: nothing was logged, so
101
+ // the Stop hook found no candidates, so no question was ever asked, while
102
+ // `get_config` went on reporting `mode: ambient`. The manual has always said
103
+ // this key "suppresses the session-start banner and the statusline output",
104
+ // and `mode: off` is the documented way to stop Eklavya doing anything.
105
+ if (!quiet) {
106
+ let levelLabel = `${difficulty} (pinned)`;
107
+ if (difficulty === 'auto') {
108
+ const standing = levelStanding(db, resolved.config, resolved.repoRoot);
109
+ levelLabel = `${standing.level} (${standing.counts.passed}/${standing.needed.answers} on this project)`;
110
+ }
111
+ banner(db, out, { mode, focusLabel, cadence, levelLabel, overrides: resolved.overrides });
101
112
  }
102
- if (quiet)
103
- return 0;
113
+ // The directive is on screen again, so the nudge's clock starts again. This
114
+ // hook fires on resume and after a compaction, not only at startup, and both
115
+ // reuse the session id -- without this the first prompt of a resumed session
116
+ // restates what was printed seconds ago.
117
+ if (sid)
118
+ clearNudgeState(db, sid);
119
+ out.push(DIRECTIVE);
120
+ process.stdout.write(`${out.join('\n')}\n`);
121
+ return 0;
122
+ });
123
+ /** The developer-facing greeting. Everything `quiet` is about. */
124
+ function banner(db, out, parts) {
125
+ const { mode, focusLabel, cadence, levelLabel, overrides } = parts;
104
126
  const domains = domainSummary(db);
105
- const out = [];
106
127
  if (!domains) {
107
128
  // Nothing learned yet — say the useful thing instead of an empty scoreboard.
108
129
  out.push(`[Eklavya] No learning history yet. Mode: ${mode}. Focus: ${focusLabel}. Cadence: ${cadence}. Level: ${levelLabel}.`);
@@ -117,11 +138,8 @@ await run(async (input) => {
117
138
  line += ` ${due} concept(s) due for review.`;
118
139
  out.push(`${line} Mode: ${mode}. Focus: ${focusLabel}. Cadence: ${cadence}. Level: ${levelLabel}.`);
119
140
  }
120
- if (resolved.overrides.length > 0) {
121
- out.push(`[Eklavya] This repo overrides your global setting for: ${resolved.overrides.join(' ')} (.eklavya.json wins).`);
141
+ if (overrides.length > 0) {
142
+ out.push(`[Eklavya] This repo overrides your global setting for: ${overrides.join(' ')} (.eklavya.json wins).`);
122
143
  }
123
- out.push(DIRECTIVE);
124
- process.stdout.write(`${out.join('\n')}\n`);
125
- return 0;
126
- });
144
+ }
127
145
  //# sourceMappingURL=session-start.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":"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"}
@@ -158,12 +158,13 @@ Concepts: ${concepts}
158
158
  ${ask}
159
159
 
160
160
  Ask each question as MULTIPLE CHOICE via the AskUserQuestion tool: four options,
161
- one correct and three plausible, header "Eklavya" so it is clear who is asking.
161
+ one correct and three plausible, the correct one in the slot answer_position
162
+ names, header "Eklavya" so it is clear who is asking.
162
163
  The tool's own "Other" choice is the escape hatch for "I don't know". They are
163
164
  mid-task -- a blank prompt gets skipped whether or not they knew the answer.
164
165
  Ask the stem on its own. The dials that pitched it -- mode, focus, cadence,
165
- level -- are in the status bar, so never prepend a settings line to the
166
- question.
166
+ level -- are in the status bar, so what you ask is the question and nothing
167
+ else.
167
168
  Record with format "mcq", the labels in "options", and only the stem in
168
169
  "question".
169
170
  ${framing}
@@ -1 +1 @@
1
- {"version":3,"file":"stop-quiz-check.js","sourceRoot":"","sources":["../../src/hooks/stop-quiz-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stop-quiz-check.js","sourceRoot":"","sources":["../../src/hooks/stop-quiz-check.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.14.0",
4
+ "version": "1.15.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"